From a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 Mon Sep 17 00:00:00 2001 From: Nippy Date: Sat, 9 May 2026 13:33:09 +0200 Subject: raveos update --- .../theme-data/DankMaterialShell/core/Makefile | 163 - .../theme-data/DankMaterialShell/core/README.md | 217 - .../DankMaterialShell/core/assets/dank.svg | 46 - .../DankMaterialShell/core/assets/danklogo.svg | 113 - .../DankMaterialShell/core/build_dankinstall.sh | 38 - .../DankMaterialShell/core/cmd/dankinstall/main.go | 219 - .../core/cmd/dms/assets/cli-policy.default.json | 10 - .../core/cmd/dms/commands_auth.go | 76 - .../core/cmd/dms/commands_blur.go | 40 - .../core/cmd/dms/commands_brightness.go | 311 - .../core/cmd/dms/commands_chroma.go | 300 - .../core/cmd/dms/commands_clipboard.go | 1004 --- .../core/cmd/dms/commands_colorpicker.go | 139 - .../core/cmd/dms/commands_common.go | 530 -- .../core/cmd/dms/commands_config.go | 318 - .../core/cmd/dms/commands_dank16.go | 126 - .../core/cmd/dms/commands_doctor.go | 1107 --- .../core/cmd/dms/commands_download.go | 99 - .../core/cmd/dms/commands_dpms.go | 105 - .../core/cmd/dms/commands_features.go | 491 -- .../core/cmd/dms/commands_greeter.go | 1850 ----- .../core/cmd/dms/commands_greeter_test.go | 87 - .../core/cmd/dms/commands_keybinds.go | 265 - .../core/cmd/dms/commands_matugen.go | 202 - .../core/cmd/dms/commands_notify.go | 68 - .../core/cmd/dms/commands_open.go | 205 - .../core/cmd/dms/commands_randr.go | 58 - .../core/cmd/dms/commands_root.go | 74 - .../core/cmd/dms/commands_screenshot.go | 422 -- .../core/cmd/dms/commands_setup.go | 436 -- .../core/cmd/dms/commands_windowrules.go | 338 - .../DankMaterialShell/core/cmd/dms/dpms_client.go | 339 - .../core/cmd/dms/immutable_policy.go | 271 - .../DankMaterialShell/core/cmd/dms/main.go | 43 - .../DankMaterialShell/core/cmd/dms/main_distro.go | 40 - .../DankMaterialShell/core/cmd/dms/randr_client.go | 172 - .../core/cmd/dms/server_client.go | 114 - .../DankMaterialShell/core/cmd/dms/shell.go | 739 -- .../DankMaterialShell/core/cmd/dms/ui.go | 53 - .../DankMaterialShell/core/cmd/dms/utils.go | 89 - .../theme-data/DankMaterialShell/core/go.mod | 87 - .../theme-data/DankMaterialShell/core/go.sum | 191 - .../theme-data/DankMaterialShell/core/install.sh | 86 - .../DankMaterialShell/core/internal/blur/probe.go | 35 - .../core/internal/clipboard/clipboard.go | 630 -- .../core/internal/clipboard/store.go | 248 - .../core/internal/clipboard/watch.go | 303 - .../core/internal/colorpicker/color.go | 306 - .../core/internal/colorpicker/picker.go | 744 -- .../core/internal/colorpicker/shm.go | 55 - .../core/internal/colorpicker/state.go | 1195 ---- .../core/internal/colorpicker/state_test.go | 314 - .../core/internal/config/deployer.go | 718 -- .../core/internal/config/deployer_test.go | 792 -- .../DankMaterialShell/core/internal/config/dms.go | 57 - .../internal/config/embedded/alacritty-theme.toml | 31 - .../core/internal/config/embedded/alacritty.toml | 37 - .../internal/config/embedded/ghostty-colors.conf | 21 - .../core/internal/config/embedded/ghostty.conf | 51 - .../core/internal/config/embedded/hypr-binds.conf | 162 - .../core/internal/config/embedded/hypr-colors.conf | 25 - .../core/internal/config/embedded/hypr-layout.conf | 11 - .../core/internal/config/embedded/hyprland.conf | 121 - .../core/internal/config/embedded/kitty-tabs.conf | 24 - .../core/internal/config/embedded/kitty-theme.conf | 24 - .../core/internal/config/embedded/kitty.conf | 37 - .../core/internal/config/embedded/niri-alttab.kdl | 10 - .../core/internal/config/embedded/niri-binds.kdl | 221 - .../core/internal/config/embedded/niri-colors.kdl | 39 - .../core/internal/config/embedded/niri-greeter.kdl | 17 - .../core/internal/config/embedded/niri-layout.kdl | 22 - .../core/internal/config/embedded/niri.kdl | 279 - .../core/internal/config/embedded/testpage.pdf | Bin 7396 -> 0 bytes .../core/internal/config/hyprland.go | 15 - .../DankMaterialShell/core/internal/config/niri.go | 21 - .../core/internal/config/terminals.go | 24 - .../core/internal/config/testpage.go | 6 - .../core/internal/dank16/dank16.go | 651 -- .../core/internal/dank16/dank16_test.go | 681 -- .../core/internal/dank16/terminals.go | 135 - .../core/internal/deps/detector.go | 51 - .../core/internal/distros/arch.go | 807 --- .../core/internal/distros/base.go | 721 -- .../core/internal/distros/base_test.go | 216 - .../core/internal/distros/debian.go | 706 -- .../core/internal/distros/factory.go | 19 - .../core/internal/distros/fedora.go | 550 -- .../core/internal/distros/gentoo.go | 700 -- .../core/internal/distros/interface.go | 158 - .../core/internal/distros/manual_packages.go | 663 -- .../core/internal/distros/manual_packages_test.go | 122 - .../core/internal/distros/minimal_install.go | 44 - .../core/internal/distros/opensuse.go | 856 --- .../core/internal/distros/osinfo.go | 122 - .../core/internal/distros/ubuntu.go | 702 -- .../core/internal/errdefs/errdefs.go | 65 - .../core/internal/geolocation/client.go | 42 - .../core/internal/geolocation/client_geoclue.go | 243 - .../core/internal/geolocation/client_ip.go | 91 - .../core/internal/geolocation/types.go | 15 - .../greeter/assets/apparmor/usr.bin.dms-greeter | 91 - .../core/internal/greeter/installer.go | 2076 ------ .../core/internal/greeter/installer_test.go | 98 - .../core/internal/headless/runner.go | 418 -- .../core/internal/headless/runner_test.go | 459 -- .../core/internal/keybinds/discovery.go | 107 - .../core/internal/keybinds/discovery_test.go | 287 - .../core/internal/keybinds/providers/hyprland.go | 497 -- .../internal/keybinds/providers/hyprland_parser.go | 627 -- .../keybinds/providers/hyprland_parser_test.go | 519 -- .../internal/keybinds/providers/hyprland_test.go | 220 - .../core/internal/keybinds/providers/jsonfile.go | 119 - .../internal/keybinds/providers/jsonfile_test.go | 281 - .../core/internal/keybinds/providers/mangowc.go | 442 -- .../internal/keybinds/providers/mangowc_parser.go | 613 -- .../keybinds/providers/mangowc_parser_test.go | 499 -- .../internal/keybinds/providers/mangowc_test.go | 320 - .../core/internal/keybinds/providers/miracle.go | 95 - .../internal/keybinds/providers/miracle_parser.go | 320 - .../core/internal/keybinds/providers/niri.go | 650 -- .../internal/keybinds/providers/niri_parser.go | 400 -- .../keybinds/providers/niri_parser_test.go | 630 -- .../core/internal/keybinds/providers/niri_test.go | 661 -- .../core/internal/keybinds/providers/sway.go | 150 - .../internal/keybinds/providers/sway_parser.go | 364 - .../keybinds/providers/sway_parser_test.go | 471 -- .../core/internal/keybinds/providers/sway_test.go | 293 - .../core/internal/keybinds/registry.go | 79 - .../core/internal/keybinds/registry_test.go | 183 - .../core/internal/keybinds/types.go | 47 - .../core/internal/log/filelogger.go | 114 - .../DankMaterialShell/core/internal/log/log.go | 116 - .../core/internal/matugen/matugen.go | 978 --- .../core/internal/matugen/matugen_test.go | 443 -- .../core/internal/matugen/queue.go | 143 - .../internal/mocks/brightness/mock_DBusConn.go | 130 - .../mocks/cups/mock_CUPSClientInterface.go | 1246 ---- .../internal/mocks/cups_pkhelper/mock_PkHelper.go | 708 -- .../core/internal/mocks/evdev/mock_EvdevDevice.go | 295 - .../core/internal/mocks/geolocation/mock_Client.go | 203 - .../Wifx/gonetworkmanager/v2/mock_AccessPoint.go | 689 -- .../gonetworkmanager/v2/mock_ActiveConnection.go | 1025 --- .../Wifx/gonetworkmanager/v2/mock_Connection.go | 646 -- .../Wifx/gonetworkmanager/v2/mock_Device.go | 1638 ----- .../gonetworkmanager/v2/mock_DeviceWireless.go | 2241 ------ .../Wifx/gonetworkmanager/v2/mock_IP4Config.go | 772 -- .../gonetworkmanager/v2/mock_NetworkManager.go | 2349 ------ .../Wifx/gonetworkmanager/v2/mock_Settings.go | 467 -- .../github.com/godbus/dbus/v5/mock_BusObject.go | 649 -- .../mocks/internal/plugins/mock_GitClient.go | 181 - .../core/internal/mocks/net/mock_Conn.go | 427 -- .../core/internal/mocks/network/mock_Backend.go | 1975 ----- .../core/internal/mocks/utils/mock_AppChecker.go | 242 - .../internal/mocks/version/mock_VersionFetcher.go | 144 - .../internal/mocks/wlclient/mock_WaylandDisplay.go | 229 - .../mocks/wlcontext/mock_WaylandContext.go | 226 - .../core/internal/notify/notify.go | 185 - .../DankMaterialShell/core/internal/pam/pam.go | 892 --- .../core/internal/pam/pam_test.go | 671 -- .../core/internal/plugins/manager.go | 588 -- .../core/internal/plugins/manager_test.go | 247 - .../core/internal/plugins/registry.go | 258 - .../core/internal/plugins/registry_test.go | 326 - .../core/internal/plugins/search.go | 91 - .../core/internal/proto/dwl_ipc/dwl_ipc.go | 791 -- .../proto/ext_data_control/data_control.go | 695 -- .../core/internal/proto/ext_workspace/workspace.go | 1040 --- .../keyboard_shortcuts_inhibit.go | 284 - .../proto/wlr_gamma_control/gamma_control.go | 268 - .../internal/proto/wlr_layer_shell/layer_shell.go | 792 -- .../wlr_output_management/output_management.go | 1515 ---- .../proto/wlr_output_power/output_power.go | 283 - .../internal/proto/wlr_screencopy/screencopy.go | 532 -- .../internal/proto/wp_viewporter/viewporter.go | 399 -- .../internal/proto/xml/dwl-ipc-unstable-v2.xml | 252 - .../internal/proto/xml/ext-data-control-v1.xml | 276 - .../core/internal/proto/xml/ext-workspace-v1.xml | 422 -- .../xml/keyboard-shortcuts-inhibit-unstable-v1.xml | 143 - .../proto/xml/wlr-gamma-control-unstable-v1.xml | 126 - .../proto/xml/wlr-layer-shell-unstable-v1.xml | 407 -- .../xml/wlr-output-management-unstable-v1.xml | 611 -- .../wlr-output-power-management-unstable-v1.xml | 128 - .../proto/xml/wlr-screencopy-unstable-v1.xml | 234 - .../core/internal/screenshot/compositor.go | 694 -- .../core/internal/screenshot/encode.go | 172 - .../core/internal/screenshot/notify.go | 180 - .../core/internal/screenshot/region.go | 859 --- .../core/internal/screenshot/region_input.go | 274 - .../core/internal/screenshot/region_render.go | 326 - .../core/internal/screenshot/screenshot.go | 1089 --- .../core/internal/screenshot/shm.go | 35 - .../core/internal/screenshot/state.go | 65 - .../core/internal/screenshot/theme.go | 125 - .../core/internal/screenshot/types.go | 81 - .../core/internal/server/apppicker/handlers.go | 51 - .../core/internal/server/apppicker/manager.go | 48 - .../core/internal/server/apppicker/models.go | 8 - .../core/internal/server/bluez/agent.go | 341 - .../core/internal/server/bluez/broker.go | 21 - .../core/internal/server/bluez/broker_test.go | 220 - .../core/internal/server/bluez/handlers.go | 237 - .../core/internal/server/bluez/handlers_test.go | 41 - .../core/internal/server/bluez/manager.go | 588 -- .../internal/server/bluez/subscription_broker.go | 86 - .../core/internal/server/bluez/types.go | 78 - .../core/internal/server/bluez/types_test.go | 210 - .../core/internal/server/brightness/ddc.go | 510 -- .../core/internal/server/brightness/ddc_filter.go | 135 - .../internal/server/brightness/ddc_filter_test.go | 122 - .../core/internal/server/brightness/ddc_test.go | 135 - .../core/internal/server/brightness/handlers.go | 124 - .../core/internal/server/brightness/logind.go | 66 - .../core/internal/server/brightness/logind_test.go | 95 - .../core/internal/server/brightness/manager.go | 389 - .../internal/server/brightness/manager_test.go | 11 - .../core/internal/server/brightness/sysfs.go | 267 - .../server/brightness/sysfs_logind_test.go | 278 - .../core/internal/server/brightness/sysfs_test.go | 180 - .../core/internal/server/brightness/types.go | 184 - .../core/internal/server/brightness/udev.go | 237 - .../core/internal/server/brightness/udev_test.go | 260 - .../core/internal/server/browser/handlers.go | 22 - .../core/internal/server/browser/manager.go | 49 - .../core/internal/server/browser/models.go | 5 - .../core/internal/server/clipboard/handlers.go | 325 - .../core/internal/server/clipboard/manager.go | 1911 ----- .../core/internal/server/clipboard/manager_test.go | 531 -- .../core/internal/server/clipboard/types.go | 196 - .../core/internal/server/cups/actions.go | 479 -- .../internal/server/cups/actions_pkhelper_test.go | 235 - .../core/internal/server/cups/actions_test.go | 450 -- .../core/internal/server/cups/handlers.go | 487 -- .../core/internal/server/cups/handlers_test.go | 711 -- .../core/internal/server/cups/manager.go | 390 - .../core/internal/server/cups/manager_test.go | 363 - .../core/internal/server/cups/pkhelper.go | 184 - .../core/internal/server/cups/pkhelper_test.go | 95 - .../core/internal/server/cups/subscription.go | 245 - .../core/internal/server/cups/subscription_dbus.go | 295 - .../core/internal/server/cups/test_connection.go | 176 - .../internal/server/cups/test_connection_test.go | 397 -- .../core/internal/server/cups/testing.go | 13 - .../core/internal/server/cups/types.go | 132 - .../core/internal/server/dbus/handlers.go | 237 - .../core/internal/server/dbus/manager.go | 392 - .../core/internal/server/dbus/types.go | 52 - .../core/internal/server/dwl/handlers.go | 138 - .../core/internal/server/dwl/manager.go | 522 -- .../core/internal/server/dwl/manager_test.go | 366 - .../core/internal/server/dwl/types.go | 176 - .../core/internal/server/evdev/handlers.go | 20 - .../core/internal/server/evdev/handlers_test.go | 130 - .../core/internal/server/evdev/manager.go | 413 -- .../core/internal/server/evdev/manager_test.go | 344 - .../core/internal/server/evdev/models.go | 6 - .../core/internal/server/extworkspace/handlers.go | 134 - .../core/internal/server/extworkspace/manager.go | 598 -- .../internal/server/extworkspace/manager_test.go | 392 - .../core/internal/server/extworkspace/types.go | 169 - .../core/internal/server/freedesktop/actions.go | 112 - .../internal/server/freedesktop/actions_test.go | 145 - .../core/internal/server/freedesktop/constants.go | 23 - .../core/internal/server/freedesktop/handlers.go | 154 - .../internal/server/freedesktop/handlers_test.go | 581 -- .../core/internal/server/freedesktop/manager.go | 206 - .../internal/server/freedesktop/manager_test.go | 143 - .../internal/server/freedesktop/screensaver.go | 349 - .../server/freedesktop/screensaver_test.go | 102 - .../core/internal/server/freedesktop/types.go | 66 - .../core/internal/server/freedesktop/types_test.go | 70 - .../core/internal/server/location/handlers.go | 61 - .../core/internal/server/location/manager.go | 175 - .../core/internal/server/location/types.go | 28 - .../core/internal/server/loginctl/actions.go | 88 - .../core/internal/server/loginctl/constants.go | 10 - .../core/internal/server/loginctl/handlers.go | 156 - .../core/internal/server/loginctl/handlers_test.go | 500 -- .../core/internal/server/loginctl/manager.go | 723 -- .../core/internal/server/loginctl/manager_test.go | 296 - .../core/internal/server/loginctl/monitor.go | 155 - .../core/internal/server/loginctl/monitor_test.go | 298 - .../core/internal/server/loginctl/types.go | 76 - .../core/internal/server/loginctl/types_test.go | 50 - .../core/internal/server/matugen_handler.go | 79 - .../core/internal/server/models/types.go | 47 - .../core/internal/server/models/types_test.go | 52 - .../core/internal/server/network/API.md | 552 -- .../core/internal/server/network/agent_iwd.go | 306 - .../server/network/agent_networkmanager.go | 897 --- .../server/network/agent_networkmanager_test.go | 355 - .../core/internal/server/network/backend.go | 80 - .../server/network/backend_hybrid_iwd_networkd.go | 245 - .../internal/server/network/backend_hybrid_test.go | 135 - .../core/internal/server/network/backend_iwd.go | 238 - .../internal/server/network/backend_iwd_signals.go | 355 - .../internal/server/network/backend_iwd_test.go | 212 - .../server/network/backend_iwd_unimplemented.go | 133 - .../internal/server/network/backend_iwd_wifi.go | 662 -- .../internal/server/network/backend_networkd.go | 301 - .../server/network/backend_networkd_ethernet.go | 120 - .../server/network/backend_networkd_signals.go | 68 - .../server/network/backend_networkd_test.go | 147 - .../network/backend_networkd_unimplemented.go | 99 - .../server/network/backend_networkmanager.go | 405 -- .../network/backend_networkmanager_ethernet.go | 402 -- .../backend_networkmanager_ethernet_test.go | 134 - .../network/backend_networkmanager_gp_saml.go | 203 - .../network/backend_networkmanager_gp_saml_test.go | 169 - .../network/backend_networkmanager_signals.go | 530 -- .../network/backend_networkmanager_signals_test.go | 255 - .../server/network/backend_networkmanager_state.go | 277 - .../network/backend_networkmanager_state_test.go | 82 - .../server/network/backend_networkmanager_test.go | 154 - .../server/network/backend_networkmanager_vpn.go | 1394 ---- .../network/backend_networkmanager_vpn_test.go | 138 - .../server/network/backend_networkmanager_wifi.go | 1173 --- .../network/backend_networkmanager_wifi_test.go | 202 - .../core/internal/server/network/broker.go | 22 - .../internal/server/network/connection_test.go | 109 - .../core/internal/server/network/detect.go | 89 - .../core/internal/server/network/detect_test.go | 41 - .../core/internal/server/network/handlers.go | 630 -- .../core/internal/server/network/handlers_test.go | 269 - .../core/internal/server/network/helpers.go | 53 - .../core/internal/server/network/manager.go | 626 -- .../core/internal/server/network/manager_test.go | 191 - .../core/internal/server/network/monitor.go | 1 - .../core/internal/server/network/priority.go | 187 - .../core/internal/server/network/priority_test.go | 50 - .../internal/server/network/subscription_broker.go | 125 - .../core/internal/server/network/testing.go | 14 - .../core/internal/server/network/types.go | 267 - .../core/internal/server/network/types_test.go | 178 - .../core/internal/server/network/wifi_qrcode.go | 59 - .../core/internal/server/network/wifi_test.go | 148 - .../core/internal/server/network/wired_test.go | 51 - .../core/internal/server/params/params.go | 113 - .../core/internal/server/params/params_test.go | 154 - .../core/internal/server/plugins/handlers.go | 27 - .../core/internal/server/plugins/handlers_test.go | 196 - .../core/internal/server/plugins/install.go | 69 - .../core/internal/server/plugins/list.go | 53 - .../core/internal/server/plugins/list_installed.go | 77 - .../core/internal/server/plugins/search.go | 74 - .../core/internal/server/plugins/types.go | 25 - .../core/internal/server/plugins/uninstall.go | 66 - .../core/internal/server/plugins/update.go | 64 - .../core/internal/server/plugins/utils.go | 17 - .../core/internal/server/router.go | 250 - .../core/internal/server/server.go | 1758 ----- .../core/internal/server/server_test.go | 180 - .../core/internal/server/thememode/handlers.go | 154 - .../core/internal/server/thememode/manager.go | 462 -- .../core/internal/server/thememode/types.go | 23 - .../core/internal/server/themes/handlers.go | 27 - .../core/internal/server/themes/install.go | 52 - .../core/internal/server/themes/list.go | 54 - .../core/internal/server/themes/list_installed.go | 149 - .../core/internal/server/themes/search.go | 53 - .../core/internal/server/themes/types.go | 47 - .../core/internal/server/themes/uninstall.go | 63 - .../core/internal/server/themes/update.go | 57 - .../core/internal/server/trayrecovery/manager.go | 93 - .../core/internal/server/trayrecovery/recovery.go | 262 - .../core/internal/server/wayland/gamma.go | 167 - .../core/internal/server/wayland/gamma_test.go | 120 - .../core/internal/server/wayland/geolocation.go | 50 - .../core/internal/server/wayland/handlers.go | 182 - .../core/internal/server/wayland/manager.go | 1256 ---- .../core/internal/server/wayland/manager_test.go | 414 -- .../core/internal/server/wayland/suncalc.go | 122 - .../core/internal/server/wayland/suncalc_test.go | 387 - .../core/internal/server/wayland/types.go | 216 - .../core/internal/server/wayland/types_test.go | 330 - .../core/internal/server/wlcontext/context.go | 200 - .../core/internal/server/wlcontext/context_test.go | 128 - .../core/internal/server/wlroutput/handlers.go | 282 - .../core/internal/server/wlroutput/manager.go | 492 -- .../core/internal/server/wlroutput/manager_test.go | 414 -- .../core/internal/server/wlroutput/types.go | 192 - .../core/internal/themes/manager.go | 258 - .../core/internal/themes/registry.go | 309 - .../core/internal/themes/search.go | 40 - .../DankMaterialShell/core/internal/tui/app.go | 240 - .../DankMaterialShell/core/internal/tui/banner.go | 21 - .../core/internal/tui/messages.go | 39 - .../DankMaterialShell/core/internal/tui/states.go | 22 - .../DankMaterialShell/core/internal/tui/styles.go | 124 - .../core/internal/tui/views_config.go | 382 - .../core/internal/tui/views_dependencies.go | 304 - .../core/internal/tui/views_gentoo_gcc.go | 103 - .../core/internal/tui/views_gentoo_use_flags.go | 92 - .../core/internal/tui/views_install.go | 405 -- .../core/internal/tui/views_password.go | 338 - .../core/internal/tui/views_selection.go | 224 - .../core/internal/tui/views_welcome.go | 216 - .../DankMaterialShell/core/internal/utils/dbus.go | 37 - .../DankMaterialShell/core/internal/utils/exec.go | 44 - .../core/internal/utils/flatpak.go | 83 - .../core/internal/utils/flatpak_test.go | 271 - .../DankMaterialShell/core/internal/utils/group.go | 37 - .../core/internal/utils/group_test.go | 142 - .../core/internal/utils/gsettings.go | 31 - .../DankMaterialShell/core/internal/utils/math.go | 13 - .../DankMaterialShell/core/internal/utils/paths.go | 70 - .../core/internal/utils/paths_test.go | 43 - .../core/internal/utils/slices.go | 56 - .../core/internal/utils/slices_test.go | 72 - .../core/internal/version/version.go | 268 - .../core/internal/version/version_test.go | 362 - .../core/internal/wayland/client/helpers.go | 26 - .../core/internal/wayland/shm/buffer.go | 298 - .../windowrules/providers/hyprland_parser.go | 658 -- .../windowrules/providers/hyprland_parser_test.go | 280 - .../internal/windowrules/providers/niri_parser.go | 875 --- .../windowrules/providers/niri_parser_test.go | 335 - .../windowrules/providers/providers_test.go | 22 - .../core/internal/windowrules/types.go | 103 - .../DankMaterialShell/core/pkg/dbusutil/variant.go | 95 - .../core/pkg/dbusutil/variant_test.go | 155 - .../DankMaterialShell/core/pkg/go-wayland/AUTHORS | 3 - .../DankMaterialShell/core/pkg/go-wayland/LICENSE | 24 - .../core/pkg/go-wayland/README.md | 25 - .../DankMaterialShell/core/pkg/go-wayland/generate | 4 - .../core/pkg/go-wayland/generatep | 9 - .../core/pkg/go-wayland/wayland/client/client.go | 7542 -------------------- .../core/pkg/go-wayland/wayland/client/common.go | 55 - .../core/pkg/go-wayland/wayland/client/context.go | 143 - .../pkg/go-wayland/wayland/client/context_test.go | 111 - .../core/pkg/go-wayland/wayland/client/display.go | 37 - .../core/pkg/go-wayland/wayland/client/doc.go | 6 - .../core/pkg/go-wayland/wayland/client/event.go | 120 - .../core/pkg/go-wayland/wayland/client/request.go | 44 - .../core/pkg/go-wayland/wayland/client/util.go | 24 - .../wayland/stable/xdg-shell/xdg_shell.go | 13 - .../DankMaterialShell/core/pkg/ipp/CREDITS.MD | 5 - .../DankMaterialShell/core/pkg/ipp/LICENSE | 201 - .../DankMaterialShell/core/pkg/ipp/adapter-http.go | 132 - .../DankMaterialShell/core/pkg/ipp/adapter.go | 9 - .../DankMaterialShell/core/pkg/ipp/attribute.go | 528 -- .../DankMaterialShell/core/pkg/ipp/constants.go | 450 -- .../DankMaterialShell/core/pkg/ipp/cups-client.go | 313 - .../DankMaterialShell/core/pkg/ipp/error.go | 31 - .../DankMaterialShell/core/pkg/ipp/ipp-client.go | 329 - .../DankMaterialShell/core/pkg/ipp/request.go | 299 - .../DankMaterialShell/core/pkg/ipp/response.go | 383 - .../DankMaterialShell/core/pkg/ipp/utils.go | 28 - .../DankMaterialShell/core/pkg/syncmap/LICENSE | 28 - .../DankMaterialShell/core/pkg/syncmap/syncmap.go | 537 -- 449 files changed, 136761 deletions(-) delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/Makefile delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/README.md delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/assets/dank.svg delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/assets/danklogo.svg delete mode 100755 raveos-hyprland-theme/theme-data/DankMaterialShell/core/build_dankinstall.sh delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dankinstall/main.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/assets/cli-policy.default.json delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_auth.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_blur.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_brightness.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_chroma.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_clipboard.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_colorpicker.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_common.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_config.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_dank16.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_doctor.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_download.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_dpms.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_features.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_greeter.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_greeter_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_keybinds.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_matugen.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_notify.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_open.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_randr.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_root.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_screenshot.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_setup.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_windowrules.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/dpms_client.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/immutable_policy.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/main.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/main_distro.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/randr_client.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/server_client.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/shell.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/ui.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/utils.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/go.mod delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/go.sum delete mode 100755 raveos-hyprland-theme/theme-data/DankMaterialShell/core/install.sh delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/blur/probe.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/clipboard.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/store.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/watch.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/color.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/picker.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/shm.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/state.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/state_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/dms.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty-theme.toml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty.toml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty-colors.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-binds.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-colors.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-layout.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hyprland.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-tabs.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-theme.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty.conf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-alttab.kdl delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-binds.kdl delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-colors.kdl delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-greeter.kdl delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-layout.kdl delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri.kdl delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/testpage.pdf delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/hyprland.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/niri.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/terminals.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/testpage.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/terminals.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/deps/detector.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/arch.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/base.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/base_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/debian.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/factory.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/fedora.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/gentoo.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/interface.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/manual_packages.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/manual_packages_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/minimal_install.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/osinfo.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/ubuntu.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/errdefs/errdefs.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client_geoclue.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client_ip.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/assets/apparmor/usr.bin.dms-greeter delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/installer.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/installer_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/headless/runner.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/headless/runner_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/discovery.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/discovery_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_parser.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_parser_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/jsonfile.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/jsonfile_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_parser.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_parser_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/miracle.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/miracle_parser.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_parser.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_parser_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_parser.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_parser_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/registry.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/registry_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/log/filelogger.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/log/log.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/matugen.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/matugen_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/queue.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/brightness/mock_DBusConn.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/cups/mock_CUPSClientInterface.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/cups_pkhelper/mock_PkHelper.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/evdev/mock_EvdevDevice.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/geolocation/mock_Client.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_AccessPoint.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_ActiveConnection.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Connection.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Device.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_DeviceWireless.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_IP4Config.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_NetworkManager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Settings.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5/mock_BusObject.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/internal/plugins/mock_GitClient.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/net/mock_Conn.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/network/mock_Backend.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/utils/mock_AppChecker.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/version/mock_VersionFetcher.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/wlclient/mock_WaylandDisplay.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/wlcontext/mock_WaylandContext.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/notify/notify.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/pam/pam.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/pam/pam_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/registry.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/registry_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/search.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/dwl_ipc/dwl_ipc.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/ext_data_control/data_control.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/ext_workspace/workspace.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/keyboard_shortcuts_inhibit/keyboard_shortcuts_inhibit.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_gamma_control/gamma_control.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_layer_shell/layer_shell.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_output_management/output_management.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_output_power/output_power.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_screencopy/screencopy.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wp_viewporter/viewporter.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/dwl-ipc-unstable-v2.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/ext-data-control-v1.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/ext-workspace-v1.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-gamma-control-unstable-v1.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-layer-shell-unstable-v1.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-output-management-unstable-v1.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-output-power-management-unstable-v1.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-screencopy-unstable-v1.xml delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/compositor.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/encode.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/notify.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_input.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_render.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/shm.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/state.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/theme.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/models.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/agent.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/subscription_broker.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_filter.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_filter_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/logind.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/logind_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs_logind_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/udev.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/udev_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/models.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions_pkhelper_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/handlers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/pkhelper.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/pkhelper_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/subscription.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/subscription_dbus.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/test_connection.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/test_connection_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/testing.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/models.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/constants.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/actions.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/constants.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/matugen_handler.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/models/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/models/types_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/API.md delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_iwd.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_networkmanager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_networkmanager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_hybrid_iwd_networkd.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_hybrid_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_signals.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_unimplemented.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_wifi.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_ethernet.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_signals.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_unimplemented.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_ethernet.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_ethernet_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_gp_saml.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_gp_saml_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_signals.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_signals_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_state.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_state_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_vpn.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_vpn_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_wifi.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_wifi_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/broker.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/connection_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/detect.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/detect_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/handlers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/helpers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/monitor.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/priority.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/priority_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/subscription_broker.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/testing.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/types_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wifi_qrcode.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wifi_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wired_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/params/params.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/params/params_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/handlers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/install.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/list.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/list_installed.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/search.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/uninstall.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/update.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/utils.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/router.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/install.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/list.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/list_installed.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/search.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/uninstall.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/update.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/recovery.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/gamma.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/gamma_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/geolocation.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/suncalc.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/suncalc_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/types_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/registry.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/search.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/app.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/banner.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/messages.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/states.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/styles.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_config.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_dependencies.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_gcc.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_use_flags.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_install.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_password.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_selection.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_welcome.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/dbus.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/exec.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/gsettings.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/math.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/version/version.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/version/version_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/wayland/client/helpers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/wayland/shm/buffer.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/providers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md delete mode 100755 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate delete mode 100755 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core') diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/Makefile b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/Makefile deleted file mode 100644 index cad9b95..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/Makefile +++ /dev/null @@ -1,163 +0,0 @@ -BINARY_NAME=dms -BINARY_NAME_INSTALL=dankinstall -SOURCE_DIR=cmd/dms -SOURCE_DIR_INSTALL=cmd/dankinstall -BUILD_DIR=bin -PREFIX ?= /usr/local -INSTALL_DIR=$(PREFIX)/bin - -GO=go -GOFLAGS=-ldflags="-s -w" - -# Version and build info -BASE_VERSION=$(shell git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.0.0") -COMMIT_COUNT=$(shell git rev-list --count HEAD 2>/dev/null || echo "0") -COMMIT_HASH=$(shell git rev-parse --short=8 HEAD 2>/dev/null || echo "unknown") -VERSION?=$(BASE_VERSION)+git$(COMMIT_COUNT).$(COMMIT_HASH) -BUILD_TIME?=$(shell date -u '+%Y-%m-%d_%H:%M:%S') -COMMIT?=$(COMMIT_HASH) - -BUILD_LDFLAGS=-ldflags='-s -w -X main.Version=$(VERSION) -X main.buildTime=$(BUILD_TIME) -X main.commit=$(COMMIT)' - -# Architecture to build for dist target (amd64, arm64, or all) -ARCH ?= all - -.PHONY: all build dankinstall dist clean install install-all install-dankinstall uninstall uninstall-all uninstall-dankinstall install-config uninstall-config test fmt vet deps print-version help - -# Default target -all: build - -# Build the main binary (dms) -build: - @echo "Building $(BINARY_NAME)..." - @mkdir -p $(BUILD_DIR) - CGO_ENABLED=0 $(GO) build $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./$(SOURCE_DIR) - @echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)" - -dankinstall: - @echo "Building $(BINARY_NAME_INSTALL)..." - @mkdir -p $(BUILD_DIR) - CGO_ENABLED=0 $(GO) build $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME_INSTALL) ./$(SOURCE_DIR_INSTALL) - @echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME_INSTALL)" - -# Build distro binaries for amd64 and arm64 (Linux only, no update/greeter support) -dist: -ifeq ($(ARCH),all) - @echo "Building $(BINARY_NAME) for distribution (amd64 and arm64)..." - @mkdir -p $(BUILD_DIR) - @echo "Building for linux/amd64..." - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./$(SOURCE_DIR) - @echo "Building for linux/arm64..." - CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./$(SOURCE_DIR) - @echo "Distribution builds complete:" - @echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64" - @echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64" -else - @echo "Building $(BINARY_NAME) for distribution ($(ARCH))..." - @mkdir -p $(BUILD_DIR) - @echo "Building for linux/$(ARCH)..." - CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH) ./$(SOURCE_DIR) - @echo "Distribution build complete:" - @echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH)" -endif - -build-all: build dankinstall - -install: - @echo "Installing $(BINARY_NAME) to $(INSTALL_DIR)..." - @install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_DIR)/$(BINARY_NAME) - @echo "Installation complete" - -install-all: - @echo "Installing $(BINARY_NAME) to $(INSTALL_DIR)..." - @install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_DIR)/$(BINARY_NAME) - @echo "Installing $(BINARY_NAME_INSTALL) to $(INSTALL_DIR)..." - @install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME_INSTALL) $(INSTALL_DIR)/$(BINARY_NAME_INSTALL) - @echo "Installation complete" - -install-dankinstall: - @echo "Installing $(BINARY_NAME_INSTALL) to $(INSTALL_DIR)..." - @install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME_INSTALL) $(INSTALL_DIR)/$(BINARY_NAME_INSTALL) - @echo "Installation complete" - -uninstall: - @echo "Uninstalling $(BINARY_NAME) from $(INSTALL_DIR)..." - @rm -f $(INSTALL_DIR)/$(BINARY_NAME) - @echo "Uninstall complete" - -uninstall-all: - @echo "Uninstalling $(BINARY_NAME) from $(INSTALL_DIR)..." - @rm -f $(INSTALL_DIR)/$(BINARY_NAME) - @echo "Uninstalling $(BINARY_NAME_INSTALL) from $(INSTALL_DIR)..." - @rm -f $(INSTALL_DIR)/$(BINARY_NAME_INSTALL) - @echo "Uninstall complete" - -uninstall-dankinstall: - @echo "Uninstalling $(BINARY_NAME_INSTALL) from $(INSTALL_DIR)..." - @rm -f $(INSTALL_DIR)/$(BINARY_NAME_INSTALL) - @echo "Uninstall complete" - -clean: - @echo "Cleaning build artifacts..." - @rm -rf $(BUILD_DIR) - @echo "Clean complete" - -test: - @echo "Running tests..." - $(GO) test -v ./... - -fmt: - @echo "Formatting Go code..." - $(GO) fmt ./... - -vet: - @echo "Running go vet..." - $(GO) vet ./... - -deps: - @echo "Updating dependencies..." - $(GO) mod tidy - $(GO) mod download - -dev: - @echo "Building $(BINARY_NAME) for development..." - @mkdir -p $(BUILD_DIR) - $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) ./$(SOURCE_DIR) - @echo "Development build complete: $(BUILD_DIR)/$(BINARY_NAME)" - -check-go: - @echo "Checking Go version..." - @go version | grep -E "go1\.(2[2-9]|[3-9][0-9])" > /dev/null || (echo "ERROR: Go 1.22 or higher required" && exit 1) - @echo "Go version OK" - -version: check-go - @echo "Version: $(VERSION)" - @echo "Build Time: $(BUILD_TIME)" - @echo "Commit: $(COMMIT)" - -print-version: - @echo "$(VERSION)" - -help: - @echo "Available targets:" - @echo " all - Build the main binary (dms) (default)" - @echo " build - Build the main binary (dms)" - @echo " dankinstall - Build dankinstall binary" - @echo " dist - Build dms for linux amd64/arm64 (no update/greeter)" - @echo " Use ARCH=amd64 or ARCH=arm64 to build only one" - @echo " build-all - Build both binaries" - @echo " install - Install dms to $(INSTALL_DIR)" - @echo " install-all - Install both dms and dankinstall to $(INSTALL_DIR)" - @echo " install-dankinstall - Install only dankinstall to $(INSTALL_DIR)" - @echo " uninstall - Remove dms from $(INSTALL_DIR)" - @echo " uninstall-all - Remove both binaries from $(INSTALL_DIR)" - @echo " uninstall-dankinstall - Remove only dankinstall from $(INSTALL_DIR)" - @echo " clean - Clean build artifacts" - @echo " test - Run tests" - @echo " fmt - Format Go code" - @echo " vet - Run go vet" - @echo " deps - Update dependencies" - @echo " dev - Build with debug info" - @echo " check-go - Check Go version compatibility" - @echo " version - Show version information" - @echo " help - Show this help message" diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/README.md b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/README.md deleted file mode 100644 index fed5bfa..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/README.md +++ /dev/null @@ -1,217 +0,0 @@ -# DMS Backend & CLI - -Go-based backend for DankMaterialShell providing system integration, IPC, and installation tools. - -**See [root README](../README.md) for project overview and installation.** - -## Components - -**dms CLI** -Command-line interface and daemon for shell management and system control. - -**dankinstall** -Distribution-aware installer for deploying DMS and compositor configurations on Arch, Fedora, Debian, Ubuntu, openSUSE, and Gentoo. Supports both an interactive TUI and a headless (unattended) mode via CLI flags. - -## System Integration - -### Wayland Protocols (Client) - -All Wayland protocols are consumed as a client - connecting to the compositor. - -| Protocol | Purpose | -| ----------------------------------------- | ----------------------------------------------------------- | -| `wlr-gamma-control-unstable-v1` | Night mode color temperature control | -| `wlr-screencopy-unstable-v1` | Screen capture for color picker/screenshot | -| `wlr-layer-shell-unstable-v1` | Overlay surfaces for color picker UI/screenshot | -| `wlr-output-management-unstable-v1` | Display configuration | -| `wlr-output-power-management-unstable-v1` | DPMS on/off CLI | -| `wp-viewporter` | Fractional scaling support (color picker/screenshot UIs) | -| `keyboard-shortcuts-inhibit-unstable-v1` | Inhibit compositor shortcuts during color picker/screenshot | -| `ext-data-control-v1` | Clipboard history and persistence | -| `ext-workspace-v1` | Workspace integration | -| `dwl-ipc-unstable-v2` | dwl/MangoWC IPC for tags, outputs, etc. | - -### DBus Interfaces - -**Client (consuming external services):** - -| Interface | Purpose | -| -------------------------------- | --------------------------------------------- | -| `org.bluez` | Bluetooth management with pairing agent | -| `org.freedesktop.NetworkManager` | Network management | -| `net.connman.iwd` | iwd Wi-Fi backend | -| `org.freedesktop.network1` | systemd-networkd integration | -| `org.freedesktop.login1` | Session control, sleep inhibitors, brightness | -| `org.freedesktop.Accounts` | User account information | -| `org.freedesktop.portal.Desktop` | Desktop appearance settings (color scheme) | -| CUPS via IPP + D-Bus | Printer management with job notifications | - -**Server (implementing interfaces):** - -| Interface | Purpose | -| ----------------------------- | -------------------------------------- | -| `org.freedesktop.ScreenSaver` | Screensaver inhibit for video playback | - -Custom IPC via unix socket (JSON API) for shell communication. - -### Hardware Control - -| Subsystem | Method | Purpose | -| --------- | ------------------- | ---------------------------------- | -| DDC/CI | I2C direct | External monitor brightness | -| Backlight | logind or sysfs | Internal display brightness | -| evdev | `/dev/input/event*` | Keyboard state (caps lock LED) | -| udev | netlink monitor | Backlight device updates (for OSD) | - -### Plugin System - -- Plugin registry integration -- Plugin lifecycle management -- Settings persistence - -## CLI Commands - -- `dms run [-d]` - Start shell (optionally as daemon) -- `dms restart` / `dms kill` - Manage running processes -- `dms ipc ` - Send IPC commands (toggle launcher, notifications, etc.) -- `dms plugins [install|browse|search]` - Plugin management -- `dms brightness [list|set]` - Control display/monitor brightness -- `dms color pick` - Native color picker (see below) -- `dms update` - Update DMS and dependencies (disabled in distro packages) -- `dms greeter install` - Install greetd greeter (disabled in distro packages) - -### Color Picker - -Native Wayland color picker with magnifier, no external dependencies. Supports HiDPI and fractional scaling. - -```bash -dms color pick # Pick color, output hex -dms color pick --rgb # Output as RGB (255 128 64) -dms color pick --hsv # Output as HSV (24 75% 100%) -dms color pick --json # Output all formats as JSON -dms color pick -a # Auto-copy to clipboard -``` - -The on-screen preview displays the selected format. JSON output includes hex, RGB, HSL, HSV, and CMYK values. - -## Building - -Requires Go 1.25+ - -**Development build:** - -```bash -make # Build dms CLI -make dankinstall # Build installer -make test # Run tests -``` - -**Distribution build:** - -```bash -make dist # Build without update/greeter features -``` - -Produces `bin/dms-linux-amd64` and `bin/dms-linux-arm64` - -**Installation:** - -```bash -sudo make install # Install to /usr/local/bin/dms -``` - -## Development - -**Setup pre-commit hooks:** - -```bash -git config core.hooksPath .githooks -``` - -This runs gofmt, golangci-lint, tests, and builds before each commit when `core/` files are staged. - -**Regenerating Wayland Protocol Bindings:** - -```bash -go install github.com/rajveermalviya/go-wayland/cmd/go-wayland-scanner@latest -go-wayland-scanner -i internal/proto/xml/wlr-gamma-control-unstable-v1.xml \ - -pkg wlr_gamma_control -o internal/proto/wlr_gamma_control/gamma_control.go -``` - -**Module Structure:** - -- `cmd/` - Binary entrypoints (dms, dankinstall) -- `internal/distros/` - Distribution-specific installation logic -- `internal/proto/` - Wayland protocol bindings -- `pkg/` - Shared packages - -## Installation via dankinstall - -**Interactive (TUI):** - -```bash -curl -fsSL https://install.danklinux.com | sh -``` - -**Headless (unattended):** - -Headless mode requires cached sudo credentials. Run `sudo -v` first: - -```bash -sudo -v && curl -fsSL https://install.danklinux.com | sh -s -- -c niri -t ghostty -y -sudo -v && curl -fsSL https://install.danklinux.com | sh -s -- -c hyprland -t kitty --include-deps dms-greeter -y -``` - -| Flag | Short | Description | -|------|-------|-------------| -| `--compositor ` | `-c` | Compositor/WM to install (required for headless) | -| `--term ` | `-t` | Terminal emulator (required for headless) | -| `--include-deps ` | | Enable optional dependencies (e.g. `dms-greeter`) | -| `--exclude-deps ` | | Skip specific dependencies | -| `--replace-configs ` | | Replace specific configuration files (mutually exclusive with `--replace-configs-all`) | -| `--replace-configs-all` | | Replace all configuration files (mutually exclusive with `--replace-configs`) | -| `--yes` | `-y` | Required for headless mode — confirms installation without interactive prompts | - -Headless mode requires `--yes` to proceed; without it, the installer exits with an error. -Configuration files are not replaced by default unless `--replace-configs` or `--replace-configs-all` is specified. -`dms-greeter` is disabled by default; use `--include-deps dms-greeter` to enable it. - -When no flags are provided, `dankinstall` launches the interactive TUI. - -### Headless mode validation rules - -Headless mode activates when `--compositor` or `--term` is provided. - -- Both `--compositor` and `--term` are required; providing only one results in an error. -- Headless-only flags (`--include-deps`, `--exclude-deps`, `--replace-configs`, `--replace-configs-all`, `--yes`) are rejected in TUI mode. -- Positional arguments are not accepted. - -### Log file location - -`dankinstall` writes logs to `/tmp` by default. -Set the `DANKINSTALL_LOG_DIR` environment variable to override the log directory. - -## Supported Distributions - -Arch, Fedora, Debian, Ubuntu, openSUSE, Gentoo (and derivatives) - -**Arch Linux** -Uses `pacman` for system packages, builds AUR packages via `makepkg`, no AUR helper dependency. - -**Fedora** - -Uses COPR repositories (`avengemedia/danklinux`, `avengemedia/dms`). - -**Ubuntu** -Requires PPA support. Most packages built from source (slow first install). - -**Debian** -Debian 13+ (Trixie). niri only, no Hyprland support. Builds from source. - -**openSUSE** -Most packages available in standard repos. Minimal building required. - -**Gentoo** -Uses Portage with GURU overlay. Automatically configures USE flags. Variable success depending on system configuration. - -See installer output for distribution-specific details during installation. diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/assets/dank.svg b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/assets/dank.svg deleted file mode 100644 index 3b4b334..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/assets/dank.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/assets/danklogo.svg b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/assets/danklogo.svg deleted file mode 100644 index 1ee6d51..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/assets/danklogo.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/build_dankinstall.sh b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/build_dankinstall.sh deleted file mode 100755 index c430e73..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/build_dankinstall.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -# Colors for output -GREEN='\033[0;32m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Get latest version tag -VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") - -echo -e "${GREEN}Building dankinstall ${VERSION}${NC}" - -# Create bin directory if it doesn't exist -mkdir -p bin - -# Build for each architecture -for ARCH in amd64 arm64; do - echo -e "${BLUE}Building for ${ARCH}...${NC}" - - cd cmd/dankinstall - GOOS=linux CGO_ENABLED=0 GOARCH=${ARCH} \ - go build -trimpath -ldflags "-s -w -X main.Version=${VERSION}" \ - -o ../../bin/dankinstall-${ARCH} - cd ../.. - - # Compress - gzip -9 -k -f bin/dankinstall-${ARCH} - - # Generate checksum - (cd bin && sha256sum dankinstall-${ARCH}.gz > dankinstall-${ARCH}.gz.sha256) - - echo -e "${GREEN}✓ Built bin/dankinstall-${ARCH}.gz${NC}" -done - -echo -e "${GREEN}Done! Files ready in bin/:${NC}" -ls -lh bin/dankinstall-* diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dankinstall/main.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dankinstall/main.go deleted file mode 100644 index 042506e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dankinstall/main.go +++ /dev/null @@ -1,219 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/headless" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/tui" - tea "github.com/charmbracelet/bubbletea" - "github.com/spf13/cobra" -) - -var Version = "dev" - -// Flag variables bound via pflag -var ( - compositor string - term string - includeDeps []string - excludeDeps []string - replaceConfigs []string - replaceConfigsAll bool - yes bool -) - -var rootCmd = &cobra.Command{ - Use: "dankinstall", - Short: "Install DankMaterialShell and its dependencies", - Long: `dankinstall sets up DankMaterialShell with your chosen compositor and terminal. - -Without flags, it launches an interactive TUI. Providing either --compositor -or --term activates headless (unattended) mode, which requires both flags. - -Headless mode requires cached sudo credentials. Run 'sudo -v' beforehand, or -configure passwordless sudo for your user.`, - Args: cobra.NoArgs, - RunE: runDankinstall, - SilenceErrors: true, - SilenceUsage: true, -} - -func init() { - rootCmd.Flags().StringVarP(&compositor, "compositor", "c", "", "Compositor/WM to install: niri or hyprland (enables headless mode)") - rootCmd.Flags().StringVarP(&term, "term", "t", "", "Terminal emulator to install: ghostty, kitty, or alacritty (enables headless mode)") - rootCmd.Flags().StringSliceVar(&includeDeps, "include-deps", []string{}, "Optional deps to enable (e.g. dms-greeter)") - rootCmd.Flags().StringSliceVar(&excludeDeps, "exclude-deps", []string{}, "Deps to skip during installation") - rootCmd.Flags().StringSliceVar(&replaceConfigs, "replace-configs", []string{}, "Deploy only named configs (e.g. niri,ghostty)") - rootCmd.Flags().BoolVar(&replaceConfigsAll, "replace-configs-all", false, "Deploy and replace all configurations") - rootCmd.Flags().BoolVarP(&yes, "yes", "y", false, "Auto-confirm all prompts") -} - -func main() { - if os.Getuid() == 0 { - fmt.Fprintln(os.Stderr, "Error: dankinstall must not be run as root") - os.Exit(1) - } - - if err := rootCmd.Execute(); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } -} - -func runDankinstall(cmd *cobra.Command, args []string) error { - headlessMode := compositor != "" || term != "" - - if !headlessMode { - // Reject headless-only flags when running in TUI mode. - headlessOnly := []string{ - "include-deps", - "exclude-deps", - "replace-configs", - "replace-configs-all", - "yes", - } - var set []string - for _, name := range headlessOnly { - if cmd.Flags().Changed(name) { - set = append(set, "--"+name) - } - } - if len(set) > 0 { - return fmt.Errorf("flags %s are only valid in headless mode (requires both --compositor and --term)", strings.Join(set, ", ")) - } - } - - if headlessMode { - return runHeadless() - } - return runTUI() -} - -func runHeadless() error { - // Validate required flags - if compositor == "" { - return fmt.Errorf("--compositor is required for headless mode (niri or hyprland)") - } - if term == "" { - return fmt.Errorf("--term is required for headless mode (ghostty, kitty, or alacritty)") - } - - cfg := headless.Config{ - Compositor: compositor, - Terminal: term, - IncludeDeps: includeDeps, - ExcludeDeps: excludeDeps, - ReplaceConfigs: replaceConfigs, - ReplaceConfigsAll: replaceConfigsAll, - Yes: yes, - } - - runner := headless.NewRunner(cfg) - - // Set up file logging - fileLogger, err := log.NewFileLogger() - if err != nil { - fmt.Fprintf(os.Stderr, "Warning: Failed to create log file: %v\n", err) - } - - if fileLogger != nil { - fmt.Printf("Logging to: %s\n", fileLogger.GetLogPath()) - fileLogger.StartListening(runner.GetLogChan()) - defer func() { - if err := fileLogger.Close(); err != nil { - fmt.Fprintf(os.Stderr, "Warning: Failed to close log file: %v\n", err) - } - }() - } else { - // Drain the log channel to prevent blocking sends from deadlocking - // downstream components (distros, config deployer) that write to it. - // Use an explicit stop signal because this code does not own the - // runner log channel and cannot assume it will be closed. - defer drainLogChan(runner.GetLogChan())() - } - - if err := runner.Run(); err != nil { - if fileLogger != nil { - fmt.Fprintf(os.Stderr, "\nFull logs are available at: %s\n", fileLogger.GetLogPath()) - } - return err - } - - if fileLogger != nil { - fmt.Printf("\nFull logs are available at: %s\n", fileLogger.GetLogPath()) - } - return nil -} - -func runTUI() error { - fileLogger, err := log.NewFileLogger() - if err != nil { - fmt.Printf("Warning: Failed to create log file: %v\n", err) - fmt.Println("Continuing without file logging...") - } - - logFilePath := "" - if fileLogger != nil { - logFilePath = fileLogger.GetLogPath() - fmt.Printf("Logging to: %s\n", logFilePath) - defer func() { - if err := fileLogger.Close(); err != nil { - fmt.Printf("Warning: Failed to close log file: %v\n", err) - } - }() - } - - model := tui.NewModel(Version, logFilePath) - - if fileLogger != nil { - fileLogger.StartListening(model.GetLogChan()) - } else { - // Drain the log channel to prevent blocking sends from deadlocking - // downstream components (distros, config deployer) that write to it. - // Use an explicit stop signal because this code does not own the - // model log channel and cannot assume it will be closed. - defer drainLogChan(model.GetLogChan())() - } - - p := tea.NewProgram(model, tea.WithAltScreen()) - if _, err := p.Run(); err != nil { - if logFilePath != "" { - fmt.Fprintf(os.Stderr, "\nFull logs are available at: %s\n", logFilePath) - } - return fmt.Errorf("error running program: %w", err) - } - - if logFilePath != "" { - fmt.Printf("\nFull logs are available at: %s\n", logFilePath) - } - return nil -} - -// drainLogChan starts a goroutine that discards all messages from logCh, -// preventing blocking sends from deadlocking downstream components. It returns -// a cleanup function that signals the goroutine to stop and waits for it to -// exit. Callers should defer the returned function. -func drainLogChan(logCh <-chan string) func() { - drainStop := make(chan struct{}) - drainDone := make(chan struct{}) - go func() { - defer close(drainDone) - for { - select { - case <-drainStop: - return - case _, ok := <-logCh: - if !ok { - return - } - } - } - }() - return func() { - close(drainStop) - <-drainDone - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/assets/cli-policy.default.json b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/assets/cli-policy.default.json deleted file mode 100644 index de9b8ad..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/assets/cli-policy.default.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "policy_version": 1, - "blocked_commands": [ - "greeter install", - "greeter enable", - "greeter uninstall", - "setup" - ], - "message": "This command is disabled on immutable/image-based systems. Use your distro-native workflow for system-level changes." -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_auth.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_auth.go deleted file mode 100644 index 727700e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_auth.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - sharedpam "github.com/AvengeMedia/DankMaterialShell/core/internal/pam" - "github.com/spf13/cobra" -) - -var authCmd = &cobra.Command{ - Use: "auth", - Short: "Manage DMS authentication sync", - Long: "Manage shared PAM/authentication setup for DMS greeter and lock screen", -} - -var authSyncCmd = &cobra.Command{ - Use: "sync", - Short: "Sync DMS authentication configuration", - Long: "Apply shared PAM/authentication changes for the lock screen and greeter based on current DMS settings", - Run: func(cmd *cobra.Command, args []string) { - yes, _ := cmd.Flags().GetBool("yes") - term, _ := cmd.Flags().GetBool("terminal") - if term { - if err := syncAuthInTerminal(yes); err != nil { - log.Fatalf("Error launching auth sync in terminal: %v", err) - } - return - } - if err := syncAuth(yes); err != nil { - log.Fatalf("Error syncing authentication: %v", err) - } - }, -} - -func init() { - authSyncCmd.Flags().BoolP("yes", "y", false, "Non-interactive mode: skip prompts") - authSyncCmd.Flags().BoolP("terminal", "t", false, "Run auth sync in a new terminal (for entering sudo password)") -} - -func syncAuth(nonInteractive bool) error { - if !nonInteractive { - fmt.Println("=== DMS Authentication Sync ===") - fmt.Println() - } - - logFunc := func(msg string) { - fmt.Println(msg) - } - - if err := sharedpam.SyncAuthConfig(logFunc, "", sharedpam.SyncAuthOptions{}); err != nil { - return err - } - - if !nonInteractive { - fmt.Println("\n=== Authentication Sync Complete ===") - fmt.Println("\nAuthentication changes have been applied.") - } - - return nil -} - -func syncAuthInTerminal(nonInteractive bool) error { - syncFlags := make([]string, 0, 1) - if nonInteractive { - syncFlags = append(syncFlags, "--yes") - } - - shellSyncCmd := "dms auth sync" - if len(syncFlags) > 0 { - shellSyncCmd += " " + strings.Join(syncFlags, " ") - } - shellCmd := shellSyncCmd + `; echo; echo "Authentication sync finished. Closing in 3 seconds..."; sleep 3` - return runCommandInTerminal(shellCmd) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_blur.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_blur.go deleted file mode 100644 index d01eb93..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_blur.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/blur" - "github.com/spf13/cobra" -) - -var blurCmd = &cobra.Command{ - Use: "blur", - Short: "Background blur utilities", -} - -var blurCheckCmd = &cobra.Command{ - Use: "check", - Short: "Check if the compositor supports background blur (ext-background-effect-v1)", - Args: cobra.NoArgs, - Run: runBlurCheck, -} - -func init() { - blurCmd.AddCommand(blurCheckCmd) -} - -func runBlurCheck(cmd *cobra.Command, args []string) { - supported, err := blur.ProbeSupport() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - - switch supported { - case true: - fmt.Println("supported") - default: - fmt.Println("unsupported") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_brightness.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_brightness.go deleted file mode 100644 index 0f73493..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_brightness.go +++ /dev/null @@ -1,311 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/brightness" - "github.com/spf13/cobra" -) - -var brightnessCmd = &cobra.Command{ - Use: "brightness", - Short: "Control device brightness", - Long: "Control brightness for backlight and LED devices (use --ddc to include DDC/I2C monitors)", -} - -var brightnessListCmd = &cobra.Command{ - Use: "list", - Short: "List all brightness devices", - Long: "List all available brightness devices with their current values", - Run: runBrightnessList, -} - -var brightnessSetCmd = &cobra.Command{ - Use: "set ", - Short: "Set brightness for a device", - Long: "Set brightness percentage (0-100) for a specific device", - Args: cobra.ExactArgs(2), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - includeDDC, _ := cmd.Flags().GetBool("ddc") - return getBrightnessDevices(includeDDC), cobra.ShellCompDirectiveNoFileComp - }, - Run: runBrightnessSet, -} - -var brightnessGetCmd = &cobra.Command{ - Use: "get ", - Short: "Get brightness for a device", - Long: "Get current brightness percentage for a specific device", - Args: cobra.ExactArgs(1), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - includeDDC, _ := cmd.Flags().GetBool("ddc") - return getBrightnessDevices(includeDDC), cobra.ShellCompDirectiveNoFileComp - }, - Run: runBrightnessGet, -} - -func init() { - brightnessListCmd.Flags().Bool("ddc", false, "Include DDC/I2C monitors (slower)") - brightnessSetCmd.Flags().Bool("ddc", false, "Include DDC/I2C monitors (slower)") - brightnessSetCmd.Flags().Bool("exponential", false, "Use exponential brightness scaling") - brightnessSetCmd.Flags().Float64("exponent", 1.2, "Exponent for exponential scaling (default 1.2)") - brightnessGetCmd.Flags().Bool("ddc", false, "Include DDC/I2C monitors (slower)") - - brightnessCmd.SetHelpTemplate(`{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableSubCommands}} - -Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} - -Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} - {{rpad .Name .NamePadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - -Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} -`) - - brightnessListCmd.SetHelpTemplate(`{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}} -`) - - brightnessSetCmd.SetHelpTemplate(`{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}} -`) - - brightnessGetCmd.SetHelpTemplate(`{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}} -`) - - brightnessCmd.AddCommand(brightnessListCmd, brightnessSetCmd, brightnessGetCmd) -} - -func getAllBrightnessDevices(includeDDC bool) []brightness.Device { - allDevices := []brightness.Device{} - - sysfs, err := brightness.NewSysfsBackend() - if err != nil { - log.Debugf("Failed to initialize sysfs backend: %v", err) - } else { - devices, err := sysfs.GetDevices() - if err != nil { - log.Debugf("Failed to get sysfs devices: %v", err) - } else { - allDevices = append(allDevices, devices...) - } - } - - if includeDDC { - ddc, err := brightness.NewDDCBackend() - if err != nil { - fmt.Printf("Warning: Failed to initialize DDC backend: %v\n", err) - } else { - time.Sleep(100 * time.Millisecond) - devices, err := ddc.GetDevices() - if err != nil { - fmt.Printf("Warning: Failed to get DDC devices: %v\n", err) - } else { - allDevices = append(allDevices, devices...) - } - ddc.Close() - } - } - - return allDevices -} - -func runBrightnessList(cmd *cobra.Command, args []string) { - includeDDC, _ := cmd.Flags().GetBool("ddc") - allDevices := getAllBrightnessDevices(includeDDC) - - if len(allDevices) == 0 { - fmt.Println("No brightness devices found") - return - } - - maxIDLen := len("Device") - maxNameLen := len("Name") - for _, dev := range allDevices { - if len(dev.ID) > maxIDLen { - maxIDLen = len(dev.ID) - } - if len(dev.Name) > maxNameLen { - maxNameLen = len(dev.Name) - } - } - - idPad := maxIDLen + 2 - namePad := maxNameLen + 2 - - fmt.Printf("%-*s %-12s %-*s %s\n", idPad, "Device", "Class", namePad, "Name", "Brightness") - - sepLen := idPad + 2 + 12 + 2 + namePad + 2 + 15 - for range sepLen { - fmt.Print("─") - } - fmt.Println() - - for _, device := range allDevices { - fmt.Printf("%-*s %-12s %-*s %3d%%\n", - idPad, - device.ID, - device.Class, - namePad, - device.Name, - device.CurrentPercent, - ) - } -} - -func runBrightnessSet(cmd *cobra.Command, args []string) { - deviceID := args[0] - var percent int - if _, err := fmt.Sscanf(args[1], "%d", &percent); err != nil { - log.Fatalf("Invalid percent value: %s", args[1]) - } - - if percent < 0 || percent > 100 { - log.Fatalf("Percent must be between 0 and 100") - } - - includeDDC, _ := cmd.Flags().GetBool("ddc") - exponential, _ := cmd.Flags().GetBool("exponential") - exponent, _ := cmd.Flags().GetFloat64("exponent") - - parts := strings.SplitN(deviceID, ":", 2) - if len(parts) == 2 && (parts[0] == "backlight" || parts[0] == "leds") { - if ok := tryLogindBrightness(parts[0], parts[1], deviceID, percent, exponential, exponent); ok { - return - } - } - - sysfs, err := brightness.NewSysfsBackend() - if err == nil { - if err := sysfs.SetBrightnessWithExponent(deviceID, percent, exponential, exponent); err == nil { - fmt.Printf("Set %s to %d%%\n", deviceID, percent) - return - } - log.Debugf("sysfs.SetBrightness failed: %v", err) - } else { - log.Debugf("NewSysfsBackend failed: %v", err) - } - - // Try DDC if requested - if includeDDC { - ddc, err := brightness.NewDDCBackend() - if err == nil { - defer ddc.Close() - time.Sleep(100 * time.Millisecond) - if err := ddc.SetBrightnessWithExponent(deviceID, percent, exponential, exponent, nil); err == nil { - ddc.WaitPending() - fmt.Printf("Set %s to %d%%\n", deviceID, percent) - return - } - log.Debugf("ddc.SetBrightness failed: %v", err) - } else { - log.Debugf("NewDDCBackend failed: %v", err) - } - } - - log.Fatalf("Failed to set brightness for device: %s", deviceID) -} - -func tryLogindBrightness(subsystem, name, deviceID string, percent int, exponential bool, exponent float64) bool { - sysfs, err := brightness.NewSysfsBackend() - if err != nil { - log.Debugf("NewSysfsBackend failed: %v", err) - return false - } - - logind, err := brightness.NewLogindBackend() - if err != nil { - log.Debugf("NewLogindBackend failed: %v", err) - return false - } - defer logind.Close() - - dev, err := sysfs.GetDevice(deviceID) - if err != nil { - log.Debugf("sysfs.GetDeviceByID failed: %v", err) - return false - } - - value := sysfs.PercentToValueWithExponent(percent, dev, exponential, exponent) - if err := logind.SetBrightness(subsystem, name, uint32(value)); err != nil { - log.Debugf("logind.SetBrightness failed: %v", err) - return false - } - - log.Debugf("set %s to %d%% (%d) via logind", deviceID, percent, value) - fmt.Printf("Set %s to %d%%\n", deviceID, percent) - return true -} - -func getBrightnessDevices(includeDDC bool) []string { - allDevices := getAllBrightnessDevices(includeDDC) - - var deviceIDs []string - for _, device := range allDevices { - deviceIDs = append(deviceIDs, device.ID) - } - return deviceIDs -} - -func runBrightnessGet(cmd *cobra.Command, args []string) { - deviceID := args[0] - includeDDC, _ := cmd.Flags().GetBool("ddc") - allDevices := getAllBrightnessDevices(includeDDC) - - for _, device := range allDevices { - if device.ID == deviceID { - fmt.Printf("%s: %d%% (%d/%d)\n", - device.ID, - device.CurrentPercent, - device.Current, - device.Max, - ) - return - } - } - - log.Fatalf("Device not found: %s", deviceID) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_chroma.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_chroma.go deleted file mode 100644 index 4945f33..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_chroma.go +++ /dev/null @@ -1,300 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "io" - "os" - "strings" - "sync" - - "github.com/alecthomas/chroma/v2" - "github.com/alecthomas/chroma/v2/formatters/html" - "github.com/alecthomas/chroma/v2/lexers" - "github.com/alecthomas/chroma/v2/styles" - "github.com/spf13/cobra" - "github.com/yuin/goldmark" - highlighting "github.com/yuin/goldmark-highlighting/v2" - "github.com/yuin/goldmark/extension" - "github.com/yuin/goldmark/parser" - ghtml "github.com/yuin/goldmark/renderer/html" -) - -var ( - chromaLanguage string - chromaStyle string - chromaInline bool - chromaMarkdown bool - chromaLineNumbers bool - - // Caching layer for performance - lexerCache = make(map[string]chroma.Lexer) - styleCache = make(map[string]*chroma.Style) - formatterCache = make(map[string]*html.Formatter) - cacheMutex sync.RWMutex - maxFileSize = int64(5 * 1024 * 1024) // 5MB default -) - -var chromaCmd = &cobra.Command{ - Use: "chroma [file]", - Short: "Syntax highlight source code", - Long: `Generate syntax-highlighted HTML from source code. - -Reads from file or stdin, outputs HTML with syntax highlighting. -Language is auto-detected from filename or can be specified with --language. - -Examples: - dms chroma main.go - dms chroma --language python script.py - echo "def foo(): pass" | dms chroma -l python - cat code.rs | dms chroma -l rust --style dracula - dms chroma --markdown README.md - dms chroma --markdown --style github-dark notes.md - dms chroma list-languages - dms chroma list-styles`, - Args: cobra.MaximumNArgs(1), - Run: runChroma, -} - -var chromaListLanguagesCmd = &cobra.Command{ - Use: "list-languages", - Short: "List all supported languages", - Run: func(cmd *cobra.Command, args []string) { - for _, name := range lexers.Names(true) { - fmt.Println(name) - } - }, -} - -var chromaListStylesCmd = &cobra.Command{ - Use: "list-styles", - Short: "List all available color styles", - Run: func(cmd *cobra.Command, args []string) { - for _, name := range styles.Names() { - fmt.Println(name) - } - }, -} - -func init() { - chromaCmd.Flags().StringVarP(&chromaLanguage, "language", "l", "", "Language for highlighting (auto-detect if not specified)") - chromaCmd.Flags().StringVarP(&chromaStyle, "style", "s", "monokai", "Color style (monokai, dracula, github, etc.)") - chromaCmd.Flags().BoolVar(&chromaInline, "inline", false, "Output inline styles instead of CSS classes") - chromaCmd.Flags().BoolVar(&chromaLineNumbers, "line-numbers", false, "Show line numbers in output") - chromaCmd.Flags().BoolVarP(&chromaMarkdown, "markdown", "m", false, "Render markdown with syntax-highlighted code blocks") - chromaCmd.Flags().Int64Var(&maxFileSize, "max-size", 5*1024*1024, "Maximum file size to process without warning (bytes)") - - chromaCmd.AddCommand(chromaListLanguagesCmd) - chromaCmd.AddCommand(chromaListStylesCmd) -} - -func getCachedLexer(key string, fallbackFunc func() chroma.Lexer) chroma.Lexer { - cacheMutex.RLock() - if lexer, ok := lexerCache[key]; ok { - cacheMutex.RUnlock() - return lexer - } - cacheMutex.RUnlock() - - lexer := fallbackFunc() - if lexer != nil { - cacheMutex.Lock() - lexerCache[key] = lexer - cacheMutex.Unlock() - } - return lexer -} - -func getCachedStyle(name string) *chroma.Style { - cacheMutex.RLock() - if style, ok := styleCache[name]; ok { - cacheMutex.RUnlock() - return style - } - cacheMutex.RUnlock() - - style := styles.Get(name) - if style == nil { - fmt.Fprintf(os.Stderr, "Warning: Style '%s' not found, using fallback\n", name) - style = styles.Fallback - } - - cacheMutex.Lock() - styleCache[name] = style - cacheMutex.Unlock() - return style -} - -func getCachedFormatter(inline bool, lineNumbers bool) *html.Formatter { - key := fmt.Sprintf("inline=%t,lineNumbers=%t", inline, lineNumbers) - - cacheMutex.RLock() - if formatter, ok := formatterCache[key]; ok { - cacheMutex.RUnlock() - return formatter - } - cacheMutex.RUnlock() - - var opts []html.Option - if inline { - opts = append(opts, html.WithClasses(false)) - } else { - opts = append(opts, html.WithClasses(true)) - } - opts = append(opts, html.TabWidth(4)) - - if lineNumbers { - opts = append(opts, html.WithLineNumbers(true)) - opts = append(opts, html.LineNumbersInTable(false)) - opts = append(opts, html.WithLinkableLineNumbers(false, "")) - } - - formatter := html.New(opts...) - - cacheMutex.Lock() - formatterCache[key] = formatter - cacheMutex.Unlock() - return formatter -} - -func runChroma(cmd *cobra.Command, args []string) { - var source string - var filename string - - // Read from file or stdin - if len(args) > 0 { - filename = args[0] - - // Check file size before reading - fileInfo, err := os.Stat(filename) - if err != nil { - fmt.Fprintf(os.Stderr, "Error reading file info: %v\n", err) - os.Exit(1) - } - - if fileInfo.Size() > maxFileSize { - fmt.Fprintf(os.Stderr, "Warning: File size (%d bytes) exceeds recommended limit (%d bytes)\n", - fileInfo.Size(), maxFileSize) - fmt.Fprintf(os.Stderr, "Processing may be slow. Consider using smaller files.\n") - } - - content, err := os.ReadFile(filename) - if err != nil { - fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err) - os.Exit(1) - } - source = string(content) - } else { - stat, _ := os.Stdin.Stat() - if (stat.Mode() & os.ModeCharDevice) != 0 { - _ = cmd.Help() - os.Exit(0) - } - - content, err := io.ReadAll(os.Stdin) - if err != nil { - fmt.Fprintf(os.Stderr, "Error reading stdin: %v\n", err) - os.Exit(1) - } - source = string(content) - } - - // Handle empty input - if strings.TrimSpace(source) == "" { - return - } - - // Handle Markdown rendering - if chromaMarkdown { - md := goldmark.New( - goldmark.WithExtensions( - extension.GFM, - highlighting.NewHighlighting( - highlighting.WithStyle(chromaStyle), - highlighting.WithFormatOptions( - html.WithClasses(!chromaInline), - ), - ), - ), - goldmark.WithParserOptions( - parser.WithAutoHeadingID(), - ), - goldmark.WithRendererOptions( - ghtml.WithHardWraps(), - ghtml.WithXHTML(), - ), - ) - - var buf bytes.Buffer - if err := md.Convert([]byte(source), &buf); err != nil { - fmt.Fprintf(os.Stderr, "Markdown rendering error: %v\n", err) - os.Exit(1) - } - fmt.Print(buf.String()) - return - } - - // Detect or use specified lexer - var lexer chroma.Lexer - if chromaLanguage != "" { - lexer = getCachedLexer(chromaLanguage, func() chroma.Lexer { - l := lexers.Get(chromaLanguage) - if l == nil { - fmt.Fprintf(os.Stderr, "Unknown language: %s\n", chromaLanguage) - os.Exit(1) - } - return l - }) - } else if filename != "" { - lexer = getCachedLexer("file:"+filename, func() chroma.Lexer { - return lexers.Match(filename) - }) - } - - // Try content analysis if no lexer found (limit to first 1KB for performance) - if lexer == nil { - analyzeContent := source - if len(source) > 1024 { - analyzeContent = source[:1024] - } - lexer = lexers.Analyse(analyzeContent) - } - - // Fallback to plaintext - if lexer == nil { - lexer = lexers.Fallback - } - - lexer = chroma.Coalesce(lexer) - - // Get cached style - style := getCachedStyle(chromaStyle) - - // Get cached formatter - formatter := getCachedFormatter(chromaInline, chromaLineNumbers) - - // Tokenize - iterator, err := lexer.Tokenise(nil, source) - if err != nil { - fmt.Fprintf(os.Stderr, "Tokenization error: %v\n", err) - os.Exit(1) - } - - // Format and output - if chromaLineNumbers { - var buf bytes.Buffer - if err := formatter.Format(&buf, style, iterator); err != nil { - fmt.Fprintf(os.Stderr, "Formatting error: %v\n", err) - os.Exit(1) - } - // Add spacing between line numbers - output := buf.String() - output = strings.ReplaceAll(output, "", "\u00A0\u00A0") - fmt.Print(output) - } else { - if err := formatter.Format(os.Stdout, style, iterator); err != nil { - fmt.Fprintf(os.Stderr, "Formatting error: %v\n", err) - os.Exit(1) - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_clipboard.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_clipboard.go deleted file mode 100644 index 8a9b858..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_clipboard.go +++ /dev/null @@ -1,1004 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/binary" - "encoding/json" - "fmt" - "image" - _ "image/gif" - _ "image/jpeg" - _ "image/png" - "io" - "net/http" - "net/url" - "os" - "os/exec" - "os/signal" - "path/filepath" - "strconv" - "strings" - "syscall" - "time" - - bolt "go.etcd.io/bbolt" - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/tiff" - _ "golang.org/x/image/webp" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/spf13/cobra" -) - -var clipboardCmd = &cobra.Command{ - Use: "clipboard", - Aliases: []string{"cl"}, - Short: "Manage clipboard", - Long: "Interact with the clipboard manager", -} - -var clipCopyCmd = &cobra.Command{ - Use: "copy [text]", - Short: "Copy text to clipboard", - Long: "Copy text to clipboard. If no text provided, reads from stdin. Works without server.", - Run: runClipCopy, -} - -var ( - clipCopyForeground bool - clipCopyPasteOnce bool - clipCopyType string - clipCopyDownload bool - clipJSONOutput bool -) - -var clipPasteCmd = &cobra.Command{ - Use: "paste", - Short: "Paste text from clipboard", - Long: "Paste text from clipboard to stdout. Works without server.", - Run: runClipPaste, -} - -var clipWatchCmd = &cobra.Command{ - Use: "watch [command]", - Short: "Watch clipboard for changes", - Long: `Watch clipboard for changes and optionally execute a command. -Works like wl-paste --watch. Does not require server. - -If a command is provided, it will be executed each time the clipboard changes, -with the clipboard content piped to its stdin. - -Examples: - dms cl watch # Print clipboard changes to stdout - dms cl watch cat # Same as above - dms cl watch notify-send # Send notification on clipboard change`, - Run: runClipWatch, -} - -var clipHistoryCmd = &cobra.Command{ - Use: "history", - Short: "Show clipboard history", - Long: "Show clipboard history with previews (requires server)", - Run: runClipHistory, -} - -var clipGetCmd = &cobra.Command{ - Use: "get ", - Short: "Get clipboard entry by ID", - Long: "Get full clipboard entry data by ID (requires server). Use --copy to copy it to clipboard.", - Args: cobra.ExactArgs(1), - Run: runClipGet, -} - -var clipGetCopy bool - -var clipDeleteCmd = &cobra.Command{ - Use: "delete ", - Short: "Delete clipboard entry", - Long: "Delete a clipboard history entry by ID (requires server)", - Args: cobra.ExactArgs(1), - Run: runClipDelete, -} - -var clipClearCmd = &cobra.Command{ - Use: "clear", - Short: "Clear clipboard history", - Long: "Clear all clipboard history (requires server)", - Run: runClipClear, -} - -var clipWatchStore bool -var clipWatchMimes bool - -var clipSearchCmd = &cobra.Command{ - Use: "search [query]", - Short: "Search clipboard history", - Long: "Search clipboard history with filters (requires server)", - Run: runClipSearch, -} - -var ( - clipSearchLimit int - clipSearchOffset int - clipSearchMimeType string - clipSearchImages bool - clipSearchText bool -) - -var clipConfigCmd = &cobra.Command{ - Use: "config", - Short: "Manage clipboard config", - Long: "Get or set clipboard configuration (requires server)", -} - -var clipConfigGetCmd = &cobra.Command{ - Use: "get", - Short: "Get clipboard config", - Run: runClipConfigGet, -} - -var clipConfigSetCmd = &cobra.Command{ - Use: "set", - Short: "Set clipboard config", - Long: `Set clipboard configuration options. - -Examples: - dms cl config set --max-history 200 - dms cl config set --auto-clear-days 7 - dms cl config set --clear-at-startup`, - Run: runClipConfigSet, -} - -var ( - clipConfigMaxHistory int - clipConfigAutoClearDays int - clipConfigClearAtStartup bool - clipConfigNoClearStartup bool - clipConfigDisabled bool - clipConfigEnabled bool -) - -var clipExportCmd = &cobra.Command{ - Use: "export [file]", - Short: "Export clipboard history to JSON", - Long: "Export clipboard history to JSON file. If no file specified, writes to stdout.", - Run: runClipExport, -} - -var clipImportCmd = &cobra.Command{ - Use: "import ", - Short: "Import clipboard history from JSON", - Long: "Import clipboard history from JSON file exported by 'dms cl export'.", - Args: cobra.ExactArgs(1), - Run: runClipImport, -} - -var clipMigrateCmd = &cobra.Command{ - Use: "cliphist-migrate [db-path]", - Short: "Migrate from cliphist", - Long: "Migrate clipboard history from cliphist. Uses default cliphist path if not specified.", - Run: runClipMigrate, -} - -var clipMigrateDelete bool - -func init() { - clipCopyCmd.Flags().BoolVarP(&clipCopyForeground, "foreground", "f", false, "Stay in foreground instead of forking") - clipCopyCmd.Flags().BoolVarP(&clipCopyPasteOnce, "paste-once", "o", false, "Exit after first paste") - clipCopyCmd.Flags().StringVarP(&clipCopyType, "type", "t", "text/plain;charset=utf-8", "MIME type") - clipCopyCmd.Flags().BoolVarP(&clipCopyDownload, "download", "d", false, "Download URL as image and copy as file") - - clipWatchCmd.Flags().BoolVar(&clipJSONOutput, "json", false, "Output as JSON") - clipHistoryCmd.Flags().BoolVar(&clipJSONOutput, "json", false, "Output as JSON") - clipGetCmd.Flags().BoolVar(&clipJSONOutput, "json", false, "Output as JSON") - clipGetCmd.Flags().BoolVarP(&clipGetCopy, "copy", "C", false, "Copy entry to clipboard") - - clipSearchCmd.Flags().IntVarP(&clipSearchLimit, "limit", "l", 50, "Max results") - clipSearchCmd.Flags().IntVarP(&clipSearchOffset, "offset", "o", 0, "Result offset") - clipSearchCmd.Flags().StringVarP(&clipSearchMimeType, "mime", "m", "", "Filter by MIME type") - clipSearchCmd.Flags().BoolVar(&clipSearchImages, "images", false, "Only images") - clipSearchCmd.Flags().BoolVar(&clipSearchText, "text", false, "Only text") - clipSearchCmd.Flags().BoolVar(&clipJSONOutput, "json", false, "Output as JSON") - - clipConfigSetCmd.Flags().IntVar(&clipConfigMaxHistory, "max-history", 0, "Max history entries") - clipConfigSetCmd.Flags().IntVar(&clipConfigAutoClearDays, "auto-clear-days", -1, "Auto-clear entries older than N days (0 to disable)") - clipConfigSetCmd.Flags().BoolVar(&clipConfigClearAtStartup, "clear-at-startup", false, "Clear history on startup") - clipConfigSetCmd.Flags().BoolVar(&clipConfigNoClearStartup, "no-clear-at-startup", false, "Don't clear history on startup") - clipConfigSetCmd.Flags().BoolVar(&clipConfigDisabled, "disable", false, "Disable clipboard tracking") - clipConfigSetCmd.Flags().BoolVar(&clipConfigEnabled, "enable", false, "Enable clipboard tracking") - - clipWatchCmd.Flags().BoolVarP(&clipWatchStore, "store", "s", false, "Store clipboard changes to history (no server required)") - clipWatchCmd.Flags().BoolVarP(&clipWatchMimes, "mimes", "m", false, "Show all offered MIME types") - - clipMigrateCmd.Flags().BoolVar(&clipMigrateDelete, "delete", false, "Delete cliphist db after successful migration") - - clipConfigCmd.AddCommand(clipConfigGetCmd, clipConfigSetCmd) - clipboardCmd.AddCommand(clipCopyCmd, clipPasteCmd, clipWatchCmd, clipHistoryCmd, clipGetCmd, clipDeleteCmd, clipClearCmd, clipSearchCmd, clipConfigCmd, clipExportCmd, clipImportCmd, clipMigrateCmd) -} - -func runClipCopy(cmd *cobra.Command, args []string) { - var data []byte - copyFromStdin := false - - switch { - case len(args) > 0: - data = []byte(args[0]) - case clipCopyDownload || clipCopyType == "__multi__": - var err error - data, err = io.ReadAll(os.Stdin) - if err != nil { - log.Fatalf("read stdin: %v", err) - } - default: - copyFromStdin = true - } - - if clipCopyDownload { - filePath, err := downloadToTempFile(strings.TrimSpace(string(data))) - if err != nil { - log.Fatalf("download: %v", err) - } - if err := copyFileToClipboard(filePath); err != nil { - log.Fatalf("copy file: %v", err) - } - fmt.Printf("Downloaded and copied: %s\n", filePath) - return - } - - if clipCopyType == "__multi__" { - offers, err := parseMultiOffers(data) - if err != nil { - log.Fatalf("parse multi offers: %v", err) - } - if err := clipboard.CopyMulti(offers, true, clipCopyPasteOnce); err != nil { - log.Fatalf("copy multi: %v", err) - } - return - } - - if copyFromStdin { - if err := clipboard.CopyReader(os.Stdin, clipCopyType, clipCopyForeground, clipCopyPasteOnce); err != nil { - log.Fatalf("copy: %v", err) - } - return - } - - if err := clipboard.CopyOpts(data, clipCopyType, clipCopyForeground, clipCopyPasteOnce); err != nil { - log.Fatalf("copy: %v", err) - } -} - -func parseMultiOffers(data []byte) ([]clipboard.Offer, error) { - var offers []clipboard.Offer - pos := 0 - - for pos < len(data) { - mimeEnd := bytes.IndexByte(data[pos:], 0) - if mimeEnd == -1 { - break - } - mimeType := string(data[pos : pos+mimeEnd]) - pos += mimeEnd + 1 - - lenEnd := bytes.IndexByte(data[pos:], 0) - if lenEnd == -1 { - break - } - dataLen, err := strconv.Atoi(string(data[pos : pos+lenEnd])) - if err != nil { - return nil, fmt.Errorf("parse length: %w", err) - } - pos += lenEnd + 1 - - if pos+dataLen > len(data) { - return nil, fmt.Errorf("data truncated") - } - offerData := data[pos : pos+dataLen] - pos += dataLen - - offers = append(offers, clipboard.Offer{MimeType: mimeType, Data: offerData}) - } - - return offers, nil -} - -func runClipPaste(cmd *cobra.Command, args []string) { - data, _, err := clipboard.Paste() - if err != nil { - log.Fatalf("paste: %v", err) - } - os.Stdout.Write(data) -} - -func runClipWatch(cmd *cobra.Command, args []string) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) - go func() { - <-sigCh - cancel() - }() - - switch { - case len(args) > 0: - if err := clipboard.Watch(ctx, func(data []byte, mimeType string) { - runCommand(args, data) - }); err != nil && err != context.Canceled { - log.Fatalf("Watch error: %v", err) - } - case clipWatchStore: - if err := clipboard.Watch(ctx, func(data []byte, mimeType string) { - if err := clipboard.Store(data, mimeType); err != nil { - log.Errorf("store: %v", err) - } - }); err != nil && err != context.Canceled { - log.Fatalf("Watch error: %v", err) - } - case clipWatchMimes: - if err := clipboard.WatchAll(ctx, func(data []byte, mimeType string, allMimes []string) { - if clipJSONOutput { - out := map[string]any{ - "data": string(data), - "mimeType": mimeType, - "mimeTypes": allMimes, - "timestamp": time.Now().Format(time.RFC3339), - "size": len(data), - } - j, _ := json.Marshal(out) - fmt.Println(string(j)) - return - } - fmt.Printf("=== Clipboard Change ===\n") - fmt.Printf("Selected: %s\n", mimeType) - fmt.Printf("All MIME types:\n") - for _, m := range allMimes { - fmt.Printf(" - %s\n", m) - } - fmt.Printf("Size: %d bytes\n\n", len(data)) - }); err != nil && err != context.Canceled { - log.Fatalf("Watch error: %v", err) - } - case clipJSONOutput: - if err := clipboard.Watch(ctx, func(data []byte, mimeType string) { - out := map[string]any{ - "data": string(data), - "mimeType": mimeType, - "timestamp": time.Now().Format(time.RFC3339), - "size": len(data), - } - j, _ := json.Marshal(out) - fmt.Println(string(j)) - }); err != nil && err != context.Canceled { - log.Fatalf("Watch error: %v", err) - } - default: - if err := clipboard.Watch(ctx, func(data []byte, mimeType string) { - os.Stdout.Write(data) - os.Stdout.WriteString("\n") - }); err != nil && err != context.Canceled { - log.Fatalf("Watch error: %v", err) - } - } -} - -func runCommand(args []string, stdin []byte) { - cmd := exec.Command(args[0], args[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if len(stdin) == 0 { - cmd.Run() - return - } - - r, w, err := os.Pipe() - if err != nil { - cmd.Run() - return - } - - cmd.Stdin = r - go func() { - w.Write(stdin) - w.Close() - }() - cmd.Run() -} - -func runClipHistory(cmd *cobra.Command, args []string) { - req := models.Request{ - ID: 1, - Method: "clipboard.getHistory", - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to get clipboard history: %v", err) - } - - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - - if resp.Result == nil { - if clipJSONOutput { - fmt.Println("[]") - } else { - fmt.Println("No clipboard history") - } - return - } - - historyList, ok := (*resp.Result).([]any) - if !ok { - log.Fatal("Invalid response format") - } - - if clipJSONOutput { - out, _ := json.MarshalIndent(historyList, "", " ") - fmt.Println(string(out)) - return - } - - if len(historyList) == 0 { - fmt.Println("No clipboard history") - return - } - - fmt.Println("Clipboard History:") - fmt.Println() - - for _, item := range historyList { - entry, ok := item.(map[string]any) - if !ok { - continue - } - - id := uint64(entry["id"].(float64)) - preview := entry["preview"].(string) - timestamp := entry["timestamp"].(string) - isImage := entry["isImage"].(bool) - - typeStr := "text" - if isImage { - typeStr = "image" - } - - fmt.Printf("ID: %d | %s | %s\n", id, typeStr, timestamp) - fmt.Printf(" %s\n", preview) - fmt.Println() - } -} - -func runClipGet(cmd *cobra.Command, args []string) { - id, err := strconv.ParseUint(args[0], 10, 64) - if err != nil { - log.Fatalf("Invalid ID: %v", err) - } - - if clipGetCopy { - req := models.Request{ - ID: 1, - Method: "clipboard.copyEntry", - Params: map[string]any{"id": id}, - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to copy clipboard entry: %v", err) - } - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - - fmt.Printf("Copied entry %d to clipboard\n", id) - return - } - - req := models.Request{ - ID: 1, - Method: "clipboard.getEntry", - Params: map[string]any{ - "id": id, - }, - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to get clipboard entry: %v", err) - } - - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - - if resp.Result == nil { - log.Fatal("Entry not found") - } - - entry, ok := (*resp.Result).(map[string]any) - if !ok { - log.Fatal("Invalid response format") - } - - switch { - case clipJSONOutput: - output, _ := json.MarshalIndent(entry, "", " ") - fmt.Println(string(output)) - default: - if data, ok := entry["data"].(string); ok { - fmt.Print(data) - } else { - output, _ := json.MarshalIndent(entry, "", " ") - fmt.Println(string(output)) - } - } -} - -func runClipDelete(cmd *cobra.Command, args []string) { - id, err := strconv.ParseUint(args[0], 10, 64) - if err != nil { - log.Fatalf("Invalid ID: %v", err) - } - - req := models.Request{ - ID: 1, - Method: "clipboard.deleteEntry", - Params: map[string]any{ - "id": id, - }, - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to delete clipboard entry: %v", err) - } - - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - - fmt.Printf("Deleted entry %d\n", id) -} - -func runClipClear(cmd *cobra.Command, args []string) { - req := models.Request{ - ID: 1, - Method: "clipboard.clearHistory", - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to clear clipboard history: %v", err) - } - - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - - fmt.Println("Clipboard history cleared") -} - -func runClipSearch(cmd *cobra.Command, args []string) { - params := map[string]any{ - "limit": clipSearchLimit, - "offset": clipSearchOffset, - } - - if len(args) > 0 { - params["query"] = args[0] - } - if clipSearchMimeType != "" { - params["mimeType"] = clipSearchMimeType - } - if clipSearchImages { - params["isImage"] = true - } else if clipSearchText { - params["isImage"] = false - } - - req := models.Request{ - ID: 1, - Method: "clipboard.search", - Params: params, - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to search clipboard: %v", err) - } - - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - - if resp.Result == nil { - log.Fatal("No results") - } - - result, ok := (*resp.Result).(map[string]any) - if !ok { - log.Fatal("Invalid response format") - } - - if clipJSONOutput { - out, _ := json.MarshalIndent(result, "", " ") - fmt.Println(string(out)) - return - } - - entries, _ := result["entries"].([]any) - total := int(result["total"].(float64)) - hasMore := result["hasMore"].(bool) - - if len(entries) == 0 { - fmt.Println("No results found") - return - } - - fmt.Printf("Results: %d of %d\n\n", len(entries), total) - - for _, item := range entries { - entry, ok := item.(map[string]any) - if !ok { - continue - } - - id := uint64(entry["id"].(float64)) - preview := entry["preview"].(string) - timestamp := entry["timestamp"].(string) - isImage := entry["isImage"].(bool) - - typeStr := "text" - if isImage { - typeStr = "image" - } - - fmt.Printf("ID: %d | %s | %s\n", id, typeStr, timestamp) - fmt.Printf(" %s\n\n", preview) - } - - if hasMore { - fmt.Printf("Use --offset %d to see more results\n", clipSearchOffset+clipSearchLimit) - } -} - -func runClipConfigGet(cmd *cobra.Command, args []string) { - req := models.Request{ - ID: 1, - Method: "clipboard.getConfig", - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to get config: %v", err) - } - - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - - if resp.Result == nil { - log.Fatal("No config returned") - } - - cfg, ok := (*resp.Result).(map[string]any) - if !ok { - log.Fatal("Invalid response format") - } - - output, _ := json.MarshalIndent(cfg, "", " ") - fmt.Println(string(output)) -} - -func runClipConfigSet(cmd *cobra.Command, args []string) { - params := map[string]any{} - - if cmd.Flags().Changed("max-history") { - params["maxHistory"] = clipConfigMaxHistory - } - if cmd.Flags().Changed("auto-clear-days") { - params["autoClearDays"] = clipConfigAutoClearDays - } - if clipConfigClearAtStartup { - params["clearAtStartup"] = true - } - if clipConfigNoClearStartup { - params["clearAtStartup"] = false - } - if clipConfigDisabled { - params["disabled"] = true - } - if clipConfigEnabled { - params["disabled"] = false - } - - if len(params) == 0 { - fmt.Println("No config options specified") - return - } - - req := models.Request{ - ID: 1, - Method: "clipboard.setConfig", - Params: params, - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to set config: %v", err) - } - - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - - fmt.Println("Config updated") -} - -func runClipExport(cmd *cobra.Command, args []string) { - req := models.Request{ - ID: 1, - Method: "clipboard.getHistory", - } - - resp, err := sendServerRequest(req) - if err != nil { - log.Fatalf("Failed to get clipboard history: %v", err) - } - if resp.Error != "" { - log.Fatalf("Error: %s", resp.Error) - } - if resp.Result == nil { - log.Fatal("No clipboard history") - } - - out, err := json.MarshalIndent(resp.Result, "", " ") - if err != nil { - log.Fatalf("Failed to marshal: %v", err) - } - - if len(args) == 0 { - fmt.Println(string(out)) - return - } - - if err := os.WriteFile(args[0], out, 0o644); err != nil { - log.Fatalf("Failed to write file: %v", err) - } - fmt.Printf("Exported to %s\n", args[0]) -} - -func runClipImport(cmd *cobra.Command, args []string) { - data, err := os.ReadFile(args[0]) - if err != nil { - log.Fatalf("Failed to read file: %v", err) - } - - var entries []map[string]any - if err := json.Unmarshal(data, &entries); err != nil { - log.Fatalf("Failed to parse JSON: %v", err) - } - - var imported int - for _, entry := range entries { - dataStr, ok := entry["data"].(string) - if !ok { - continue - } - mimeType, _ := entry["mimeType"].(string) - if mimeType == "" { - mimeType = "text/plain" - } - - var entryData []byte - if decoded, err := base64.StdEncoding.DecodeString(dataStr); err == nil { - entryData = decoded - } else { - entryData = []byte(dataStr) - } - - if err := clipboard.Store(entryData, mimeType); err != nil { - log.Errorf("Failed to store entry: %v", err) - continue - } - imported++ - } - - fmt.Printf("Imported %d entries\n", imported) -} - -func runClipMigrate(cmd *cobra.Command, args []string) { - dbPath := getCliphistPath() - if len(args) > 0 { - dbPath = args[0] - } - - if _, err := os.Stat(dbPath); err != nil { - log.Fatalf("Cliphist db not found: %s", dbPath) - } - - db, err := bolt.Open(dbPath, 0o644, &bolt.Options{ - ReadOnly: true, - Timeout: 1 * time.Second, - }) - if err != nil { - log.Fatalf("Failed to open cliphist db: %v", err) - } - defer db.Close() - - var migrated int - err = db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("b")) - if b == nil { - return fmt.Errorf("cliphist bucket not found") - } - - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - if len(v) == 0 { - continue - } - - mimeType := detectMimeType(v) - if err := clipboard.Store(v, mimeType); err != nil { - log.Errorf("Failed to store entry %d: %v", btoi(k), err) - continue - } - migrated++ - } - return nil - }) - if err != nil { - log.Fatalf("Migration failed: %v", err) - } - - fmt.Printf("Migrated %d entries from cliphist\n", migrated) - - if !clipMigrateDelete { - return - } - - db.Close() - if err := os.Remove(dbPath); err != nil { - log.Errorf("Failed to delete cliphist db: %v", err) - return - } - os.Remove(filepath.Dir(dbPath)) - fmt.Println("Deleted cliphist db") -} - -func getCliphistPath() string { - cacheDir, err := os.UserCacheDir() - if err != nil { - return filepath.Join(os.Getenv("HOME"), ".cache", "cliphist", "db") - } - return filepath.Join(cacheDir, "cliphist", "db") -} - -func detectMimeType(data []byte) string { - if _, _, err := image.DecodeConfig(bytes.NewReader(data)); err == nil { - return "image/png" - } - return "text/plain" -} - -func btoi(v []byte) uint64 { - return binary.BigEndian.Uint64(v) -} - -func downloadToTempFile(rawURL string) (string, error) { - if !strings.HasPrefix(rawURL, "http://") && !strings.HasPrefix(rawURL, "https://") { - return "", fmt.Errorf("invalid URL: %s", rawURL) - } - - parsedURL, err := url.Parse(rawURL) - if err != nil { - return "", fmt.Errorf("parse URL: %w", err) - } - - ext := filepath.Ext(parsedURL.Path) - if ext == "" { - ext = ".png" - } - - client := &http.Client{Timeout: 30 * time.Second} - - var data []byte - var contentType string - var lastErr error - - for attempt := 0; attempt < 3; attempt++ { - if attempt > 0 { - time.Sleep(time.Duration(attempt) * 500 * time.Millisecond) - } - - req, err := http.NewRequest("GET", rawURL, nil) - if err != nil { - lastErr = fmt.Errorf("create request: %w", err) - continue - } - req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") - req.Header.Set("Accept", "image/*,video/*,*/*") - - resp, err := client.Do(req) - if err != nil { - lastErr = fmt.Errorf("download (attempt %d): %w", attempt+1, err) - continue - } - - if resp.StatusCode != http.StatusOK { - resp.Body.Close() - lastErr = fmt.Errorf("download failed (attempt %d): status %d", attempt+1, resp.StatusCode) - continue - } - - data, err = io.ReadAll(resp.Body) - resp.Body.Close() - if err != nil { - lastErr = fmt.Errorf("read response (attempt %d): %w", attempt+1, err) - continue - } - - contentType = resp.Header.Get("Content-Type") - if idx := strings.Index(contentType, ";"); idx != -1 { - contentType = strings.TrimSpace(contentType[:idx]) - } - - lastErr = nil - break - } - - if lastErr != nil { - return "", lastErr - } - - if len(data) == 0 { - return "", fmt.Errorf("downloaded empty file") - } - - if !strings.HasPrefix(contentType, "image/") && !strings.HasPrefix(contentType, "video/") { - if _, _, err := image.DecodeConfig(bytes.NewReader(data)); err != nil { - return "", fmt.Errorf("not a valid media file (content-type: %s)", contentType) - } - } - - cacheDir, err := os.UserCacheDir() - if err != nil { - cacheDir = "/tmp" - } - clipDir := filepath.Join(cacheDir, "dms", "clipboard") - if err := os.MkdirAll(clipDir, 0o755); err != nil { - return "", fmt.Errorf("create cache dir: %w", err) - } - - filePath := filepath.Join(clipDir, fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)) - if err := os.WriteFile(filePath, data, 0o644); err != nil { - return "", fmt.Errorf("write file: %w", err) - } - - return filePath, nil -} - -func copyFileToClipboard(filePath string) error { - req := models.Request{ - ID: 1, - Method: "clipboard.copyFile", - Params: map[string]any{"filePath": filePath}, - } - - resp, err := sendServerRequest(req) - if err != nil { - return fmt.Errorf("server request: %w", err) - } - if resp.Error != "" { - return fmt.Errorf("server error: %s", resp.Error) - } - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_colorpicker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_colorpicker.go deleted file mode 100644 index 7c366e8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_colorpicker.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/colorpicker" - "github.com/spf13/cobra" -) - -var ( - colorOutputFmt string - colorAutocopy bool - colorNotify bool - colorLowercase bool -) - -var colorCmd = &cobra.Command{ - Use: "color", - Short: "Color utilities", - Long: "Color utilities including picking colors from the screen", -} - -var colorPickCmd = &cobra.Command{ - Use: "pick", - Short: "Pick a color from the screen", - Long: `Pick a color from anywhere on your screen using an interactive color picker. - -Click on any pixel to capture its color, or press Escape to cancel. - -Output format flags (mutually exclusive, default: --hex): - --hex - Hexadecimal (#RRGGBB) - --rgb - RGB values (R G B) - --hsl - HSL values (H S% L%) - --hsv - HSV values (H S% V%) - --cmyk - CMYK values (C% M% Y% K%) - --json - JSON with all formats - -Optional: - --raw - Removes ANSI escape codes and background colors. Use this when piping to other commands - -Examples: - dms color pick # Pick color, output as hex - dms color pick --rgb # Output as RGB - dms color pick --json # Output all formats as JSON - dms color pick --hex -l # Output hex in lowercase - dms color pick -a # Auto-copy result to clipboard`, - Run: runColorPick, -} - -func init() { - colorPickCmd.Flags().Bool("hex", false, "Output as hexadecimal (#RRGGBB)") - colorPickCmd.Flags().Bool("rgb", false, "Output as RGB (R G B)") - colorPickCmd.Flags().Bool("hsl", false, "Output as HSL (H S% L%)") - colorPickCmd.Flags().Bool("hsv", false, "Output as HSV (H S% V%)") - colorPickCmd.Flags().Bool("cmyk", false, "Output as CMYK (C% M% Y% K%)") - colorPickCmd.Flags().Bool("json", false, "Output all formats as JSON") - colorPickCmd.Flags().Bool("raw", false, "Removes ANSI escape codes and background colors. Use this when piping to other commands") - colorPickCmd.Flags().StringVarP(&colorOutputFmt, "output-format", "o", "", "Custom output format template") - colorPickCmd.Flags().BoolVarP(&colorAutocopy, "autocopy", "a", false, "Copy result to clipboard") - colorPickCmd.Flags().BoolVarP(&colorLowercase, "lowercase", "l", false, "Output hex in lowercase") - - colorPickCmd.MarkFlagsMutuallyExclusive("hex", "rgb", "hsl", "hsv", "cmyk", "json") - - colorCmd.AddCommand(colorPickCmd) -} - -func runColorPick(cmd *cobra.Command, args []string) { - format := colorpicker.FormatHex // default - jsonOutput, _ := cmd.Flags().GetBool("json") - - if rgb, _ := cmd.Flags().GetBool("rgb"); rgb { - format = colorpicker.FormatRGB - } else if hsl, _ := cmd.Flags().GetBool("hsl"); hsl { - format = colorpicker.FormatHSL - } else if hsv, _ := cmd.Flags().GetBool("hsv"); hsv { - format = colorpicker.FormatHSV - } else if cmyk, _ := cmd.Flags().GetBool("cmyk"); cmyk { - format = colorpicker.FormatCMYK - } - - config := colorpicker.Config{ - Format: format, - CustomFormat: colorOutputFmt, - Lowercase: colorLowercase, - Autocopy: colorAutocopy, - Notify: colorNotify, - } - - picker := colorpicker.New(config) - color, err := picker.Run() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - - if color == nil { - os.Exit(0) - } - - var output string - if jsonOutput { - jsonStr, err := color.ToJSON() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - output = jsonStr - } else { - output = color.Format(config.Format, config.Lowercase, config.CustomFormat) - } - - if colorAutocopy { - copyToClipboard(output) - } - - if jsonOutput { - fmt.Println(output) - return - } - - if raw, _ := cmd.Flags().GetBool("raw"); raw { - fmt.Printf("%s\n", output) - return - } - - if color.IsDark() { - fmt.Printf("\033[48;2;%d;%d;%dm\033[97m %s \033[0m\n", color.R, color.G, color.B, output) - } else { - fmt.Printf("\033[48;2;%d;%d;%dm\033[30m %s \033[0m\n", color.R, color.G, color.B, output) - } -} - -func copyToClipboard(text string) { - if err := clipboard.CopyText(text); err != nil { - fmt.Fprintln(os.Stderr, "clipboard copy failed:", err) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_common.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_common.go deleted file mode 100644 index 6557860..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_common.go +++ /dev/null @@ -1,530 +0,0 @@ -package main - -import ( - "fmt" - "os" - "regexp" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server" - "github.com/spf13/cobra" -) - -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Show version information", - Run: runVersion, -} - -var runCmd = &cobra.Command{ - Use: "run", - Short: "Launch quickshell with DMS configuration", - Long: "Launch quickshell with DMS configuration (qs -c dms)", - PreRunE: findConfig, - Run: func(cmd *cobra.Command, args []string) { - daemon, _ := cmd.Flags().GetBool("daemon") - session, _ := cmd.Flags().GetBool("session") - if daemon { - runShellDaemon(session) - } else { - runShellInteractive(session) - } - }, -} - -var restartCmd = &cobra.Command{ - Use: "restart", - Short: "Restart quickshell with DMS configuration", - Long: "Kill existing DMS shell processes and restart quickshell with DMS configuration", - PreRunE: findConfig, - Run: func(cmd *cobra.Command, args []string) { - restartShell() - }, -} - -var restartDetachedCmd = &cobra.Command{ - Use: "restart-detached ", - Hidden: true, - Args: cobra.ExactArgs(1), - PreRunE: findConfig, - Run: func(cmd *cobra.Command, args []string) { - runDetachedRestart(args[0]) - }, -} - -var killCmd = &cobra.Command{ - Use: "kill", - Short: "Kill running DMS shell processes", - Long: "Kill all running quickshell processes with DMS configuration", - Run: func(cmd *cobra.Command, args []string) { - killShell() - }, -} - -var ipcCmd = &cobra.Command{ - Use: "ipc [target] [function] [args...]", - Short: "Send IPC commands to running DMS shell", - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - _ = findConfig(cmd, args) - return getShellIPCCompletions(args, toComplete), cobra.ShellCompDirectiveNoFileComp - }, - Run: func(cmd *cobra.Command, args []string) { - runShellIPCCommand(args) - }, -} - -func init() { - ipcCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { - _ = findConfig(cmd, args) - printIPCHelp() - }) -} - -var debugSrvCmd = &cobra.Command{ - Use: "debug-srv", - Short: "Start the debug server", - Long: "Start the Unix socket debug server for DMS", - Run: func(cmd *cobra.Command, args []string) { - if err := startDebugServer(); err != nil { - log.Fatalf("Error starting debug server: %v", err) - } - }, -} - -var pluginsCmd = &cobra.Command{ - Use: "plugins", - Short: "Manage DMS plugins", - Long: "Browse and manage DMS plugins from the registry", -} - -var pluginsBrowseCmd = &cobra.Command{ - Use: "browse", - Short: "Browse available plugins", - Long: "Browse available plugins from the DMS plugin registry", - Run: func(cmd *cobra.Command, args []string) { - if err := browsePlugins(); err != nil { - log.Fatalf("Error browsing plugins: %v", err) - } - }, -} - -var pluginsListCmd = &cobra.Command{ - Use: "list", - Short: "List installed plugins", - Long: "List all installed DMS plugins", - Run: func(cmd *cobra.Command, args []string) { - if err := listInstalledPlugins(); err != nil { - log.Fatalf("Error listing plugins: %v", err) - } - }, -} - -var pluginsInstallCmd = &cobra.Command{ - Use: "install ", - Short: "Install a plugin by ID", - Long: "Install a DMS plugin from the registry using its ID (e.g., 'myPlugin'). Plugin names with spaces are also supported for backward compatibility.", - Args: cobra.ExactArgs(1), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return getAvailablePluginIDs(), cobra.ShellCompDirectiveNoFileComp - }, - Run: func(cmd *cobra.Command, args []string) { - if err := installPluginCLI(args[0]); err != nil { - log.Fatalf("Error installing plugin: %v", err) - } - }, -} - -var pluginsUninstallCmd = &cobra.Command{ - Use: "uninstall ", - Short: "Uninstall a plugin by ID", - Long: "Uninstall a DMS plugin using its ID (e.g., 'myPlugin'). Plugin names with spaces are also supported for backward compatibility.", - Args: cobra.ExactArgs(1), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return getInstalledPluginIDs(), cobra.ShellCompDirectiveNoFileComp - }, - Run: func(cmd *cobra.Command, args []string) { - if err := uninstallPluginCLI(args[0]); err != nil { - log.Fatalf("Error uninstalling plugin: %v", err) - } - }, -} - -var pluginsUpdateCmd = &cobra.Command{ - Use: "update ", - Short: "Update a plugin by ID", - Long: "Update an installed DMS plugin using its ID (e.g., 'myPlugin'). Plugin names are also supported.", - Args: cobra.ExactArgs(1), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return getInstalledPluginIDs(), cobra.ShellCompDirectiveNoFileComp - }, - Run: func(cmd *cobra.Command, args []string) { - if err := updatePluginCLI(args[0]); err != nil { - log.Fatalf("Error updating plugin: %v", err) - } - }, -} - -func runVersion(cmd *cobra.Command, args []string) { - fmt.Printf("%s\n", formatVersion(Version)) -} - -// Git builds: dms (git) v0.6.2-XXXX -// Stable releases: dms v0.6.2 -func formatVersion(version string) string { - // Arch/Debian/Ubuntu/OpenSUSE git format: 0.6.2+git2264.c5c5ce84 - re := regexp.MustCompile(`^([\d.]+)\+git(\d+)\.`) - if matches := re.FindStringSubmatch(version); matches != nil { - return fmt.Sprintf("dms (git) v%s-%s", matches[1], matches[2]) - } - - // Fedora COPR git format: 0.0.git.2267.d430cae9 - re = regexp.MustCompile(`^[\d.]+\.git\.(\d+)\.`) - if matches := re.FindStringSubmatch(version); matches != nil { - baseVersion := getBaseVersion() - return fmt.Sprintf("dms (git) v%s-%s", baseVersion, matches[1]) - } - - // Stable release format: 0.6.2 - re = regexp.MustCompile(`^([\d.]+)$`) - if matches := re.FindStringSubmatch(version); matches != nil { - return fmt.Sprintf("dms v%s", matches[1]) - } - - return fmt.Sprintf("dms %s", version) -} - -func getBaseVersion() string { - paths := []string{ - "/usr/share/quickshell/dms/VERSION", - "/usr/local/share/quickshell/dms/VERSION", - "/etc/xdg/quickshell/dms/VERSION", - } - - for _, path := range paths { - if content, err := os.ReadFile(path); err == nil { - ver := strings.TrimSpace(string(content)) - ver = strings.TrimPrefix(ver, "v") - if re := regexp.MustCompile(`^([\d.]+)`); re.MatchString(ver) { - if matches := re.FindStringSubmatch(ver); matches != nil { - return matches[1] - } - } - } - } - - // Fallback - return "1.0.2" -} - -func startDebugServer() error { - server.CLIVersion = Version - return server.Start(true) -} - -func browsePlugins() error { - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - fmt.Println("Fetching plugin registry...") - pluginList, err := registry.List() - if err != nil { - return fmt.Errorf("failed to list plugins: %w", err) - } - - if len(pluginList) == 0 { - fmt.Println("No plugins found in registry.") - return nil - } - - fmt.Printf("\nAvailable Plugins (%d):\n\n", len(pluginList)) - for _, plugin := range pluginList { - installed, _ := manager.IsInstalled(plugin) - installedMarker := "" - if installed { - installedMarker = " [Installed]" - } - - fmt.Printf(" %s%s\n", plugin.Name, installedMarker) - fmt.Printf(" ID: %s\n", plugin.ID) - fmt.Printf(" Category: %s\n", plugin.Category) - fmt.Printf(" Author: %s\n", plugin.Author) - fmt.Printf(" Description: %s\n", plugin.Description) - fmt.Printf(" Repository: %s\n", plugin.Repo) - if len(plugin.Capabilities) > 0 { - fmt.Printf(" Capabilities: %s\n", strings.Join(plugin.Capabilities, ", ")) - } - if len(plugin.Compositors) > 0 { - fmt.Printf(" Compositors: %s\n", strings.Join(plugin.Compositors, ", ")) - } - if len(plugin.Dependencies) > 0 { - fmt.Printf(" Dependencies: %s\n", strings.Join(plugin.Dependencies, ", ")) - } - fmt.Println() - } - - return nil -} - -func listInstalledPlugins() error { - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - installedNames, err := manager.ListInstalled() - if err != nil { - return fmt.Errorf("failed to list installed plugins: %w", err) - } - - if len(installedNames) == 0 { - fmt.Println("No plugins installed.") - return nil - } - - allPlugins, err := registry.List() - if err != nil { - return fmt.Errorf("failed to list plugins: %w", err) - } - - pluginMap := make(map[string]plugins.Plugin) - for _, p := range allPlugins { - pluginMap[p.ID] = p - } - - fmt.Printf("\nInstalled Plugins (%d):\n\n", len(installedNames)) - for _, id := range installedNames { - if plugin, ok := pluginMap[id]; ok { - fmt.Printf(" %s\n", plugin.Name) - fmt.Printf(" ID: %s\n", plugin.ID) - fmt.Printf(" Category: %s\n", plugin.Category) - fmt.Printf(" Author: %s\n", plugin.Author) - fmt.Println() - } else { - fmt.Printf(" %s (not in registry)\n\n", id) - } - } - - return nil -} - -func installPluginCLI(idOrName string) error { - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - pluginList, err := registry.List() - if err != nil { - return fmt.Errorf("failed to list plugins: %w", err) - } - - // First, try to find by ID (preferred method) - var plugin *plugins.Plugin - for _, p := range pluginList { - if p.ID == idOrName { - plugin = &p - break - } - } - - // Fallback to name for backward compatibility - if plugin == nil { - for _, p := range pluginList { - if p.Name == idOrName { - plugin = &p - break - } - } - } - - if plugin == nil { - return fmt.Errorf("plugin not found: %s", idOrName) - } - - installed, err := manager.IsInstalled(*plugin) - if err != nil { - return fmt.Errorf("failed to check install status: %w", err) - } - - if installed { - return fmt.Errorf("plugin already installed: %s", plugin.Name) - } - - fmt.Printf("Installing plugin: %s (ID: %s)\n", plugin.Name, plugin.ID) - if err := manager.Install(*plugin); err != nil { - return fmt.Errorf("failed to install plugin: %w", err) - } - - fmt.Printf("Plugin installed successfully: %s\n", plugin.Name) - return nil -} - -func getAvailablePluginIDs() []string { - registry, err := plugins.NewRegistry() - if err != nil { - return nil - } - - pluginList, err := registry.List() - if err != nil { - return nil - } - - var ids []string - for _, p := range pluginList { - ids = append(ids, p.ID) - } - return ids -} - -func getInstalledPluginIDs() []string { - manager, err := plugins.NewManager() - if err != nil { - return nil - } - - installed, err := manager.ListInstalled() - if err != nil { - return nil - } - - return installed -} - -func uninstallPluginCLI(idOrName string) error { - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - pluginList, _ := registry.List() - plugin := plugins.FindByIDOrName(idOrName, pluginList) - - if plugin != nil { - installed, err := manager.IsInstalled(*plugin) - if err != nil { - return fmt.Errorf("failed to check install status: %w", err) - } - if !installed { - return fmt.Errorf("plugin not installed: %s", plugin.Name) - } - - fmt.Printf("Uninstalling plugin: %s (ID: %s)\n", plugin.Name, plugin.ID) - if err := manager.Uninstall(*plugin); err != nil { - return fmt.Errorf("failed to uninstall plugin: %w", err) - } - fmt.Printf("Plugin uninstalled successfully: %s\n", plugin.Name) - return nil - } - - fmt.Printf("Uninstalling plugin: %s\n", idOrName) - if err := manager.UninstallByIDOrName(idOrName); err != nil { - return err - } - fmt.Printf("Plugin uninstalled successfully: %s\n", idOrName) - return nil -} - -func updatePluginCLI(idOrName string) error { - manager, err := plugins.NewManager() - if err != nil { - return fmt.Errorf("failed to create manager: %w", err) - } - - registry, err := plugins.NewRegistry() - if err != nil { - return fmt.Errorf("failed to create registry: %w", err) - } - - pluginList, _ := registry.List() - plugin := plugins.FindByIDOrName(idOrName, pluginList) - - if plugin != nil { - installed, err := manager.IsInstalled(*plugin) - if err != nil { - return fmt.Errorf("failed to check install status: %w", err) - } - if !installed { - return fmt.Errorf("plugin not installed: %s", plugin.Name) - } - - fmt.Printf("Updating plugin: %s (ID: %s)\n", plugin.Name, plugin.ID) - if err := manager.Update(*plugin); err != nil { - return fmt.Errorf("failed to update plugin: %w", err) - } - fmt.Printf("Plugin updated successfully: %s\n", plugin.Name) - return nil - } - - fmt.Printf("Updating plugin: %s\n", idOrName) - if err := manager.UpdateByIDOrName(idOrName); err != nil { - return err - } - fmt.Printf("Plugin updated successfully: %s\n", idOrName) - return nil -} - -func getCommonCommands() []*cobra.Command { - return []*cobra.Command{ - versionCmd, - runCmd, - restartCmd, - restartDetachedCmd, - killCmd, - ipcCmd, - debugSrvCmd, - pluginsCmd, - dank16Cmd, - brightnessCmd, - dpmsCmd, - keybindsCmd, - greeterCmd, - setupCmd, - colorCmd, - screenshotCmd, - notifyActionCmd, - notifyCmd, - genericNotifyActionCmd, - matugenCmd, - clipboardCmd, - chromaCmd, - doctorCmd, - configCmd, - dlCmd, - randrCmd, - blurCmd, - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_config.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_config.go deleted file mode 100644 index d604256..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_config.go +++ /dev/null @@ -1,318 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/spf13/cobra" -) - -var configCmd = &cobra.Command{ - Use: "config", - Short: "Configuration utilities", -} - -var resolveIncludeCmd = &cobra.Command{ - Use: "resolve-include ", - Short: "Check if a file is included in compositor config", - Long: "Recursively check if a file is included/sourced in compositor configuration. Returns JSON with exists and included status.", - Args: cobra.ExactArgs(2), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - switch len(args) { - case 0: - return []string{"hyprland", "niri", "mangowc"}, cobra.ShellCompDirectiveNoFileComp - case 1: - return []string{"cursor.kdl", "cursor.conf", "outputs.kdl", "outputs.conf", "binds.kdl", "binds.conf"}, cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, - Run: runResolveInclude, -} - -func init() { - configCmd.AddCommand(resolveIncludeCmd) -} - -type IncludeResult struct { - Exists bool `json:"exists"` - Included bool `json:"included"` -} - -func runResolveInclude(cmd *cobra.Command, args []string) { - compositor := strings.ToLower(args[0]) - filename := args[1] - - var result IncludeResult - var err error - - switch compositor { - case "hyprland": - result, err = checkHyprlandInclude(filename) - case "niri": - result, err = checkNiriInclude(filename) - case "mangowc", "dwl", "mango": - result, err = checkMangoWCInclude(filename) - default: - log.Fatalf("Unknown compositor: %s", compositor) - } - - if err != nil { - log.Fatalf("Error checking include: %v", err) - } - - output, _ := json.Marshal(result) - fmt.Fprintln(os.Stdout, string(output)) -} - -func checkHyprlandInclude(filename string) (IncludeResult, error) { - configDir, err := utils.ExpandPath("$HOME/.config/hypr") - if err != nil { - return IncludeResult{}, err - } - - targetPath := filepath.Join(configDir, "dms", filename) - result := IncludeResult{} - - if _, err := os.Stat(targetPath); err == nil { - result.Exists = true - } - - mainConfig := filepath.Join(configDir, "hyprland.conf") - if _, err := os.Stat(mainConfig); os.IsNotExist(err) { - return result, nil - } - - processed := make(map[string]bool) - result.Included = hyprlandFindInclude(mainConfig, "dms/"+filename, processed) - return result, nil -} - -func hyprlandFindInclude(filePath, target string, processed map[string]bool) bool { - absPath, err := filepath.Abs(filePath) - if err != nil { - return false - } - - if processed[absPath] { - return false - } - processed[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return false - } - - baseDir := filepath.Dir(absPath) - lines := strings.Split(string(data), "\n") - - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, "#") || trimmed == "" { - continue - } - - if !strings.HasPrefix(trimmed, "source") { - continue - } - - parts := strings.SplitN(trimmed, "=", 2) - if len(parts) < 2 { - continue - } - - sourcePath := strings.TrimSpace(parts[1]) - if matchesTarget(sourcePath, target) { - return true - } - - fullPath := sourcePath - if !filepath.IsAbs(sourcePath) { - fullPath = filepath.Join(baseDir, sourcePath) - } - - expanded, err := utils.ExpandPath(fullPath) - if err != nil { - continue - } - - if hyprlandFindInclude(expanded, target, processed) { - return true - } - } - - return false -} - -func checkNiriInclude(filename string) (IncludeResult, error) { - configDir, err := utils.ExpandPath("$HOME/.config/niri") - if err != nil { - return IncludeResult{}, err - } - - targetPath := filepath.Join(configDir, "dms", filename) - result := IncludeResult{} - - if _, err := os.Stat(targetPath); err == nil { - result.Exists = true - } - - mainConfig := filepath.Join(configDir, "config.kdl") - if _, err := os.Stat(mainConfig); os.IsNotExist(err) { - return result, nil - } - - processed := make(map[string]bool) - result.Included = niriFindInclude(mainConfig, "dms/"+filename, processed) - return result, nil -} - -func niriFindInclude(filePath, target string, processed map[string]bool) bool { - absPath, err := filepath.Abs(filePath) - if err != nil { - return false - } - - if processed[absPath] { - return false - } - processed[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return false - } - - baseDir := filepath.Dir(absPath) - content := string(data) - - for _, line := range strings.Split(content, "\n") { - trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, "//") || trimmed == "" { - continue - } - - if !strings.HasPrefix(trimmed, "include") { - continue - } - - startQuote := strings.Index(trimmed, "\"") - if startQuote == -1 { - continue - } - endQuote := strings.LastIndex(trimmed, "\"") - if endQuote <= startQuote { - continue - } - - includePath := trimmed[startQuote+1 : endQuote] - if matchesTarget(includePath, target) { - return true - } - - fullPath := includePath - if !filepath.IsAbs(includePath) { - fullPath = filepath.Join(baseDir, includePath) - } - - if niriFindInclude(fullPath, target, processed) { - return true - } - } - - return false -} - -func checkMangoWCInclude(filename string) (IncludeResult, error) { - configDir, err := utils.ExpandPath("$HOME/.config/mango") - if err != nil { - return IncludeResult{}, err - } - - targetPath := filepath.Join(configDir, "dms", filename) - result := IncludeResult{} - - if _, err := os.Stat(targetPath); err == nil { - result.Exists = true - } - - mainConfig := filepath.Join(configDir, "config.conf") - if _, err := os.Stat(mainConfig); os.IsNotExist(err) { - mainConfig = filepath.Join(configDir, "mango.conf") - } - if _, err := os.Stat(mainConfig); os.IsNotExist(err) { - return result, nil - } - - processed := make(map[string]bool) - result.Included = mangowcFindInclude(mainConfig, "dms/"+filename, processed) - return result, nil -} - -func mangowcFindInclude(filePath, target string, processed map[string]bool) bool { - absPath, err := filepath.Abs(filePath) - if err != nil { - return false - } - - if processed[absPath] { - return false - } - processed[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return false - } - - baseDir := filepath.Dir(absPath) - lines := strings.Split(string(data), "\n") - - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, "#") || trimmed == "" { - continue - } - - if !strings.HasPrefix(trimmed, "source") { - continue - } - - parts := strings.SplitN(trimmed, "=", 2) - if len(parts) < 2 { - continue - } - - sourcePath := strings.TrimSpace(parts[1]) - if matchesTarget(sourcePath, target) { - return true - } - - fullPath := sourcePath - if !filepath.IsAbs(sourcePath) { - fullPath = filepath.Join(baseDir, sourcePath) - } - - expanded, err := utils.ExpandPath(fullPath) - if err != nil { - continue - } - - if mangowcFindInclude(expanded, target, processed) { - return true - } - } - - return false -} - -func matchesTarget(path, target string) bool { - path = strings.TrimPrefix(path, "./") - target = strings.TrimPrefix(target, "./") - return path == target || strings.HasSuffix(path, "/"+target) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_dank16.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_dank16.go deleted file mode 100644 index 0bbbab1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_dank16.go +++ /dev/null @@ -1,126 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/dank16" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/spf13/cobra" -) - -var dank16Cmd = &cobra.Command{ - Use: "dank16 [hex_color]", - Short: "Generate Base16 color palettes", - Long: "Generate Base16 color palettes from a color with support for various output formats", - Args: cobra.MaximumNArgs(1), - Run: runDank16, -} - -func init() { - dank16Cmd.Flags().Bool("light", false, "Generate light theme variant (sets default to light)") - dank16Cmd.Flags().Bool("json", false, "Output in JSON format") - dank16Cmd.Flags().Bool("kitty", false, "Output in Kitty terminal format") - dank16Cmd.Flags().Bool("foot", false, "Output in Foot terminal format") - dank16Cmd.Flags().Bool("neovim", false, "Output in Neovim plugin format") - dank16Cmd.Flags().Bool("alacritty", false, "Output in Alacritty terminal format") - dank16Cmd.Flags().Bool("ghostty", false, "Output in Ghostty terminal format") - dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm terminal format") - dank16Cmd.Flags().String("background", "", "Custom background color") - dank16Cmd.Flags().String("contrast", "dps", "Contrast algorithm: dps (Delta Phi Star, default) or wcag") - dank16Cmd.Flags().Bool("variants", false, "Output all variants (dark/light/default) in JSON") - dank16Cmd.Flags().String("primary-dark", "", "Primary color for dark mode (use with --variants)") - dank16Cmd.Flags().String("primary-light", "", "Primary color for light mode (use with --variants)") - _ = dank16Cmd.RegisterFlagCompletionFunc("contrast", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"dps", "wcag"}, cobra.ShellCompDirectiveNoFileComp - }) -} - -func runDank16(cmd *cobra.Command, args []string) { - isLight, _ := cmd.Flags().GetBool("light") - isJson, _ := cmd.Flags().GetBool("json") - isKitty, _ := cmd.Flags().GetBool("kitty") - isFoot, _ := cmd.Flags().GetBool("foot") - isNeovim, _ := cmd.Flags().GetBool("neovim") - isAlacritty, _ := cmd.Flags().GetBool("alacritty") - isGhostty, _ := cmd.Flags().GetBool("ghostty") - isWezterm, _ := cmd.Flags().GetBool("wezterm") - background, _ := cmd.Flags().GetString("background") - contrastAlgo, _ := cmd.Flags().GetString("contrast") - useVariants, _ := cmd.Flags().GetBool("variants") - primaryDark, _ := cmd.Flags().GetString("primary-dark") - primaryLight, _ := cmd.Flags().GetString("primary-light") - - if background != "" && !strings.HasPrefix(background, "#") { - background = "#" + background - } - if primaryDark != "" && !strings.HasPrefix(primaryDark, "#") { - primaryDark = "#" + primaryDark - } - if primaryLight != "" && !strings.HasPrefix(primaryLight, "#") { - primaryLight = "#" + primaryLight - } - - contrastAlgo = strings.ToLower(contrastAlgo) - if contrastAlgo != "dps" && contrastAlgo != "wcag" { - log.Fatalf("Invalid contrast algorithm: %s (must be 'dps' or 'wcag')", contrastAlgo) - } - - if useVariants { - if primaryDark == "" || primaryLight == "" { - if len(args) == 0 { - log.Fatalf("--variants requires either a positional color argument or both --primary-dark and --primary-light") - } - primaryColor := args[0] - if !strings.HasPrefix(primaryColor, "#") { - primaryColor = "#" + primaryColor - } - primaryDark = primaryColor - primaryLight = primaryColor - } - variantOpts := dank16.VariantOptions{ - PrimaryDark: primaryDark, - PrimaryLight: primaryLight, - Background: background, - UseDPS: contrastAlgo == "dps", - IsLightMode: isLight, - } - variantColors := dank16.GenerateVariantPalette(variantOpts) - fmt.Print(dank16.GenerateVariantJSON(variantColors)) - return - } - - if len(args) == 0 { - log.Fatalf("A color argument is required (or use --variants with --primary-dark and --primary-light)") - } - primaryColor := args[0] - if !strings.HasPrefix(primaryColor, "#") { - primaryColor = "#" + primaryColor - } - - opts := dank16.PaletteOptions{ - IsLight: isLight, - Background: background, - UseDPS: contrastAlgo == "dps", - } - - colors := dank16.GeneratePalette(primaryColor, opts) - - if isJson { - fmt.Print(dank16.GenerateJSON(colors)) - } else if isKitty { - fmt.Print(dank16.GenerateKittyTheme(colors)) - } else if isFoot { - fmt.Print(dank16.GenerateFootTheme(colors)) - } else if isAlacritty { - fmt.Print(dank16.GenerateAlacrittyTheme(colors)) - } else if isGhostty { - fmt.Print(dank16.GenerateGhosttyTheme(colors)) - } else if isWezterm { - fmt.Print(dank16.GenerateWeztermTheme(colors)) - } else if isNeovim { - fmt.Print(dank16.GenerateNeovimTheme(colors)) - } else { - fmt.Print(dank16.GenerateGhosttyTheme(colors)) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_doctor.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_doctor.go deleted file mode 100644 index b182d06..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_doctor.go +++ /dev/null @@ -1,1107 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "regexp" - "runtime" - "slices" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/config" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/brightness" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/network" - "github.com/AvengeMedia/DankMaterialShell/core/internal/tui" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/AvengeMedia/DankMaterialShell/core/internal/version" - "github.com/charmbracelet/lipgloss" - "github.com/spf13/cobra" -) - -type status string - -const ( - statusOK status = "ok" - statusWarn status = "warn" - statusError status = "error" - statusInfo status = "info" -) - -func (s status) IconStyle(styles tui.Styles) (string, lipgloss.Style) { - switch s { - case statusOK: - return "●", styles.Success - case statusWarn: - return "●", styles.Warning - case statusError: - return "●", styles.Error - default: - return "○", styles.Subtle - } -} - -type DoctorStatus struct { - Errors []checkResult - Warnings []checkResult - OK []checkResult - Info []checkResult -} - -func (ds *DoctorStatus) Add(r checkResult) { - switch r.status { - case statusError: - ds.Errors = append(ds.Errors, r) - case statusWarn: - ds.Warnings = append(ds.Warnings, r) - case statusOK: - ds.OK = append(ds.OK, r) - case statusInfo: - ds.Info = append(ds.Info, r) - } -} - -func (ds *DoctorStatus) HasIssues() bool { - return len(ds.Errors) > 0 || len(ds.Warnings) > 0 -} - -func (ds *DoctorStatus) ErrorCount() int { - return len(ds.Errors) -} - -func (ds *DoctorStatus) WarningCount() int { - return len(ds.Warnings) -} - -func (ds *DoctorStatus) OKCount() int { - return len(ds.OK) -} - -var ( - quickshellVersionRegex = regexp.MustCompile(`(?i)quickshell (\d+\.\d+\.\d+)`) - hyprlandVersionRegex = regexp.MustCompile(`v?(\d+\.\d+\.\d+)`) - niriVersionRegex = regexp.MustCompile(`niri (\d+\.\d+)`) - swayVersionRegex = regexp.MustCompile(`sway version (\d+\.\d+)`) - riverVersionRegex = regexp.MustCompile(`river (\d+\.\d+)`) - wayfireVersionRegex = regexp.MustCompile(`wayfire (\d+\.\d+)`) - labwcVersionRegex = regexp.MustCompile(`labwc (\d+\.\d+\.\d+)`) - mangowcVersionRegex = regexp.MustCompile(`mango (\d+\.\d+\.\d+)`) -) - -var doctorCmd = &cobra.Command{ - Use: "doctor", - Short: "Diagnose DMS installation and dependencies", - Long: "Check system health, verify dependencies, and diagnose configuration issues for DMS", - Run: runDoctor, -} - -var ( - doctorVerbose bool - doctorJSON bool - doctorCopy bool -) - -func init() { - doctorCmd.Flags().BoolVarP(&doctorVerbose, "verbose", "v", false, "Show detailed output including paths and versions") - doctorCmd.Flags().BoolVarP(&doctorJSON, "json", "j", false, "Output results in JSON format") - doctorCmd.Flags().BoolVarP(&doctorCopy, "copy", "C", false, "Copy results to clipboard in GitHub-friendly format") -} - -type category int - -const ( - catSystem category = iota - catVersions - catInstallation - catCompositor - catQuickshellFeatures - catOptionalFeatures - catConfigFiles - catServices - catEnvironment -) - -func (c category) String() string { - switch c { - case catSystem: - return "System" - case catVersions: - return "Versions" - case catInstallation: - return "Installation" - case catCompositor: - return "Compositor" - case catQuickshellFeatures: - return "Quickshell Features" - case catOptionalFeatures: - return "Optional Features" - case catConfigFiles: - return "Config Files" - case catServices: - return "Services" - case catEnvironment: - return "Environment" - default: - return "Unknown" - } -} - -const ( - checkNameMaxLength = 21 - doctorDocsURL = "https://danklinux.com/docs/dankmaterialshell/cli-doctor" -) - -type checkResult struct { - category category - name string - status status - message string - details string - url string -} - -type checkResultJSON struct { - Category string `json:"category"` - Name string `json:"name"` - Status string `json:"status"` - Message string `json:"message"` - Details string `json:"details,omitempty"` - URL string `json:"url,omitempty"` -} - -type doctorOutputJSON struct { - Summary struct { - Errors int `json:"errors"` - Warnings int `json:"warnings"` - OK int `json:"ok"` - Info int `json:"info"` - } `json:"summary"` - Results []checkResultJSON `json:"results"` -} - -func (r checkResult) toJSON() checkResultJSON { - return checkResultJSON{ - Category: r.category.String(), - Name: r.name, - Status: string(r.status), - Message: r.message, - Details: r.details, - URL: r.url, - } -} - -func runDoctor(cmd *cobra.Command, args []string) { - if !doctorJSON && !doctorCopy { - printDoctorHeader() - } - - qsFeatures, qsMissingFeatures := checkQuickshellFeatures() - - results := slices.Concat( - checkSystemInfo(), - checkVersions(qsMissingFeatures), - checkDMSInstallation(), - checkWindowManagers(), - qsFeatures, - checkOptionalDependencies(), - checkConfigurationFiles(), - checkSystemdServices(), - checkEnvironmentVars(), - ) - - switch { - case doctorCopy: - text := formatResultsPlain(results) - if err := clipboard.CopyOpts([]byte(text), "text/plain;charset=utf-8", false, false); err != nil { - fmt.Fprintf(os.Stderr, "Failed to copy to clipboard: %v\n", err) - os.Exit(1) - } - fmt.Println("Doctor report copied to clipboard") - case doctorJSON: - printResultsJSON(results) - default: - printResults(results) - printSummary(results, qsMissingFeatures) - } -} - -func printDoctorHeader() { - theme := tui.TerminalTheme() - styles := tui.NewStyles(theme) - - fmt.Println(getThemedASCII()) - fmt.Println(styles.Title.Render("System Health Check")) - fmt.Println(styles.Subtle.Render("──────────────────────────────────────")) - fmt.Println() -} - -func checkSystemInfo() []checkResult { - var results []checkResult - - osInfo, err := distros.GetOSInfo() - if err != nil { - status, message, details := statusWarn, fmt.Sprintf("Unknown (%v)", err), "" - - if strings.Contains(err.Error(), "Unsupported distribution") { - osRelease := readOSRelease() - switch { - case osRelease["ID"] == "nixos": - status = statusOK - message = osRelease["PRETTY_NAME"] - if message == "" { - message = fmt.Sprintf("NixOS %s", osRelease["VERSION_ID"]) - } - details = "Supported for runtime (install via NixOS module or Flake)" - case osRelease["PRETTY_NAME"] != "": - message = fmt.Sprintf("%s (not supported by dms setup)", osRelease["PRETTY_NAME"]) - details = "DMS may work but automatic installation is not available" - } - } - - results = append(results, checkResult{catSystem, "Operating System", status, message, details, doctorDocsURL + "#operating-system"}) - } else { - status := statusOK - message := osInfo.PrettyName - if message == "" { - message = fmt.Sprintf("%s %s", osInfo.Distribution.ID, osInfo.VersionID) - } - if distros.IsUnsupportedDistro(osInfo.Distribution.ID, osInfo.VersionID) { - status = statusWarn - message += " (version may not be fully supported)" - } - results = append(results, checkResult{ - catSystem, "Operating System", status, message, - fmt.Sprintf("ID: %s, Version: %s, Arch: %s", osInfo.Distribution.ID, osInfo.VersionID, osInfo.Architecture), - doctorDocsURL + "#operating-system", - }) - } - - arch := runtime.GOARCH - archStatus := statusOK - if arch != "amd64" && arch != "arm64" { - archStatus = statusError - } - results = append(results, checkResult{catSystem, "Architecture", archStatus, arch, "", doctorDocsURL + "#architecture"}) - - waylandDisplay := os.Getenv("WAYLAND_DISPLAY") - xdgSessionType := os.Getenv("XDG_SESSION_TYPE") - - switch { - case waylandDisplay != "" || xdgSessionType == "wayland": - results = append(results, checkResult{ - catSystem, "Display Server", statusOK, "Wayland", - fmt.Sprintf("WAYLAND_DISPLAY=%s", waylandDisplay), - doctorDocsURL + "#display-server", - }) - case xdgSessionType == "x11": - results = append(results, checkResult{catSystem, "Display Server", statusError, "X11 (DMS requires Wayland)", "", doctorDocsURL + "#display-server"}) - default: - results = append(results, checkResult{ - catSystem, "Display Server", statusWarn, "Unknown (ensure you're running Wayland)", - fmt.Sprintf("XDG_SESSION_TYPE=%s", xdgSessionType), - doctorDocsURL + "#display-server", - }) - } - - return results -} - -func checkEnvironmentVars() []checkResult { - var results []checkResult - results = append(results, checkEnvVar("QT_QPA_PLATFORMTHEME")...) - results = append(results, checkEnvVar("QS_ICON_THEME")...) - return results -} - -func checkEnvVar(name string) []checkResult { - value := os.Getenv(name) - if value != "" { - return []checkResult{{catEnvironment, name, statusInfo, value, "", doctorDocsURL + "#environment-variables"}} - } - if doctorVerbose { - return []checkResult{{catEnvironment, name, statusInfo, "Not set", "", doctorDocsURL + "#environment-variables"}} - } - return nil -} - -func readOSRelease() map[string]string { - result := make(map[string]string) - data, err := os.ReadFile("/etc/os-release") - if err != nil { - return result - } - for line := range strings.SplitSeq(string(data), "\n") { - if parts := strings.SplitN(line, "=", 2); len(parts) == 2 { - result[parts[0]] = strings.Trim(parts[1], "\"") - } - } - return result -} - -func checkVersions(qsMissingFeatures bool) []checkResult { - dmsCliPath, _ := os.Executable() - dmsCliDetails := "" - if doctorVerbose { - dmsCliDetails = dmsCliPath - } - - results := []checkResult{ - {catVersions, "DMS CLI", statusOK, formatVersion(Version), dmsCliDetails, doctorDocsURL + "#dms-cli"}, - } - - qsVersion, qsStatus, qsPath := getQuickshellVersionInfo(qsMissingFeatures) - qsDetails := "" - if doctorVerbose && qsPath != "" { - qsDetails = qsPath - } - results = append(results, checkResult{catVersions, "Quickshell", qsStatus, qsVersion, qsDetails, doctorDocsURL + "#quickshell"}) - - dmsVersion, dmsPath := getDMSShellVersion() - if dmsVersion != "" { - results = append(results, checkResult{catVersions, "DMS Shell", statusOK, dmsVersion, dmsPath, doctorDocsURL + "#dms-shell"}) - } else { - results = append(results, checkResult{catVersions, "DMS Shell", statusError, "Not installed or not detected", "Run 'dms setup' to install", doctorDocsURL + "#dms-shell"}) - } - - return results -} - -func getDMSShellVersion() (version, path string) { - if err := findConfig(nil, nil); err == nil && configPath != "" { - versionFile := filepath.Join(configPath, "VERSION") - if data, err := os.ReadFile(versionFile); err == nil { - return strings.TrimSpace(string(data)), configPath - } - return "installed", configPath - } - - if dmsPath, err := config.LocateDMSConfig(); err == nil { - versionFile := filepath.Join(dmsPath, "VERSION") - if data, err := os.ReadFile(versionFile); err == nil { - return strings.TrimSpace(string(data)), dmsPath - } - return "installed", dmsPath - } - - return "", "" -} - -func getQuickshellVersionInfo(missingFeatures bool) (string, status, string) { - if !utils.CommandExists("qs") { - return "Not installed", statusError, "" - } - - qsPath, _ := exec.LookPath("qs") - - output, err := exec.Command("qs", "--version").Output() - if err != nil { - return "Installed (version check failed)", statusWarn, qsPath - } - - fullVersion := strings.TrimSpace(string(output)) - if matches := quickshellVersionRegex.FindStringSubmatch(fullVersion); len(matches) >= 2 { - if version.CompareVersions(matches[1], "0.2.0") < 0 { - return fmt.Sprintf("%s (needs >= 0.2.0)", fullVersion), statusError, qsPath - } - if missingFeatures { - return fullVersion, statusWarn, qsPath - } - return fullVersion, statusOK, qsPath - } - - return fullVersion, statusWarn, qsPath -} - -func checkDMSInstallation() []checkResult { - var results []checkResult - - dmsPath := "" - if err := findConfig(nil, nil); err == nil && configPath != "" { - dmsPath = configPath - } else if path, err := config.LocateDMSConfig(); err == nil { - dmsPath = path - } - - if dmsPath == "" { - return []checkResult{{catInstallation, "DMS Configuration", statusError, "Not found", "shell.qml not found in any config path", doctorDocsURL + "#dms-configuration"}} - } - - results = append(results, checkResult{catInstallation, "DMS Configuration", statusOK, "Found", dmsPath, doctorDocsURL + "#dms-configuration"}) - - shellQml := filepath.Join(dmsPath, "shell.qml") - if _, err := os.Stat(shellQml); err != nil { - results = append(results, checkResult{catInstallation, "shell.qml", statusError, "Missing", shellQml, doctorDocsURL + "#dms-configuration"}) - } else { - results = append(results, checkResult{catInstallation, "shell.qml", statusOK, "Present", shellQml, doctorDocsURL + "#dms-configuration"}) - } - - if doctorVerbose { - installType := "Unknown" - switch { - case strings.Contains(dmsPath, "/nix/store"): - installType = "Nix store" - case strings.Contains(dmsPath, ".local/share") || strings.Contains(dmsPath, "/usr/share"): - installType = "System package" - case strings.Contains(dmsPath, ".config"): - installType = "User config" - } - results = append(results, checkResult{catInstallation, "Install Type", statusInfo, installType, dmsPath, doctorDocsURL + "#dms-configuration"}) - } - - return results -} - -func checkWindowManagers() []checkResult { - compositors := []struct { - name, versionCmd, versionArg string - versionRegex *regexp.Regexp - commands []string - }{ - {"Hyprland", "Hyprland", "--version", hyprlandVersionRegex, []string{"hyprland", "Hyprland"}}, - {"niri", "niri", "--version", niriVersionRegex, []string{"niri"}}, - {"Sway", "sway", "--version", swayVersionRegex, []string{"sway"}}, - {"River", "river", "-version", riverVersionRegex, []string{"river"}}, - {"Wayfire", "wayfire", "--version", wayfireVersionRegex, []string{"wayfire"}}, - {"labwc", "labwc", "--version", labwcVersionRegex, []string{"labwc"}}, - {"mangowc", "mango", "-v", mangowcVersionRegex, []string{"mango"}}, - } - - var results []checkResult - foundAny := false - - for _, c := range compositors { - if !slices.ContainsFunc(c.commands, utils.CommandExists) { - continue - } - foundAny = true - var compositorPath string - for _, cmd := range c.commands { - if path, err := exec.LookPath(cmd); err == nil { - compositorPath = path - break - } - } - details := "" - if doctorVerbose && compositorPath != "" { - details = compositorPath - } - results = append(results, checkResult{ - catCompositor, c.name, statusOK, - getVersionFromCommand(c.versionCmd, c.versionArg, c.versionRegex), details, - doctorDocsURL + "#compositor-checks", - }) - } - - if !foundAny { - results = append(results, checkResult{ - catCompositor, "Compositor", statusError, - "No supported Wayland compositor found", - "Install Hyprland, niri, Sway, River, or Wayfire", - doctorDocsURL + "#compositor-checks", - }) - } - - if wm := detectRunningWM(); wm != "" { - results = append(results, checkResult{catCompositor, "Active", statusInfo, wm, "", doctorDocsURL + "#compositor"}) - } - - return results -} - -func getVersionFromCommand(cmd, arg string, regex *regexp.Regexp) string { - output, err := exec.Command(cmd, arg).CombinedOutput() - if err != nil && len(output) == 0 { - return "installed" - } - - outStr := string(output) - if matches := regex.FindStringSubmatch(outStr); len(matches) > 1 { - ver := matches[1] - if strings.Contains(outStr, "git") || strings.Contains(outStr, "dirty") { - return ver + " (git)" - } - return ver - } - return strings.TrimSpace(outStr) -} - -func detectRunningWM() string { - switch { - case os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "": - return "Hyprland" - case os.Getenv("NIRI_SOCKET") != "": - return "niri" - case os.Getenv("XDG_CURRENT_DESKTOP") != "": - return os.Getenv("XDG_CURRENT_DESKTOP") - } - return "" -} - -func checkQuickshellFeatures() ([]checkResult, bool) { - if !utils.CommandExists("qs") { - return nil, false - } - - tmpDir := os.TempDir() - testScript := filepath.Join(tmpDir, "qs-feature-test.qml") - defer os.Remove(testScript) - - qmlContent := ` -import QtQuick -import Quickshell - -ShellRoot { - id: root - - property bool polkitAvailable: false - property bool idleMonitorAvailable: false - property bool idleInhibitorAvailable: false - property bool shortcutInhibitorAvailable: false - - Timer { - interval: 50 - running: true - repeat: false - onTriggered: { - try { - var polkitTest = Qt.createQmlObject( - 'import Quickshell.Services.Polkit; import QtQuick; Item {}', - root - ) - root.polkitAvailable = true - polkitTest.destroy() - } catch (e) {} - - try { - var testItem = Qt.createQmlObject( - 'import Quickshell.Wayland; import QtQuick; QtObject { ' + - 'readonly property bool hasIdleMonitor: typeof IdleMonitor !== "undefined"; ' + - 'readonly property bool hasIdleInhibitor: typeof IdleInhibitor !== "undefined"; ' + - 'readonly property bool hasShortcutInhibitor: typeof ShortcutInhibitor !== "undefined" ' + - '}', - root - ) - root.idleMonitorAvailable = testItem.hasIdleMonitor - root.idleInhibitorAvailable = testItem.hasIdleInhibitor - root.shortcutInhibitorAvailable = testItem.hasShortcutInhibitor - testItem.destroy() - } catch (e) {} - - console.warn(root.polkitAvailable ? "FEATURE:Polkit:OK" : "FEATURE:Polkit:UNAVAILABLE") - console.warn(root.idleMonitorAvailable ? "FEATURE:IdleMonitor:OK" : "FEATURE:IdleMonitor:UNAVAILABLE") - console.warn(root.idleInhibitorAvailable ? "FEATURE:IdleInhibitor:OK" : "FEATURE:IdleInhibitor:UNAVAILABLE") - console.warn(root.shortcutInhibitorAvailable ? "FEATURE:ShortcutInhibitor:OK" : "FEATURE:ShortcutInhibitor:UNAVAILABLE") - - Quickshell.execDetached(["kill", "-TERM", String(Quickshell.processId)]) - } - } -} -` - - if err := os.WriteFile(testScript, []byte(qmlContent), 0o644); err != nil { - return nil, false - } - - cmd := exec.Command("qs", "-p", testScript) - cmd.Env = append(os.Environ(), "NO_COLOR=1") - output, _ := cmd.CombinedOutput() - outputStr := string(output) - - features := []struct{ name, desc string }{ - {"Polkit", "Authentication prompts"}, - {"IdleMonitor", "Idle detection"}, - {"IdleInhibitor", "Prevent idle/sleep"}, - {"ShortcutInhibitor", "Allow shortcut management (niri)"}, - } - - var results []checkResult - missingFeatures := false - - for _, f := range features { - available := strings.Contains(outputStr, fmt.Sprintf("FEATURE:%s:OK", f.name)) - status, message := statusOK, "Available" - if !available { - status, message = statusInfo, "Not available" - missingFeatures = true - } - results = append(results, checkResult{catQuickshellFeatures, f.name, status, message, f.desc, doctorDocsURL + "#quickshell-features"}) - } - - return results, missingFeatures -} - -func checkI2CAvailability() checkResult { - ddc, err := brightness.NewDDCBackend() - if err != nil { - return checkResult{catOptionalFeatures, "I2C/DDC", statusInfo, "Not available", "External monitor brightness control", doctorDocsURL + "#optional-features"} - } - defer ddc.Close() - - devices, err := ddc.GetDevices() - if err != nil || len(devices) == 0 { - return checkResult{catOptionalFeatures, "I2C/DDC", statusInfo, "No monitors detected", "External monitor brightness control", doctorDocsURL + "#optional-features"} - } - - return checkResult{catOptionalFeatures, "I2C/DDC", statusOK, fmt.Sprintf("%d monitor(s) detected", len(devices)), "External monitor brightness control", doctorDocsURL + "#optional-features"} -} - -func checkImageFormatPlugins() []checkResult { - url := doctorDocsURL + "#optional-features" - - pluginDirs := findQtPluginDirs() - if len(pluginDirs) == 0 { - return []checkResult{ - {catOptionalFeatures, "qt6-imageformats", statusInfo, "Cannot detect (plugin dir not found)", "WebP, TIFF, JP2 support", url}, - {catOptionalFeatures, "kimageformats", statusInfo, "Cannot detect (plugin dir not found)", "AVIF, HEIF, JXL support", url}, - } - } - - type pluginCheck struct { - name string - desc string - plugins []struct{ file, format string } - } - - checks := []pluginCheck{ - { - name: "qt6-imageformats", - desc: "WebP, TIFF, GIF, JP2 support", - plugins: []struct{ file, format string }{ - {"libqwebp.so", "WebP"}, - {"libqtiff.so", "TIFF"}, - {"libqgif.so", "GIF"}, - {"libqjp2.so", "JP2"}, - {"libqicns.so", "ICNS"}, - }, - }, - { - name: "kimageformats", - desc: "AVIF, HEIF, JXL support", - plugins: []struct{ file, format string }{ - {"kimg_avif.so", "AVIF"}, - {"kimg_heif.so", "HEIF"}, - {"kimg_jxl.so", "JXL"}, - {"kimg_exr.so", "EXR"}, - }, - }, - } - - var results []checkResult - for _, c := range checks { - var found []string - var foundDirs []string - for _, pluginDir := range pluginDirs { - imageFormatsDir := filepath.Join(pluginDir, "imageformats") - for _, p := range c.plugins { - if _, err := os.Stat(filepath.Join(imageFormatsDir, p.file)); err == nil { - if !slices.Contains(found, p.format) { - found = append(found, p.format) - } - if !slices.Contains(foundDirs, imageFormatsDir) { - foundDirs = append(foundDirs, imageFormatsDir) - } - } - } - } - - var result checkResult - switch { - case len(found) == 0: - result = checkResult{catOptionalFeatures, c.name, statusWarn, "Not installed", c.desc, url} - default: - details := "" - if doctorVerbose { - details = fmt.Sprintf("Formats: %s (%s)", strings.Join(found, ", "), strings.Join(foundDirs, ":")) - } - result = checkResult{catOptionalFeatures, c.name, statusOK, fmt.Sprintf("Installed (%d formats)", len(found)), details, url} - } - results = append(results, result) - } - - return results -} - -func findQtPluginDirs() []string { - var dirs []string - - addDir := func(dir string) { - if dir != "" { - if _, err := os.Stat(filepath.Join(dir, "imageformats")); err == nil { - dirs = append(dirs, dir) - } - } - } - - // Check all paths in QT_PLUGIN_PATH env var (used by NixOS and custom setups) - if envPath := os.Getenv("QT_PLUGIN_PATH"); envPath != "" { - for dir := range strings.SplitSeq(envPath, ":") { - addDir(dir) - } - } - - // Try qtpaths - for _, cmd := range []string{"qtpaths6", "qtpaths"} { - if output, err := exec.Command(cmd, "-query", "QT_INSTALL_PLUGINS").Output(); err == nil { - addDir(strings.TrimSpace(string(output))) - } - } - - // Fallback: common distro paths - for _, dir := range []string{ - "/usr/lib/qt6/plugins", - "/usr/lib64/qt6/plugins", - "/usr/lib/x86_64-linux-gnu/qt6/plugins", - "/usr/lib/aarch64-linux-gnu/qt6/plugins", - } { - addDir(dir) - } - - return dirs -} - -func detectNetworkBackend(stackResult *network.DetectResult) string { - switch stackResult.Backend { - case network.BackendNetworkManager: - return "NetworkManager" - case network.BackendIwd: - return "iwd" - case network.BackendNetworkd: - if stackResult.HasIwd { - return "iwd + systemd-networkd" - } - return "systemd-networkd" - case network.BackendConnMan: - return "ConnMan" - default: - return "" - } -} - -func getOptionalDBusStatus(busName string) (status, string) { - if utils.IsDBusServiceAvailable(busName) { - return statusOK, "Available" - } else { - return statusWarn, "Not available" - } -} - -func checkOptionalDependencies() []checkResult { - var results []checkResult - - optionalFeaturesURL := doctorDocsURL + "#optional-features" - - accountsStatus, accountsMsg := getOptionalDBusStatus("org.freedesktop.Accounts") - results = append(results, checkResult{catOptionalFeatures, "accountsservice", accountsStatus, accountsMsg, "User accounts", optionalFeaturesURL}) - - ppdStatus, ppdMsg := getOptionalDBusStatus("org.freedesktop.UPower.PowerProfiles") - results = append(results, checkResult{catOptionalFeatures, "power-profiles-daemon", ppdStatus, ppdMsg, "Power profile management", optionalFeaturesURL}) - - logindStatus, logindMsg := getOptionalDBusStatus("org.freedesktop.login1") - results = append(results, checkResult{catOptionalFeatures, "logind", logindStatus, logindMsg, "Session management", optionalFeaturesURL}) - - cupsPkHelperBus := "org.opensuse.CupsPkHelper.Mechanism" - var cupsPkStatus status - var cupsPkMsg string - switch { - case utils.IsDBusServiceAvailable(cupsPkHelperBus): - cupsPkStatus, cupsPkMsg = statusOK, "Running" - case utils.IsDBusServiceActivatable(cupsPkHelperBus): - cupsPkStatus, cupsPkMsg = statusOK, "Available" - default: - cupsPkStatus, cupsPkMsg = statusWarn, "Not available (install cups-pk-helper)" - } - results = append(results, checkResult{catOptionalFeatures, "cups-pk-helper", cupsPkStatus, cupsPkMsg, "Printer management", optionalFeaturesURL}) - - results = append(results, checkI2CAvailability()) - results = append(results, checkImageFormatPlugins()...) - - terminals := []string{"ghostty", "kitty", "alacritty", "foot", "wezterm"} - terminals = slices.DeleteFunc(terminals, func(t string) bool { - return !utils.CommandExists(t) - }) - - if len(terminals) > 0 { - results = append(results, checkResult{catOptionalFeatures, "Terminal", statusOK, strings.Join(terminals, ", "), "", optionalFeaturesURL}) - } else { - results = append(results, checkResult{catOptionalFeatures, "Terminal", statusWarn, "None found", "Install ghostty, kitty, foot or alacritty", optionalFeaturesURL}) - } - - networkResult, err := network.DetectNetworkStack() - networkStatus, networkMessage, networkDetails := statusOK, "Not available", "Network management" - - if err == nil && networkResult.Backend != network.BackendNone { - networkMessage = detectNetworkBackend(networkResult) - if doctorVerbose { - networkDetails = networkResult.ChosenReason - } - } else { - networkStatus = statusInfo - } - - results = append(results, checkResult{catOptionalFeatures, "Network", networkStatus, networkMessage, networkDetails, optionalFeaturesURL}) - - deps := []struct { - name, cmd, desc string - important bool - }{ - {"matugen", "matugen", "Dynamic theming", true}, - {"dgop", "dgop", "System monitoring", true}, - {"cava", "cava", "Audio visualizer", true}, - {"khal", "khal", "Calendar events", false}, - {"danksearch", "dsearch", "File search", false}, - {"fprintd", "fprintd-list", "Fingerprint auth", false}, - } - - for _, d := range deps { - found := utils.CommandExists(d.cmd) - - switch { - case found: - results = append(results, checkResult{catOptionalFeatures, d.name, statusOK, "Installed", d.desc, optionalFeaturesURL}) - case d.important: - results = append(results, checkResult{catOptionalFeatures, d.name, statusWarn, "Missing", d.desc, optionalFeaturesURL}) - default: - results = append(results, checkResult{catOptionalFeatures, d.name, statusInfo, "Not installed", d.desc, optionalFeaturesURL}) - } - } - - return results -} - -func checkConfigurationFiles() []checkResult { - configDir, _ := os.UserConfigDir() - cacheDir, _ := os.UserCacheDir() - dmsDir := "DankMaterialShell" - - configFiles := []struct{ name, path string }{ - {"settings.json", filepath.Join(configDir, dmsDir, "settings.json")}, - {"clsettings.json", filepath.Join(configDir, dmsDir, "clsettings.json")}, - {"plugin_settings.json", filepath.Join(configDir, dmsDir, "plugin_settings.json")}, - {"session.json", filepath.Join(utils.XDGStateHome(), dmsDir, "session.json")}, - {"dms-colors.json", filepath.Join(cacheDir, dmsDir, "dms-colors.json")}, - } - - var results []checkResult - for _, cf := range configFiles { - info, err := os.Stat(cf.path) - if err != nil { - results = append(results, checkResult{catConfigFiles, cf.name, statusInfo, "Not yet created", cf.path, doctorDocsURL + "#config-files"}) - continue - } - - status := statusOK - message := "Present" - if info.Mode().Perm()&0o200 == 0 { - status = statusWarn - message += " (read-only)" - } - results = append(results, checkResult{catConfigFiles, cf.name, status, message, cf.path, doctorDocsURL + "#config-files"}) - } - return results -} - -func checkSystemdServices() []checkResult { - if !utils.CommandExists("systemctl") { - return nil - } - - var results []checkResult - - dmsState := getServiceState("dms", true) - if !dmsState.exists { - results = append(results, checkResult{catServices, "dms.service", statusInfo, "Not installed", "Optional user service", doctorDocsURL + "#services"}) - } else { - status, message := statusOK, dmsState.enabled - if dmsState.active != "" { - message = fmt.Sprintf("%s, %s", dmsState.enabled, dmsState.active) - } - switch { - case dmsState.enabled == "disabled": - status, message = statusWarn, "Disabled" - case dmsState.active == "failed" || dmsState.active == "inactive": - status = statusError - } - results = append(results, checkResult{catServices, "dms.service", status, message, "", doctorDocsURL + "#services"}) - } - - greetdState := getServiceState("greetd", false) - switch { - case greetdState.exists: - status := statusOK - if greetdState.enabled == "disabled" { - status = statusInfo - } - results = append(results, checkResult{catServices, "greetd", status, greetdState.enabled, "", doctorDocsURL + "#services"}) - case doctorVerbose: - results = append(results, checkResult{catServices, "greetd", statusInfo, "Not installed", "Optional greeter service", doctorDocsURL + "#services"}) - } - - return results -} - -type serviceState struct { - exists bool - enabled string - active string -} - -func getServiceState(name string, userService bool) serviceState { - args := []string{"is-enabled", name} - if userService { - args = []string{"--user", "is-enabled", name} - } - - output, _ := exec.Command("systemctl", args...).Output() - enabled := strings.TrimSpace(string(output)) - - if enabled == "" || enabled == "not-found" { - return serviceState{} - } - - state := serviceState{exists: true, enabled: enabled} - - if userService { - output, _ = exec.Command("systemctl", "--user", "is-active", name).Output() - if active := strings.TrimSpace(string(output)); active != "" && active != "unknown" { - state.active = active - } - } - - return state -} - -func printResults(results []checkResult) { - theme := tui.TerminalTheme() - styles := tui.NewStyles(theme) - - currentCategory := category(-1) - for _, r := range results { - if r.category != currentCategory { - if currentCategory != -1 { - fmt.Println() - } - fmt.Printf(" %s\n", styles.Bold.Render(r.category.String())) - currentCategory = r.category - } - printResultLine(r, styles) - } -} - -func printResultsJSON(results []checkResult) { - var ds DoctorStatus - for _, r := range results { - ds.Add(r) - } - - output := doctorOutputJSON{} - output.Summary.Errors = ds.ErrorCount() - output.Summary.Warnings = ds.WarningCount() - output.Summary.OK = ds.OKCount() - output.Summary.Info = len(ds.Info) - - output.Results = make([]checkResultJSON, 0, len(results)) - for _, r := range results { - output.Results = append(output.Results, r.toJSON()) - } - - encoder := json.NewEncoder(os.Stdout) - encoder.SetIndent("", " ") - if err := encoder.Encode(output); err != nil { - fmt.Fprintf(os.Stderr, "Error encoding JSON: %v\n", err) - os.Exit(1) - } -} - -func printResultLine(r checkResult, styles tui.Styles) { - icon, style := r.status.IconStyle(styles) - - name := r.name - nameLen := len(name) - - if nameLen > checkNameMaxLength { - name = name[:checkNameMaxLength-1] + "…" - nameLen = checkNameMaxLength - } - dots := strings.Repeat("·", checkNameMaxLength-nameLen) - - fmt.Printf(" %s %s %s %s\n", style.Render(icon), name, styles.Subtle.Render(dots), r.message) - - if doctorVerbose && r.details != "" { - fmt.Printf(" %s\n", styles.Subtle.Render("└─ "+r.details)) - } - - if (r.status == statusError || r.status == statusWarn) && r.url != "" { - fmt.Printf(" %s\n", styles.Subtle.Render("→ "+r.url)) - } -} - -func printSummary(results []checkResult, qsMissingFeatures bool) { - theme := tui.TerminalTheme() - styles := tui.NewStyles(theme) - - var ds DoctorStatus - for _, r := range results { - ds.Add(r) - } - - fmt.Println() - fmt.Printf(" %s\n", styles.Subtle.Render("──────────────────────────────────────")) - - if !ds.HasIssues() { - fmt.Printf(" %s\n", styles.Success.Render("✓ All checks passed!")) - } else { - var parts []string - - if ds.ErrorCount() > 0 { - parts = append(parts, styles.Error.Render(fmt.Sprintf("%d error(s)", ds.ErrorCount()))) - } - if ds.WarningCount() > 0 { - parts = append(parts, styles.Warning.Render(fmt.Sprintf("%d warning(s)", ds.WarningCount()))) - } - parts = append(parts, styles.Success.Render(fmt.Sprintf("%d ok", ds.OKCount()))) - fmt.Printf(" %s\n", strings.Join(parts, ", ")) - - if qsMissingFeatures { - fmt.Println() - fmt.Printf(" %s\n", styles.Subtle.Render("→ Consider using quickshell-git for full feature support")) - } - } - fmt.Println() -} - -func formatResultsPlain(results []checkResult) string { - var sb strings.Builder - sb.WriteString("## DMS Doctor Report\n\n") - - currentCategory := category(-1) - for _, r := range results { - if r.category != currentCategory { - if currentCategory != -1 { - sb.WriteString("\n") - } - fmt.Fprintf(&sb, "**%s**\n", r.category.String()) - currentCategory = r.category - } - - fmt.Fprintf(&sb, "- [%s] %s: %s\n", r.status, r.name, r.message) - - if doctorVerbose && r.details != "" { - fmt.Fprintf(&sb, " - %s\n", r.details) - } - } - - var ds DoctorStatus - for _, r := range results { - ds.Add(r) - } - - sb.WriteString("\n---\n") - fmt.Fprintf(&sb, "**Summary:** %d error(s), %d warning(s), %d ok\n", - ds.ErrorCount(), ds.WarningCount(), ds.OKCount()) - - return sb.String() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_download.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_download.go deleted file mode 100644 index e9caeb0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_download.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io" - "net" - "net/http" - "os" - "path/filepath" - "time" - - "github.com/spf13/cobra" -) - -var dlOutput string -var dlUserAgent string -var dlTimeout int -var dlIPv4Only bool - -var dlCmd = &cobra.Command{ - Use: "dl ", - Short: "Download a URL to stdout or file", - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - if err := runDownload(args[0]); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - }, -} - -func init() { - dlCmd.Flags().StringVarP(&dlOutput, "output", "o", "", "Output file path (default: stdout)") - dlCmd.Flags().StringVar(&dlUserAgent, "user-agent", "", "Custom User-Agent header") - dlCmd.Flags().IntVar(&dlTimeout, "timeout", 10, "Request timeout in seconds") - dlCmd.Flags().BoolVarP(&dlIPv4Only, "ipv4", "4", false, "Force IPv4 only") -} - -func runDownload(url string) error { - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(dlTimeout)*time.Second) - defer cancel() - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("invalid request: %w", err) - } - - switch { - case dlUserAgent != "": - req.Header.Set("User-Agent", dlUserAgent) - default: - req.Header.Set("User-Agent", "DankMaterialShell/1.0 (Linux)") - } - - dialer := &net.Dialer{Timeout: 5 * time.Second} - transport := &http.Transport{DialContext: dialer.DialContext} - if dlIPv4Only { - transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { - return dialer.DialContext(ctx, "tcp4", addr) - } - } - client := &http.Client{Transport: transport} - - resp, err := client.Do(req) - if err != nil { - return fmt.Errorf("download failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return fmt.Errorf("HTTP %d", resp.StatusCode) - } - - if dlOutput == "" { - _, err = io.Copy(os.Stdout, resp.Body) - return err - } - - if dir := filepath.Dir(dlOutput); dir != "." { - if err := os.MkdirAll(dir, 0755); err != nil { - return fmt.Errorf("mkdir failed: %w", err) - } - } - - f, err := os.Create(dlOutput) - if err != nil { - return fmt.Errorf("create failed: %w", err) - } - defer f.Close() - - if _, err := io.Copy(f, resp.Body); err != nil { - os.Remove(dlOutput) - return fmt.Errorf("write failed: %w", err) - } - - fmt.Println(dlOutput) - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_dpms.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_dpms.go deleted file mode 100644 index 77eff1a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_dpms.go +++ /dev/null @@ -1,105 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/spf13/cobra" -) - -var dpmsCmd = &cobra.Command{ - Use: "dpms", - Short: "Control display power management", -} - -var dpmsOnCmd = &cobra.Command{ - Use: "on [output]", - Short: "Turn display(s) on", - Args: cobra.MaximumNArgs(1), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return getDPMSOutputs(), cobra.ShellCompDirectiveNoFileComp - }, - Run: runDPMSOn, -} - -var dpmsOffCmd = &cobra.Command{ - Use: "off [output]", - Short: "Turn display(s) off", - Args: cobra.MaximumNArgs(1), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return getDPMSOutputs(), cobra.ShellCompDirectiveNoFileComp - }, - Run: runDPMSOff, -} - -var dpmsListCmd = &cobra.Command{ - Use: "list", - Short: "List outputs", - Args: cobra.NoArgs, - Run: runDPMSList, -} - -func init() { - dpmsCmd.AddCommand(dpmsOnCmd, dpmsOffCmd, dpmsListCmd) -} - -func runDPMSOn(cmd *cobra.Command, args []string) { - outputName := "" - if len(args) > 0 { - outputName = args[0] - } - - client, err := newDPMSClient() - if err != nil { - log.Fatalf("%v", err) - } - defer client.Close() - - if err := client.SetDPMS(outputName, true); err != nil { - log.Fatalf("%v", err) - } -} - -func runDPMSOff(cmd *cobra.Command, args []string) { - outputName := "" - if len(args) > 0 { - outputName = args[0] - } - - client, err := newDPMSClient() - if err != nil { - log.Fatalf("%v", err) - } - defer client.Close() - - if err := client.SetDPMS(outputName, false); err != nil { - log.Fatalf("%v", err) - } -} - -func getDPMSOutputs() []string { - client, err := newDPMSClient() - if err != nil { - return nil - } - defer client.Close() - return client.ListOutputs() -} - -func runDPMSList(cmd *cobra.Command, args []string) { - client, err := newDPMSClient() - if err != nil { - log.Fatalf("%v", err) - } - defer client.Close() - - for _, output := range client.ListOutputs() { - fmt.Println(output) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_features.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_features.go deleted file mode 100644 index 7ec5437..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_features.go +++ /dev/null @@ -1,491 +0,0 @@ -//go:build !distro_binary - -package main - -import ( - "bufio" - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/AvengeMedia/DankMaterialShell/core/internal/version" - "github.com/spf13/cobra" -) - -var updateCmd = &cobra.Command{ - Use: "update", - Short: "Update DankMaterialShell to the latest version", - Long: "Update DankMaterialShell to the latest version using the appropriate package manager for your distribution", - PreRunE: findConfig, - Run: func(cmd *cobra.Command, args []string) { - runUpdate() - }, -} - -var updateCheckCmd = &cobra.Command{ - Use: "check", - Short: "Check if updates are available for DankMaterialShell", - Long: "Check for available updates without performing the actual update", - Run: func(cmd *cobra.Command, args []string) { - runUpdateCheck() - }, -} - -func runUpdateCheck() { - fmt.Println("Checking for DankMaterialShell updates...") - fmt.Println() - - versionInfo, err := version.GetDMSVersionInfo() - if err != nil { - log.Fatalf("Error checking for updates: %v", err) - } - - fmt.Printf("Current version: %s\n", versionInfo.Current) - fmt.Printf("Latest version: %s\n", versionInfo.Latest) - fmt.Println() - - if versionInfo.HasUpdate { - fmt.Println("✓ Update available!") - fmt.Println() - fmt.Println("Run 'dms update' to install the latest version.") - os.Exit(0) - } else { - fmt.Println("✓ You are running the latest version.") - os.Exit(0) - } -} - -func runUpdate() { - osInfo, err := distros.GetOSInfo() - if err != nil { - log.Fatalf("Error detecting OS: %v", err) - } - - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - log.Fatalf("Unsupported distribution: %s", osInfo.Distribution.ID) - } - - var updateErr error - switch config.Family { - case distros.FamilyArch: - updateErr = updateArchLinux() - case distros.FamilySUSE: - updateErr = updateOtherDistros() - default: - updateErr = updateOtherDistros() - } - - if updateErr != nil { - if errors.Is(updateErr, errdefs.ErrUpdateCancelled) { - log.Info("Update cancelled.") - return - } - if errors.Is(updateErr, errdefs.ErrNoUpdateNeeded) { - return - } - log.Fatalf("Error updating DMS: %v", updateErr) - } - - log.Info("Update complete! Restarting DMS...") - restartShell() -} - -func updateArchLinux() error { - homeDir, err := os.UserHomeDir() - if err == nil { - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - if _, err := os.Stat(dmsPath); err == nil { - return updateOtherDistros() - } - } - - var packageName string - var isAUR bool - if isArchPackageInstalled("dms-shell") { - packageName = "dms-shell" - } else if isArchPackageInstalled("dms-shell-git") { - packageName = "dms-shell-git" - isAUR = true - } else if isArchPackageInstalled("dms-shell-bin") { - packageName = "dms-shell-bin" - isAUR = true - } else { - fmt.Println("Info: No dms-shell package found.") - fmt.Println("Info: Falling back to git-based update method...") - return updateOtherDistros() - } - - if !isAUR { - fmt.Printf("This will update %s using pacman.\n", packageName) - if !confirmUpdate() { - return errdefs.ErrUpdateCancelled - } - - fmt.Printf("\nRunning: sudo pacman -S %s\n", packageName) - cmd := exec.Command("sudo", "pacman", "-S", "--noconfirm", packageName) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - fmt.Printf("Error: Failed to update using pacman: %v\n", err) - return err - } - - fmt.Println("dms successfully updated") - return nil - } - - var helper string - var updateCmd *exec.Cmd - - if utils.CommandExists("yay") { - helper = "yay" - updateCmd = exec.Command("yay", "-S", packageName) - } else if utils.CommandExists("paru") { - helper = "paru" - updateCmd = exec.Command("paru", "-S", packageName) - } else { - fmt.Println("Error: Neither yay nor paru found - please install an AUR helper") - fmt.Println("Info: Falling back to git-based update method...") - return updateOtherDistros() - } - - fmt.Printf("This will update DankMaterialShell using %s.\n", helper) - if !confirmUpdate() { - return errdefs.ErrUpdateCancelled - } - - fmt.Printf("\nRunning: %s -S %s\n", helper, packageName) - updateCmd.Stdout = os.Stdout - updateCmd.Stderr = os.Stderr - err = updateCmd.Run() - if err != nil { - fmt.Printf("Error: Failed to update using %s: %v\n", helper, err) - } - - fmt.Println("dms successfully updated") - return nil -} - -func updateOtherDistros() error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - - if _, err := os.Stat(dmsPath); os.IsNotExist(err) { - return fmt.Errorf("DMS configuration directory not found at %s", dmsPath) - } - - fmt.Printf("Found DMS configuration at %s\n", dmsPath) - - versionInfo, err := version.GetDMSVersionInfo() - if err == nil && !versionInfo.HasUpdate { - fmt.Println() - fmt.Printf("Current version: %s\n", versionInfo.Current) - fmt.Printf("Latest version: %s\n", versionInfo.Latest) - fmt.Println() - fmt.Println("✓ You are already running the latest version.") - return errdefs.ErrNoUpdateNeeded - } - - fmt.Println("\nThis will update:") - fmt.Println(" 1. The dms binary from GitHub releases") - fmt.Println(" 2. DankMaterialShell configuration using git") - if !confirmUpdate() { - return errdefs.ErrUpdateCancelled - } - - fmt.Println("\n=== Updating dms binary ===") - if err := updateDMSBinary(); err != nil { - fmt.Printf("Warning: Failed to update dms binary: %v\n", err) - fmt.Println("Continuing with shell configuration update...") - } else { - fmt.Println("dms binary successfully updated") - } - - fmt.Println("\n=== Updating DMS shell configuration ===") - - if err := os.Chdir(dmsPath); err != nil { - return fmt.Errorf("failed to change to DMS directory: %w", err) - } - - statusCmd := exec.Command("git", "status", "--porcelain") - statusOutput, _ := statusCmd.Output() - hasLocalChanges := len(strings.TrimSpace(string(statusOutput))) > 0 - - currentRefCmd := exec.Command("git", "symbolic-ref", "-q", "HEAD") - currentRefOutput, _ := currentRefCmd.Output() - onBranch := len(currentRefOutput) > 0 - - var currentTag string - var currentBranch string - - if !onBranch { - tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD") - if tagOutput, err := tagCmd.Output(); err == nil { - currentTag = strings.TrimSpace(string(tagOutput)) - } - } else { - branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") - if branchOutput, err := branchCmd.Output(); err == nil { - currentBranch = strings.TrimSpace(string(branchOutput)) - } - } - - fmt.Println("Fetching latest changes...") - fetchCmd := exec.Command("git", "fetch", "origin", "--tags", "--force") - fetchCmd.Stdout = os.Stdout - fetchCmd.Stderr = os.Stderr - if err := fetchCmd.Run(); err != nil { - return fmt.Errorf("failed to fetch changes: %w", err) - } - - if currentTag != "" { - latestTagCmd := exec.Command("git", "tag", "-l", "v*", "--sort=-version:refname") - latestTagOutput, err := latestTagCmd.Output() - if err != nil { - return fmt.Errorf("failed to get latest tag: %w", err) - } - - tags := strings.Split(strings.TrimSpace(string(latestTagOutput)), "\n") - if len(tags) == 0 || tags[0] == "" { - return fmt.Errorf("no version tags found") - } - latestTag := tags[0] - - if latestTag == currentTag { - fmt.Printf("Already on latest tag: %s\n", currentTag) - return nil - } - - fmt.Printf("Current tag: %s\n", currentTag) - fmt.Printf("Latest tag: %s\n", latestTag) - - if hasLocalChanges { - fmt.Println("\nWarning: You have local changes in your DMS configuration.") - if offerReclone(dmsPath) { - return nil - } - return errdefs.ErrUpdateCancelled - } - - fmt.Printf("Updating to %s...\n", latestTag) - checkoutCmd := exec.Command("git", "checkout", latestTag) - checkoutCmd.Stdout = os.Stdout - checkoutCmd.Stderr = os.Stderr - if err := checkoutCmd.Run(); err != nil { - fmt.Printf("Error: Failed to checkout %s: %v\n", latestTag, err) - if offerReclone(dmsPath) { - return nil - } - return fmt.Errorf("update cancelled") - } - - fmt.Printf("\nUpdate complete! Updated from %s to %s\n", currentTag, latestTag) - return nil - } - - if currentBranch == "" { - currentBranch = "master" - } - - fmt.Printf("Current branch: %s\n", currentBranch) - - if hasLocalChanges { - fmt.Println("\nWarning: You have local changes in your DMS configuration.") - if offerReclone(dmsPath) { - return nil - } - return errdefs.ErrUpdateCancelled - } - - pullCmd := exec.Command("git", "pull", "origin", currentBranch) - pullCmd.Stdout = os.Stdout - pullCmd.Stderr = os.Stderr - if err := pullCmd.Run(); err != nil { - fmt.Printf("Error: Failed to pull latest changes: %v\n", err) - if offerReclone(dmsPath) { - return nil - } - return fmt.Errorf("update cancelled") - } - - fmt.Println("\nUpdate complete!") - return nil -} - -func offerReclone(dmsPath string) bool { - fmt.Println("\nWould you like to backup and re-clone the repository? (y/N): ") - reader := bufio.NewReader(os.Stdin) - response, err := reader.ReadString('\n') - if err != nil || !strings.HasPrefix(strings.ToLower(strings.TrimSpace(response)), "y") { - return false - } - - timestamp := time.Now().Unix() - backupPath := fmt.Sprintf("%s.backup-%d", dmsPath, timestamp) - - fmt.Printf("Backing up current directory to %s...\n", backupPath) - if err := os.Rename(dmsPath, backupPath); err != nil { - fmt.Printf("Error: Failed to backup directory: %v\n", err) - return false - } - - fmt.Println("Cloning fresh copy...") - cloneCmd := exec.Command("git", "clone", "https://github.com/AvengeMedia/DankMaterialShell.git", dmsPath) - cloneCmd.Stdout = os.Stdout - cloneCmd.Stderr = os.Stderr - if err := cloneCmd.Run(); err != nil { - fmt.Printf("Error: Failed to clone repository: %v\n", err) - fmt.Printf("Restoring backup...\n") - os.Rename(backupPath, dmsPath) - return false - } - - fmt.Printf("Successfully re-cloned repository (backup at %s)\n", backupPath) - return true -} - -func confirmUpdate() bool { - fmt.Print("Do you want to proceed with the update? (y/N): ") - reader := bufio.NewReader(os.Stdin) - response, err := reader.ReadString('\n') - if err != nil { - fmt.Printf("Error reading input: %v\n", err) - return false - } - response = strings.TrimSpace(strings.ToLower(response)) - return response == "y" || response == "yes" -} - -func updateDMSBinary() error { - arch := "" - switch strings.ToLower(os.Getenv("HOSTTYPE")) { - case "x86_64", "amd64": - arch = "amd64" - case "aarch64", "arm64": - arch = "arm64" - default: - cmd := exec.Command("uname", "-m") - output, err := cmd.Output() - if err != nil { - return fmt.Errorf("failed to detect architecture: %w", err) - } - archStr := strings.TrimSpace(string(output)) - switch archStr { - case "x86_64": - arch = "amd64" - case "aarch64": - arch = "arm64" - default: - return fmt.Errorf("unsupported architecture: %s", archStr) - } - } - - fmt.Println("Fetching latest release version...") - cmd := exec.Command("curl", "-s", "https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest") - output, err := cmd.Output() - if err != nil { - return fmt.Errorf("failed to fetch latest release: %w", err) - } - - version := "" - for line := range strings.SplitSeq(string(output), "\n") { - if strings.Contains(line, "\"tag_name\"") { - parts := strings.Split(line, "\"") - if len(parts) >= 4 { - version = parts[3] - break - } - } - } - - if version == "" { - return fmt.Errorf("could not determine latest version") - } - - fmt.Printf("Latest version: %s\n", version) - - tempDir, err := os.MkdirTemp("", "dms-update-*") - if err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tempDir) - - binaryURL := fmt.Sprintf("https://github.com/AvengeMedia/DankMaterialShell/releases/download/%s/dms-cli-%s.gz", version, arch) - checksumURL := fmt.Sprintf("https://github.com/AvengeMedia/DankMaterialShell/releases/download/%s/dms-cli-%s.gz.sha256", version, arch) - - binaryPath := filepath.Join(tempDir, "dms.gz") - checksumPath := filepath.Join(tempDir, "dms.gz.sha256") - - fmt.Println("Downloading dms binary...") - downloadCmd := exec.Command("curl", "-L", binaryURL, "-o", binaryPath) - if err := downloadCmd.Run(); err != nil { - return fmt.Errorf("failed to download binary: %w", err) - } - - fmt.Println("Downloading checksum...") - downloadCmd = exec.Command("curl", "-L", checksumURL, "-o", checksumPath) - if err := downloadCmd.Run(); err != nil { - return fmt.Errorf("failed to download checksum: %w", err) - } - - fmt.Println("Verifying checksum...") - checksumData, err := os.ReadFile(checksumPath) - if err != nil { - return fmt.Errorf("failed to read checksum file: %w", err) - } - expectedChecksum := strings.Fields(string(checksumData))[0] - - actualCmd := exec.Command("sha256sum", binaryPath) - actualOutput, err := actualCmd.Output() - if err != nil { - return fmt.Errorf("failed to calculate checksum: %w", err) - } - actualChecksum := strings.Fields(string(actualOutput))[0] - - if expectedChecksum != actualChecksum { - return fmt.Errorf("checksum verification failed\nExpected: %s\nGot: %s", expectedChecksum, actualChecksum) - } - - fmt.Println("Decompressing binary...") - decompressCmd := exec.Command("gunzip", binaryPath) - if err := decompressCmd.Run(); err != nil { - return fmt.Errorf("failed to decompress binary: %w", err) - } - - decompressedPath := filepath.Join(tempDir, "dms") - - if err := os.Chmod(decompressedPath, 0o755); err != nil { - return fmt.Errorf("failed to make binary executable: %w", err) - } - - currentPath, err := exec.LookPath("dms") - if err != nil { - return fmt.Errorf("could not find current dms binary: %w", err) - } - - fmt.Printf("Installing to %s...\n", currentPath) - - replaceCmd := exec.Command("sudo", "install", "-m", "0755", decompressedPath, currentPath) - replaceCmd.Stdin = os.Stdin - replaceCmd.Stdout = os.Stdout - replaceCmd.Stderr = os.Stderr - if err := replaceCmd.Run(); err != nil { - return fmt.Errorf("failed to replace binary: %w", err) - } - - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_greeter.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_greeter.go deleted file mode 100644 index 1efa545..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_greeter.go +++ /dev/null @@ -1,1850 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "os" - "os/exec" - "os/user" - "path/filepath" - "strconv" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/AvengeMedia/DankMaterialShell/core/internal/greeter" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - sharedpam "github.com/AvengeMedia/DankMaterialShell/core/internal/pam" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/spf13/cobra" - "golang.org/x/text/cases" - "golang.org/x/text/language" -) - -var greeterCmd = &cobra.Command{ - Use: "greeter", - Short: "Manage DMS greeter", - Long: "Manage DMS greeter (greetd)", -} - -var ( - greeterConfigSyncFn = greeter.SyncDMSConfigs - sharedAuthSyncFn = sharedpam.SyncAuthConfig -) - -var greeterInstallCmd = &cobra.Command{ - Use: "install", - Short: "Install and configure DMS greeter", - Long: "Install greetd and configure it to use DMS as the greeter interface", - PreRunE: requireMutableSystemCommand, - Run: func(cmd *cobra.Command, args []string) { - yes, _ := cmd.Flags().GetBool("yes") - term, _ := cmd.Flags().GetBool("terminal") - if term { - installCmd := "dms greeter install" - if yes { - installCmd += " --yes" - } - installCmd += "; echo; echo \"Install finished. Closing in 3 seconds...\"; sleep 3" - if err := runCommandInTerminal(installCmd); err != nil { - log.Fatalf("Error launching install in terminal: %v", err) - } - return - } - if err := installGreeter(yes); err != nil { - log.Fatalf("Error installing greeter: %v", err) - } - }, -} - -var greeterSyncCmd = &cobra.Command{ - Use: "sync", - Short: "Sync DMS theme and settings with greeter", - Long: "Synchronize your current user's DMS theme, settings, and wallpaper configuration with the login greeter screen", - Run: func(cmd *cobra.Command, args []string) { - yes, _ := cmd.Flags().GetBool("yes") - auth, _ := cmd.Flags().GetBool("auth") - local, _ := cmd.Flags().GetBool("local") - term, _ := cmd.Flags().GetBool("terminal") - if term { - if err := syncInTerminal(yes, auth, local); err != nil { - log.Fatalf("Error launching sync in terminal: %v", err) - } - return - } - if err := syncGreeter(yes, auth, local); err != nil { - log.Fatalf("Error syncing greeter: %v", err) - } - }, -} - -func init() { - greeterSyncCmd.Flags().BoolP("yes", "y", false, "Non-interactive mode: skip prompts, use defaults (for UI)") - greeterSyncCmd.Flags().BoolP("terminal", "t", false, "Run sync in a new terminal (for entering sudo password); terminal auto-closes when done") - greeterSyncCmd.Flags().BoolP("auth", "a", false, "Configure PAM for fingerprint and U2F (adds both if modules exist); overrides UI toggles") - greeterSyncCmd.Flags().BoolP("local", "l", false, "Developer mode: force greetd config to use a local DMS checkout path") -} - -var greeterEnableCmd = &cobra.Command{ - Use: "enable", - Short: "Enable DMS greeter in greetd config", - Long: "Configure greetd to use DMS as the greeter", - PreRunE: requireMutableSystemCommand, - Run: func(cmd *cobra.Command, args []string) { - yes, _ := cmd.Flags().GetBool("yes") - term, _ := cmd.Flags().GetBool("terminal") - if term { - enableCmd := "dms greeter enable" - if yes { - enableCmd += " --yes" - } - enableCmd += "; echo; echo \"Enable finished. Closing in 3 seconds...\"; sleep 3" - if err := runCommandInTerminal(enableCmd); err != nil { - log.Fatalf("Error launching enable in terminal: %v", err) - } - return - } - if err := enableGreeter(yes); err != nil { - log.Fatalf("Error enabling greeter: %v", err) - } - }, -} - -var greeterStatusCmd = &cobra.Command{ - Use: "status", - Short: "Check greeter sync status", - Long: "Check the status of greeter installation and configuration sync", - Run: func(cmd *cobra.Command, args []string) { - if err := checkGreeterStatus(); err != nil { - log.Fatalf("Error checking greeter status: %v", err) - } - }, -} - -var greeterUninstallCmd = &cobra.Command{ - Use: "uninstall", - Short: "Remove DMS greeter configuration and restore previous display manager", - Long: "Disable greetd, remove DMS managed configs, and restore the system to its pre-DMS-greeter state", - PreRunE: requireMutableSystemCommand, - Run: func(cmd *cobra.Command, args []string) { - yes, _ := cmd.Flags().GetBool("yes") - term, _ := cmd.Flags().GetBool("terminal") - if term { - uninstallCmd := "dms greeter uninstall" - if yes { - uninstallCmd += " --yes" - } - uninstallCmd += "; echo; echo \"Uninstall finished. Closing in 3 seconds...\"; sleep 3" - if err := runCommandInTerminal(uninstallCmd); err != nil { - log.Fatalf("Error launching uninstall in terminal: %v", err) - } - return - } - if err := uninstallGreeter(yes); err != nil { - log.Fatalf("Error uninstalling greeter: %v", err) - } - }, -} - -func init() { - greeterInstallCmd.Flags().BoolP("yes", "y", false, "Non-interactive: skip confirmation prompt") - greeterInstallCmd.Flags().BoolP("terminal", "t", false, "Run in a new terminal (for entering sudo password)") - greeterEnableCmd.Flags().BoolP("yes", "y", false, "Non-interactive: skip confirmation prompt") - greeterEnableCmd.Flags().BoolP("terminal", "t", false, "Run in a new terminal (for entering sudo password)") - greeterUninstallCmd.Flags().BoolP("yes", "y", false, "Non-interactive: skip confirmation prompt") - greeterUninstallCmd.Flags().BoolP("terminal", "t", false, "Run in a new terminal (for entering sudo password)") -} - -func syncGreeterConfigsAndAuth(dmsPath, compositor string, logFunc func(string), options sharedpam.SyncAuthOptions, beforeAuth func()) error { - if err := greeterConfigSyncFn(dmsPath, compositor, logFunc, ""); err != nil { - return err - } - if beforeAuth != nil { - beforeAuth() - } - return sharedAuthSyncFn(logFunc, "", options) -} - -func installGreeter(nonInteractive bool) error { - fmt.Println("=== DMS Greeter Installation ===") - - logFunc := func(msg string) { - fmt.Println(msg) - } - - if !nonInteractive { - fmt.Print("\nThis will install greetd (if needed), configure the DMS greeter, and enable it. Continue? [Y/n]: ") - var response string - fmt.Scanln(&response) - if strings.ToLower(strings.TrimSpace(response)) == "n" || strings.ToLower(strings.TrimSpace(response)) == "no" { - fmt.Println("Aborted.") - return nil - } - fmt.Println() - } - - if err := greeter.EnsureGreetdInstalled(logFunc, ""); err != nil { - return err - } - - greeter.TryInstallGreeterPackage(logFunc, "") - if isPackageOnlyGreeterDistro() && !greeter.IsGreeterPackaged() { - return fmt.Errorf("dms-greeter must be installed from distro packages on this distribution. %s", packageInstallHint()) - } - if greeter.IsGreeterPackaged() && greeter.HasLegacyLocalGreeterWrapper() { - return fmt.Errorf("legacy manual wrapper detected at /usr/local/bin/dms-greeter; remove it before using packaged dms-greeter: sudo rm -f /usr/local/bin/dms-greeter") - } - - if isGreeterEnabled() { - fmt.Print("\nGreeter is already installed and configured. Re-run to re-sync settings and permissions? [Y/n]: ") - var response string - fmt.Scanln(&response) - response = strings.TrimSpace(strings.ToLower(response)) - if response == "n" || response == "no" { - fmt.Println("Run 'dms greeter sync' to re-sync theme and settings at any time.") - return nil - } - fmt.Println() - } - - fmt.Println("\nDetecting DMS installation...") - dmsPath, err := greeter.DetectDMSPath() - if err != nil { - return err - } - fmt.Printf("✓ Found DMS at: %s\n", dmsPath) - - fmt.Println("\nDetecting installed compositors...") - compositors := greeter.DetectCompositors() - if len(compositors) == 0 { - return fmt.Errorf("no supported compositors found (niri or Hyprland required)") - } - - var selectedCompositor string - if len(compositors) == 1 { - selectedCompositor = compositors[0] - fmt.Printf("✓ Found compositor: %s\n", selectedCompositor) - } else { - var err error - selectedCompositor, err = greeter.PromptCompositorChoice(compositors) - if err != nil { - return err - } - fmt.Printf("✓ Selected compositor: %s\n", selectedCompositor) - } - - fmt.Println("\nSetting up dms-greeter group and permissions...") - if err := greeter.SetupDMSGroup(logFunc, ""); err != nil { - return err - } - - fmt.Println("\nCopying greeter files...") - if err := greeter.CopyGreeterFiles(dmsPath, selectedCompositor, logFunc, ""); err != nil { - return err - } - - if greeter.IsAppArmorEnabled() { - fmt.Println("\nConfiguring AppArmor profile...") - if err := greeter.InstallAppArmorProfile(logFunc, ""); err != nil { - logFunc(fmt.Sprintf("⚠ AppArmor profile setup failed: %v", err)) - } - } - - fmt.Println("\nConfiguring greetd...") - greeterPathForConfig := "" - if !greeter.IsGreeterPackaged() { - greeterPathForConfig = dmsPath - } - if err := greeter.ConfigureGreetd(greeterPathForConfig, selectedCompositor, logFunc, ""); err != nil { - return err - } - - fmt.Println("\nSynchronizing DMS configurations...") - if err := syncGreeterConfigsAndAuth(dmsPath, selectedCompositor, logFunc, sharedpam.SyncAuthOptions{}, func() { - fmt.Println("\nConfiguring authentication...") - }); err != nil { - return err - } - - if err := ensureGraphicalTarget(); err != nil { - return err - } - - if err := handleConflictingDisplayManagers(); err != nil { - return err - } - - if err := ensureGreetdEnabled(); err != nil { - return err - } - - fmt.Println("\n=== Installation Complete ===") - fmt.Println("\nTo start the greeter now, run:") - fmt.Println(" sudo systemctl start greetd") - fmt.Println("\nOr reboot to see the greeter at next boot.") - - return nil -} - -func uninstallGreeter(nonInteractive bool) error { - fmt.Println("=== DMS Greeter Uninstall ===") - - logFunc := func(msg string) { fmt.Println(msg) } - - if !isGreeterEnabled() { - fmt.Println("ℹ DMS greeter is not currently configured in /etc/greetd/config.toml.") - fmt.Println(" Nothing to undo for greetd configuration.") - } - - if !nonInteractive { - fmt.Print("\nThis will:\n • Stop and disable greetd\n • Remove the DMS-managed greeter auth block\n • Remove the DMS AppArmor profile\n • Restore the most recent pre-DMS greetd config (if available)\n\nContinue? [y/N]: ") - var response string - fmt.Scanln(&response) - if strings.ToLower(strings.TrimSpace(response)) != "y" { - fmt.Println("Aborted.") - return nil - } - } - - fmt.Println("\nDisabling greetd...") - disableCmd := exec.Command("sudo", "systemctl", "disable", "greetd") - disableCmd.Stdout = os.Stdout - disableCmd.Stderr = os.Stderr - if err := disableCmd.Run(); err != nil { - fmt.Printf(" ⚠ Could not disable greetd: %v\n", err) - } else { - fmt.Println(" ✓ greetd disabled") - } - - fmt.Println("\nRemoving DMS authentication configuration...") - if err := sharedpam.RemoveManagedGreeterPamBlock(logFunc, ""); err != nil { - fmt.Printf(" ⚠ PAM cleanup failed: %v\n", err) - } - - fmt.Println("\nRemoving DMS AppArmor profile...") - if err := greeter.UninstallAppArmorProfile(logFunc, ""); err != nil { - fmt.Printf(" ⚠ AppArmor cleanup failed: %v\n", err) - } - - fmt.Println("\nRestoring greetd configuration...") - if err := restorePreDMSGreetdConfig(""); err != nil { - fmt.Printf(" ⚠ Could not restore previous greetd config: %v\n", err) - fmt.Println(" You may need to manually edit /etc/greetd/config.toml.") - } - - fmt.Println("\nChecking for other display managers to re-enable...") - suggestDisplayManagerRestore(nonInteractive) - - fmt.Println("\n=== Uninstall Complete ===") - fmt.Println("\nReboot to complete the uninstallation and switch to your previous display manager.") - fmt.Println("To re-enable DMS greeter at any time, run: dms greeter enable") - - return nil -} - -func restorePreDMSGreetdConfig(sudoPassword string) error { - const configPath = "/etc/greetd/config.toml" - const backupGlob = "/etc/greetd/config.toml.backup-*" - - matches, _ := filepath.Glob(backupGlob) - - for i := 0; i < len(matches)-1; i++ { - for j := i + 1; j < len(matches); j++ { - if matches[j] > matches[i] { - matches[i], matches[j] = matches[j], matches[i] - } - } - } - - for _, candidate := range matches { - data, err := os.ReadFile(candidate) - if err != nil { - continue - } - if strings.Contains(string(data), "dms-greeter") { - continue - } - tmp, err := os.CreateTemp("", "greetd-restore-*") - if err != nil { - return fmt.Errorf("could not create temp file: %w", err) - } - tmpPath := tmp.Name() - defer os.Remove(tmpPath) - if _, err := tmp.Write(data); err != nil { - tmp.Close() - return err - } - tmp.Close() - - if err := runSudoCommand(sudoPassword, "cp", tmpPath, configPath); err != nil { - return fmt.Errorf("failed to restore %s: %w", candidate, err) - } - if err := runSudoCommand(sudoPassword, "chmod", "644", configPath); err != nil { - return err - } - fmt.Printf(" ✓ Restored greetd config from %s\n", candidate) - return nil - } - - minimal := `[terminal] -vt = 1 - -# DMS greeter has been uninstalled. -# Configure a greeter command here or re-enable a display manager. -[default_session] -user = "greeter" -command = "agreety --cmd /bin/bash" -` - tmp, err := os.CreateTemp("", "greetd-minimal-*") - if err != nil { - return fmt.Errorf("could not create temp file: %w", err) - } - tmpPath := tmp.Name() - defer os.Remove(tmpPath) - if _, err := tmp.WriteString(minimal); err != nil { - tmp.Close() - return err - } - tmp.Close() - - if err := runSudoCommand(sudoPassword, "cp", tmpPath, configPath); err != nil { - return fmt.Errorf("failed to write fallback greetd config: %w", err) - } - _ = runSudoCommand(sudoPassword, "chmod", "644", configPath) - fmt.Println(" ✓ Wrote minimal fallback greetd config (configure a greeter command manually if needed)") - return nil -} - -func runSudoCommand(_ string, command string, args ...string) error { - cmd := exec.Command("sudo", append([]string{command}, args...)...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} - -// suggestDisplayManagerRestore scans for installed DMs and re-enables one -func suggestDisplayManagerRestore(nonInteractive bool) { - knownDMs := []string{"gdm", "gdm3", "lightdm", "sddm", "lxdm", "xdm", "cosmic-greeter"} - var found []string - for _, dm := range knownDMs { - if utils.CommandExists(dm) || isSystemdUnitInstalled(dm) { - found = append(found, dm) - } - } - if len(found) == 0 { - fmt.Println(" ℹ No other display managers detected.") - fmt.Println(" You can install one (e.g. gdm, lightdm, sddm) and then run:") - fmt.Println(" sudo systemctl enable --now ") - return - } - - enableDM := func(dm string) { - fmt.Printf(" Enabling %s...\n", dm) - cmd := exec.Command("sudo", "systemctl", "enable", "--force", dm) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - fmt.Printf(" ⚠ Failed to enable %s: %v\n", dm, err) - } else { - fmt.Printf(" ✓ %s enabled (will take effect on next boot).\n", dm) - } - } - - if len(found) == 1 || nonInteractive { - chosen := found[0] - if len(found) > 1 { - fmt.Printf(" ℹ Multiple display managers found (%s); enabling %s automatically.\n", - strings.Join(found, ", "), chosen) - } else { - fmt.Printf(" ℹ Found display manager: %s\n", chosen) - } - enableDM(chosen) - return - } - - fmt.Println(" ℹ Found the following display managers:") - for i, dm := range found { - fmt.Printf(" %d) %s\n", i+1, dm) - } - fmt.Print(" Choose a number to re-enable (or press Enter to skip): ") - - scanner := bufio.NewScanner(os.Stdin) - if !scanner.Scan() { - return - } - input := strings.TrimSpace(scanner.Text()) - if input == "" { - fmt.Println(" Skipped. You can re-enable a display manager later with:") - fmt.Println(" sudo systemctl enable --now ") - return - } - - n, err := strconv.Atoi(input) - if err != nil || n < 1 || n > len(found) { - fmt.Printf(" Invalid selection %q — skipping.\n", input) - return - } - - enableDM(found[n-1]) -} - -func isSystemdUnitInstalled(unit string) bool { - cmd := exec.Command("systemctl", "list-unit-files", unit+".service", "--no-legend", "--no-pager") - out, err := cmd.Output() - return err == nil && strings.Contains(string(out), unit) -} - -func runCommandInTerminal(shellCmd string) error { - terminals := []struct { - name string - args []string - }{ - {"gnome-terminal", []string{"--", "bash", "-c", shellCmd}}, - {"konsole", []string{"-e", "bash", "-c", shellCmd}}, - {"xfce4-terminal", []string{"-e", "bash -c \"" + strings.ReplaceAll(shellCmd, `"`, `\"`) + "\""}}, - {"ghostty", []string{"-e", "bash", "-c", shellCmd}}, - {"wezterm", []string{"start", "--", "bash", "-c", shellCmd}}, - {"alacritty", []string{"-e", "bash", "-c", shellCmd}}, - {"kitty", []string{"bash", "-c", shellCmd}}, - {"xterm", []string{"-e", "bash -c \"" + strings.ReplaceAll(shellCmd, `"`, `\"`) + "\""}}, - } - for _, t := range terminals { - if _, err := exec.LookPath(t.name); err != nil { - continue - } - cmd := exec.Command(t.name, t.args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return err - } - return nil - } - return fmt.Errorf("no terminal emulator found (tried: gnome-terminal, konsole, xfce4-terminal, ghostty, wezterm, alacritty, kitty, xterm)") -} - -func syncInTerminal(nonInteractive bool, forceAuth bool, local bool) error { - syncFlags := make([]string, 0, 3) - if nonInteractive { - syncFlags = append(syncFlags, "--yes") - } - if forceAuth { - syncFlags = append(syncFlags, "--auth") - } - if local { - syncFlags = append(syncFlags, "--local") - } - shellSyncCmd := "dms greeter sync" - if len(syncFlags) > 0 { - shellSyncCmd += " " + strings.Join(syncFlags, " ") - } - shellCmd := shellSyncCmd + `; echo; echo "Sync finished. Closing in 3 seconds..."; sleep 3` - return runCommandInTerminal(shellCmd) -} - -func resolveLocalWrapperShell() (string, error) { - for _, shellName := range []string{"bash", "sh"} { - shellPath, err := exec.LookPath(shellName) - if err == nil { - return shellPath, nil - } - } - return "", fmt.Errorf("could not find bash or sh in PATH for local greeter wrapper") -} - -func syncGreeter(nonInteractive bool, forceAuth bool, local bool) error { - if !nonInteractive { - fmt.Println("=== DMS Greeter Sync ===") - fmt.Println() - } - - logFunc := func(msg string) { - fmt.Println(msg) - } - - if !nonInteractive { - fmt.Println("Detecting DMS installation...") - } - var dmsPath string - var err error - if local { - dmsPath, err = resolveLocalDMSPath() - if err != nil { - return err - } - if !nonInteractive { - fmt.Printf("✓ Using local DMS path: %s\n", dmsPath) - } - } else { - dmsPath, err = greeter.DetectDMSPath() - if err != nil { - return err - } - if !nonInteractive { - fmt.Printf("✓ Found DMS at: %s\n", dmsPath) - } - } - - if !isGreeterEnabled() { - if nonInteractive { - return fmt.Errorf("greeter is not enabled; run 'dms greeter install' or 'dms greeter enable' first") - } - fmt.Println("\n⚠ DMS greeter is not enabled in greetd config.") - fmt.Print("Would you like to enable it now? (Y/n): ") - - var response string - fmt.Scanln(&response) - response = strings.ToLower(strings.TrimSpace(response)) - - if response != "n" && response != "no" { - if err := enableGreeter(false); err != nil { - return err - } - } else { - return fmt.Errorf("greeter must be enabled before syncing") - } - } - - if greeter.IsGreeterPackaged() && greeter.HasLegacyLocalGreeterWrapper() { - return fmt.Errorf("legacy manual wrapper detected at /usr/local/bin/dms-greeter; remove it before using packaged dms-greeter: sudo rm -f /usr/local/bin/dms-greeter") - } - - cacheDir := greeter.GreeterCacheDir - if _, err := os.Stat(cacheDir); os.IsNotExist(err) { - logFunc("Cache directory not found — attempting to create it...") - } - - greeterGroup := greeter.DetectGreeterGroup() - greeterGroupExists := utils.HasGroup(greeterGroup) - if greeterGroupExists { - currentUser, err := user.Current() - if err != nil { - return fmt.Errorf("failed to get current user: %w", err) - } - - groupsCmd := exec.Command("groups", currentUser.Username) - groupsOutput, err := groupsCmd.Output() - if err != nil { - return fmt.Errorf("failed to check groups: %w", err) - } - - inGreeterGroup := strings.Contains(string(groupsOutput), greeterGroup) - if !inGreeterGroup { - if nonInteractive { - logFunc(fmt.Sprintf("⚠ Not yet in %s group — will be added during sync (logout/login required to take effect).", greeterGroup)) - } else { - fmt.Printf("\n⚠ Warning: You are not in the %s group.\n", greeterGroup) - fmt.Printf("Would you like to add your user to the %s group? (Y/n): ", greeterGroup) - - var response string - fmt.Scanln(&response) - response = strings.ToLower(strings.TrimSpace(response)) - - if response != "n" && response != "no" { - fmt.Printf("\nAdding user to %s group...\n", greeterGroup) - addUserCmd := exec.Command("sudo", "usermod", "-aG", greeterGroup, currentUser.Username) - addUserCmd.Stdout = os.Stdout - addUserCmd.Stderr = os.Stderr - if err := addUserCmd.Run(); err != nil { - return fmt.Errorf("failed to add user to %s group: %w", greeterGroup, err) - } - fmt.Printf("✓ User added to %s group\n", greeterGroup) - fmt.Println("⚠ You will need to log out and back in for the group change to take effect") - } else { - return fmt.Errorf("aborted: user must be in the greeter group before syncing") - } - } - } - } - - compositor := detectConfiguredCompositor() - if compositor == "" { - compositors := greeter.DetectCompositors() - switch len(compositors) { - case 0: - return fmt.Errorf("no supported compositors found") - case 1: - compositor = compositors[0] - if !nonInteractive { - fmt.Printf("✓ Using compositor: %s\n", compositor) - } - default: - if nonInteractive { - compositor = compositors[0] - break - } - var err error - compositor, err = promptCompositorChoice(compositors) - if err != nil { - return err - } - fmt.Printf("✓ Selected compositor: %s\n", compositor) - } - } else if !nonInteractive { - fmt.Printf("✓ Detected compositor from config: %s\n", compositor) - } - - if local { - localWrapperScript := filepath.Join(dmsPath, "Modules", "Greetd", "assets", "dms-greeter") - restoreWrapperOverride := func() {} - if info, statErr := os.Stat(localWrapperScript); statErr == nil && !info.IsDir() { - wrapperShell, shellErr := resolveLocalWrapperShell() - if shellErr != nil { - return shellErr - } - previousWrapperOverride, hadWrapperOverride := os.LookupEnv("DMS_GREETER_WRAPPER_CMD") - wrapperCmdOverride := wrapperShell + " " + localWrapperScript - _ = os.Setenv("DMS_GREETER_WRAPPER_CMD", wrapperCmdOverride) - restoreWrapperOverride = func() { - if hadWrapperOverride { - _ = os.Setenv("DMS_GREETER_WRAPPER_CMD", previousWrapperOverride) - } else { - _ = os.Unsetenv("DMS_GREETER_WRAPPER_CMD") - } - } - if !nonInteractive { - fmt.Printf("✓ Using local greeter wrapper script: %s\n", localWrapperScript) - } - } else if !nonInteractive { - fmt.Printf("ℹ Local wrapper script not found at %s; using system wrapper.\n", localWrapperScript) - } - - fmt.Println("\nUpdating greetd command to use local DMS path...") - err := greeter.ConfigureGreetd(dmsPath, compositor, logFunc, "") - restoreWrapperOverride() - if err != nil { - return fmt.Errorf("failed to apply local greeter path: %w", err) - } - if !nonInteractive { - fmt.Println("ℹ Local mode applies both DMS path override (-p) and local wrapper behavior when available.") - } - } else { - greeterPathForConfig := "" - if !greeter.IsGreeterPackaged() { - greeterPathForConfig = dmsPath - } - fmt.Println("\nUpdating greetd command...") - if err := greeter.ConfigureGreetd(greeterPathForConfig, compositor, logFunc, ""); err != nil { - return fmt.Errorf("failed to update greetd command: %w", err) - } - } - - fmt.Println("\nSetting up permissions and ACLs...") - greeter.RemediateStaleACLs(logFunc, "") - greeter.RemediateStaleAppArmor(logFunc, "") - if err := greeter.SetupDMSGroup(logFunc, ""); err != nil { - return err - } - if err := greeter.EnsureGreeterCacheDir(logFunc, ""); err != nil { - return fmt.Errorf("failed to ensure greeter cache directory at %s: %w\nRun: sudo mkdir -p %s && sudo chown root:%s %s && sudo chmod 2770 %s", cacheDir, err, cacheDir, greeterGroup, cacheDir, cacheDir) - } - - fmt.Println("\nSynchronizing DMS configurations...") - if err := syncGreeterConfigsAndAuth(dmsPath, compositor, logFunc, sharedpam.SyncAuthOptions{ - ForceGreeterAuth: forceAuth, - }, func() { - fmt.Println("\nConfiguring authentication...") - }); err != nil { - return err - } - - if greeter.IsAppArmorEnabled() { - fmt.Println("\nConfiguring AppArmor profile...") - if err := greeter.InstallAppArmorProfile(logFunc, ""); err != nil { - logFunc(fmt.Sprintf("⚠ AppArmor profile setup failed: %v", err)) - } - } - - fmt.Println("\n=== Sync Complete ===") - fmt.Println("\nYour theme, settings, and wallpaper configuration have been synced with the greeter.") - fmt.Println("Shared authentication settings were also checked and reconciled where needed.") - if forceAuth { - fmt.Println("Authentication has been configured for fingerprint and U2F (where modules exist).") - } - fmt.Println("The changes will be visible on the next login screen.") - - return nil -} - -func hasDmsShellQml(dir string) bool { - info, err := os.Stat(filepath.Join(dir, "shell.qml")) - return err == nil && !info.IsDir() -} - -func resolveDMSLocalCandidate(path string) (string, bool) { - if path == "" { - return "", false - } - if hasDmsShellQml(path) { - abs, err := filepath.Abs(path) - if err != nil { - return path, true - } - return abs, true - } - - quickshellPath := filepath.Join(path, "quickshell") - if hasDmsShellQml(quickshellPath) { - abs, err := filepath.Abs(quickshellPath) - if err != nil { - return quickshellPath, true - } - return abs, true - } - - return "", false -} - -func resolveLocalDMSPath() (string, error) { - if override := strings.TrimSpace(os.Getenv("DMS_LOCAL_PATH")); override != "" { - if resolved, ok := resolveDMSLocalCandidate(override); ok { - return resolved, nil - } - return "", fmt.Errorf("DMS_LOCAL_PATH is set but does not point to a valid DMS quickshell path: %s", override) - } - - wd, err := os.Getwd() - if err != nil { - return "", fmt.Errorf("failed to get current directory: %w", err) - } - - dir := wd - for { - if resolved, ok := resolveDMSLocalCandidate(dir); ok { - return resolved, nil - } - - parent := filepath.Dir(dir) - if parent == dir { - break - } - dir = parent - } - - homeDir, err := os.UserHomeDir() - if err == nil && homeDir != "" { - for _, candidate := range []string{ - filepath.Join(homeDir, "dms"), - filepath.Join(homeDir, "DankMaterialShell"), - filepath.Join(homeDir, "dankmaterialshell"), - filepath.Join(homeDir, "projects", "dms"), - filepath.Join(homeDir, "src", "dms"), - } { - if resolved, ok := resolveDMSLocalCandidate(candidate); ok { - return resolved, nil - } - } - - if entries, readErr := os.ReadDir(homeDir); readErr == nil { - for _, entry := range entries { - if !entry.IsDir() { - continue - } - name := strings.ToLower(entry.Name()) - if !strings.Contains(name, "dms") && !strings.Contains(name, "dank") { - continue - } - if resolved, ok := resolveDMSLocalCandidate(filepath.Join(homeDir, entry.Name())); ok { - return resolved, nil - } - } - } - } - - return "", fmt.Errorf("could not locate a local DMS checkout from %s; run from repo root or set DMS_LOCAL_PATH=/absolute/path/to/repo", wd) -} - -func disableDisplayManager(dmName string) (bool, error) { - state, err := getSystemdServiceState(dmName) - if err != nil { - return false, fmt.Errorf("failed to check %s state: %w", dmName, err) - } - - if !state.Exists { - return false, nil - } - - fmt.Printf("\nChecking %s...\n", dmName) - fmt.Printf(" Current state: enabled=%s\n", state.EnabledState) - - actionTaken := false - - if state.NeedsDisable { - var disableCmd *exec.Cmd - var actionVerb string - - if state.EnabledState == "static" { - fmt.Printf(" Masking %s (static service cannot be disabled)...\n", dmName) - disableCmd = exec.Command("sudo", "systemctl", "mask", dmName) - actionVerb = "masked" - } else { - fmt.Printf(" Disabling %s...\n", dmName) - disableCmd = exec.Command("sudo", "systemctl", "disable", dmName) - actionVerb = "disabled" - } - - disableCmd.Stdout = os.Stdout - disableCmd.Stderr = os.Stderr - if err := disableCmd.Run(); err != nil { - return actionTaken, fmt.Errorf("failed to disable/mask %s: %w", dmName, err) - } - - enabledState, shouldDisable, verifyErr := checkSystemdServiceEnabled(dmName) - if verifyErr != nil { - fmt.Printf(" ⚠ Warning: Could not verify %s was %s: %v\n", dmName, actionVerb, verifyErr) - } else if shouldDisable { - return actionTaken, fmt.Errorf("%s is still in state '%s' after %s operation", dmName, enabledState, actionVerb) - } else { - fmt.Printf(" ✓ %s %s (now: %s)\n", cases.Title(language.English).String(actionVerb), dmName, enabledState) - } - - actionTaken = true - } else { - if state.EnabledState == "masked" || state.EnabledState == "masked-runtime" { - fmt.Printf(" ✓ %s is already masked\n", dmName) - } else { - fmt.Printf(" ✓ %s is already disabled\n", dmName) - } - } - - return actionTaken, nil -} - -func ensureGreetdEnabled() error { - fmt.Println("\nChecking greetd service status...") - - state, err := getSystemdServiceState("greetd") - if err != nil { - return fmt.Errorf("failed to check greetd state: %w", err) - } - - if !state.Exists { - return fmt.Errorf("greetd service not found. Please install greetd first") - } - - fmt.Printf(" Current state: %s\n", state.EnabledState) - - if state.EnabledState == "masked" || state.EnabledState == "masked-runtime" { - fmt.Println(" Unmasking greetd...") - unmaskCmd := exec.Command("sudo", "systemctl", "unmask", "greetd") - unmaskCmd.Stdout = os.Stdout - unmaskCmd.Stderr = os.Stderr - if err := unmaskCmd.Run(); err != nil { - return fmt.Errorf("failed to unmask greetd: %w", err) - } - fmt.Println(" ✓ Unmasked greetd") - } - - if state.EnabledState == "enabled" || state.EnabledState == "enabled-runtime" { - fmt.Println(" Reasserting greetd as active display manager...") - } else { - fmt.Println(" Enabling greetd service...") - } - - enableCmd := exec.Command("sudo", "systemctl", "enable", "--force", "greetd") - enableCmd.Stdout = os.Stdout - enableCmd.Stderr = os.Stderr - if err := enableCmd.Run(); err != nil { - return fmt.Errorf("failed to enable greetd: %w", err) - } - - enabledState, _, verifyErr := checkSystemdServiceEnabled("greetd") - if verifyErr != nil { - fmt.Printf(" ⚠ Warning: Could not verify greetd enabled state: %v\n", verifyErr) - } else { - switch enabledState { - case "enabled", "enabled-runtime", "static", "indirect", "alias": - fmt.Printf(" ✓ greetd enabled (state: %s)\n", enabledState) - default: - return fmt.Errorf("greetd is still in state '%s' after enable operation", enabledState) - } - } - - return nil -} - -func ensureGraphicalTarget() error { - getDefaultCmd := exec.Command("systemctl", "get-default") - currentTarget, err := getDefaultCmd.Output() - if err != nil { - fmt.Println("⚠ Warning: Could not detect current default systemd target") - return nil - } - - currentTargetStr := strings.TrimSpace(string(currentTarget)) - if currentTargetStr != "graphical.target" { - fmt.Printf("\nSetting graphical.target as default (current: %s)...\n", currentTargetStr) - setDefaultCmd := exec.Command("sudo", "systemctl", "set-default", "graphical.target") - setDefaultCmd.Stdout = os.Stdout - setDefaultCmd.Stderr = os.Stderr - if err := setDefaultCmd.Run(); err != nil { - fmt.Println("⚠ Warning: Failed to set graphical.target as default") - fmt.Println(" Greeter may not start on boot. Run manually:") - fmt.Println(" sudo systemctl set-default graphical.target") - return nil - } - fmt.Println("✓ Set graphical.target as default") - } else { - fmt.Println("✓ Default target already set to graphical.target") - } - - return nil -} - -func handleConflictingDisplayManagers() error { - fmt.Println("\n=== Checking for Conflicting Display Managers ===") - - conflictingDMs := []string{"gdm", "gdm3", "lightdm", "sddm", "lxdm", "xdm", "cosmic-greeter"} - - disabledAny := false - var errors []string - - for _, dm := range conflictingDMs { - actionTaken, err := disableDisplayManager(dm) - if err != nil { - errMsg := fmt.Sprintf("Failed to handle %s: %v", dm, err) - errors = append(errors, errMsg) - fmt.Printf(" ⚠⚠⚠ ERROR: %s\n", errMsg) - continue - } - if actionTaken { - disabledAny = true - } - } - - if len(errors) > 0 { - fmt.Println("\n╔════════════════════════════════════════════════════════════╗") - fmt.Println("║ ⚠⚠⚠ ERRORS OCCURRED ⚠⚠⚠ ║") - fmt.Println("╚════════════════════════════════════════════════════════════╝") - fmt.Println("\nSome display managers could not be disabled:") - for _, err := range errors { - fmt.Printf(" ✗ %s\n", err) - } - fmt.Println("\nThis may prevent greetd from starting properly.") - fmt.Println("You may need to manually disable them before greetd will work.") - fmt.Println("\nManual commands to try:") - for _, dm := range conflictingDMs { - fmt.Printf(" sudo systemctl disable %s\n", dm) - fmt.Printf(" sudo systemctl mask %s\n", dm) - } - fmt.Print("\nContinue with greeter enablement anyway? (Y/n): ") - - var response string - fmt.Scanln(&response) - response = strings.ToLower(strings.TrimSpace(response)) - - if response == "n" || response == "no" { - return fmt.Errorf("aborted due to display manager conflicts") - } - fmt.Println("\nContinuing despite errors...") - } - - if !disabledAny && len(errors) == 0 { - fmt.Println("\n✓ No conflicting display managers found") - } else if disabledAny && len(errors) == 0 { - fmt.Println("\n✓ Successfully handled all conflicting display managers") - } - - return nil -} - -func enableGreeter(nonInteractive bool) error { - fmt.Println("=== DMS Greeter Enable ===") - fmt.Println() - - configPath := "/etc/greetd/config.toml" - if _, err := os.Stat(configPath); os.IsNotExist(err) { - return fmt.Errorf("greetd config not found at %s\nPlease install greetd first", configPath) - } else if err != nil { - return fmt.Errorf("failed to access greetd config at %s: %w", configPath, err) - } - - if greeter.IsGreeterPackaged() && greeter.HasLegacyLocalGreeterWrapper() { - return fmt.Errorf("legacy manual wrapper detected at /usr/local/bin/dms-greeter; remove it before using packaged dms-greeter: sudo rm -f /usr/local/bin/dms-greeter") - } - - configAlreadyCorrect := isGreeterEnabled() - configuredCompositor := detectConfiguredCompositor() - - logFunc := func(msg string) { - fmt.Println(msg) - } - greeterGroup := greeter.DetectGreeterGroup() - - if configAlreadyCorrect { - fmt.Println("✓ Greeter is already configured with dms-greeter") - if configuredCompositor != "" { - fmt.Printf("✓ Configured compositor: %s\n", configuredCompositor) - } - - fmt.Println("\nSetting up dms-greeter group and permissions...") - if err := greeter.SetupDMSGroup(logFunc, ""); err != nil { - return err - } - if err := greeter.EnsureGreeterCacheDir(logFunc, ""); err != nil { - fmt.Printf("⚠ Could not ensure cache directory: %v\n Run: sudo mkdir -p %s && sudo chown root:%s %s && sudo chmod 2770 %s\n", err, greeter.GreeterCacheDir, greeterGroup, greeter.GreeterCacheDir, greeter.GreeterCacheDir) - } - - if err := ensureGraphicalTarget(); err != nil { - return err - } - - if err := handleConflictingDisplayManagers(); err != nil { - return err - } - - if err := ensureGreetdEnabled(); err != nil { - return err - } - - fmt.Println("\n=== Enable Complete ===") - fmt.Println("\nGreeter configuration verified and system state corrected.") - fmt.Println("To start the greeter now, run:") - fmt.Println(" sudo systemctl start greetd") - fmt.Println("\nOr reboot to see the greeter at boot time.") - - return nil - } - - if !nonInteractive { - fmt.Print("\nThis will configure greetd to use the DMS greeter and may disable other display managers. Continue? [Y/n]: ") - var response string - fmt.Scanln(&response) - if strings.ToLower(strings.TrimSpace(response)) == "n" || strings.ToLower(strings.TrimSpace(response)) == "no" { - fmt.Println("Aborted.") - return nil - } - fmt.Println() - } - - fmt.Println("Detecting installed compositors...") - compositors := greeter.DetectCompositors() - - if utils.CommandExists("sway") { - compositors = append(compositors, "sway") - } - - if len(compositors) == 0 { - return fmt.Errorf("no supported compositors found (niri, Hyprland, or sway required)") - } - - var selectedCompositor string - if len(compositors) == 1 { - selectedCompositor = compositors[0] - fmt.Printf("✓ Found compositor: %s\n", selectedCompositor) - } else { - var err error - selectedCompositor, err = promptCompositorChoice(compositors) - if err != nil { - return err - } - fmt.Printf("✓ Selected compositor: %s\n", selectedCompositor) - } - - greeterPathForConfig := "" - if !greeter.IsGreeterPackaged() { - dmsPath, err := greeter.DetectDMSPath() - if err != nil { - return fmt.Errorf("failed to detect DMS path for manual greeter configuration: %w", err) - } - greeterPathForConfig = dmsPath - } - if err := greeter.ConfigureGreetd(greeterPathForConfig, selectedCompositor, logFunc, ""); err != nil { - return fmt.Errorf("failed to configure greetd: %w", err) - } - - fmt.Println("\nSetting up dms-greeter group and permissions...") - if err := greeter.SetupDMSGroup(logFunc, ""); err != nil { - return err - } - if err := greeter.EnsureGreeterCacheDir(logFunc, ""); err != nil { - fmt.Printf("⚠ Could not ensure cache directory: %v\n Run: sudo mkdir -p %s && sudo chown root:%s %s && sudo chmod 2770 %s\n", err, greeter.GreeterCacheDir, greeterGroup, greeter.GreeterCacheDir, greeter.GreeterCacheDir) - } - - if greeter.IsAppArmorEnabled() { - if err := greeter.InstallAppArmorProfile(logFunc, ""); err != nil { - logFunc(fmt.Sprintf("⚠ AppArmor profile setup failed: %v", err)) - } - } - - if err := ensureGraphicalTarget(); err != nil { - return err - } - - if err := handleConflictingDisplayManagers(); err != nil { - return err - } - - if err := ensureGreetdEnabled(); err != nil { - return err - } - - fmt.Println("\n=== Enable Complete ===") - fmt.Println("\nTo start the greeter now, run:") - fmt.Println(" sudo systemctl start greetd") - fmt.Println("\nOr reboot to see the greeter at boot time.") - - return nil -} - -func isGreeterEnabled() bool { - command := readDefaultSessionCommand("/etc/greetd/config.toml") - return command != "" && strings.Contains(command, "dms-greeter") -} - -func detectConfiguredCompositor() string { - command := strings.ToLower(readDefaultSessionCommand("/etc/greetd/config.toml")) - switch { - case strings.Contains(command, "--command niri"): - return "niri" - case strings.Contains(command, "--command hyprland"): - return "hyprland" - case strings.Contains(command, "--command sway"): - return "sway" - } - return "" -} - -func stripTomlComment(line string) string { - trimmed := strings.TrimSpace(line) - if idx := strings.Index(trimmed, "#"); idx >= 0 { - return strings.TrimSpace(trimmed[:idx]) - } - return trimmed -} - -func parseTomlSection(line string) (string, bool) { - trimmed := stripTomlComment(line) - if len(trimmed) < 3 || !strings.HasPrefix(trimmed, "[") || !strings.HasSuffix(trimmed, "]") { - return "", false - } - return strings.TrimSpace(trimmed[1 : len(trimmed)-1]), true -} - -func readDefaultSessionCommand(configPath string) string { - data, err := os.ReadFile(configPath) - if err != nil { - return "" - } - - inDefaultSession := false - for line := range strings.SplitSeq(string(data), "\n") { - if section, ok := parseTomlSection(line); ok { - inDefaultSession = section == "default_session" - continue - } - - if !inDefaultSession { - continue - } - - trimmed := stripTomlComment(line) - if !strings.HasPrefix(trimmed, "command =") && !strings.HasPrefix(trimmed, "command=") { - continue - } - - parts := strings.SplitN(trimmed, "=", 2) - if len(parts) != 2 { - continue - } - - command := strings.Trim(strings.TrimSpace(parts[1]), `"`) - if command != "" { - return command - } - } - - return "" -} - -func extractGreeterCacheDirFromCommand(command string) string { - if command == "" { - return greeter.GreeterCacheDir - } - tokens := strings.Fields(command) - for i := 0; i < len(tokens); i++ { - token := strings.Trim(tokens[i], "\"") - if token == "--cache-dir" && i+1 < len(tokens) { - return strings.Trim(tokens[i+1], "\"") - } - if strings.HasPrefix(token, "--cache-dir=") { - value := strings.TrimPrefix(token, "--cache-dir=") - value = strings.Trim(value, "\"") - if value != "" { - return value - } - } - } - return greeter.GreeterCacheDir -} - -func extractGreeterWrapperFromCommand(command string) string { - if command == "" { - return "" - } - tokens := strings.Fields(command) - if len(tokens) == 0 { - return "" - } - wrapper := strings.Trim(tokens[0], "\"") - if wrapper == "" { - return "" - } - if len(tokens) > 1 { - next := strings.Trim(tokens[1], "\"") - if next != "" && (filepath.Base(wrapper) == "bash" || filepath.Base(wrapper) == "sh") && strings.Contains(filepath.Base(next), "dms-greeter") { - return fmt.Sprintf("%s (script: %s)", wrapper, next) - } - } - return wrapper -} - -func extractGreeterPathOverrideFromCommand(command string) string { - if command == "" { - return "" - } - tokens := strings.Fields(command) - for i := 0; i < len(tokens); i++ { - token := strings.Trim(tokens[i], "\"") - if (token == "-p" || token == "--path") && i+1 < len(tokens) { - return strings.Trim(tokens[i+1], "\"") - } - if strings.HasPrefix(token, "--path=") { - value := strings.TrimPrefix(token, "--path=") - value = strings.Trim(value, "\"") - if value != "" { - return value - } - } - } - return "" -} - -func parseManagedGreeterPamAuth(pamText string) (managed bool, fingerprint bool, u2f bool, legacy bool) { - return sharedpam.ParseManagedGreeterPamAuth(pamText) -} - -func packageInstallHint() string { - osInfo, err := distros.GetOSInfo() - if err != nil { - return "Install package: dms-greeter" - } - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - return "Install package: dms-greeter" - } - - switch config.Family { - case distros.FamilyDebian: - return "Install with 'sudo apt install dms-greeter' (requires DankLinux OBS repo — see https://danklinux.com/docs/dankgreeter/installation#debian)" - case distros.FamilySUSE: - return "Install with 'sudo zypper install dms-greeter' (requires DankLinux OBS repo — see https://danklinux.com/docs/dankgreeter/installation#opensuse)" - case distros.FamilyUbuntu: - return "Install with 'sudo apt install dms-greeter' (requires ppa:avengemedia/danklinux: sudo add-apt-repository ppa:avengemedia/danklinux)" - case distros.FamilyFedora: - return "Install with 'sudo dnf install dms-greeter' (requires COPR: sudo dnf copr enable avengemedia/danklinux)" - case distros.FamilyArch: - return "Install from AUR with 'paru -S greetd-dms-greeter-git' or 'yay -S greetd-dms-greeter-git'" - default: - return "Run 'dms greeter install' to install greeter" - } -} - -func systemPamManagerRemediationHint() string { - osInfo, err := distros.GetOSInfo() - if err != nil { - return "Disable it in your PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login." - } - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - return "Disable it in your PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login." - } - - switch config.Family { - case distros.FamilyFedora: - return "Disable it in authselect to force password-only greeter login." - case distros.FamilyDebian, distros.FamilyUbuntu: - return "Disable it in pam-auth-update to force password-only greeter login." - default: - return "Disable it in your distro PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login." - } -} - -func isPackageOnlyGreeterDistro() bool { - osInfo, err := distros.GetOSInfo() - if err != nil { - return false - } - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - return false - } - return config.Family == distros.FamilyDebian || - config.Family == distros.FamilySUSE || - config.Family == distros.FamilyUbuntu || - config.Family == distros.FamilyFedora || - config.Family == distros.FamilyArch -} - -func promptCompositorChoice(compositors []string) (string, error) { - fmt.Println("\nMultiple compositors detected:") - for i, comp := range compositors { - fmt.Printf("%d) %s\n", i+1, comp) - } - - var response string - fmt.Print("Choose compositor for greeter: ") - fmt.Scanln(&response) - response = strings.TrimSpace(response) - - choice := 0 - fmt.Sscanf(response, "%d", &choice) - - if choice < 1 || choice > len(compositors) { - return "", fmt.Errorf("invalid choice") - } - - return compositors[choice-1], nil -} - -func checkGreeterStatus() error { - fmt.Println("=== DMS Greeter Status ===") - fmt.Println() - - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - currentUser, err := user.Current() - if err != nil { - return fmt.Errorf("failed to get current user: %w", err) - } - - configPath := "/etc/greetd/config.toml" - configuredCommand := "" - allGood := true - fmt.Println("Greeter Configuration:") - if _, err := os.ReadFile(configPath); err == nil { - configuredCommand = readDefaultSessionCommand(configPath) - if configuredCommand != "" && strings.Contains(configuredCommand, "dms-greeter") { - fmt.Println(" ✓ Greeter is enabled") - if wrapper := extractGreeterWrapperFromCommand(configuredCommand); wrapper != "" { - fmt.Printf(" Wrapper: %s\n", wrapper) - } - if pathOverride := extractGreeterPathOverrideFromCommand(configuredCommand); pathOverride != "" { - fmt.Printf(" DMS path override: %s\n", pathOverride) - } - - compositor := detectConfiguredCompositor() - switch compositor { - case "niri": - fmt.Println(" Compositor: niri") - case "hyprland": - fmt.Println(" Compositor: Hyprland") - case "sway": - fmt.Println(" Compositor: sway") - default: - fmt.Println(" Compositor: unknown") - } - } else { - fmt.Println(" ✗ Greeter is NOT enabled") - fmt.Println(" Run 'dms greeter enable' to enable it, or use the Activate button in Settings → Greeter, then Sync.") - allGood = false - } - } else { - fmt.Println(" ✗ Greeter config not found") - fmt.Printf(" %s\n", packageInstallHint()) - allGood = false - } - - fmt.Println("\nGroup Membership:") - groupsCmd := exec.Command("groups", currentUser.Username) - groupsOutput, err := groupsCmd.Output() - if err != nil { - return fmt.Errorf("failed to check groups: %w", err) - } - - greeterGroup := greeter.DetectGreeterGroup() - inGreeterGroup := strings.Contains(string(groupsOutput), greeterGroup) - if inGreeterGroup { - fmt.Printf(" ✓ User is in %s group\n", greeterGroup) - } else { - fmt.Printf(" ✗ User is NOT in %s group\n", greeterGroup) - fmt.Println(" Run 'dms greeter sync' to set up group membership and permissions") - } - - cacheDir := extractGreeterCacheDirFromCommand(configuredCommand) - fmt.Println("\nGreeter Cache Directory:") - fmt.Printf(" Effective cache dir: %s\n", cacheDir) - if cacheDir != greeter.GreeterCacheDir { - fmt.Printf(" ⚠ Non-default cache dir detected (default: %s)\n", greeter.GreeterCacheDir) - } - if stat, err := os.Stat(cacheDir); err == nil && stat.IsDir() { - fmt.Printf(" ✓ %s exists\n", cacheDir) - requiredSubdirs := []string{".local/state", ".local/share", ".cache"} - missingSubdirs := false - for _, sub := range requiredSubdirs { - subPath := filepath.Join(cacheDir, sub) - if _, err := os.Stat(subPath); os.IsNotExist(err) { - fmt.Printf(" ⚠ Missing required subdir: %s\n", subPath) - missingSubdirs = true - } - } - if missingSubdirs { - fmt.Println(" Run 'dms greeter sync' to initialize the cache directory structure.") - allGood = false - } - } else { - fmt.Printf(" ✗ %s not found\n", cacheDir) - fmt.Printf(" %s\n", packageInstallHint()) - return nil - } - - fmt.Println("\nConfiguration Symlinks:") - colorSyncInfo, colorSyncErr := greeter.ResolveGreeterColorSyncInfo(homeDir) - if colorSyncErr != nil { - fmt.Printf(" ✗ Failed to resolve expected greeter color source: %v\n", colorSyncErr) - allGood = false - colorSyncInfo = greeter.GreeterColorSyncInfo{ - SourcePath: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"), - } - } - - colorThemeDesc := "Color theme" - if colorSyncInfo.UsesDynamicWallpaperOverride { - colorThemeDesc = "Color theme (greeter wallpaper override)" - } - - symlinks := []struct { - source string - target string - desc string - }{ - { - source: filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json"), - target: filepath.Join(cacheDir, "settings.json"), - desc: "Settings", - }, - { - source: filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json"), - target: filepath.Join(cacheDir, "session.json"), - desc: "Session state", - }, - { - source: colorSyncInfo.SourcePath, - target: filepath.Join(cacheDir, "colors.json"), - desc: colorThemeDesc, - }, - } - - for _, link := range symlinks { - targetInfo, err := os.Lstat(link.target) - if err != nil { - fmt.Printf(" ✗ %s: symlink not found at %s\n", link.desc, link.target) - allGood = false - continue - } - - if targetInfo.Mode()&os.ModeSymlink == 0 { - fmt.Printf(" ✗ %s: %s is not a symlink\n", link.desc, link.target) - allGood = false - continue - } - - linkDest, err := os.Readlink(link.target) - if err != nil { - fmt.Printf(" ✗ %s: failed to read symlink\n", link.desc) - allGood = false - continue - } - - if linkDest != link.source { - fmt.Printf(" ✗ %s: symlink points to wrong location\n", link.desc) - fmt.Printf(" Expected: %s\n", link.source) - fmt.Printf(" Got: %s\n", linkDest) - allGood = false - continue - } - - if _, err := os.Stat(link.source); os.IsNotExist(err) { - fmt.Printf(" ⚠ %s: symlink OK, but source file doesn't exist yet\n", link.desc) - fmt.Printf(" Will be created when you run DMS\n") - continue - } - - fmt.Printf(" ✓ %s: synced correctly\n", link.desc) - } - - if colorSyncInfo.UsesDynamicWallpaperOverride { - fmt.Printf(" ℹ Dynamic theme uses greeter override colors from %s\n", colorSyncInfo.SourcePath) - } - - fmt.Println("\nGreeter Wallpaper Override:") - overridePath := filepath.Join(cacheDir, "greeter_wallpaper_override.jpg") - if stat, err := os.Stat(overridePath); err == nil && !stat.IsDir() { - fmt.Printf(" ✓ Override file present: %s\n", overridePath) - } else if os.IsNotExist(err) { - fmt.Println(" ℹ Override file not present (desktop/session wallpaper fallback in effect)") - } else if err != nil { - fmt.Printf(" ✗ Could not inspect override file: %v\n", err) - allGood = false - } else { - fmt.Printf(" ✗ Override path is not a regular file: %s\n", overridePath) - allGood = false - } - - fmt.Println("\nGreeter PAM Authentication (DMS-managed block):") - if greeter.IsNixOS() { - fmt.Println(" ℹ NixOS detected: PAM is managed by NixOS modules.") - fmt.Println(" Configure fingerprint/U2F via your greetd NixOS module (security.pam.services.greetd).") - fmt.Println() - if allGood && inGreeterGroup { - fmt.Println("✓ All checks passed! Greeter is properly configured.") - } else if !allGood { - fmt.Println("⚠ Some issues detected. Run 'dms greeter sync' to repair configuration.") - } else if !inGreeterGroup { - fmt.Printf("⚠ User is not in %s group. Run 'dms greeter sync' after adding group membership.\n", greeterGroup) - } - return nil - } - greetdPamPath := "/etc/pam.d/greetd" - pamData, err := os.ReadFile(greetdPamPath) - if err != nil { - fmt.Printf(" ✗ Failed to read %s: %v\n", greetdPamPath, err) - allGood = false - } else { - managed, managedFprint, managedU2f, legacyManaged := parseManagedGreeterPamAuth(string(pamData)) - if managed { - fmt.Println(" ✓ Managed auth block present") - if managedFprint { - fmt.Println(" - fingerprint: enabled") - } else { - fmt.Println(" - fingerprint: disabled") - } - if managedU2f { - fmt.Println(" - security key (U2F): enabled") - } else { - fmt.Println(" - security key (U2F): disabled") - } - } else { - fmt.Println(" ℹ No managed auth block present (DMS-managed fingerprint/U2F lines are disabled)") - } - if legacyManaged { - fmt.Println(" ⚠ Legacy unmanaged DMS PAM lines detected. Run 'dms auth sync' to normalize.") - allGood = false - } - enableFprintToggle, enableU2fToggle := false, false - if enableFprint, enableU2f, settingsErr := sharedpam.ReadGreeterAuthToggles(homeDir); settingsErr == nil { - enableFprintToggle = enableFprint - enableU2fToggle = enableU2f - } else { - fmt.Printf(" ℹ Could not read greeter auth toggles from settings: %v\n", settingsErr) - } - - includedFprintFile := sharedpam.DetectIncludedPamModule(string(pamData), "pam_fprintd.so") - includedU2fFile := sharedpam.DetectIncludedPamModule(string(pamData), "pam_u2f.so") - fprintAvailableForCurrentUser := sharedpam.FingerprintAuthAvailableForCurrentUser() - - if managedFprint && includedFprintFile != "" { - fmt.Printf(" ⚠ pam_fprintd found in both DMS managed block and %s.\n", includedFprintFile) - fmt.Println(" Double fingerprint auth detected — run 'dms auth sync' to resolve.") - allGood = false - } - if managedU2f && includedU2fFile != "" { - fmt.Printf(" ⚠ pam_u2f found in both DMS managed block and %s.\n", includedU2fFile) - fmt.Println(" Double security-key auth detected — run 'dms auth sync' to resolve.") - allGood = false - } - - if includedFprintFile != "" && !managedFprint { - if enableFprintToggle { - fmt.Printf(" ℹ Fingerprint auth is enabled via included %s.\n", includedFprintFile) - if fprintAvailableForCurrentUser { - fmt.Println(" DMS toggle is enabled, and effective auth is coming from the included PAM stack.") - } else { - fmt.Println(" No enrolled fingerprints detected for the current user; password auth remains the effective path.") - } - } else { - if fprintAvailableForCurrentUser { - fmt.Printf(" ℹ Fingerprint auth is active via included %s while DMS fingerprint toggle is off.\n", includedFprintFile) - fmt.Println(" Password login will work but may be delayed while the fingerprint module runs first.") - fmt.Printf(" To eliminate the delay, %s\n", systemPamManagerRemediationHint()) - } else { - fmt.Printf(" ℹ pam_fprintd is present via included %s, but no enrolled fingerprints were detected for user %s.\n", includedFprintFile, currentUser.Username) - fmt.Println(" Password auth remains the effective login path.") - } - } - } - if includedU2fFile != "" && !managedU2f { - if enableU2fToggle { - fmt.Printf(" ℹ Security-key auth is enabled via included %s.\n", includedU2fFile) - fmt.Println(" DMS toggle is enabled, but effective auth is coming from the included PAM stack.") - } else { - fmt.Printf(" ⚠ Security-key auth is active via included %s while DMS security-key toggle is off.\n", includedU2fFile) - fmt.Printf(" %s\n", systemPamManagerRemediationHint()) - } - } - } - - fmt.Println("\nSecurity (AppArmor):") - if !greeter.IsAppArmorEnabled() { - fmt.Println(" ℹ AppArmor not enabled") - } else { - fmt.Println(" ℹ AppArmor is enabled") - - const appArmorProfilePath = "/etc/apparmor.d/usr.bin.dms-greeter" - if _, err := os.Stat(appArmorProfilePath); os.IsNotExist(err) { - fmt.Println(" ⚠ DMS AppArmor profile not installed") - fmt.Println(" Run 'dms greeter sync' to install it and prevent potential TTY fallback") - allGood = false - } else { - mode := appArmorProfileMode("dms-greeter") - if mode != "" { - fmt.Printf(" ✓ DMS AppArmor profile installed (%s mode)\n", mode) - } else { - fmt.Println(" ✓ DMS AppArmor profile installed") - } - } - - denialCount, denialSamples, denialErr := recentAppArmorGreeterDenials(3) - if denialErr != nil { - fmt.Printf(" ℹ Could not inspect AppArmor denials automatically: %v\n", denialErr) - fmt.Println(" If greetd falls back to TTY, run: sudo journalctl -b -k | grep 'apparmor.*DENIED'") - } else if denialCount > 0 { - fmt.Printf(" ⚠ Found %d recent AppArmor denial(s) related to greeter runtime.\n", denialCount) - fmt.Println(" This can cause greetd fallback to TTY (for example: 'Failed to create stream fd: Permission denied').") - fmt.Println(" Review denials with: sudo journalctl -b -k | grep 'apparmor.*DENIED'") - fmt.Println(" Then refine the profile with: sudo aa-logprof") - for i, sample := range denialSamples { - fmt.Printf(" %d) %s\n", i+1, sample) - } - allGood = false - } else { - fmt.Println(" ✓ No recent AppArmor denials detected for common greeter components") - } - } - - fmt.Println() - if allGood && inGreeterGroup { - fmt.Println("✓ All checks passed! Greeter is properly configured.") - } else if !allGood { - fmt.Println("⚠ Some issues detected. Run 'dms greeter sync' to repair configuration.") - } else if !inGreeterGroup { - fmt.Printf("⚠ User is not in %s group. Run 'dms greeter sync' after adding group membership.\n", greeterGroup) - } - - return nil -} - -func recentAppArmorGreeterDenials(sampleLimit int) (int, []string, error) { - if sampleLimit <= 0 { - sampleLimit = 3 - } - if !utils.CommandExists("journalctl") { - return 0, nil, fmt.Errorf("journalctl not found") - } - - queries := [][]string{ - {"-b", "-k", "--no-pager", "-n", "2000", "-o", "cat"}, - {"-b", "--no-pager", "-n", "2000", "-o", "cat"}, - } - - seen := make(map[string]bool) - samples := make([]string, 0, sampleLimit) - total := 0 - var lastErr error - successfulQuery := false - - for _, query := range queries { - cmd := exec.Command("journalctl", query...) - output, err := cmd.CombinedOutput() - if err != nil { - lastErr = err - continue - } - successfulQuery = true - total += collectGreeterAppArmorDenials(string(output), seen, &samples, sampleLimit) - } - - if !successfulQuery && lastErr != nil { - return 0, nil, lastErr - } - - return total, samples, nil -} - -func collectGreeterAppArmorDenials(text string, seen map[string]bool, samples *[]string, sampleLimit int) int { - count := 0 - for _, rawLine := range strings.Split(text, "\n") { - line := strings.TrimSpace(rawLine) - if line == "" || !isGreeterRelatedAppArmorDenial(line) { - continue - } - if seen[line] { - continue - } - seen[line] = true - count++ - if len(*samples) < sampleLimit { - *samples = append(*samples, line) - } - } - return count -} - -func isGreeterRelatedAppArmorDenial(line string) bool { - lower := strings.ToLower(line) - if !strings.Contains(lower, "apparmor") || !strings.Contains(lower, "denied") { - return false - } - - greeterTokens := []string{ - "dms-greeter", - "/usr/bin/dms-greeter", - "greetd", - "quickshell", - "/usr/bin/qs", - "/usr/bin/quickshell", - "niri", - "hyprland", - "sway", - "mango", - "miracle", - "labwc", - "pipewire", - "wireplumber", - "stream fd", - } - - for _, token := range greeterTokens { - if strings.Contains(lower, token) { - return true - } - } - return false -} - -// appArmorProfileMode returns "complain", "enforce", or "" for a named AppArmor profile. -func appArmorProfileMode(profileName string) string { - data, err := os.ReadFile("/sys/kernel/security/apparmor/profiles") - if err != nil { - return "" - } - for _, line := range strings.Split(string(data), "\n") { - line = strings.TrimSpace(line) - if !strings.Contains(line, profileName) { - continue - } - lower := strings.ToLower(line) - if strings.Contains(lower, "(complain)") { - return "complain" - } - if strings.Contains(lower, "(enforce)") { - return "enforce" - } - if strings.Contains(lower, "(kill)") { - return "kill" - } - } - return "" -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_greeter_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_greeter_test.go deleted file mode 100644 index 775149f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_greeter_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "errors" - "reflect" - "testing" - - sharedpam "github.com/AvengeMedia/DankMaterialShell/core/internal/pam" -) - -func TestSyncGreeterConfigsAndAuthDelegatesSharedAuth(t *testing.T) { - origGreeterConfigSyncFn := greeterConfigSyncFn - origSharedAuthSyncFn := sharedAuthSyncFn - t.Cleanup(func() { - greeterConfigSyncFn = origGreeterConfigSyncFn - sharedAuthSyncFn = origSharedAuthSyncFn - }) - - var calls []string - greeterConfigSyncFn = func(dmsPath, compositor string, logFunc func(string), sudoPassword string) error { - if dmsPath != "/tmp/dms" { - t.Fatalf("unexpected dmsPath %q", dmsPath) - } - if compositor != "niri" { - t.Fatalf("unexpected compositor %q", compositor) - } - if sudoPassword != "" { - t.Fatalf("expected empty sudoPassword, got %q", sudoPassword) - } - calls = append(calls, "configs") - return nil - } - - var gotOptions sharedpam.SyncAuthOptions - sharedAuthSyncFn = func(logFunc func(string), sudoPassword string, options sharedpam.SyncAuthOptions) error { - if sudoPassword != "" { - t.Fatalf("expected empty sudoPassword, got %q", sudoPassword) - } - gotOptions = options - calls = append(calls, "auth") - return nil - } - - err := syncGreeterConfigsAndAuth("/tmp/dms", "niri", func(string) {}, sharedpam.SyncAuthOptions{ - ForceGreeterAuth: true, - }, func() { - calls = append(calls, "before-auth") - }) - if err != nil { - t.Fatalf("syncGreeterConfigsAndAuth returned error: %v", err) - } - - wantCalls := []string{"configs", "before-auth", "auth"} - if !reflect.DeepEqual(calls, wantCalls) { - t.Fatalf("call order = %v, want %v", calls, wantCalls) - } - if !gotOptions.ForceGreeterAuth { - t.Fatalf("expected ForceGreeterAuth to be true, got %+v", gotOptions) - } -} - -func TestSyncGreeterConfigsAndAuthStopsOnConfigError(t *testing.T) { - origGreeterConfigSyncFn := greeterConfigSyncFn - origSharedAuthSyncFn := sharedAuthSyncFn - t.Cleanup(func() { - greeterConfigSyncFn = origGreeterConfigSyncFn - sharedAuthSyncFn = origSharedAuthSyncFn - }) - - greeterConfigSyncFn = func(string, string, func(string), string) error { - return errors.New("config sync failed") - } - - authCalled := false - sharedAuthSyncFn = func(func(string), string, sharedpam.SyncAuthOptions) error { - authCalled = true - return nil - } - - err := syncGreeterConfigsAndAuth("/tmp/dms", "niri", func(string) {}, sharedpam.SyncAuthOptions{}, nil) - if err == nil || err.Error() != "config sync failed" { - t.Fatalf("expected config sync error, got %v", err) - } - if authCalled { - t.Fatal("expected auth sync not to run after config sync failure") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_keybinds.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_keybinds.go deleted file mode 100644 index cdb15ac..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_keybinds.go +++ /dev/null @@ -1,265 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" - "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds/providers" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/spf13/cobra" -) - -var keybindsCmd = &cobra.Command{ - Use: "keybinds", - Aliases: []string{"cheatsheet", "chsht"}, - Short: "Manage keybinds and cheatsheets", - Long: "Display and manage keybinds and cheatsheets for various applications", -} - -var keybindsListCmd = &cobra.Command{ - Use: "list", - Short: "List available providers", - Long: "List all available keybind/cheatsheet providers", - Run: runKeybindsList, -} - -var keybindsShowCmd = &cobra.Command{ - Use: "show ", - Short: "Show keybinds for a provider", - Long: "Display keybinds/cheatsheet for the specified provider", - Args: cobra.ExactArgs(1), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - registry := keybinds.GetDefaultRegistry() - return registry.List(), cobra.ShellCompDirectiveNoFileComp - }, - Run: runKeybindsShow, -} - -var keybindsSetCmd = &cobra.Command{ - Use: "set ", - Short: "Set a keybind override", - Long: "Create or update a keybind override for the specified provider", - Args: cobra.ExactArgs(3), - Run: runKeybindsSet, -} - -var keybindsRemoveCmd = &cobra.Command{ - Use: "remove ", - Short: "Remove a keybind override", - Long: "Remove a keybind override from the specified provider", - Args: cobra.ExactArgs(2), - Run: runKeybindsRemove, -} - -func init() { - keybindsListCmd.Flags().BoolP("json", "j", false, "Output as JSON") - keybindsShowCmd.Flags().String("path", "", "Override config path for the provider") - keybindsSetCmd.Flags().String("desc", "", "Description for hotkey overlay") - keybindsSetCmd.Flags().Bool("allow-when-locked", false, "Allow when screen is locked") - keybindsSetCmd.Flags().Int("cooldown-ms", 0, "Cooldown in milliseconds") - keybindsSetCmd.Flags().Bool("no-repeat", false, "Disable key repeat") - keybindsSetCmd.Flags().Bool("no-inhibiting", false, "Keep bind active when shortcuts are inhibited (allow-inhibiting=false)") - keybindsSetCmd.Flags().String("replace-key", "", "Original key to replace (removes old key)") - keybindsSetCmd.Flags().String("flags", "", "Hyprland bind flags (e.g., 'e' for repeat, 'l' for locked, 'r' for release)") - - keybindsCmd.AddCommand(keybindsListCmd) - keybindsCmd.AddCommand(keybindsShowCmd) - keybindsCmd.AddCommand(keybindsSetCmd) - keybindsCmd.AddCommand(keybindsRemoveCmd) - - keybinds.SetJSONProviderFactory(func(filePath string) (keybinds.Provider, error) { - return providers.NewJSONFileProvider(filePath) - }) - - initializeProviders() -} - -func initializeProviders() { - registry := keybinds.GetDefaultRegistry() - - hyprlandProvider := providers.NewHyprlandProvider("") - if err := registry.Register(hyprlandProvider); err != nil { - log.Warnf("Failed to register Hyprland provider: %v", err) - } - - mangowcProvider := providers.NewMangoWCProvider("") - if err := registry.Register(mangowcProvider); err != nil { - log.Warnf("Failed to register MangoWC provider: %v", err) - } - - configDir, _ := os.UserConfigDir() - - if configDir != "" { - scrollProvider := providers.NewSwayProvider(filepath.Join(configDir, "scroll")) - if err := registry.Register(scrollProvider); err != nil { - log.Warnf("Failed to register Scroll provider: %v", err) - } - } - - miracleProvider := providers.NewMiracleProvider("") - if err := registry.Register(miracleProvider); err != nil { - log.Warnf("Failed to register Miracle WM provider: %v", err) - } - - if configDir != "" { - swayProvider := providers.NewSwayProvider(filepath.Join(configDir, "sway")) - if err := registry.Register(swayProvider); err != nil { - log.Warnf("Failed to register Sway provider: %v", err) - } - } - - niriProvider := providers.NewNiriProvider("") - if err := registry.Register(niriProvider); err != nil { - log.Warnf("Failed to register Niri provider: %v", err) - } - - config := keybinds.DefaultDiscoveryConfig() - if err := keybinds.AutoDiscoverProviders(registry, config); err != nil { - log.Warnf("Failed to auto-discover providers: %v", err) - } -} - -func runKeybindsList(cmd *cobra.Command, _ []string) { - providerList := keybinds.GetDefaultRegistry().List() - asJSON, _ := cmd.Flags().GetBool("json") - - if asJSON { - output, _ := json.Marshal(providerList) - fmt.Fprintln(os.Stdout, string(output)) - return - } - - if len(providerList) == 0 { - fmt.Fprintln(os.Stdout, "No providers available") - return - } - - fmt.Fprintln(os.Stdout, "Available providers:") - for _, name := range providerList { - fmt.Fprintf(os.Stdout, " - %s\n", name) - } -} - -func makeProviderWithPath(name, path string) keybinds.Provider { - switch name { - case "hyprland": - return providers.NewHyprlandProvider(path) - case "mangowc": - return providers.NewMangoWCProvider(path) - case "sway": - return providers.NewSwayProvider(path) - case "scroll": - return providers.NewSwayProvider(path) - case "miracle": - return providers.NewMiracleProvider(path) - case "niri": - return providers.NewNiriProvider(path) - default: - return nil - } -} - -func printCheatSheet(provider keybinds.Provider) { - sheet, err := provider.GetCheatSheet() - if err != nil { - log.Fatalf("Error getting cheatsheet: %v", err) - } - output, err := json.MarshalIndent(sheet, "", " ") - if err != nil { - log.Fatalf("Error generating JSON: %v", err) - } - fmt.Fprintln(os.Stdout, string(output)) -} - -func runKeybindsShow(cmd *cobra.Command, args []string) { - providerName := args[0] - customPath, _ := cmd.Flags().GetString("path") - - if customPath != "" { - provider := makeProviderWithPath(providerName, customPath) - if provider == nil { - log.Fatalf("Provider %s does not support custom path", providerName) - } - printCheatSheet(provider) - return - } - - provider, err := keybinds.GetDefaultRegistry().Get(providerName) - if err != nil { - log.Fatalf("Error: %v", err) - } - printCheatSheet(provider) -} - -func getWritableProvider(name string) keybinds.WritableProvider { - provider, err := keybinds.GetDefaultRegistry().Get(name) - if err != nil { - log.Fatalf("Error: %v", err) - } - writable, ok := provider.(keybinds.WritableProvider) - if !ok { - log.Fatalf("Provider %s does not support writing keybinds", name) - } - return writable -} - -func runKeybindsSet(cmd *cobra.Command, args []string) { - providerName, key, action := args[0], args[1], args[2] - writable := getWritableProvider(providerName) - - if replaceKey, _ := cmd.Flags().GetString("replace-key"); replaceKey != "" && replaceKey != key { - _ = writable.RemoveBind(replaceKey) - } - - options := make(map[string]any) - if v, _ := cmd.Flags().GetBool("allow-when-locked"); v { - options["allow-when-locked"] = true - } - if v, _ := cmd.Flags().GetInt("cooldown-ms"); v > 0 { - options["cooldown-ms"] = v - } - if v, _ := cmd.Flags().GetBool("no-repeat"); v { - options["repeat"] = false - } - if v, _ := cmd.Flags().GetBool("no-inhibiting"); v { - options["allow-inhibiting"] = false - } - if v, _ := cmd.Flags().GetString("flags"); v != "" { - options["flags"] = v - } - - desc, _ := cmd.Flags().GetString("desc") - if err := writable.SetBind(key, action, desc, options); err != nil { - log.Fatalf("Error setting keybind: %v", err) - } - - output, _ := json.MarshalIndent(map[string]any{ - "success": true, - "key": key, - "action": action, - "path": writable.GetOverridePath(), - }, "", " ") - fmt.Fprintln(os.Stdout, string(output)) -} - -func runKeybindsRemove(_ *cobra.Command, args []string) { - providerName, key := args[0], args[1] - writable := getWritableProvider(providerName) - - if err := writable.RemoveBind(key); err != nil { - log.Fatalf("Error removing keybind: %v", err) - } - - output, _ := json.MarshalIndent(map[string]any{ - "success": true, - "key": key, - "removed": true, - }, "", " ") - fmt.Fprintln(os.Stdout, string(output)) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_matugen.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_matugen.go deleted file mode 100644 index 566f360..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_matugen.go +++ /dev/null @@ -1,202 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "os" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/matugen" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/spf13/cobra" -) - -var matugenCmd = &cobra.Command{ - Use: "matugen", - Short: "Generate Material Design themes", - Long: "Generate Material Design themes using matugen with dank16 color integration", -} - -var matugenGenerateCmd = &cobra.Command{ - Use: "generate", - Short: "Generate theme synchronously", - Run: runMatugenGenerate, -} - -var matugenQueueCmd = &cobra.Command{ - Use: "queue", - Short: "Queue theme generation (uses socket if available)", - Run: runMatugenQueue, -} - -var matugenCheckCmd = &cobra.Command{ - Use: "check", - Short: "Check which template apps are detected", - Run: runMatugenCheck, -} - -func init() { - matugenCmd.AddCommand(matugenGenerateCmd) - matugenCmd.AddCommand(matugenQueueCmd) - matugenCmd.AddCommand(matugenCheckCmd) - - for _, cmd := range []*cobra.Command{matugenGenerateCmd, matugenQueueCmd} { - cmd.Flags().String("state-dir", "", "State directory for cache files") - cmd.Flags().String("shell-dir", "", "DMS shell installation directory") - cmd.Flags().String("config-dir", "", "User config directory") - cmd.Flags().String("kind", "image", "Source type: image or hex") - cmd.Flags().String("value", "", "Wallpaper path or hex color") - cmd.Flags().String("mode", "dark", "Color mode: dark or light") - cmd.Flags().String("icon-theme", "System Default", "Icon theme name") - cmd.Flags().String("matugen-type", "scheme-tonal-spot", "Matugen scheme type") - cmd.Flags().Bool("run-user-templates", true, "Run user matugen templates") - cmd.Flags().String("stock-colors", "", "Stock theme colors JSON") - cmd.Flags().Bool("sync-mode-with-portal", false, "Sync color scheme with GNOME portal") - cmd.Flags().Bool("terminals-always-dark", false, "Force terminal themes to dark variant") - cmd.Flags().String("skip-templates", "", "Comma-separated list of templates to skip") - cmd.Flags().Float64("contrast", 0, "Contrast value from -1 to 1 (0 = standard)") - } - - matugenQueueCmd.Flags().Bool("wait", true, "Wait for completion") - matugenQueueCmd.Flags().Duration("timeout", 90*time.Second, "Timeout for waiting") -} - -func buildMatugenOptions(cmd *cobra.Command) matugen.Options { - stateDir, _ := cmd.Flags().GetString("state-dir") - shellDir, _ := cmd.Flags().GetString("shell-dir") - configDir, _ := cmd.Flags().GetString("config-dir") - kind, _ := cmd.Flags().GetString("kind") - value, _ := cmd.Flags().GetString("value") - mode, _ := cmd.Flags().GetString("mode") - iconTheme, _ := cmd.Flags().GetString("icon-theme") - matugenType, _ := cmd.Flags().GetString("matugen-type") - runUserTemplates, _ := cmd.Flags().GetBool("run-user-templates") - stockColors, _ := cmd.Flags().GetString("stock-colors") - syncModeWithPortal, _ := cmd.Flags().GetBool("sync-mode-with-portal") - terminalsAlwaysDark, _ := cmd.Flags().GetBool("terminals-always-dark") - skipTemplates, _ := cmd.Flags().GetString("skip-templates") - contrast, _ := cmd.Flags().GetFloat64("contrast") - - return matugen.Options{ - StateDir: stateDir, - ShellDir: shellDir, - ConfigDir: configDir, - Kind: kind, - Value: value, - Mode: matugen.ColorMode(mode), - IconTheme: iconTheme, - MatugenType: matugenType, - Contrast: contrast, - RunUserTemplates: runUserTemplates, - StockColors: stockColors, - SyncModeWithPortal: syncModeWithPortal, - TerminalsAlwaysDark: terminalsAlwaysDark, - SkipTemplates: skipTemplates, - } -} - -func runMatugenGenerate(cmd *cobra.Command, args []string) { - opts := buildMatugenOptions(cmd) - err := matugen.Run(opts) - switch { - case errors.Is(err, matugen.ErrNoChanges): - os.Exit(2) - case err != nil: - log.Fatalf("Theme generation failed: %v", err) - } -} - -func runMatugenQueue(cmd *cobra.Command, args []string) { - opts := buildMatugenOptions(cmd) - wait, _ := cmd.Flags().GetBool("wait") - timeout, _ := cmd.Flags().GetDuration("timeout") - - request := models.Request{ - ID: 1, - Method: "matugen.queue", - Params: map[string]any{ - "stateDir": opts.StateDir, - "shellDir": opts.ShellDir, - "configDir": opts.ConfigDir, - "kind": opts.Kind, - "value": opts.Value, - "mode": opts.Mode, - "iconTheme": opts.IconTheme, - "matugenType": opts.MatugenType, - "runUserTemplates": opts.RunUserTemplates, - "stockColors": opts.StockColors, - "syncModeWithPortal": opts.SyncModeWithPortal, - "terminalsAlwaysDark": opts.TerminalsAlwaysDark, - "skipTemplates": opts.SkipTemplates, - "contrast": opts.Contrast, - "wait": wait, - }, - } - - if !wait { - if err := sendServerRequestFireAndForget(request); err != nil { - log.Info("Server unavailable, running synchronously") - err := matugen.Run(opts) - switch { - case errors.Is(err, matugen.ErrNoChanges): - os.Exit(2) - case err != nil: - log.Fatalf("Theme generation failed: %v", err) - } - return - } - fmt.Println("Theme generation queued") - return - } - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - resultCh := make(chan error, 1) - go func() { - resp, ok := tryServerRequest(request) - if !ok { - log.Info("Server unavailable, running synchronously") - err := matugen.Run(opts) - switch { - case errors.Is(err, matugen.ErrNoChanges): - resultCh <- matugen.ErrNoChanges - case err != nil: - resultCh <- err - default: - resultCh <- nil - } - return - } - if resp.Error != "" { - resultCh <- fmt.Errorf("server error: %s", resp.Error) - return - } - resultCh <- nil - }() - - select { - case err := <-resultCh: - switch { - case errors.Is(err, matugen.ErrNoChanges): - os.Exit(2) - case err != nil: - log.Fatalf("Theme generation failed: %v", err) - } - fmt.Println("Theme generation completed") - case <-ctx.Done(): - log.Fatalf("Timeout waiting for theme generation") - } -} - -func runMatugenCheck(cmd *cobra.Command, args []string) { - checks := matugen.CheckTemplates(nil) - data, err := json.Marshal(checks) - if err != nil { - log.Fatalf("Failed to marshal check results: %v", err) - } - fmt.Println(string(data)) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_notify.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_notify.go deleted file mode 100644 index e04b60f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_notify.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/notify" - "github.com/spf13/cobra" -) - -var ( - notifyAppName string - notifyIcon string - notifyFile string - notifyTimeout int -) - -var notifyCmd = &cobra.Command{ - Use: "notify [body]", - Short: "Send a desktop notification", - Long: `Send a desktop notification with optional actions. - -If --file is provided, the notification will have "Open" and "Open Folder" actions. - -Examples: - dms notify "Hello" "World" - dms notify "File received" "photo.jpg" --file ~/Downloads/photo.jpg --icon smartphone - dms notify "Download complete" --file ~/Downloads/file.zip --app "My App"`, - Args: cobra.MinimumNArgs(1), - Run: runNotify, -} - -var genericNotifyActionCmd = &cobra.Command{ - Use: "notify-action-generic", - Hidden: true, - Run: func(cmd *cobra.Command, args []string) { - notify.RunActionListener(args) - }, -} - -func init() { - notifyCmd.Flags().StringVar(¬ifyAppName, "app", "DMS", "Application name") - notifyCmd.Flags().StringVar(¬ifyIcon, "icon", "", "Icon name or path") - notifyCmd.Flags().StringVar(¬ifyFile, "file", "", "File path (enables Open/Open Folder actions)") - notifyCmd.Flags().IntVar(¬ifyTimeout, "timeout", 5000, "Timeout in milliseconds") -} - -func runNotify(cmd *cobra.Command, args []string) { - summary := args[0] - body := "" - if len(args) > 1 { - body = args[1] - } - - n := notify.Notification{ - AppName: notifyAppName, - Icon: notifyIcon, - Summary: summary, - Body: body, - FilePath: notifyFile, - Timeout: int32(notifyTimeout), - } - - if err := notify.Send(n); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_open.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_open.go deleted file mode 100644 index 8cc16cf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_open.go +++ /dev/null @@ -1,205 +0,0 @@ -package main - -import ( - "fmt" - "mime" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/spf13/cobra" -) - -var ( - openMimeType string - openCategories []string - openRequestType string -) - -var openCmd = &cobra.Command{ - Use: "open [target]", - Short: "Open a file, URL, or resource with an application picker", - Long: `Open a target (URL, file, or other resource) using the DMS application picker. -By default, this opens URLs with the browser picker. You can customize the behavior -with flags to handle different MIME types or application categories. - -Examples: - dms open https://example.com # Open URL with browser picker - dms open file.pdf --mime application/pdf # Open PDF with compatible apps - dms open document.odt --category Office # Open with office applications - dms open --mime image/png image.png # Open image with image viewers`, - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - runOpen(args[0]) - }, -} - -func init() { - rootCmd.AddCommand(openCmd) - openCmd.Flags().StringVar(&openMimeType, "mime", "", "MIME type for filtering applications") - openCmd.Flags().StringSliceVar(&openCategories, "category", []string{}, "Application categories to filter (e.g., WebBrowser, Office, Graphics)") - openCmd.Flags().StringVar(&openRequestType, "type", "url", "Request type (url, file, or custom)") - _ = openCmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"url", "file", "custom"}, cobra.ShellCompDirectiveNoFileComp - }) -} - -// mimeTypeToCategories maps MIME types to desktop file categories -func mimeTypeToCategories(mimeType string) []string { - // Split MIME type to get the main type - parts := strings.Split(mimeType, "/") - if len(parts) < 1 { - return nil - } - - mainType := parts[0] - - switch mainType { - case "image": - return []string{"Graphics", "Viewer"} - case "video": - return []string{"Video", "AudioVideo"} - case "audio": - return []string{"Audio", "AudioVideo"} - case "text": - if strings.Contains(mimeType, "html") { - return []string{"WebBrowser"} - } - return []string{"TextEditor", "Office"} - case "application": - if strings.Contains(mimeType, "pdf") { - return []string{"Office", "Viewer"} - } - if strings.Contains(mimeType, "document") || strings.Contains(mimeType, "spreadsheet") || - strings.Contains(mimeType, "presentation") || strings.Contains(mimeType, "msword") || - strings.Contains(mimeType, "ms-excel") || strings.Contains(mimeType, "ms-powerpoint") || - strings.Contains(mimeType, "opendocument") { - return []string{"Office"} - } - if strings.Contains(mimeType, "zip") || strings.Contains(mimeType, "tar") || - strings.Contains(mimeType, "gzip") || strings.Contains(mimeType, "compress") { - return []string{"Archiving", "Utility"} - } - return []string{"Office", "Viewer"} - } - - return nil -} - -func runOpen(target string) { - // Parse file:// URIs to extract the actual file path - actualTarget := target - detectedMimeType := openMimeType - detectedCategories := openCategories - detectedRequestType := openRequestType - - log.Infof("Processing target: %s", target) - - if parsedURL, err := url.Parse(target); err == nil && parsedURL.Scheme == "file" { - // Extract file path from file:// URI and convert to absolute path - actualTarget = parsedURL.Path - if absPath, err := filepath.Abs(actualTarget); err == nil { - actualTarget = absPath - } - - if detectedRequestType == "url" || detectedRequestType == "" { - detectedRequestType = "file" - } - - log.Infof("Detected file:// URI, extracted absolute path: %s", actualTarget) - - // Auto-detect MIME type if not provided - if detectedMimeType == "" { - ext := filepath.Ext(actualTarget) - if ext != "" { - detectedMimeType = mime.TypeByExtension(ext) - log.Infof("Detected MIME type from extension %s: %s", ext, detectedMimeType) - } - } - - // Auto-detect categories based on MIME type if not provided - if len(detectedCategories) == 0 && detectedMimeType != "" { - detectedCategories = mimeTypeToCategories(detectedMimeType) - log.Infof("Detected categories from MIME type: %v", detectedCategories) - } - } else if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") { - // Handle HTTP(S) URLs - if detectedRequestType == "" { - detectedRequestType = "url" - } - log.Infof("Detected HTTP(S) URL") - } else if strings.HasPrefix(target, "dms://") { - // Handle DMS internal URLs (theme/plugin install, etc.) - if detectedRequestType == "" { - detectedRequestType = "url" - } - log.Infof("Detected DMS internal URL") - } else if _, err := os.Stat(target); err == nil { - // Handle local file paths directly (not file:// URIs) - // Convert to absolute path - if absPath, err := filepath.Abs(target); err == nil { - actualTarget = absPath - } - - if detectedRequestType == "url" || detectedRequestType == "" { - detectedRequestType = "file" - } - - log.Infof("Detected local file path, converted to absolute: %s", actualTarget) - - // Auto-detect MIME type if not provided - if detectedMimeType == "" { - ext := filepath.Ext(actualTarget) - if ext != "" { - detectedMimeType = mime.TypeByExtension(ext) - log.Infof("Detected MIME type from extension %s: %s", ext, detectedMimeType) - } - } - - // Auto-detect categories based on MIME type if not provided - if len(detectedCategories) == 0 && detectedMimeType != "" { - detectedCategories = mimeTypeToCategories(detectedMimeType) - log.Infof("Detected categories from MIME type: %v", detectedCategories) - } - } - - params := map[string]any{ - "target": actualTarget, - } - - if detectedMimeType != "" { - params["mimeType"] = detectedMimeType - } - - if len(detectedCategories) > 0 { - params["categories"] = detectedCategories - } - - if detectedRequestType != "" { - params["requestType"] = detectedRequestType - } - - method := "apppicker.open" - if detectedMimeType == "" && len(detectedCategories) == 0 && (strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") || strings.HasPrefix(target, "dms://")) { - method = "browser.open" - params["url"] = target - } - - req := models.Request{ - ID: 1, - Method: method, - Params: params, - } - - log.Infof("Sending request - Method: %s, Params: %+v", method, params) - - if err := sendServerRequestFireAndForget(req); err != nil { - fmt.Println("DMS is not running. Please start DMS first.") - os.Exit(1) - } - - log.Infof("Request sent successfully") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_randr.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_randr.go deleted file mode 100644 index fa0c990..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_randr.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/spf13/cobra" -) - -var randrCmd = &cobra.Command{ - Use: "randr", - Short: "Query output display information", - Long: "Query Wayland compositor for output names, scales, resolutions and refresh rates via zwlr-output-management", - Run: runRandr, -} - -func init() { - randrCmd.Flags().Bool("json", false, "Output in JSON format") -} - -type randrJSON struct { - Outputs []randrOutput `json:"outputs"` -} - -func runRandr(cmd *cobra.Command, args []string) { - outputs, err := queryRandr() - if err != nil { - log.Fatalf("%v", err) - } - - jsonFlag, _ := cmd.Flags().GetBool("json") - - if jsonFlag { - data, err := json.Marshal(randrJSON{Outputs: outputs}) - if err != nil { - log.Fatalf("failed to marshal JSON: %v", err) - } - fmt.Println(string(data)) - return - } - - for i, out := range outputs { - if i > 0 { - fmt.Println() - } - status := "enabled" - if !out.Enabled { - status = "disabled" - } - fmt.Printf("%s (%s)\n", out.Name, status) - fmt.Printf(" Scale: %.4g\n", out.Scale) - fmt.Printf(" Resolution: %dx%d\n", out.Width, out.Height) - if out.Refresh > 0 { - fmt.Printf(" Refresh: %.2f Hz\n", float64(out.Refresh)/1000.0) - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_root.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_root.go deleted file mode 100644 index b263ae6..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_root.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/config" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/spf13/cobra" -) - -var customConfigPath string -var configPath string - -var rootCmd = &cobra.Command{ - Use: "dms", - Short: "dms CLI", - Long: "dms is the DankMaterialShell management CLI and backend server.", -} - -func init() { - rootCmd.PersistentFlags().StringVarP(&customConfigPath, "config", "c", "", "Specify a custom path to the DMS config directory") -} - -func findConfig(cmd *cobra.Command, args []string) error { - if customConfigPath != "" { - log.Debug("Custom config path provided via -c flag: %s", customConfigPath) - shellPath := filepath.Join(customConfigPath, "shell.qml") - - info, statErr := os.Stat(shellPath) - - if statErr == nil && !info.IsDir() { - configPath = customConfigPath - log.Debug("Using config from: %s", configPath) - return nil - } - - if statErr != nil { - return fmt.Errorf("custom config path error: %w", statErr) - } - - return fmt.Errorf("path is a directory, not a file: %s", shellPath) - } - - configStateFile := filepath.Join(getRuntimeDir(), "danklinux.path") - if data, readErr := os.ReadFile(configStateFile); readErr == nil { - if len(getAllDMSPIDs()) == 0 { - os.Remove(configStateFile) - } else { - statePath := strings.TrimSpace(string(data)) - shellPath := filepath.Join(statePath, "shell.qml") - - if info, statErr := os.Stat(shellPath); statErr == nil && !info.IsDir() { - log.Debug("Using config from active session state file: %s", statePath) - configPath = statePath - log.Debug("Using config from: %s", configPath) - return nil - } - os.Remove(configStateFile) - } - } - - log.Debug("No custom path or active session, searching default XDG locations...") - var err error - configPath, err = config.LocateDMSConfig() - if err != nil { - return err - } - - log.Debug("Using config from: %s", configPath) - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_screenshot.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_screenshot.go deleted file mode 100644 index 2875019..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_screenshot.go +++ /dev/null @@ -1,422 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/screenshot" - "github.com/spf13/cobra" -) - -var ( - ssOutputName string - ssCursor string - ssFormat string - ssQuality int - ssOutputDir string - ssFilename string - ssNoClipboard bool - ssNoFile bool - ssNoNotify bool - ssNoConfirm bool - ssReset bool - ssStdout bool -) - -var screenshotCmd = &cobra.Command{ - Use: "screenshot", - Short: "Capture screenshots", - Long: `Capture screenshots from Wayland displays. - -Modes: - region - Select a region interactively (default) - full - Capture the focused output - all - Capture all outputs combined - output - Capture a specific output by name - window - Capture the focused window (Hyprland/DWL) - last - Capture the last selected region - -Output format (--format): - png - PNG format (default) - jpg/jpeg - JPEG format - ppm - PPM format - -Examples: - dms screenshot # Region select, save file + clipboard - dms screenshot full # Full screen of focused output - dms screenshot all # All screens combined - dms screenshot output -o DP-1 # Specific output - dms screenshot window # Focused window (Hyprland) - dms screenshot last # Last region (pre-selected) - dms screenshot --reset # Reset last region pre-selection - dms screenshot --no-clipboard # Save file only - dms screenshot --no-file # Clipboard only - dms screenshot --no-confirm # Region capture on mouse release - dms screenshot --cursor=on # Include cursor - dms screenshot -f jpg -q 85 # JPEG with quality 85`, -} - -var ssRegionCmd = &cobra.Command{ - Use: "region", - Short: "Select a region interactively", - Run: runScreenshotRegion, -} - -var ssFullCmd = &cobra.Command{ - Use: "full", - Short: "Capture the focused output", - Run: runScreenshotFull, -} - -var ssAllCmd = &cobra.Command{ - Use: "all", - Short: "Capture all outputs combined", - Run: runScreenshotAll, -} - -var ssOutputCmd = &cobra.Command{ - Use: "output", - Short: "Capture a specific output", - Run: runScreenshotOutput, -} - -var ssLastCmd = &cobra.Command{ - Use: "last", - Short: "Capture the last selected region", - Long: `Capture the previously selected region without interactive selection. -If no previous region exists, falls back to interactive selection.`, - Run: runScreenshotLast, -} - -var ssWindowCmd = &cobra.Command{ - Use: "window", - Short: "Capture the focused window", - Long: `Capture the currently focused window. Supported on Hyprland and DWL.`, - Run: runScreenshotWindow, -} - -var ssListCmd = &cobra.Command{ - Use: "list", - Short: "List available outputs", - Run: runScreenshotList, -} - -var notifyActionCmd = &cobra.Command{ - Use: "notify-action", - Hidden: true, - Run: func(cmd *cobra.Command, args []string) { - screenshot.RunNotifyActionListener(args) - }, -} - -func init() { - screenshotCmd.PersistentFlags().StringVarP(&ssOutputName, "output", "o", "", "Output name for 'output' mode") - screenshotCmd.PersistentFlags().StringVar(&ssCursor, "cursor", "off", "Include cursor in screenshot (on/off)") - screenshotCmd.PersistentFlags().StringVarP(&ssFormat, "format", "f", "png", "Output format (png, jpg, ppm)") - screenshotCmd.PersistentFlags().IntVarP(&ssQuality, "quality", "q", 90, "JPEG quality (1-100)") - screenshotCmd.PersistentFlags().StringVarP(&ssOutputDir, "dir", "d", "", "Output directory") - screenshotCmd.PersistentFlags().StringVar(&ssFilename, "filename", "", "Output filename (auto-generated if empty)") - screenshotCmd.PersistentFlags().BoolVar(&ssNoClipboard, "no-clipboard", false, "Don't copy to clipboard") - screenshotCmd.PersistentFlags().BoolVar(&ssNoFile, "no-file", false, "Don't save to file") - screenshotCmd.PersistentFlags().BoolVar(&ssNoNotify, "no-notify", false, "Don't show notification") - screenshotCmd.PersistentFlags().BoolVar(&ssNoConfirm, "no-confirm", false, "Region mode: capture on mouse release without Enter/Space confirmation") - screenshotCmd.PersistentFlags().BoolVar(&ssReset, "reset", false, "Reset saved last-region preselection before capturing") - screenshotCmd.PersistentFlags().BoolVar(&ssStdout, "stdout", false, "Output image to stdout (for piping to swappy, etc.)") - - screenshotCmd.AddCommand(ssRegionCmd) - screenshotCmd.AddCommand(ssFullCmd) - screenshotCmd.AddCommand(ssAllCmd) - screenshotCmd.AddCommand(ssOutputCmd) - screenshotCmd.AddCommand(ssLastCmd) - screenshotCmd.AddCommand(ssWindowCmd) - screenshotCmd.AddCommand(ssListCmd) - - screenshotCmd.Run = runScreenshotRegion -} - -func getScreenshotConfig(mode screenshot.Mode) screenshot.Config { - config := screenshot.DefaultConfig() - config.Mode = mode - config.OutputName = ssOutputName - if strings.EqualFold(ssCursor, "on") { - config.Cursor = screenshot.CursorOn - } - config.Clipboard = !ssNoClipboard - config.SaveFile = !ssNoFile - config.Notify = !ssNoNotify - config.NoConfirm = ssNoConfirm - config.Reset = ssReset - config.Stdout = ssStdout - - if ssOutputDir != "" { - config.OutputDir = ssOutputDir - } - if ssFilename != "" { - config.Filename = ssFilename - } - - switch strings.ToLower(ssFormat) { - case "jpg", "jpeg": - config.Format = screenshot.FormatJPEG - case "ppm": - config.Format = screenshot.FormatPPM - default: - config.Format = screenshot.FormatPNG - } - - if ssQuality < 1 { - ssQuality = 1 - } - if ssQuality > 100 { - ssQuality = 100 - } - config.Quality = ssQuality - - return config -} - -func runScreenshot(config screenshot.Config) { - sc := screenshot.New(config) - result, err := sc.Run() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - - if result == nil { - os.Exit(0) - } - - defer result.Buffer.Close() - - if result.YInverted { - result.Buffer.FlipVertical() - } - - if config.Stdout { - if err := writeImageToStdout(result.Buffer, config.Format, config.Quality, result.Format); err != nil { - fmt.Fprintf(os.Stderr, "Error writing to stdout: %v\n", err) - os.Exit(1) - } - return - } - - var filePath string - - if config.SaveFile { - outputDir := config.OutputDir - if outputDir == "" { - outputDir = screenshot.GetOutputDir() - } - - filename := config.Filename - if filename == "" { - filename = screenshot.GenerateFilename(config.Format) - } - - filePath = filepath.Join(outputDir, filename) - if err := screenshot.WriteToFileWithFormat(result.Buffer, filePath, config.Format, config.Quality, result.Format); err != nil { - fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err) - os.Exit(1) - } - fmt.Println(filePath) - } - - if config.Clipboard { - if err := copyImageToClipboard(result.Buffer, config.Format, config.Quality, result.Format); err != nil { - fmt.Fprintf(os.Stderr, "Error copying to clipboard: %v\n", err) - os.Exit(1) - } - if !config.SaveFile { - fmt.Println("Copied to clipboard") - } - } - - if config.Notify { - thumbData, thumbW, thumbH := bufferToRGBThumbnail(result.Buffer, 256, result.Format) - screenshot.SendNotification(screenshot.NotifyResult{ - FilePath: filePath, - Clipboard: config.Clipboard, - ImageData: thumbData, - Width: thumbW, - Height: thumbH, - }) - } -} - -func copyImageToClipboard(buf *screenshot.ShmBuffer, format screenshot.Format, quality int, pixelFormat uint32) error { - var mimeType string - var data bytes.Buffer - - img := screenshot.BufferToImageWithFormat(buf, pixelFormat) - - switch format { - case screenshot.FormatJPEG: - mimeType = "image/jpeg" - if err := screenshot.EncodeJPEG(&data, img, quality); err != nil { - return err - } - default: - mimeType = "image/png" - if err := screenshot.EncodePNG(&data, img); err != nil { - return err - } - } - - return clipboard.Copy(data.Bytes(), mimeType) -} - -func writeImageToStdout(buf *screenshot.ShmBuffer, format screenshot.Format, quality int, pixelFormat uint32) error { - img := screenshot.BufferToImageWithFormat(buf, pixelFormat) - - switch format { - case screenshot.FormatJPEG: - return screenshot.EncodeJPEG(os.Stdout, img, quality) - default: - return screenshot.EncodePNG(os.Stdout, img) - } -} - -func bufferToRGBThumbnail(buf *screenshot.ShmBuffer, maxSize int, pixelFormat uint32) ([]byte, int, int) { - srcW, srcH := buf.Width, buf.Height - scale := 1.0 - if srcW > maxSize || srcH > maxSize { - if srcW > srcH { - scale = float64(maxSize) / float64(srcW) - } else { - scale = float64(maxSize) / float64(srcH) - } - } - - dstW := int(float64(srcW) * scale) - dstH := int(float64(srcH) * scale) - if dstW < 1 { - dstW = 1 - } - if dstH < 1 { - dstH = 1 - } - - data := buf.Data() - rgb := make([]byte, dstW*dstH*3) - - var swapRB bool - switch pixelFormat { - case uint32(screenshot.FormatABGR8888), uint32(screenshot.FormatXBGR8888): - swapRB = false - default: - swapRB = true - } - - for y := 0; y < dstH; y++ { - srcY := int(float64(y) / scale) - if srcY >= srcH { - srcY = srcH - 1 - } - for x := 0; x < dstW; x++ { - srcX := int(float64(x) / scale) - if srcX >= srcW { - srcX = srcW - 1 - } - si := srcY*buf.Stride + srcX*4 - di := (y*dstW + x) * 3 - if si+3 >= len(data) { - continue - } - if swapRB { - rgb[di+0] = data[si+2] - rgb[di+1] = data[si+1] - rgb[di+2] = data[si+0] - } else { - rgb[di+0] = data[si+0] - rgb[di+1] = data[si+1] - rgb[di+2] = data[si+2] - } - } - } - return rgb, dstW, dstH -} - -func runScreenshotRegion(cmd *cobra.Command, args []string) { - config := getScreenshotConfig(screenshot.ModeRegion) - runScreenshot(config) -} - -func runScreenshotFull(cmd *cobra.Command, args []string) { - config := getScreenshotConfig(screenshot.ModeFullScreen) - runScreenshot(config) -} - -func runScreenshotAll(cmd *cobra.Command, args []string) { - config := getScreenshotConfig(screenshot.ModeAllScreens) - runScreenshot(config) -} - -func runScreenshotOutput(cmd *cobra.Command, args []string) { - if ssOutputName == "" && len(args) > 0 { - ssOutputName = args[0] - } - if ssOutputName == "" { - fmt.Fprintln(os.Stderr, "Error: output name required (use -o or provide as argument)") - os.Exit(1) - } - config := getScreenshotConfig(screenshot.ModeOutput) - runScreenshot(config) -} - -func runScreenshotLast(cmd *cobra.Command, args []string) { - config := getScreenshotConfig(screenshot.ModeLastRegion) - runScreenshot(config) -} - -func runScreenshotWindow(cmd *cobra.Command, args []string) { - config := getScreenshotConfig(screenshot.ModeWindow) - runScreenshot(config) -} - -func runScreenshotList(cmd *cobra.Command, args []string) { - outputs, err := screenshot.ListOutputs() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - - for _, o := range outputs { - scaleStr := fmt.Sprintf("%.2f", o.FractionalScale) - if o.FractionalScale == float64(int(o.FractionalScale)) { - scaleStr = fmt.Sprintf("%d", int(o.FractionalScale)) - } - - transformStr := transformName(o.Transform) - - fmt.Printf("%s: %dx%d+%d+%d scale=%s transform=%s\n", - o.Name, o.Width, o.Height, o.X, o.Y, scaleStr, transformStr) - } -} - -func transformName(t int32) string { - switch t { - case 0: - return "normal" - case 1: - return "90" - case 2: - return "180" - case 3: - return "270" - case 4: - return "flipped" - case 5: - return "flipped-90" - case 6: - return "flipped-180" - case 7: - return "flipped-270" - default: - return fmt.Sprintf("%d", t) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_setup.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_setup.go deleted file mode 100644 index 0f4a58d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_setup.go +++ /dev/null @@ -1,436 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/config" - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/greeter" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/spf13/cobra" -) - -var setupCmd = &cobra.Command{ - Use: "setup", - Short: "Deploy DMS configurations", - Long: "Deploy compositor and terminal configurations with interactive prompts", - PersistentPreRunE: requireMutableSystemCommand, - Run: func(cmd *cobra.Command, args []string) { - if err := runSetup(); err != nil { - log.Fatalf("Error during setup: %v", err) - } - }, -} - -var setupBindsCmd = &cobra.Command{ - Use: "binds", - Short: "Deploy default keybinds config", - Run: func(cmd *cobra.Command, args []string) { - if err := runSetupDmsConfig("binds"); err != nil { - log.Fatalf("Error: %v", err) - } - }, -} - -var setupLayoutCmd = &cobra.Command{ - Use: "layout", - Short: "Deploy default layout config", - Run: func(cmd *cobra.Command, args []string) { - if err := runSetupDmsConfig("layout"); err != nil { - log.Fatalf("Error: %v", err) - } - }, -} - -var setupColorsCmd = &cobra.Command{ - Use: "colors", - Short: "Deploy default colors config", - Run: func(cmd *cobra.Command, args []string) { - if err := runSetupDmsConfig("colors"); err != nil { - log.Fatalf("Error: %v", err) - } - }, -} - -var setupAlttabCmd = &cobra.Command{ - Use: "alttab", - Short: "Deploy default alt-tab config (niri only)", - Run: func(cmd *cobra.Command, args []string) { - if err := runSetupDmsConfig("alttab"); err != nil { - log.Fatalf("Error: %v", err) - } - }, -} - -var setupOutputsCmd = &cobra.Command{ - Use: "outputs", - Short: "Deploy default outputs config", - Run: func(cmd *cobra.Command, args []string) { - if err := runSetupDmsConfig("outputs"); err != nil { - log.Fatalf("Error: %v", err) - } - }, -} - -var setupCursorCmd = &cobra.Command{ - Use: "cursor", - Short: "Deploy default cursor config", - Run: func(cmd *cobra.Command, args []string) { - if err := runSetupDmsConfig("cursor"); err != nil { - log.Fatalf("Error: %v", err) - } - }, -} - -var setupWindowrulesCmd = &cobra.Command{ - Use: "windowrules", - Short: "Deploy default window rules config", - Run: func(cmd *cobra.Command, args []string) { - if err := runSetupDmsConfig("windowrules"); err != nil { - log.Fatalf("Error: %v", err) - } - }, -} - -type dmsConfigSpec struct { - niriFile string - hyprFile string - niriContent func(terminal string) string - hyprContent func(terminal string) string -} - -var dmsConfigSpecs = map[string]dmsConfigSpec{ - "binds": { - niriFile: "binds.kdl", - hyprFile: "binds.conf", - niriContent: func(t string) string { - return strings.ReplaceAll(config.NiriBindsConfig, "{{TERMINAL_COMMAND}}", t) - }, - hyprContent: func(t string) string { - return strings.ReplaceAll(config.HyprBindsConfig, "{{TERMINAL_COMMAND}}", t) - }, - }, - "layout": { - niriFile: "layout.kdl", - hyprFile: "layout.conf", - niriContent: func(_ string) string { return config.NiriLayoutConfig }, - hyprContent: func(_ string) string { return config.HyprLayoutConfig }, - }, - "colors": { - niriFile: "colors.kdl", - hyprFile: "colors.conf", - niriContent: func(_ string) string { return config.NiriColorsConfig }, - hyprContent: func(_ string) string { return config.HyprColorsConfig }, - }, - "alttab": { - niriFile: "alttab.kdl", - niriContent: func(_ string) string { return config.NiriAlttabConfig }, - }, - "outputs": { - niriFile: "outputs.kdl", - hyprFile: "outputs.conf", - niriContent: func(_ string) string { return "" }, - hyprContent: func(_ string) string { return "" }, - }, - "cursor": { - niriFile: "cursor.kdl", - hyprFile: "cursor.conf", - niriContent: func(_ string) string { return "" }, - hyprContent: func(_ string) string { return "" }, - }, - "windowrules": { - niriFile: "windowrules.kdl", - hyprFile: "windowrules.conf", - niriContent: func(_ string) string { return "" }, - hyprContent: func(_ string) string { return "" }, - }, -} - -func detectTerminal() (string, error) { - terminals := []string{"ghostty", "foot", "kitty", "alacritty"} - var found []string - for _, t := range terminals { - if utils.CommandExists(t) { - found = append(found, t) - } - } - - switch len(found) { - case 0: - return "ghostty", nil - case 1: - return found[0], nil - } - - fmt.Println("Multiple terminals detected:") - for i, t := range found { - fmt.Printf("%d) %s\n", i+1, t) - } - fmt.Printf("\nChoice (1-%d): ", len(found)) - - var response string - fmt.Scanln(&response) - response = strings.TrimSpace(response) - - choice := 0 - fmt.Sscanf(response, "%d", &choice) - if choice < 1 || choice > len(found) { - return "", fmt.Errorf("invalid choice") - } - return found[choice-1], nil -} - -func detectCompositorForSetup() (string, error) { - compositors := greeter.DetectCompositors() - - switch len(compositors) { - case 0: - return "", fmt.Errorf("no supported compositors found (niri or Hyprland required)") - case 1: - return strings.ToLower(compositors[0]), nil - } - - selected, err := greeter.PromptCompositorChoice(compositors) - if err != nil { - return "", err - } - return strings.ToLower(selected), nil -} - -func runSetupDmsConfig(name string) error { - spec, ok := dmsConfigSpecs[name] - if !ok { - return fmt.Errorf("unknown config: %s", name) - } - - compositor, err := detectCompositorForSetup() - if err != nil { - return err - } - - var filename string - var contentFn func(string) string - switch compositor { - case "niri": - filename = spec.niriFile - contentFn = spec.niriContent - case "hyprland": - filename = spec.hyprFile - contentFn = spec.hyprContent - default: - return fmt.Errorf("unsupported compositor: %s", compositor) - } - - if filename == "" { - return fmt.Errorf("%s is not supported for %s", name, compositor) - } - - var dmsDir string - switch compositor { - case "niri": - dmsDir = filepath.Join(os.Getenv("HOME"), ".config", "niri", "dms") - case "hyprland": - dmsDir = filepath.Join(os.Getenv("HOME"), ".config", "hypr", "dms") - } - - if err := os.MkdirAll(dmsDir, 0o755); err != nil { - return fmt.Errorf("failed to create dms directory: %w", err) - } - - path := filepath.Join(dmsDir, filename) - if info, err := os.Stat(path); err == nil && info.Size() > 0 { - return fmt.Errorf("%s already exists and is not empty: %s", name, path) - } - - terminal := "ghostty" - if contentFn != nil && name == "binds" { - terminal, err = detectTerminal() - if err != nil { - return err - } - } - - content := contentFn(terminal) - if err := os.WriteFile(path, []byte(content), 0o644); err != nil { - return fmt.Errorf("failed to write %s: %w", filename, err) - } - - fmt.Printf("Deployed %s to %s\n", name, path) - return nil -} - -func runSetup() error { - fmt.Println("=== DMS Configuration Setup ===") - - wm, wmSelected := promptCompositor() - terminal, terminalSelected := promptTerminal() - useSystemd := promptSystemd() - - if !wmSelected && !terminalSelected { - fmt.Println("No configurations selected. Exiting.") - return nil - } - - if wmSelected || terminalSelected { - willBackup := checkExistingConfigs(wm, wmSelected, terminal, terminalSelected) - if willBackup { - fmt.Println("\n⚠ Existing configurations will be backed up with timestamps.") - } - - fmt.Print("\nProceed with deployment? (y/N): ") - var response string - fmt.Scanln(&response) - response = strings.ToLower(strings.TrimSpace(response)) - - if response != "y" && response != "yes" { - fmt.Println("Setup cancelled.") - return nil - } - } - - fmt.Println("\nDeploying configurations...") - logChan := make(chan string, 100) - deployer := config.NewConfigDeployer(logChan) - - go func() { - for msg := range logChan { - fmt.Println(" " + msg) - } - }() - - ctx := context.Background() - var results []config.DeploymentResult - var err error - - if wmSelected && terminalSelected { - results, err = deployer.DeployConfigurationsWithSystemd(ctx, wm, terminal, useSystemd) - } else if wmSelected { - results, err = deployer.DeployConfigurationsWithSystemd(ctx, wm, deps.TerminalGhostty, useSystemd) - if len(results) > 1 { - results = results[:1] - } - } else if terminalSelected { - results, err = deployer.DeployConfigurationsWithSystemd(ctx, deps.WindowManagerNiri, terminal, useSystemd) - if len(results) > 0 && results[0].ConfigType == "Niri" { - results = results[1:] - } - } - - close(logChan) - - if err != nil { - return fmt.Errorf("deployment failed: %w", err) - } - - fmt.Println("\n=== Deployment Complete ===") - for _, result := range results { - if result.Deployed { - fmt.Printf("✓ %s: %s\n", result.ConfigType, result.Path) - if result.BackupPath != "" { - fmt.Printf(" Backup: %s\n", result.BackupPath) - } - } - } - - return nil -} - -func promptCompositor() (deps.WindowManager, bool) { - fmt.Println("Select compositor:") - fmt.Println("1) Niri") - fmt.Println("2) Hyprland") - fmt.Println("3) None") - - var response string - fmt.Print("\nChoice (1-3): ") - fmt.Scanln(&response) - response = strings.TrimSpace(response) - - switch response { - case "1": - return deps.WindowManagerNiri, true - case "2": - return deps.WindowManagerHyprland, true - default: - return deps.WindowManagerNiri, false - } -} - -func promptTerminal() (deps.Terminal, bool) { - fmt.Println("\nSelect terminal:") - fmt.Println("1) Ghostty") - fmt.Println("2) Kitty") - fmt.Println("3) Alacritty") - fmt.Println("4) None") - - var response string - fmt.Print("\nChoice (1-4): ") - fmt.Scanln(&response) - response = strings.TrimSpace(response) - - switch response { - case "1": - return deps.TerminalGhostty, true - case "2": - return deps.TerminalKitty, true - case "3": - return deps.TerminalAlacritty, true - default: - return deps.TerminalGhostty, false - } -} - -func promptSystemd() bool { - fmt.Println("\nUse systemd for session management?") - fmt.Println("1) Yes (recommended for most distros)") - fmt.Println("2) No (standalone, no systemd integration)") - - var response string - fmt.Print("\nChoice (1-2): ") - fmt.Scanln(&response) - response = strings.TrimSpace(response) - - return response != "2" -} - -func checkExistingConfigs(wm deps.WindowManager, wmSelected bool, terminal deps.Terminal, terminalSelected bool) bool { - homeDir := os.Getenv("HOME") - willBackup := false - - if wmSelected { - var configPath string - switch wm { - case deps.WindowManagerNiri: - configPath = filepath.Join(homeDir, ".config", "niri", "config.kdl") - case deps.WindowManagerHyprland: - configPath = filepath.Join(homeDir, ".config", "hypr", "hyprland.conf") - } - - if _, err := os.Stat(configPath); err == nil { - willBackup = true - } - } - - if terminalSelected { - var configPath string - switch terminal { - case deps.TerminalGhostty: - configPath = filepath.Join(homeDir, ".config", "ghostty", "config") - case deps.TerminalKitty: - configPath = filepath.Join(homeDir, ".config", "kitty", "kitty.conf") - case deps.TerminalAlacritty: - configPath = filepath.Join(homeDir, ".config", "alacritty", "alacritty.toml") - } - - if _, err := os.Stat(configPath); err == nil { - willBackup = true - } - } - - return willBackup -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_windowrules.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_windowrules.go deleted file mode 100644 index 0883811..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/commands_windowrules.go +++ /dev/null @@ -1,338 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/AvengeMedia/DankMaterialShell/core/internal/windowrules" - "github.com/AvengeMedia/DankMaterialShell/core/internal/windowrules/providers" - "github.com/spf13/cobra" -) - -var windowrulesCmd = &cobra.Command{ - Use: "windowrules", - Short: "Manage window rules", -} - -var windowrulesListCmd = &cobra.Command{ - Use: "list [compositor]", - Short: "List all window rules", - Long: "List all window rules from compositor config file. Returns JSON with rules and DMS status.", - Args: cobra.MaximumNArgs(1), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return []string{"niri"}, cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, - Run: runWindowrulesList, -} - -var windowrulesAddCmd = &cobra.Command{ - Use: "add ''", - Short: "Add a window rule to DMS file", - Long: "Add a new window rule to the DMS-managed rules file.", - Args: cobra.ExactArgs(2), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - // ! disabled hyprland return []string{"hyprland", "niri"}, cobra.ShellCompDirectiveNoFileComp - return []string{"niri"}, cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, - Run: runWindowrulesAdd, -} - -var windowrulesUpdateCmd = &cobra.Command{ - Use: "update ''", - Short: "Update a window rule in DMS file", - Long: "Update an existing window rule in the DMS-managed rules file.", - Args: cobra.ExactArgs(3), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return []string{"niri"}, cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, - Run: runWindowrulesUpdate, -} - -var windowrulesRemoveCmd = &cobra.Command{ - Use: "remove ", - Short: "Remove a window rule from DMS file", - Long: "Remove a window rule from the DMS-managed rules file.", - Args: cobra.ExactArgs(2), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return []string{"niri"}, cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, - Run: runWindowrulesRemove, -} - -var windowrulesReorderCmd = &cobra.Command{ - Use: "reorder ''", - Short: "Reorder window rules in DMS file", - Long: "Reorder window rules by providing a JSON array of rule IDs in the desired order.", - Args: cobra.ExactArgs(2), - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) == 0 { - return []string{"niri"}, cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, - Run: runWindowrulesReorder, -} - -func init() { - configCmd.AddCommand(windowrulesCmd) - windowrulesCmd.AddCommand(windowrulesListCmd) - windowrulesCmd.AddCommand(windowrulesAddCmd) - windowrulesCmd.AddCommand(windowrulesUpdateCmd) - windowrulesCmd.AddCommand(windowrulesRemoveCmd) - windowrulesCmd.AddCommand(windowrulesReorderCmd) -} - -type WindowRulesListResult struct { - Rules []windowrules.WindowRule `json:"rules"` - DMSStatus *windowrules.DMSRulesStatus `json:"dmsStatus,omitempty"` -} - -type WindowRuleWriteResult struct { - Success bool `json:"success"` - ID string `json:"id,omitempty"` - Path string `json:"path,omitempty"` - Error string `json:"error,omitempty"` -} - -func getCompositor(args []string) string { - if len(args) > 0 { - return strings.ToLower(args[0]) - } - if os.Getenv("NIRI_SOCKET") != "" { - return "niri" - } - // if os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "" { - // return "hyprland" - // } - return "" -} - -func writeRuleError(errMsg string) { - result := WindowRuleWriteResult{Success: false, Error: errMsg} - output, _ := json.Marshal(result) - fmt.Fprintln(os.Stdout, string(output)) - os.Exit(1) -} - -func writeRuleSuccess(id, path string) { - result := WindowRuleWriteResult{Success: true, ID: id, Path: path} - output, _ := json.Marshal(result) - fmt.Fprintln(os.Stdout, string(output)) -} - -func runWindowrulesList(cmd *cobra.Command, args []string) { - compositor := getCompositor(args) - if compositor == "" { - log.Fatalf("Could not detect compositor. Please specify: hyprland or niri") - } - - var result WindowRulesListResult - - switch compositor { - case "niri": - configDir, err := utils.ExpandPath("$HOME/.config/niri") - if err != nil { - log.Fatalf("Failed to expand niri config path: %v", err) - } - - parseResult, err := providers.ParseNiriWindowRules(configDir) - if err != nil { - log.Fatalf("Failed to parse niri window rules: %v", err) - } - - allRules := providers.ConvertNiriRulesToWindowRules(parseResult.Rules) - - provider := providers.NewNiriWritableProvider(configDir) - dmsRulesPath := provider.GetOverridePath() - dmsRules, _ := provider.LoadDMSRules() - - dmsRuleMap := make(map[int]windowrules.WindowRule) - for i, dr := range dmsRules { - dmsRuleMap[i] = dr - } - - dmsIdx := 0 - for i, r := range allRules { - if r.Source == dmsRulesPath { - if dmr, ok := dmsRuleMap[dmsIdx]; ok { - allRules[i].ID = dmr.ID - allRules[i].Name = dmr.Name - } - dmsIdx++ - } - } - - result.Rules = allRules - result.DMSStatus = parseResult.DMSStatus - - case "hyprland": - log.Fatalf("Hyprland support is currently disabled.") // ! disabled hyprland - configDir, err := utils.ExpandPath("$HOME/.config/hypr") - if err != nil { - log.Fatalf("Failed to expand hyprland config path: %v", err) - } - - parseResult, err := providers.ParseHyprlandWindowRules(configDir) - if err != nil { - log.Fatalf("Failed to parse hyprland window rules: %v", err) - } - - allRules := providers.ConvertHyprlandRulesToWindowRules(parseResult.Rules) - - provider := providers.NewHyprlandWritableProvider(configDir) - dmsRulesPath := provider.GetOverridePath() - dmsRules, _ := provider.LoadDMSRules() - - dmsRuleMap := make(map[int]windowrules.WindowRule) - for i, dr := range dmsRules { - dmsRuleMap[i] = dr - } - - dmsIdx := 0 - for i, r := range allRules { - if r.Source == dmsRulesPath { - if dmr, ok := dmsRuleMap[dmsIdx]; ok { - allRules[i].ID = dmr.ID - allRules[i].Name = dmr.Name - } - dmsIdx++ - } - } - - result.Rules = allRules - result.DMSStatus = parseResult.DMSStatus - - default: - log.Fatalf("Unknown compositor: %s", compositor) - } - - output, _ := json.Marshal(result) - fmt.Fprintln(os.Stdout, string(output)) -} - -func runWindowrulesAdd(cmd *cobra.Command, args []string) { - compositor := strings.ToLower(args[0]) - ruleJSON := args[1] - - var rule windowrules.WindowRule - if err := json.Unmarshal([]byte(ruleJSON), &rule); err != nil { - writeRuleError(fmt.Sprintf("Invalid JSON: %v", err)) - } - - if rule.ID == "" { - rule.ID = generateRuleID() - } - rule.Enabled = true - - provider := getWindowRulesProvider(compositor) - if provider == nil { - writeRuleError(fmt.Sprintf("Unknown compositor: %s", compositor)) - } - - if err := provider.SetRule(rule); err != nil { - writeRuleError(err.Error()) - } - - writeRuleSuccess(rule.ID, provider.GetOverridePath()) -} - -func runWindowrulesUpdate(cmd *cobra.Command, args []string) { - compositor := strings.ToLower(args[0]) - ruleID := args[1] - ruleJSON := args[2] - - var rule windowrules.WindowRule - if err := json.Unmarshal([]byte(ruleJSON), &rule); err != nil { - writeRuleError(fmt.Sprintf("Invalid JSON: %v", err)) - } - - rule.ID = ruleID - - provider := getWindowRulesProvider(compositor) - if provider == nil { - writeRuleError(fmt.Sprintf("Unknown compositor: %s", compositor)) - } - - if err := provider.SetRule(rule); err != nil { - writeRuleError(err.Error()) - } - - writeRuleSuccess(rule.ID, provider.GetOverridePath()) -} - -func runWindowrulesRemove(cmd *cobra.Command, args []string) { - compositor := strings.ToLower(args[0]) - ruleID := args[1] - - provider := getWindowRulesProvider(compositor) - if provider == nil { - writeRuleError(fmt.Sprintf("Unknown compositor: %s", compositor)) - } - - if err := provider.RemoveRule(ruleID); err != nil { - writeRuleError(err.Error()) - } - - writeRuleSuccess(ruleID, provider.GetOverridePath()) -} - -func runWindowrulesReorder(cmd *cobra.Command, args []string) { - compositor := strings.ToLower(args[0]) - idsJSON := args[1] - - var ids []string - if err := json.Unmarshal([]byte(idsJSON), &ids); err != nil { - writeRuleError(fmt.Sprintf("Invalid JSON array: %v", err)) - } - - provider := getWindowRulesProvider(compositor) - if provider == nil { - writeRuleError(fmt.Sprintf("Unknown compositor: %s", compositor)) - } - - if err := provider.ReorderRules(ids); err != nil { - writeRuleError(err.Error()) - } - - writeRuleSuccess("", provider.GetOverridePath()) -} - -func getWindowRulesProvider(compositor string) windowrules.WritableProvider { - switch compositor { - case "niri": - configDir, err := utils.ExpandPath("$HOME/.config/niri") - if err != nil { - return nil - } - return providers.NewNiriWritableProvider(configDir) - case "hyprland": - configDir, err := utils.ExpandPath("$HOME/.config/hypr") - if err != nil { - return nil - } - return providers.NewHyprlandWritableProvider(configDir) - default: - return nil - } -} - -func generateRuleID() string { - return fmt.Sprintf("wr_%d", time.Now().UnixNano()) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/dpms_client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/dpms_client.go deleted file mode 100644 index 98f66d9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/dpms_client.go +++ /dev/null @@ -1,339 +0,0 @@ -package main - -import ( - "fmt" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_power" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type cmd struct { - fn func() - done chan error -} - -type dpmsClient struct { - display *wlclient.Display - ctx *wlclient.Context - powerMgr *wlr_output_power.ZwlrOutputPowerManagerV1 - outputs map[string]*outputState - mu sync.Mutex - syncRound int - done bool - err error - cmdq chan cmd - stopChan chan struct{} - wg sync.WaitGroup -} - -type outputState struct { - wlOutput *wlclient.Output - powerCtrl *wlr_output_power.ZwlrOutputPowerV1 - name string - mode uint32 - failed bool - waitCh chan struct{} - wantMode *uint32 -} - -func (c *dpmsClient) post(fn func()) { - done := make(chan error, 1) - select { - case c.cmdq <- cmd{fn: fn, done: done}: - <-done - case <-c.stopChan: - } -} - -func (c *dpmsClient) waylandActor() { - defer c.wg.Done() - for { - select { - case <-c.stopChan: - return - case cmd := <-c.cmdq: - cmd.fn() - close(cmd.done) - } - } -} - -func newDPMSClient() (*dpmsClient, error) { - display, err := wlclient.Connect("") - if err != nil { - return nil, fmt.Errorf("failed to connect to Wayland: %w", err) - } - - c := &dpmsClient{ - display: display, - ctx: display.Context(), - outputs: make(map[string]*outputState), - cmdq: make(chan cmd, 128), - stopChan: make(chan struct{}), - } - - c.wg.Add(1) - go c.waylandActor() - - registry, err := display.GetRegistry() - if err != nil { - display.Context().Close() - return nil, fmt.Errorf("failed to get registry: %w", err) - } - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case wlr_output_power.ZwlrOutputPowerManagerV1InterfaceName: - powerMgr := wlr_output_power.NewZwlrOutputPowerManagerV1(c.ctx) - version := min(e.Version, 1) - if err := registry.Bind(e.Name, e.Interface, version, powerMgr); err == nil { - c.powerMgr = powerMgr - } - - case "wl_output": - output := wlclient.NewOutput(c.ctx) - version := min(e.Version, 4) - if err := registry.Bind(e.Name, e.Interface, version, output); err == nil { - outputID := fmt.Sprintf("output-%d", output.ID()) - state := &outputState{ - wlOutput: output, - name: outputID, - } - - c.mu.Lock() - c.outputs[outputID] = state - c.mu.Unlock() - - output.SetNameHandler(func(ev wlclient.OutputNameEvent) { - c.mu.Lock() - delete(c.outputs, state.name) - state.name = ev.Name - c.outputs[ev.Name] = state - c.mu.Unlock() - }) - } - } - }) - - syncCallback, err := display.Sync() - if err != nil { - c.Close() - return nil, fmt.Errorf("failed to sync display: %w", err) - } - syncCallback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) { - c.handleSync() - }) - - for !c.done { - if err := c.ctx.Dispatch(); err != nil { - c.Close() - return nil, fmt.Errorf("dispatch error: %w", err) - } - } - - if c.err != nil { - c.Close() - return nil, c.err - } - - return c, nil -} - -func (c *dpmsClient) handleSync() { - c.syncRound++ - - switch c.syncRound { - case 1: - if c.powerMgr == nil { - c.err = fmt.Errorf("wlr-output-power-management protocol not supported by compositor") - c.done = true - return - } - - c.mu.Lock() - for _, state := range c.outputs { - powerCtrl, err := c.powerMgr.GetOutputPower(state.wlOutput) - if err != nil { - continue - } - state.powerCtrl = powerCtrl - - powerCtrl.SetModeHandler(func(e wlr_output_power.ZwlrOutputPowerV1ModeEvent) { - c.mu.Lock() - defer c.mu.Unlock() - if state.powerCtrl == nil { - return - } - state.mode = e.Mode - if state.wantMode != nil && e.Mode == *state.wantMode && state.waitCh != nil { - close(state.waitCh) - state.wantMode = nil - } - }) - - powerCtrl.SetFailedHandler(func(e wlr_output_power.ZwlrOutputPowerV1FailedEvent) { - c.mu.Lock() - defer c.mu.Unlock() - if state.powerCtrl == nil { - return - } - state.failed = true - if state.waitCh != nil { - close(state.waitCh) - state.wantMode = nil - } - }) - } - c.mu.Unlock() - - syncCallback, err := c.display.Sync() - if err != nil { - c.err = fmt.Errorf("failed to sync display: %w", err) - c.done = true - return - } - syncCallback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) { - c.handleSync() - }) - - default: - c.done = true - } -} - -func (c *dpmsClient) ListOutputs() []string { - c.mu.Lock() - defer c.mu.Unlock() - - names := make([]string, 0, len(c.outputs)) - for name := range c.outputs { - names = append(names, name) - } - return names -} - -func (c *dpmsClient) SetDPMS(outputName string, on bool) error { - var mode uint32 - if on { - mode = uint32(wlr_output_power.ZwlrOutputPowerV1ModeOn) - } else { - mode = uint32(wlr_output_power.ZwlrOutputPowerV1ModeOff) - } - - var setErr error - c.post(func() { - c.mu.Lock() - var waitStates []*outputState - - if outputName == "" || outputName == "all" { - if len(c.outputs) == 0 { - c.mu.Unlock() - setErr = fmt.Errorf("no outputs found") - return - } - - for _, state := range c.outputs { - if state.powerCtrl == nil { - continue - } - state.wantMode = &mode - state.waitCh = make(chan struct{}) - state.failed = false - waitStates = append(waitStates, state) - state.powerCtrl.SetMode(mode) - } - } else { - state, ok := c.outputs[outputName] - if !ok { - c.mu.Unlock() - setErr = fmt.Errorf("output not found: %s", outputName) - return - } - if state.powerCtrl == nil { - c.mu.Unlock() - setErr = fmt.Errorf("output %s has nil powerCtrl", outputName) - return - } - state.wantMode = &mode - state.waitCh = make(chan struct{}) - state.failed = false - waitStates = append(waitStates, state) - state.powerCtrl.SetMode(mode) - } - c.mu.Unlock() - - deadline := time.Now().Add(10 * time.Second) - - for _, state := range waitStates { - c.mu.Lock() - ch := state.waitCh - c.mu.Unlock() - - done := false - for !done { - if err := c.ctx.Dispatch(); err != nil { - setErr = fmt.Errorf("dispatch error: %w", err) - return - } - - select { - case <-ch: - c.mu.Lock() - if state.failed { - setErr = fmt.Errorf("compositor reported failed for %s", state.name) - c.mu.Unlock() - return - } - c.mu.Unlock() - done = true - default: - if time.Now().After(deadline) { - setErr = fmt.Errorf("timeout waiting for mode change on %s", state.name) - return - } - time.Sleep(10 * time.Millisecond) - } - } - } - - c.mu.Lock() - for _, state := range waitStates { - if state.powerCtrl != nil { - state.powerCtrl.Destroy() - state.powerCtrl = nil - } - } - c.mu.Unlock() - - c.display.Roundtrip() - }) - - return setErr -} - -func (c *dpmsClient) Close() { - close(c.stopChan) - c.wg.Wait() - - c.mu.Lock() - defer c.mu.Unlock() - - for _, state := range c.outputs { - if state.powerCtrl != nil { - state.powerCtrl.Destroy() - } - } - c.outputs = nil - - if c.powerMgr != nil { - c.powerMgr.Destroy() - c.powerMgr = nil - } - - if c.display != nil { - c.ctx.Close() - c.display = nil - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/immutable_policy.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/immutable_policy.go deleted file mode 100644 index 5529c9f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/immutable_policy.go +++ /dev/null @@ -1,271 +0,0 @@ -package main - -import ( - "bufio" - _ "embed" - "encoding/json" - "fmt" - "os" - "strings" - "sync" - - "github.com/spf13/cobra" -) - -const ( - cliPolicyPackagedPath = "/usr/share/dms/cli-policy.json" - cliPolicyAdminPath = "/etc/dms/cli-policy.json" -) - -var ( - immutablePolicyOnce sync.Once - immutablePolicy immutableCommandPolicy - immutablePolicyErr error -) - -//go:embed assets/cli-policy.default.json -var defaultCLIPolicyJSON []byte - -type immutableCommandPolicy struct { - ImmutableSystem bool - ImmutableReason string - BlockedCommands []string - Message string -} - -type cliPolicyFile struct { - PolicyVersion int `json:"policy_version"` - ImmutableSystem *bool `json:"immutable_system"` - BlockedCommands *[]string `json:"blocked_commands"` - Message *string `json:"message"` -} - -func normalizeCommandSpec(raw string) string { - normalized := strings.ToLower(strings.TrimSpace(raw)) - normalized = strings.TrimPrefix(normalized, "dms ") - return strings.Join(strings.Fields(normalized), " ") -} - -func normalizeBlockedCommands(raw []string) []string { - normalized := make([]string, 0, len(raw)) - seen := make(map[string]bool) - - for _, cmd := range raw { - spec := normalizeCommandSpec(cmd) - if spec == "" || seen[spec] { - continue - } - seen[spec] = true - normalized = append(normalized, spec) - } - - return normalized -} - -func commandBlockedByPolicy(commandPath string, blocked []string) bool { - normalizedPath := normalizeCommandSpec(commandPath) - if normalizedPath == "" { - return false - } - - for _, entry := range blocked { - spec := normalizeCommandSpec(entry) - if spec == "" { - continue - } - if normalizedPath == spec || strings.HasPrefix(normalizedPath, spec+" ") { - return true - } - } - - return false -} - -func loadPolicyFile(path string) (*cliPolicyFile, error) { - data, err := os.ReadFile(path) - if err != nil { - if os.IsNotExist(err) { - return nil, nil - } - return nil, fmt.Errorf("failed to read %s: %w", path, err) - } - - var policy cliPolicyFile - if err := json.Unmarshal(data, &policy); err != nil { - return nil, fmt.Errorf("failed to parse %s: %w", path, err) - } - - return &policy, nil -} - -func mergePolicyFile(base *immutableCommandPolicy, path string) error { - policyFile, err := loadPolicyFile(path) - if err != nil { - return err - } - if policyFile == nil { - return nil - } - - if policyFile.ImmutableSystem != nil { - base.ImmutableSystem = *policyFile.ImmutableSystem - } - if policyFile.BlockedCommands != nil { - base.BlockedCommands = normalizeBlockedCommands(*policyFile.BlockedCommands) - } - if policyFile.Message != nil { - msg := strings.TrimSpace(*policyFile.Message) - if msg != "" { - base.Message = msg - } - } - - return nil -} - -func readOSReleaseMap(path string) map[string]string { - values := make(map[string]string) - - file, err := os.Open(path) - if err != nil { - return values - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" || strings.HasPrefix(line, "#") { - continue - } - parts := strings.SplitN(line, "=", 2) - if len(parts) != 2 { - continue - } - key := strings.ToUpper(strings.TrimSpace(parts[0])) - value := strings.Trim(strings.TrimSpace(parts[1]), "\"") - values[key] = strings.ToLower(value) - } - - return values -} - -func hasAnyToken(text string, tokens ...string) bool { - if text == "" { - return false - } - for _, token := range tokens { - if strings.Contains(text, token) { - return true - } - } - return false -} - -func detectImmutableSystem() (bool, string) { - if _, err := os.Stat("/run/ostree-booted"); err == nil { - return true, "/run/ostree-booted is present" - } - - osRelease := readOSReleaseMap("/etc/os-release") - if len(osRelease) == 0 { - return false, "" - } - - id := osRelease["ID"] - idLike := osRelease["ID_LIKE"] - variantID := osRelease["VARIANT_ID"] - name := osRelease["NAME"] - prettyName := osRelease["PRETTY_NAME"] - - immutableIDs := map[string]bool{ - "bluefin": true, - "bazzite": true, - "silverblue": true, - "kinoite": true, - "sericea": true, - "onyx": true, - "aurora": true, - "fedora-iot": true, - "fedora-coreos": true, - } - if immutableIDs[id] { - return true, "os-release ID=" + id - } - - markers := []string{"silverblue", "kinoite", "sericea", "onyx", "bazzite", "bluefin", "aurora", "ostree", "atomic"} - if hasAnyToken(variantID, markers...) { - return true, "os-release VARIANT_ID=" + variantID - } - if hasAnyToken(idLike, "ostree", "rpm-ostree") { - return true, "os-release ID_LIKE=" + idLike - } - if hasAnyToken(name, markers...) || hasAnyToken(prettyName, markers...) { - return true, "os-release identifies an atomic/ostree variant" - } - - return false, "" -} - -func getImmutablePolicy() (*immutableCommandPolicy, error) { - immutablePolicyOnce.Do(func() { - detectedImmutable, reason := detectImmutableSystem() - immutablePolicy = immutableCommandPolicy{ - ImmutableSystem: detectedImmutable, - ImmutableReason: reason, - BlockedCommands: []string{"greeter install", "greeter enable", "setup"}, - Message: "This command is disabled on immutable/image-based systems. Use your distro-native workflow for system-level changes.", - } - - var defaultPolicy cliPolicyFile - if err := json.Unmarshal(defaultCLIPolicyJSON, &defaultPolicy); err != nil { - immutablePolicyErr = fmt.Errorf("failed to parse embedded default CLI policy: %w", err) - return - } - if defaultPolicy.BlockedCommands != nil { - immutablePolicy.BlockedCommands = normalizeBlockedCommands(*defaultPolicy.BlockedCommands) - } - if defaultPolicy.Message != nil { - msg := strings.TrimSpace(*defaultPolicy.Message) - if msg != "" { - immutablePolicy.Message = msg - } - } - - if err := mergePolicyFile(&immutablePolicy, cliPolicyPackagedPath); err != nil { - immutablePolicyErr = err - return - } - if err := mergePolicyFile(&immutablePolicy, cliPolicyAdminPath); err != nil { - immutablePolicyErr = err - return - } - }) - - if immutablePolicyErr != nil { - return nil, immutablePolicyErr - } - return &immutablePolicy, nil -} - -func requireMutableSystemCommand(cmd *cobra.Command, _ []string) error { - policy, err := getImmutablePolicy() - if err != nil { - return err - } - if !policy.ImmutableSystem { - return nil - } - - commandPath := normalizeCommandSpec(cmd.CommandPath()) - if !commandBlockedByPolicy(commandPath, policy.BlockedCommands) { - return nil - } - - reason := "" - if policy.ImmutableReason != "" { - reason = "Detected immutable system: " + policy.ImmutableReason + "\n" - } - - return fmt.Errorf("%s%s\nCommand: dms %s\nPolicy files:\n %s\n %s", reason, policy.Message, commandPath, cliPolicyPackagedPath, cliPolicyAdminPath) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/main.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/main.go deleted file mode 100644 index c818148..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/main.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build !distro_binary - -package main - -import ( - "os" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -var Version = "dev" - -func init() { - runCmd.Flags().BoolP("daemon", "d", false, "Run in daemon mode") - runCmd.Flags().Bool("daemon-child", false, "Internal flag for daemon child process") - runCmd.Flags().Bool("session", false, "Session managed (like as a systemd unit)") - runCmd.Flags().MarkHidden("daemon-child") - - greeterCmd.AddCommand(greeterInstallCmd, greeterSyncCmd, greeterEnableCmd, greeterStatusCmd, greeterUninstallCmd) - authCmd.AddCommand(authSyncCmd) - setupCmd.AddCommand(setupBindsCmd, setupLayoutCmd, setupColorsCmd, setupAlttabCmd, setupOutputsCmd, setupCursorCmd, setupWindowrulesCmd) - updateCmd.AddCommand(updateCheckCmd) - pluginsCmd.AddCommand(pluginsBrowseCmd, pluginsListCmd, pluginsInstallCmd, pluginsUninstallCmd, pluginsUpdateCmd) - rootCmd.AddCommand(getCommonCommands()...) - - rootCmd.AddCommand(authCmd) - rootCmd.AddCommand(updateCmd) - - rootCmd.SetHelpTemplate(getHelpTemplate()) -} - -func main() { - clipboard.MaybeServeAndExit() - - if os.Geteuid() == 0 && !isReadOnlyCommand(os.Args) { - log.Fatal("This program should not be run as root. Exiting.") - } - - if err := rootCmd.Execute(); err != nil { - log.Fatal(err) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/main_distro.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/main_distro.go deleted file mode 100644 index 2b544ff..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/main_distro.go +++ /dev/null @@ -1,40 +0,0 @@ -//go:build distro_binary - -package main - -import ( - "os" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -var Version = "dev" - -func init() { - runCmd.Flags().BoolP("daemon", "d", false, "Run in daemon mode") - runCmd.Flags().Bool("daemon-child", false, "Internal flag for daemon child process") - runCmd.Flags().Bool("session", false, "Session managed (like as a systemd unit)") - runCmd.Flags().MarkHidden("daemon-child") - - greeterCmd.AddCommand(greeterInstallCmd, greeterSyncCmd, greeterEnableCmd, greeterStatusCmd, greeterUninstallCmd) - authCmd.AddCommand(authSyncCmd) - setupCmd.AddCommand(setupBindsCmd, setupLayoutCmd, setupColorsCmd, setupAlttabCmd, setupOutputsCmd, setupCursorCmd, setupWindowrulesCmd) - pluginsCmd.AddCommand(pluginsBrowseCmd, pluginsListCmd, pluginsInstallCmd, pluginsUninstallCmd, pluginsUpdateCmd) - rootCmd.AddCommand(getCommonCommands()...) - rootCmd.AddCommand(authCmd) - - rootCmd.SetHelpTemplate(getHelpTemplate()) -} - -func main() { - clipboard.MaybeServeAndExit() - - if os.Geteuid() == 0 && !isReadOnlyCommand(os.Args) { - log.Fatal("This program should not be run as root. Exiting.") - } - - if err := rootCmd.Execute(); err != nil { - log.Fatal(err) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/randr_client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/randr_client.go deleted file mode 100644 index 1a149ca..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/randr_client.go +++ /dev/null @@ -1,172 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type randrOutput struct { - Name string `json:"name"` - Scale float64 `json:"scale"` - Width int32 `json:"width"` - Height int32 `json:"height"` - Refresh int32 `json:"refresh"` - Enabled bool `json:"enabled"` -} - -type randrHead struct { - name string - enabled bool - scale float64 - currentModeID uint32 - modeIDs []uint32 -} - -type randrMode struct { - width int32 - height int32 - refresh int32 -} - -type randrClient struct { - display *wlclient.Display - ctx *wlclient.Context - manager *wlr_output_management.ZwlrOutputManagerV1 - heads map[uint32]*randrHead - modes map[uint32]*randrMode - done bool - err error -} - -func queryRandr() ([]randrOutput, error) { - display, err := wlclient.Connect("") - if err != nil { - return nil, fmt.Errorf("failed to connect to Wayland: %w", err) - } - - c := &randrClient{ - display: display, - ctx: display.Context(), - heads: make(map[uint32]*randrHead), - modes: make(map[uint32]*randrMode), - } - defer c.ctx.Close() - - registry, err := display.GetRegistry() - if err != nil { - return nil, fmt.Errorf("failed to get registry: %w", err) - } - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - if e.Interface == wlr_output_management.ZwlrOutputManagerV1InterfaceName { - mgr := wlr_output_management.NewZwlrOutputManagerV1(c.ctx) - version := min(e.Version, 4) - - mgr.SetHeadHandler(func(e wlr_output_management.ZwlrOutputManagerV1HeadEvent) { - c.handleHead(e) - }) - - mgr.SetDoneHandler(func(e wlr_output_management.ZwlrOutputManagerV1DoneEvent) { - c.done = true - }) - - if err := registry.Bind(e.Name, e.Interface, version, mgr); err == nil { - c.manager = mgr - } - } - }) - - // First roundtrip: discover globals and bind manager - syncCallback, err := display.Sync() - if err != nil { - return nil, fmt.Errorf("failed to sync display: %w", err) - } - syncCallback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) { - if c.manager == nil { - c.err = fmt.Errorf("zwlr_output_manager_v1 protocol not supported by compositor") - c.done = true - } - // Otherwise wait for manager's DoneHandler - }) - - for !c.done { - if err := c.ctx.Dispatch(); err != nil { - return nil, fmt.Errorf("dispatch error: %w", err) - } - } - - if c.err != nil { - return nil, c.err - } - - return c.buildOutputs(), nil -} - -func (c *randrClient) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEvent) { - handle := e.Head - headID := handle.ID() - - head := &randrHead{ - modeIDs: make([]uint32, 0), - } - c.heads[headID] = head - - handle.SetNameHandler(func(e wlr_output_management.ZwlrOutputHeadV1NameEvent) { - head.name = e.Name - }) - - handle.SetEnabledHandler(func(e wlr_output_management.ZwlrOutputHeadV1EnabledEvent) { - head.enabled = e.Enabled != 0 - }) - - handle.SetScaleHandler(func(e wlr_output_management.ZwlrOutputHeadV1ScaleEvent) { - head.scale = e.Scale - }) - - handle.SetCurrentModeHandler(func(e wlr_output_management.ZwlrOutputHeadV1CurrentModeEvent) { - head.currentModeID = e.Mode.ID() - }) - - handle.SetModeHandler(func(e wlr_output_management.ZwlrOutputHeadV1ModeEvent) { - modeHandle := e.Mode - modeID := modeHandle.ID() - - head.modeIDs = append(head.modeIDs, modeID) - - mode := &randrMode{} - c.modes[modeID] = mode - - modeHandle.SetSizeHandler(func(e wlr_output_management.ZwlrOutputModeV1SizeEvent) { - mode.width = e.Width - mode.height = e.Height - }) - - modeHandle.SetRefreshHandler(func(e wlr_output_management.ZwlrOutputModeV1RefreshEvent) { - mode.refresh = e.Refresh - }) - }) -} - -func (c *randrClient) buildOutputs() []randrOutput { - outputs := make([]randrOutput, 0, len(c.heads)) - - for _, head := range c.heads { - out := randrOutput{ - Name: head.name, - Scale: head.scale, - Enabled: head.enabled, - } - - if mode, ok := c.modes[head.currentModeID]; ok { - out.Width = mode.width - out.Height = mode.height - out.Refresh = mode.refresh - } - - outputs = append(outputs, out) - } - - return outputs -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/server_client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/server_client.go deleted file mode 100644 index 871d21d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/server_client.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "net" - "os" - "path/filepath" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func sendServerRequest(req models.Request) (*models.Response[any], error) { - socketPath := getServerSocketPath() - - conn, err := net.Dial("unix", socketPath) - if err != nil { - return nil, fmt.Errorf("failed to connect to server (is it running?): %w", err) - } - defer conn.Close() - - scanner := bufio.NewScanner(conn) - scanner.Scan() // discard initial capabilities message - - reqData, err := json.Marshal(req) - if err != nil { - return nil, fmt.Errorf("failed to marshal request: %w", err) - } - - if _, err := conn.Write(reqData); err != nil { - return nil, fmt.Errorf("failed to write request: %w", err) - } - - if _, err := conn.Write([]byte("\n")); err != nil { - return nil, fmt.Errorf("failed to write newline: %w", err) - } - - if !scanner.Scan() { - return nil, fmt.Errorf("failed to read response") - } - - var resp models.Response[any] - if err := json.Unmarshal(scanner.Bytes(), &resp); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &resp, nil -} - -// sendServerRequestFireAndForget sends a request without waiting for a response. -// Useful for commands that trigger UI or async operations. -func sendServerRequestFireAndForget(req models.Request) error { - socketPath := getServerSocketPath() - - conn, err := net.Dial("unix", socketPath) - if err != nil { - return fmt.Errorf("failed to connect to server (is it running?): %w", err) - } - defer conn.Close() - - scanner := bufio.NewScanner(conn) - scanner.Scan() // discard initial capabilities message - - reqData, err := json.Marshal(req) - if err != nil { - return fmt.Errorf("failed to marshal request: %w", err) - } - - if _, err := conn.Write(reqData); err != nil { - return fmt.Errorf("failed to write request: %w", err) - } - - if _, err := conn.Write([]byte("\n")); err != nil { - return fmt.Errorf("failed to write newline: %w", err) - } - - return nil -} - -// tryServerRequest attempts to send a request but returns false if server unavailable. -// Does not log errors - caller can decide what to do on failure. -func tryServerRequest(req models.Request) (*models.Response[any], bool) { - resp, err := sendServerRequest(req) - if err != nil { - return nil, false - } - return resp, true -} - -func getServerSocketPath() string { - runtimeDir := os.Getenv("XDG_RUNTIME_DIR") - if runtimeDir == "" { - runtimeDir = os.TempDir() - } - - entries, err := os.ReadDir(runtimeDir) - if err != nil { - return filepath.Join(runtimeDir, "danklinux.sock") - } - - for _, entry := range entries { - name := entry.Name() - if name == "danklinux.sock" { - return filepath.Join(runtimeDir, name) - } - if len(name) > 10 && name[:10] == "danklinux-" && filepath.Ext(name) == ".sock" { - return filepath.Join(runtimeDir, name) - } - } - - return server.GetSocketPath() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/shell.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/shell.go deleted file mode 100644 index 7ce2003..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/shell.go +++ /dev/null @@ -1,739 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - "os/exec" - "os/signal" - "path/filepath" - "slices" - "strconv" - "strings" - "sync" - "syscall" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server" -) - -type ipcTargets map[string]map[string][]string - -// getProcessExitCode returns the exit code from a ProcessState. -// For normal exits, returns the exit code directly. -// For signal termination, returns 128 + signal number (Unix convention). -func getProcessExitCode(state *os.ProcessState) int { - if state == nil { - return 1 - } - if code := state.ExitCode(); code != -1 { - return code - } - // Process was killed by signal - extract signal number - if status, ok := state.Sys().(syscall.WaitStatus); ok { - if status.Signaled() { - return 128 + int(status.Signal()) - } - } - return 1 -} - -var isSessionManaged bool - -func execDetachedRestart(targetPID int) { - selfPath, err := os.Executable() - if err != nil { - return - } - - cmd := exec.Command(selfPath, "restart-detached", strconv.Itoa(targetPID)) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - cmd.Start() -} - -func runDetachedRestart(targetPIDStr string) { - targetPID, err := strconv.Atoi(targetPIDStr) - if err != nil { - return - } - - time.Sleep(200 * time.Millisecond) - - proc, err := os.FindProcess(targetPID) - if err == nil { - proc.Signal(syscall.SIGTERM) - } - - time.Sleep(500 * time.Millisecond) - - killShell() - runShellDaemon(false) -} - -func getRuntimeDir() string { - if runtime := os.Getenv("XDG_RUNTIME_DIR"); runtime != "" { - return runtime - } - return os.TempDir() -} - -func hasSystemdRun() bool { - _, err := exec.LookPath("systemd-run") - return err == nil -} - -func getPIDFilePath() string { - return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", os.Getpid())) -} - -func writePIDFile(childPID int) error { - pidFile := getPIDFilePath() - return os.WriteFile(pidFile, []byte(strconv.Itoa(childPID)), 0o644) -} - -func removePIDFile() { - pidFile := getPIDFilePath() - os.Remove(pidFile) -} - -func getAllDMSPIDs() []int { - dir := getRuntimeDir() - entries, err := os.ReadDir(dir) - if err != nil { - return nil - } - - var pids []int - - for _, entry := range entries { - if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".pid") { - continue - } - - pidFile := filepath.Join(dir, entry.Name()) - data, err := os.ReadFile(pidFile) - if err != nil { - continue - } - - childPID, err := strconv.Atoi(strings.TrimSpace(string(data))) - if err != nil { - os.Remove(pidFile) - continue - } - - proc, err := os.FindProcess(childPID) - if err != nil { - os.Remove(pidFile) - continue - } - - if err := proc.Signal(syscall.Signal(0)); err != nil { - os.Remove(pidFile) - continue - } - - pids = append(pids, childPID) - - parentPIDStr := strings.TrimPrefix(entry.Name(), "danklinux-") - parentPIDStr = strings.TrimSuffix(parentPIDStr, ".pid") - if parentPID, err := strconv.Atoi(parentPIDStr); err == nil { - if parentProc, err := os.FindProcess(parentPID); err == nil { - if err := parentProc.Signal(syscall.Signal(0)); err == nil { - pids = append(pids, parentPID) - } - } - } - } - - return pids -} - -func runShellInteractive(session bool) { - isSessionManaged = session - go printASCII() - fmt.Fprintf(os.Stderr, "dms %s\n", Version) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - socketPath := server.GetSocketPath() - - configStateFile := filepath.Join(getRuntimeDir(), "danklinux.path") - if err := os.WriteFile(configStateFile, []byte(configPath), 0o644); err != nil { - log.Warnf("Failed to write config state file: %v", err) - } - defer os.Remove(configStateFile) - - errChan := make(chan error, 2) - - go func() { - defer func() { - if r := recover(); r != nil { - errChan <- fmt.Errorf("server panic: %v", r) - } - }() - server.CLIVersion = Version - if err := server.Start(false); err != nil { - errChan <- fmt.Errorf("server error: %w", err) - } - }() - - log.Infof("Spawning quickshell with -p %s", configPath) - - cmd := exec.CommandContext(ctx, "qs", "-p", configPath) - cmd.Env = append(os.Environ(), "DMS_SOCKET="+socketPath) - if os.Getenv("QT_LOGGING_RULES") == "" { - if qtRules := log.GetQtLoggingRules(); qtRules != "" { - cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules) - } - } - - // ! TODO - remove when QS 0.3 is up and we can use the pragma - cmd.Env = append(cmd.Env, "QS_APP_ID=com.danklinux.dms") - - if isSessionManaged && hasSystemdRun() { - cmd.Env = append(cmd.Env, "DMS_DEFAULT_LAUNCH_PREFIX=systemd-run --user --scope") - } - - homeDir, err := os.UserHomeDir() - if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" { - if !strings.HasPrefix(configPath, homeDir) { - cmd.Env = append(cmd.Env, "DMS_DISABLE_HOT_RELOAD=1") - } - } - - if os.Getenv("QT_QPA_PLATFORMTHEME") == "" { - cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME=gtk3") - } - if os.Getenv("QT_QPA_PLATFORMTHEME_QT6") == "" { - cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME_QT6=gtk3") - } - if os.Getenv("QT_QPA_PLATFORM") == "" { - cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland;xcb") - } - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Start(); err != nil { - log.Fatalf("Error starting quickshell: %v", err) - } - - // Write PID file for the quickshell child process - if err := writePIDFile(cmd.Process.Pid); err != nil { - log.Warnf("Failed to write PID file: %v", err) - } - defer removePIDFile() - - defer func() { - if cmd.Process != nil { - cmd.Process.Signal(syscall.SIGTERM) - } - }() - - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1) - - go func() { - if err := cmd.Wait(); err != nil { - errChan <- fmt.Errorf("quickshell exited: %w", err) - } else { - errChan <- fmt.Errorf("quickshell exited") - } - }() - - for { - select { - case sig := <-sigChan: - if sig == syscall.SIGUSR1 { - if isSessionManaged { - log.Infof("Received SIGUSR1, exiting for systemd restart...") - cancel() - cmd.Process.Signal(syscall.SIGTERM) - os.Remove(socketPath) - os.Exit(1) - } - log.Infof("Received SIGUSR1, spawning detached restart process...") - execDetachedRestart(os.Getpid()) - return - } - - // Check if qs already crashed before we got SIGTERM (systemd sends SIGTERM when D-Bus name is released) - select { - case <-errChan: - cancel() - os.Remove(socketPath) - os.Exit(getProcessExitCode(cmd.ProcessState)) - case <-time.After(500 * time.Millisecond): - } - - log.Infof("\nReceived signal %v, shutting down...", sig) - cancel() - cmd.Process.Signal(syscall.SIGTERM) - os.Remove(socketPath) - return - - case err := <-errChan: - log.Error(err) - cancel() - if cmd.Process != nil { - cmd.Process.Signal(syscall.SIGTERM) - } - os.Remove(socketPath) - os.Exit(getProcessExitCode(cmd.ProcessState)) - } - } -} - -func restartShell() { - pids := getAllDMSPIDs() - - if len(pids) == 0 { - log.Info("No running DMS shell instances found. Starting daemon...") - runShellDaemon(false) - return - } - - currentPid := os.Getpid() - uniquePids := make(map[int]bool) - - for _, pid := range pids { - if pid != currentPid { - uniquePids[pid] = true - } - } - - for pid := range uniquePids { - proc, err := os.FindProcess(pid) - if err != nil { - log.Errorf("Error finding process %d: %v", pid, err) - continue - } - - if err := proc.Signal(syscall.Signal(0)); err != nil { - continue - } - - if err := proc.Signal(syscall.SIGUSR1); err != nil { - log.Errorf("Error sending SIGUSR1 to process %d: %v", pid, err) - } else { - log.Infof("Sent SIGUSR1 to DMS process with PID %d", pid) - } - } -} - -func killShell() { - pids := getAllDMSPIDs() - - if len(pids) == 0 { - log.Info("No running DMS shell instances found.") - return - } - - currentPid := os.Getpid() - uniquePids := make(map[int]bool) - - for _, pid := range pids { - if pid != currentPid { - uniquePids[pid] = true - } - } - - for pid := range uniquePids { - proc, err := os.FindProcess(pid) - if err != nil { - log.Errorf("Error finding process %d: %v", pid, err) - continue - } - - if err := proc.Signal(syscall.Signal(0)); err != nil { - continue - } - - if err := proc.Kill(); err != nil { - log.Errorf("Error killing process %d: %v", pid, err) - } else { - log.Infof("Killed DMS process with PID %d", pid) - } - } - - dir := getRuntimeDir() - entries, err := os.ReadDir(dir) - if err != nil { - return - } - - for _, entry := range entries { - if strings.HasPrefix(entry.Name(), "danklinux-") && strings.HasSuffix(entry.Name(), ".pid") { - pidFile := filepath.Join(dir, entry.Name()) - os.Remove(pidFile) - } - } -} - -func runShellDaemon(session bool) { - isSessionManaged = session - isDaemonChild := slices.Contains(os.Args, "--daemon-child") - - if !isDaemonChild { - fmt.Fprintf(os.Stderr, "dms %s\n", Version) - - cmd := exec.Command(os.Args[0], "run", "-d", "--daemon-child") - cmd.Env = os.Environ() - - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - - if err := cmd.Start(); err != nil { - log.Fatalf("Error starting daemon: %v", err) - } - - log.Infof("DMS shell daemon started (PID: %d)", cmd.Process.Pid) - return - } - - fmt.Fprintf(os.Stderr, "dms %s\n", Version) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - socketPath := server.GetSocketPath() - - configStateFile := filepath.Join(getRuntimeDir(), "danklinux.path") - if err := os.WriteFile(configStateFile, []byte(configPath), 0o644); err != nil { - log.Warnf("Failed to write config state file: %v", err) - } - defer os.Remove(configStateFile) - - errChan := make(chan error, 2) - - go func() { - defer func() { - if r := recover(); r != nil { - errChan <- fmt.Errorf("server panic: %v", r) - } - }() - server.CLIVersion = Version - if err := server.Start(false); err != nil { - errChan <- fmt.Errorf("server error: %w", err) - } - }() - - log.Infof("Spawning quickshell with -p %s", configPath) - - cmd := exec.CommandContext(ctx, "qs", "-p", configPath) - cmd.Env = append(os.Environ(), "DMS_SOCKET="+socketPath) - if os.Getenv("QT_LOGGING_RULES") == "" { - if qtRules := log.GetQtLoggingRules(); qtRules != "" { - cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules) - } - } - - // ! TODO - remove when QS 0.3 is up and we can use the pragma - cmd.Env = append(cmd.Env, "QS_APP_ID=com.danklinux.dms") - - if isSessionManaged && hasSystemdRun() { - cmd.Env = append(cmd.Env, "DMS_DEFAULT_LAUNCH_PREFIX=systemd-run --user --scope") - } - - homeDir, err := os.UserHomeDir() - if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" { - if !strings.HasPrefix(configPath, homeDir) { - cmd.Env = append(cmd.Env, "DMS_DISABLE_HOT_RELOAD=1") - } - } - - if os.Getenv("QT_QPA_PLATFORMTHEME") == "" { - cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME=gtk3") - } - if os.Getenv("QT_QPA_PLATFORMTHEME_QT6") == "" { - cmd.Env = append(cmd.Env, "QT_QPA_PLATFORMTHEME_QT6=gtk3") - } - if os.Getenv("QT_QPA_PLATFORM") == "" { - cmd.Env = append(cmd.Env, "QT_QPA_PLATFORM=wayland;xcb") - } - - devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0) - if err != nil { - log.Fatalf("Error opening /dev/null: %v", err) - } - defer devNull.Close() - - cmd.Stdin = devNull - cmd.Stdout = devNull - cmd.Stderr = devNull - - if err := cmd.Start(); err != nil { - log.Fatalf("Error starting daemon: %v", err) - } - - // Write PID file for the quickshell child process - if err := writePIDFile(cmd.Process.Pid); err != nil { - log.Warnf("Failed to write PID file: %v", err) - } - defer removePIDFile() - - defer func() { - if cmd.Process != nil { - cmd.Process.Signal(syscall.SIGTERM) - } - }() - - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1) - - go func() { - if err := cmd.Wait(); err != nil { - errChan <- fmt.Errorf("quickshell exited: %w", err) - } else { - errChan <- fmt.Errorf("quickshell exited") - } - }() - - for { - select { - case sig := <-sigChan: - if sig == syscall.SIGUSR1 { - if isSessionManaged { - log.Infof("Received SIGUSR1, exiting for systemd restart...") - cancel() - cmd.Process.Signal(syscall.SIGTERM) - os.Remove(socketPath) - os.Exit(1) - } - log.Infof("Received SIGUSR1, spawning detached restart process...") - execDetachedRestart(os.Getpid()) - return - } - - // Check if qs already crashed before we got SIGTERM (systemd sends SIGTERM when D-Bus name is released) - select { - case <-errChan: - cancel() - os.Remove(socketPath) - os.Exit(getProcessExitCode(cmd.ProcessState)) - case <-time.After(500 * time.Millisecond): - } - - cancel() - cmd.Process.Signal(syscall.SIGTERM) - os.Remove(socketPath) - return - - case <-errChan: - cancel() - if cmd.Process != nil { - cmd.Process.Signal(syscall.SIGTERM) - } - os.Remove(socketPath) - os.Exit(getProcessExitCode(cmd.ProcessState)) - } - } -} - -var qsHasAnyDisplay = sync.OnceValue(func() bool { - out, err := exec.Command("qs", "ipc", "--help").Output() - if err != nil { - return false - } - return strings.Contains(string(out), "--any-display") -}) - -func parseTargetsFromIPCShowOutput(output string) ipcTargets { - targets := make(ipcTargets) - var currentTarget string - for line := range strings.SplitSeq(output, "\n") { - if after, ok := strings.CutPrefix(line, "target "); ok { - currentTarget = strings.TrimSpace(after) - targets[currentTarget] = make(map[string][]string) - } - if strings.HasPrefix(line, " function") && currentTarget != "" { - argsList := []string{} - currentFunc := strings.TrimPrefix(line, " function ") - funcDef := strings.SplitN(currentFunc, "(", 2) - argList := strings.SplitN(funcDef[1], ")", 2)[0] - args := strings.Split(argList, ",") - if len(args) > 0 && strings.TrimSpace(args[0]) != "" { - argsList = append(argsList, funcDef[0]) - for _, arg := range args { - argName := strings.SplitN(strings.TrimSpace(arg), ":", 2)[0] - argsList = append(argsList, argName) - } - targets[currentTarget][funcDef[0]] = argsList - } else { - targets[currentTarget][funcDef[0]] = make([]string, 0) - } - } - } - return targets -} - -func getShellIPCCompletions(args []string, _ string) []string { - cmdArgs := []string{"ipc"} - if qsHasAnyDisplay() { - cmdArgs = append(cmdArgs, "--any-display") - } - cmdArgs = append(cmdArgs, "-p", configPath, "show") - cmd := exec.Command("qs", cmdArgs...) - var targets ipcTargets - - if output, err := cmd.Output(); err == nil { - targets = parseTargetsFromIPCShowOutput(string(output)) - } else { - log.Debugf("Error getting IPC show output for completions: %v", err) - return nil - } - - if len(args) > 0 && args[0] == "call" { - args = args[1:] - } - - if len(args) == 0 { - targetNames := make([]string, 0) - targetNames = append(targetNames, "call") - for k := range targets { - targetNames = append(targetNames, k) - } - return targetNames - } - if len(args) == 1 { - if targetFuncs, ok := targets[args[0]]; ok { - funcNames := make([]string, 0) - for k := range targetFuncs { - funcNames = append(funcNames, k) - } - return funcNames - } - return nil - } - if len(args) <= len(targets[args[0]]) { - funcArgs := targets[args[0]][args[1]] - if len(funcArgs) >= len(args) { - return []string{fmt.Sprintf("[%s]", funcArgs[len(args)-1])} - } - } - - return nil -} - -func getFirstDMSPID() (int, bool) { - dir := getRuntimeDir() - entries, err := os.ReadDir(dir) - if err != nil { - return 0, false - } - - for _, entry := range entries { - if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".pid") { - continue - } - - data, err := os.ReadFile(filepath.Join(dir, entry.Name())) - if err != nil { - continue - } - - pid, err := strconv.Atoi(strings.TrimSpace(string(data))) - if err != nil { - continue - } - - proc, err := os.FindProcess(pid) - if err != nil { - continue - } - - if proc.Signal(syscall.Signal(0)) != nil { - continue - } - - return pid, true - } - - return 0, false -} - -func runShellIPCCommand(args []string) { - if len(args) == 0 { - printIPCHelp() - return - } - - if args[0] != "call" { - args = append([]string{"call"}, args...) - } - - cmdArgs := []string{"ipc"} - - switch pid, ok := getFirstDMSPID(); { - case ok: - cmdArgs = append(cmdArgs, "--pid", strconv.Itoa(pid)) - default: - if err := findConfig(nil, nil); err != nil { - log.Fatalf("Error finding config: %v", err) - } - // ! TODO - remove check when QS 0.3 is released - if qsHasAnyDisplay() { - cmdArgs = append(cmdArgs, "--any-display") - } - cmdArgs = append(cmdArgs, "-p", configPath) - } - - cmdArgs = append(cmdArgs, args...) - cmd := exec.Command("qs", cmdArgs...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Run(); err != nil { - log.Fatalf("Error running IPC command: %v", err) - } -} - -func printIPCHelp() { - fmt.Println("Usage: dms ipc [args...]") - fmt.Println() - - cmdArgs := []string{"ipc"} - if qsHasAnyDisplay() { - cmdArgs = append(cmdArgs, "--any-display") - } - cmdArgs = append(cmdArgs, "-p", configPath, "show") - cmd := exec.Command("qs", cmdArgs...) - - output, err := cmd.Output() - if err != nil { - fmt.Println("Could not retrieve available IPC targets (is DMS running?)") - return - } - - targets := parseTargetsFromIPCShowOutput(string(output)) - if len(targets) == 0 { - fmt.Println("No IPC targets available") - return - } - - fmt.Println("Targets:") - - targetNames := make([]string, 0, len(targets)) - for name := range targets { - targetNames = append(targetNames, name) - } - slices.Sort(targetNames) - - for _, targetName := range targetNames { - funcs := targets[targetName] - funcNames := make([]string, 0, len(funcs)) - for fn := range funcs { - funcNames = append(funcNames, fn) - } - slices.Sort(funcNames) - fmt.Printf(" %-16s %s\n", targetName, strings.Join(funcNames, ", ")) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/ui.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/ui.go deleted file mode 100644 index c0b50e6..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/ui.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/tui" - "github.com/charmbracelet/lipgloss" -) - -func printASCII() { - fmt.Print(getThemedASCII()) -} - -func getThemedASCII() string { - theme := tui.TerminalTheme() - - logo := ` -██████╗ █████╗ ███╗ ██╗██╗ ██╗ -██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝ -██║ ██║███████║██╔██╗ ██║█████╔╝ -██║ ██║██╔══██║██║╚██╗██║██╔═██╗ -██████╔╝██║ ██║██║ ╚████║██║ ██╗ -╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝` - - style := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true) - - return style.Render(logo) + "\n" -} - -func getHelpTemplate() string { - return getThemedASCII() + ` -{{.Long}} - -Usage: - {{.UseLine}}{{if .HasAvailableSubCommands}} - -Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} - -Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} - {{rpad .Name .NamePadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - -Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} -` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/utils.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/utils.go deleted file mode 100644 index 9459e41..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/cmd/dms/utils.go +++ /dev/null @@ -1,89 +0,0 @@ -package main - -import ( - "fmt" - "os/exec" - "slices" - "strings" -) - -// isReadOnlyCommand returns true if the CLI args indicate a command that is -// safe to run as root (e.g. shell completion, help). -func isReadOnlyCommand(args []string) bool { - for _, arg := range args[1:] { - if strings.HasPrefix(arg, "-") { - continue - } - switch arg { - case "completion", "help", "__complete": - return true - } - return false - } - return false -} - -func isArchPackageInstalled(packageName string) bool { - cmd := exec.Command("pacman", "-Q", packageName) - err := cmd.Run() - return err == nil -} - -type systemdServiceState struct { - Name string - EnabledState string - NeedsDisable bool - Exists bool -} - -// checkSystemdServiceEnabled returns (state, should_disable, error) for a systemd service -func checkSystemdServiceEnabled(serviceName string) (string, bool, error) { - cmd := exec.Command("systemctl", "is-enabled", serviceName) - output, err := cmd.Output() - - stateStr := strings.TrimSpace(string(output)) - - if err != nil { - knownStates := []string{"disabled", "masked", "masked-runtime", "not-found", "enabled", "enabled-runtime", "static", "indirect", "alias"} - isKnownState := slices.Contains(knownStates, stateStr) - - if !isKnownState { - return stateStr, false, fmt.Errorf("systemctl is-enabled failed: %w (output: %s)", err, stateStr) - } - } - - shouldDisable := false - switch stateStr { - case "enabled", "enabled-runtime", "static", "indirect", "alias": - shouldDisable = true - case "disabled", "masked", "masked-runtime", "not-found": - shouldDisable = false - default: - shouldDisable = true - } - - return stateStr, shouldDisable, nil -} - -func getSystemdServiceState(serviceName string) (*systemdServiceState, error) { - state := &systemdServiceState{ - Name: serviceName, - Exists: false, - } - - enabledState, needsDisable, err := checkSystemdServiceEnabled(serviceName) - if err != nil { - return nil, fmt.Errorf("failed to check enabled state: %w", err) - } - - state.EnabledState = enabledState - state.NeedsDisable = needsDisable - - if enabledState == "not-found" { - state.Exists = false - return state, nil - } - - state.Exists = true - return state, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/go.mod b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/go.mod deleted file mode 100644 index 9f95be8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/go.mod +++ /dev/null @@ -1,87 +0,0 @@ -module github.com/AvengeMedia/DankMaterialShell/core - -go 1.26.0 - -toolchain go1.26.1 - -require ( - github.com/Wifx/gonetworkmanager/v2 v2.2.0 - github.com/alecthomas/chroma/v2 v2.23.1 - github.com/charmbracelet/bubbles v1.0.0 - github.com/charmbracelet/bubbletea v1.3.10 - github.com/charmbracelet/lipgloss v1.1.0 - github.com/charmbracelet/log v0.4.2 - github.com/fsnotify/fsnotify v1.9.0 - github.com/godbus/dbus/v5 v5.2.2 - github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83 - github.com/pilebones/go-udev v0.9.1 - github.com/sblinch/kdl-go v0.0.0-20260121213736-8b7053306ca6 - github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 - github.com/yeqown/go-qrcode/v2 v2.2.5 - github.com/yeqown/go-qrcode/writer/standard v1.3.0 - github.com/yuin/goldmark v1.7.16 - github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc - go.etcd.io/bbolt v1.4.3 - golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a - golang.org/x/image v0.36.0 -) - -require ( - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.3.0 // indirect - github.com/clipperhouse/displaywidth v0.10.0 // indirect - github.com/clipperhouse/uax29/v2 v2.7.0 // indirect - github.com/cloudflare/circl v1.6.3 // indirect - github.com/cyphar/filepath-securejoin v0.6.1 // indirect - github.com/dlclark/regexp2 v1.11.5 // indirect - github.com/emirpasic/gods v1.18.1 // indirect - github.com/fogleman/gg v1.3.0 // indirect - github.com/go-git/gcfg/v2 v2.0.2 // indirect - github.com/go-git/go-billy/v6 v6.0.0-20260209124918-37866f83c2d3 // indirect - github.com/go-logfmt/logfmt v0.6.1 // indirect - github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/kevinburke/ssh_config v1.6.0 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/pjbgf/sha1cd v0.5.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/sergi/go-diff v1.4.0 // indirect - github.com/stretchr/objx v0.5.3 // indirect - github.com/yeqown/reedsolomon v1.0.0 // indirect - golang.org/x/crypto v0.48.0 // indirect - golang.org/x/net v0.50.0 // indirect -) - -require ( - github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/colorprofile v0.4.2 // indirect - github.com/charmbracelet/harmonica v0.2.0 // indirect - github.com/charmbracelet/x/ansi v0.11.6 // indirect - github.com/charmbracelet/x/cellbuf v0.0.15 // indirect - github.com/charmbracelet/x/term v0.2.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/go-git/go-git/v6 v6.0.0-20260216160506-e6a3f881772f - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/lucasb-eyer/go-colorful v1.3.0 - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.19 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/spf13/afero v1.15.0 - github.com/spf13/pflag v1.0.10 // indirect - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/sys v0.41.0 - golang.org/x/text v0.34.0 - gopkg.in/yaml.v3 v3.0.1 -) - -// v0.0.1 tag is missing a LICENSE file; master has it. -// See: https://github.com/mattn/go-localereader/issues/2 -replace github.com/mattn/go-localereader v0.0.1 => github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/go.sum b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/go.sum deleted file mode 100644 index 4817d4f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/go.sum +++ /dev/null @@ -1,191 +0,0 @@ -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= -github.com/Wifx/gonetworkmanager/v2 v2.2.0 h1:kPstgsQtY8CmDOOFZd81ytM9Gi3f6ImzPCKF7nNhQ2U= -github.com/Wifx/gonetworkmanager/v2 v2.2.0/go.mod h1:fMDb//SHsKWxyDUAwXvCqurV3npbIyyaQWenGpZ/uXg= -github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= -github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY= -github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o= -github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= -github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= -github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc= -github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E= -github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= -github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY= -github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8= -github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= -github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= -github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= -github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= -github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= -github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= -github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= -github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= -github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g= -github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs= -github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= -github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= -github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= -github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= -github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= -github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= -github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= -github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo= -github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs= -github.com/go-git/go-billy/v6 v6.0.0-20260209124918-37866f83c2d3 h1:UU7oARtwQ5g85aFiCSwIUA6PBmAshYj0sytl/5CCBgs= -github.com/go-git/go-billy/v6 v6.0.0-20260209124918-37866f83c2d3/go.mod h1:ZW9JC5gionMP1kv5uiaOaV23q0FFmNrVOV8VW+y/acc= -github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20260122163445-0622d7459a67 h1:3hutPZF+/FBjR/9MdsLJ7e1mlt9pwHgwxMW7CrbmWII= -github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20260122163445-0622d7459a67/go.mod h1:xKt0pNHST9tYHvbiLxSY27CQWFwgIxBJuDrOE0JvbZw= -github.com/go-git/go-git/v6 v6.0.0-20260216160506-e6a3f881772f h1:TBkCJv9YwPOuXq1OG0r01bcxRrvs15Hp/DtZuPt4H6s= -github.com/go-git/go-git/v6 v6.0.0-20260216160506-e6a3f881772f/go.mod h1:B88nWzfnhTlIikoJ4d84Nc9noKS5mJoA7SgDdkt0aPU= -github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE= -github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= -github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83 h1:B+A58zGFuDrvEZpPN+yS6swJA0nzqgZvDzgl/OPyefU= -github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83/go.mod h1:iHAf8OIncO2gcQ8XOjS7CMJ2aPbX2Bs0wl5pZyanEqk= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY= -github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= -github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw= -github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= -github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/pilebones/go-udev v0.9.1 h1:uN72M1C1fgzhsVmBGEM8w9RD1JY4iVsPZpr+Z6rb3O8= -github.com/pilebones/go-udev v0.9.1/go.mod h1:Bgcl07crebF3JSeS4+nuaRvhWFdCeFoBhXXeAp93XNo= -github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= -github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sblinch/kdl-go v0.0.0-20260121213736-8b7053306ca6 h1:JsjzqC6ymELkN4XlTjZPSahSAem21GySugLbKz6uF5E= -github.com/sblinch/kdl-go v0.0.0-20260121213736-8b7053306ca6/go.mod h1:b3oNGuAKOQzhsCKmuLc/urEOPzgHj6fB8vl8bwTBh28= -github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= -github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= -github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= -github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= -github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= -github.com/yeqown/go-qrcode/v2 v2.2.5 h1:HCOe2bSjkhZyYoyyNaXNzh4DJZll6inVJQQw+8228Zk= -github.com/yeqown/go-qrcode/v2 v2.2.5/go.mod h1:uHpt9CM0V1HeXLz+Wg5MN50/sI/fQhfkZlOM+cOTHxw= -github.com/yeqown/go-qrcode/writer/standard v1.3.0 h1:chdyhEfRtUPgQtuPeaWVGQ/TQx4rE1PqeoW3U+53t34= -github.com/yeqown/go-qrcode/writer/standard v1.3.0/go.mod h1:O4MbzsotGCvy8upYPCR91j81dr5XLT7heuljcNXW+oQ= -github.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr4B0= -github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM= -github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= -github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= -github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= -go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= -go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o= -golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= -golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc= -golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/install.sh b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/install.sh deleted file mode 100755 index baca588..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/install.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bash - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -# Check for root privileges -if [ "$(id -u)" == "0" ]; then - printf "%bError: This script must not be run as root%b\n" "$RED" "$NC" - exit 1 -fi - -# Check if running on Linux -if [ "$(uname)" != "Linux" ]; then - printf "%bError: This installer only supports Linux systems%b\n" "$RED" "$NC" - exit 1 -fi - -# Detect architecture -ARCH=$(uname -m) -case "$ARCH" in -x86_64) - ARCH="amd64" - ;; -aarch64) - ARCH="arm64" - ;; -*) - printf "%bError: Unsupported architecture: %s%b\n" "$RED" "$ARCH" "$NC" - printf "This installer only supports x86_64 (amd64) and aarch64 (arm64) architectures\n" - exit 1 - ;; -esac - -# Get the latest release version -LATEST_VERSION=$(curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') - -if [ -z "$LATEST_VERSION" ]; then - printf "%bError: Could not fetch latest version%b\n" "$RED" "$NC" - exit 1 -fi - -printf "%bInstalling Dankinstall %s for %s...%b\n" "$GREEN" "$LATEST_VERSION" "$ARCH" "$NC" - -# Download and install -TEMP_DIR=$(mktemp -d) -cd "$TEMP_DIR" || exit 1 - -# Download the gzipped binary and its checksum -printf "%bDownloading installer...%b\n" "$GREEN" "$NC" -curl -L "https://github.com/AvengeMedia/DankMaterialShell/releases/download/$LATEST_VERSION/dankinstall-$ARCH.gz" -o "installer.gz" -curl -L "https://github.com/AvengeMedia/DankMaterialShell/releases/download/$LATEST_VERSION/dankinstall-$ARCH.gz.sha256" -o "expected.sha256" - -# Get the expected checksum -EXPECTED_CHECKSUM=$(awk '{print $1}' expected.sha256) - -# Calculate actual checksum -printf "%bVerifying checksum...%b\n" "$GREEN" "$NC" -ACTUAL_CHECKSUM=$(sha256sum installer.gz | awk '{print $1}') - -# Compare checksums -if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then - printf "%bError: Checksum verification failed%b\n" "$RED" "$NC" - printf "Expected: %s\n" "$EXPECTED_CHECKSUM" - printf "Got: %s\n" "$ACTUAL_CHECKSUM" - printf "The downloaded file may be corrupted or tampered with\n" - cd - >/dev/null - rm -rf "$TEMP_DIR" - exit 1 -fi - -# Decompress the binary -printf "%bDecompressing installer...%b\n" "$GREEN" "$NC" -gunzip installer.gz -chmod +x installer - -# Execute the installer -printf "%bRunning installer...%b\n" "$GREEN" "$NC" -./installer - -# Cleanup -cd - >/dev/null -rm -rf "$TEMP_DIR" diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/blur/probe.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/blur/probe.go deleted file mode 100644 index e9c24b8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/blur/probe.go +++ /dev/null @@ -1,35 +0,0 @@ -package blur - -import ( - wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client" - client "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -const extBackgroundEffectInterface = "ext_background_effect_manager_v1" - -func ProbeSupport() (bool, error) { - display, err := client.Connect("") - if err != nil { - return false, err - } - defer display.Context().Close() - - registry, err := display.GetRegistry() - if err != nil { - return false, err - } - - found := false - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - switch e.Interface { - case extBackgroundEffectInterface: - found = true - } - }) - - if err := wlhelpers.Roundtrip(display, display.Context()); err != nil { - return false, err - } - - return found, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/clipboard.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/clipboard.go deleted file mode 100644 index d27d6ff..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/clipboard.go +++ /dev/null @@ -1,630 +0,0 @@ -package clipboard - -import ( - "fmt" - "io" - "os" - "os/exec" - "path/filepath" - "syscall" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_data_control" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -const envServe = "_DMS_CLIPBOARD_SERVE" -const envMime = "_DMS_CLIPBOARD_MIME" -const envPasteOnce = "_DMS_CLIPBOARD_PASTE_ONCE" -const envCacheFile = "_DMS_CLIPBOARD_CACHE" - -// MaybeServeAndExit intercepts before cobra when re-exec'd as a clipboard -// child. Reads source data into memory, deletes any cache file, then serves. -func MaybeServeAndExit() { - if os.Getenv(envServe) == "" { - return - } - - mimeType := os.Getenv(envMime) - pasteOnce := os.Getenv(envPasteOnce) == "1" - cachePath := os.Getenv(envCacheFile) - - var data []byte - var err error - - switch { - case cachePath != "": - data, err = os.ReadFile(cachePath) - os.Remove(cachePath) - default: - data, err = io.ReadAll(os.Stdin) - } - - if err != nil { - fmt.Fprintf(os.Stderr, "clipboard: read source: %v\n", err) - os.Exit(1) - } - - if err := serveClipboard(data, mimeType, pasteOnce); err != nil { - fmt.Fprintf(os.Stderr, "clipboard: serve: %v\n", err) - os.Exit(1) - } - os.Exit(0) -} - -func Copy(data []byte, mimeType string) error { - return copyForkCached(data, mimeType, false) -} - -func CopyOpts(data []byte, mimeType string, foreground, pasteOnce bool) error { - if foreground { - return serveClipboard(data, mimeType, pasteOnce) - } - return copyForkCached(data, mimeType, pasteOnce) -} - -func CopyReader(data io.Reader, mimeType string, foreground, pasteOnce bool) error { - if foreground { - buf, err := io.ReadAll(data) - if err != nil { - return fmt.Errorf("read source: %w", err) - } - return serveClipboard(buf, mimeType, pasteOnce) - } - return copyFork(data, mimeType, pasteOnce) -} - -func newForkCmd(mimeType string, pasteOnce bool, extra ...string) *exec.Cmd { - cmd := exec.Command(os.Args[0]) - cmd.Stderr = nil - cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - cmd.Env = append(os.Environ(), - envServe+"=1", - envMime+"="+mimeType, - ) - if pasteOnce { - cmd.Env = append(cmd.Env, envPasteOnce+"=1") - } - cmd.Env = append(cmd.Env, extra...) - return cmd -} - -func waitReady(cmd *exec.Cmd) error { - stdout, err := cmd.StdoutPipe() - if err != nil { - return fmt.Errorf("stdout pipe: %w", err) - } - if err := cmd.Start(); err != nil { - return fmt.Errorf("start: %w", err) - } - var buf [1]byte - if _, err := stdout.Read(buf[:]); err != nil { - return fmt.Errorf("waiting for clipboard ready: %w", err) - } - return nil -} - -func copyForkCached(data []byte, mimeType string, pasteOnce bool) error { - cacheFile, err := createClipboardCacheFile() - if err != nil { - return fmt.Errorf("create cache file: %w", err) - } - cachePath := cacheFile.Name() - - if _, err := cacheFile.Write(data); err != nil { - cacheFile.Close() - os.Remove(cachePath) - return fmt.Errorf("write cache file: %w", err) - } - if err := cacheFile.Close(); err != nil { - os.Remove(cachePath) - return fmt.Errorf("close cache file: %w", err) - } - - cmd := newForkCmd(mimeType, pasteOnce, envCacheFile+"="+cachePath) - cmd.Stdin = nil - if err := waitReady(cmd); err != nil { - os.Remove(cachePath) - return err - } - return nil -} - -func copyFork(data io.Reader, mimeType string, pasteOnce bool) error { - cmd := newForkCmd(mimeType, pasteOnce) - - switch src := data.(type) { - case *os.File: - cmd.Stdin = src - return waitReady(cmd) - - default: - stdin, err := cmd.StdinPipe() - if err != nil { - return fmt.Errorf("stdin pipe: %w", err) - } - - stdout, err := cmd.StdoutPipe() - if err != nil { - return fmt.Errorf("stdout pipe: %w", err) - } - - if err := cmd.Start(); err != nil { - return fmt.Errorf("start: %w", err) - } - if _, err := io.Copy(stdin, data); err != nil { - stdin.Close() - return fmt.Errorf("write stdin: %w", err) - } - if err := stdin.Close(); err != nil { - return fmt.Errorf("close stdin: %w", err) - } - - var buf [1]byte - if _, err := stdout.Read(buf[:]); err != nil { - return fmt.Errorf("waiting for clipboard ready: %w", err) - } - return nil - } -} - -func signalReady() { - if os.Getenv(envServe) == "" { - return - } - os.Stdout.Write([]byte{1}) -} - -func createClipboardCacheFile() (*os.File, error) { - preferredDirs := []string{} - - if cacheDir, err := os.UserCacheDir(); err == nil { - preferredDirs = append(preferredDirs, filepath.Join(cacheDir, "dms", "clipboard")) - } - preferredDirs = append(preferredDirs, "/var/tmp/dms/clipboard") - - for _, dir := range preferredDirs { - if err := os.MkdirAll(dir, 0o700); err != nil { - continue - } - cachedData, err := os.CreateTemp(dir, "dms-clipboard-*") - if err == nil { - return cachedData, nil - } - } - return os.CreateTemp("", "dms-clipboard-*") -} - -func serveClipboard(data []byte, mimeType string, pasteOnce bool) error { - display, err := wlclient.Connect("") - if err != nil { - return fmt.Errorf("wayland connect: %w", err) - } - defer display.Destroy() - - ctx := display.Context() - registry, err := display.GetRegistry() - if err != nil { - return fmt.Errorf("get registry: %w", err) - } - defer registry.Destroy() - - var dataControlMgr *ext_data_control.ExtDataControlManagerV1 - var seat *wlclient.Seat - var bindErr error - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case "ext_data_control_manager_v1": - dataControlMgr = ext_data_control.NewExtDataControlManagerV1(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil { - bindErr = err - } - case "wl_seat": - if seat != nil { - return - } - seat = wlclient.NewSeat(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil { - bindErr = err - } - } - }) - - display.Roundtrip() - display.Roundtrip() - - if bindErr != nil { - return fmt.Errorf("registry bind: %w", bindErr) - } - if dataControlMgr == nil { - return fmt.Errorf("compositor does not support ext_data_control_manager_v1") - } - defer dataControlMgr.Destroy() - if seat == nil { - return fmt.Errorf("no seat available") - } - - device, err := dataControlMgr.GetDataDevice(seat) - if err != nil { - return fmt.Errorf("get data device: %w", err) - } - defer device.Destroy() - - source, err := dataControlMgr.CreateDataSource() - if err != nil { - return fmt.Errorf("create data source: %w", err) - } - - if err := source.Offer(mimeType); err != nil { - return fmt.Errorf("offer mime type: %w", err) - } - if mimeType == "text/plain;charset=utf-8" || mimeType == "text/plain" { - if err := source.Offer("text/plain"); err != nil { - return fmt.Errorf("offer text/plain: %w", err) - } - if err := source.Offer("text/plain;charset=utf-8"); err != nil { - return fmt.Errorf("offer text/plain;charset=utf-8: %w", err) - } - if err := source.Offer("UTF8_STRING"); err != nil { - return fmt.Errorf("offer UTF8_STRING: %w", err) - } - if err := source.Offer("STRING"); err != nil { - return fmt.Errorf("offer STRING: %w", err) - } - if err := source.Offer("TEXT"); err != nil { - return fmt.Errorf("offer TEXT: %w", err) - } - } - - cancelled := make(chan struct{}) - pasted := make(chan struct{}, 1) - - source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) { - _ = syscall.SetNonblock(e.Fd, false) - file := os.NewFile(uintptr(e.Fd), "pipe") - defer file.Close() - _, _ = file.Write(data) - select { - case pasted <- struct{}{}: - default: - } - }) - - source.SetCancelledHandler(func(e ext_data_control.ExtDataControlSourceV1CancelledEvent) { - close(cancelled) - }) - - if err := device.SetSelection(source); err != nil { - return fmt.Errorf("set selection: %w", err) - } - - display.Roundtrip() - signalReady() - - for { - select { - case <-cancelled: - return nil - case <-pasted: - if pasteOnce { - return nil - } - default: - if err := ctx.Dispatch(); err != nil { - return nil - } - } - } -} - -func CopyText(text string) error { - return Copy([]byte(text), "text/plain;charset=utf-8") -} - -func Paste() ([]byte, string, error) { - display, err := wlclient.Connect("") - if err != nil { - return nil, "", fmt.Errorf("wayland connect: %w", err) - } - defer display.Destroy() - - ctx := display.Context() - registry, err := display.GetRegistry() - if err != nil { - return nil, "", fmt.Errorf("get registry: %w", err) - } - defer registry.Destroy() - - var dataControlMgr *ext_data_control.ExtDataControlManagerV1 - var seat *wlclient.Seat - var bindErr error - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case "ext_data_control_manager_v1": - dataControlMgr = ext_data_control.NewExtDataControlManagerV1(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil { - bindErr = err - } - case "wl_seat": - if seat != nil { - return - } - seat = wlclient.NewSeat(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil { - bindErr = err - } - } - }) - - display.Roundtrip() - display.Roundtrip() - - if bindErr != nil { - return nil, "", fmt.Errorf("registry bind: %w", bindErr) - } - - if dataControlMgr == nil { - return nil, "", fmt.Errorf("compositor does not support ext_data_control_manager_v1") - } - defer dataControlMgr.Destroy() - - if seat == nil { - return nil, "", fmt.Errorf("no seat available") - } - - device, err := dataControlMgr.GetDataDevice(seat) - if err != nil { - return nil, "", fmt.Errorf("get data device: %w", err) - } - defer device.Destroy() - - offerMimeTypes := make(map[*ext_data_control.ExtDataControlOfferV1][]string) - - device.SetDataOfferHandler(func(e ext_data_control.ExtDataControlDeviceV1DataOfferEvent) { - if e.Id == nil { - return - } - offerMimeTypes[e.Id] = nil - e.Id.SetOfferHandler(func(me ext_data_control.ExtDataControlOfferV1OfferEvent) { - offerMimeTypes[e.Id] = append(offerMimeTypes[e.Id], me.MimeType) - }) - }) - - var selectionOffer *ext_data_control.ExtDataControlOfferV1 - gotSelection := false - - device.SetSelectionHandler(func(e ext_data_control.ExtDataControlDeviceV1SelectionEvent) { - selectionOffer = e.Id - gotSelection = true - }) - - display.Roundtrip() - display.Roundtrip() - - if !gotSelection || selectionOffer == nil { - return nil, "", fmt.Errorf("no clipboard data") - } - - mimeTypes := offerMimeTypes[selectionOffer] - selectedMime := selectPreferredMimeType(mimeTypes) - if selectedMime == "" { - return nil, "", fmt.Errorf("no supported mime type") - } - - r, w, err := os.Pipe() - if err != nil { - return nil, "", fmt.Errorf("create pipe: %w", err) - } - defer r.Close() - - if err := selectionOffer.Receive(selectedMime, int(w.Fd())); err != nil { - w.Close() - return nil, "", fmt.Errorf("receive: %w", err) - } - w.Close() - - display.Roundtrip() - - data, err := io.ReadAll(r) - if err != nil { - return nil, "", fmt.Errorf("read: %w", err) - } - - return data, selectedMime, nil -} - -func PasteText() (string, error) { - data, _, err := Paste() - if err != nil { - return "", err - } - return string(data), nil -} - -func selectPreferredMimeType(mimes []string) string { - preferred := []string{ - "text/plain;charset=utf-8", - "text/plain", - "UTF8_STRING", - "STRING", - "TEXT", - "image/png", - "image/jpeg", - } - - for _, pref := range preferred { - for _, mime := range mimes { - if mime == pref { - return mime - } - } - } - - if len(mimes) > 0 { - return mimes[0] - } - return "" -} - -func IsImageMimeType(mime string) bool { - return len(mime) > 6 && mime[:6] == "image/" -} - -type Offer struct { - MimeType string - Data []byte -} - -func CopyMulti(offers []Offer, foreground, pasteOnce bool) error { - if !foreground { - return copyMultiFork(offers, pasteOnce) - } - return copyMultiServe(offers, pasteOnce) -} - -func copyMultiFork(offers []Offer, pasteOnce bool) error { - args := []string{os.Args[0], "cl", "copy", "--foreground", "--type", "__multi__"} - if pasteOnce { - args = append(args, "--paste-once") - } - - cmd := exec.Command(args[0], args[1:]...) - cmd.Stdin = nil - cmd.Stdout = nil - cmd.Stderr = nil - cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - - stdin, err := cmd.StdinPipe() - if err != nil { - return fmt.Errorf("stdin pipe: %w", err) - } - - if err := cmd.Start(); err != nil { - return fmt.Errorf("start: %w", err) - } - - for _, offer := range offers { - fmt.Fprintf(stdin, "%s\x00%d\x00", offer.MimeType, len(offer.Data)) - if _, err := stdin.Write(offer.Data); err != nil { - stdin.Close() - return fmt.Errorf("write offer data: %w", err) - } - } - stdin.Close() - - return nil -} - -func copyMultiServe(offers []Offer, pasteOnce bool) error { - display, err := wlclient.Connect("") - if err != nil { - return fmt.Errorf("wayland connect: %w", err) - } - defer display.Destroy() - - ctx := display.Context() - registry, err := display.GetRegistry() - if err != nil { - return fmt.Errorf("get registry: %w", err) - } - defer registry.Destroy() - - var dataControlMgr *ext_data_control.ExtDataControlManagerV1 - var seat *wlclient.Seat - var bindErr error - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case "ext_data_control_manager_v1": - dataControlMgr = ext_data_control.NewExtDataControlManagerV1(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil { - bindErr = err - } - case "wl_seat": - if seat != nil { - return - } - seat = wlclient.NewSeat(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil { - bindErr = err - } - } - }) - - display.Roundtrip() - display.Roundtrip() - - if bindErr != nil { - return fmt.Errorf("registry bind: %w", bindErr) - } - if dataControlMgr == nil { - return fmt.Errorf("compositor does not support ext_data_control_manager_v1") - } - defer dataControlMgr.Destroy() - if seat == nil { - return fmt.Errorf("no seat available") - } - - device, err := dataControlMgr.GetDataDevice(seat) - if err != nil { - return fmt.Errorf("get data device: %w", err) - } - defer device.Destroy() - - source, err := dataControlMgr.CreateDataSource() - if err != nil { - return fmt.Errorf("create data source: %w", err) - } - - offerMap := make(map[string][]byte) - for _, offer := range offers { - if err := source.Offer(offer.MimeType); err != nil { - return fmt.Errorf("offer %s: %w", offer.MimeType, err) - } - offerMap[offer.MimeType] = offer.Data - } - - cancelled := make(chan struct{}) - pasted := make(chan struct{}, 1) - - source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) { - _ = syscall.SetNonblock(e.Fd, false) - file := os.NewFile(uintptr(e.Fd), "pipe") - defer file.Close() - - if data, ok := offerMap[e.MimeType]; ok { - _, _ = file.Write(data) - } - - select { - case pasted <- struct{}{}: - default: - } - }) - - source.SetCancelledHandler(func(e ext_data_control.ExtDataControlSourceV1CancelledEvent) { - close(cancelled) - }) - - if err := device.SetSelection(source); err != nil { - return fmt.Errorf("set selection: %w", err) - } - - display.Roundtrip() - - for { - select { - case <-cancelled: - return nil - case <-pasted: - if pasteOnce { - return nil - } - default: - if err := ctx.Dispatch(); err != nil { - return nil - } - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/store.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/store.go deleted file mode 100644 index 17ad7a9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/store.go +++ /dev/null @@ -1,248 +0,0 @@ -package clipboard - -import ( - "bytes" - "encoding/binary" - "fmt" - "image" - _ "image/gif" - _ "image/jpeg" - _ "image/png" - "os" - "path/filepath" - "strings" - "time" - - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/tiff" - "hash/fnv" - - bolt "go.etcd.io/bbolt" -) - -type StoreConfig struct { - MaxHistory int - MaxEntrySize int64 -} - -func DefaultStoreConfig() StoreConfig { - return StoreConfig{ - MaxHistory: 100, - MaxEntrySize: 5 * 1024 * 1024, - } -} - -type Entry struct { - ID uint64 - Data []byte - MimeType string - Preview string - Size int - Timestamp time.Time - IsImage bool - Hash uint64 -} - -func Store(data []byte, mimeType string) error { - return StoreWithConfig(data, mimeType, DefaultStoreConfig()) -} - -func StoreWithConfig(data []byte, mimeType string, cfg StoreConfig) error { - if len(data) == 0 { - return nil - } - if int64(len(data)) > cfg.MaxEntrySize { - return fmt.Errorf("data too large: %d > %d", len(data), cfg.MaxEntrySize) - } - - dbPath, err := GetDBPath() - if err != nil { - return fmt.Errorf("get db path: %w", err) - } - - db, err := bolt.Open(dbPath, 0o644, &bolt.Options{Timeout: 1 * time.Second}) - if err != nil { - return fmt.Errorf("open db: %w", err) - } - defer db.Close() - - entry := Entry{ - Data: data, - MimeType: mimeType, - Size: len(data), - Timestamp: time.Now(), - IsImage: IsImageMimeType(mimeType), - Hash: computeHash(data), - } - - switch { - case entry.IsImage: - entry.Preview = imagePreview(data, mimeType) - default: - entry.Preview = textPreview(data) - } - - return db.Update(func(tx *bolt.Tx) error { - b, err := tx.CreateBucketIfNotExists([]byte("clipboard")) - if err != nil { - return err - } - - if err := deduplicateInTx(b, entry.Hash); err != nil { - return err - } - - id, err := b.NextSequence() - if err != nil { - return err - } - entry.ID = id - - encoded, err := encodeEntry(entry) - if err != nil { - return err - } - - if err := b.Put(itob(id), encoded); err != nil { - return err - } - - return trimLengthInTx(b, cfg.MaxHistory) - }) -} - -func GetDBPath() (string, error) { - cacheDir, err := os.UserCacheDir() - if err != nil { - homeDir, err := os.UserHomeDir() - if err != nil { - return "", err - } - cacheDir = filepath.Join(homeDir, ".cache") - } - - newDir := filepath.Join(cacheDir, "DankMaterialShell", "clipboard") - newPath := filepath.Join(newDir, "db") - - if _, err := os.Stat(newPath); err == nil { - return newPath, nil - } - - oldDir := filepath.Join(cacheDir, "dms-clipboard") - oldPath := filepath.Join(oldDir, "db") - - if _, err := os.Stat(oldPath); err == nil { - if err := os.MkdirAll(newDir, 0o700); err != nil { - return "", err - } - if err := os.Rename(oldPath, newPath); err != nil { - return "", err - } - os.Remove(oldDir) - return newPath, nil - } - - if err := os.MkdirAll(newDir, 0o700); err != nil { - return "", err - } - return newPath, nil -} - -func deduplicateInTx(b *bolt.Bucket, hash uint64) error { - c := b.Cursor() - for k, v := c.Last(); k != nil; k, v = c.Prev() { - if extractHash(v) != hash { - continue - } - if err := b.Delete(k); err != nil { - return err - } - } - return nil -} - -func trimLengthInTx(b *bolt.Bucket, maxHistory int) error { - c := b.Cursor() - var count int - for k, _ := c.Last(); k != nil; k, _ = c.Prev() { - if count < maxHistory { - count++ - continue - } - if err := b.Delete(k); err != nil { - return err - } - } - return nil -} - -func encodeEntry(e Entry) ([]byte, error) { - buf := new(bytes.Buffer) - - binary.Write(buf, binary.BigEndian, e.ID) - binary.Write(buf, binary.BigEndian, uint32(len(e.Data))) - buf.Write(e.Data) - binary.Write(buf, binary.BigEndian, uint32(len(e.MimeType))) - buf.WriteString(e.MimeType) - binary.Write(buf, binary.BigEndian, uint32(len(e.Preview))) - buf.WriteString(e.Preview) - binary.Write(buf, binary.BigEndian, int32(e.Size)) - binary.Write(buf, binary.BigEndian, e.Timestamp.Unix()) - if e.IsImage { - buf.WriteByte(1) - } else { - buf.WriteByte(0) - } - binary.Write(buf, binary.BigEndian, e.Hash) - - return buf.Bytes(), nil -} - -func itob(v uint64) []byte { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, v) - return b -} - -func computeHash(data []byte) uint64 { - h := fnv.New64a() - h.Write(data) - return h.Sum64() -} - -func extractHash(data []byte) uint64 { - if len(data) < 8 { - return 0 - } - return binary.BigEndian.Uint64(data[len(data)-8:]) -} - -func textPreview(data []byte) string { - text := string(data) - text = strings.TrimSpace(text) - text = strings.Join(strings.Fields(text), " ") - - if len(text) > 100 { - return text[:100] + "…" - } - return text -} - -func imagePreview(data []byte, format string) string { - config, imgFmt, err := image.DecodeConfig(bytes.NewReader(data)) - if err != nil { - return fmt.Sprintf("[[ image %s %s ]]", sizeStr(len(data)), format) - } - return fmt.Sprintf("[[ image %s %s %dx%d ]]", sizeStr(len(data)), imgFmt, config.Width, config.Height) -} - -func sizeStr(size int) string { - units := []string{"B", "KiB", "MiB"} - var i int - fsize := float64(size) - for fsize >= 1024 && i < len(units)-1 { - fsize /= 1024 - i++ - } - return fmt.Sprintf("%.0f %s", fsize, units[i]) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/watch.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/watch.go deleted file mode 100644 index 327c5c5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/watch.go +++ /dev/null @@ -1,303 +0,0 @@ -package clipboard - -import ( - "context" - "errors" - "fmt" - "io" - "os" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_data_control" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type ClipboardChange struct { - Data []byte - MimeType string - MimeTypes []string -} - -func Watch(ctx context.Context, callback func(data []byte, mimeType string)) error { - display, err := wlclient.Connect("") - if err != nil { - return fmt.Errorf("wayland connect: %w", err) - } - defer display.Destroy() - - wlCtx := display.Context() - registry, err := display.GetRegistry() - if err != nil { - return fmt.Errorf("get registry: %w", err) - } - defer registry.Destroy() - - var dataControlMgr *ext_data_control.ExtDataControlManagerV1 - var seat *wlclient.Seat - var bindErr error - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case "ext_data_control_manager_v1": - dataControlMgr = ext_data_control.NewExtDataControlManagerV1(wlCtx) - if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil { - bindErr = err - } - case "wl_seat": - if seat != nil { - return - } - seat = wlclient.NewSeat(wlCtx) - if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil { - bindErr = err - } - } - }) - - display.Roundtrip() - display.Roundtrip() - - if bindErr != nil { - return fmt.Errorf("registry bind: %w", bindErr) - } - - if dataControlMgr == nil { - return fmt.Errorf("compositor does not support ext_data_control_manager_v1") - } - defer dataControlMgr.Destroy() - - if seat == nil { - return fmt.Errorf("no seat available") - } - - device, err := dataControlMgr.GetDataDevice(seat) - if err != nil { - return fmt.Errorf("get data device: %w", err) - } - defer device.Destroy() - - offerMimeTypes := make(map[*ext_data_control.ExtDataControlOfferV1][]string) - - device.SetDataOfferHandler(func(e ext_data_control.ExtDataControlDeviceV1DataOfferEvent) { - if e.Id == nil { - return - } - offerMimeTypes[e.Id] = nil - e.Id.SetOfferHandler(func(me ext_data_control.ExtDataControlOfferV1OfferEvent) { - offerMimeTypes[e.Id] = append(offerMimeTypes[e.Id], me.MimeType) - }) - }) - - device.SetSelectionHandler(func(e ext_data_control.ExtDataControlDeviceV1SelectionEvent) { - if e.Id == nil { - return - } - - mimes := offerMimeTypes[e.Id] - selectedMime := selectPreferredMimeType(mimes) - if selectedMime == "" { - return - } - - r, w, err := os.Pipe() - if err != nil { - return - } - - if err := e.Id.Receive(selectedMime, int(w.Fd())); err != nil { - w.Close() - r.Close() - return - } - w.Close() - - go func() { - defer r.Close() - data, err := io.ReadAll(r) - if err != nil || len(data) == 0 { - return - } - callback(data, selectedMime) - }() - }) - - display.Roundtrip() - display.Roundtrip() - - for { - select { - case <-ctx.Done(): - return ctx.Err() - default: - if err := wlCtx.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { - return fmt.Errorf("set read deadline: %w", err) - } - if err := wlCtx.Dispatch(); err != nil { - if isTimeoutError(err) { - continue - } - return fmt.Errorf("dispatch: %w", err) - } - } - } -} - -func WatchAll(ctx context.Context, callback func(data []byte, mimeType string, allMimeTypes []string)) error { - display, err := wlclient.Connect("") - if err != nil { - return fmt.Errorf("wayland connect: %w", err) - } - defer display.Destroy() - - wlCtx := display.Context() - registry, err := display.GetRegistry() - if err != nil { - return fmt.Errorf("get registry: %w", err) - } - defer registry.Destroy() - - var dataControlMgr *ext_data_control.ExtDataControlManagerV1 - var seat *wlclient.Seat - var bindErr error - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case "ext_data_control_manager_v1": - dataControlMgr = ext_data_control.NewExtDataControlManagerV1(wlCtx) - if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil { - bindErr = err - } - case "wl_seat": - if seat != nil { - return - } - seat = wlclient.NewSeat(wlCtx) - if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil { - bindErr = err - } - } - }) - - display.Roundtrip() - display.Roundtrip() - - if bindErr != nil { - return fmt.Errorf("registry bind: %w", bindErr) - } - if dataControlMgr == nil { - return fmt.Errorf("compositor does not support ext_data_control_manager_v1") - } - defer dataControlMgr.Destroy() - if seat == nil { - return fmt.Errorf("no seat available") - } - - device, err := dataControlMgr.GetDataDevice(seat) - if err != nil { - return fmt.Errorf("get data device: %w", err) - } - defer device.Destroy() - - offerMimeTypes := make(map[*ext_data_control.ExtDataControlOfferV1][]string) - - device.SetDataOfferHandler(func(e ext_data_control.ExtDataControlDeviceV1DataOfferEvent) { - if e.Id == nil { - return - } - offerMimeTypes[e.Id] = nil - e.Id.SetOfferHandler(func(me ext_data_control.ExtDataControlOfferV1OfferEvent) { - offerMimeTypes[e.Id] = append(offerMimeTypes[e.Id], me.MimeType) - }) - }) - - device.SetSelectionHandler(func(e ext_data_control.ExtDataControlDeviceV1SelectionEvent) { - if e.Id == nil { - return - } - - mimes := offerMimeTypes[e.Id] - selectedMime := selectPreferredMimeType(mimes) - if selectedMime == "" { - return - } - - mimesCopy := make([]string, len(mimes)) - copy(mimesCopy, mimes) - - r, w, err := os.Pipe() - if err != nil { - return - } - - if err := e.Id.Receive(selectedMime, int(w.Fd())); err != nil { - w.Close() - r.Close() - return - } - w.Close() - - go func() { - defer r.Close() - data, err := io.ReadAll(r) - if err != nil || len(data) == 0 { - return - } - callback(data, selectedMime, mimesCopy) - }() - }) - - display.Roundtrip() - display.Roundtrip() - - for { - select { - case <-ctx.Done(): - return ctx.Err() - default: - if err := wlCtx.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { - return fmt.Errorf("set read deadline: %w", err) - } - if err := wlCtx.Dispatch(); err != nil { - if isTimeoutError(err) { - continue - } - return fmt.Errorf("dispatch: %w", err) - } - } - } -} - -func isTimeoutError(err error) bool { - if err == nil { - return false - } - if errors.Is(err, os.ErrDeadlineExceeded) { - return true - } - if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() { - return true - } - return false -} - -func WatchChan(ctx context.Context) (<-chan ClipboardChange, <-chan error) { - ch := make(chan ClipboardChange, 16) - errCh := make(chan error, 1) - - go func() { - defer close(ch) - err := Watch(ctx, func(data []byte, mimeType string) { - select { - case ch <- ClipboardChange{Data: data, MimeType: mimeType}: - default: - } - }) - if err != nil && err != context.Canceled { - errCh <- err - } - close(errCh) - }() - - time.Sleep(50 * time.Millisecond) - return ch, errCh -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/color.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/color.go deleted file mode 100644 index 461730a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/color.go +++ /dev/null @@ -1,306 +0,0 @@ -package colorpicker - -import ( - "encoding/json" - "fmt" - "math" - "strings" -) - -type Color struct { - R, G, B, A uint8 -} - -type OutputFormat int - -const ( - FormatHex OutputFormat = iota - FormatRGB - FormatHSL - FormatHSV - FormatCMYK -) - -func ParseFormat(s string) OutputFormat { - switch strings.ToLower(s) { - case "rgb": - return FormatRGB - case "hsl": - return FormatHSL - case "hsv": - return FormatHSV - case "cmyk": - return FormatCMYK - default: - return FormatHex - } -} - -func (c Color) ToHex(lowercase bool) string { - if lowercase { - return fmt.Sprintf("#%02x%02x%02x", c.R, c.G, c.B) - } - return fmt.Sprintf("#%02X%02X%02X", c.R, c.G, c.B) -} - -func (c Color) ToRGB() string { - return fmt.Sprintf("%d %d %d", c.R, c.G, c.B) -} - -func (c Color) ToHSL() string { - h, s, l := rgbToHSL(c.R, c.G, c.B) - return fmt.Sprintf("%d %d%% %d%%", h, s, l) -} - -func (c Color) ToHSV() string { - h, s, v := rgbToHSV(c.R, c.G, c.B) - return fmt.Sprintf("%d %d%% %d%%", h, s, v) -} - -func (c Color) ToCMYK() string { - cy, m, y, k := rgbToCMYK(c.R, c.G, c.B) - return fmt.Sprintf("%d%% %d%% %d%% %d%%", cy, m, y, k) -} - -func (c Color) Format(format OutputFormat, lowercase bool, customFmt string) string { - if customFmt != "" { - return c.formatCustom(format, customFmt) - } - - switch format { - case FormatRGB: - return c.ToRGB() - case FormatHSL: - return c.ToHSL() - case FormatHSV: - return c.ToHSV() - case FormatCMYK: - return c.ToCMYK() - default: - return c.ToHex(lowercase) - } -} - -func (c Color) formatCustom(format OutputFormat, customFmt string) string { - switch format { - case FormatRGB: - return replaceArgs(customFmt, c.R, c.G, c.B) - case FormatHSL: - h, s, l := rgbToHSL(c.R, c.G, c.B) - return replaceArgs(customFmt, h, s, l) - case FormatHSV: - h, s, v := rgbToHSV(c.R, c.G, c.B) - return replaceArgs(customFmt, h, s, v) - case FormatCMYK: - cy, m, y, k := rgbToCMYK(c.R, c.G, c.B) - return replaceArgs4(customFmt, cy, m, y, k) - default: - if strings.Contains(customFmt, "{0}") { - r := fmt.Sprintf("%02X", c.R) - g := fmt.Sprintf("%02X", c.G) - b := fmt.Sprintf("%02X", c.B) - return replaceArgsStr(customFmt, r, g, b) - } - return c.ToHex(false) - } -} - -func replaceArgs[T any](format string, a, b, c T) string { - result := format - result = strings.ReplaceAll(result, "{0}", fmt.Sprintf("%v", a)) - result = strings.ReplaceAll(result, "{1}", fmt.Sprintf("%v", b)) - result = strings.ReplaceAll(result, "{2}", fmt.Sprintf("%v", c)) - return result -} - -func replaceArgs4[T any](format string, a, b, c, d T) string { - result := format - result = strings.ReplaceAll(result, "{0}", fmt.Sprintf("%v", a)) - result = strings.ReplaceAll(result, "{1}", fmt.Sprintf("%v", b)) - result = strings.ReplaceAll(result, "{2}", fmt.Sprintf("%v", c)) - result = strings.ReplaceAll(result, "{3}", fmt.Sprintf("%v", d)) - return result -} - -func replaceArgsStr(format, a, b, c string) string { - result := format - result = strings.ReplaceAll(result, "{0}", a) - result = strings.ReplaceAll(result, "{1}", b) - result = strings.ReplaceAll(result, "{2}", c) - return result -} - -func rgbToHSL(r, g, b uint8) (int, int, int) { - rf := float64(r) / 255.0 - gf := float64(g) / 255.0 - bf := float64(b) / 255.0 - - maxVal := math.Max(rf, math.Max(gf, bf)) - minVal := math.Min(rf, math.Min(gf, bf)) - l := (maxVal + minVal) / 2 - - if maxVal == minVal { - return 0, 0, int(math.Round(l * 100)) - } - - d := maxVal - minVal - var s float64 - if l > 0.5 { - s = d / (2 - maxVal - minVal) - } else { - s = d / (maxVal + minVal) - } - - var h float64 - switch maxVal { - case rf: - h = (gf - bf) / d - if gf < bf { - h += 6 - } - case gf: - h = (bf-rf)/d + 2 - case bf: - h = (rf-gf)/d + 4 - } - h /= 6 - - return int(math.Round(h * 360)), int(math.Round(s * 100)), int(math.Round(l * 100)) -} - -func rgbToHSV(r, g, b uint8) (int, int, int) { - rf := float64(r) / 255.0 - gf := float64(g) / 255.0 - bf := float64(b) / 255.0 - - maxVal := math.Max(rf, math.Max(gf, bf)) - minVal := math.Min(rf, math.Min(gf, bf)) - v := maxVal - d := maxVal - minVal - - var s float64 - if maxVal != 0 { - s = d / maxVal - } - - if maxVal == minVal { - return 0, int(math.Round(s * 100)), int(math.Round(v * 100)) - } - - var h float64 - switch maxVal { - case rf: - h = (gf - bf) / d - if gf < bf { - h += 6 - } - case gf: - h = (bf-rf)/d + 2 - case bf: - h = (rf-gf)/d + 4 - } - h /= 6 - - return int(math.Round(h * 360)), int(math.Round(s * 100)), int(math.Round(v * 100)) -} - -func rgbToCMYK(r, g, b uint8) (int, int, int, int) { - if r == 0 && g == 0 && b == 0 { - return 0, 0, 0, 100 - } - - rf := float64(r) / 255.0 - gf := float64(g) / 255.0 - bf := float64(b) / 255.0 - - k := 1 - math.Max(rf, math.Max(gf, bf)) - c := (1 - rf - k) / (1 - k) - m := (1 - gf - k) / (1 - k) - y := (1 - bf - k) / (1 - k) - - return int(math.Round(c * 100)), int(math.Round(m * 100)), int(math.Round(y * 100)), int(math.Round(k * 100)) -} - -func (c Color) Luminance() float64 { - r := float64(c.R) / 255.0 - g := float64(c.G) / 255.0 - b := float64(c.B) / 255.0 - - if r <= 0.03928 { - r = r / 12.92 - } else { - r = math.Pow((r+0.055)/1.055, 2.4) - } - - if g <= 0.03928 { - g = g / 12.92 - } else { - g = math.Pow((g+0.055)/1.055, 2.4) - } - - if b <= 0.03928 { - b = b / 12.92 - } else { - b = math.Pow((b+0.055)/1.055, 2.4) - } - - return 0.2126*r + 0.7152*g + 0.0722*b -} - -func (c Color) IsDark() bool { - return c.Luminance() < 0.179 -} - -type ColorJSON struct { - Hex string `json:"hex"` - RGB struct { - R int `json:"r"` - G int `json:"g"` - B int `json:"b"` - } `json:"rgb"` - HSL struct { - H int `json:"h"` - S int `json:"s"` - L int `json:"l"` - } `json:"hsl"` - HSV struct { - H int `json:"h"` - S int `json:"s"` - V int `json:"v"` - } `json:"hsv"` - CMYK struct { - C int `json:"c"` - M int `json:"m"` - Y int `json:"y"` - K int `json:"k"` - } `json:"cmyk"` -} - -func (c Color) ToJSON() (string, error) { - h, s, l := rgbToHSL(c.R, c.G, c.B) - hv, sv, v := rgbToHSV(c.R, c.G, c.B) - cy, m, y, k := rgbToCMYK(c.R, c.G, c.B) - - data := ColorJSON{ - Hex: c.ToHex(false), - } - data.RGB.R = int(c.R) - data.RGB.G = int(c.G) - data.RGB.B = int(c.B) - data.HSL.H = h - data.HSL.S = s - data.HSL.L = l - data.HSV.H = hv - data.HSV.S = sv - data.HSV.V = v - data.CMYK.C = cy - data.CMYK.M = m - data.CMYK.Y = y - data.CMYK.K = k - - bytes, err := json.MarshalIndent(data, "", " ") - if err != nil { - return "", err - } - return string(bytes), nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/picker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/picker.go deleted file mode 100644 index 688c381..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/picker.go +++ /dev/null @@ -1,744 +0,0 @@ -package colorpicker - -import ( - "fmt" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/keyboard_shortcuts_inhibit" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_layer_shell" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_screencopy" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wp_viewporter" - wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type Config struct { - Format OutputFormat - CustomFormat string - Lowercase bool - Autocopy bool - Notify bool -} - -type Output struct { - wlOutput *client.Output - name string - globalName uint32 - x, y int32 - width int32 - height int32 - scale int32 - fractionalScale float64 - transform int32 -} - -type LayerSurface struct { - output *Output - state *SurfaceState - wlSurface *client.Surface - layerSurf *wlr_layer_shell.ZwlrLayerSurfaceV1 - viewport *wp_viewporter.WpViewport - wlPools [2]*client.ShmPool - wlBuffers [2]*client.Buffer - slotBusy [2]bool - needsRedraw bool - scopyBuffer *client.Buffer - configured bool - hidden bool -} - -type Picker struct { - config Config - - display *client.Display - registry *client.Registry - ctx *client.Context - - compositor *client.Compositor - shm *client.Shm - seat *client.Seat - pointer *client.Pointer - keyboard *client.Keyboard - layerShell *wlr_layer_shell.ZwlrLayerShellV1 - screencopy *wlr_screencopy.ZwlrScreencopyManagerV1 - viewporter *wp_viewporter.WpViewporter - - shortcutsInhibitMgr *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1 - shortcutsInhibitor *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1 - - outputs map[uint32]*Output - outputsMu sync.Mutex - - surfaces []*LayerSurface - activeSurface *LayerSurface - - running bool - pickedColor *Color - err error -} - -func New(config Config) *Picker { - return &Picker{ - config: config, - outputs: make(map[uint32]*Output), - } -} - -func (p *Picker) Run() (*Color, error) { - if err := p.connect(); err != nil { - return nil, fmt.Errorf("wayland connect: %w", err) - } - defer p.cleanup() - - if err := p.setupRegistry(); err != nil { - return nil, fmt.Errorf("registry setup: %w", err) - } - - if err := p.roundtrip(); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - if p.screencopy == nil { - return nil, fmt.Errorf("compositor does not support wlr-screencopy-unstable-v1") - } - - if p.layerShell == nil { - return nil, fmt.Errorf("compositor does not support wlr-layer-shell-unstable-v1") - } - - if p.seat == nil { - return nil, fmt.Errorf("no seat available") - } - - if err := p.roundtrip(); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - // Extra roundtrip to ensure pointer/keyboard from seat capabilities are registered - if err := p.roundtrip(); err != nil { - return nil, fmt.Errorf("roundtrip after seat: %w", err) - } - - if err := p.createSurfaces(); err != nil { - return nil, fmt.Errorf("create surfaces: %w", err) - } - - if err := p.roundtrip(); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - p.running = true - for p.running { - if err := p.ctx.Dispatch(); err != nil { - p.err = err - break - } - - p.flushRedraws() - p.checkDone() - } - - if p.err != nil { - return nil, p.err - } - - return p.pickedColor, nil -} - -func (p *Picker) checkDone() { - for _, ls := range p.surfaces { - picked, cancelled := ls.state.IsDone() - switch { - case cancelled: - p.running = false - return - case picked: - color, ok := ls.state.PickColor() - if ok { - p.pickedColor = &color - } - p.running = false - return - } - } -} - -func (p *Picker) flushRedraws() { - for _, ls := range p.surfaces { - if !ls.needsRedraw { - continue - } - p.redrawSurface(ls) - } -} - -func (p *Picker) connect() error { - display, err := client.Connect("") - if err != nil { - return err - } - p.display = display - p.ctx = display.Context() - return nil -} - -func (p *Picker) roundtrip() error { - return wlhelpers.Roundtrip(p.display, p.ctx) -} - -func (p *Picker) setupRegistry() error { - registry, err := p.display.GetRegistry() - if err != nil { - return err - } - p.registry = registry - - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - p.handleGlobal(e) - }) - - registry.SetGlobalRemoveHandler(func(e client.RegistryGlobalRemoveEvent) { - p.outputsMu.Lock() - delete(p.outputs, e.Name) - p.outputsMu.Unlock() - }) - - return nil -} - -func (p *Picker) handleGlobal(e client.RegistryGlobalEvent) { - switch e.Interface { - case client.CompositorInterfaceName: - compositor := client.NewCompositor(p.ctx) - if err := p.registry.Bind(e.Name, e.Interface, e.Version, compositor); err == nil { - p.compositor = compositor - } - - case client.ShmInterfaceName: - shm := client.NewShm(p.ctx) - if err := p.registry.Bind(e.Name, e.Interface, e.Version, shm); err == nil { - p.shm = shm - } - - case client.SeatInterfaceName: - seat := client.NewSeat(p.ctx) - if err := p.registry.Bind(e.Name, e.Interface, e.Version, seat); err == nil { - p.seat = seat - p.setupInput() - } - - case client.OutputInterfaceName: - output := client.NewOutput(p.ctx) - version := min(e.Version, 4) - if err := p.registry.Bind(e.Name, e.Interface, version, output); err == nil { - p.outputsMu.Lock() - p.outputs[e.Name] = &Output{ - wlOutput: output, - globalName: e.Name, - scale: 1, - fractionalScale: 1.0, - } - p.outputsMu.Unlock() - p.setupOutputHandlers(e.Name, output) - } - - case wlr_layer_shell.ZwlrLayerShellV1InterfaceName: - layerShell := wlr_layer_shell.NewZwlrLayerShellV1(p.ctx) - version := min(e.Version, 4) - if err := p.registry.Bind(e.Name, e.Interface, version, layerShell); err == nil { - p.layerShell = layerShell - } - - case wlr_screencopy.ZwlrScreencopyManagerV1InterfaceName: - screencopy := wlr_screencopy.NewZwlrScreencopyManagerV1(p.ctx) - version := min(e.Version, 3) - if err := p.registry.Bind(e.Name, e.Interface, version, screencopy); err == nil { - p.screencopy = screencopy - } - - case wp_viewporter.WpViewporterInterfaceName: - viewporter := wp_viewporter.NewWpViewporter(p.ctx) - if err := p.registry.Bind(e.Name, e.Interface, e.Version, viewporter); err == nil { - p.viewporter = viewporter - } - - case keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1InterfaceName: - mgr := keyboard_shortcuts_inhibit.NewZwpKeyboardShortcutsInhibitManagerV1(p.ctx) - if err := p.registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil { - p.shortcutsInhibitMgr = mgr - } - } -} - -func (p *Picker) setupOutputHandlers(name uint32, output *client.Output) { - output.SetGeometryHandler(func(e client.OutputGeometryEvent) { - p.outputsMu.Lock() - if o, ok := p.outputs[name]; ok { - o.x = e.X - o.y = e.Y - o.transform = int32(e.Transform) - } - p.outputsMu.Unlock() - }) - - output.SetModeHandler(func(e client.OutputModeEvent) { - if e.Flags&uint32(client.OutputModeCurrent) == 0 { - return - } - p.outputsMu.Lock() - if o, ok := p.outputs[name]; ok { - o.width = e.Width - o.height = e.Height - } - p.outputsMu.Unlock() - }) - - output.SetScaleHandler(func(e client.OutputScaleEvent) { - p.outputsMu.Lock() - if o, ok := p.outputs[name]; ok { - o.scale = e.Factor - o.fractionalScale = float64(e.Factor) - } - p.outputsMu.Unlock() - }) - - output.SetNameHandler(func(e client.OutputNameEvent) { - p.outputsMu.Lock() - if o, ok := p.outputs[name]; ok { - o.name = e.Name - } - p.outputsMu.Unlock() - }) -} - -func (p *Picker) createSurfaces() error { - p.outputsMu.Lock() - outputs := make([]*Output, 0, len(p.outputs)) - for _, o := range p.outputs { - outputs = append(outputs, o) - } - p.outputsMu.Unlock() - - for _, output := range outputs { - ls, err := p.createLayerSurface(output) - if err != nil { - return fmt.Errorf("output %s: %w", output.name, err) - } - p.surfaces = append(p.surfaces, ls) - } - - return nil -} - -func (p *Picker) createLayerSurface(output *Output) (*LayerSurface, error) { - surface, err := p.compositor.CreateSurface() - if err != nil { - return nil, fmt.Errorf("create surface: %w", err) - } - - layerSurf, err := p.layerShell.GetLayerSurface( - surface, - output.wlOutput, - uint32(wlr_layer_shell.ZwlrLayerShellV1LayerOverlay), - "dms-colorpicker", - ) - if err != nil { - return nil, fmt.Errorf("get layer surface: %w", err) - } - - ls := &LayerSurface{ - output: output, - state: NewSurfaceState(p.config.Format, p.config.Lowercase), - wlSurface: surface, - layerSurf: layerSurf, - hidden: true, // Start hidden, will show overlay when pointer enters - } - - if p.viewporter != nil { - vp, err := p.viewporter.GetViewport(surface) - if err == nil { - ls.viewport = vp - } - } - - if err := layerSurf.SetAnchor( - uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorTop) | - uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorBottom) | - uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorLeft) | - uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorRight), - ); err != nil { - log.Warn("failed to set layer anchor", "err", err) - } - if err := layerSurf.SetExclusiveZone(-1); err != nil { - log.Warn("failed to set exclusive zone", "err", err) - } - if err := layerSurf.SetKeyboardInteractivity(uint32(wlr_layer_shell.ZwlrLayerSurfaceV1KeyboardInteractivityExclusive)); err != nil { - log.Warn("failed to set keyboard interactivity", "err", err) - } - - layerSurf.SetConfigureHandler(func(e wlr_layer_shell.ZwlrLayerSurfaceV1ConfigureEvent) { - if err := layerSurf.AckConfigure(e.Serial); err != nil { - log.Warn("failed to ack configure", "err", err) - } - if err := ls.state.OnLayerConfigure(int(e.Width), int(e.Height)); err != nil { - log.Warn("failed to handle layer configure", "err", err) - } - ls.configured = true - - scale := p.computeSurfaceScale(ls) - ls.state.SetScale(scale) - - if !ls.state.IsReady() { - p.captureForSurface(ls) - } else { - p.redrawSurface(ls) - } - - // Request shortcut inhibition once surface is configured - p.ensureShortcutsInhibitor(ls) - }) - - layerSurf.SetClosedHandler(func(e wlr_layer_shell.ZwlrLayerSurfaceV1ClosedEvent) { - p.running = false - }) - - if err := surface.Commit(); err != nil { - log.Warn("failed to commit surface", "err", err) - } - return ls, nil -} - -func (p *Picker) computeSurfaceScale(ls *LayerSurface) int32 { - out := ls.output - if out == nil || out.scale <= 0 { - return 1 - } - return out.scale -} - -func (p *Picker) ensureShortcutsInhibitor(ls *LayerSurface) { - if p.shortcutsInhibitMgr == nil || p.seat == nil || p.shortcutsInhibitor != nil { - return - } - - inhibitor, err := p.shortcutsInhibitMgr.InhibitShortcuts(ls.wlSurface, p.seat) - if err != nil { - log.Debug("failed to create shortcuts inhibitor", "err", err) - return - } - - p.shortcutsInhibitor = inhibitor - - inhibitor.SetActiveHandler(func(e keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1ActiveEvent) { - log.Debug("shortcuts inhibitor active") - }) - - inhibitor.SetInactiveHandler(func(e keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1InactiveEvent) { - log.Debug("shortcuts inhibitor deactivated by compositor") - }) -} - -func (p *Picker) captureForSurface(ls *LayerSurface) { - frame, err := p.screencopy.CaptureOutput(0, ls.output.wlOutput) - if err != nil { - return - } - - frame.SetBufferHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferEvent) { - if err := ls.state.OnScreencopyBuffer(PixelFormat(e.Format), int(e.Width), int(e.Height), int(e.Stride)); err != nil { - log.Error("failed to create screencopy buffer", "err", err) - } - }) - - frame.SetBufferDoneHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferDoneEvent) { - screenBuf := ls.state.ScreenBuffer() - if screenBuf == nil { - return - } - - pool, err := p.shm.CreatePool(screenBuf.Fd(), int32(screenBuf.Size())) - if err != nil { - return - } - - wlBuffer, err := pool.CreateBuffer(0, int32(screenBuf.Width), int32(screenBuf.Height), int32(screenBuf.Stride), uint32(ls.state.screenFormat)) - if err != nil { - pool.Destroy() - return - } - - if ls.scopyBuffer != nil { - ls.scopyBuffer.Destroy() - } - ls.scopyBuffer = wlBuffer - wlBuffer.SetReleaseHandler(func(e client.BufferReleaseEvent) {}) - - if err := frame.Copy(wlBuffer); err != nil { - log.Error("failed to copy frame", "err", err) - } - pool.Destroy() - }) - - frame.SetFlagsHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FlagsEvent) { - ls.state.OnScreencopyFlags(e.Flags) - }) - - frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) { - ls.state.OnScreencopyReady() - - screenBuf := ls.state.ScreenBuffer() - if screenBuf != nil && ls.output.transform != TransformNormal { - invTransform := InverseTransform(ls.output.transform) - transformed, err := screenBuf.ApplyTransform(invTransform) - if err != nil { - log.Error("apply transform failed", "err", err) - } else if transformed != screenBuf { - ls.state.ReplaceScreenBuffer(transformed) - } - } - - logicalW, _ := ls.state.LogicalSize() - screenBuf = ls.state.ScreenBuffer() - if logicalW > 0 && screenBuf != nil { - ls.output.fractionalScale = float64(screenBuf.Width) / float64(logicalW) - } - - scale := p.computeSurfaceScale(ls) - ls.state.SetScale(scale) - frame.Destroy() - p.redrawSurface(ls) - }) - - frame.SetFailedHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FailedEvent) { - frame.Destroy() - }) -} - -func (p *Picker) redrawSurface(ls *LayerSurface) { - slot := ls.state.FrontIndex() - if ls.slotBusy[slot] { - ls.needsRedraw = true - return - } - - var renderBuf *ShmBuffer - switch { - case ls.hidden: - renderBuf = ls.state.RedrawScreenOnly() - default: - renderBuf = ls.state.Redraw() - } - if renderBuf == nil { - return - } - - ls.needsRedraw = false - - if ls.wlPools[slot] == nil { - pool, err := p.shm.CreatePool(renderBuf.Fd(), int32(renderBuf.Size())) - if err != nil { - return - } - ls.wlPools[slot] = pool - - wlBuffer, err := pool.CreateBuffer(0, int32(renderBuf.Width), int32(renderBuf.Height), int32(renderBuf.Stride), uint32(ls.state.ScreenFormat())) - if err != nil { - return - } - ls.wlBuffers[slot] = wlBuffer - - s := slot - wlBuffer.SetReleaseHandler(func(e client.BufferReleaseEvent) { - ls.slotBusy[s] = false - }) - } - - ls.slotBusy[slot] = true - - logicalW, logicalH := ls.state.LogicalSize() - if logicalW == 0 || logicalH == 0 { - logicalW = int(ls.output.width) - logicalH = int(ls.output.height) - } - - if ls.viewport != nil { - _ = ls.wlSurface.SetBufferScale(1) - _ = ls.viewport.SetSource(0, 0, float64(renderBuf.Width), float64(renderBuf.Height)) - _ = ls.viewport.SetDestination(int32(logicalW), int32(logicalH)) - } else { - bufferScale := ls.output.scale - if bufferScale <= 0 { - bufferScale = 1 - } - _ = ls.wlSurface.SetBufferScale(bufferScale) - } - _ = ls.wlSurface.Attach(ls.wlBuffers[slot], 0, 0) - _ = ls.wlSurface.Damage(0, 0, int32(logicalW), int32(logicalH)) - _ = ls.wlSurface.Commit() - - ls.state.SwapBuffers() -} - -func (p *Picker) hideSurface(ls *LayerSurface) { - if ls == nil || ls.wlSurface == nil || ls.hidden { - return - } - ls.hidden = true - // Redraw without the crosshair overlay - p.redrawSurface(ls) -} - -func (p *Picker) setupInput() { - if p.seat == nil { - return - } - - p.seat.SetCapabilitiesHandler(func(e client.SeatCapabilitiesEvent) { - if e.Capabilities&uint32(client.SeatCapabilityPointer) != 0 && p.pointer == nil { - pointer, err := p.seat.GetPointer() - if err != nil { - return - } - p.pointer = pointer - p.setupPointerHandlers() - } - if e.Capabilities&uint32(client.SeatCapabilityKeyboard) != 0 && p.keyboard == nil { - keyboard, err := p.seat.GetKeyboard() - if err != nil { - return - } - p.keyboard = keyboard - p.setupKeyboardHandlers() - } - }) -} - -func (p *Picker) setupPointerHandlers() { - p.pointer.SetEnterHandler(func(e client.PointerEnterEvent) { - if err := p.pointer.SetCursor(e.Serial, nil, 0, 0); err != nil { - log.Debug("failed to hide cursor", "err", err) - } - - if e.Surface == nil { - return - } - - p.activeSurface = nil - surfaceID := e.Surface.ID() - for _, ls := range p.surfaces { - if ls.wlSurface.ID() == surfaceID { - p.activeSurface = ls - break - } - } - if p.activeSurface == nil { - return - } - - if p.activeSurface.hidden { - p.activeSurface.hidden = false - } - - p.activeSurface.state.OnPointerMotion(e.SurfaceX, e.SurfaceY) - p.activeSurface.needsRedraw = true - }) - - p.pointer.SetLeaveHandler(func(e client.PointerLeaveEvent) { - if e.Surface == nil { - return - } - surfaceID := e.Surface.ID() - for _, ls := range p.surfaces { - if ls.wlSurface.ID() == surfaceID { - p.hideSurface(ls) - break - } - } - }) - - p.pointer.SetMotionHandler(func(e client.PointerMotionEvent) { - if p.activeSurface == nil { - return - } - p.activeSurface.state.OnPointerMotion(e.SurfaceX, e.SurfaceY) - p.activeSurface.needsRedraw = true - }) - - p.pointer.SetButtonHandler(func(e client.PointerButtonEvent) { - if p.activeSurface == nil { - return - } - p.activeSurface.state.OnPointerButton(e.Button, e.State) - }) -} - -func (p *Picker) setupKeyboardHandlers() { - p.keyboard.SetKeyHandler(func(e client.KeyboardKeyEvent) { - for _, ls := range p.surfaces { - ls.state.OnKey(e.Key, e.State) - } - }) -} - -func (p *Picker) cleanup() { - for _, ls := range p.surfaces { - if ls.scopyBuffer != nil { - ls.scopyBuffer.Destroy() - } - for i := range ls.wlBuffers { - if ls.wlBuffers[i] != nil { - ls.wlBuffers[i].Destroy() - } - if ls.wlPools[i] != nil { - ls.wlPools[i].Destroy() - } - } - if ls.viewport != nil { - ls.viewport.Destroy() - } - if ls.layerSurf != nil { - ls.layerSurf.Destroy() - } - if ls.wlSurface != nil { - ls.wlSurface.Destroy() - } - if ls.state != nil { - ls.state.Destroy() - } - } - - if p.shortcutsInhibitor != nil { - if err := p.shortcutsInhibitor.Destroy(); err != nil { - log.Debug("failed to destroy shortcuts inhibitor", "err", err) - } - p.shortcutsInhibitor = nil - } - - if p.shortcutsInhibitMgr != nil { - if err := p.shortcutsInhibitMgr.Destroy(); err != nil { - log.Debug("failed to destroy shortcuts inhibit manager", "err", err) - } - p.shortcutsInhibitMgr = nil - } - - if p.viewporter != nil { - p.viewporter.Destroy() - } - - if p.screencopy != nil { - p.screencopy.Destroy() - } - - if p.pointer != nil { - p.pointer.Release() - } - - if p.keyboard != nil { - p.keyboard.Release() - } - - if p.display != nil { - p.ctx.Close() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/shm.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/shm.go deleted file mode 100644 index 6ee9dc0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/shm.go +++ /dev/null @@ -1,55 +0,0 @@ -package colorpicker - -import "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/shm" - -type ShmBuffer = shm.Buffer - -const ( - TransformNormal = shm.TransformNormal - Transform90 = shm.Transform90 - Transform180 = shm.Transform180 - Transform270 = shm.Transform270 - TransformFlipped = shm.TransformFlipped - TransformFlipped90 = shm.TransformFlipped90 - TransformFlipped180 = shm.TransformFlipped180 - TransformFlipped270 = shm.TransformFlipped270 -) - -func CreateShmBuffer(width, height, stride int) (*ShmBuffer, error) { - return shm.CreateBuffer(width, height, stride) -} - -func InverseTransform(transform int32) int32 { - return shm.InverseTransform(transform) -} - -func GetPixelColor(buf *ShmBuffer, x, y int) Color { - return GetPixelColorWithFormat(buf, x, y, FormatARGB8888) -} - -func GetPixelColorWithFormat(buf *ShmBuffer, x, y int, format PixelFormat) Color { - if x < 0 || x >= buf.Width || y < 0 || y >= buf.Height { - return Color{} - } - - data := buf.Data() - offset := y*buf.Stride + x*4 - if offset+3 >= len(data) { - return Color{} - } - - if format == FormatABGR8888 || format == FormatXBGR8888 { - return Color{ - R: data[offset], - G: data[offset+1], - B: data[offset+2], - A: data[offset+3], - } - } - return Color{ - B: data[offset], - G: data[offset+1], - R: data[offset+2], - A: data[offset+3], - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/state.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/state.go deleted file mode 100644 index f518a5b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/state.go +++ /dev/null @@ -1,1195 +0,0 @@ -package colorpicker - -import ( - "fmt" - "math" - "strings" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/shm" -) - -type PixelFormat = shm.PixelFormat - -const ( - FormatARGB8888 = shm.FormatARGB8888 - FormatXRGB8888 = shm.FormatXRGB8888 - FormatABGR8888 = shm.FormatABGR8888 - FormatXBGR8888 = shm.FormatXBGR8888 - FormatRGB888 = shm.FormatRGB888 - FormatBGR888 = shm.FormatBGR888 -) - -type SurfaceState struct { - mu sync.Mutex - - screenBuf *ShmBuffer - screenFormat PixelFormat - yInverted bool - - logicalW int - logicalH int - - renderBufs [2]*ShmBuffer - front int - - scale int32 - scaleX float64 - scaleY float64 - - pointerX int - pointerY int - - displayFormat OutputFormat - lowercase bool - - readyForDisplay bool - colorPicked bool - cancelled bool -} - -func NewSurfaceState(format OutputFormat, lowercase bool) *SurfaceState { - return &SurfaceState{ - scale: 1, - displayFormat: format, - lowercase: lowercase, - } -} - -func (s *SurfaceState) SetScale(scale int32) { - s.mu.Lock() - defer s.mu.Unlock() - - if scale <= 0 { - scale = 1 - } - s.scale = scale -} - -func (s *SurfaceState) Scale() int32 { - s.mu.Lock() - defer s.mu.Unlock() - return s.scale -} - -func (s *SurfaceState) LogicalSize() (int, int) { - s.mu.Lock() - defer s.mu.Unlock() - return s.logicalW, s.logicalH -} - -func (s *SurfaceState) OnScreencopyBuffer(format PixelFormat, width, height, stride int) error { - s.mu.Lock() - defer s.mu.Unlock() - - bpp := format.BytesPerPixel() - if stride < width*bpp { - return fmt.Errorf("invalid stride %d for width %d (bpp=%d)", stride, width, bpp) - } - - if s.screenBuf != nil { - s.screenBuf.Close() - s.screenBuf = nil - } - - buf, err := CreateShmBuffer(width, height, stride) - if err != nil { - return err - } - - s.screenBuf = buf - s.screenBuf.Format = format - s.screenFormat = format - return nil -} - -func (s *SurfaceState) ScreenBuffer() *ShmBuffer { - s.mu.Lock() - defer s.mu.Unlock() - return s.screenBuf -} - -func (s *SurfaceState) ScreenFormat() PixelFormat { - s.mu.Lock() - defer s.mu.Unlock() - return s.screenFormat -} - -func (s *SurfaceState) ReplaceScreenBuffer(newBuf *ShmBuffer) { - s.mu.Lock() - defer s.mu.Unlock() - - if s.screenBuf != nil { - s.screenBuf.Close() - } - s.screenBuf = newBuf - s.screenFormat = newBuf.Format - - s.recomputeScale() - s.ensureRenderBuffers() -} - -func (s *SurfaceState) OnScreencopyFlags(flags uint32) { - s.mu.Lock() - s.yInverted = (flags & 1) != 0 - s.mu.Unlock() -} - -func (s *SurfaceState) OnScreencopyReady() { - s.mu.Lock() - defer s.mu.Unlock() - - if s.screenBuf == nil || s.logicalW == 0 || s.logicalH == 0 { - return - } - - if s.screenFormat.Is24Bit() { - converted, newFormat, err := s.screenBuf.ConvertTo32Bit(s.screenFormat) - if err == nil && converted != s.screenBuf { - s.screenBuf.Close() - s.screenBuf = converted - s.screenFormat = newFormat - } - } - - s.recomputeScale() - s.ensureRenderBuffers() - s.readyForDisplay = true -} - -func (s *SurfaceState) OnLayerConfigure(width, height int) error { - s.mu.Lock() - defer s.mu.Unlock() - - if width <= 0 || height <= 0 { - return nil - } - - if s.logicalW == width && s.logicalH == height { - return nil - } - - s.logicalW = width - s.logicalH = height - - s.recomputeScale() - s.ensureRenderBuffers() - - return nil -} - -func (s *SurfaceState) recomputeScale() { - if s.screenBuf == nil || s.logicalW == 0 || s.logicalH == 0 { - s.scaleX = 1 - s.scaleY = 1 - return - } - s.scaleX = float64(s.screenBuf.Width) / float64(s.logicalW) - s.scaleY = float64(s.screenBuf.Height) / float64(s.logicalH) -} - -func (s *SurfaceState) ensureRenderBuffers() { - if s.screenBuf == nil { - return - } - - width := s.screenBuf.Width - height := s.screenBuf.Height - stride := s.screenBuf.Stride - - for i := range s.renderBufs { - buf := s.renderBufs[i] - if buf != nil { - if buf.Width == width && buf.Height == height && buf.Stride == stride { - continue - } - buf.Close() - s.renderBufs[i] = nil - } - - newBuf, err := CreateShmBuffer(width, height, stride) - if err != nil { - continue - } - s.renderBufs[i] = newBuf - } -} - -func (s *SurfaceState) OnPointerMotion(x, y float64) { - s.mu.Lock() - s.pointerX = int(x) - s.pointerY = int(y) - s.mu.Unlock() -} - -func (s *SurfaceState) OnPointerButton(button, state uint32) { - if state != 1 { - return - } - - s.mu.Lock() - defer s.mu.Unlock() - - switch button { - case 0x110: // BTN_LEFT - if s.readyForDisplay && s.screenBuf != nil { - s.colorPicked = true - } - } -} - -func (s *SurfaceState) OnKey(key, state uint32) { - if state != 1 { - return - } - - s.mu.Lock() - defer s.mu.Unlock() - - switch key { - case 1: // KEY_ESC - s.cancelled = true - case 28: // KEY_ENTER - if s.readyForDisplay && s.screenBuf != nil { - s.colorPicked = true - } - } -} - -func (s *SurfaceState) IsDone() (picked, cancelled bool) { - s.mu.Lock() - defer s.mu.Unlock() - return s.colorPicked, s.cancelled -} - -func (s *SurfaceState) IsReady() bool { - s.mu.Lock() - defer s.mu.Unlock() - return s.readyForDisplay -} - -func (s *SurfaceState) FrontRenderBuffer() *ShmBuffer { - s.mu.Lock() - defer s.mu.Unlock() - return s.renderBufs[s.front] -} - -func (s *SurfaceState) FrontIndex() int { - s.mu.Lock() - defer s.mu.Unlock() - return s.front -} - -func (s *SurfaceState) SwapBuffers() { - s.mu.Lock() - s.front ^= 1 - s.mu.Unlock() -} - -func (s *SurfaceState) Redraw() *ShmBuffer { - s.mu.Lock() - defer s.mu.Unlock() - - if !s.readyForDisplay || s.screenBuf == nil { - return nil - } - - dst := s.renderBufs[s.front] - if dst == nil { - return nil - } - - dst.CopyFrom(s.screenBuf) - - px := int(math.Round(float64(s.pointerX) * s.scaleX)) - py := int(math.Round(float64(s.pointerY) * s.scaleY)) - - px = clamp(px, 0, dst.Width-1) - py = clamp(py, 0, dst.Height-1) - - sampleY := py - if s.yInverted { - sampleY = s.screenBuf.Height - 1 - py - } - - picked := GetPixelColorWithFormat(s.screenBuf, px, sampleY, s.screenFormat) - - drawMagnifierWithInversion( - dst.Data(), dst.Stride, dst.Width, dst.Height, - s.screenBuf.Data(), s.screenBuf.Stride, s.screenBuf.Width, s.screenBuf.Height, - px, py, picked, s.yInverted, s.screenFormat, - ) - - drawColorPreview(dst.Data(), dst.Stride, dst.Width, dst.Height, px, py, picked, s.displayFormat, s.lowercase, s.screenFormat) - - return dst -} - -// RedrawScreenOnly renders just the screenshot without any overlay (magnifier, preview). -// Used for when pointer leaves the surface. -func (s *SurfaceState) RedrawScreenOnly() *ShmBuffer { - s.mu.Lock() - defer s.mu.Unlock() - - if !s.readyForDisplay || s.screenBuf == nil { - return nil - } - - dst := s.renderBufs[s.front] - if dst == nil { - return nil - } - - dst.CopyFrom(s.screenBuf) - return dst -} - -func (s *SurfaceState) PickColor() (Color, bool) { - s.mu.Lock() - defer s.mu.Unlock() - - if s.screenBuf == nil { - return Color{}, false - } - - sx := int(math.Round(float64(s.pointerX) * s.scaleX)) - sy := int(math.Round(float64(s.pointerY) * s.scaleY)) - - sx = clamp(sx, 0, s.screenBuf.Width-1) - sy = clamp(sy, 0, s.screenBuf.Height-1) - - if s.yInverted { - sy = s.screenBuf.Height - 1 - sy - } - - return GetPixelColorWithFormat(s.screenBuf, sx, sy, s.screenFormat), true -} - -func (s *SurfaceState) Destroy() { - s.mu.Lock() - defer s.mu.Unlock() - - if s.screenBuf != nil { - s.screenBuf.Close() - s.screenBuf = nil - } - - for i := range s.renderBufs { - if s.renderBufs[i] != nil { - s.renderBufs[i].Close() - s.renderBufs[i] = nil - } - } -} - -func clamp(v, lo, hi int) int { - switch { - case v < lo: - return lo - case v > hi: - return hi - default: - return v - } -} - -func clampF(v, lo, hi float64) float64 { - switch { - case v < lo: - return lo - case v > hi: - return hi - default: - return v - } -} - -func abs(v int) int { - if v < 0 { - return -v - } - return v -} - -func blendColors(bg, fg Color, alpha float64) Color { - alpha = clampF(alpha, 0, 1) - invAlpha := 1.0 - alpha - return Color{ - R: uint8(clampF(float64(bg.R)*invAlpha+float64(fg.R)*alpha, 0, 255)), - G: uint8(clampF(float64(bg.G)*invAlpha+float64(fg.G)*alpha, 0, 255)), - B: uint8(clampF(float64(bg.B)*invAlpha+float64(fg.B)*alpha, 0, 255)), - A: 255, - } -} - -func drawMagnifierWithInversion( - dst []byte, dstStride, dstW, dstH int, - src []byte, srcStride, srcW, srcH int, - cx, cy int, - borderColor Color, - yInverted bool, - format PixelFormat, -) { - if dstW <= 0 || dstH <= 0 || srcW <= 0 || srcH <= 0 { - return - } - - const ( - outerRadius = 80 - borderThickness = 4 - aaWidth = 1.5 - zoom = 8.0 - crossThickness = 2 - crossInnerRadius = 8 - ) - - innerRadius := float64(outerRadius - borderThickness) - outerRadiusF := float64(outerRadius) - - var rOff, bOff int - switch format { - case FormatABGR8888, FormatXBGR8888: - rOff, bOff = 0, 2 - default: - rOff, bOff = 2, 0 - } - - for dy := -outerRadius - 2; dy <= outerRadius+2; dy++ { - y := cy + dy - if y < 0 || y >= dstH { - continue - } - dstRowOff := y * dstStride - - for dx := -outerRadius - 2; dx <= outerRadius+2; dx++ { - x := cx + dx - if x < 0 || x >= dstW { - continue - } - - dist := math.Sqrt(float64(dx*dx + dy*dy)) - if dist > outerRadiusF+aaWidth { - continue - } - - dstOff := dstRowOff + x*4 - if dstOff+4 > len(dst) { - continue - } - - bgColor := Color{ - R: dst[dstOff+rOff], - G: dst[dstOff+1], - B: dst[dstOff+bOff], - A: dst[dstOff+3], - } - - var finalColor Color - - switch { - case dist > outerRadiusF: - alpha := clampF(1.0-(dist-outerRadiusF)/aaWidth, 0, 1) - finalColor = blendColors(bgColor, borderColor, alpha) - - case dist > innerRadius: - switch { - case dist > outerRadiusF-aaWidth: - alpha := clampF((outerRadiusF-dist)/aaWidth, 0, 1) - finalColor = blendColors(borderColor, borderColor, alpha) - case dist < innerRadius+aaWidth: - alpha := clampF((dist-innerRadius)/aaWidth, 0, 1) - fx := float64(dx) / zoom - fy := float64(dy) / zoom - sx := cx + int(math.Round(fx)) - sy := cy + int(math.Round(fy)) - sx = clamp(sx, 0, srcW-1) - sy = clamp(sy, 0, srcH-1) - if yInverted { - sy = srcH - 1 - sy - } - srcOff := sy*srcStride + sx*4 - if srcOff+4 <= len(src) { - magColor := Color{R: src[srcOff+rOff], G: src[srcOff+1], B: src[srcOff+bOff], A: 255} - finalColor = blendColors(magColor, borderColor, alpha) - } else { - finalColor = borderColor - } - default: - finalColor = borderColor - } - - default: - fx := float64(dx) / zoom - fy := float64(dy) / zoom - sx := cx + int(math.Round(fx)) - sy := cy + int(math.Round(fy)) - sx = clamp(sx, 0, srcW-1) - sy = clamp(sy, 0, srcH-1) - if yInverted { - sy = srcH - 1 - sy - } - srcOff := sy*srcStride + sx*4 - if srcOff+4 <= len(src) { - finalColor = Color{R: src[srcOff+rOff], G: src[srcOff+1], B: src[srcOff+bOff], A: 255} - } else { - continue - } - } - - dst[dstOff+rOff] = finalColor.R - dst[dstOff+1] = finalColor.G - dst[dstOff+bOff] = finalColor.B - dst[dstOff+3] = 255 - } - } - - drawMagnifierCrosshair(dst, dstStride, dstW, dstH, cx, cy, int(innerRadius), crossThickness, crossInnerRadius, format) -} - -func drawMagnifierCrosshair( - data []byte, stride, width, height, cx, cy, radius, thickness, innerRadius int, - format PixelFormat, -) { - if width <= 0 || height <= 0 { - return - } - - cx = clamp(cx, 0, width-1) - cy = clamp(cy, 0, height-1) - - innerR2 := innerRadius * innerRadius - - for dy := -radius; dy <= radius; dy++ { - y := cy + dy - if y < 0 || y >= height { - continue - } - rowOff := y * stride - - for dx := -radius; dx <= radius; dx++ { - x := cx + dx - if x < 0 || x >= width { - continue - } - - dist2 := dx*dx + dy*dy - if dist2 > innerR2 { - continue - } - - absX := abs(dx) - absY := abs(dy) - if absX > thickness && absY > thickness { - continue - } - - off := rowOff + x*4 - if off+4 > len(data) { - continue - } - - isOutline := absX == thickness || absY == thickness - if isOutline { - data[off+0] = 0 - data[off+1] = 0 - data[off+2] = 0 - data[off+3] = 255 - } else { - data[off+0] = 255 - data[off+1] = 255 - data[off+2] = 255 - data[off+3] = 255 - } - } - } -} - -const ( - fontW = 8 - fontH = 12 -) - -var fontGlyphs = map[rune][fontH]uint8{ - '0': { - 0b00111100, - 0b01100110, - 0b01100110, - 0b01101110, - 0b01110110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00111100, - 0b00000000, - 0b00000000, - }, - '1': { - 0b00011000, - 0b00111000, - 0b01111000, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00011000, - 0b01111110, - 0b00000000, - 0b00000000, - }, - '2': { - 0b00111100, - 0b01100110, - 0b01100110, - 0b00000110, - 0b00001100, - 0b00011000, - 0b00110000, - 0b01100000, - 0b01100110, - 0b01111110, - 0b00000000, - 0b00000000, - }, - '3': { - 0b00111100, - 0b01100110, - 0b00000110, - 0b00000110, - 0b00011100, - 0b00000110, - 0b00000110, - 0b00000110, - 0b01100110, - 0b00111100, - 0b00000000, - 0b00000000, - }, - '4': { - 0b00001100, - 0b00011100, - 0b00111100, - 0b01101100, - 0b11001100, - 0b11001100, - 0b11111110, - 0b00001100, - 0b00001100, - 0b00011110, - 0b00000000, - 0b00000000, - }, - '5': { - 0b01111110, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01111100, - 0b00000110, - 0b00000110, - 0b00000110, - 0b01100110, - 0b00111100, - 0b00000000, - 0b00000000, - }, - '6': { - 0b00011100, - 0b00110000, - 0b01100000, - 0b01100000, - 0b01111100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00111100, - 0b00000000, - 0b00000000, - }, - '7': { - 0b01111110, - 0b01100110, - 0b00000110, - 0b00000110, - 0b00001100, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00000000, - 0b00000000, - }, - '8': { - 0b00111100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00111100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00111100, - 0b00000000, - 0b00000000, - }, - '9': { - 0b00111100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00111110, - 0b00000110, - 0b00000110, - 0b00000110, - 0b00001100, - 0b00111000, - 0b00000000, - 0b00000000, - }, - 'A': { - 0b00011000, - 0b00111100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01111110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00000000, - 0b00000000, - }, - 'B': { - 0b01111100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01111100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01111100, - 0b00000000, - 0b00000000, - }, - 'C': { - 0b00111100, - 0b01100110, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100110, - 0b00111100, - 0b00000000, - 0b00000000, - }, - 'D': { - 0b01111000, - 0b01101100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01101100, - 0b01111000, - 0b00000000, - 0b00000000, - }, - 'E': { - 0b01111110, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01111100, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01111110, - 0b00000000, - 0b00000000, - }, - 'F': { - 0b01111110, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01111100, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b00000000, - 0b00000000, - }, - '#': { - 0b00000000, - 0b00100100, - 0b00100100, - 0b01111110, - 0b00100100, - 0b00100100, - 0b01111110, - 0b00100100, - 0b00100100, - 0b00000000, - 0b00000000, - 0b00000000, - }, - 'G': { - 0b00111100, - 0b01100110, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01101110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00111100, - 0b00000000, - 0b00000000, - }, - 'H': { - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01111110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00000000, - 0b00000000, - }, - 'K': { - 0b01100110, - 0b01100110, - 0b01101100, - 0b01111000, - 0b01110000, - 0b01111000, - 0b01101100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00000000, - 0b00000000, - }, - 'L': { - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01100000, - 0b01111110, - 0b00000000, - 0b00000000, - }, - 'M': { - 0b01100011, - 0b01110111, - 0b01111111, - 0b01101011, - 0b01100011, - 0b01100011, - 0b01100011, - 0b01100011, - 0b01100011, - 0b01100011, - 0b00000000, - 0b00000000, - }, - 'R': { - 0b01111100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01111100, - 0b01111000, - 0b01101100, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00000000, - 0b00000000, - }, - 'S': { - 0b00111100, - 0b01100110, - 0b01100000, - 0b01100000, - 0b00111100, - 0b00000110, - 0b00000110, - 0b00000110, - 0b01100110, - 0b00111100, - 0b00000000, - 0b00000000, - }, - 'V': { - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00111100, - 0b00011000, - 0b00011000, - 0b00000000, - 0b00000000, - }, - 'Y': { - 0b01100110, - 0b01100110, - 0b01100110, - 0b01100110, - 0b00111100, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00011000, - 0b00000000, - 0b00000000, - }, - '(': { - 0b00001100, - 0b00011000, - 0b00110000, - 0b00110000, - 0b00110000, - 0b00110000, - 0b00110000, - 0b00110000, - 0b00011000, - 0b00001100, - 0b00000000, - 0b00000000, - }, - ')': { - 0b00110000, - 0b00011000, - 0b00001100, - 0b00001100, - 0b00001100, - 0b00001100, - 0b00001100, - 0b00001100, - 0b00011000, - 0b00110000, - 0b00000000, - 0b00000000, - }, - ',': { - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00011000, - 0b00011000, - 0b00110000, - 0b00000000, - 0b00000000, - }, - '%': { - 0b01100010, - 0b01100110, - 0b00001100, - 0b00001100, - 0b00011000, - 0b00011000, - 0b00110000, - 0b00110000, - 0b01100110, - 0b01000110, - 0b00000000, - 0b00000000, - }, - ' ': { - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - }, -} - -func drawColorPreview(data []byte, stride, width, height int, cx, cy int, c Color, format OutputFormat, lowercase bool, pixelFormat PixelFormat) { - text := formatColorForPreview(c, format, lowercase) - if len(text) == 0 { - return - } - - const ( - paddingX = 8 - paddingY = 4 - space = 2 - offset = 88 - ) - - textW := len(text)*(fontW+space) - space - textH := fontH - - boxW := textW + paddingX*2 - boxH := textH + paddingY*2 - - x := cx + offset - y := cy - boxH/2 - - if x+boxW >= width { - x = cx - boxW - offset - } - if x < 0 { - x = 0 - } - if y < 0 { - y = 0 - } - if y+boxH >= height { - y = height - boxH - } - - drawFilledRect(data, stride, width, height, x, y, boxW, boxH, c, pixelFormat) - - lum := 0.299*float64(c.R) + 0.587*float64(c.G) + 0.114*float64(c.B) - var fg Color - if lum > 128 { - fg = Color{R: 0, G: 0, B: 0, A: 255} - } else { - fg = Color{R: 255, G: 255, B: 255, A: 255} - } - drawText(data, stride, width, height, x+paddingX, y+paddingY, text, fg, pixelFormat) -} - -func formatColorForPreview(c Color, format OutputFormat, lowercase bool) string { - switch format { - case FormatRGB: - return strings.ToUpper(c.ToRGB()) - case FormatHSL: - return strings.ToUpper(c.ToHSL()) - case FormatHSV: - return strings.ToUpper(c.ToHSV()) - case FormatCMYK: - return strings.ToUpper(c.ToCMYK()) - default: - if lowercase { - return c.ToHex(true) - } - return c.ToHex(false) - } -} - -func drawFilledRect(data []byte, stride, width, height, x, y, w, h int, col Color, format PixelFormat) { - if w <= 0 || h <= 0 { - return - } - xEnd := clamp(x+w, 0, width) - yEnd := clamp(y+h, 0, height) - x = clamp(x, 0, width) - y = clamp(y, 0, height) - - var rOff, bOff int - switch format { - case FormatABGR8888, FormatXBGR8888: - rOff, bOff = 0, 2 - default: - rOff, bOff = 2, 0 - } - - for yy := y; yy < yEnd; yy++ { - rowOff := yy * stride - for xx := x; xx < xEnd; xx++ { - off := rowOff + xx*4 - if off+4 > len(data) { - continue - } - data[off+rOff] = col.R - data[off+1] = col.G - data[off+bOff] = col.B - data[off+3] = 255 - } - } -} - -func drawText(data []byte, stride, width, height, x, y int, text string, col Color, format PixelFormat) { - for i, r := range text { - drawGlyph(data, stride, width, height, x+i*(fontW+2), y, r, col, format) - } -} - -func drawGlyph(data []byte, stride, width, height, x, y int, r rune, col Color, format PixelFormat) { - g, ok := fontGlyphs[r] - if !ok { - return - } - - var rOff, bOff int - switch format { - case FormatABGR8888, FormatXBGR8888: - rOff, bOff = 0, 2 - default: - rOff, bOff = 2, 0 - } - - for row := range fontH { - yy := y + row - if yy < 0 || yy >= height { - continue - } - rowPattern := g[row] - dstRowOff := yy * stride - - for colIdx := range fontW { - if (rowPattern & (1 << (fontW - 1 - colIdx))) == 0 { - continue - } - - xx := x + colIdx - if xx < 0 || xx >= width { - continue - } - - off := dstRowOff + xx*4 - if off+4 > len(data) { - continue - } - - data[off+rOff] = col.R - data[off+1] = col.G - data[off+bOff] = col.B - data[off+3] = 255 - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/state_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/state_test.go deleted file mode 100644 index a6599aa..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/colorpicker/state_test.go +++ /dev/null @@ -1,314 +0,0 @@ -package colorpicker - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSurfaceState_ConcurrentPointerMotion(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - - var wg sync.WaitGroup - const goroutines = 50 - const iterations = 100 - - for i := range goroutines { - wg.Add(1) - go func(id int) { - defer wg.Done() - for j := range iterations { - s.OnPointerMotion(float64(id*10+j), float64(id*10+j)) - } - }(i) - } - - wg.Wait() -} - -func TestSurfaceState_ConcurrentScaleAccess(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 100 - - for i := range goroutines / 2 { - wg.Add(1) - go func(id int) { - defer wg.Done() - for range iterations { - s.SetScale(int32(id%3 + 1)) - } - }(i) - } - - for range goroutines / 2 { - wg.Add(1) - go func() { - defer wg.Done() - for range iterations { - scale := s.Scale() - assert.GreaterOrEqual(t, scale, int32(1)) - } - }() - } - - wg.Wait() -} - -func TestSurfaceState_ConcurrentLogicalSize(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - - var wg sync.WaitGroup - const goroutines = 20 - const iterations = 100 - - for i := range goroutines / 2 { - wg.Add(1) - go func(id int) { - defer wg.Done() - for j := range iterations { - _ = s.OnLayerConfigure(1920+id, 1080+j) - } - }(i) - } - - for range goroutines / 2 { - wg.Add(1) - go func() { - defer wg.Done() - for range iterations { - w, h := s.LogicalSize() - _ = w - _ = h - } - }() - } - - wg.Wait() -} - -func TestSurfaceState_ConcurrentIsDone(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 100 - - for range goroutines / 3 { - wg.Add(1) - go func() { - defer wg.Done() - for range iterations { - s.OnPointerButton(0x110, 1) - } - }() - } - - for range goroutines / 3 { - wg.Add(1) - go func() { - defer wg.Done() - for range iterations { - s.OnKey(1, 1) - } - }() - } - - for range goroutines / 3 { - wg.Add(1) - go func() { - defer wg.Done() - for range iterations { - picked, cancelled := s.IsDone() - _ = picked - _ = cancelled - } - }() - } - - wg.Wait() -} - -func TestSurfaceState_ConcurrentIsReady(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - - var wg sync.WaitGroup - const goroutines = 20 - const iterations = 100 - - for range goroutines { - wg.Add(1) - go func() { - defer wg.Done() - for range iterations { - _ = s.IsReady() - } - }() - } - - wg.Wait() -} - -func TestSurfaceState_ConcurrentSwapBuffers(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - - var wg sync.WaitGroup - const goroutines = 20 - const iterations = 100 - - for range goroutines { - wg.Add(1) - go func() { - defer wg.Done() - for range iterations { - s.SwapBuffers() - } - }() - } - - wg.Wait() -} - -func TestSurfaceState_ZeroScale(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - s.SetScale(0) - assert.Equal(t, int32(1), s.Scale()) -} - -func TestSurfaceState_NegativeScale(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - s.SetScale(-5) - assert.Equal(t, int32(1), s.Scale()) -} - -func TestSurfaceState_ZeroDimensionConfigure(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - - err := s.OnLayerConfigure(0, 100) - assert.NoError(t, err) - - err = s.OnLayerConfigure(100, 0) - assert.NoError(t, err) - - err = s.OnLayerConfigure(-1, 100) - assert.NoError(t, err) - - w, h := s.LogicalSize() - assert.Equal(t, 0, w) - assert.Equal(t, 0, h) -} - -func TestSurfaceState_PickColorNilBuffer(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - color, ok := s.PickColor() - assert.False(t, ok) - assert.Equal(t, Color{}, color) -} - -func TestSurfaceState_RedrawNilBuffer(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - buf := s.Redraw() - assert.Nil(t, buf) -} - -func TestSurfaceState_RedrawScreenOnlyNilBuffer(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - buf := s.RedrawScreenOnly() - assert.Nil(t, buf) -} - -func TestSurfaceState_FrontRenderBufferNil(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - buf := s.FrontRenderBuffer() - assert.Nil(t, buf) -} - -func TestSurfaceState_ScreenBufferNil(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - buf := s.ScreenBuffer() - assert.Nil(t, buf) -} - -func TestSurfaceState_DestroyMultipleTimes(t *testing.T) { - s := NewSurfaceState(FormatHex, false) - s.Destroy() - s.Destroy() -} - -func TestClamp(t *testing.T) { - tests := []struct { - v, lo, hi, expected int - }{ - {5, 0, 10, 5}, - {-5, 0, 10, 0}, - {15, 0, 10, 10}, - {0, 0, 10, 0}, - {10, 0, 10, 10}, - } - - for _, tt := range tests { - result := clamp(tt.v, tt.lo, tt.hi) - assert.Equal(t, tt.expected, result) - } -} - -func TestClampF(t *testing.T) { - tests := []struct { - v, lo, hi, expected float64 - }{ - {5.0, 0.0, 10.0, 5.0}, - {-5.0, 0.0, 10.0, 0.0}, - {15.0, 0.0, 10.0, 10.0}, - {0.0, 0.0, 10.0, 0.0}, - {10.0, 0.0, 10.0, 10.0}, - } - - for _, tt := range tests { - result := clampF(tt.v, tt.lo, tt.hi) - assert.InDelta(t, tt.expected, result, 0.001) - } -} - -func TestAbs(t *testing.T) { - tests := []struct { - v, expected int - }{ - {5, 5}, - {-5, 5}, - {0, 0}, - } - - for _, tt := range tests { - result := abs(tt.v) - assert.Equal(t, tt.expected, result) - } -} - -func TestBlendColors(t *testing.T) { - bg := Color{R: 0, G: 0, B: 0, A: 255} - fg := Color{R: 255, G: 255, B: 255, A: 255} - - result := blendColors(bg, fg, 0.0) - assert.Equal(t, bg.R, result.R) - assert.Equal(t, bg.G, result.G) - assert.Equal(t, bg.B, result.B) - - result = blendColors(bg, fg, 1.0) - assert.Equal(t, fg.R, result.R) - assert.Equal(t, fg.G, result.G) - assert.Equal(t, fg.B, result.B) - - result = blendColors(bg, fg, 0.5) - assert.InDelta(t, 127, int(result.R), 1) - assert.InDelta(t, 127, int(result.G), 1) - assert.InDelta(t, 127, int(result.B), 1) - - result = blendColors(bg, fg, -1.0) - assert.Equal(t, bg.R, result.R) - - result = blendColors(bg, fg, 2.0) - assert.Equal(t, fg.R, result.R) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer.go deleted file mode 100644 index 7cc513f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer.go +++ /dev/null @@ -1,718 +0,0 @@ -package config - -import ( - "context" - "fmt" - "os" - "path/filepath" - "regexp" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -type ConfigDeployer struct { - logChan chan<- string -} - -type DeploymentResult struct { - ConfigType string - Path string - BackupPath string - Deployed bool - Error error -} - -func NewConfigDeployer(logChan chan<- string) *ConfigDeployer { - return &ConfigDeployer{ - logChan: logChan, - } -} - -func (cd *ConfigDeployer) log(message string) { - if cd.logChan != nil { - cd.logChan <- message - } -} - -// DeployConfigurations deploys all necessary configurations based on the chosen window manager -func (cd *ConfigDeployer) DeployConfigurations(ctx context.Context, wm deps.WindowManager) ([]DeploymentResult, error) { - return cd.DeployConfigurationsWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -// DeployConfigurationsWithTerminal deploys all necessary configurations based on chosen window manager and terminal -func (cd *ConfigDeployer) DeployConfigurationsWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]DeploymentResult, error) { - return cd.DeployConfigurationsSelective(ctx, wm, terminal, nil, nil) -} - -// DeployConfigurationsWithSystemd deploys configurations with systemd option -func (cd *ConfigDeployer) DeployConfigurationsWithSystemd(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, useSystemd bool) ([]DeploymentResult, error) { - return cd.deployConfigurationsInternal(ctx, wm, terminal, nil, nil, nil, useSystemd) -} - -func (cd *ConfigDeployer) DeployConfigurationsSelective(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, installedDeps []deps.Dependency, replaceConfigs map[string]bool) ([]DeploymentResult, error) { - return cd.DeployConfigurationsSelectiveWithReinstalls(ctx, wm, terminal, installedDeps, replaceConfigs, nil) -} - -func (cd *ConfigDeployer) DeployConfigurationsSelectiveWithReinstalls(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, installedDeps []deps.Dependency, replaceConfigs map[string]bool, reinstallItems map[string]bool) ([]DeploymentResult, error) { - return cd.deployConfigurationsInternal(ctx, wm, terminal, installedDeps, replaceConfigs, reinstallItems, true) -} - -func (cd *ConfigDeployer) deployConfigurationsInternal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal, installedDeps []deps.Dependency, replaceConfigs map[string]bool, reinstallItems map[string]bool, useSystemd bool) ([]DeploymentResult, error) { - var results []DeploymentResult - - // Primary config file paths used to detect fresh installs. - configPrimaryPaths := map[string]string{ - "Niri": filepath.Join(os.Getenv("HOME"), ".config", "niri", "config.kdl"), - "Hyprland": filepath.Join(os.Getenv("HOME"), ".config", "hypr", "hyprland.conf"), - "Ghostty": filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config"), - "Kitty": filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf"), - "Alacritty": filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml"), - } - - shouldReplaceConfig := func(configType string) bool { - if replaceConfigs == nil { - return true - } - replace, exists := replaceConfigs[configType] - if !exists || replace { - return true - } - // Config is explicitly set to "don't replace" — but still deploy - // if the config file doesn't exist yet (fresh install scenario). - if primaryPath, ok := configPrimaryPaths[configType]; ok { - if _, err := os.Stat(primaryPath); os.IsNotExist(err) { - return true - } - } - return false - } - - switch wm { - case deps.WindowManagerNiri: - if shouldReplaceConfig("Niri") { - result, err := cd.deployNiriConfig(terminal, useSystemd) - results = append(results, result) - if err != nil { - return results, fmt.Errorf("failed to deploy Niri config: %w", err) - } - } - case deps.WindowManagerHyprland: - if shouldReplaceConfig("Hyprland") { - result, err := cd.deployHyprlandConfig(terminal, useSystemd) - results = append(results, result) - if err != nil { - return results, fmt.Errorf("failed to deploy Hyprland config: %w", err) - } - } - } - - switch terminal { - case deps.TerminalGhostty: - if shouldReplaceConfig("Ghostty") { - ghosttyResults, err := cd.deployGhosttyConfig() - results = append(results, ghosttyResults...) - if err != nil { - return results, fmt.Errorf("failed to deploy Ghostty config: %w", err) - } - } - case deps.TerminalKitty: - if shouldReplaceConfig("Kitty") { - kittyResults, err := cd.deployKittyConfig() - results = append(results, kittyResults...) - if err != nil { - return results, fmt.Errorf("failed to deploy Kitty config: %w", err) - } - } - case deps.TerminalAlacritty: - if shouldReplaceConfig("Alacritty") { - alacrittyResults, err := cd.deployAlacrittyConfig() - results = append(results, alacrittyResults...) - if err != nil { - return results, fmt.Errorf("failed to deploy Alacritty config: %w", err) - } - } - } - - return results, nil -} - -func (cd *ConfigDeployer) deployNiriConfig(terminal deps.Terminal, useSystemd bool) (DeploymentResult, error) { - result := DeploymentResult{ - ConfigType: "Niri", - Path: filepath.Join(os.Getenv("HOME"), ".config", "niri", "config.kdl"), - } - - configDir := filepath.Dir(result.Path) - if err := os.MkdirAll(configDir, 0o755); err != nil { - result.Error = fmt.Errorf("failed to create config directory: %w", err) - return result, result.Error - } - - dmsDir := filepath.Join(configDir, "dms") - if err := os.MkdirAll(dmsDir, 0o755); err != nil { - result.Error = fmt.Errorf("failed to create dms directory: %w", err) - return result, result.Error - } - - var existingConfig string - if _, err := os.Stat(result.Path); err == nil { - cd.log("Found existing Niri configuration") - - existingData, err := os.ReadFile(result.Path) - if err != nil { - result.Error = fmt.Errorf("failed to read existing config: %w", err) - return result, result.Error - } - existingConfig = string(existingData) - - timestamp := time.Now().Format("2006-01-02_15-04-05") - result.BackupPath = result.Path + ".backup." + timestamp - if err := os.WriteFile(result.BackupPath, existingData, 0o644); err != nil { - result.Error = fmt.Errorf("failed to create backup: %w", err) - return result, result.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", result.BackupPath)) - } - - var terminalCommand string - switch terminal { - case deps.TerminalGhostty: - terminalCommand = "ghostty" - case deps.TerminalKitty: - terminalCommand = "kitty" - case deps.TerminalAlacritty: - terminalCommand = "alacritty" - default: - terminalCommand = "ghostty" - } - - newConfig := strings.ReplaceAll(NiriConfig, "{{TERMINAL_COMMAND}}", terminalCommand) - - if !useSystemd { - newConfig = cd.transformNiriConfigForNonSystemd(newConfig, terminalCommand) - } - - if existingConfig != "" { - mergedConfig, err := cd.mergeNiriOutputSections(newConfig, existingConfig, dmsDir) - if err != nil { - cd.log(fmt.Sprintf("Warning: Failed to merge output sections: %v", err)) - } else { - newConfig = mergedConfig - cd.log("Successfully merged existing output sections") - } - } - - if err := os.WriteFile(result.Path, []byte(newConfig), 0o644); err != nil { - result.Error = fmt.Errorf("failed to write config: %w", err) - return result, result.Error - } - - if err := cd.deployNiriDmsConfigs(dmsDir, terminalCommand); err != nil { - result.Error = fmt.Errorf("failed to deploy dms configs: %w", err) - return result, result.Error - } - - result.Deployed = true - cd.log("Successfully deployed Niri configuration") - return result, nil -} - -func (cd *ConfigDeployer) deployNiriDmsConfigs(dmsDir, terminalCommand string) error { - configs := []struct { - name string - content string - }{ - {"colors.kdl", NiriColorsConfig}, - {"layout.kdl", NiriLayoutConfig}, - {"alttab.kdl", NiriAlttabConfig}, - {"binds.kdl", strings.ReplaceAll(NiriBindsConfig, "{{TERMINAL_COMMAND}}", terminalCommand)}, - {"outputs.kdl", ""}, - {"cursor.kdl", ""}, - {"windowrules.kdl", ""}, - } - - for _, cfg := range configs { - path := filepath.Join(dmsDir, cfg.name) - // Skip if file already exists and is not empty to preserve user modifications - if info, err := os.Stat(path); err == nil && info.Size() > 0 { - cd.log(fmt.Sprintf("Skipping %s (already exists)", cfg.name)) - continue - } - if err := os.WriteFile(path, []byte(cfg.content), 0o644); err != nil { - return fmt.Errorf("failed to write %s: %w", cfg.name, err) - } - cd.log(fmt.Sprintf("Deployed %s", cfg.name)) - } - - return nil -} - -func (cd *ConfigDeployer) deployGhosttyConfig() ([]DeploymentResult, error) { - var results []DeploymentResult - - mainResult := DeploymentResult{ - ConfigType: "Ghostty", - Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config"), - } - - configDir := filepath.Dir(mainResult.Path) - if err := os.MkdirAll(configDir, 0o755); err != nil { - mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - if _, err := os.Stat(mainResult.Path); err == nil { - cd.log("Found existing Ghostty configuration") - - existingData, err := os.ReadFile(mainResult.Path) - if err != nil { - mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - timestamp := time.Now().Format("2006-01-02_15-04-05") - mainResult.BackupPath = mainResult.Path + ".backup." + timestamp - if err := os.WriteFile(mainResult.BackupPath, existingData, 0o644); err != nil { - mainResult.Error = fmt.Errorf("failed to create backup: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) - } - - if err := os.WriteFile(mainResult.Path, []byte(GhosttyConfig), 0o644); err != nil { - mainResult.Error = fmt.Errorf("failed to write config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - mainResult.Deployed = true - cd.log("Successfully deployed Ghostty configuration") - results = append(results, mainResult) - - colorResult := DeploymentResult{ - ConfigType: "Ghostty Colors", - Path: filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "themes", "dankcolors"), - } - - themesDir := filepath.Dir(colorResult.Path) - if err := os.MkdirAll(themesDir, 0o755); err != nil { - mainResult.Error = fmt.Errorf("failed to create themes directory: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - if err := os.WriteFile(colorResult.Path, []byte(GhosttyColorConfig), 0o644); err != nil { - colorResult.Error = fmt.Errorf("failed to write color config: %w", err) - return results, colorResult.Error - } - - colorResult.Deployed = true - cd.log("Successfully deployed Ghostty color configuration") - results = append(results, colorResult) - - return results, nil -} - -func (cd *ConfigDeployer) deployKittyConfig() ([]DeploymentResult, error) { - var results []DeploymentResult - - mainResult := DeploymentResult{ - ConfigType: "Kitty", - Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf"), - } - - configDir := filepath.Dir(mainResult.Path) - if err := os.MkdirAll(configDir, 0o755); err != nil { - mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - if _, err := os.Stat(mainResult.Path); err == nil { - cd.log("Found existing Kitty configuration") - - existingData, err := os.ReadFile(mainResult.Path) - if err != nil { - mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - timestamp := time.Now().Format("2006-01-02_15-04-05") - mainResult.BackupPath = mainResult.Path + ".backup." + timestamp - if err := os.WriteFile(mainResult.BackupPath, existingData, 0o644); err != nil { - mainResult.Error = fmt.Errorf("failed to create backup: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) - } - - if err := os.WriteFile(mainResult.Path, []byte(KittyConfig), 0o644); err != nil { - mainResult.Error = fmt.Errorf("failed to write config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - mainResult.Deployed = true - cd.log("Successfully deployed Kitty configuration") - results = append(results, mainResult) - - themeResult := DeploymentResult{ - ConfigType: "Kitty Theme", - Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "dank-theme.conf"), - } - - if err := os.WriteFile(themeResult.Path, []byte(KittyThemeConfig), 0o644); err != nil { - themeResult.Error = fmt.Errorf("failed to write theme config: %w", err) - return results, themeResult.Error - } - - themeResult.Deployed = true - cd.log("Successfully deployed Kitty theme configuration") - results = append(results, themeResult) - - tabsResult := DeploymentResult{ - ConfigType: "Kitty Tabs", - Path: filepath.Join(os.Getenv("HOME"), ".config", "kitty", "dank-tabs.conf"), - } - - if err := os.WriteFile(tabsResult.Path, []byte(KittyTabsConfig), 0o644); err != nil { - tabsResult.Error = fmt.Errorf("failed to write tabs config: %w", err) - return results, tabsResult.Error - } - - tabsResult.Deployed = true - cd.log("Successfully deployed Kitty tabs configuration") - results = append(results, tabsResult) - - return results, nil -} - -func (cd *ConfigDeployer) deployAlacrittyConfig() ([]DeploymentResult, error) { - var results []DeploymentResult - - mainResult := DeploymentResult{ - ConfigType: "Alacritty", - Path: filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml"), - } - - configDir := filepath.Dir(mainResult.Path) - if err := os.MkdirAll(configDir, 0o755); err != nil { - mainResult.Error = fmt.Errorf("failed to create config directory: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - if _, err := os.Stat(mainResult.Path); err == nil { - cd.log("Found existing Alacritty configuration") - - existingData, err := os.ReadFile(mainResult.Path) - if err != nil { - mainResult.Error = fmt.Errorf("failed to read existing config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - timestamp := time.Now().Format("2006-01-02_15-04-05") - mainResult.BackupPath = mainResult.Path + ".backup." + timestamp - if err := os.WriteFile(mainResult.BackupPath, existingData, 0o644); err != nil { - mainResult.Error = fmt.Errorf("failed to create backup: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", mainResult.BackupPath)) - } - - if err := os.WriteFile(mainResult.Path, []byte(AlacrittyConfig), 0o644); err != nil { - mainResult.Error = fmt.Errorf("failed to write config: %w", err) - return []DeploymentResult{mainResult}, mainResult.Error - } - - mainResult.Deployed = true - cd.log("Successfully deployed Alacritty configuration") - results = append(results, mainResult) - - themeResult := DeploymentResult{ - ConfigType: "Alacritty Theme", - Path: filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "dank-theme.toml"), - } - - if err := os.WriteFile(themeResult.Path, []byte(AlacrittyThemeConfig), 0o644); err != nil { - themeResult.Error = fmt.Errorf("failed to write theme config: %w", err) - return results, themeResult.Error - } - - themeResult.Deployed = true - cd.log("Successfully deployed Alacritty theme configuration") - results = append(results, themeResult) - - return results, nil -} - -func (cd *ConfigDeployer) mergeNiriOutputSections(newConfig, existingConfig, dmsDir string) (string, error) { - outputRegex := regexp.MustCompile(`(?m)^(/-)?\s*output\s+"[^"]+"\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}`) - existingOutputs := outputRegex.FindAllString(existingConfig, -1) - - if len(existingOutputs) == 0 { - return newConfig, nil - } - - outputsPath := filepath.Join(dmsDir, "outputs.kdl") - if _, err := os.Stat(outputsPath); err != nil { - var outputsContent strings.Builder - for _, output := range existingOutputs { - outputsContent.WriteString(output) - outputsContent.WriteString("\n\n") - } - if err := os.WriteFile(outputsPath, []byte(outputsContent.String()), 0o644); err != nil { - cd.log(fmt.Sprintf("Warning: Failed to migrate outputs to %s: %v", outputsPath, err)) - } else { - cd.log("Migrated output sections to dms/outputs.kdl") - } - } - - exampleOutputRegex := regexp.MustCompile(`(?m)^/-output "eDP-2" \{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}`) - mergedConfig := exampleOutputRegex.ReplaceAllString(newConfig, "") - - inputEndRegex := regexp.MustCompile(`(?m)^}$`) - inputMatches := inputEndRegex.FindAllStringIndex(newConfig, -1) - - if len(inputMatches) < 1 { - return "", fmt.Errorf("could not find insertion point for output sections") - } - - insertPos := inputMatches[0][1] - - var builder strings.Builder - builder.WriteString(mergedConfig[:insertPos]) - builder.WriteString("\n// Outputs from existing configuration\n") - - for _, output := range existingOutputs { - builder.WriteString(output) - builder.WriteString("\n") - } - - builder.WriteString(mergedConfig[insertPos:]) - - return builder.String(), nil -} - -// deployHyprlandConfig handles Hyprland configuration deployment with backup and merging -func (cd *ConfigDeployer) deployHyprlandConfig(terminal deps.Terminal, useSystemd bool) (DeploymentResult, error) { - result := DeploymentResult{ - ConfigType: "Hyprland", - Path: filepath.Join(os.Getenv("HOME"), ".config", "hypr", "hyprland.conf"), - } - - configDir := filepath.Dir(result.Path) - if err := os.MkdirAll(configDir, 0o755); err != nil { - result.Error = fmt.Errorf("failed to create config directory: %w", err) - return result, result.Error - } - - dmsDir := filepath.Join(configDir, "dms") - if err := os.MkdirAll(dmsDir, 0o755); err != nil { - result.Error = fmt.Errorf("failed to create dms directory: %w", err) - return result, result.Error - } - - var existingConfig string - if _, err := os.Stat(result.Path); err == nil { - cd.log("Found existing Hyprland configuration") - - existingData, err := os.ReadFile(result.Path) - if err != nil { - result.Error = fmt.Errorf("failed to read existing config: %w", err) - return result, result.Error - } - existingConfig = string(existingData) - - timestamp := time.Now().Format("2006-01-02_15-04-05") - result.BackupPath = result.Path + ".backup." + timestamp - if err := os.WriteFile(result.BackupPath, existingData, 0o644); err != nil { - result.Error = fmt.Errorf("failed to create backup: %w", err) - return result, result.Error - } - cd.log(fmt.Sprintf("Backed up existing config to %s", result.BackupPath)) - } - - var terminalCommand string - switch terminal { - case deps.TerminalGhostty: - terminalCommand = "ghostty" - case deps.TerminalKitty: - terminalCommand = "kitty" - case deps.TerminalAlacritty: - terminalCommand = "alacritty" - default: - terminalCommand = "ghostty" - } - - newConfig := strings.ReplaceAll(HyprlandConfig, "{{TERMINAL_COMMAND}}", terminalCommand) - - if !useSystemd { - newConfig = cd.transformHyprlandConfigForNonSystemd(newConfig, terminalCommand) - } - - if existingConfig != "" { - mergedConfig, err := cd.mergeHyprlandMonitorSections(newConfig, existingConfig, dmsDir) - if err != nil { - cd.log(fmt.Sprintf("Warning: Failed to merge monitor sections: %v", err)) - } else { - newConfig = mergedConfig - cd.log("Successfully merged existing monitor sections") - } - } - - if err := os.WriteFile(result.Path, []byte(newConfig), 0o644); err != nil { - result.Error = fmt.Errorf("failed to write config: %w", err) - return result, result.Error - } - - if err := cd.deployHyprlandDmsConfigs(dmsDir, terminalCommand); err != nil { - result.Error = fmt.Errorf("failed to deploy dms configs: %w", err) - return result, result.Error - } - - result.Deployed = true - cd.log("Successfully deployed Hyprland configuration") - return result, nil -} - -func (cd *ConfigDeployer) deployHyprlandDmsConfigs(dmsDir string, terminalCommand string) error { - configs := []struct { - name string - content string - }{ - {"colors.conf", HyprColorsConfig}, - {"layout.conf", HyprLayoutConfig}, - {"binds.conf", strings.ReplaceAll(HyprBindsConfig, "{{TERMINAL_COMMAND}}", terminalCommand)}, - {"outputs.conf", ""}, - {"cursor.conf", ""}, - {"windowrules.conf", ""}, - } - - for _, cfg := range configs { - path := filepath.Join(dmsDir, cfg.name) - // Skip if file already exists and is not empty to preserve user modifications - if info, err := os.Stat(path); err == nil && info.Size() > 0 { - cd.log(fmt.Sprintf("Skipping %s (already exists)", cfg.name)) - continue - } - if err := os.WriteFile(path, []byte(cfg.content), 0o644); err != nil { - return fmt.Errorf("failed to write %s: %w", cfg.name, err) - } - cd.log(fmt.Sprintf("Deployed %s", cfg.name)) - } - - return nil -} - -func (cd *ConfigDeployer) mergeHyprlandMonitorSections(newConfig, existingConfig, dmsDir string) (string, error) { - monitorRegex := regexp.MustCompile(`(?m)^#?\s*monitor\s*=.*$`) - existingMonitors := monitorRegex.FindAllString(existingConfig, -1) - - if len(existingMonitors) == 0 { - return newConfig, nil - } - - outputsPath := filepath.Join(dmsDir, "outputs.conf") - if _, err := os.Stat(outputsPath); err != nil { - var outputsContent strings.Builder - for _, monitor := range existingMonitors { - outputsContent.WriteString(monitor) - outputsContent.WriteString("\n") - } - if err := os.WriteFile(outputsPath, []byte(outputsContent.String()), 0o644); err != nil { - cd.log(fmt.Sprintf("Warning: Failed to migrate monitors to %s: %v", outputsPath, err)) - } else { - cd.log("Migrated monitor sections to dms/outputs.conf") - } - } - - exampleMonitorRegex := regexp.MustCompile(`(?m)^# monitor = eDP-2.*$`) - mergedConfig := exampleMonitorRegex.ReplaceAllString(newConfig, "") - - monitorHeaderRegex := regexp.MustCompile(`(?m)^# MONITOR CONFIG\n# ==================$`) - headerMatch := monitorHeaderRegex.FindStringIndex(mergedConfig) - - if headerMatch == nil { - return "", fmt.Errorf("could not find MONITOR CONFIG section") - } - - insertPos := headerMatch[1] + 1 - - var builder strings.Builder - builder.WriteString(mergedConfig[:insertPos]) - builder.WriteString("# Monitors from existing configuration\n") - - for _, monitor := range existingMonitors { - builder.WriteString(monitor) - builder.WriteString("\n") - } - - builder.WriteString(mergedConfig[insertPos:]) - - return builder.String(), nil -} - -func (cd *ConfigDeployer) transformHyprlandConfigForNonSystemd(config, terminalCommand string) string { - lines := strings.Split(config, "\n") - var result []string - startupSectionFound := false - - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, "exec-once = dbus-update-activation-environment") { - continue - } - if strings.HasPrefix(trimmed, "exec-once = systemctl --user start") { - startupSectionFound = true - result = append(result, "exec-once = dms run") - result = append(result, "env = QT_QPA_PLATFORM,wayland;xcb") - result = append(result, "env = ELECTRON_OZONE_PLATFORM_HINT,auto") - result = append(result, "env = QT_QPA_PLATFORMTHEME,gtk3") - result = append(result, "env = QT_QPA_PLATFORMTHEME_QT6,gtk3") - result = append(result, fmt.Sprintf("env = TERMINAL,%s", terminalCommand)) - continue - } - result = append(result, line) - } - - if !startupSectionFound { - for i, line := range result { - if strings.Contains(line, "STARTUP APPS") { - insertLines := []string{ - "exec-once = dms run", - "env = QT_QPA_PLATFORM,wayland;xcb", - "env = ELECTRON_OZONE_PLATFORM_HINT,auto", - "env = QT_QPA_PLATFORMTHEME,gtk3", - "env = QT_QPA_PLATFORMTHEME_QT6,gtk3", - fmt.Sprintf("env = TERMINAL,%s", terminalCommand), - } - result = append(result[:i+2], append(insertLines, result[i+2:]...)...) - break - } - } - } - - return strings.Join(result, "\n") -} - -func (cd *ConfigDeployer) transformNiriConfigForNonSystemd(config, terminalCommand string) string { - envVars := fmt.Sprintf(`environment { - XDG_CURRENT_DESKTOP "niri" - QT_QPA_PLATFORM "wayland;xcb" - ELECTRON_OZONE_PLATFORM_HINT "auto" - QT_QPA_PLATFORMTHEME "gtk3" - QT_QPA_PLATFORMTHEME_QT6 "gtk3" - TERMINAL "%s" -}`, terminalCommand) - - config = regexp.MustCompile(`environment \{[^}]*\}`).ReplaceAllString(config, envVars) - - spawnDms := `spawn-at-startup "dms" "run"` - if !strings.Contains(config, spawnDms) { - // Insert spawn-at-startup for dms after the environment block - envBlockEnd := regexp.MustCompile(`environment \{[^}]*\}`) - if loc := envBlockEnd.FindStringIndex(config); loc != nil { - config = config[:loc[1]] + "\n" + spawnDms + config[loc[1]:] - } - } - - return config -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer_test.go deleted file mode 100644 index 3726634..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/deployer_test.go +++ /dev/null @@ -1,792 +0,0 @@ -package config - -import ( - "context" - "os" - "path/filepath" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMergeNiriOutputSections(t *testing.T) { - cd := &ConfigDeployer{} - - tests := []struct { - name string - newConfig string - existingConfig string - wantError bool - wantContains []string - }{ - { - name: "no existing outputs", - newConfig: `input { - keyboard { - xkb { - } - } -} -layout { - gaps 5 -}`, - existingConfig: `input { - keyboard { - xkb { - } - } -} -layout { - gaps 10 -}`, - wantError: false, - wantContains: []string{"gaps 5"}, // Should keep new config - }, - { - name: "merge single output", - newConfig: `input { - keyboard { - xkb { - } - } -} -/-output "eDP-2" { - mode "2560x1600@239.998993" - position x=2560 y=0 -} -layout { - gaps 5 -}`, - existingConfig: `input { - keyboard { - xkb { - } - } -} -output "eDP-1" { - mode "1920x1080@60.000000" - position x=0 y=0 - scale 1.0 -} -layout { - gaps 10 -}`, - wantError: false, - wantContains: []string{ - "gaps 5", // New config preserved - `output "eDP-1"`, // Existing output merged - "1920x1080@60.000000", // Existing output details - "Outputs from existing configuration", // Comment added - }, - }, - { - name: "merge multiple outputs", - newConfig: `input { - keyboard { - xkb { - } - } -} -/-output "eDP-2" { - mode "2560x1600@239.998993" - position x=2560 y=0 -} -layout { - gaps 5 -}`, - existingConfig: `input { - keyboard { - xkb { - } - } -} -output "eDP-1" { - mode "1920x1080@60.000000" - position x=0 y=0 - scale 1.0 -} -/-output "HDMI-1" { - mode "1920x1080@60.000000" - position x=1920 y=0 -} -layout { - gaps 10 -}`, - wantError: false, - wantContains: []string{ - "gaps 5", // New config preserved - `output "eDP-1"`, // First existing output - `/-output "HDMI-1"`, // Second existing output (commented) - "1920x1080@60.000000", // Output details - }, - }, - { - name: "merge commented outputs", - newConfig: `input { - keyboard { - xkb { - } - } -} -/-output "eDP-2" { - mode "2560x1600@239.998993" - position x=2560 y=0 -} -layout { - gaps 5 -}`, - existingConfig: `input { - keyboard { - xkb { - } - } -} -/-output "eDP-1" { - mode "1920x1080@60.000000" - position x=0 y=0 - scale 1.0 -} -layout { - gaps 10 -}`, - wantError: false, - wantContains: []string{ - "gaps 5", // New config preserved - `/-output "eDP-1"`, // Commented output preserved - "1920x1080@60.000000", // Output details - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tmpDir := t.TempDir() - result, err := cd.mergeNiriOutputSections(tt.newConfig, tt.existingConfig, tmpDir) - - if tt.wantError { - assert.Error(t, err) - return - } - - require.NoError(t, err) - - for _, want := range tt.wantContains { - assert.Contains(t, result, want, "merged config should contain: %s", want) - } - - assert.NotContains(t, result, `/-output "eDP-2"`, "example output should be removed") - }) - } -} - -func TestConfigDeploymentFlow(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - t.Run("deploy ghostty config to empty directory", func(t *testing.T) { - results, err := cd.deployGhosttyConfig() - require.NoError(t, err) - require.Len(t, results, 2) - - mainResult := results[0] - assert.Equal(t, "Ghostty", mainResult.ConfigType) - assert.True(t, mainResult.Deployed) - assert.Empty(t, mainResult.BackupPath) - assert.FileExists(t, mainResult.Path) - - content, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.Contains(t, string(content), "window-decoration = false") - - colorResult := results[1] - assert.Equal(t, "Ghostty Colors", colorResult.ConfigType) - assert.True(t, colorResult.Deployed) - assert.FileExists(t, colorResult.Path) - - colorContent, err := os.ReadFile(colorResult.Path) - require.NoError(t, err) - assert.Contains(t, string(colorContent), "background = #101418") - }) - - t.Run("deploy ghostty config with existing file", func(t *testing.T) { - existingContent := "# Old config\nfont-size = 14\n" - ghosttyPath := getGhosttyPath() - err := os.MkdirAll(filepath.Dir(ghosttyPath), 0o755) - require.NoError(t, err) - err = os.WriteFile(ghosttyPath, []byte(existingContent), 0o644) - require.NoError(t, err) - - results, err := cd.deployGhosttyConfig() - require.NoError(t, err) - require.Len(t, results, 2) - - mainResult := results[0] - assert.Equal(t, "Ghostty", mainResult.ConfigType) - assert.True(t, mainResult.Deployed) - assert.NotEmpty(t, mainResult.BackupPath) - assert.FileExists(t, mainResult.Path) - assert.FileExists(t, mainResult.BackupPath) - - backupContent, err := os.ReadFile(mainResult.BackupPath) - require.NoError(t, err) - assert.Equal(t, existingContent, string(backupContent)) - - newContent, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.NotContains(t, string(newContent), "# Old config") - - colorResult := results[1] - assert.Equal(t, "Ghostty Colors", colorResult.ConfigType) - assert.True(t, colorResult.Deployed) - assert.FileExists(t, colorResult.Path) - }) -} - -func getGhosttyPath() string { - return filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config") -} - -func TestMergeHyprlandMonitorSections(t *testing.T) { - cd := &ConfigDeployer{} - - tests := []struct { - name string - newConfig string - existingConfig string - wantError bool - wantContains []string - wantNotContains []string - }{ - { - name: "no existing monitors", - newConfig: `# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 - -# ================== -# ENVIRONMENT VARS -# ================== -env = XDG_CURRENT_DESKTOP,niri`, - existingConfig: `# Some other config -input { - kb_layout = us -}`, - wantError: false, - wantContains: []string{"MONITOR CONFIG", "ENVIRONMENT VARS"}, - }, - { - name: "merge single monitor", - newConfig: `# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 - -# ================== -# ENVIRONMENT VARS -# ==================`, - existingConfig: `# My config -monitor = DP-1, 1920x1080@144, 0x0, 1 -input { - kb_layout = us -}`, - wantError: false, - wantContains: []string{ - "MONITOR CONFIG", - "monitor = DP-1, 1920x1080@144, 0x0, 1", - "Monitors from existing configuration", - }, - wantNotContains: []string{ - "monitor = eDP-2", // Example monitor should be removed - }, - }, - { - name: "merge multiple monitors", - newConfig: `# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 - -# ================== -# ENVIRONMENT VARS -# ==================`, - existingConfig: `monitor = DP-1, 1920x1080@144, 0x0, 1 -# monitor = HDMI-A-1, 1920x1080@60, 1920x0, 1 -monitor = eDP-1, 2560x1440@165, auto, 1.25`, - wantError: false, - wantContains: []string{ - "monitor = DP-1", - "# monitor = HDMI-A-1", // Commented monitor preserved - "monitor = eDP-1", - "Monitors from existing configuration", - }, - wantNotContains: []string{ - "monitor = eDP-2", // Example monitor should be removed - }, - }, - { - name: "preserve commented monitors", - newConfig: `# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 - -# ==================`, - existingConfig: `# monitor = DP-1, 1920x1080@144, 0x0, 1 -# monitor = HDMI-A-1, 1920x1080@60, 1920x0, 1`, - wantError: false, - wantContains: []string{ - "# monitor = DP-1", - "# monitor = HDMI-A-1", - "Monitors from existing configuration", - }, - }, - { - name: "no monitor config section", - newConfig: `# Some config without monitor section -input { - kb_layout = us -}`, - existingConfig: `monitor = DP-1, 1920x1080@144, 0x0, 1`, - wantError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tmpDir := t.TempDir() - result, err := cd.mergeHyprlandMonitorSections(tt.newConfig, tt.existingConfig, tmpDir) - - if tt.wantError { - assert.Error(t, err) - return - } - - require.NoError(t, err) - - for _, want := range tt.wantContains { - assert.Contains(t, result, want, "merged config should contain: %s", want) - } - - for _, notWant := range tt.wantNotContains { - assert.NotContains(t, result, notWant, "merged config should NOT contain: %s", notWant) - } - }) - } -} - -func TestHyprlandConfigDeployment(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-hyprland-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - t.Run("deploy hyprland config to empty directory", func(t *testing.T) { - result, err := cd.deployHyprlandConfig(deps.TerminalGhostty, true) - require.NoError(t, err) - - assert.Equal(t, "Hyprland", result.ConfigType) - assert.True(t, result.Deployed) - assert.Empty(t, result.BackupPath) - assert.FileExists(t, result.Path) - - content, err := os.ReadFile(result.Path) - require.NoError(t, err) - assert.Contains(t, string(content), "# MONITOR CONFIG") - assert.Contains(t, string(content), "source = ./dms/binds.conf") - assert.Contains(t, string(content), "exec-once = ") - }) - - t.Run("deploy hyprland config with existing monitors", func(t *testing.T) { - existingContent := `# My existing Hyprland config -monitor = DP-1, 1920x1080@144, 0x0, 1 -monitor = HDMI-A-1, 3840x2160@60, 1920x0, 1.5 - -general { - gaps_in = 10 -} -` - hyprPath := filepath.Join(tempDir, ".config", "hypr", "hyprland.conf") - err := os.MkdirAll(filepath.Dir(hyprPath), 0o755) - require.NoError(t, err) - err = os.WriteFile(hyprPath, []byte(existingContent), 0o644) - require.NoError(t, err) - - result, err := cd.deployHyprlandConfig(deps.TerminalKitty, true) - require.NoError(t, err) - - assert.Equal(t, "Hyprland", result.ConfigType) - assert.True(t, result.Deployed) - assert.NotEmpty(t, result.BackupPath) - assert.FileExists(t, result.Path) - assert.FileExists(t, result.BackupPath) - - backupContent, err := os.ReadFile(result.BackupPath) - require.NoError(t, err) - assert.Equal(t, existingContent, string(backupContent)) - - newContent, err := os.ReadFile(result.Path) - require.NoError(t, err) - assert.Contains(t, string(newContent), "monitor = DP-1, 1920x1080@144") - assert.Contains(t, string(newContent), "monitor = HDMI-A-1, 3840x2160@60") - assert.Contains(t, string(newContent), "source = ./dms/binds.conf") - assert.NotContains(t, string(newContent), "monitor = eDP-2") - }) -} - -func TestNiriConfigStructure(t *testing.T) { - assert.Contains(t, NiriConfig, "input {") - assert.Contains(t, NiriConfig, "layout {") - - assert.Contains(t, NiriBindsConfig, "binds {") - assert.Contains(t, NiriBindsConfig, `spawn "{{TERMINAL_COMMAND}}"`) -} - -func TestHyprlandConfigStructure(t *testing.T) { - assert.Contains(t, HyprlandConfig, "# MONITOR CONFIG") - assert.Contains(t, HyprlandConfig, "# STARTUP APPS") - assert.Contains(t, HyprlandConfig, "# INPUT CONFIG") - assert.Contains(t, HyprlandConfig, "source = ./dms/binds.conf") -} - -func TestGhosttyConfigStructure(t *testing.T) { - assert.Contains(t, GhosttyConfig, "window-decoration = false") - assert.Contains(t, GhosttyConfig, "background-opacity = 1.0") - assert.Contains(t, GhosttyConfig, "theme = dankcolors") -} - -func TestGhosttyColorConfigStructure(t *testing.T) { - assert.Contains(t, GhosttyColorConfig, "background = #101418") - assert.Contains(t, GhosttyColorConfig, "foreground = #e0e2e8") - assert.Contains(t, GhosttyColorConfig, "cursor-color = #9dcbfb") - assert.Contains(t, GhosttyColorConfig, "palette = 0=#101418") - assert.Contains(t, GhosttyColorConfig, "palette = 15=#ffffff") -} - -func TestKittyConfigStructure(t *testing.T) { - assert.Contains(t, KittyConfig, "font_size 12.0") - assert.Contains(t, KittyConfig, "window_padding_width 12") - assert.Contains(t, KittyConfig, "background_opacity 1.0") - assert.Contains(t, KittyConfig, "include dank-tabs.conf") - assert.Contains(t, KittyConfig, "include dank-theme.conf") -} - -func TestKittyThemeConfigStructure(t *testing.T) { - assert.Contains(t, KittyThemeConfig, "foreground #e0e2e8") - assert.Contains(t, KittyThemeConfig, "background #101418") - assert.Contains(t, KittyThemeConfig, "cursor #e0e2e8") - assert.Contains(t, KittyThemeConfig, "color0 #101418") - assert.Contains(t, KittyThemeConfig, "color15 #ffffff") -} - -func TestKittyTabsConfigStructure(t *testing.T) { - assert.Contains(t, KittyTabsConfig, "tab_bar_style powerline") - assert.Contains(t, KittyTabsConfig, "tab_powerline_style slanted") - assert.Contains(t, KittyTabsConfig, "active_tab_background #124a73") - assert.Contains(t, KittyTabsConfig, "inactive_tab_background #101418") -} - -func TestAlacrittyConfigStructure(t *testing.T) { - assert.Contains(t, AlacrittyConfig, "[general]") - assert.Contains(t, AlacrittyConfig, "~/.config/alacritty/dank-theme.toml") - assert.Contains(t, AlacrittyConfig, "[window]") - assert.Contains(t, AlacrittyConfig, "decorations = \"None\"") - assert.Contains(t, AlacrittyConfig, "padding = { x = 12, y = 12 }") - assert.Contains(t, AlacrittyConfig, "[cursor]") - assert.Contains(t, AlacrittyConfig, "[keyboard]") -} - -func TestAlacrittyThemeConfigStructure(t *testing.T) { - assert.Contains(t, AlacrittyThemeConfig, "[colors.primary]") - assert.Contains(t, AlacrittyThemeConfig, "background = '#101418'") - assert.Contains(t, AlacrittyThemeConfig, "foreground = '#e0e2e8'") - assert.Contains(t, AlacrittyThemeConfig, "[colors.cursor]") - assert.Contains(t, AlacrittyThemeConfig, "cursor = '#9dcbfb'") - assert.Contains(t, AlacrittyThemeConfig, "[colors.normal]") - assert.Contains(t, AlacrittyThemeConfig, "[colors.bright]") -} - -func TestKittyConfigDeployment(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-kitty-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - t.Run("deploy kitty config to empty directory", func(t *testing.T) { - results, err := cd.deployKittyConfig() - require.NoError(t, err) - require.Len(t, results, 3) - - mainResult := results[0] - assert.Equal(t, "Kitty", mainResult.ConfigType) - assert.True(t, mainResult.Deployed) - assert.FileExists(t, mainResult.Path) - - content, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.Contains(t, string(content), "include dank-theme.conf") - - themeResult := results[1] - assert.Equal(t, "Kitty Theme", themeResult.ConfigType) - assert.True(t, themeResult.Deployed) - assert.FileExists(t, themeResult.Path) - - tabsResult := results[2] - assert.Equal(t, "Kitty Tabs", tabsResult.ConfigType) - assert.True(t, tabsResult.Deployed) - assert.FileExists(t, tabsResult.Path) - }) -} - -func TestAlacrittyConfigDeployment(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-alacritty-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - t.Run("deploy alacritty config to empty directory", func(t *testing.T) { - results, err := cd.deployAlacrittyConfig() - require.NoError(t, err) - require.Len(t, results, 2) - - mainResult := results[0] - assert.Equal(t, "Alacritty", mainResult.ConfigType) - assert.True(t, mainResult.Deployed) - assert.FileExists(t, mainResult.Path) - - content, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.Contains(t, string(content), "~/.config/alacritty/dank-theme.toml") - assert.Contains(t, string(content), "[window]") - - themeResult := results[1] - assert.Equal(t, "Alacritty Theme", themeResult.ConfigType) - assert.True(t, themeResult.Deployed) - assert.FileExists(t, themeResult.Path) - - themeContent, err := os.ReadFile(themeResult.Path) - require.NoError(t, err) - assert.Contains(t, string(themeContent), "[colors.primary]") - assert.Contains(t, string(themeContent), "background = '#101418'") - }) - - t.Run("deploy alacritty config with existing file", func(t *testing.T) { - existingContent := "# Old alacritty config\n[window]\nopacity = 0.9\n" - alacrittyPath := filepath.Join(tempDir, ".config", "alacritty", "alacritty.toml") - err := os.MkdirAll(filepath.Dir(alacrittyPath), 0o755) - require.NoError(t, err) - err = os.WriteFile(alacrittyPath, []byte(existingContent), 0o644) - require.NoError(t, err) - - results, err := cd.deployAlacrittyConfig() - require.NoError(t, err) - require.Len(t, results, 2) - - mainResult := results[0] - assert.True(t, mainResult.Deployed) - assert.NotEmpty(t, mainResult.BackupPath) - assert.FileExists(t, mainResult.BackupPath) - - backupContent, err := os.ReadFile(mainResult.BackupPath) - require.NoError(t, err) - assert.Equal(t, existingContent, string(backupContent)) - - newContent, err := os.ReadFile(mainResult.Path) - require.NoError(t, err) - assert.NotContains(t, string(newContent), "# Old alacritty config") - assert.Contains(t, string(newContent), "decorations = \"None\"") - }) -} - -func TestShouldReplaceConfigDeployIfMissing(t *testing.T) { - allFalse := map[string]bool{ - "Niri": false, - "Hyprland": false, - "Ghostty": false, - "Kitty": false, - "Alacritty": false, - } - - t.Run("replaceConfigs nil deploys config", func(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-replace-nil-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - results, err := cd.DeployConfigurationsSelectiveWithReinstalls( - context.Background(), - deps.WindowManagerNiri, - deps.TerminalGhostty, - nil, // installedDeps - nil, // replaceConfigs - nil, // reinstallItems - ) - require.NoError(t, err) - - // With replaceConfigs=nil, all configs should be deployed - hasDeployed := false - for _, r := range results { - if r.Deployed { - hasDeployed = true - break - } - } - assert.True(t, hasDeployed, "expected at least one config to be deployed when replaceConfigs is nil") - }) - - t.Run("replaceConfigs all false and config missing deploys config", func(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-replace-missing-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - results, err := cd.DeployConfigurationsSelectiveWithReinstalls( - context.Background(), - deps.WindowManagerNiri, - deps.TerminalGhostty, - nil, // installedDeps - allFalse, // replaceConfigs — all false - nil, // reinstallItems - ) - require.NoError(t, err) - - // Config files don't exist on disk, so they should still be deployed - hasDeployed := false - for _, r := range results { - if r.Deployed { - hasDeployed = true - break - } - } - assert.True(t, hasDeployed, "expected configs to be deployed when files are missing, even with replaceConfigs all false") - }) - - t.Run("replaceConfigs false and config exists skips config", func(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-replace-exists-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - // Create the Ghostty primary config file so shouldReplaceConfig returns false - ghosttyPath := filepath.Join(tempDir, ".config", "ghostty", "config") - err = os.MkdirAll(filepath.Dir(ghosttyPath), 0o755) - require.NoError(t, err) - err = os.WriteFile(ghosttyPath, []byte("# existing ghostty config\n"), 0o644) - require.NoError(t, err) - - // Also create the Niri primary config file - niriPath := filepath.Join(tempDir, ".config", "niri", "config.kdl") - err = os.MkdirAll(filepath.Dir(niriPath), 0o755) - require.NoError(t, err) - err = os.WriteFile(niriPath, []byte("// existing niri config\n"), 0o644) - require.NoError(t, err) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - results, err := cd.DeployConfigurationsSelectiveWithReinstalls( - context.Background(), - deps.WindowManagerNiri, - deps.TerminalGhostty, - nil, // installedDeps - allFalse, // replaceConfigs — all false - nil, // reinstallItems - ) - require.NoError(t, err) - - // Both Niri and Ghostty config files exist, so with all false they should be skipped - for _, r := range results { - assert.Fail(t, "expected no configs to be deployed", "got deployed config: %s", r.ConfigType) - } - }) - - t.Run("replaceConfigs true and config exists deploys config", func(t *testing.T) { - tempDir, err := os.MkdirTemp("", "dankinstall-replace-true-test") - require.NoError(t, err) - defer os.RemoveAll(tempDir) - - originalHome := os.Getenv("HOME") - os.Setenv("HOME", tempDir) - defer os.Setenv("HOME", originalHome) - - // Create the Ghostty primary config file - ghosttyPath := filepath.Join(tempDir, ".config", "ghostty", "config") - err = os.MkdirAll(filepath.Dir(ghosttyPath), 0o755) - require.NoError(t, err) - err = os.WriteFile(ghosttyPath, []byte("# existing ghostty config\n"), 0o644) - require.NoError(t, err) - - logChan := make(chan string, 100) - cd := NewConfigDeployer(logChan) - - replaceConfigs := map[string]bool{ - "Niri": false, - "Hyprland": false, - "Ghostty": true, // explicitly true - "Kitty": false, - "Alacritty": false, - } - - results, err := cd.DeployConfigurationsSelectiveWithReinstalls( - context.Background(), - deps.WindowManagerNiri, - deps.TerminalGhostty, - nil, // installedDeps - replaceConfigs, // Ghostty=true, rest=false - nil, // reinstallItems - ) - require.NoError(t, err) - - // Ghostty should be deployed because replaceConfigs["Ghostty"]=true - foundGhostty := false - for _, r := range results { - if r.ConfigType == "Ghostty" && r.Deployed { - foundGhostty = true - } - } - assert.True(t, foundGhostty, "expected Ghostty config to be deployed when replaceConfigs is true") - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/dms.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/dms.go deleted file mode 100644 index 7842fa5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/dms.go +++ /dev/null @@ -1,57 +0,0 @@ -package config - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -func LocateDMSConfig() (string, error) { - var primaryPaths []string - - configHome, err := os.UserConfigDir() - if err == nil && configHome != "" { - primaryPaths = append(primaryPaths, filepath.Join(configHome, "quickshell", "dms")) - } - - // System data directories - dataDirs := os.Getenv("XDG_DATA_DIRS") - if dataDirs == "" { - dataDirs = "/usr/local/share:/usr/share" - } - - for dir := range strings.SplitSeq(dataDirs, ":") { - if dir != "" { - primaryPaths = append(primaryPaths, filepath.Join(dir, "quickshell", "dms")) - } - } - - // System config directories (fallback) - configDirs := os.Getenv("XDG_CONFIG_DIRS") - if configDirs == "" { - configDirs = "/etc/xdg" - } - - for dir := range strings.SplitSeq(configDirs, ":") { - if dir != "" { - primaryPaths = append(primaryPaths, filepath.Join(dir, "quickshell", "dms")) - } - } - - // Build search paths with secondary (monorepo) paths interleaved - var searchPaths []string - for _, path := range primaryPaths { - searchPaths = append(searchPaths, path) - searchPaths = append(searchPaths, filepath.Join(path, "quickshell")) - } - - for _, path := range searchPaths { - shellPath := filepath.Join(path, "shell.qml") - if info, err := os.Stat(shellPath); err == nil && !info.IsDir() { - return path, nil - } - } - - return "", fmt.Errorf("could not find DMS config (shell.qml) in any valid config path") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty-theme.toml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty-theme.toml deleted file mode 100644 index 4048457..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty-theme.toml +++ /dev/null @@ -1,31 +0,0 @@ -[colors.primary] -background = '#101418' -foreground = '#e0e2e8' - -[colors.selection] -text = '#e0e2e8' -background = '#124a73' - -[colors.cursor] -text = '#101418' -cursor = '#9dcbfb' - -[colors.normal] -black = '#101418' -red = '#d75a59' -green = '#8ed88c' -yellow = '#e0d99d' -blue = '#4087bc' -magenta = '#839fbc' -cyan = '#9dcbfb' -white = '#abb2bf' - -[colors.bright] -black = '#5c6370' -red = '#e57e7e' -green = '#a2e5a0' -yellow = '#efe9b3' -blue = '#a7d9ff' -magenta = '#3d8197' -cyan = '#5c7ba3' -white = '#ffffff' diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty.toml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty.toml deleted file mode 100644 index b666177..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/alacritty.toml +++ /dev/null @@ -1,37 +0,0 @@ -[general] -import = [ - "~/.config/alacritty/dank-theme.toml" -] - -[window] -decorations = "None" -padding = { x = 12, y = 12 } -opacity = 1.0 - -[scrolling] -history = 3023 - -[cursor] -style = { shape = "Block", blinking = "On" } -blink_interval = 500 -unfocused_hollow = true - -[mouse] -hide_when_typing = true - -[selection] -save_to_clipboard = false - -[bell] -duration = 0 - -[keyboard] -bindings = [ - { key = "C", mods = "Control|Shift", action = "Copy" }, - { key = "V", mods = "Control|Shift", action = "Paste" }, - { key = "N", mods = "Control|Shift", action = "SpawnNewInstance" }, - { key = "Equals", mods = "Control|Shift", action = "IncreaseFontSize" }, - { key = "Minus", mods = "Control", action = "DecreaseFontSize" }, - { key = "Key0", mods = "Control", action = "ResetFontSize" }, - { key = "Enter", mods = "Shift", chars = "\n" }, -] diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty-colors.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty-colors.conf deleted file mode 100644 index 56dca5f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty-colors.conf +++ /dev/null @@ -1,21 +0,0 @@ -background = #101418 -foreground = #e0e2e8 -cursor-color = #9dcbfb -selection-background = #124a73 -selection-foreground = #e0e2e8 -palette = 0=#101418 -palette = 1=#d75a59 -palette = 2=#8ed88c -palette = 3=#e0d99d -palette = 4=#4087bc -palette = 5=#839fbc -palette = 6=#9dcbfb -palette = 7=#abb2bf -palette = 8=#5c6370 -palette = 9=#e57e7e -palette = 10=#a2e5a0 -palette = 11=#efe9b3 -palette = 12=#a7d9ff -palette = 13=#3d8197 -palette = 14=#5c7ba3 -palette = 15=#ffffff diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty.conf deleted file mode 100644 index 8ca05a2..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/ghostty.conf +++ /dev/null @@ -1,51 +0,0 @@ -# Font Configuration -font-size = 12 - -# Window Configuration -window-decoration = false -window-padding-x = 12 -window-padding-y = 12 -background-opacity = 1.0 -background-blur-radius = 32 - -# Cursor Configuration -cursor-style = block -cursor-style-blink = true - -# Scrollback -scrollback-limit = 3023 - -# Terminal features -mouse-hide-while-typing = true -copy-on-select = false -confirm-close-surface = false - -# Disable annoying copied to clipboard -app-notifications = no-clipboard-copy,no-config-reload - -# Key bindings for common actions -#keybind = ctrl+c=copy_to_clipboard -#keybind = ctrl+v=paste_from_clipboard -keybind = ctrl+shift+n=new_window -keybind = ctrl+t=new_tab -keybind = ctrl+plus=increase_font_size:1 -keybind = ctrl+minus=decrease_font_size:1 -keybind = ctrl+zero=reset_font_size - -# Material 3 UI elements -unfocused-split-opacity = 0.7 -unfocused-split-fill = #44464f - -# Tab configuration -gtk-titlebar = false - -# Shell integration -shell-integration = detect -shell-integration-features = cursor,sudo,title,no-cursor -keybind = shift+enter=text:\n - -# Rando stuff -gtk-single-instance = true - -# Dank color generation -theme = dankcolors diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-binds.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-binds.conf deleted file mode 100644 index dfe5630..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-binds.conf +++ /dev/null @@ -1,162 +0,0 @@ -# === Application Launchers === -bind = SUPER, T, exec, {{TERMINAL_COMMAND}} -bind = SUPER, space, exec, dms ipc call spotlight toggle -bind = SUPER, V, exec, dms ipc call clipboard toggle -bind = SUPER, M, exec, dms ipc call processlist focusOrToggle -bind = SUPER, comma, exec, dms ipc call settings focusOrToggle -bind = SUPER, N, exec, dms ipc call notifications toggle -bind = SUPER SHIFT, N, exec, dms ipc call notepad toggle -bind = SUPER, Y, exec, dms ipc call dankdash wallpaper -bind = SUPER, TAB, exec, dms ipc call hypr toggleOverview -bind = SUPER, X, exec, dms ipc call powermenu toggle - -# === Cheat sheet -bind = SUPER SHIFT, Slash, exec, dms ipc call keybinds toggle hyprland - -# === Security === -bind = SUPER ALT, L, exec, dms ipc call lock lock -bind = SUPER SHIFT, E, exit -bind = CTRL ALT, Delete, exec, dms ipc call processlist focusOrToggle - -# === Audio Controls === -bindel = , XF86AudioRaiseVolume, exec, dms ipc call audio increment 3 -bindel = , XF86AudioLowerVolume, exec, dms ipc call audio decrement 3 -bindl = , XF86AudioMute, exec, dms ipc call audio mute -bindl = , XF86AudioMicMute, exec, dms ipc call audio micmute -bindl = , XF86AudioPause, exec, dms ipc call mpris playPause -bindl = , XF86AudioPlay, exec, dms ipc call mpris playPause -bindl = , XF86AudioPrev, exec, dms ipc call mpris previous -bindl = , XF86AudioNext, exec, dms ipc call mpris next -bindel = CTRL, XF86AudioRaiseVolume, exec, dms ipc call mpris increment 3 -bindel = CTRL, XF86AudioLowerVolume, exec, dms ipc call mpris decrement 3 - -# === Brightness Controls === -bindel = , XF86MonBrightnessUp, exec, dms ipc call brightness increment 5 "" -bindel = , XF86MonBrightnessDown, exec, dms ipc call brightness decrement 5 "" - -# === Window Management === -bind = SUPER, Q, killactive -bind = SUPER, F, fullscreen, 1 -bind = SUPER SHIFT, F, fullscreen, 0 -bind = SUPER SHIFT, T, togglefloating -bind = SUPER, W, togglegroup -bind = SUPER SHIFT, W, exec, dms ipc call window-rules toggle - -# === Focus Navigation === -bind = SUPER, left, movefocus, l -bind = SUPER, down, movefocus, d -bind = SUPER, up, movefocus, u -bind = SUPER, right, movefocus, r -bind = SUPER, H, movefocus, l -bind = SUPER, J, movefocus, d -bind = SUPER, K, movefocus, u -bind = SUPER, L, movefocus, r - -# === Window Movement === -bind = SUPER SHIFT, left, movewindow, l -bind = SUPER SHIFT, down, movewindow, d -bind = SUPER SHIFT, up, movewindow, u -bind = SUPER SHIFT, right, movewindow, r -bind = SUPER SHIFT, H, movewindow, l -bind = SUPER SHIFT, J, movewindow, d -bind = SUPER SHIFT, K, movewindow, u -bind = SUPER SHIFT, L, movewindow, r - -# === Column Navigation === -bind = SUPER, Home, focuswindow, first -bind = SUPER, End, focuswindow, last - -# === Monitor Navigation === -bind = SUPER CTRL, left, focusmonitor, l -bind = SUPER CTRL, right, focusmonitor, r -bind = SUPER CTRL, H, focusmonitor, l -bind = SUPER CTRL, J, focusmonitor, d -bind = SUPER CTRL, K, focusmonitor, u -bind = SUPER CTRL, L, focusmonitor, r - -# === Move to Monitor === -bind = SUPER SHIFT CTRL, left, movewindow, mon:l -bind = SUPER SHIFT CTRL, down, movewindow, mon:d -bind = SUPER SHIFT CTRL, up, movewindow, mon:u -bind = SUPER SHIFT CTRL, right, movewindow, mon:r -bind = SUPER SHIFT CTRL, H, movewindow, mon:l -bind = SUPER SHIFT CTRL, J, movewindow, mon:d -bind = SUPER SHIFT CTRL, K, movewindow, mon:u -bind = SUPER SHIFT CTRL, L, movewindow, mon:r - -# === Workspace Navigation === -bind = SUPER, Page_Down, workspace, e+1 -bind = SUPER, Page_Up, workspace, e-1 -bind = SUPER, U, workspace, e+1 -bind = SUPER, I, workspace, e-1 -bind = SUPER CTRL, down, movetoworkspace, e+1 -bind = SUPER CTRL, up, movetoworkspace, e-1 -bind = SUPER CTRL, U, movetoworkspace, e+1 -bind = SUPER CTRL, I, movetoworkspace, e-1 - -# === Workspace Management === -bind = CTRL SHIFT, R, exec, dms ipc call workspace-rename open - -# === Move Workspaces === -bind = SUPER SHIFT, Page_Down, movetoworkspace, e+1 -bind = SUPER SHIFT, Page_Up, movetoworkspace, e-1 -bind = SUPER SHIFT, U, movetoworkspace, e+1 -bind = SUPER SHIFT, I, movetoworkspace, e-1 - -# === Mouse Wheel Navigation === -bind = SUPER, mouse_down, workspace, e+1 -bind = SUPER, mouse_up, workspace, e-1 -bind = SUPER CTRL, mouse_down, movetoworkspace, e+1 -bind = SUPER CTRL, mouse_up, movetoworkspace, e-1 - -# === Numbered Workspaces === -bind = SUPER, 1, workspace, 1 -bind = SUPER, 2, workspace, 2 -bind = SUPER, 3, workspace, 3 -bind = SUPER, 4, workspace, 4 -bind = SUPER, 5, workspace, 5 -bind = SUPER, 6, workspace, 6 -bind = SUPER, 7, workspace, 7 -bind = SUPER, 8, workspace, 8 -bind = SUPER, 9, workspace, 9 - -# === Move to Numbered Workspaces === -bind = SUPER SHIFT, 1, movetoworkspace, 1 -bind = SUPER SHIFT, 2, movetoworkspace, 2 -bind = SUPER SHIFT, 3, movetoworkspace, 3 -bind = SUPER SHIFT, 4, movetoworkspace, 4 -bind = SUPER SHIFT, 5, movetoworkspace, 5 -bind = SUPER SHIFT, 6, movetoworkspace, 6 -bind = SUPER SHIFT, 7, movetoworkspace, 7 -bind = SUPER SHIFT, 8, movetoworkspace, 8 -bind = SUPER SHIFT, 9, movetoworkspace, 9 - -# === Column Management === -bind = SUPER, bracketleft, layoutmsg, preselect l -bind = SUPER, bracketright, layoutmsg, preselect r - -# === Sizing & Layout === -bind = SUPER, R, layoutmsg, togglesplit -bind = SUPER CTRL, F, resizeactive, exact 100% 100% - -# === Move/resize windows with mainMod + LMB/RMB and dragging === -bindmd = SUPER, mouse:272, Move window, movewindow -bindmd = SUPER, mouse:273, Resize window, resizewindow - -# === Move/resize windows with mainMod + LMB/RMB and dragging === -bindd = SUPER, code:20, Expand window left, resizeactive, -100 0 -bindd = SUPER, code:21, Shrink window left, resizeactive, 100 0 - -# === Manual Sizing === -binde = SUPER, minus, resizeactive, -10% 0 -binde = SUPER, equal, resizeactive, 10% 0 -binde = SUPER SHIFT, minus, resizeactive, 0 -10% -binde = SUPER SHIFT, equal, resizeactive, 0 10% - -# === Screenshots === -bind = , Print, exec, dms screenshot -bind = CTRL, Print, exec, dms screenshot full -bind = ALT, Print, exec, dms screenshot window - -# === System Controls === -bind = SUPER SHIFT, P, dpms, toggle diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-colors.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-colors.conf deleted file mode 100644 index 38bcb0e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-colors.conf +++ /dev/null @@ -1,25 +0,0 @@ -# ! Auto-generated file. Do not edit directly. -# Remove source = ./dms/colors.conf from your config to override. - -$primary = rgb(d0bcff) -$outline = rgb(948f99) -$error = rgb(f2b8b5) - -general { - col.active_border = $primary - col.inactive_border = $outline -} - -group { - col.border_active = $primary - col.border_inactive = $outline - col.border_locked_active = $error - col.border_locked_inactive = $outline - - groupbar { - col.active = $primary - col.inactive = $outline - col.locked_active = $error - col.locked_inactive = $outline - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-layout.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-layout.conf deleted file mode 100644 index b9a8ad9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hypr-layout.conf +++ /dev/null @@ -1,11 +0,0 @@ -# Auto-generated by DMS - do not edit manually - -general { - gaps_in = 4 - gaps_out = 4 - border_size = 2 -} - -decoration { - rounding = 12 -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hyprland.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hyprland.conf deleted file mode 100644 index f4e7a03..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/hyprland.conf +++ /dev/null @@ -1,121 +0,0 @@ -# Hyprland Configuration -# https://wiki.hypr.land/Configuring/ - -# ================== -# MONITOR CONFIG -# ================== -# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 -monitor = , preferred,auto,auto - -# ================== -# STARTUP APPS -# ================== -exec-once = dbus-update-activation-environment --systemd --all -exec-once = systemctl --user start hyprland-session.target - -# ================== -# INPUT CONFIG -# ================== -input { - kb_layout = us - numlock_by_default = true -} - -# ================== -# GENERAL LAYOUT -# ================== -general { - gaps_in = 5 - gaps_out = 5 - border_size = 2 - - layout = dwindle -} - -# ================== -# DECORATION -# ================== -decoration { - rounding = 12 - - active_opacity = 1.0 - inactive_opacity = 1.0 - - shadow { - enabled = true - range = 30 - render_power = 5 - offset = 0 5 - color = rgba(00000070) - } -} - -# ================== -# ANIMATIONS -# ================== -animations { - enabled = true - - animation = windowsIn, 1, 3, default - animation = windowsOut, 1, 3, default - animation = workspaces, 1, 5, default - animation = windowsMove, 1, 4, default - animation = fade, 1, 3, default - animation = border, 1, 3, default -} - -# ================== -# LAYOUTS -# ================== -dwindle { - preserve_split = true -} - -master { - mfact = 0.5 -} - -# ================== -# MISC -# ================== -misc { - disable_hyprland_logo = true - disable_splash_rendering = true -} - -# ================== -# WINDOW RULES -# ================== -windowrule = tile on, match:class ^(org\.wezfurlong\.wezterm)$ - -windowrule = rounding 12, match:class ^(org\.gnome\.) - -windowrule = tile on, match:class ^(gnome-control-center)$ -windowrule = tile on, match:class ^(pavucontrol)$ -windowrule = tile on, match:class ^(nm-connection-editor)$ - -windowrule = float on, match:class ^(org\.gnome\.Calculator)$ -windowrule = float on, match:class ^(gnome-calculator)$ -windowrule = float on, match:class ^(galculator)$ -windowrule = float on, match:class ^(blueman-manager)$ -windowrule = float on, match:class ^(org\.gnome\.Nautilus)$ -windowrule = float on, match:class ^(xdg-desktop-portal)$ - -windowrule = no_initial_focus on, match:class ^(steam)$, match:title ^(notificationtoasts) -windowrule = pin on, match:class ^(steam)$, match:title ^(notificationtoasts) - -windowrule = float on, match:class ^(firefox)$, match:title ^(Picture-in-Picture)$ -windowrule = float on, match:class ^(zoom)$ - -# DMS windows floating by default -# ! Hyprland doesn't size these windows correctly so disabling by default here -# windowrule = float on, match:class ^(org.quickshell)$ - -layerrule = no_anim on, match:namespace ^(quickshell)$ -layerrule = no_anim on, match:namespace ^dms:.* - -source = ./dms/colors.conf -source = ./dms/outputs.conf -source = ./dms/layout.conf -source = ./dms/cursor.conf -source = ./dms/binds.conf diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-tabs.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-tabs.conf deleted file mode 100644 index b2305c8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-tabs.conf +++ /dev/null @@ -1,24 +0,0 @@ -tab_bar_edge top -tab_bar_style powerline -tab_powerline_style slanted -tab_bar_align left -tab_bar_min_tabs 2 -tab_bar_margin_width 0.0 -tab_bar_margin_height 2.5 1.5 -tab_bar_margin_color #101418 - -tab_bar_background #101418 - -active_tab_foreground #cfe5ff -active_tab_background #124a73 -active_tab_font_style bold - -inactive_tab_foreground #c2c7cf -inactive_tab_background #101418 -inactive_tab_font_style normal - -tab_activity_symbol " ● " -tab_numbers_style 1 - -tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title[:30]}{title[30:] and '…'} [{index}]" -active_tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title[:30]}{title[30:] and '…'} [{index}]" diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-theme.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-theme.conf deleted file mode 100644 index 2587bcf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty-theme.conf +++ /dev/null @@ -1,24 +0,0 @@ -cursor #e0e2e8 -cursor_text_color #c2c7cf - -foreground #e0e2e8 -background #101418 -selection_foreground #243240 -selection_background #b9c8da -url_color #9dcbfb -color0 #101418 -color1 #d75a59 -color2 #8ed88c -color3 #e0d99d -color4 #4087bc -color5 #839fbc -color6 #9dcbfb -color7 #abb2bf -color8 #5c6370 -color9 #e57e7e -color10 #a2e5a0 -color11 #efe9b3 -color12 #a7d9ff -color13 #3d8197 -color14 #5c7ba3 -color15 #ffffff diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty.conf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty.conf deleted file mode 100644 index 2ae96a0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/kitty.conf +++ /dev/null @@ -1,37 +0,0 @@ -# Font Configuration -font_size 12.0 - -# Window Configuration -window_padding_width 12 -background_opacity 1.0 -background_blur 32 -hide_window_decorations yes - -# Cursor Configuration -cursor_shape block -cursor_blink_interval 1 - -# Scrollback -scrollback_lines 3000 - -# Terminal features -copy_on_select yes -strip_trailing_spaces smart - -# Key bindings for common actions -map ctrl+shift+n new_window -map ctrl+t new_tab -map ctrl+plus change_font_size all +1.0 -map ctrl+minus change_font_size all -1.0 -map ctrl+0 change_font_size all 0 - -# Tab configuration -tab_bar_style powerline -tab_bar_align left - -# Shell integration -shell_integration enabled - -# Dank color generation -include dank-tabs.conf -include dank-theme.conf diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-alttab.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-alttab.kdl deleted file mode 100644 index 5f9bdcd..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-alttab.kdl +++ /dev/null @@ -1,10 +0,0 @@ -// ! DO NOT EDIT ! -// ! AUTO-GENERATED BY DMS ! -// ! CHANGES WILL BE OVERWRITTEN ! -// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE ! - -recent-windows { - highlight { - corner-radius 12 - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-binds.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-binds.kdl deleted file mode 100644 index 27cb860..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-binds.kdl +++ /dev/null @@ -1,221 +0,0 @@ -binds { - // === System & Overview === - Mod+D repeat=false { toggle-overview; } - Mod+Tab repeat=false { toggle-overview; } - Mod+Shift+Slash { show-hotkey-overlay; } - - // === Application Launchers === - Mod+T hotkey-overlay-title="Open Terminal" { spawn "{{TERMINAL_COMMAND}}"; } - Mod+Space hotkey-overlay-title="Application Launcher" { - spawn "dms" "ipc" "call" "spotlight" "toggle"; - } - Mod+V hotkey-overlay-title="Clipboard Manager" { - spawn "dms" "ipc" "call" "clipboard" "toggle"; - } - Mod+M hotkey-overlay-title="Task Manager" { - spawn "dms" "ipc" "call" "processlist" "focusOrToggle"; - } - - Super+X hotkey-overlay-title="Power Menu: Toggle" { spawn "dms" "ipc" "call" "powermenu" "toggle"; } - Mod+Comma hotkey-overlay-title="Settings" { - spawn "dms" "ipc" "call" "settings" "focusOrToggle"; - } - Mod+Y hotkey-overlay-title="Browse Wallpapers" { - spawn "dms" "ipc" "call" "dankdash" "wallpaper"; - } - Mod+N hotkey-overlay-title="Notification Center" { spawn "dms" "ipc" "call" "notifications" "toggle"; } - Mod+Shift+N hotkey-overlay-title="Notepad" { spawn "dms" "ipc" "call" "notepad" "toggle"; } - - // === Security === - Mod+Alt+L hotkey-overlay-title="Lock Screen" { - spawn "dms" "ipc" "call" "lock" "lock"; - } - Mod+Shift+E { quit; } - Ctrl+Alt+Delete hotkey-overlay-title="Task Manager" { - spawn "dms" "ipc" "call" "processlist" "focusOrToggle"; - } - - // === Audio Controls === - XF86AudioRaiseVolume allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "increment" "3"; - } - XF86AudioLowerVolume allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "decrement" "3"; - } - XF86AudioMute allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "mute"; - } - XF86AudioMicMute allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "micmute"; - } - XF86AudioPause allow-when-locked=true { - spawn "dms" "ipc" "call" "mpris" "playPause"; - } - XF86AudioPlay allow-when-locked=true { - spawn "dms" "ipc" "call" "mpris" "playPause"; - } - XF86AudioPrev allow-when-locked=true { - spawn "dms" "ipc" "call" "mpris" "previous"; - } - XF86AudioNext allow-when-locked=true { - spawn "dms" "ipc" "call" "mpris" "next"; - } - Ctrl+XF86AudioRaiseVolume allow-when-locked=true { - spawn "dms" "ipc" "call" "mpris" "increment" "3"; - } - Ctrl+XF86AudioLowerVolume allow-when-locked=true { - spawn "dms" "ipc" "call" "mpris" "decrement" "3"; - } - - // === Brightness Controls === - XF86MonBrightnessUp allow-when-locked=true { - spawn "dms" "ipc" "call" "brightness" "increment" "5" ""; - } - XF86MonBrightnessDown allow-when-locked=true { - spawn "dms" "ipc" "call" "brightness" "decrement" "5" ""; - } - - // === Window Management === - Mod+Q repeat=false { close-window; } - Mod+F { maximize-column; } - Mod+Shift+F { fullscreen-window; } - Mod+Shift+T { toggle-window-floating; } - Mod+Shift+V { switch-focus-between-floating-and-tiling; } - Mod+W { toggle-column-tabbed-display; } - Mod+Shift+W hotkey-overlay-title="Create window rule" { spawn "dms" "ipc" "call" "window-rules" "toggle"; } - - // === Focus Navigation === - Mod+Left { focus-column-left; } - Mod+Down { focus-window-down; } - Mod+Up { focus-window-up; } - Mod+Right { focus-column-right; } - Mod+H { focus-column-left; } - Mod+J { focus-window-down; } - Mod+K { focus-window-up; } - Mod+L { focus-column-right; } - - // === Window Movement === - Mod+Shift+Left { move-column-left; } - Mod+Shift+Down { move-window-down; } - Mod+Shift+Up { move-window-up; } - Mod+Shift+Right { move-column-right; } - Mod+Shift+H { move-column-left; } - Mod+Shift+J { move-window-down; } - Mod+Shift+K { move-window-up; } - Mod+Shift+L { move-column-right; } - - // === Column Navigation === - Mod+Home { focus-column-first; } - Mod+End { focus-column-last; } - Mod+Ctrl+Home { move-column-to-first; } - Mod+Ctrl+End { move-column-to-last; } - - // === Monitor Navigation === - Mod+Ctrl+Left { focus-monitor-left; } - //Mod+Ctrl+Down { focus-monitor-down; } - //Mod+Ctrl+Up { focus-monitor-up; } - Mod+Ctrl+Right { focus-monitor-right; } - Mod+Ctrl+H { focus-monitor-left; } - Mod+Ctrl+J { focus-monitor-down; } - Mod+Ctrl+K { focus-monitor-up; } - Mod+Ctrl+L { focus-monitor-right; } - - // === Move to Monitor === - Mod+Shift+Ctrl+Left { move-column-to-monitor-left; } - Mod+Shift+Ctrl+Down { move-column-to-monitor-down; } - Mod+Shift+Ctrl+Up { move-column-to-monitor-up; } - Mod+Shift+Ctrl+Right { move-column-to-monitor-right; } - Mod+Shift+Ctrl+H { move-column-to-monitor-left; } - Mod+Shift+Ctrl+J { move-column-to-monitor-down; } - Mod+Shift+Ctrl+K { move-column-to-monitor-up; } - Mod+Shift+Ctrl+L { move-column-to-monitor-right; } - - // === Workspace Navigation === - Mod+Page_Down { focus-workspace-down; } - Mod+Page_Up { focus-workspace-up; } - Mod+U { focus-workspace-down; } - Mod+I { focus-workspace-up; } - Mod+Ctrl+Down { move-column-to-workspace-down; } - Mod+Ctrl+Up { move-column-to-workspace-up; } - Mod+Ctrl+U { move-column-to-workspace-down; } - Mod+Ctrl+I { move-column-to-workspace-up; } - - // === Workspace Management === - Ctrl+Shift+R hotkey-overlay-title="Rename Workspace" { - spawn "dms" "ipc" "call" "workspace-rename" "open"; - } - - // === Move Workspaces === - Mod+Shift+Page_Down { move-workspace-down; } - Mod+Shift+Page_Up { move-workspace-up; } - Mod+Shift+U { move-workspace-down; } - Mod+Shift+I { move-workspace-up; } - - // === Mouse Wheel Navigation === - Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; } - Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; } - Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; } - Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; } - - Mod+WheelScrollRight { focus-column-right; } - Mod+WheelScrollLeft { focus-column-left; } - Mod+Ctrl+WheelScrollRight { move-column-right; } - Mod+Ctrl+WheelScrollLeft { move-column-left; } - - Mod+Shift+WheelScrollDown { focus-column-right; } - Mod+Shift+WheelScrollUp { focus-column-left; } - Mod+Ctrl+Shift+WheelScrollDown { move-column-right; } - Mod+Ctrl+Shift+WheelScrollUp { move-column-left; } - - // === Numbered Workspaces === - Mod+1 { focus-workspace 1; } - Mod+2 { focus-workspace 2; } - Mod+3 { focus-workspace 3; } - Mod+4 { focus-workspace 4; } - Mod+5 { focus-workspace 5; } - Mod+6 { focus-workspace 6; } - Mod+7 { focus-workspace 7; } - Mod+8 { focus-workspace 8; } - Mod+9 { focus-workspace 9; } - - // === Move to Numbered Workspaces === - Mod+Shift+1 { move-column-to-workspace 1; } - Mod+Shift+2 { move-column-to-workspace 2; } - Mod+Shift+3 { move-column-to-workspace 3; } - Mod+Shift+4 { move-column-to-workspace 4; } - Mod+Shift+5 { move-column-to-workspace 5; } - Mod+Shift+6 { move-column-to-workspace 6; } - Mod+Shift+7 { move-column-to-workspace 7; } - Mod+Shift+8 { move-column-to-workspace 8; } - Mod+Shift+9 { move-column-to-workspace 9; } - - // === Column Management === - Mod+BracketLeft { consume-or-expel-window-left; } - Mod+BracketRight { consume-or-expel-window-right; } - Mod+Period { expel-window-from-column; } - - // === Sizing & Layout === - Mod+R { switch-preset-column-width; } - Mod+Shift+R { switch-preset-window-height; } - Mod+Ctrl+R { reset-window-height; } - Mod+Ctrl+F { expand-column-to-available-width; } - Mod+C { center-column; } - Mod+Ctrl+C { center-visible-columns; } - - // === Manual Sizing === - Mod+Minus { set-column-width "-10%"; } - Mod+Equal { set-column-width "+10%"; } - Mod+Shift+Minus { set-window-height "-10%"; } - Mod+Shift+Equal { set-window-height "+10%"; } - - // === Screenshots === - XF86Launch1 { screenshot; } - Ctrl+XF86Launch1 { screenshot-screen; } - Alt+XF86Launch1 { screenshot-window; } - Print { screenshot; } - Ctrl+Print { screenshot-screen; } - Alt+Print { screenshot-window; } - // === System Controls === - Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; } - Mod+Shift+P { power-off-monitors; } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-colors.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-colors.kdl deleted file mode 100644 index 145a179..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-colors.kdl +++ /dev/null @@ -1,39 +0,0 @@ -// ! Auto-generated file. Do not edit directly. -// Remove `include "dms/colors.kdl"` from your config to override. - -layout { - background-color "transparent" - - focus-ring { - active-color "#d0bcff" - inactive-color "#948f99" - urgent-color "#f2b8b5" - } - - border { - active-color "#d0bcff" - inactive-color "#948f99" - urgent-color "#f2b8b5" - } - - shadow { - color "#00000070" - } - - tab-indicator { - active-color "#d0bcff" - inactive-color "#948f99" - urgent-color "#f2b8b5" - } - - insert-hint { - color "#d0bcff80" - } -} - -recent-windows { - highlight { - active-color "#4f378b" - urgent-color "#f2b8b5" - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-greeter.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-greeter.kdl deleted file mode 100644 index 5653a16..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-greeter.kdl +++ /dev/null @@ -1,17 +0,0 @@ -hotkey-overlay { - skip-at-startup -} - -environment { - DMS_RUN_GREETER "1" -} - -gestures { - hot-corners { - off - } -} - -layout { - background-color "#000000" -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-layout.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-layout.kdl deleted file mode 100644 index 1951500..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri-layout.kdl +++ /dev/null @@ -1,22 +0,0 @@ -// ! DO NOT EDIT ! -// ! AUTO-GENERATED BY DMS ! -// ! CHANGES WILL BE OVERWRITTEN ! -// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE ! - -layout { - gaps 4 - - border { - width 2 - } - - focus-ring { - width 2 - } -} -window-rule { - geometry-corner-radius 12 - clip-to-geometry true - tiled-state true - draw-border-with-background false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri.kdl b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri.kdl deleted file mode 100644 index 0a759e4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/niri.kdl +++ /dev/null @@ -1,279 +0,0 @@ -// This config is in the KDL format: https://kdl.dev -// "/-" comments out the following node. -// Check the wiki for a full description of the configuration: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Introduction -config-notification { - disable-failed -} - -gestures { - hot-corners { - off - } -} - -// Input device configuration. -// Find the full list of options on the wiki: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Input -input { - keyboard { - xkb { - // You can set rules, model, layout, variant and options. - // For more information, see xkeyboard-config(7). - - // For example: - // layout "us,ru" - // options "grp:win_space_toggle,compose:ralt,ctrl:nocaps" - - // If this section is empty, niri will fetch xkb settings - // from org.freedesktop.locale1. You can control these using - // localectl set-x11-keymap. - } - - // Enable numlock on startup, omitting this setting disables it. - numlock - } - - // Next sections include libinput settings. - // Omitting settings disables them, or leaves them at their default values. - // All commented-out settings here are examples, not defaults. - touchpad { - // off - tap - // dwt - // dwtp - // drag false - // drag-lock - natural-scroll - // accel-speed 0.2 - // accel-profile "flat" - // scroll-method "two-finger" - // disabled-on-external-mouse - } - - mouse { - // off - // natural-scroll - // accel-speed 0.2 - // accel-profile "flat" - // scroll-method "no-scroll" - } - - trackpoint { - // off - // natural-scroll - // accel-speed 0.2 - // accel-profile "flat" - // scroll-method "on-button-down" - // scroll-button 273 - // scroll-button-lock - // middle-emulation - } - - // Uncomment this to make the mouse warp to the center of newly focused windows. - // warp-mouse-to-focus - - // Focus windows and outputs automatically when moving the mouse into them. - // Setting max-scroll-amount="0%" makes it work only on windows already fully on screen. - // focus-follows-mouse max-scroll-amount="0%" -} -// You can configure outputs by their name, which you can find -// by running `niri msg outputs` while inside a niri instance. -// The built-in laptop monitor is usually called "eDP-1". -// Find more information on the wiki: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Outputs -// Remember to uncomment the node by removing "/-"! -/-output "eDP-2" { - mode "2560x1600@239.998993" - position x=2560 y=0 - variable-refresh-rate -} -// Settings that influence how windows are positioned and sized. -// Find more information on the wiki: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Layout -layout { - // Set gaps around windows in logical pixels. - background-color "transparent" - // When to center a column when changing focus, options are: - // - "never", default behavior, focusing an off-screen column will keep at the left - // or right edge of the screen. - // - "always", the focused column will always be centered. - // - "on-overflow", focusing a column will center it if it doesn't fit - // together with the previously focused column. - center-focused-column "never" - // You can customize the widths that "switch-preset-column-width" (Mod+R) toggles between. - preset-column-widths { - // Proportion sets the width as a fraction of the output width, taking gaps into account. - // For example, you can perfectly fit four windows sized "proportion 0.25" on an output. - // The default preset widths are 1/3, 1/2 and 2/3 of the output. - proportion 0.33333 - proportion 0.5 - proportion 0.66667 - // Fixed sets the width in logical pixels exactly. - // fixed 1920 - } - // You can also customize the heights that "switch-preset-window-height" (Mod+Shift+R) toggles between. - // preset-window-heights { } - // You can change the default width of the new windows. - default-column-width { proportion 0.5; } - // If you leave the brackets empty, the windows themselves will decide their initial width. - // default-column-width {} - // By default focus ring and border are rendered as a solid background rectangle - // behind windows. That is, they will show up through semitransparent windows. - // This is because windows using client-side decorations can have an arbitrary shape. - // - // If you don't like that, you should uncomment `prefer-no-csd` below. - // Niri will draw focus ring and border *around* windows that agree to omit their - // client-side decorations. - // - // Alternatively, you can override it with a window rule called - // `draw-border-with-background`. - border { - off - width 4 - active-color "#707070" // Neutral gray - inactive-color "#d0d0d0" // Light gray - urgent-color "#cc4444" // Softer red - } - shadow { - softness 30 - spread 5 - offset x=0 y=5 - color "#0007" - } - struts { - } -} -layer-rule { - match namespace="^quickshell$" - place-within-backdrop true -} -overview { - workspace-shadow { - off - } -} -// Add lines like this to spawn processes at startup. -// Note that running niri as a session supports xdg-desktop-autostart, -// which may be more convenient to use. -// See the binds section below for more spawn examples. -// This line starts waybar, a commonly used bar for Wayland compositors. -environment { - XDG_CURRENT_DESKTOP "niri" -} -hotkey-overlay { - skip-at-startup -} -prefer-no-csd -screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" -animations { - workspace-switch { - spring damping-ratio=0.80 stiffness=523 epsilon=0.0001 - } - window-open { - duration-ms 150 - curve "ease-out-expo" - } - window-close { - duration-ms 150 - curve "ease-out-quad" - } - horizontal-view-movement { - spring damping-ratio=0.85 stiffness=423 epsilon=0.0001 - } - window-movement { - spring damping-ratio=0.75 stiffness=323 epsilon=0.0001 - } - window-resize { - spring damping-ratio=0.85 stiffness=423 epsilon=0.0001 - } - config-notification-open-close { - spring damping-ratio=0.65 stiffness=923 epsilon=0.001 - } - screenshot-ui-open { - duration-ms 200 - curve "ease-out-quad" - } - overview-open-close { - spring damping-ratio=0.85 stiffness=800 epsilon=0.0001 - } -} -// Window rules let you adjust behavior for individual windows. -// Find more information on the wiki: -// https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules -// Work around WezTerm's initial configure bug -// by setting an empty default-column-width. -window-rule { - // This regular expression is intentionally made as specific as possible, - // since this is the default config, and we want no false positives. - // You can get away with just app-id="wezterm" if you want. - match app-id=r#"^org\.wezfurlong\.wezterm$"# - default-column-width {} -} -window-rule { - match app-id=r#"^org\.gnome\."# - draw-border-with-background false - geometry-corner-radius 12 - clip-to-geometry true -} -window-rule { - match app-id=r#"^gnome-control-center$"# - match app-id=r#"^pavucontrol$"# - match app-id=r#"^nm-connection-editor$"# - default-column-width { proportion 0.5; } - open-floating false -} -window-rule { - match app-id=r#"^org\.gnome\.Calculator$"# - match app-id=r#"^gnome-calculator$"# - match app-id=r#"^galculator$"# - match app-id=r#"^blueman-manager$"# - match app-id=r#"^org\.gnome\.Nautilus$"# - match app-id=r#"^xdg-desktop-portal$"# - open-floating true -} -window-rule { - match app-id=r#"^steam$"# title=r#"^notificationtoasts_\d+_desktop$"# - default-floating-position x=10 y=10 relative-to="bottom-right" - open-focused false -} -window-rule { - match app-id=r#"^org\.wezfurlong\.wezterm$"# - match app-id="Alacritty" - match app-id="zen" - match app-id="com.mitchellh.ghostty" - match app-id="kitty" - draw-border-with-background false -} -window-rule { - match app-id=r#"firefox$"# title="^Picture-in-Picture$" - match app-id="zoom" - open-floating true -} -// Open dms windows as floating by default -window-rule { - match app-id=r#"org.quickshell$"# - match app-id=r#"com.danklinux.dms$"# - open-floating true -} -debug { - honor-xdg-activation-with-invalid-serial -} - -// Override to disable super+tab -recent-windows { - binds { - Alt+Tab { next-window scope="output"; } - Alt+Shift+Tab { previous-window scope="output"; } - Alt+grave { next-window filter="app-id"; } - Alt+Shift+grave { previous-window filter="app-id"; } - } -} - -// Include dms files -include "dms/colors.kdl" -include "dms/layout.kdl" -include "dms/alttab.kdl" -include "dms/binds.kdl" -include "dms/outputs.kdl" -include "dms/cursor.kdl" diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/testpage.pdf b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/testpage.pdf deleted file mode 100644 index 03e05c4..0000000 Binary files a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/embedded/testpage.pdf and /dev/null differ diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/hyprland.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/hyprland.go deleted file mode 100644 index f6bb964..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/hyprland.go +++ /dev/null @@ -1,15 +0,0 @@ -package config - -import _ "embed" - -//go:embed embedded/hyprland.conf -var HyprlandConfig string - -//go:embed embedded/hypr-colors.conf -var HyprColorsConfig string - -//go:embed embedded/hypr-layout.conf -var HyprLayoutConfig string - -//go:embed embedded/hypr-binds.conf -var HyprBindsConfig string diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/niri.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/niri.go deleted file mode 100644 index 01801ca..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/niri.go +++ /dev/null @@ -1,21 +0,0 @@ -package config - -import _ "embed" - -//go:embed embedded/niri.kdl -var NiriConfig string - -//go:embed embedded/niri-colors.kdl -var NiriColorsConfig string - -//go:embed embedded/niri-layout.kdl -var NiriLayoutConfig string - -//go:embed embedded/niri-alttab.kdl -var NiriAlttabConfig string - -//go:embed embedded/niri-binds.kdl -var NiriBindsConfig string - -//go:embed embedded/niri-greeter.kdl -var NiriGreeterConfig string diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/terminals.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/terminals.go deleted file mode 100644 index a5bba04..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/terminals.go +++ /dev/null @@ -1,24 +0,0 @@ -package config - -import _ "embed" - -//go:embed embedded/ghostty.conf -var GhosttyConfig string - -//go:embed embedded/ghostty-colors.conf -var GhosttyColorConfig string - -//go:embed embedded/kitty.conf -var KittyConfig string - -//go:embed embedded/kitty-theme.conf -var KittyThemeConfig string - -//go:embed embedded/kitty-tabs.conf -var KittyTabsConfig string - -//go:embed embedded/alacritty.toml -var AlacrittyConfig string - -//go:embed embedded/alacritty-theme.toml -var AlacrittyThemeConfig string diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/testpage.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/testpage.go deleted file mode 100644 index 2c5bbc7..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/config/testpage.go +++ /dev/null @@ -1,6 +0,0 @@ -package config - -import _ "embed" - -//go:embed embedded/testpage.pdf -var TestPage string diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16.go deleted file mode 100644 index fedd783..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16.go +++ /dev/null @@ -1,651 +0,0 @@ -package dank16 - -import ( - "fmt" - "math" - - "github.com/lucasb-eyer/go-colorful" -) - -type RGB struct { - R, G, B float64 -} - -type HSV struct { - H, S, V float64 -} - -type ColorInfo struct { - Hex string `json:"hex"` - HexStripped string `json:"hex_stripped"` - R int `json:"r"` - G int `json:"g"` - B int `json:"b"` -} - -type VariantColorValue struct { - Hex string `json:"hex"` - HexStripped string `json:"hex_stripped"` -} - -type VariantColorInfo struct { - Dark VariantColorValue `json:"dark"` - Light VariantColorValue `json:"light"` - Default VariantColorValue `json:"default"` -} - -type Palette struct { - Color0 ColorInfo `json:"color0"` - Color1 ColorInfo `json:"color1"` - Color2 ColorInfo `json:"color2"` - Color3 ColorInfo `json:"color3"` - Color4 ColorInfo `json:"color4"` - Color5 ColorInfo `json:"color5"` - Color6 ColorInfo `json:"color6"` - Color7 ColorInfo `json:"color7"` - Color8 ColorInfo `json:"color8"` - Color9 ColorInfo `json:"color9"` - Color10 ColorInfo `json:"color10"` - Color11 ColorInfo `json:"color11"` - Color12 ColorInfo `json:"color12"` - Color13 ColorInfo `json:"color13"` - Color14 ColorInfo `json:"color14"` - Color15 ColorInfo `json:"color15"` -} - -type VariantPalette struct { - Color0 VariantColorInfo `json:"color0"` - Color1 VariantColorInfo `json:"color1"` - Color2 VariantColorInfo `json:"color2"` - Color3 VariantColorInfo `json:"color3"` - Color4 VariantColorInfo `json:"color4"` - Color5 VariantColorInfo `json:"color5"` - Color6 VariantColorInfo `json:"color6"` - Color7 VariantColorInfo `json:"color7"` - Color8 VariantColorInfo `json:"color8"` - Color9 VariantColorInfo `json:"color9"` - Color10 VariantColorInfo `json:"color10"` - Color11 VariantColorInfo `json:"color11"` - Color12 VariantColorInfo `json:"color12"` - Color13 VariantColorInfo `json:"color13"` - Color14 VariantColorInfo `json:"color14"` - Color15 VariantColorInfo `json:"color15"` -} - -func NewColorInfo(hex string) ColorInfo { - rgb := HexToRGB(hex) - stripped := hex - if len(hex) > 0 && hex[0] == '#' { - stripped = hex[1:] - } - return ColorInfo{ - Hex: hex, - HexStripped: stripped, - R: int(math.Round(rgb.R * 255)), - G: int(math.Round(rgb.G * 255)), - B: int(math.Round(rgb.B * 255)), - } -} - -func HexToRGB(hex string) RGB { - if hex[0] == '#' { - hex = hex[1:] - } - var r, g, b uint8 - fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b) - return RGB{ - R: float64(r) / 255.0, - G: float64(g) / 255.0, - B: float64(b) / 255.0, - } -} - -func RGBToHex(rgb RGB) string { - r := math.Max(0, math.Min(1, rgb.R)) - g := math.Max(0, math.Min(1, rgb.G)) - b := math.Max(0, math.Min(1, rgb.B)) - return fmt.Sprintf("#%02x%02x%02x", int(r*255), int(g*255), int(b*255)) -} - -func RGBToHSV(rgb RGB) HSV { - max := math.Max(math.Max(rgb.R, rgb.G), rgb.B) - min := math.Min(math.Min(rgb.R, rgb.G), rgb.B) - delta := max - min - - var h float64 - switch { - case delta == 0: - h = 0 - case max == rgb.R: - h = math.Mod((rgb.G-rgb.B)/delta, 6.0) / 6.0 - case max == rgb.G: - h = ((rgb.B-rgb.R)/delta + 2.0) / 6.0 - default: - h = ((rgb.R-rgb.G)/delta + 4.0) / 6.0 - } - - if h < 0 { - h += 1.0 - } - - var s float64 - if max == 0 { - s = 0 - } else { - s = delta / max - } - - return HSV{H: h, S: s, V: max} -} - -func HSVToRGB(hsv HSV) RGB { - h := hsv.H * 6.0 - c := hsv.V * hsv.S - x := c * (1.0 - math.Abs(math.Mod(h, 2.0)-1.0)) - m := hsv.V - c - - var r, g, b float64 - switch int(h) { - case 0: - r, g, b = c, x, 0 - case 1: - r, g, b = x, c, 0 - case 2: - r, g, b = 0, c, x - case 3: - r, g, b = 0, x, c - case 4: - r, g, b = x, 0, c - case 5: - r, g, b = c, 0, x - default: - r, g, b = c, 0, x - } - - return RGB{R: r + m, G: g + m, B: b + m} -} - -func sRGBToLinear(c float64) float64 { - if c <= 0.04045 { - return c / 12.92 - } - return math.Pow((c+0.055)/1.055, 2.4) -} - -func Luminance(hex string) float64 { - rgb := HexToRGB(hex) - return 0.2126*sRGBToLinear(rgb.R) + 0.7152*sRGBToLinear(rgb.G) + 0.0722*sRGBToLinear(rgb.B) -} - -func ContrastRatio(hexFg, hexBg string) float64 { - lumFg := Luminance(hexFg) - lumBg := Luminance(hexBg) - lighter := math.Max(lumFg, lumBg) - darker := math.Min(lumFg, lumBg) - return (lighter + 0.05) / (darker + 0.05) -} - -func getLstar(hex string) float64 { - rgb := HexToRGB(hex) - col := colorful.Color{R: rgb.R, G: rgb.G, B: rgb.B} - L, _, _ := col.Lab() - return L * 100.0 // go-colorful uses 0-1, we need 0-100 for DPS -} - -// Lab to hex, clamping if needed -func labToHex(L, a, b float64) string { - c := colorful.Lab(L/100.0, a, b) // back to 0-1 for go-colorful - r, g, b2 := c.Clamped().RGB255() - return fmt.Sprintf("#%02x%02x%02x", r, g, b2) -} - -func DeltaPhiStar(hexFg, hexBg string, negativePolarity bool) float64 { - Lf := getLstar(hexFg) - Lb := getLstar(hexBg) - - phi := 1.618 - inv := 0.618 - lc := math.Pow(math.Abs(math.Pow(Lb, phi)-math.Pow(Lf, phi)), inv)*1.414 - 40 - - if negativePolarity { - lc += 5 - } - - return lc -} - -func DeltaPhiStarContrast(hexFg, hexBg string, isLightMode bool) float64 { - negativePolarity := !isLightMode - return DeltaPhiStar(hexFg, hexBg, negativePolarity) -} - -func EnsureContrast(hexColor, hexBg string, minRatio float64, isLightMode bool) string { - currentRatio := ContrastRatio(hexColor, hexBg) - if currentRatio >= minRatio { - return hexColor - } - - rgb := HexToRGB(hexColor) - hsv := RGBToHSV(rgb) - - for step := 1; step < 30; step++ { - delta := float64(step) * 0.02 - - if isLightMode { - newV := math.Max(0, hsv.V-delta) - candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if ContrastRatio(candidate, hexBg) >= minRatio { - return candidate - } - - newV = math.Min(1, hsv.V+delta) - candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if ContrastRatio(candidate, hexBg) >= minRatio { - return candidate - } - } else { - newV := math.Min(1, hsv.V+delta) - candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if ContrastRatio(candidate, hexBg) >= minRatio { - return candidate - } - - newV = math.Max(0, hsv.V-delta) - candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if ContrastRatio(candidate, hexBg) >= minRatio { - return candidate - } - } - } - - return hexColor -} - -func EnsureContrastDPS(hexColor, hexBg string, minLc float64, isLightMode bool) string { - currentLc := DeltaPhiStarContrast(hexColor, hexBg, isLightMode) - if currentLc >= minLc { - return hexColor - } - - rgb := HexToRGB(hexColor) - hsv := RGBToHSV(rgb) - - for step := 1; step < 50; step++ { - delta := float64(step) * 0.015 - - if isLightMode { - newV := math.Max(0, hsv.V-delta) - candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { - return candidate - } - - newV = math.Min(1, hsv.V+delta) - candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { - return candidate - } - } else { - newV := math.Min(1, hsv.V+delta) - candidate := RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { - return candidate - } - - newV = math.Max(0, hsv.V-delta) - candidate = RGBToHex(HSVToRGB(HSV{H: hsv.H, S: hsv.S, V: newV})) - if DeltaPhiStarContrast(candidate, hexBg, isLightMode) >= minLc { - return candidate - } - } - } - - return hexColor -} - -// Nudge L* until contrast is good enough. Keeps hue intact unlike HSV fiddling. -func EnsureContrastDPSLstar(hexColor, hexBg string, minLc float64, isLightMode bool) string { - current := DeltaPhiStarContrast(hexColor, hexBg, isLightMode) - if current >= minLc { - return hexColor - } - - fg := HexToRGB(hexColor) - cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B} - Lf, af, bf := cf.Lab() - - dir := 1.0 - if isLightMode { - dir = -1.0 // light mode = darker text - } - - step := 0.5 - for range 120 { - Lf = math.Max(0, math.Min(100, Lf+dir*step)) - cand := labToHex(Lf, af, bf) - if DeltaPhiStarContrast(cand, hexBg, isLightMode) >= minLc { - return cand - } - } - - return hexColor -} - -// Bidirectional contrast - tries both lighter and darker, picks closest to original -func EnsureContrastDPSBidirectional(hexColor, hexBg string, minLc float64, isLightMode bool) string { - current := DeltaPhiStarContrast(hexColor, hexBg, isLightMode) - if current >= minLc { - return hexColor - } - - fg := HexToRGB(hexColor) - cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B} - origL, af, bf := cf.Lab() - - var darkerResult, lighterResult string - darkerL, lighterL := origL, origL - darkerFound, lighterFound := false, false - - step := 0.5 - for i := range 120 { - if !darkerFound { - darkerL = math.Max(0, origL-float64(i)*step) - cand := labToHex(darkerL, af, bf) - if DeltaPhiStarContrast(cand, hexBg, isLightMode) >= minLc { - darkerResult = cand - darkerFound = true - } - } - if !lighterFound { - lighterL = math.Min(100, origL+float64(i)*step) - cand := labToHex(lighterL, af, bf) - if DeltaPhiStarContrast(cand, hexBg, isLightMode) >= minLc { - lighterResult = cand - lighterFound = true - } - } - if darkerFound && lighterFound { - break - } - } - - if darkerFound && lighterFound { - if math.Abs(darkerL-origL) <= math.Abs(lighterL-origL) { - return darkerResult - } - return lighterResult - } - if darkerFound { - return darkerResult - } - if lighterFound { - return lighterResult - } - return hexColor -} - -type PaletteOptions struct { - IsLight bool - Background string - UseDPS bool -} - -func ensureContrastAuto(hexColor, hexBg string, target float64, opts PaletteOptions) string { - if opts.UseDPS { - return EnsureContrastDPSLstar(hexColor, hexBg, target, opts.IsLight) - } - return EnsureContrast(hexColor, hexBg, target, opts.IsLight) -} - -func ensureContrastBidirectional(hexColor, hexBg string, target float64, opts PaletteOptions) string { - if opts.UseDPS { - return EnsureContrastDPSBidirectional(hexColor, hexBg, target, opts.IsLight) - } - return EnsureContrast(hexColor, hexBg, target, opts.IsLight) -} - -func blendHue(base, target, factor float64) float64 { - diff := target - base - if diff > 0.5 { - diff -= 1.0 - } else if diff < -0.5 { - diff += 1.0 - } - result := base + diff*factor - if result < 0 { - result += 1.0 - } else if result >= 1.0 { - result -= 1.0 - } - return result -} - -func DeriveContainer(primary string, isLight bool) string { - rgb := HexToRGB(primary) - hsv := RGBToHSV(rgb) - - if isLight { - containerV := math.Min(hsv.V*1.77, 1.0) - containerS := hsv.S * 0.32 - return RGBToHex(HSVToRGB(HSV{H: hsv.H, S: containerS, V: containerV})) - } - containerV := hsv.V * 0.463 - containerS := math.Min(hsv.S*1.834, 1.0) - return RGBToHex(HSVToRGB(HSV{H: hsv.H, S: containerS, V: containerV})) -} - -func GeneratePalette(primaryColor string, opts PaletteOptions) Palette { - baseColor := DeriveContainer(primaryColor, opts.IsLight) - - rgb := HexToRGB(baseColor) - hsv := RGBToHSV(rgb) - - pr := HexToRGB(primaryColor) - ph := RGBToHSV(pr) - - var palette Palette - - var normalTextTarget, secondaryTarget float64 - if opts.UseDPS { - normalTextTarget = 40.0 - secondaryTarget = 35.0 - } else { - normalTextTarget = 4.5 - secondaryTarget = 3.0 - } - - var bgColor string - if opts.Background != "" { - bgColor = opts.Background - } else if opts.IsLight { - bgColor = "#f8f8f8" - } else { - bgColor = "#1a1a1a" - } - palette.Color0 = NewColorInfo(bgColor) - - baseSat := math.Max(ph.S, 0.5) - baseVal := math.Max(ph.V, 0.5) - - redH := blendHue(0.0, ph.H, 0.12) - greenH := blendHue(0.33, ph.H, 0.10) - yellowH := blendHue(0.14, ph.H, 0.04) - - accentTarget := secondaryTarget * 0.7 - - if opts.IsLight { - redS := math.Min(baseSat*1.2, 1.0) - redV := baseVal * 0.95 - palette.Color1 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: redH, S: redS, V: redV})), bgColor, normalTextTarget, opts)) - - greenS := math.Min(baseSat*1.3, 1.0) - greenV := baseVal * 0.75 - palette.Color2 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: greenH, S: greenS, V: greenV})), bgColor, normalTextTarget, opts)) - - yellowS := math.Min(baseSat*1.5, 1.0) - yellowV := math.Min(baseVal*1.2, 1.0) - palette.Color3 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: yellowH, S: yellowS, V: yellowV})), bgColor, accentTarget, opts)) - - blueS := math.Min(ph.S*1.05, 1.0) - blueV := math.Min(ph.V*1.05, 1.0) - palette.Color4 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: ph.H, S: blueS, V: blueV})), bgColor, normalTextTarget, opts)) - - // Color5 matches primary_container exactly (light container in light mode) - container5 := DeriveContainer(primaryColor, true) - palette.Color5 = NewColorInfo(container5) - - palette.Color6 = NewColorInfo(primaryColor) - - gray7S := baseSat * 0.08 - gray7V := baseVal * 0.28 - palette.Color7 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray7S, V: gray7V})), bgColor, normalTextTarget, opts)) - - gray8S := baseSat * 0.05 - gray8V := baseVal * 0.85 - dimTarget := secondaryTarget * 0.5 - palette.Color8 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray8S, V: gray8V})), bgColor, dimTarget, opts)) - - brightRedS := math.Min(baseSat*1.0, 1.0) - brightRedV := math.Min(baseVal*1.2, 1.0) - palette.Color9 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: redH, S: brightRedS, V: brightRedV})), bgColor, accentTarget, opts)) - - brightGreenS := math.Min(baseSat*1.1, 1.0) - brightGreenV := math.Min(baseVal*1.1, 1.0) - palette.Color10 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: greenH, S: brightGreenS, V: brightGreenV})), bgColor, accentTarget, opts)) - - brightYellowS := math.Min(baseSat*1.4, 1.0) - brightYellowV := math.Min(baseVal*1.3, 1.0) - palette.Color11 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: yellowH, S: brightYellowS, V: brightYellowV})), bgColor, accentTarget, opts)) - - brightBlueS := math.Min(ph.S*1.1, 1.0) - brightBlueV := math.Min(ph.V*1.15, 1.0) - palette.Color12 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: ph.H, S: brightBlueS, V: brightBlueV})), bgColor, accentTarget, opts)) - - lightContainer := DeriveContainer(primaryColor, true) - palette.Color13 = NewColorInfo(lightContainer) - - brightCyanS := ph.S * 0.5 - brightCyanV := math.Min(ph.V*1.3, 1.0) - palette.Color14 = NewColorInfo(RGBToHex(HSVToRGB(HSV{H: ph.H, S: brightCyanS, V: brightCyanV}))) - - white15S := baseSat * 0.04 - white15V := math.Min(baseVal*1.5, 1.0) - palette.Color15 = NewColorInfo(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: white15S, V: white15V}))) - } else { - redS := math.Min(baseSat*1.1, 1.0) - redV := math.Min(baseVal*1.15, 1.0) - palette.Color1 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: redH, S: redS, V: redV})), bgColor, normalTextTarget, opts)) - - greenS := math.Min(baseSat*1.0, 1.0) - greenV := math.Min(baseVal*1.0, 1.0) - palette.Color2 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: greenH, S: greenS, V: greenV})), bgColor, normalTextTarget, opts)) - - yellowS := math.Min(baseSat*1.1, 1.0) - yellowV := math.Min(baseVal*1.25, 1.0) - palette.Color3 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: yellowH, S: yellowS, V: yellowV})), bgColor, normalTextTarget, opts)) - - // Slightly more saturated variant of primary - blueS := math.Min(ph.S*1.2, 1.0) - blueV := ph.V * 0.95 - palette.Color4 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: ph.H, S: blueS, V: blueV})), bgColor, normalTextTarget, opts)) - - // Color5 matches primary_container exactly (dark container in dark mode) - darkContainer := DeriveContainer(primaryColor, false) - palette.Color5 = NewColorInfo(darkContainer) - - palette.Color6 = NewColorInfo(primaryColor) - - gray7S := baseSat * 0.12 - gray7V := math.Min(baseVal*1.05, 1.0) - palette.Color7 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray7S, V: gray7V})), bgColor, normalTextTarget, opts)) - - gray8S := baseSat * 0.15 - gray8V := baseVal * 0.65 - palette.Color8 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray8S, V: gray8V})), bgColor, secondaryTarget, opts)) - - brightRedS := math.Min(baseSat*0.75, 1.0) - brightRedV := math.Min(baseVal*1.35, 1.0) - palette.Color9 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: redH, S: brightRedS, V: brightRedV})), bgColor, accentTarget, opts)) - - brightGreenS := math.Min(baseSat*0.7, 1.0) - brightGreenV := math.Min(baseVal*1.2, 1.0) - palette.Color10 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: greenH, S: brightGreenS, V: brightGreenV})), bgColor, accentTarget, opts)) - - brightYellowS := math.Min(baseSat*0.7, 1.0) - brightYellowV := math.Min(baseVal*1.5, 1.0) - palette.Color11 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: yellowH, S: brightYellowS, V: brightYellowV})), bgColor, accentTarget, opts)) - - // Create a gradient of primary variants: Color12 -> Color13 -> Color14 -> Color15 (near white) - // Color12: Start of the lighter gradient - slightly desaturated - brightBlueS := ph.S * 0.85 - brightBlueV := math.Min(ph.V*1.1, 1.0) - palette.Color12 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: ph.H, S: brightBlueS, V: brightBlueV})), bgColor, accentTarget, opts)) - - // Medium-high saturation pastel primary - color13S := ph.S * 0.7 - color13V := math.Min(ph.V*1.3, 1.0) - palette.Color13 = NewColorInfo(RGBToHex(HSVToRGB(HSV{H: ph.H, S: color13S, V: color13V}))) - - // Lower saturation, lighter variant - color14S := ph.S * 0.45 - color14V := math.Min(ph.V*1.4, 1.0) - palette.Color14 = NewColorInfo(RGBToHex(HSVToRGB(HSV{H: ph.H, S: color14S, V: color14V}))) - - white15S := baseSat * 0.05 - white15V := math.Min(baseVal*1.45, 1.0) - palette.Color15 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: white15S, V: white15V})), bgColor, normalTextTarget, opts)) - } - - return palette -} - -type VariantOptions struct { - PrimaryDark string - PrimaryLight string - Background string - UseDPS bool - IsLightMode bool -} - -func mergeColorInfo(dark, light ColorInfo, isLightMode bool) VariantColorInfo { - darkVal := VariantColorValue{Hex: dark.Hex, HexStripped: dark.HexStripped} - lightVal := VariantColorValue{Hex: light.Hex, HexStripped: light.HexStripped} - - defaultVal := darkVal - if isLightMode { - defaultVal = lightVal - } - - return VariantColorInfo{ - Dark: darkVal, - Light: lightVal, - Default: defaultVal, - } -} - -func GenerateVariantPalette(opts VariantOptions) VariantPalette { - darkOpts := PaletteOptions{IsLight: false, Background: opts.Background, UseDPS: opts.UseDPS} - lightOpts := PaletteOptions{IsLight: true, Background: opts.Background, UseDPS: opts.UseDPS} - - dark := GeneratePalette(opts.PrimaryDark, darkOpts) - light := GeneratePalette(opts.PrimaryLight, lightOpts) - - return VariantPalette{ - Color0: mergeColorInfo(dark.Color0, light.Color0, opts.IsLightMode), - Color1: mergeColorInfo(dark.Color1, light.Color1, opts.IsLightMode), - Color2: mergeColorInfo(dark.Color2, light.Color2, opts.IsLightMode), - Color3: mergeColorInfo(dark.Color3, light.Color3, opts.IsLightMode), - Color4: mergeColorInfo(dark.Color4, light.Color4, opts.IsLightMode), - Color5: mergeColorInfo(dark.Color5, light.Color5, opts.IsLightMode), - Color6: mergeColorInfo(dark.Color6, light.Color6, opts.IsLightMode), - Color7: mergeColorInfo(dark.Color7, light.Color7, opts.IsLightMode), - Color8: mergeColorInfo(dark.Color8, light.Color8, opts.IsLightMode), - Color9: mergeColorInfo(dark.Color9, light.Color9, opts.IsLightMode), - Color10: mergeColorInfo(dark.Color10, light.Color10, opts.IsLightMode), - Color11: mergeColorInfo(dark.Color11, light.Color11, opts.IsLightMode), - Color12: mergeColorInfo(dark.Color12, light.Color12, opts.IsLightMode), - Color13: mergeColorInfo(dark.Color13, light.Color13, opts.IsLightMode), - Color14: mergeColorInfo(dark.Color14, light.Color14, opts.IsLightMode), - Color15: mergeColorInfo(dark.Color15, light.Color15, opts.IsLightMode), - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16_test.go deleted file mode 100644 index 9e6b5a8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/dank16_test.go +++ /dev/null @@ -1,681 +0,0 @@ -package dank16 - -import ( - "math" - "testing" -) - -func TestHexToRGB(t *testing.T) { - tests := []struct { - name string - input string - expected RGB - }{ - { - name: "black with hash", - input: "#000000", - expected: RGB{R: 0.0, G: 0.0, B: 0.0}, - }, - { - name: "white with hash", - input: "#ffffff", - expected: RGB{R: 1.0, G: 1.0, B: 1.0}, - }, - { - name: "red without hash", - input: "ff0000", - expected: RGB{R: 1.0, G: 0.0, B: 0.0}, - }, - { - name: "purple", - input: "#625690", - expected: RGB{R: 0.3843137254901961, G: 0.33725490196078434, B: 0.5647058823529412}, - }, - { - name: "mid gray", - input: "#808080", - expected: RGB{R: 0.5019607843137255, G: 0.5019607843137255, B: 0.5019607843137255}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := HexToRGB(tt.input) - if !floatEqual(result.R, tt.expected.R) || !floatEqual(result.G, tt.expected.G) || !floatEqual(result.B, tt.expected.B) { - t.Errorf("HexToRGB(%s) = %v, expected %v", tt.input, result, tt.expected) - } - }) - } -} - -func TestRGBToHex(t *testing.T) { - tests := []struct { - name string - input RGB - expected string - }{ - { - name: "black", - input: RGB{R: 0.0, G: 0.0, B: 0.0}, - expected: "#000000", - }, - { - name: "white", - input: RGB{R: 1.0, G: 1.0, B: 1.0}, - expected: "#ffffff", - }, - { - name: "red", - input: RGB{R: 1.0, G: 0.0, B: 0.0}, - expected: "#ff0000", - }, - { - name: "clamping above 1.0", - input: RGB{R: 1.5, G: 0.5, B: 0.5}, - expected: "#ff7f7f", - }, - { - name: "clamping below 0.0", - input: RGB{R: -0.5, G: 0.5, B: 0.5}, - expected: "#007f7f", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := RGBToHex(tt.input) - if result != tt.expected { - t.Errorf("RGBToHex(%v) = %s, expected %s", tt.input, result, tt.expected) - } - }) - } -} - -func TestRGBToHSV(t *testing.T) { - tests := []struct { - name string - input RGB - expected HSV - }{ - { - name: "black", - input: RGB{R: 0.0, G: 0.0, B: 0.0}, - expected: HSV{H: 0.0, S: 0.0, V: 0.0}, - }, - { - name: "white", - input: RGB{R: 1.0, G: 1.0, B: 1.0}, - expected: HSV{H: 0.0, S: 0.0, V: 1.0}, - }, - { - name: "red", - input: RGB{R: 1.0, G: 0.0, B: 0.0}, - expected: HSV{H: 0.0, S: 1.0, V: 1.0}, - }, - { - name: "green", - input: RGB{R: 0.0, G: 1.0, B: 0.0}, - expected: HSV{H: 0.3333333333333333, S: 1.0, V: 1.0}, - }, - { - name: "blue", - input: RGB{R: 0.0, G: 0.0, B: 1.0}, - expected: HSV{H: 0.6666666666666666, S: 1.0, V: 1.0}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := RGBToHSV(tt.input) - if !floatEqual(result.H, tt.expected.H) || !floatEqual(result.S, tt.expected.S) || !floatEqual(result.V, tt.expected.V) { - t.Errorf("RGBToHSV(%v) = %v, expected %v", tt.input, result, tt.expected) - } - }) - } -} - -func TestHSVToRGB(t *testing.T) { - tests := []struct { - name string - input HSV - expected RGB - }{ - { - name: "black", - input: HSV{H: 0.0, S: 0.0, V: 0.0}, - expected: RGB{R: 0.0, G: 0.0, B: 0.0}, - }, - { - name: "white", - input: HSV{H: 0.0, S: 0.0, V: 1.0}, - expected: RGB{R: 1.0, G: 1.0, B: 1.0}, - }, - { - name: "red", - input: HSV{H: 0.0, S: 1.0, V: 1.0}, - expected: RGB{R: 1.0, G: 0.0, B: 0.0}, - }, - { - name: "green", - input: HSV{H: 0.3333333333333333, S: 1.0, V: 1.0}, - expected: RGB{R: 0.0, G: 1.0, B: 0.0}, - }, - { - name: "blue", - input: HSV{H: 0.6666666666666666, S: 1.0, V: 1.0}, - expected: RGB{R: 0.0, G: 0.0, B: 1.0}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := HSVToRGB(tt.input) - if !floatEqual(result.R, tt.expected.R) || !floatEqual(result.G, tt.expected.G) || !floatEqual(result.B, tt.expected.B) { - t.Errorf("HSVToRGB(%v) = %v, expected %v", tt.input, result, tt.expected) - } - }) - } -} - -func TestLuminance(t *testing.T) { - tests := []struct { - name string - input string - expected float64 - }{ - { - name: "black", - input: "#000000", - expected: 0.0, - }, - { - name: "white", - input: "#ffffff", - expected: 1.0, - }, - { - name: "red", - input: "#ff0000", - expected: 0.2126, - }, - { - name: "green", - input: "#00ff00", - expected: 0.7152, - }, - { - name: "blue", - input: "#0000ff", - expected: 0.0722, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := Luminance(tt.input) - if !floatEqual(result, tt.expected) { - t.Errorf("Luminance(%s) = %f, expected %f", tt.input, result, tt.expected) - } - }) - } -} - -func TestContrastRatio(t *testing.T) { - tests := []struct { - name string - fg string - bg string - expected float64 - }{ - { - name: "black on white", - fg: "#000000", - bg: "#ffffff", - expected: 21.0, - }, - { - name: "white on black", - fg: "#ffffff", - bg: "#000000", - expected: 21.0, - }, - { - name: "same color", - fg: "#808080", - bg: "#808080", - expected: 1.0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := ContrastRatio(tt.fg, tt.bg) - if !floatEqual(result, tt.expected) { - t.Errorf("ContrastRatio(%s, %s) = %f, expected %f", tt.fg, tt.bg, result, tt.expected) - } - }) - } -} - -func TestEnsureContrast(t *testing.T) { - tests := []struct { - name string - color string - bg string - minRatio float64 - isLightMode bool - }{ - { - name: "already sufficient contrast dark mode", - color: "#ffffff", - bg: "#000000", - minRatio: 4.5, - isLightMode: false, - }, - { - name: "already sufficient contrast light mode", - color: "#000000", - bg: "#ffffff", - minRatio: 4.5, - isLightMode: true, - }, - { - name: "needs adjustment dark mode", - color: "#404040", - bg: "#1a1a1a", - minRatio: 4.5, - isLightMode: false, - }, - { - name: "needs adjustment light mode", - color: "#c0c0c0", - bg: "#f8f8f8", - minRatio: 4.5, - isLightMode: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := EnsureContrast(tt.color, tt.bg, tt.minRatio, tt.isLightMode) - actualRatio := ContrastRatio(result, tt.bg) - if actualRatio < tt.minRatio { - t.Errorf("EnsureContrast(%s, %s, %f, %t) = %s with ratio %f, expected ratio >= %f", - tt.color, tt.bg, tt.minRatio, tt.isLightMode, result, actualRatio, tt.minRatio) - } - }) - } -} - -func TestGeneratePalette(t *testing.T) { - tests := []struct { - name string - base string - opts PaletteOptions - }{ - { - name: "dark theme default", - base: "#625690", - opts: PaletteOptions{IsLight: false}, - }, - { - name: "light theme default", - base: "#625690", - opts: PaletteOptions{IsLight: true}, - }, - { - name: "light theme with custom background", - base: "#625690", - opts: PaletteOptions{ - IsLight: true, - Background: "#fafafa", - }, - }, - { - name: "dark theme with custom background", - base: "#625690", - opts: PaletteOptions{ - IsLight: false, - Background: "#0a0a0a", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := GeneratePalette(tt.base, tt.opts) - - colors := []ColorInfo{ - result.Color0, result.Color1, result.Color2, result.Color3, - result.Color4, result.Color5, result.Color6, result.Color7, - result.Color8, result.Color9, result.Color10, result.Color11, - result.Color12, result.Color13, result.Color14, result.Color15, - } - - for i, color := range colors { - if len(color.Hex) != 7 || color.Hex[0] != '#' { - t.Errorf("Color at index %d (%s) is not a valid hex color", i, color.Hex) - } - } - - if tt.opts.Background != "" && result.Color0.Hex != tt.opts.Background { - t.Errorf("Background color = %s, expected %s", result.Color0.Hex, tt.opts.Background) - } else if !tt.opts.IsLight && tt.opts.Background == "" && result.Color0.Hex != "#1a1a1a" { - t.Errorf("Dark mode background = %s, expected #1a1a1a", result.Color0.Hex) - } else if tt.opts.IsLight && tt.opts.Background == "" && result.Color0.Hex != "#f8f8f8" { - t.Errorf("Light mode background = %s, expected #f8f8f8", result.Color0.Hex) - } - - // Color15 is now derived from primary, so just verify it's a valid color - // and has appropriate luminance for the mode (now theme-tinted, not pure white/black) - color15Lum := Luminance(result.Color15.Hex) - if tt.opts.IsLight { - // Light mode: Color15 should still be relatively light - if color15Lum < 0.5 { - t.Errorf("Light mode Color15 = %s (lum %.2f) is too dark", result.Color15.Hex, color15Lum) - } - } else { - // Dark mode: Color15 should be light (but may have theme tint, so lower threshold) - if color15Lum < 0.5 { - t.Errorf("Dark mode Color15 = %s (lum %.2f) is too dark", result.Color15.Hex, color15Lum) - } - } - }) - } -} - -func TestRoundTripConversion(t *testing.T) { - testColors := []string{"#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff", "#625690", "#808080"} - - for _, hex := range testColors { - t.Run(hex, func(t *testing.T) { - rgb := HexToRGB(hex) - result := RGBToHex(rgb) - if result != hex { - t.Errorf("Round trip %s -> RGB -> %s failed", hex, result) - } - }) - } -} - -func TestRGBHSVRoundTrip(t *testing.T) { - testCases := []RGB{ - {R: 0.0, G: 0.0, B: 0.0}, - {R: 1.0, G: 1.0, B: 1.0}, - {R: 1.0, G: 0.0, B: 0.0}, - {R: 0.0, G: 1.0, B: 0.0}, - {R: 0.0, G: 0.0, B: 1.0}, - {R: 0.5, G: 0.5, B: 0.5}, - {R: 0.3843137254901961, G: 0.33725490196078434, B: 0.5647058823529412}, - } - - for _, rgb := range testCases { - t.Run("", func(t *testing.T) { - hsv := RGBToHSV(rgb) - result := HSVToRGB(hsv) - if !floatEqual(result.R, rgb.R) || !floatEqual(result.G, rgb.G) || !floatEqual(result.B, rgb.B) { - t.Errorf("Round trip RGB->HSV->RGB failed: %v -> %v -> %v", rgb, hsv, result) - } - }) - } -} - -func floatEqual(a, b float64) bool { - return math.Abs(a-b) < 1e-9 -} - -func TestDeltaPhiStar(t *testing.T) { - tests := []struct { - name string - fg string - bg string - negativePolarity bool - minExpected float64 - }{ - { - name: "white on black (negative polarity)", - fg: "#ffffff", - bg: "#000000", - negativePolarity: true, - minExpected: 100.0, - }, - { - name: "black on white (positive polarity)", - fg: "#000000", - bg: "#ffffff", - negativePolarity: false, - minExpected: 100.0, - }, - { - name: "low contrast same color", - fg: "#808080", - bg: "#808080", - negativePolarity: false, - minExpected: -40.0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := DeltaPhiStar(tt.fg, tt.bg, tt.negativePolarity) - if result < tt.minExpected { - t.Errorf("DeltaPhiStar(%s, %s, %v) = %f, expected >= %f", - tt.fg, tt.bg, tt.negativePolarity, result, tt.minExpected) - } - }) - } -} - -func TestDeltaPhiStarContrast(t *testing.T) { - tests := []struct { - name string - fg string - bg string - isLightMode bool - minExpected float64 - }{ - { - name: "white on black (dark mode)", - fg: "#ffffff", - bg: "#000000", - isLightMode: false, - minExpected: 100.0, - }, - { - name: "black on white (light mode)", - fg: "#000000", - bg: "#ffffff", - isLightMode: true, - minExpected: 100.0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := DeltaPhiStarContrast(tt.fg, tt.bg, tt.isLightMode) - if result < tt.minExpected { - t.Errorf("DeltaPhiStarContrast(%s, %s, %v) = %f, expected >= %f", - tt.fg, tt.bg, tt.isLightMode, result, tt.minExpected) - } - }) - } -} - -func TestEnsureContrastDPS(t *testing.T) { - tests := []struct { - name string - color string - bg string - minLc float64 - isLightMode bool - }{ - { - name: "already sufficient contrast dark mode", - color: "#ffffff", - bg: "#000000", - minLc: 60.0, - isLightMode: false, - }, - { - name: "already sufficient contrast light mode", - color: "#000000", - bg: "#ffffff", - minLc: 60.0, - isLightMode: true, - }, - { - name: "needs adjustment dark mode", - color: "#404040", - bg: "#1a1a1a", - minLc: 60.0, - isLightMode: false, - }, - { - name: "needs adjustment light mode", - color: "#c0c0c0", - bg: "#f8f8f8", - minLc: 60.0, - isLightMode: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := EnsureContrastDPS(tt.color, tt.bg, tt.minLc, tt.isLightMode) - actualLc := DeltaPhiStarContrast(result, tt.bg, tt.isLightMode) - if actualLc < tt.minLc { - t.Errorf("EnsureContrastDPS(%s, %s, %f, %t) = %s with Lc %f, expected Lc >= %f", - tt.color, tt.bg, tt.minLc, tt.isLightMode, result, actualLc, tt.minLc) - } - }) - } -} - -func TestGeneratePaletteWithDPS(t *testing.T) { - tests := []struct { - name string - base string - opts PaletteOptions - }{ - { - name: "dark theme with DPS", - base: "#625690", - opts: PaletteOptions{IsLight: false, UseDPS: true}, - }, - { - name: "light theme with DPS", - base: "#625690", - opts: PaletteOptions{IsLight: true, UseDPS: true}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := GeneratePalette(tt.base, tt.opts) - - colors := []ColorInfo{ - result.Color0, result.Color1, result.Color2, result.Color3, - result.Color4, result.Color5, result.Color6, result.Color7, - result.Color8, result.Color9, result.Color10, result.Color11, - result.Color12, result.Color13, result.Color14, result.Color15, - } - - for i, color := range colors { - if len(color.Hex) != 7 || color.Hex[0] != '#' { - t.Errorf("Color at index %d (%s) is not a valid hex color", i, color.Hex) - } - } - - bgColor := result.Color0.Hex - for i := 1; i < 8; i++ { - // Skip Color5 (container) and Color6 (exact primary) - intentionally not contrast-adjusted - if i == 5 || i == 6 { - continue - } - lc := DeltaPhiStarContrast(colors[i].Hex, bgColor, tt.opts.IsLight) - minLc := 30.0 - if lc < minLc && lc > 0 { - t.Errorf("Color %d (%s) has insufficient DPS contrast %f with background %s (expected >= %f)", - i, colors[i].Hex, lc, bgColor, minLc) - } - } - }) - } -} - -func TestDeriveContainer(t *testing.T) { - tests := []struct { - name string - primary string - isLight bool - expected string - }{ - { - name: "dark mode", - primary: "#ccbdff", - isLight: false, - expected: "#4a3e76", - }, - { - name: "light mode", - primary: "#625690", - isLight: true, - expected: "#e7deff", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := DeriveContainer(tt.primary, tt.isLight) - - resultRGB := HexToRGB(result) - expectedRGB := HexToRGB(tt.expected) - - rDiff := math.Abs(resultRGB.R - expectedRGB.R) - gDiff := math.Abs(resultRGB.G - expectedRGB.G) - bDiff := math.Abs(resultRGB.B - expectedRGB.B) - - tolerance := 0.02 - if rDiff > tolerance || gDiff > tolerance || bDiff > tolerance { - t.Errorf("DeriveContainer(%s, %v) = %s, expected %s (RGB diff: R:%.4f G:%.4f B:%.4f)", - tt.primary, tt.isLight, result, tt.expected, rDiff, gDiff, bDiff) - } - }) - } -} - -func TestContrastAlgorithmComparison(t *testing.T) { - base := "#625690" - - optsWCAG := PaletteOptions{IsLight: false, UseDPS: false} - optsDPS := PaletteOptions{IsLight: false, UseDPS: true} - - paletteWCAG := GeneratePalette(base, optsWCAG) - paletteDPS := GeneratePalette(base, optsDPS) - - wcagColors := []ColorInfo{ - paletteWCAG.Color0, paletteWCAG.Color1, paletteWCAG.Color2, paletteWCAG.Color3, - paletteWCAG.Color4, paletteWCAG.Color5, paletteWCAG.Color6, paletteWCAG.Color7, - paletteWCAG.Color8, paletteWCAG.Color9, paletteWCAG.Color10, paletteWCAG.Color11, - paletteWCAG.Color12, paletteWCAG.Color13, paletteWCAG.Color14, paletteWCAG.Color15, - } - dpsColors := []ColorInfo{ - paletteDPS.Color0, paletteDPS.Color1, paletteDPS.Color2, paletteDPS.Color3, - paletteDPS.Color4, paletteDPS.Color5, paletteDPS.Color6, paletteDPS.Color7, - paletteDPS.Color8, paletteDPS.Color9, paletteDPS.Color10, paletteDPS.Color11, - paletteDPS.Color12, paletteDPS.Color13, paletteDPS.Color14, paletteDPS.Color15, - } - - if paletteWCAG.Color0.Hex != paletteDPS.Color0.Hex { - t.Errorf("Background colors differ: WCAG=%s, DPS=%s", paletteWCAG.Color0.Hex, paletteDPS.Color0.Hex) - } - - differentCount := 0 - for i := range 16 { - if wcagColors[i].Hex != dpsColors[i].Hex { - differentCount++ - } - } - - t.Logf("WCAG and DPS palettes differ in %d/16 colors", differentCount) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/terminals.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/terminals.go deleted file mode 100644 index 9423311..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/dank16/terminals.go +++ /dev/null @@ -1,135 +0,0 @@ -package dank16 - -import ( - "encoding/json" - "fmt" - "strings" -) - -func GenerateJSON(p Palette) string { - marshalled, _ := json.Marshal(p) - return string(marshalled) -} - -func GenerateVariantJSON(p VariantPalette) string { - marshalled, _ := json.Marshal(p) - return string(marshalled) -} - -func GenerateKittyTheme(p Palette) string { - var result strings.Builder - fmt.Fprintf(&result, "color0 %s\n", p.Color0.Hex) - fmt.Fprintf(&result, "color1 %s\n", p.Color1.Hex) - fmt.Fprintf(&result, "color2 %s\n", p.Color2.Hex) - fmt.Fprintf(&result, "color3 %s\n", p.Color3.Hex) - fmt.Fprintf(&result, "color4 %s\n", p.Color4.Hex) - fmt.Fprintf(&result, "color5 %s\n", p.Color5.Hex) - fmt.Fprintf(&result, "color6 %s\n", p.Color6.Hex) - fmt.Fprintf(&result, "color7 %s\n", p.Color7.Hex) - fmt.Fprintf(&result, "color8 %s\n", p.Color8.Hex) - fmt.Fprintf(&result, "color9 %s\n", p.Color9.Hex) - fmt.Fprintf(&result, "color10 %s\n", p.Color10.Hex) - fmt.Fprintf(&result, "color11 %s\n", p.Color11.Hex) - fmt.Fprintf(&result, "color12 %s\n", p.Color12.Hex) - fmt.Fprintf(&result, "color13 %s\n", p.Color13.Hex) - fmt.Fprintf(&result, "color14 %s\n", p.Color14.Hex) - fmt.Fprintf(&result, "color15 %s\n", p.Color15.Hex) - return result.String() -} - -func GenerateFootTheme(p Palette) string { - var result strings.Builder - fmt.Fprintf(&result, "regular0=%s\n", p.Color0.HexStripped) - fmt.Fprintf(&result, "regular1=%s\n", p.Color1.HexStripped) - fmt.Fprintf(&result, "regular2=%s\n", p.Color2.HexStripped) - fmt.Fprintf(&result, "regular3=%s\n", p.Color3.HexStripped) - fmt.Fprintf(&result, "regular4=%s\n", p.Color4.HexStripped) - fmt.Fprintf(&result, "regular5=%s\n", p.Color5.HexStripped) - fmt.Fprintf(&result, "regular6=%s\n", p.Color6.HexStripped) - fmt.Fprintf(&result, "regular7=%s\n", p.Color7.HexStripped) - fmt.Fprintf(&result, "bright0=%s\n", p.Color8.HexStripped) - fmt.Fprintf(&result, "bright1=%s\n", p.Color9.HexStripped) - fmt.Fprintf(&result, "bright2=%s\n", p.Color10.HexStripped) - fmt.Fprintf(&result, "bright3=%s\n", p.Color11.HexStripped) - fmt.Fprintf(&result, "bright4=%s\n", p.Color12.HexStripped) - fmt.Fprintf(&result, "bright5=%s\n", p.Color13.HexStripped) - fmt.Fprintf(&result, "bright6=%s\n", p.Color14.HexStripped) - fmt.Fprintf(&result, "bright7=%s\n", p.Color15.HexStripped) - return result.String() -} - -func GenerateAlacrittyTheme(p Palette) string { - var result strings.Builder - result.WriteString("[colors.normal]\n") - fmt.Fprintf(&result, "black = '%s'\n", p.Color0.Hex) - fmt.Fprintf(&result, "red = '%s'\n", p.Color1.Hex) - fmt.Fprintf(&result, "green = '%s'\n", p.Color2.Hex) - fmt.Fprintf(&result, "yellow = '%s'\n", p.Color3.Hex) - fmt.Fprintf(&result, "blue = '%s'\n", p.Color4.Hex) - fmt.Fprintf(&result, "magenta = '%s'\n", p.Color5.Hex) - fmt.Fprintf(&result, "cyan = '%s'\n", p.Color6.Hex) - fmt.Fprintf(&result, "white = '%s'\n", p.Color7.Hex) - result.WriteString("\n[colors.bright]\n") - fmt.Fprintf(&result, "black = '%s'\n", p.Color8.Hex) - fmt.Fprintf(&result, "red = '%s'\n", p.Color9.Hex) - fmt.Fprintf(&result, "green = '%s'\n", p.Color10.Hex) - fmt.Fprintf(&result, "yellow = '%s'\n", p.Color11.Hex) - fmt.Fprintf(&result, "blue = '%s'\n", p.Color12.Hex) - fmt.Fprintf(&result, "magenta = '%s'\n", p.Color13.Hex) - fmt.Fprintf(&result, "cyan = '%s'\n", p.Color14.Hex) - fmt.Fprintf(&result, "white = '%s'\n", p.Color15.Hex) - return result.String() -} - -func GenerateGhosttyTheme(p Palette) string { - var result strings.Builder - fmt.Fprintf(&result, "palette = 0=%s\n", p.Color0.Hex) - fmt.Fprintf(&result, "palette = 1=%s\n", p.Color1.Hex) - fmt.Fprintf(&result, "palette = 2=%s\n", p.Color2.Hex) - fmt.Fprintf(&result, "palette = 3=%s\n", p.Color3.Hex) - fmt.Fprintf(&result, "palette = 4=%s\n", p.Color4.Hex) - fmt.Fprintf(&result, "palette = 5=%s\n", p.Color5.Hex) - fmt.Fprintf(&result, "palette = 6=%s\n", p.Color6.Hex) - fmt.Fprintf(&result, "palette = 7=%s\n", p.Color7.Hex) - fmt.Fprintf(&result, "palette = 8=%s\n", p.Color8.Hex) - fmt.Fprintf(&result, "palette = 9=%s\n", p.Color9.Hex) - fmt.Fprintf(&result, "palette = 10=%s\n", p.Color10.Hex) - fmt.Fprintf(&result, "palette = 11=%s\n", p.Color11.Hex) - fmt.Fprintf(&result, "palette = 12=%s\n", p.Color12.Hex) - fmt.Fprintf(&result, "palette = 13=%s\n", p.Color13.Hex) - fmt.Fprintf(&result, "palette = 14=%s\n", p.Color14.Hex) - fmt.Fprintf(&result, "palette = 15=%s\n", p.Color15.Hex) - return result.String() -} - -func GenerateWeztermTheme(p Palette) string { - var result strings.Builder - fmt.Fprintf(&result, "ansi = ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']\n", - p.Color0.Hex, p.Color1.Hex, p.Color2.Hex, p.Color3.Hex, - p.Color4.Hex, p.Color5.Hex, p.Color6.Hex, p.Color7.Hex) - fmt.Fprintf(&result, "brights = ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']\n", - p.Color8.Hex, p.Color9.Hex, p.Color10.Hex, p.Color11.Hex, - p.Color12.Hex, p.Color13.Hex, p.Color14.Hex, p.Color15.Hex) - return result.String() -} - -func GenerateNeovimTheme(p Palette) string { - var result strings.Builder - fmt.Fprintf(&result, "vim.g.terminal_color_0 = \"%s\"\n", p.Color0.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_1 = \"%s\"\n", p.Color1.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_2 = \"%s\"\n", p.Color2.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_3 = \"%s\"\n", p.Color3.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_4 = \"%s\"\n", p.Color4.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_5 = \"%s\"\n", p.Color5.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_6 = \"%s\"\n", p.Color6.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_7 = \"%s\"\n", p.Color7.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_8 = \"%s\"\n", p.Color8.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_9 = \"%s\"\n", p.Color9.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_10 = \"%s\"\n", p.Color10.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_11 = \"%s\"\n", p.Color11.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_12 = \"%s\"\n", p.Color12.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_13 = \"%s\"\n", p.Color13.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_14 = \"%s\"\n", p.Color14.Hex) - fmt.Fprintf(&result, "vim.g.terminal_color_15 = \"%s\"\n", p.Color15.Hex) - return result.String() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/deps/detector.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/deps/detector.go deleted file mode 100644 index e4f54a7..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/deps/detector.go +++ /dev/null @@ -1,51 +0,0 @@ -package deps - -import ( - "context" -) - -type DependencyStatus int - -const ( - StatusMissing DependencyStatus = iota - StatusInstalled - StatusNeedsUpdate - StatusNeedsReinstall -) - -type PackageVariant int - -const ( - VariantStable PackageVariant = iota - VariantGit -) - -type Dependency struct { - Name string - Status DependencyStatus - Version string - Description string - Required bool - Variant PackageVariant - CanToggle bool -} - -type WindowManager int - -const ( - WindowManagerHyprland WindowManager = iota - WindowManagerNiri -) - -type Terminal int - -const ( - TerminalGhostty Terminal = iota - TerminalKitty - TerminalAlacritty -) - -type DependencyDetector interface { - DetectDependencies(ctx context.Context, wm WindowManager) ([]Dependency, error) - DetectDependenciesWithTerminal(ctx context.Context, wm WindowManager, terminal Terminal) ([]Dependency, error) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/arch.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/arch.go deleted file mode 100644 index b412f88..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/arch.go +++ /dev/null @@ -1,807 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" - "slices" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -func init() { - Register("arch", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("archarm", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("archcraft", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("cachyos", "#08A283", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("catos", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("endeavouros", "#7F3FBF", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("manjaro", "#35BF5C", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("obarun", "#2494be", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("garuda", "#cba6f7", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("artix", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) - Register("XeroLinux", "#888fe2", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution { - return NewArchDistribution(config, logChan) - }) -} - -type ArchDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewArchDistribution(config DistroConfig, logChan chan<- string) *ArchDistribution { - base := NewBaseDistribution(logChan) - return &ArchDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (a *ArchDistribution) GetID() string { - return a.config.ID -} - -func (a *ArchDistribution) GetColorHex() string { - return a.config.ColorHex -} - -func (a *ArchDistribution) GetFamily() DistroFamily { - return a.config.Family -} - -func (a *ArchDistribution) GetPackageManager() PackageManagerType { - return PackageManagerPacman -} - -func (a *ArchDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return a.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (a *ArchDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, a.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, a.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, a.detectGit()) - dependencies = append(dependencies, a.detectWindowManager(wm)) - dependencies = append(dependencies, a.detectQuickshell()) - dependencies = append(dependencies, a.detectDMSGreeter()) - dependencies = append(dependencies, a.detectXDGPortal()) - dependencies = append(dependencies, a.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, a.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, a.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, a.detectMatugen()) - dependencies = append(dependencies, a.detectDgop()) - - return dependencies, nil -} - -func (a *ArchDistribution) detectXDGPortal() deps.Dependency { - return a.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", a.packageInstalled("xdg-desktop-portal-gtk")) -} - -func (a *ArchDistribution) detectAccountsService() deps.Dependency { - return a.detectPackage("accountsservice", "D-Bus interface for user account query and manipulation", a.packageInstalled("accountsservice")) -} - -func (a *ArchDistribution) detectDMSGreeter() deps.Dependency { - return a.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", a.packageInstalled("greetd-dms-greeter-git")) -} - -func (a *ArchDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("pacman", "-Q", pkg) - err := cmd.Run() - return err == nil -} - -// parseSRCINFODeps reads a .SRCINFO file and returns runtime dep and makedep package -func parseSRCINFODeps(srcinfoPath string) (deps []string, makedeps []string, err error) { - data, err := os.ReadFile(srcinfoPath) - if err != nil { - return nil, nil, err - } - for _, line := range strings.Split(string(data), "\n") { - line = strings.TrimSpace(line) - var pkg string - var target *[]string - switch { - case strings.HasPrefix(line, "makedepends = "): - pkg = strings.TrimPrefix(line, "makedepends = ") - target = &makedeps - case strings.HasPrefix(line, "depends = "): - pkg = strings.TrimPrefix(line, "depends = ") - target = &deps - default: - continue - } - // Strip version constraint (>=, <=, >, <, =) and colon-descriptions - if idx := strings.IndexAny(pkg, "><:="); idx >= 0 { - pkg = pkg[:idx] - } - pkg = strings.TrimSpace(pkg) - if pkg != "" { - *target = append(*target, pkg) - } - } - return deps, makedeps, nil -} - -func (a *ArchDistribution) isInSystemRepo(pkg string) bool { - return exec.Command("pacman", "-Si", pkg).Run() == nil -} - -func (a *ArchDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return a.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (a *ArchDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - "dms (DankMaterialShell)": a.getDMSMapping(variants["dms (DankMaterialShell)"]), - "git": {Name: "git", Repository: RepoTypeSystem}, - "quickshell": a.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": {Name: "greetd-dms-greeter-git", Repository: RepoTypeAUR}, - "matugen": a.getMatugenMapping(variants["matugen"]), - "dgop": {Name: "dgop", Repository: RepoTypeSystem}, - "ghostty": {Name: "ghostty", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = a.getHyprlandMapping(variants["hyprland"]) - packages["hyprctl"] = a.getHyprlandMapping(variants["hyprland"]) - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - packages["niri"] = a.getNiriMapping(variants["niri"]) - packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem} - } - - return packages -} - -func (a *ArchDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "quickshell-git", Repository: RepoTypeAUR} - } - // ! TODO - for now we're only forcing quickshell-git on ARCH, as other distros use DL repos which pin a newer quickshell - return PackageMapping{Name: "quickshell-git", Repository: RepoTypeAUR} -} - -func (a *ArchDistribution) getHyprlandMapping(_ deps.PackageVariant) PackageMapping { - return PackageMapping{Name: "hyprland", Repository: RepoTypeSystem} -} - -func (a *ArchDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "niri-git", Repository: RepoTypeAUR} - } - return PackageMapping{Name: "niri", Repository: RepoTypeSystem} -} - -func (a *ArchDistribution) getMatugenMapping(variant deps.PackageVariant) PackageMapping { - if runtime.GOARCH == "arm64" { - return PackageMapping{Name: "matugen-git", Repository: RepoTypeAUR} - } - - if variant == deps.VariantGit { - return PackageMapping{Name: "matugen-git", Repository: RepoTypeAUR} - } - return PackageMapping{Name: "matugen", Repository: RepoTypeSystem} -} - -func (a *ArchDistribution) getDMSMapping(variant deps.PackageVariant) PackageMapping { - if forceDMSGit || variant == deps.VariantGit { - return PackageMapping{Name: "dms-shell-git", Repository: RepoTypeAUR} - } - - if a.packageInstalled("dms-shell-git") { - return PackageMapping{Name: "dms-shell-git", Repository: RepoTypeAUR} - } - - return PackageMapping{Name: "dms-shell", Repository: RepoTypeSystem} -} - -func (a *ArchDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if a.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (a *ArchDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking base-devel...", - IsComplete: false, - LogOutput: "Checking if base-devel is installed", - } - - checkCmd := exec.CommandContext(ctx, "pacman", "-Qq", "base-devel") - if err := checkCmd.Run(); err == nil { - a.log("base-devel already installed") - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.10, - Step: "base-devel already installed", - IsComplete: false, - LogOutput: "base-devel is already installed on the system", - } - return nil - } - - a.log("Installing base-devel...") - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: "Installing base-devel...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo pacman -S --needed --noconfirm base-devel", - LogOutput: "Installing base-devel development tools", - } - - cmd := ExecSudoCommand(ctx, sudoPassword, "pacman -S --needed --noconfirm base-devel") - if err := a.runWithProgress(cmd, progressChan, PhasePrerequisites, 0.08, 0.10); err != nil { - return fmt.Errorf("failed to install base-devel: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.12, - Step: "base-devel installation complete", - IsComplete: false, - LogOutput: "base-devel successfully installed", - } - - return nil -} - -func (a *ArchDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := a.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, aurPkgs, manualPkgs, variantMap := a.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) - - if slices.Contains(aurPkgs, "quickshell-git") && slices.Contains(systemPkgs, "dms-shell") { - if err := a.preinstallQuickshellGit(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to preinstall quickshell-git: %w", err) - } - aurPkgs = slices.DeleteFunc(aurPkgs, func(p string) bool { return p == "quickshell-git" }) - } - - // Phase 3: System Packages - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := a.installSystemPackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install system packages: %w", err) - } - } - - // Phase 4: AUR Packages - if len(aurPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: fmt.Sprintf("Installing %d AUR packages...", len(aurPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing AUR packages: %s", strings.Join(aurPkgs, ", ")), - } - if err := a.installAURPackages(ctx, aurPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install AUR packages: %w", err) - } - } - - // Phase 5: Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := a.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Phase 6: Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - terminal := a.DetectTerminalFromDeps(dependencies) - if err := a.WriteEnvironmentConfig(terminal); err != nil { - a.log(fmt.Sprintf("Warning: failed to write environment config: %v", err)) - } - - if err := a.WriteWindowManagerConfig(wm); err != nil { - a.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) - } - - if err := a.EnableDMSService(ctx, wm); err != nil { - a.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) - } - - // Phase 7: Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (a *ArchDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, []string, map[string]deps.PackageVariant) { - systemPkgs := []string{} - aurPkgs := []string{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := a.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - if disabledFlags[dep.Name] { - continue - } - - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - manualPkgs = append(manualPkgs, dep.Name) - continue - } - - switch pkgInfo.Repository { - case RepoTypeAUR: - aurPkgs = append(aurPkgs, pkgInfo.Name) - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, aurPkgs, manualPkgs, variantMap -} - -func (a *ArchDistribution) preinstallQuickshellGit(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if a.packageInstalled("quickshell-git") { - return nil - } - - if a.packageInstalled("quickshell") { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.15, - Step: "Removing stable quickshell...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo pacman -Rdd --noconfirm quickshell", - LogOutput: "Removing stable quickshell so quickshell-git can be installed", - } - cmd := ExecSudoCommand(ctx, sudoPassword, "pacman -Rdd --noconfirm quickshell") - if err := a.runWithProgress(cmd, progressChan, PhaseAURPackages, 0.15, 0.18); err != nil { - return fmt.Errorf("failed to remove stable quickshell: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.18, - Step: "Building quickshell-git before system packages...", - IsComplete: false, - CommandInfo: "Installing quickshell-git ahead of dms-shell to avoid conflict", - } - return a.installSingleAURPackage(ctx, "quickshell-git", sudoPassword, progressChan, 0.18, 0.32) -} - -func (a *ArchDistribution) installSystemPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - a.log(fmt.Sprintf("Installing system packages: %s", strings.Join(packages, ", "))) - - args := []string{"pacman", "-S", "--needed", "--noconfirm"} - if slices.Contains(packages, "dms-shell") { - args = append(args, "--assume-installed", "dms-shell-compositor=1") - } - args = append(args, packages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return a.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60) -} - -func (a *ArchDistribution) installAURPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - a.log(fmt.Sprintf("Installing AUR packages manually: %s", strings.Join(packages, ", "))) - - hasNiri := false - for _, pkg := range packages { - if pkg == "niri-git" { - hasNiri = true - } - } - - // If niri is in the list, install makepkg-git-lfs-proto first if not already installed - if hasNiri { - if !a.packageInstalled("makepkg-git-lfs-proto") { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: "Installing makepkg-git-lfs-proto for niri...", - IsComplete: false, - CommandInfo: "Installing prerequisite for niri-git", - } - - if err := a.installSingleAURPackage(ctx, "makepkg-git-lfs-proto", sudoPassword, progressChan, 0.65, 0.67); err != nil { - return fmt.Errorf("failed to install makepkg-git-lfs-proto prerequisite for niri: %w", err) - } - } - } - - // Reorder packages to ensure dms-shell-git dependencies are installed first - orderedPackages := a.reorderAURPackages(packages) - - baseProgress := 0.67 - progressStep := 0.13 / float64(len(orderedPackages)) - - for i, pkg := range orderedPackages { - currentProgress := baseProgress + (float64(i) * progressStep) - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: currentProgress, - Step: fmt.Sprintf("Installing AUR package %s (%d/%d)...", pkg, i+1, len(packages)), - IsComplete: false, - CommandInfo: fmt.Sprintf("Building and installing %s", pkg), - } - - if err := a.installSingleAURPackage(ctx, pkg, sudoPassword, progressChan, currentProgress, currentProgress+progressStep); err != nil { - return fmt.Errorf("failed to install AUR package %s: %w", pkg, err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.80, - Step: "All AUR packages installed successfully", - IsComplete: false, - LogOutput: fmt.Sprintf("Successfully installed AUR packages: %s", strings.Join(packages, ", ")), - } - - return nil -} - -func (a *ArchDistribution) reorderAURPackages(packages []string) []string { - dmsDepencies := []string{"quickshell", "quickshell-git", "dgop"} - - var deps []string - var others []string - var dmsShell []string - - for _, pkg := range packages { - if pkg == "dms-shell-git" { - dmsShell = append(dmsShell, pkg) - } else { - isDep := false - if slices.Contains(dmsDepencies, pkg) { - deps = append(deps, pkg) - isDep = true - } - if !isDep { - others = append(others, pkg) - } - } - } - - result := append(deps, others...) - result = append(result, dmsShell...) - return result -} - -func (a *ArchDistribution) installSingleAURPackage(ctx context.Context, pkg, sudoPassword string, progressChan chan<- InstallProgressMsg, startProgress, endProgress float64) error { - return a.installSingleAURPackageInternal(ctx, pkg, sudoPassword, progressChan, startProgress, endProgress, make(map[string]bool)) -} - -func (a *ArchDistribution) installSingleAURPackageInternal(ctx context.Context, pkg, sudoPassword string, progressChan chan<- InstallProgressMsg, startProgress, endProgress float64, visited map[string]bool) error { - if visited[pkg] { - a.log(fmt.Sprintf("Skipping %s (already being installed, cycle detected)", pkg)) - return nil - } - visited[pkg] = true - - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - buildDir := filepath.Join(homeDir, ".cache", "dankinstall", "aur-builds", pkg) - - // Clean up any existing cache first - if err := os.RemoveAll(buildDir); err != nil { - a.log(fmt.Sprintf("Warning: failed to clean existing cache for %s: %v", pkg, err)) - } - - if err := os.MkdirAll(buildDir, 0o755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - defer func() { - if removeErr := os.RemoveAll(buildDir); removeErr != nil { - a.log(fmt.Sprintf("Warning: failed to cleanup build directory %s: %v", buildDir, removeErr)) - } - }() - - // Clone the AUR package - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.1*(endProgress-startProgress), - Step: fmt.Sprintf("Cloning %s from AUR...", pkg), - IsComplete: false, - CommandInfo: fmt.Sprintf("git clone https://aur.archlinux.org/%s.git", pkg), - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", fmt.Sprintf("https://aur.archlinux.org/%s.git", pkg), filepath.Join(buildDir, pkg)) - if err := a.runWithProgress(cloneCmd, progressChan, PhaseAURPackages, startProgress+0.1*(endProgress-startProgress), startProgress+0.2*(endProgress-startProgress)); err != nil { - return fmt.Errorf("failed to clone %s: %w", pkg, err) - } - - packageDir := filepath.Join(buildDir, pkg) - - if pkg == "niri-git" { - pkgbuildPath := filepath.Join(packageDir, "PKGBUILD") - sedCmd := exec.CommandContext(ctx, "sed", "-i", "s/makepkg-git-lfs-proto//g", pkgbuildPath) - if err := sedCmd.Run(); err != nil { - return fmt.Errorf("failed to patch PKGBUILD for niri-git: %w", err) - } - - srcinfoPath := filepath.Join(packageDir, ".SRCINFO") - sedCmd2 := exec.CommandContext(ctx, "sed", "-i", "/makedepends = makepkg-git-lfs-proto/d", srcinfoPath) - if err := sedCmd2.Run(); err != nil { - return fmt.Errorf("failed to patch .SRCINFO for niri-git: %w", err) - } - } - - if pkg == "dms-shell-git" { - srcinfoPath := filepath.Join(packageDir, ".SRCINFO") - depsToRemove := []string{ - "depends = quickshell", - "depends = dgop", - } - - for _, dep := range depsToRemove { - sedCmd := exec.CommandContext(ctx, "sed", "-i", fmt.Sprintf("/%s/d", dep), srcinfoPath) - if err := sedCmd.Run(); err != nil { - return fmt.Errorf("failed to remove dependency %s from .SRCINFO for %s: %w", dep, pkg, err) - } - } - } - - // Remove all optdepends from .SRCINFO for all packages - srcinfoPath := filepath.Join(packageDir, ".SRCINFO") - optdepsCmd := exec.CommandContext(ctx, "sed", "-i", "/^[[:space:]]*optdepends = /d", srcinfoPath) - if err := optdepsCmd.Run(); err != nil { - return fmt.Errorf("failed to remove optdepends from .SRCINFO for %s: %w", pkg, err) - } - - srcinfoPath = filepath.Join(packageDir, ".SRCINFO") - { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.3*(endProgress-startProgress), - Step: fmt.Sprintf("Resolving dependencies for %s...", pkg), - IsComplete: false, - CommandInfo: "Classifying dependencies as system or AUR", - } - - runtimeDeps, makeDeps, err := parseSRCINFODeps(srcinfoPath) - if err != nil { - return fmt.Errorf("failed to parse .SRCINFO for %s: %w", pkg, err) - } - - seen := make(map[string]bool) - var systemPkgs []string - var aurPkgs []string - - for _, dep := range append(runtimeDeps, makeDeps...) { - if seen[dep] || a.packageInstalled(dep) { - continue - } - seen[dep] = true - if a.isInSystemRepo(dep) { - systemPkgs = append(systemPkgs, dep) - } else { - aurPkgs = append(aurPkgs, dep) - } - } - - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.32*(endProgress-startProgress), - Step: fmt.Sprintf("Installing %d system dependencies for %s...", len(systemPkgs), pkg), - IsComplete: false, - CommandInfo: fmt.Sprintf("sudo pacman -S --needed --noconfirm %s", strings.Join(systemPkgs, " ")), - } - if err := a.installSystemPackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install system dependencies for %s: %w", pkg, err) - } - } - - for _, aurDep := range aurPkgs { - a.log(fmt.Sprintf("Dependency %s is AUR-only, building from source...", aurDep)) - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.35*(endProgress-startProgress), - Step: fmt.Sprintf("Installing AUR dependency %s for %s...", aurDep, pkg), - IsComplete: false, - CommandInfo: fmt.Sprintf("Building AUR dependency: %s", aurDep), - } - if err := a.installSingleAURPackageInternal(ctx, aurDep, sudoPassword, progressChan, - startProgress+0.35*(endProgress-startProgress), - startProgress+0.39*(endProgress-startProgress), - visited, - ); err != nil { - return fmt.Errorf("failed to install AUR dependency %s for %s: %w", aurDep, pkg, err) - } - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.4*(endProgress-startProgress), - Step: fmt.Sprintf("Building %s...", pkg), - IsComplete: false, - CommandInfo: "makepkg --noconfirm", - } - - buildCmd := exec.CommandContext(ctx, "makepkg", "--noconfirm") - buildCmd.Dir = packageDir - buildCmd.Env = append(os.Environ(), "PKGEXT=.pkg.tar") - - if err := a.runWithProgress(buildCmd, progressChan, PhaseAURPackages, startProgress+0.4*(endProgress-startProgress), startProgress+0.7*(endProgress-startProgress)); err != nil { - return fmt.Errorf("failed to build %s: %w", pkg, err) - } - - // Find built package file - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.7*(endProgress-startProgress), - Step: fmt.Sprintf("Installing %s...", pkg), - IsComplete: false, - CommandInfo: "sudo pacman -U built-package", - } - - var files []string - matches, _ := filepath.Glob(filepath.Join(packageDir, "*.pkg.tar*")) - files = matches - - if len(files) == 0 { - return fmt.Errorf("no package files found after building %s", pkg) - } - - installArgs := []string{"pacman", "-U", "--noconfirm"} - installArgs = append(installArgs, files...) - - installCmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(installArgs, " ")) - - fileNames := make([]string, len(files)) - for i, f := range files { - fileNames[i] = filepath.Base(f) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress + 0.7*(endProgress-startProgress), - LogOutput: fmt.Sprintf("Installing packages: %s", strings.Join(fileNames, ", ")), - } - - if err := a.runWithProgress(installCmd, progressChan, PhaseAURPackages, startProgress+0.7*(endProgress-startProgress), endProgress); err != nil { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: startProgress, - LogOutput: fmt.Sprintf("ERROR: pacman -U failed for %s with error: %v", pkg, err), - Error: err, - } - return fmt.Errorf("failed to install built package %s: %w", pkg, err) - } - - a.log(fmt.Sprintf("Successfully installed AUR package: %s", pkg)) - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/base.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/base.go deleted file mode 100644 index 63555c8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/base.go +++ /dev/null @@ -1,721 +0,0 @@ -package distros - -import ( - "bufio" - "context" - _ "embed" - "fmt" - "os" - "os/exec" - "path/filepath" - "regexp" - "runtime" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/version" -) - -const ( - forceQuickshellGit = false - forceDMSGit = false -) - -// BaseDistribution provides common functionality for all distributions -type BaseDistribution struct { - logChan chan<- string -} - -// NewBaseDistribution creates a new base distribution -func NewBaseDistribution(logChan chan<- string) *BaseDistribution { - return &BaseDistribution{ - logChan: logChan, - } -} - -// Common helper methods -func (b *BaseDistribution) commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -func (b *BaseDistribution) CommandExists(cmd string) bool { - return b.commandExists(cmd) -} - -func (b *BaseDistribution) log(message string) { - if b.logChan != nil { - b.logChan <- message - } -} - -func (b *BaseDistribution) logError(message string, err error) { - errorMsg := fmt.Sprintf("ERROR: %s: %v", message, err) - b.log(errorMsg) -} - -// escapeSingleQuotes escapes single quotes in a string for safe use in bash single-quoted strings. -// It replaces each ' with '\” which closes the quote, adds an escaped quote, and reopens the quote. -// This prevents shell injection and syntax errors when passwords contain single quotes or apostrophes. -func escapeSingleQuotes(s string) string { - return strings.ReplaceAll(s, "'", "'\\''") -} - -// MakeSudoCommand creates a command string that safely passes password to sudo. -// This helper escapes special characters in the password to prevent shell injection -// and syntax errors when passwords contain single quotes, apostrophes, or other special chars. -func MakeSudoCommand(sudoPassword string, command string) string { - return fmt.Sprintf("echo '%s' | sudo -S %s", escapeSingleQuotes(sudoPassword), command) -} - -// ExecSudoCommand creates an exec.Cmd that runs a command with sudo using the provided password. -// The password is properly escaped to prevent shell injection and syntax errors. -func ExecSudoCommand(ctx context.Context, sudoPassword string, command string) *exec.Cmd { - cmdStr := MakeSudoCommand(sudoPassword, command) - return exec.CommandContext(ctx, "bash", "-c", cmdStr) -} - -func (b *BaseDistribution) detectCommand(name, description string) deps.Dependency { - status := deps.StatusMissing - if b.commandExists(name) { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: name, - Status: status, - Description: description, - Required: true, - } -} - -func (b *BaseDistribution) detectPackage(name, description string, installed bool) deps.Dependency { - status := deps.StatusMissing - if installed { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: name, - Status: status, - Description: description, - Required: true, - } -} - -func (b *BaseDistribution) detectOptionalPackage(name, description string, installed bool) deps.Dependency { - status := deps.StatusMissing - if installed { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: name, - Status: status, - Description: description, - Required: false, - } -} - -func (b *BaseDistribution) detectGit() deps.Dependency { - return b.detectCommand("git", "Version control system") -} - -func (b *BaseDistribution) detectMatugen() deps.Dependency { - return b.detectCommand("matugen", "Material Design color generation tool") -} - -func (b *BaseDistribution) detectDgop() deps.Dependency { - return b.detectCommand("dgop", "Desktop portal management tool") -} - -func (b *BaseDistribution) detectDMS() deps.Dependency { - dmsPath := filepath.Join(os.Getenv("HOME"), ".config/quickshell/dms") - - status := deps.StatusMissing - currentVersion := "" - - if _, err := os.Stat(dmsPath); err == nil { - status = deps.StatusInstalled - - // Only get current version, don't check for updates (lazy loading) - current, err := version.GetCurrentDMSVersion() - if err == nil { - currentVersion = current - } - } - - dep := deps.Dependency{ - Name: "dms (DankMaterialShell)", - Status: status, - Description: "Desktop Management System configuration", - Required: true, - CanToggle: true, - } - - if currentVersion != "" { - dep.Version = currentVersion - } - - return dep -} - -func (b *BaseDistribution) detectSpecificTerminal(terminal deps.Terminal) deps.Dependency { - switch terminal { - case deps.TerminalGhostty: - status := deps.StatusMissing - if b.commandExists("ghostty") { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: "ghostty", - Status: status, - Description: "A fast, native terminal emulator built in Zig.", - Required: true, - } - case deps.TerminalKitty: - status := deps.StatusMissing - if b.commandExists("kitty") { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: "kitty", - Status: status, - Description: "A feature-rich, customizable terminal emulator.", - Required: true, - } - case deps.TerminalAlacritty: - status := deps.StatusMissing - if b.commandExists("alacritty") { - status = deps.StatusInstalled - } - return deps.Dependency{ - Name: "alacritty", - Status: status, - Description: "A simple terminal emulator. (No dynamic theming)", - Required: true, - } - default: - return b.detectSpecificTerminal(deps.TerminalGhostty) - } -} - -func (b *BaseDistribution) detectHyprlandTools() []deps.Dependency { - var dependencies []deps.Dependency - - tools := []struct { - name string - description string - }{ - {"hyprctl", "Hyprland control utility"}, - {"jq", "JSON processor"}, - } - - for _, tool := range tools { - status := deps.StatusMissing - if b.commandExists(tool.name) { - status = deps.StatusInstalled - } - - dependencies = append(dependencies, deps.Dependency{ - Name: tool.name, - Status: status, - Description: tool.description, - Required: true, - }) - } - - return dependencies -} - -func (b *BaseDistribution) detectQuickshell() deps.Dependency { - if !b.commandExists("qs") { - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusMissing, - Description: "QtQuick based desktop shell toolkit", - Required: true, - Variant: deps.VariantStable, - CanToggle: true, - } - } - - cmd := exec.Command("qs", "--version") - output, err := cmd.Output() - if err != nil { - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusNeedsReinstall, - Description: "QtQuick based desktop shell toolkit (version check failed)", - Required: true, - Variant: deps.VariantStable, - CanToggle: true, - } - } - - versionStr := string(output) - versionRegex := regexp.MustCompile(`quickshell (\d+\.\d+\.\d+)`) - matches := versionRegex.FindStringSubmatch(versionStr) - - if len(matches) < 2 { - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusNeedsReinstall, - Description: "QtQuick based desktop shell toolkit (unknown version)", - Required: true, - Variant: deps.VariantStable, - CanToggle: true, - } - } - - version := matches[1] - variant := deps.VariantStable - if strings.Contains(versionStr, "git") || strings.Contains(versionStr, "+") { - variant = deps.VariantGit - } - - if b.versionCompare(version, "0.2.0") >= 0 { - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusInstalled, - Version: version, - Description: "QtQuick based desktop shell toolkit", - Required: true, - Variant: variant, - CanToggle: true, - } - } - - return deps.Dependency{ - Name: "quickshell", - Status: deps.StatusNeedsUpdate, - Variant: variant, - CanToggle: true, - Version: version, - Description: "QtQuick based desktop shell toolkit (needs 0.2.0+)", - Required: true, - } -} - -func (b *BaseDistribution) detectWindowManager(wm deps.WindowManager) deps.Dependency { - switch wm { - case deps.WindowManagerHyprland: - status := deps.StatusMissing - variant := deps.VariantStable - version := "" - - if b.commandExists("hyprland") || b.commandExists("Hyprland") { - status = deps.StatusInstalled - cmd := exec.Command("hyprctl", "version") - if output, err := cmd.Output(); err == nil { - outStr := string(output) - if strings.Contains(outStr, "git") || strings.Contains(outStr, "dirty") { - variant = deps.VariantGit - } - if versionRegex := regexp.MustCompile(`v(\d+\.\d+\.\d+)`); versionRegex.MatchString(outStr) { - matches := versionRegex.FindStringSubmatch(outStr) - if len(matches) > 1 { - version = matches[1] - } - } - } - } - return deps.Dependency{ - Name: "hyprland", - Status: status, - Version: version, - Description: "Dynamic tiling Wayland compositor", - Required: true, - Variant: variant, - CanToggle: true, - } - case deps.WindowManagerNiri: - status := deps.StatusMissing - variant := deps.VariantStable - version := "" - - if b.commandExists("niri") { - status = deps.StatusInstalled - cmd := exec.Command("niri", "--version") - if output, err := cmd.Output(); err == nil { - outStr := string(output) - if strings.Contains(outStr, "git") || strings.Contains(outStr, "+") { - variant = deps.VariantGit - } - if versionRegex := regexp.MustCompile(`niri (\d+\.\d+)`); versionRegex.MatchString(outStr) { - matches := versionRegex.FindStringSubmatch(outStr) - if len(matches) > 1 { - version = matches[1] - } - } - } - } - return deps.Dependency{ - Name: "niri", - Status: status, - Version: version, - Description: "Scrollable-tiling Wayland compositor", - Required: true, - Variant: variant, - CanToggle: true, - } - default: - return deps.Dependency{ - Name: "unknown-wm", - Status: deps.StatusMissing, - Description: "Unknown window manager", - Required: true, - } - } -} - -// Version comparison helper -func (b *BaseDistribution) versionCompare(v1, v2 string) int { - parts1 := strings.Split(v1, ".") - parts2 := strings.Split(v2, ".") - - for i := 0; i < len(parts1) && i < len(parts2); i++ { - if parts1[i] < parts2[i] { - return -1 - } - if parts1[i] > parts2[i] { - return 1 - } - } - - if len(parts1) < len(parts2) { - return -1 - } - if len(parts1) > len(parts2) { - return 1 - } - - return 0 -} - -// Common installation helper -func (b *BaseDistribution) runWithProgress(cmd *exec.Cmd, progressChan chan<- InstallProgressMsg, phase InstallPhase, startProgress, endProgress float64) error { - return b.runWithProgressTimeout(cmd, progressChan, phase, startProgress, endProgress, 20*time.Minute) -} - -func (b *BaseDistribution) runWithProgressTimeout(cmd *exec.Cmd, progressChan chan<- InstallProgressMsg, phase InstallPhase, startProgress, endProgress float64, timeout time.Duration) error { - return b.runWithProgressStepTimeout(cmd, progressChan, phase, startProgress, endProgress, "Installing...", timeout) -} - -func (b *BaseDistribution) runWithProgressStep(cmd *exec.Cmd, progressChan chan<- InstallProgressMsg, phase InstallPhase, startProgress, endProgress float64, stepMessage string) error { - return b.runWithProgressStepTimeout(cmd, progressChan, phase, startProgress, endProgress, stepMessage, 20*time.Minute) -} - -func (b *BaseDistribution) runWithProgressStepTimeout(cmd *exec.Cmd, progressChan chan<- InstallProgressMsg, phase InstallPhase, startProgress, endProgress float64, stepMessage string, timeoutDuration time.Duration) error { - stdout, err := cmd.StdoutPipe() - if err != nil { - return fmt.Errorf("failed to create stdout pipe: %w", err) - } - stderr, err := cmd.StderrPipe() - if err != nil { - return fmt.Errorf("failed to create stderr pipe: %w", err) - } - - if err := cmd.Start(); err != nil { - return err - } - - outputChan := make(chan string, 100) - done := make(chan error, 1) - - go func() { - scanner := bufio.NewScanner(stdout) - for scanner.Scan() { - line := scanner.Text() - b.log(line) - outputChan <- line - } - }() - - go func() { - scanner := bufio.NewScanner(stderr) - for scanner.Scan() { - line := scanner.Text() - b.log(line) - outputChan <- line - } - }() - - go func() { - done <- cmd.Wait() - close(outputChan) - }() - - ticker := time.NewTicker(200 * time.Millisecond) - defer ticker.Stop() - - progress := startProgress - progressStep := (endProgress - startProgress) / 50 - lastOutput := "" - - var timeout *time.Timer - var timeoutChan <-chan time.Time - if timeoutDuration > 0 { - timeout = time.NewTimer(timeoutDuration) - defer timeout.Stop() - timeoutChan = timeout.C - } - - for { - select { - case err := <-done: - if err != nil { - b.logError("Command execution failed", err) - b.log(fmt.Sprintf("Last output before failure: %s", lastOutput)) - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: startProgress, - Step: "Command failed", - IsComplete: false, - LogOutput: lastOutput, - Error: err, - } - return err - } - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: endProgress, - Step: "Installation step complete", - IsComplete: false, - LogOutput: lastOutput, - } - return nil - case output, ok := <-outputChan: - if ok { - lastOutput = output - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: progress, - Step: stepMessage, - IsComplete: false, - LogOutput: output, - } - if timeout != nil { - timeout.Reset(timeoutDuration) - } - } - case <-timeoutChan: - if cmd.Process != nil { - cmd.Process.Kill() - } - err := fmt.Errorf("installation timed out after %v", timeoutDuration) - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: startProgress, - Step: "Installation timed out", - IsComplete: false, - LogOutput: lastOutput, - Error: err, - } - return err - case <-ticker.C: - if progress < endProgress-0.01 { - progress += progressStep - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: progress, - Step: "Installing...", - IsComplete: false, - LogOutput: lastOutput, - } - } - } - } -} - -func (b *BaseDistribution) DetectTerminalFromDeps(dependencies []deps.Dependency) deps.Terminal { - for _, dep := range dependencies { - switch dep.Name { - case "ghostty": - return deps.TerminalGhostty - case "kitty": - return deps.TerminalKitty - case "alacritty": - return deps.TerminalAlacritty - } - } - return deps.TerminalGhostty -} - -func (b *BaseDistribution) WriteEnvironmentConfig(terminal deps.Terminal) error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get home directory: %w", err) - } - - envDir := filepath.Join(homeDir, ".config", "environment.d") - if err := os.MkdirAll(envDir, 0o755); err != nil { - return fmt.Errorf("failed to create environment.d directory: %w", err) - } - - var terminalCmd string - switch terminal { - case deps.TerminalGhostty: - terminalCmd = "ghostty" - case deps.TerminalKitty: - terminalCmd = "kitty" - case deps.TerminalAlacritty: - terminalCmd = "alacritty" - default: - terminalCmd = "ghostty" - } - - content := fmt.Sprintf(`ELECTRON_OZONE_PLATFORM_HINT=auto -TERMINAL=%s -`, terminalCmd) - - envFile := filepath.Join(envDir, "90-dms.conf") - if err := os.WriteFile(envFile, []byte(content), 0o644); err != nil { - return fmt.Errorf("failed to write environment config: %w", err) - } - - b.log(fmt.Sprintf("Wrote environment config to %s", envFile)) - return nil -} - -func (b *BaseDistribution) EnableDMSService(ctx context.Context, wm deps.WindowManager) error { - switch wm { - case deps.WindowManagerNiri: - if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "niri.service", "dms").Run(); err != nil { - b.log("Warning: failed to add dms as a want for niri.service") - } - case deps.WindowManagerHyprland: - if err := exec.CommandContext(ctx, "systemctl", "--user", "add-wants", "hyprland-session.target", "dms").Run(); err != nil { - b.log("Warning: failed to add dms as a want for hyprland-session.target") - } - } - - return nil -} - -func (b *BaseDistribution) WriteWindowManagerConfig(wm deps.WindowManager) error { - if wm == deps.WindowManagerHyprland { - if err := b.WriteHyprlandSessionTarget(); err != nil { - return fmt.Errorf("failed to write hyprland session target: %w", err) - } - } - return nil -} - -func (b *BaseDistribution) WriteHyprlandSessionTarget() error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get home directory: %w", err) - } - - targetDir := filepath.Join(homeDir, ".config", "systemd", "user") - if err := os.MkdirAll(targetDir, 0o755); err != nil { - return fmt.Errorf("failed to create systemd user directory: %w", err) - } - - targetPath := filepath.Join(targetDir, "hyprland-session.target") - content := `[Unit] -Description=Hyprland Session Target -Requires=graphical-session.target -After=graphical-session.target -` - - if err := os.WriteFile(targetPath, []byte(content), 0o644); err != nil { - return fmt.Errorf("failed to write hyprland-session.target: %w", err) - } - - b.log(fmt.Sprintf("Wrote hyprland-session.target to %s", targetPath)) - return nil -} - -// installDMSBinary installs the DMS binary from GitHub releases -func (b *BaseDistribution) installDMSBinary(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - b.log("Installing/updating DMS binary...") - - // Detect architecture - arch := runtime.GOARCH - switch arch { - case "amd64": - case "arm64": - default: - return fmt.Errorf("unsupported architecture for DMS: %s", arch) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.80, - Step: "Downloading DMS binary...", - IsComplete: false, - CommandInfo: fmt.Sprintf("Downloading dms-%s.gz", arch), - } - - // Get latest release version - latestVersionCmd := exec.CommandContext(ctx, "bash", "-c", - `curl -s https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/'`) - versionOutput, err := latestVersionCmd.Output() - if err != nil { - return fmt.Errorf("failed to get latest DMS version: %w", err) - } - version := strings.TrimSpace(string(versionOutput)) - if version == "" { - return fmt.Errorf("could not determine latest DMS version") - } - - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - tmpDir := filepath.Join(homeDir, ".cache", "dankinstall", "manual-builds") - if err := os.MkdirAll(tmpDir, 0o755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - // Download the gzipped binary - downloadURL := fmt.Sprintf("https://github.com/AvengeMedia/DankMaterialShell/releases/download/%s/dms-cli-%s.gz", version, arch) - gzPath := filepath.Join(tmpDir, "dms.gz") - - downloadCmd := exec.CommandContext(ctx, "curl", "-L", downloadURL, "-o", gzPath) - if err := downloadCmd.Run(); err != nil { - return fmt.Errorf("failed to download DMS binary: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.85, - Step: "Extracting DMS binary...", - IsComplete: false, - CommandInfo: "gunzip dms.gz", - } - - // Extract the binary - extractCmd := exec.CommandContext(ctx, "gunzip", gzPath) - if err := extractCmd.Run(); err != nil { - return fmt.Errorf("failed to extract DMS binary: %w", err) - } - - binaryPath := filepath.Join(tmpDir, "dms") - - // Make it executable - chmodCmd := exec.CommandContext(ctx, "chmod", "+x", binaryPath) - if err := chmodCmd.Run(); err != nil { - return fmt.Errorf("failed to make DMS binary executable: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.88, - Step: "Installing DMS to /usr/local/bin...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cp dms /usr/local/bin/", - } - - // Install to /usr/local/bin - installCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("cp %s /usr/local/bin/dms", binaryPath)) - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install DMS binary: %w", err) - } - - b.log("DMS binary installed successfully") - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/base_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/base_test.go deleted file mode 100644 index 51e86d7..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/base_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package distros - -import ( - "os" - "os/exec" - "path/filepath" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -func TestBaseDistribution_detectDMS_NotInstalled(t *testing.T) { - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - - tempDir := t.TempDir() - os.Setenv("HOME", tempDir) - - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - dep := base.detectDMS() - - if dep.Status != deps.StatusMissing { - t.Errorf("Expected StatusMissing, got %d", dep.Status) - } - - if dep.Name != "dms (DankMaterialShell)" { - t.Errorf("Expected name 'dms (DankMaterialShell)', got %s", dep.Name) - } - - if !dep.Required { - t.Error("Expected Required to be true") - } -} - -func TestBaseDistribution_detectDMS_Installed(t *testing.T) { - if !utils.CommandExists("git") { - t.Skip("git not available") - } - - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0o755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - exec.Command("git", "init", dmsPath).Run() - exec.Command("git", "-C", dmsPath, "config", "user.email", "test@test.com").Run() - exec.Command("git", "-C", dmsPath, "config", "user.name", "Test User").Run() - exec.Command("git", "-C", dmsPath, "checkout", "-b", "master").Run() - - testFile := filepath.Join(dmsPath, "test.txt") - os.WriteFile(testFile, []byte("test"), 0o644) - exec.Command("git", "-C", dmsPath, "add", ".").Run() - exec.Command("git", "-C", dmsPath, "commit", "-m", "initial").Run() - - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - dep := base.detectDMS() - - if dep.Status == deps.StatusMissing { - t.Error("Expected DMS to be detected as installed") - } - - if dep.Name != "dms (DankMaterialShell)" { - t.Errorf("Expected name 'dms (DankMaterialShell)', got %s", dep.Name) - } - - if !dep.Required { - t.Error("Expected Required to be true") - } - - t.Logf("Status: %d, Version: %s", dep.Status, dep.Version) -} - -func TestBaseDistribution_detectDMS_NeedsUpdate(t *testing.T) { - if !utils.CommandExists("git") { - t.Skip("git not available") - } - - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0o755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - exec.Command("git", "init", dmsPath).Run() - exec.Command("git", "-C", dmsPath, "config", "user.email", "test@test.com").Run() - exec.Command("git", "-C", dmsPath, "config", "user.name", "Test User").Run() - exec.Command("git", "-C", dmsPath, "remote", "add", "origin", "https://github.com/AvengeMedia/DankMaterialShell.git").Run() - - testFile := filepath.Join(dmsPath, "test.txt") - os.WriteFile(testFile, []byte("test"), 0o644) - exec.Command("git", "-C", dmsPath, "add", ".").Run() - exec.Command("git", "-C", dmsPath, "commit", "-m", "initial").Run() - exec.Command("git", "-C", dmsPath, "tag", "v0.0.1").Run() - exec.Command("git", "-C", dmsPath, "checkout", "v0.0.1").Run() - - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - dep := base.detectDMS() - - if dep.Name != "dms (DankMaterialShell)" { - t.Errorf("Expected name 'dms (DankMaterialShell)', got %s", dep.Name) - } - - if !dep.Required { - t.Error("Expected Required to be true") - } - - t.Logf("Status: %d, Version: %s", dep.Status, dep.Version) -} - -func TestBaseDistribution_detectDMS_DirectoryWithoutGit(t *testing.T) { - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0o755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - dep := base.detectDMS() - - if dep.Status == deps.StatusMissing { - t.Error("Expected DMS to be detected as present") - } - - if dep.Name != "dms (DankMaterialShell)" { - t.Errorf("Expected name 'dms (DankMaterialShell)', got %s", dep.Name) - } - - if !dep.Required { - t.Error("Expected Required to be true") - } -} - -func TestBaseDistribution_NewBaseDistribution(t *testing.T) { - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - - if base == nil { - t.Fatal("NewBaseDistribution returned nil") - } - - if base.logChan == nil { - t.Error("logChan was not set") - } -} - -func TestBaseDistribution_versionCompare(t *testing.T) { - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - - tests := []struct { - v1 string - v2 string - expected int - }{ - {"0.1.0", "0.1.0", 0}, - {"0.1.0", "0.1.1", -1}, - {"0.1.1", "0.1.0", 1}, - {"0.2.0", "0.1.9", 1}, - {"1.0.0", "0.9.9", 1}, - } - - for _, tt := range tests { - result := base.versionCompare(tt.v1, tt.v2) - if result != tt.expected { - t.Errorf("versionCompare(%q, %q) = %d; want %d", tt.v1, tt.v2, result, tt.expected) - } - } -} - -func TestBaseDistribution_versionCompare_WithPrefix(t *testing.T) { - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - - tests := []struct { - v1 string - v2 string - expected int - }{ - {"v0.1.0", "v0.1.0", 0}, - {"v0.1.0", "v0.1.1", -1}, - {"v0.1.1", "v0.1.0", 1}, - } - - for _, tt := range tests { - result := base.versionCompare(tt.v1, tt.v2) - if result != tt.expected { - t.Errorf("versionCompare(%q, %q) = %d; want %d", tt.v1, tt.v2, result, tt.expected) - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/debian.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/debian.go deleted file mode 100644 index a3537ff..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/debian.go +++ /dev/null @@ -1,706 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -func init() { - Register("debian", "#A80030", FamilyDebian, func(config DistroConfig, logChan chan<- string) Distribution { - return NewDebianDistribution(config, logChan) - }) -} - -type DebianDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewDebianDistribution(config DistroConfig, logChan chan<- string) *DebianDistribution { - base := NewBaseDistribution(logChan) - return &DebianDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (d *DebianDistribution) GetID() string { - return d.config.ID -} - -func (d *DebianDistribution) GetColorHex() string { - return d.config.ColorHex -} - -func (d *DebianDistribution) GetFamily() DistroFamily { - return d.config.Family -} - -func (d *DebianDistribution) GetPackageManager() PackageManagerType { - return PackageManagerAPT -} - -func (d *DebianDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return d.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (d *DebianDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - dependencies = append(dependencies, d.detectDMS()) - - dependencies = append(dependencies, d.detectSpecificTerminal(terminal)) - - dependencies = append(dependencies, d.detectGit()) - dependencies = append(dependencies, d.detectWindowManager(wm)) - dependencies = append(dependencies, d.detectQuickshell()) - dependencies = append(dependencies, d.detectDMSGreeter()) - dependencies = append(dependencies, d.detectXDGPortal()) - dependencies = append(dependencies, d.detectAccountsService()) - - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, d.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, d.detectMatugen()) - dependencies = append(dependencies, d.detectDgop()) - - return dependencies, nil -} - -func (d *DebianDistribution) detectXDGPortal() deps.Dependency { - return d.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", d.packageInstalled("xdg-desktop-portal-gtk")) -} - -func (d *DebianDistribution) detectXwaylandSatellite() deps.Dependency { - return d.detectCommand("xwayland-satellite", "Xwayland support") -} - -func (d *DebianDistribution) detectAccountsService() deps.Dependency { - return d.detectPackage("accountsservice", "D-Bus interface for user account query and manipulation", d.packageInstalled("accountsservice")) -} - -func (d *DebianDistribution) detectDMSGreeter() deps.Dependency { - return d.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", d.packageInstalled("dms-greeter")) -} - -func (d *DebianDistribution) packageInstalled(pkg string) bool { - return debianPackageInstalledPrecisely(pkg) -} - -func debianPackageInstalledPrecisely(pkg string) bool { - cmd := exec.Command("dpkg-query", "-W", "-f=${db:Status-Status}", pkg) - output, err := cmd.Output() - if err != nil { - return false - } - return strings.TrimSpace(string(output)) == "installed" -} - -func debianRepoArchitecture(arch string) string { - switch arch { - case "amd64", "x86_64": - return "amd64" - case "arm64", "aarch64": - return "arm64" - default: - return arch - } -} - -func (d *DebianDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return d.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (d *DebianDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - // Standard APT packages - "git": {Name: "git", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - - // DMS packages from OBS with variant support - "dms (DankMaterialShell)": d.getDmsMapping(variants["dms (DankMaterialShell)"]), - "quickshell": d.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": {Name: "dms-greeter", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "matugen": {Name: "matugen", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "dgop": {Name: "dgop", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "ghostty": {Name: "ghostty", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - } - - if wm == deps.WindowManagerNiri { - niriVariant := variants["niri"] - packages["niri"] = d.getNiriMapping(niriVariant) - packages["xwayland-satellite"] = d.getXwaylandSatelliteMapping(niriVariant) - } - - return packages -} - -func (d *DebianDistribution) getDmsMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "dms-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:dms-git"} - } - return PackageMapping{Name: "dms", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:dms"} -} - -func (d *DebianDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "quickshell-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "quickshell", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} -} - -func (d *DebianDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "niri-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "niri", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} -} - -func (d *DebianDistribution) getXwaylandSatelliteMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "xwayland-satellite-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} -} - -func (d *DebianDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Updating package lists...", - IsComplete: false, - LogOutput: "Updating APT package lists", - } - - updateCmd := ExecSudoCommand(ctx, sudoPassword, "apt-get update") - if err := d.runWithProgress(updateCmd, progressChan, PhasePrerequisites, 0.06, 0.07); err != nil { - return fmt.Errorf("failed to update package lists: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: "Installing build-essential...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install -y build-essential", - LogOutput: "Installing build tools", - } - - checkCmd := exec.CommandContext(ctx, "dpkg", "-l", "build-essential") - if err := checkCmd.Run(); err != nil { - cmd := ExecSudoCommand(ctx, sudoPassword, "DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential") - if err := d.runWithProgress(cmd, progressChan, PhasePrerequisites, 0.08, 0.09); err != nil { - return fmt.Errorf("failed to install build-essential: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.10, - Step: "Installing development dependencies...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install -y curl wget git cmake ninja-build pkg-config gnupg libxcb-cursor-dev libglib2.0-dev libpolkit-agent-1-dev", - LogOutput: "Installing additional development tools", - } - - devToolsCmd := ExecSudoCommand(ctx, sudoPassword, - "DEBIAN_FRONTEND=noninteractive apt-get install -y curl wget git cmake ninja-build pkg-config gnupg libxcb-cursor-dev libglib2.0-dev libpolkit-agent-1-dev libjpeg-dev libpugixml-dev") - if err := d.runWithProgress(devToolsCmd, progressChan, PhasePrerequisites, 0.10, 0.12); err != nil { - return fmt.Errorf("failed to install development tools: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.12, - Step: "Prerequisites installation complete", - IsComplete: false, - LogOutput: "Prerequisites successfully installed", - } - - return nil -} - -func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := d.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, obsPkgs, manualPkgs, variantMap := d.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) - - // Enable OBS repositories - if len(obsPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.15, - Step: "Enabling OBS repositories...", - IsComplete: false, - LogOutput: "Setting up OBS repositories for additional packages", - } - if err := d.enableOBSRepos(ctx, obsPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to enable OBS repositories: %w", err) - } - } - - // System Packages - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := d.installAPTPackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install APT packages: %w", err) - } - } - - // OBS Packages - obsPkgNames := d.extractPackageNames(obsPkgs) - if len(obsPkgNames) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: fmt.Sprintf("Installing %d OBS packages...", len(obsPkgNames)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing OBS packages: %s", strings.Join(obsPkgNames, ", ")), - } - if err := d.installAPTPackages(ctx, obsPkgNames, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install OBS packages: %w", err) - } - } - - // Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.80, - Step: "Installing build dependencies...", - IsComplete: false, - LogOutput: "Installing build tools for manual compilation", - } - if err := d.installBuildDependencies(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install build dependencies: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := d.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - terminal := d.DetectTerminalFromDeps(dependencies) - if err := d.WriteEnvironmentConfig(terminal); err != nil { - d.log(fmt.Sprintf("Warning: failed to write environment config: %v", err)) - } - - if err := d.WriteWindowManagerConfig(wm); err != nil { - d.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) - } - - if err := d.EnableDMSService(ctx, wm); err != nil { - d.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) { - systemPkgs := []string{} - obsPkgs := []PackageMapping{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := d.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - if disabledFlags[dep.Name] { - continue - } - - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - d.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypeOBS: - obsPkgs = append(obsPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, obsPkgs, manualPkgs, variantMap -} - -func (d *DebianDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (d *DebianDistribution) aptInstallArgs(packages []string, minimal bool) []string { - args := []string{"DEBIAN_FRONTEND=noninteractive", "apt-get", "install", "-y"} - if minimal { - args = append(args, "--no-install-recommends") - } - return append(args, packages...) -} - -func (d *DebianDistribution) enableOBSRepos(ctx context.Context, obsPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - enabledRepos := make(map[string]bool) - - osInfo, err := GetOSInfo() - if err != nil { - return fmt.Errorf("failed to get OS info: %w", err) - } - - // Determine Debian version for OBS repository URL - debianVersion := "Debian_13" - if osInfo.VersionID == "testing" { - debianVersion = "Debian_Testing" - } else if osInfo.VersionCodename == "sid" || osInfo.VersionID == "sid" || strings.Contains(strings.ToLower(osInfo.PrettyName), "sid") || strings.Contains(strings.ToLower(osInfo.PrettyName), "unstable") { - debianVersion = "Debian_Unstable" - } - - for _, pkg := range obsPkgs { - if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] { - d.log(fmt.Sprintf("Enabling OBS repository: %s", pkg.RepoURL)) - - // RepoURL format: "home:AvengeMedia:danklinux" - repoPath := strings.ReplaceAll(pkg.RepoURL, ":", ":/") - repoName := strings.ReplaceAll(pkg.RepoURL, ":", "-") - baseURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/%s", repoPath, debianVersion) - - // Check if repository already exists - listFile := fmt.Sprintf("/etc/apt/sources.list.d/%s.list", repoName) - checkCmd := exec.CommandContext(ctx, "test", "-f", listFile) - if checkCmd.Run() == nil { - d.log(fmt.Sprintf("OBS repo %s already exists, skipping", pkg.RepoURL)) - enabledRepos[pkg.RepoURL] = true - continue - } - - keyringPath := fmt.Sprintf("/etc/apt/keyrings/%s.gpg", repoName) - - // Create keyrings directory if it doesn't exist - mkdirCmd := ExecSudoCommand(ctx, sudoPassword, "mkdir -p /etc/apt/keyrings") - if err := mkdirCmd.Run(); err != nil { - d.log(fmt.Sprintf("Warning: failed to create keyrings directory: %v", err)) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.18, - Step: fmt.Sprintf("Adding OBS GPG key for %s...", pkg.RepoURL), - NeedsSudo: true, - CommandInfo: fmt.Sprintf("curl & gpg to add key for %s", pkg.RepoURL), - } - - keyCmd := fmt.Sprintf("bash -c 'rm -f %s && curl -fsSL %s/Release.key | gpg --batch --dearmor -o %s'", keyringPath, baseURL, keyringPath) - cmd := ExecSudoCommand(ctx, sudoPassword, keyCmd) - if err := d.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.18, 0.20); err != nil { - return fmt.Errorf("failed to add OBS GPG key for %s: %w", pkg.RepoURL, err) - } - - // Add repository - repoLine := fmt.Sprintf("deb [signed-by=%s arch=%s] %s/ /", keyringPath, debianRepoArchitecture(osInfo.Architecture), baseURL) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.20, - Step: fmt.Sprintf("Adding OBS repository %s...", pkg.RepoURL), - NeedsSudo: true, - CommandInfo: fmt.Sprintf("echo '%s' | sudo tee %s", repoLine, listFile), - } - - addRepoCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("bash -c \"echo '%s' | tee %s\"", repoLine, listFile)) - if err := d.runWithProgress(addRepoCmd, progressChan, PhaseSystemPackages, 0.20, 0.22); err != nil { - return fmt.Errorf("failed to add OBS repo %s: %w", pkg.RepoURL, err) - } - - enabledRepos[pkg.RepoURL] = true - d.log(fmt.Sprintf("OBS repo %s enabled successfully", pkg.RepoURL)) - } - } - - if len(enabledRepos) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.25, - Step: "Updating package lists...", - NeedsSudo: true, - CommandInfo: "sudo apt-get update", - } - - updateCmd := ExecSudoCommand(ctx, sudoPassword, "apt-get update") - if err := d.runWithProgress(updateCmd, progressChan, PhaseSystemPackages, 0.25, 0.27); err != nil { - return fmt.Errorf("failed to update package lists after adding OBS repos: %w", err) - } - } - - return nil -} - -func (d *DebianDistribution) installAPTPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - d.log(fmt.Sprintf("Installing APT packages: %s", strings.Join(packages, ", "))) - - groups := orderedMinimalInstallGroups(packages) - totalGroups := len(groups) - - groupIndex := 0 - installGroup := func(groupPackages []string, minimal bool) error { - if len(groupPackages) == 0 { - return nil - } - - groupIndex++ - startProgress := 0.40 - endProgress := 0.60 - if totalGroups > 1 { - if groupIndex == 1 { - endProgress = 0.50 - } else { - startProgress = 0.50 - } - } - - args := d.aptInstallArgs(groupPackages, minimal) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: startProgress, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return d.runWithProgress(cmd, progressChan, PhaseSystemPackages, startProgress, endProgress) - } - - for _, group := range groups { - if err := installGroup(group.packages, group.minimal); err != nil { - return err - } - } - return nil -} - -func (d *DebianDistribution) installBuildDependencies(ctx context.Context, manualPkgs []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - buildDeps := make(map[string]bool) - - for _, pkg := range manualPkgs { - switch pkg { - case "niri": - buildDeps["curl"] = true - buildDeps["libxkbcommon-dev"] = true - buildDeps["libwayland-dev"] = true - buildDeps["libudev-dev"] = true - buildDeps["libinput-dev"] = true - buildDeps["libdisplay-info-dev"] = true - buildDeps["libpango1.0-dev"] = true - buildDeps["libcairo-dev"] = true - buildDeps["libpipewire-0.3-dev"] = true - buildDeps["libc6-dev"] = true - buildDeps["clang"] = true - buildDeps["libseat-dev"] = true - buildDeps["libgbm-dev"] = true - buildDeps["alacritty"] = true - buildDeps["fuzzel"] = true - case "quickshell": - buildDeps["qt6-base-dev"] = true - buildDeps["qt6-base-private-dev"] = true - buildDeps["qt6-declarative-dev"] = true - buildDeps["qt6-declarative-private-dev"] = true - buildDeps["qt6-wayland-dev"] = true - buildDeps["qt6-wayland-private-dev"] = true - buildDeps["qt6-tools-dev"] = true - buildDeps["libqt6svg6-dev"] = true - buildDeps["qt6-shadertools-dev"] = true - buildDeps["spirv-tools"] = true - buildDeps["libcli11-dev"] = true - buildDeps["libjemalloc-dev"] = true - buildDeps["libwayland-dev"] = true - buildDeps["wayland-protocols"] = true - buildDeps["libdrm-dev"] = true - buildDeps["libgbm-dev"] = true - buildDeps["libegl-dev"] = true - buildDeps["libgles2-mesa-dev"] = true - buildDeps["libgl1-mesa-dev"] = true - buildDeps["libxcb1-dev"] = true - buildDeps["libpipewire-0.3-dev"] = true - buildDeps["libpam0g-dev"] = true - case "ghostty": - buildDeps["curl"] = true - case "matugen": - buildDeps["curl"] = true - } - } - - for _, pkg := range manualPkgs { - switch pkg { - case "niri", "matugen": - if err := d.installRust(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Rust: %w", err) - } - case "dgop": - if err := d.installGo(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Go: %w", err) - } - } - } - - if len(buildDeps) == 0 { - return nil - } - - depList := make([]string, 0, len(buildDeps)) - for dep := range buildDeps { - depList = append(depList, dep) - } - - args := []string{"apt-get", "install", "-y"} - args = append(args, depList...) - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return d.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.80, 0.82) -} - -func (d *DebianDistribution) installRust(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if d.commandExists("cargo") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.82, - Step: "Installing rustup...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install rustup", - } - - rustupInstallCmd := ExecSudoCommand(ctx, sudoPassword, "DEBIAN_FRONTEND=noninteractive apt-get install -y rustup") - if err := d.runWithProgress(rustupInstallCmd, progressChan, PhaseSystemPackages, 0.82, 0.83); err != nil { - return fmt.Errorf("failed to install rustup: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.83, - Step: "Installing stable Rust toolchain...", - IsComplete: false, - CommandInfo: "rustup install stable", - } - - rustInstallCmd := exec.CommandContext(ctx, "bash", "-c", "rustup install stable && rustup default stable") - if err := d.runWithProgress(rustInstallCmd, progressChan, PhaseSystemPackages, 0.83, 0.84); err != nil { - return fmt.Errorf("failed to install Rust toolchain: %w", err) - } - - if !d.commandExists("cargo") { - d.log("Warning: cargo not found in PATH after Rust installation, trying to source environment") - } - - return nil -} - -func (d *DebianDistribution) installGo(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if d.commandExists("go") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.87, - Step: "Installing Go...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install golang-go", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, "DEBIAN_FRONTEND=noninteractive apt-get install -y golang-go") - return d.runWithProgress(installCmd, progressChan, PhaseSystemPackages, 0.87, 0.90) -} - -func (d *DebianDistribution) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - d.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - for _, pkg := range packages { - switch pkg { - default: - if err := d.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install %s: %w", pkg, err) - } - } - } - - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/factory.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/factory.go deleted file mode 100644 index 9c3826a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/factory.go +++ /dev/null @@ -1,19 +0,0 @@ -package distros - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -// NewDependencyDetector creates a DependencyDetector for the specified distribution -func NewDependencyDetector(distribution string, logChan chan<- string) (deps.DependencyDetector, error) { - distro, err := NewDistribution(distribution, logChan) - if err != nil { - return nil, err - } - return distro, nil -} - -// NewPackageInstaller creates a Distribution for package installation -func NewPackageInstaller(distribution string, logChan chan<- string) (Distribution, error) { - return NewDistribution(distribution, logChan) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/fedora.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/fedora.go deleted file mode 100644 index 4c16d4e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/fedora.go +++ /dev/null @@ -1,550 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -func init() { - Register("fedora", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) - Register("evernight", "#72B8DC", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) - Register("nobara", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) - Register("fedora-asahi-remix", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) - Register("bluefin", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) - Register("ultramarine", "#00078b", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution { - return NewFedoraDistribution(config, logChan) - }) -} - -type FedoraDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewFedoraDistribution(config DistroConfig, logChan chan<- string) *FedoraDistribution { - base := NewBaseDistribution(logChan) - return &FedoraDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (f *FedoraDistribution) GetID() string { - return f.config.ID -} - -func (f *FedoraDistribution) GetColorHex() string { - return f.config.ColorHex -} - -func (f *FedoraDistribution) GetFamily() DistroFamily { - return f.config.Family -} - -func (f *FedoraDistribution) GetPackageManager() PackageManagerType { - return PackageManagerDNF -} - -func (f *FedoraDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return f.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (f *FedoraDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, f.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, f.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, f.detectGit()) - dependencies = append(dependencies, f.detectWindowManager(wm)) - dependencies = append(dependencies, f.detectQuickshell()) - dependencies = append(dependencies, f.detectDMSGreeter()) - dependencies = append(dependencies, f.detectXDGPortal()) - dependencies = append(dependencies, f.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, f.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, f.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, f.detectMatugen()) - dependencies = append(dependencies, f.detectDgop()) - - return dependencies, nil -} - -func (f *FedoraDistribution) detectXDGPortal() deps.Dependency { - return f.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", f.packageInstalled("xdg-desktop-portal-gtk")) -} - -func (f *FedoraDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("rpm", "-q", pkg) - err := cmd.Run() - return err == nil -} - -func (f *FedoraDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return f.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (f *FedoraDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - // Standard DNF packages - "git": {Name: "git", Repository: RepoTypeSystem}, - "ghostty": {Name: "ghostty", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - - // COPR packages - "quickshell": f.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": {Name: "dms-greeter", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"}, - "matugen": {Name: "matugen", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"}, - "dms (DankMaterialShell)": f.getDmsMapping(variants["dms (DankMaterialShell)"]), - "dgop": {Name: "dgop", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = f.getHyprlandMapping(variants["hyprland"]) - packages["hyprctl"] = f.getHyprlandMapping(variants["hyprland"]) - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - packages["niri"] = f.getNiriMapping(variants["niri"]) - packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem} - } - - return packages -} - -func (f *FedoraDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "quickshell-git", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"} - } - return PackageMapping{Name: "quickshell", Repository: RepoTypeCOPR, RepoURL: "avengemedia/danklinux"} -} - -func (f *FedoraDistribution) getDmsMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "dms", Repository: RepoTypeCOPR, RepoURL: "avengemedia/dms-git"} - } - return PackageMapping{Name: "dms", Repository: RepoTypeCOPR, RepoURL: "avengemedia/dms"} -} - -func (f *FedoraDistribution) getHyprlandMapping(_ deps.PackageVariant) PackageMapping { - return PackageMapping{Name: "hyprland", Repository: RepoTypeCOPR, RepoURL: "sdegler/hyprland"} -} - -func (f *FedoraDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "niri", Repository: RepoTypeCOPR, RepoURL: "yalter/niri-git"} - } - return PackageMapping{Name: "niri", Repository: RepoTypeCOPR, RepoURL: "yalter/niri"} -} - -func (f *FedoraDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if f.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (f *FedoraDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if f.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (f *FedoraDistribution) detectDMSGreeter() deps.Dependency { - return f.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", f.packageInstalled("dms-greeter")) -} - -func (f *FedoraDistribution) getPrerequisites() []string { - return []string{ - "dnf-plugins-core", - "make", - "unzip", - "libwayland-server", - } -} - -func (f *FedoraDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - prerequisites := f.getPrerequisites() - var missingPkgs []string - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking prerequisites...", - IsComplete: false, - LogOutput: "Checking prerequisite packages", - } - - for _, pkg := range prerequisites { - checkCmd := exec.CommandContext(ctx, "rpm", "-q", pkg) - if err := checkCmd.Run(); err != nil { - missingPkgs = append(missingPkgs, pkg) - } - } - - _, err := exec.LookPath("go") - if err != nil { - f.log("go not found in PATH, will install golang-bin") - missingPkgs = append(missingPkgs, "golang-bin") - } else { - f.log("go already available in PATH") - } - - if len(missingPkgs) == 0 { - f.log("All prerequisites already installed") - return nil - } - - f.log(fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: fmt.Sprintf("Installing %d prerequisites...", len(missingPkgs)), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo dnf install -y %s", strings.Join(missingPkgs, " ")), - LogOutput: fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")), - } - - args := []string{"dnf", "install", "-y"} - args = append(args, missingPkgs...) - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - f.logError("failed to install prerequisites", err) - f.log(fmt.Sprintf("Prerequisites command output: %s", string(output))) - return fmt.Errorf("failed to install prerequisites: %w", err) - } - f.log(fmt.Sprintf("Prerequisites install output: %s", string(output))) - - return nil -} - -func (f *FedoraDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := f.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - dnfPkgs, coprPkgs, manualPkgs, variantMap := f.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) - - // Phase 2: Enable COPR repositories - if len(coprPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.15, - Step: "Enabling COPR repositories...", - IsComplete: false, - LogOutput: "Setting up COPR repositories for additional packages", - } - if err := f.enableCOPRRepos(ctx, coprPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to enable COPR repositories: %w", err) - } - } - - // Phase 3: System Packages (DNF) - if len(dnfPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(dnfPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(dnfPkgs, ", ")), - } - if err := f.installDNFPackages(ctx, dnfPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install DNF packages: %w", err) - } - } - - // Phase 4: COPR Packages - coprPkgNames := f.extractPackageNames(coprPkgs) - if len(coprPkgNames) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, // Reusing AUR phase for COPR - Progress: 0.65, - Step: fmt.Sprintf("Installing %d COPR packages...", len(coprPkgNames)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing COPR packages: %s", strings.Join(coprPkgNames, ", ")), - } - if err := f.installCOPRPackages(ctx, coprPkgNames, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install COPR packages: %w", err) - } - } - - // Phase 5: Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := f.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Phase 6: Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - terminal := f.DetectTerminalFromDeps(dependencies) - if err := f.WriteEnvironmentConfig(terminal); err != nil { - f.log(fmt.Sprintf("Warning: failed to write environment config: %v", err)) - } - - if err := f.WriteWindowManagerConfig(wm); err != nil { - f.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) - } - - if err := f.EnableDMSService(ctx, wm); err != nil { - f.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) - } - - // Phase 7: Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (f *FedoraDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) { - dnfPkgs := []string{} - coprPkgs := []PackageMapping{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := f.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - if disabledFlags[dep.Name] { - continue - } - - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - f.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - dnfPkgs = append(dnfPkgs, pkgInfo.Name) - case RepoTypeCOPR: - coprPkgs = append(coprPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return dnfPkgs, coprPkgs, manualPkgs, variantMap -} - -func (f *FedoraDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (f *FedoraDistribution) enableCOPRRepos(ctx context.Context, coprPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - enabledRepos := make(map[string]bool) - - for _, pkg := range coprPkgs { - if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] { - f.log(fmt.Sprintf("Enabling COPR repository: %s", pkg.RepoURL)) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.20, - Step: fmt.Sprintf("Enabling COPR repo %s...", pkg.RepoURL), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo dnf copr enable -y %s", pkg.RepoURL), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("dnf copr enable -y %s 2>&1", pkg.RepoURL)) - output, err := cmd.CombinedOutput() - if err != nil { - f.logError(fmt.Sprintf("failed to enable COPR repo %s", pkg.RepoURL), err) - f.log(fmt.Sprintf("COPR enable command output: %s", string(output))) - return fmt.Errorf("failed to enable COPR repo %s: %w", pkg.RepoURL, err) - } - f.log(fmt.Sprintf("COPR repo %s enabled successfully: %s", pkg.RepoURL, string(output))) - enabledRepos[pkg.RepoURL] = true - - // Special handling for niri COPR repo - set priority=1 - if pkg.RepoURL == "yalter/niri-git" { - f.log("Setting priority=1 for niri-git COPR repo...") - repoFile := "/etc/yum.repos.d/_copr:copr.fedorainfracloud.org:yalter:niri-git.repo" - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.22, - Step: "Setting niri COPR repo priority...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("echo \"priority=1\" | sudo tee -a %s", repoFile), - } - - priorityCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("bash -c 'echo \"priority=1\" | tee -a %s'", repoFile)) - priorityOutput, err := priorityCmd.CombinedOutput() - if err != nil { - f.logError("failed to set niri COPR repo priority", err) - f.log(fmt.Sprintf("Priority command output: %s", string(priorityOutput))) - return fmt.Errorf("failed to set niri COPR repo priority: %w", err) - } - f.log(fmt.Sprintf("niri COPR repo priority set successfully: %s", string(priorityOutput))) - } - } - } - - return nil -} - -func (f *FedoraDistribution) installDNFPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - f.log(fmt.Sprintf("Installing DNF packages: %s", strings.Join(packages, ", "))) - - return f.installDNFGroups(ctx, packages, sudoPassword, progressChan, PhaseSystemPackages, "Installing system packages...", 0.40, 0.60) -} - -func (f *FedoraDistribution) installCOPRPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - f.log(fmt.Sprintf("Installing COPR packages: %s", strings.Join(packages, ", "))) - - return f.installDNFGroups(ctx, packages, sudoPassword, progressChan, PhaseAURPackages, "Installing COPR packages...", 0.70, 0.85) -} - -func (f *FedoraDistribution) dnfInstallArgs(packages []string, minimal bool) []string { - args := []string{"dnf", "install", "-y"} - if minimal { - args = append(args, "--setopt=install_weak_deps=False") - } - return append(args, packages...) -} - -func (f *FedoraDistribution) installDNFGroups(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg, phase InstallPhase, step string, startProgress float64, endProgress float64) error { - groups := orderedMinimalInstallGroups(packages) - totalGroups := len(groups) - - groupIndex := 0 - installGroup := func(groupPackages []string, minimal bool) error { - if len(groupPackages) == 0 { - return nil - } - - groupIndex++ - groupStart := startProgress - groupEnd := endProgress - if totalGroups > 1 { - midpoint := startProgress + ((endProgress - startProgress) / 2) - if groupIndex == 1 { - groupEnd = midpoint - } else { - groupStart = midpoint - } - } - - args := f.dnfInstallArgs(groupPackages, minimal) - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: groupStart, - Step: step, - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return f.runWithProgress(cmd, progressChan, phase, groupStart, groupEnd) - } - - for _, group := range groups { - if err := installGroup(group.packages, group.minimal); err != nil { - return err - } - } - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/gentoo.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/gentoo.go deleted file mode 100644 index e0a2a9c..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/gentoo.go +++ /dev/null @@ -1,700 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os/exec" - "runtime" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -var GentooGlobalUseFlags = []string{ - "dbus", - "udev", - "alsa", - "policykit", - "jpeg", - "png", - "webp", - "gif", - "tiff", - "svg", - "brotli", - "gdbm", - "accessibility", - "gtk", - "qt6", - "egl", - "gbm", -} - -func init() { - Register("gentoo", "#54487A", FamilyGentoo, func(config DistroConfig, logChan chan<- string) Distribution { - return NewGentooDistribution(config, logChan) - }) -} - -type GentooDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig - skipGlobalUseFlags bool -} - -func NewGentooDistribution(config DistroConfig, logChan chan<- string) *GentooDistribution { - base := NewBaseDistribution(logChan) - return &GentooDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (g *GentooDistribution) getArchKeyword() string { - arch := runtime.GOARCH - switch arch { - case "amd64": - return "~amd64" - case "arm64": - return "~arm64" - default: - return "~amd64" - } -} - -func (g *GentooDistribution) GetID() string { - return g.config.ID -} - -func (g *GentooDistribution) GetColorHex() string { - return g.config.ColorHex -} - -func (g *GentooDistribution) GetFamily() DistroFamily { - return g.config.Family -} - -func (g *GentooDistribution) GetPackageManager() PackageManagerType { - return PackageManagerPortage -} - -func (g *GentooDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return g.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (g *GentooDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - dependencies = append(dependencies, g.detectDMS()) - - dependencies = append(dependencies, g.detectSpecificTerminal(terminal)) - - dependencies = append(dependencies, g.detectGit()) - dependencies = append(dependencies, g.detectWindowManager(wm)) - dependencies = append(dependencies, g.detectQuickshell()) - dependencies = append(dependencies, g.detectXDGPortal()) - dependencies = append(dependencies, g.detectAccountsService()) - - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, g.detectHyprlandTools()...) - } - - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, g.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, g.detectMatugen()) - dependencies = append(dependencies, g.detectDgop()) - - return dependencies, nil -} - -func (g *GentooDistribution) detectXDGPortal() deps.Dependency { - return g.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", g.packageInstalled("sys-apps/xdg-desktop-portal-gtk")) -} - -func (g *GentooDistribution) detectXwaylandSatellite() deps.Dependency { - return g.detectPackage("xwayland-satellite", "Xwayland support", g.packageInstalled("gui-apps/xwayland-satellite")) -} - -func (g *GentooDistribution) detectAccountsService() deps.Dependency { - return g.detectPackage("accountsservice", "D-Bus interface for user account query and manipulation", g.packageInstalled("sys-apps/accountsservice")) -} - -func (g *GentooDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("qlist", "-I", pkg) - err := cmd.Run() - return err == nil -} - -func (g *GentooDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return g.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (g *GentooDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - archKeyword := g.getArchKeyword() - packages := map[string]PackageMapping{ - "git": {Name: "dev-vcs/git", Repository: RepoTypeSystem}, - "kitty": {Name: "x11-terms/kitty", Repository: RepoTypeSystem, UseFlags: "X wayland"}, - "alacritty": {Name: "x11-terms/alacritty", Repository: RepoTypeSystem, UseFlags: "X wayland"}, - "xdg-desktop-portal-gtk": {Name: "sys-apps/xdg-desktop-portal-gtk", Repository: RepoTypeSystem, UseFlags: "wayland X"}, - "accountsservice": {Name: "sys-apps/accountsservice", Repository: RepoTypeSystem}, - - "qtbase": {Name: "dev-qt/qtbase", Repository: RepoTypeSystem, UseFlags: "wayland opengl vulkan widgets"}, - "qtdeclarative": {Name: "dev-qt/qtdeclarative", Repository: RepoTypeSystem, UseFlags: "opengl vulkan"}, - "qtwayland": {Name: "dev-qt/qtwayland", Repository: RepoTypeSystem}, - "mesa": {Name: "media-libs/mesa", Repository: RepoTypeSystem, UseFlags: "opengl vulkan"}, - - "quickshell": g.getQuickshellMapping(variants["quickshell"]), - "matugen": {Name: "x11-misc/matugen", Repository: RepoTypeGURU, AcceptKeywords: archKeyword}, - "dms (DankMaterialShell)": g.getDmsMapping(variants["dms (DankMaterialShell)"]), - "dgop": {Name: "dgop", Repository: RepoTypeManual, BuildFunc: "installDgop"}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = g.getHyprlandMapping(variants["hyprland"]) - packages["hyprctl"] = g.getHyprlandMapping(variants["hyprland"]) - packages["jq"] = PackageMapping{Name: "app-misc/jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - packages["niri"] = g.getNiriMapping(variants["niri"]) - packages["xwayland-satellite"] = PackageMapping{Name: "gui-apps/xwayland-satellite", Repository: RepoTypeGURU, AcceptKeywords: archKeyword} - } - - return packages -} - -func (g *GentooDistribution) getQuickshellMapping(_ deps.PackageVariant) PackageMapping { - return PackageMapping{Name: "gui-apps/quickshell", Repository: RepoTypeGURU, UseFlags: "breakpad jemalloc sockets wayland layer-shell session-lock toplevel-management screencopy X pipewire tray mpris pam hyprland hyprland-global-shortcuts hyprland-focus-grab i3 i3-ipc bluetooth", AcceptKeywords: "**"} -} - -func (g *GentooDistribution) getDmsMapping(_ deps.PackageVariant) PackageMapping { - return PackageMapping{Name: "dms", Repository: RepoTypeManual, BuildFunc: "installDankMaterialShell"} -} - -func (g *GentooDistribution) getHyprlandMapping(_ deps.PackageVariant) PackageMapping { - return PackageMapping{Name: "gui-wm/hyprland", Repository: RepoTypeSystem, UseFlags: "X", AcceptKeywords: g.getArchKeyword()} -} - -func (g *GentooDistribution) getNiriMapping(_ deps.PackageVariant) PackageMapping { - return PackageMapping{Name: "gui-wm/niri", Repository: RepoTypeGURU, UseFlags: "dbus screencast", AcceptKeywords: g.getArchKeyword()} -} - -func (g *GentooDistribution) getPrerequisites() []string { - return []string{ - "app-eselect/eselect-repository", - "dev-vcs/git", - "dev-build/make", - "app-arch/unzip", - "dev-util/pkgconf", - "dev-qt/qtdeclarative", - } -} - -func (g *GentooDistribution) setGlobalUseFlags(ctx context.Context, sudoPassword string) error { - useFlags := strings.Join(GentooGlobalUseFlags, " ") - - checkCmd := exec.CommandContext(ctx, "grep", "-q", "^USE=", "/etc/portage/make.conf") - hasUse := checkCmd.Run() == nil - - var cmd *exec.Cmd - if hasUse { - cmd = ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("sed -i 's/^USE=\"\\(.*\\)\"/USE=\"\\1 %s\"/' /etc/portage/make.conf", useFlags)) - } else { - cmd = ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("bash -c \"echo 'USE=\\\"%s\\\"' >> /etc/portage/make.conf\"", useFlags)) - } - - output, err := cmd.CombinedOutput() - if err != nil { - g.log(fmt.Sprintf("Failed to set global USE flags: %s", string(output))) - return err - } - - g.log(fmt.Sprintf("Set global USE flags: %s", useFlags)) - return nil -} - -func (g *GentooDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - prerequisites := g.getPrerequisites() - var missingPkgs []string - - if !g.skipGlobalUseFlags { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Setting global USE flags...", - IsComplete: false, - LogOutput: "Configuring global USE flags in /etc/portage/make.conf", - } - - if err := g.setGlobalUseFlags(ctx, sudoPassword); err != nil { - g.logError("failed to set global USE flags", err) - return fmt.Errorf("failed to set global USE flags: %w", err) - } - } else { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Skipping global USE flags...", - IsComplete: false, - LogOutput: "Skipping global USE flags configuration (using existing configuration)", - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking prerequisites...", - IsComplete: false, - LogOutput: "Checking prerequisite packages", - } - - for _, pkg := range prerequisites { - checkCmd := exec.CommandContext(ctx, "qlist", "-I", pkg) - if err := checkCmd.Run(); err != nil { - missingPkgs = append(missingPkgs, pkg) - } - } - - _, err := exec.LookPath("go") - if err != nil { - g.log("go not found in PATH, will install dev-lang/go") - missingPkgs = append(missingPkgs, "dev-lang/go") - } else { - g.log("go already available in PATH") - } - - if len(missingPkgs) == 0 { - g.log("All prerequisites already installed") - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.07, - Step: "Syncing Portage tree...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo emerge --sync", - LogOutput: "Syncing Portage tree with emerge --sync", - } - - syncCmd := ExecSudoCommand(ctx, sudoPassword, "emerge --sync --quiet") - syncOutput, syncErr := syncCmd.CombinedOutput() - if syncErr != nil { - g.log(fmt.Sprintf("emerge --sync output: %s", string(syncOutput))) - return fmt.Errorf("failed to sync Portage tree: %w\nOutput: %s", syncErr, string(syncOutput)) - } - g.log("Portage tree synced successfully") - - g.log(fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: fmt.Sprintf("Installing %d prerequisites...", len(missingPkgs)), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo emerge --ask=n %s", strings.Join(missingPkgs, " ")), - LogOutput: fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")), - } - - args := []string{"emerge", "--ask=n", "--quiet"} - args = append(args, missingPkgs...) - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - g.logError("failed to install prerequisites", err) - g.log(fmt.Sprintf("Prerequisites command output: %s", string(output))) - return fmt.Errorf("failed to install prerequisites: %w\nOutput: %s", err, string(output)) - } - g.log(fmt.Sprintf("Prerequisites install output: %s", string(output))) - - return nil -} - -func (g *GentooDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error { - g.skipGlobalUseFlags = skipGlobalUseFlags - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := g.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, guruPkgs, manualPkgs, variantMap := g.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) - - g.log(fmt.Sprintf("CATEGORIZED PACKAGES: system=%d, guru=%d, manual=%d", len(systemPkgs), len(guruPkgs), len(manualPkgs))) - - if len(systemPkgs) > 0 { - systemPkgNames := g.extractPackageNames(systemPkgs) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgNames, ", ")), - } - if err := g.installPortagePackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Portage packages: %w", err) - } - } - - if len(guruPkgs) > 0 { - g.log(fmt.Sprintf("FOUND %d GURU PACKAGES - WILL SYNC GURU REPO", len(guruPkgs))) - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.60, - Step: "Syncing GURU repository...", - IsComplete: false, - LogOutput: "Syncing GURU repository to fetch latest ebuilds", - } - g.log("ABOUT TO CALL syncGURURepo") - if err := g.syncGURURepo(ctx, sudoPassword, progressChan); err != nil { - g.log(fmt.Sprintf("syncGURURepo RETURNED ERROR: %v", err)) - return fmt.Errorf("failed to sync GURU repository: %w", err) - } - g.log("syncGURURepo COMPLETED SUCCESSFULLY") - - guruPkgNames := g.extractPackageNames(guruPkgs) - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: fmt.Sprintf("Installing %d GURU packages...", len(guruPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing GURU packages: %s", strings.Join(guruPkgNames, ", ")), - } - if err := g.installGURUPackages(ctx, guruPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install GURU packages: %w", err) - } - } - - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := g.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - terminal := g.DetectTerminalFromDeps(dependencies) - if err := g.WriteEnvironmentConfig(terminal); err != nil { - g.log(fmt.Sprintf("Warning: failed to write environment config: %v", err)) - } - - if err := g.WriteWindowManagerConfig(wm); err != nil { - g.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) - } - - if err := g.EnableDMSService(ctx, wm); err != nil { - g.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (g *GentooDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]PackageMapping, []PackageMapping, []string, map[string]deps.PackageVariant) { - systemPkgs := []PackageMapping{} - guruPkgs := []PackageMapping{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := g.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - if disabledFlags[dep.Name] { - continue - } - - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - g.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo) - case RepoTypeGURU: - guruPkgs = append(guruPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, guruPkgs, manualPkgs, variantMap -} - -func (g *GentooDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (g *GentooDistribution) installPortagePackages(ctx context.Context, packages []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - packageNames := g.extractPackageNames(packages) - g.log(fmt.Sprintf("Installing Portage packages: %s", strings.Join(packageNames, ", "))) - - for _, pkg := range packages { - if pkg.AcceptKeywords != "" { - if err := g.setPackageAcceptKeywords(ctx, pkg.Name, pkg.AcceptKeywords, sudoPassword); err != nil { - return fmt.Errorf("failed to set accept keywords for %s: %w", pkg.Name, err) - } - } - if pkg.UseFlags != "" { - if err := g.setPackageUseFlags(ctx, pkg.Name, pkg.UseFlags, sudoPassword); err != nil { - return fmt.Errorf("failed to set USE flags for %s: %w", pkg.Name, err) - } - } - } - - args := []string{"emerge", "--ask=n", "--quiet"} - args = append(args, packageNames...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.40, - Step: "Installing system packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return g.runWithProgressTimeout(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60, 0) -} - -func (g *GentooDistribution) setPackageUseFlags(ctx context.Context, packageName, useFlags, sudoPassword string) error { - packageUseDir := "/etc/portage/package.use" - - mkdirCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("mkdir -p %s", packageUseDir)) - if output, err := mkdirCmd.CombinedOutput(); err != nil { - g.log(fmt.Sprintf("mkdir output: %s", string(output))) - return fmt.Errorf("failed to create package.use directory: %w", err) - } - - useFlagLine := fmt.Sprintf("%s %s", packageName, useFlags) - - checkExistingCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("grep -q '^%s ' %s/danklinux 2>/dev/null", packageName, packageUseDir)) - if checkExistingCmd.Run() == nil { - g.log(fmt.Sprintf("Updating USE flags for %s from existing entry", packageName)) - escapedPkg := strings.ReplaceAll(packageName, "/", "\\/") - replaceCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("sed -i '/^%s /d' %s/danklinux; exit_code=$?; exit $exit_code", escapedPkg, packageUseDir)) - if output, err := replaceCmd.CombinedOutput(); err != nil { - g.log(fmt.Sprintf("sed delete output: %s", string(output))) - return fmt.Errorf("failed to remove old USE flags: %w", err) - } - } - - appendCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("bash -c \"echo '%s' >> %s/danklinux\"", useFlagLine, packageUseDir)) - - output, err := appendCmd.CombinedOutput() - if err != nil { - g.log(fmt.Sprintf("append output: %s", string(output))) - return fmt.Errorf("failed to write USE flags to package.use: %w", err) - } - - g.log(fmt.Sprintf("Set USE flags for %s: %s", packageName, useFlags)) - return nil -} - -func (g *GentooDistribution) syncGURURepo(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.55, - Step: "Enabling GURU repository...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo eselect repository enable guru", - LogOutput: "Enabling GURU repository with eselect", - } - - // Enable GURU repository - enableCmd := ExecSudoCommand(ctx, sudoPassword, - "eselect repository enable guru 2>&1; exit_code=$?; exit $exit_code") - output, err := enableCmd.CombinedOutput() - - g.log(fmt.Sprintf("eselect repository enable guru output:\n%s", string(output))) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.55, - LogOutput: "GURU repository enabled", - } - - if err != nil { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.55, - LogOutput: fmt.Sprintf("ERROR enabling GURU: %v", err), - Error: err, - } - return fmt.Errorf("failed to enable GURU repository: %w\nOutput: %s", err, string(output)) - } - - // Sync GURU repository - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.57, - Step: "Syncing GURU repository...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo emaint sync --repo guru", - LogOutput: "Syncing GURU repository", - } - - syncCmd := ExecSudoCommand(ctx, sudoPassword, - "emaint sync --repo guru 2>&1; exit_code=$?; exit $exit_code") - syncOutput, syncErr := syncCmd.CombinedOutput() - - g.log(fmt.Sprintf("emaint sync --repo guru output:\n%s", string(syncOutput))) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.57, - LogOutput: "GURU repository synced", - } - - if syncErr != nil { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.57, - LogOutput: fmt.Sprintf("ERROR syncing GURU: %v", syncErr), - Error: syncErr, - } - return fmt.Errorf("failed to sync GURU repository: %w\nOutput: %s", syncErr, string(syncOutput)) - } - - return nil -} - -func (g *GentooDistribution) setPackageAcceptKeywords(ctx context.Context, packageName, keywords, sudoPassword string) error { - checkCmd := exec.CommandContext(ctx, "portageq", "match", "/", packageName) - if output, err := checkCmd.CombinedOutput(); err == nil && len(output) > 0 { - g.log(fmt.Sprintf("Package %s is already available (may already be unmasked)", packageName)) - return nil - } - - acceptKeywordsDir := "/etc/portage/package.accept_keywords" - - mkdirCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("mkdir -p %s", acceptKeywordsDir)) - if output, err := mkdirCmd.CombinedOutput(); err != nil { - g.log(fmt.Sprintf("mkdir output: %s", string(output))) - return fmt.Errorf("failed to create package.accept_keywords directory: %w", err) - } - - keywordLine := fmt.Sprintf("%s %s", packageName, keywords) - - checkExistingCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf("grep -q '^%s ' %s/danklinux 2>/dev/null", packageName, acceptKeywordsDir)) - if checkExistingCmd.Run() == nil { - g.log(fmt.Sprintf("Updating accept keywords for %s from existing entry", packageName)) - escapedPkg := strings.ReplaceAll(packageName, "/", "\\/") - replaceCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("sed -i '/^%s /d' %s/danklinux; exit_code=$?; exit $exit_code", escapedPkg, acceptKeywordsDir)) - if output, err := replaceCmd.CombinedOutput(); err != nil { - g.log(fmt.Sprintf("sed delete output: %s", string(output))) - return fmt.Errorf("failed to remove old accept keywords: %w", err) - } - } - - appendCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("bash -c \"echo '%s' >> %s/danklinux\"", keywordLine, acceptKeywordsDir)) - - output, err := appendCmd.CombinedOutput() - if err != nil { - g.log(fmt.Sprintf("append output: %s", string(output))) - return fmt.Errorf("failed to write accept keywords: %w", err) - } - - g.log(fmt.Sprintf("Set accept keywords for %s: %s", packageName, keywords)) - return nil -} - -func (g *GentooDistribution) installGURUPackages(ctx context.Context, packages []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - packageNames := g.extractPackageNames(packages) - g.log(fmt.Sprintf("Installing GURU packages: %s", strings.Join(packageNames, ", "))) - - for _, pkg := range packages { - if pkg.AcceptKeywords != "" { - if err := g.setPackageAcceptKeywords(ctx, pkg.Name, pkg.AcceptKeywords, sudoPassword); err != nil { - return fmt.Errorf("failed to set accept keywords for %s: %w", pkg.Name, err) - } - } - if pkg.UseFlags != "" { - if err := g.setPackageUseFlags(ctx, pkg.Name, pkg.UseFlags, sudoPassword); err != nil { - return fmt.Errorf("failed to set USE flags for %s: %w", pkg.Name, err) - } - } - } - - guruPackages := make([]string, len(packageNames)) - for i, pkg := range packageNames { - guruPackages[i] = pkg + "::guru" - } - - args := []string{"emerge", "--ask=n", "--quiet"} - args = append(args, guruPackages...) - - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.70, - Step: "Installing GURU packages...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return g.runWithProgressTimeout(cmd, progressChan, PhaseAURPackages, 0.70, 0.85, 0) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/interface.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/interface.go deleted file mode 100644 index f719f1b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/interface.go +++ /dev/null @@ -1,158 +0,0 @@ -package distros - -import ( - "context" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -// DistroFamily represents a family of related distributions -type DistroFamily string - -const ( - FamilyArch DistroFamily = "arch" - FamilyFedora DistroFamily = "fedora" - FamilySUSE DistroFamily = "suse" - FamilyUbuntu DistroFamily = "ubuntu" - FamilyDebian DistroFamily = "debian" - FamilyNix DistroFamily = "nix" - FamilyGentoo DistroFamily = "gentoo" -) - -// PackageManagerType defines the package manager a distro uses -type PackageManagerType string - -const ( - PackageManagerPacman PackageManagerType = "pacman" - PackageManagerDNF PackageManagerType = "dnf" - PackageManagerAPT PackageManagerType = "apt" - PackageManagerZypper PackageManagerType = "zypper" - PackageManagerNix PackageManagerType = "nix" - PackageManagerPortage PackageManagerType = "portage" -) - -// RepositoryType defines the type of repository for a package -type RepositoryType string - -const ( - RepoTypeSystem RepositoryType = "system" // Standard system repo (pacman, dnf, apt) - RepoTypeAUR RepositoryType = "aur" // Arch User Repository - RepoTypeCOPR RepositoryType = "copr" // Fedora COPR - RepoTypePPA RepositoryType = "ppa" // Ubuntu PPA - RepoTypeOBS RepositoryType = "obs" // OpenBuild Service (Debian/OpenSUSE) - RepoTypeFlake RepositoryType = "flake" // Nix flake - RepoTypeGURU RepositoryType = "guru" // Gentoo GURU - RepoTypeManual RepositoryType = "manual" // Manual build from source -) - -// InstallPhase represents the current phase of installation -type InstallPhase int - -const ( - PhasePrerequisites InstallPhase = iota - PhaseAURHelper - PhaseSystemPackages - PhaseAURPackages - PhaseCursorTheme - PhaseConfiguration - PhaseGreeterSetup - PhaseComplete -) - -// InstallProgressMsg represents progress during package installation -type InstallProgressMsg struct { - Phase InstallPhase - Progress float64 - Step string - IsComplete bool - NeedsSudo bool - CommandInfo string - LogOutput string - Error error -} - -// PackageMapping defines how to install a package on a specific distro -type PackageMapping struct { - Name string // Package name to install - Repository RepositoryType // Repository type - RepoURL string // Repository URL if needed (e.g., COPR repo, PPA) - BuildFunc string // Name of manual build function if RepoTypeManual - UseFlags string // USE flags for Gentoo packages - AcceptKeywords string // Accept keywords for Gentoo packages (e.g., "~amd64") -} - -// Distribution defines a Linux distribution with all its specific configurations -type Distribution interface { - // Metadata - GetID() string - GetColorHex() string - GetFamily() DistroFamily - GetPackageManager() PackageManagerType - - // Dependency Detection - DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) - DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) - - // Package Installation - InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error - - // Package Mapping - GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping - - // Prerequisites - InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error -} - -// DistroConfig holds configuration for a distribution -type DistroConfig struct { - ID string - ColorHex string - Family DistroFamily - Constructor func(config DistroConfig, logChan chan<- string) Distribution -} - -// Registry holds all supported distributions -var Registry = make(map[string]DistroConfig) - -// Register adds a distribution to the registry -func Register(id, colorHex string, family DistroFamily, constructor func(config DistroConfig, logChan chan<- string) Distribution) { - Registry[id] = DistroConfig{ - ID: id, - ColorHex: colorHex, - Family: family, - Constructor: constructor, - } -} - -// GetSupportedDistros returns a list of all supported distribution IDs -func GetSupportedDistros() []string { - ids := make([]string, 0, len(Registry)) - for id := range Registry { - ids = append(ids, id) - } - return ids -} - -// IsDistroSupported checks if a distribution ID is supported -func IsDistroSupported(id string) bool { - _, exists := Registry[id] - return exists -} - -// NewDistribution creates a distribution instance by ID -func NewDistribution(id string, logChan chan<- string) (Distribution, error) { - config, exists := Registry[id] - if !exists { - return nil, &UnsupportedDistributionError{ID: id} - } - return config.Constructor(config, logChan), nil -} - -// UnsupportedDistributionError is returned when a distribution is not supported -type UnsupportedDistributionError struct { - ID string -} - -func (e *UnsupportedDistributionError) Error() string { - return "unsupported distribution: " + e.ID -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/manual_packages.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/manual_packages.go deleted file mode 100644 index 2f38161..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/manual_packages.go +++ /dev/null @@ -1,663 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -// ManualPackageInstaller provides methods for installing packages from source -type ManualPackageInstaller struct { - *BaseDistribution -} - -// parseLatestTagFromGitOutput parses git ls-remote output and returns the latest tag -func (m *ManualPackageInstaller) parseLatestTagFromGitOutput(output string) string { - lines := strings.SplitSeq(output, "\n") - for line := range lines { - if strings.Contains(line, "refs/tags/") && !strings.Contains(line, "^{}") { - parts := strings.Split(line, "refs/tags/") - if len(parts) > 1 { - latestTag := strings.TrimSpace(parts[1]) - return latestTag - } - } - } - return "" -} - -// getLatestQuickshellTag fetches the latest tag from the quickshell repository -func (m *ManualPackageInstaller) getLatestQuickshellTag(ctx context.Context) string { - tagCmd := exec.CommandContext(ctx, "git", "ls-remote", "--tags", "--sort=-v:refname", - "https://github.com/quickshell-mirror/quickshell.git") - tagOutput, err := tagCmd.Output() - if err != nil { - m.log(fmt.Sprintf("Warning: failed to fetch quickshell tags: %v", err)) - return "" - } - - return m.parseLatestTagFromGitOutput(string(tagOutput)) -} - -func (m *ManualPackageInstaller) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - m.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - for _, pkg := range packages { - variant := variantMap[pkg] - switch pkg { - case "dms (DankMaterialShell)", "dms": - if err := m.installDankMaterialShell(ctx, variant, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install DankMaterialShell: %w", err) - } - case "dgop": - if err := m.installDgop(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install dgop: %w", err) - } - case "niri": - if err := m.installNiri(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install niri: %w", err) - } - case "quickshell": - if err := m.installQuickshell(ctx, variant, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - case "hyprland": - if err := m.installHyprland(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install hyprland: %w", err) - } - case "ghostty": - if err := m.installGhostty(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install ghostty: %w", err) - } - case "matugen": - if err := m.installMatugen(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install matugen: %w", err) - } - case "xwayland-satellite": - if err := m.installXwaylandSatellite(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install xwayland-satellite: %w", err) - } - default: - m.log(fmt.Sprintf("Warning: No manual build method for %s", pkg)) - } - } - - return nil -} - -func (m *ManualPackageInstaller) installDgop(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing dgop from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "dgop-build") - if err := os.MkdirAll(tmpDir, 0o755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning dgop repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/AvengeMedia/dgop.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/AvengeMedia/dgop.git", tmpDir) - if err := cloneCmd.Run(); err != nil { - m.logError("failed to clone dgop repository", err) - return fmt.Errorf("failed to clone dgop repository: %w", err) - } - - buildCmd := exec.CommandContext(ctx, "make") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.4, 0.7, "Building dgop..."); err != nil { - return fmt.Errorf("failed to build dgop: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.7, - Step: "Installing dgop...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo make install", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, "make install") - installCmd.Dir = tmpDir - if err := installCmd.Run(); err != nil { - m.logError("failed to install dgop", err) - return fmt.Errorf("failed to install dgop: %w", err) - } - - m.log("dgop installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installNiri(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing niri from source...") - - homeDir, _ := os.UserHomeDir() - buildDir := filepath.Join(homeDir, ".cache", "dankinstall", "niri-build") - tmpDir := filepath.Join(homeDir, ".cache", "dankinstall", "tmp") - if err := os.MkdirAll(buildDir, 0o755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - if err := os.MkdirAll(tmpDir, 0o755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer func() { - os.RemoveAll(buildDir) - os.RemoveAll(tmpDir) - }() - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.2, - Step: "Cloning niri repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/YaLTeR/niri.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/YaLTeR/niri.git", buildDir) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone niri: %w", err) - } - - checkoutCmd := exec.CommandContext(ctx, "git", "-C", buildDir, "checkout", "v25.08") - if err := checkoutCmd.Run(); err != nil { - m.log(fmt.Sprintf("Warning: failed to checkout v25.08, using main: %v", err)) - } - - if !m.commandExists("cargo-deb") { - cargoDebInstallCmd := exec.CommandContext(ctx, "cargo", "install", "cargo-deb") - cargoDebInstallCmd.Env = append(os.Environ(), "TMPDIR="+tmpDir) - if err := m.runWithProgressStep(cargoDebInstallCmd, progressChan, PhaseSystemPackages, 0.3, 0.35, "Installing cargo-deb..."); err != nil { - return fmt.Errorf("failed to install cargo-deb: %w", err) - } - } - - buildDebCmd := exec.CommandContext(ctx, "cargo", "deb") - buildDebCmd.Dir = buildDir - buildDebCmd.Env = append(os.Environ(), "TMPDIR="+tmpDir) - if err := m.runWithProgressStep(buildDebCmd, progressChan, PhaseSystemPackages, 0.35, 0.95, "Building niri deb package..."); err != nil { - return fmt.Errorf("failed to build niri deb: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.95, - Step: "Installing niri deb package...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "dpkg -i niri.deb", - } - - installDebCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("dpkg -i %s/target/debian/niri_*.deb", buildDir)) - - output, err := installDebCmd.CombinedOutput() - if err != nil { - m.log(fmt.Sprintf("dpkg install failed. Output:\n%s", string(output))) - return fmt.Errorf("failed to install niri deb package: %w\nOutput:\n%s", err, string(output)) - } - - m.log(fmt.Sprintf("dpkg install successful. Output:\n%s", string(output))) - - m.log("niri installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installQuickshell(ctx context.Context, variant deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing quickshell from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "quickshell-build") - if err := os.MkdirAll(tmpDir, 0o755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning quickshell repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/quickshell-mirror/quickshell.git", - } - - var cloneCmd *exec.Cmd - if forceQuickshellGit || variant == deps.VariantGit { - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - latestTag := m.getLatestQuickshellTag(ctx) - if latestTag != "" { - m.log(fmt.Sprintf("Using latest quickshell tag: %s", latestTag)) - cloneCmd = exec.CommandContext(ctx, "git", "clone", "--branch", latestTag, "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - m.log("Warning: failed to fetch latest tag, using default branch") - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } - } - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone quickshell: %w", err) - } - - buildDir := tmpDir + "/build" - if err := os.MkdirAll(buildDir, 0o755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.3, - Step: "Configuring quickshell build...", - IsComplete: false, - CommandInfo: "cmake -B build -S . -G Ninja", - } - - configureCmd := exec.CommandContext(ctx, "cmake", "-GNinja", "-B", "build", - "-DCMAKE_BUILD_TYPE=RelWithDebInfo", - "-DCRASH_REPORTER=off", - "-DCMAKE_CXX_STANDARD=20") - configureCmd.Dir = tmpDir - configureCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - - output, err := configureCmd.CombinedOutput() - if err != nil { - m.log(fmt.Sprintf("cmake configure failed. Output:\n%s", string(output))) - return fmt.Errorf("failed to configure quickshell: %w\nCMake output:\n%s", err, string(output)) - } - - m.log(fmt.Sprintf("cmake configure successful. Output:\n%s", string(output))) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.4, - Step: "Building quickshell (this may take a while)...", - IsComplete: false, - CommandInfo: "cmake --build build", - } - - buildCmd := exec.CommandContext(ctx, "cmake", "--build", "build") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.4, 0.8, "Building quickshell..."); err != nil { - return fmt.Errorf("failed to build quickshell: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing quickshell...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cmake --install build", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install build") - installCmd.Dir = tmpDir - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - - m.log("quickshell installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installHyprland(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing Hyprland from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "hyprland-build") - if err := os.MkdirAll(tmpDir, 0o755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning Hyprland repository...", - IsComplete: false, - CommandInfo: "git clone --recursive https://github.com/hyprwm/Hyprland.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "--recursive", "https://github.com/hyprwm/Hyprland.git", tmpDir) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone Hyprland: %w", err) - } - - checkoutCmd := exec.CommandContext(ctx, "git", "-C", tmpDir, "checkout", "v0.50.1") - if err := checkoutCmd.Run(); err != nil { - m.log(fmt.Sprintf("Warning: failed to checkout v0.50.1, using main: %v", err)) - } - - buildCmd := exec.CommandContext(ctx, "make", "all") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.2, 0.8, "Building Hyprland..."); err != nil { - return fmt.Errorf("failed to build Hyprland: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing Hyprland...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo make install", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, "make install") - installCmd.Dir = tmpDir - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install Hyprland: %w", err) - } - - m.log("Hyprland installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installGhostty(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing Ghostty from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "ghostty-build") - if err := os.MkdirAll(tmpDir, 0o755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning Ghostty repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/ghostty-org/ghostty.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/ghostty-org/ghostty.git", tmpDir) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone Ghostty: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.2, - Step: "Building Ghostty (this may take a while)...", - IsComplete: false, - CommandInfo: "zig build -Doptimize=ReleaseFast", - } - - buildCmd := exec.CommandContext(ctx, "zig", "build", "-Doptimize=ReleaseFast") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := buildCmd.Run(); err != nil { - return fmt.Errorf("failed to build Ghostty: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing Ghostty...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cp zig-out/bin/ghostty /usr/local/bin/", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("cp %s/zig-out/bin/ghostty /usr/local/bin/", tmpDir)) - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install Ghostty: %w", err) - } - - m.log("Ghostty installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installMatugen(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing matugen from source...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Installing matugen via cargo...", - IsComplete: false, - CommandInfo: "cargo install matugen", - } - - installCmd := exec.CommandContext(ctx, "cargo", "install", "matugen") - if err := m.runWithProgressStep(installCmd, progressChan, PhaseSystemPackages, 0.1, 0.7, "Building matugen..."); err != nil { - return fmt.Errorf("failed to install matugen: %w", err) - } - - homeDir := os.Getenv("HOME") - sourcePath := filepath.Join(homeDir, ".cargo", "bin", "matugen") - targetPath := "/usr/local/bin/matugen" - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.7, - Step: "Installing matugen binary to system...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo cp %s %s", sourcePath, targetPath), - } - - copyCmd := exec.CommandContext(ctx, "sudo", "-S", "cp", sourcePath, targetPath) - copyCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := copyCmd.Run(); err != nil { - return fmt.Errorf("failed to copy matugen to /usr/local/bin: %w", err) - } - - // Make it executable - chmodCmd := exec.CommandContext(ctx, "sudo", "-S", "chmod", "+x", targetPath) - chmodCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := chmodCmd.Run(); err != nil { - return fmt.Errorf("failed to make matugen executable: %w", err) - } - - m.log("matugen installed successfully from source") - return nil -} - -func (m *ManualPackageInstaller) installDankMaterialShell(ctx context.Context, variant deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing DankMaterialShell (DMS)...") - - if err := m.installDMSBinary(ctx, sudoPassword, progressChan); err != nil { - m.logError("Failed to install DMS binary", err) - } - - dmsPath := filepath.Join(os.Getenv("HOME"), ".config/quickshell/dms") - - if _, err := os.Stat(dmsPath); os.IsNotExist(err) { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.90, - Step: "Cloning DankMaterialShell...", - IsComplete: false, - CommandInfo: "git clone https://github.com/AvengeMedia/DankMaterialShell.git", - } - - configDir := filepath.Dir(dmsPath) - if err := os.MkdirAll(configDir, 0o755); err != nil { - return fmt.Errorf("failed to create quickshell config directory: %w", err) - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", - "https://github.com/AvengeMedia/DankMaterialShell.git", dmsPath) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone DankMaterialShell: %w", err) - } - - if forceDMSGit || variant == deps.VariantGit { - m.log("Using git variant (master branch)") - return nil - } - - tagCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "describe", "--tags", "--abbrev=0", "origin/master") - tagOutput, err := tagCmd.Output() - if err != nil { - m.log("Using default branch (no tags found)") - return nil - } - - latestTag := strings.TrimSpace(string(tagOutput)) - checkoutCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "checkout", latestTag) - if err := checkoutCmd.Run(); err != nil { - m.logError(fmt.Sprintf("Failed to checkout tag %s", latestTag), err) - return nil - } - - m.log(fmt.Sprintf("Checked out latest tag: %s", latestTag)) - m.log("DankMaterialShell cloned successfully") - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.90, - Step: "Updating DankMaterialShell...", - IsComplete: false, - CommandInfo: "Updating ~/.config/quickshell/dms", - } - - fetchCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "fetch", "origin", "--tags", "--force") - if err := fetchCmd.Run(); err != nil { - m.logError("Failed to fetch updates", err) - return nil - } - - if forceDMSGit || variant == deps.VariantGit { - branchCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "rev-parse", "--abbrev-ref", "HEAD") - branchOutput, err := branchCmd.Output() - if err != nil { - m.logError("Failed to get current branch", err) - return nil - } - - branch := strings.TrimSpace(string(branchOutput)) - if branch == "" { - branch = "master" - } - - pullCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "pull", "origin", branch) - if err := pullCmd.Run(); err != nil { - m.logError("Failed to pull updates", err) - return nil - } - - m.log("DankMaterialShell updated successfully (git variant)") - return nil - } - - latestTagCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "describe", "--tags", "--abbrev=0", "origin/master") - tagOutput, err := latestTagCmd.Output() - if err != nil { - m.logError("Failed to get latest tag", err) - return nil - } - - latestTag := strings.TrimSpace(string(tagOutput)) - checkoutCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "checkout", latestTag) - if err := checkoutCmd.Run(); err != nil { - m.logError(fmt.Sprintf("Failed to checkout tag %s", latestTag), err) - return nil - } - - m.log(fmt.Sprintf("Updated to tag: %s", latestTag)) - return nil -} - -func (m *ManualPackageInstaller) installXwaylandSatellite(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing xwayland-satellite from source...") - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Installing xwayland-satellite via cargo...", - IsComplete: false, - CommandInfo: "cargo install --git https://github.com/Supreeeme/xwayland-satellite --tag v0.7", - } - - installCmd := exec.CommandContext(ctx, "cargo", "install", "--git", "https://github.com/Supreeeme/xwayland-satellite", "--tag", "v0.7") - if err := m.runWithProgressStep(installCmd, progressChan, PhaseSystemPackages, 0.1, 0.7, "Building xwayland-satellite..."); err != nil { - return fmt.Errorf("failed to install xwayland-satellite: %w", err) - } - - homeDir := os.Getenv("HOME") - sourcePath := filepath.Join(homeDir, ".cargo", "bin", "xwayland-satellite") - targetPath := "/usr/local/bin/xwayland-satellite" - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.7, - Step: "Installing xwayland-satellite binary to system...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo cp %s %s", sourcePath, targetPath), - } - - copyCmd := exec.CommandContext(ctx, "sudo", "-S", "cp", sourcePath, targetPath) - copyCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := copyCmd.Run(); err != nil { - return fmt.Errorf("failed to copy xwayland-satellite to /usr/local/bin: %w", err) - } - - chmodCmd := exec.CommandContext(ctx, "sudo", "-S", "chmod", "+x", targetPath) - chmodCmd.Stdin = strings.NewReader(sudoPassword + "\n") - if err := chmodCmd.Run(); err != nil { - return fmt.Errorf("failed to make xwayland-satellite executable: %w", err) - } - - m.log("xwayland-satellite installed successfully from source") - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/manual_packages_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/manual_packages_test.go deleted file mode 100644 index 4d9fc62..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/manual_packages_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package distros - -import ( - "testing" -) - -func TestManualPackageInstaller_parseLatestTagFromGitOutput(t *testing.T) { - tests := []struct { - name string - input string - expected string - }{ - { - name: "normal tag output", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0 -703a3789083d2f990c4e99cd25c97c2a4cccbd81 refs/tags/v0.1.0`, - expected: "v0.2.1", - }, - { - name: "annotated tags with ^{}", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1 -b1b150fab00a93ea983aaca5df55304bc837f51c refs/tags/v0.2.1^{} -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0`, - expected: "v0.2.1", - }, - { - name: "mixed tags", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.3.0 -b1b150fab00a93ea983aaca5df55304bc837f51c refs/tags/v0.3.0^{} -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0 -c1c150fab00a93ea983aaca5df55304bc837f51d refs/tags/beta-1`, - expected: "v0.3.0", - }, - { - name: "empty output", - input: "", - expected: "", - }, - { - name: "no tags", - input: "some other output\nwithout tags", - expected: "", - }, - { - name: "only annotated tags", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1^{} -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0^{}`, - expected: "", - }, - { - name: "single tag", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v1.0.0`, - expected: "v1.0.0", - }, - { - name: "tag with extra whitespace", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0`, - expected: "v0.2.1", - }, - { - name: "beta and rc tags", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.3.0-beta.1 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v0.2.0`, - expected: "v0.3.0-beta.1", - }, - { - name: "tags without v prefix", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/0.2.1 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/0.2.0`, - expected: "0.2.1", - }, - { - name: "multiple lines with spaces", - input: ` -a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v1.2.3 -a5431dd02dc23d9ef1680e67777fed00fe5f7cda refs/tags/v1.2.2 -`, - expected: "v1.2.3", - }, - { - name: "tag at end of line", - input: `a1a150fab00a93ea983aaca5df55304bc837f51b refs/tags/v0.2.1`, - expected: "v0.2.1", - }, - } - - logChan := make(chan string, 100) - defer close(logChan) - - base := NewBaseDistribution(logChan) - installer := &ManualPackageInstaller{BaseDistribution: base} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := installer.parseLatestTagFromGitOutput(tt.input) - - if result != tt.expected { - t.Errorf("parseLatestTagFromGitOutput() = %q, expected %q", result, tt.expected) - } - }) - } -} - -func TestManualPackageInstaller_parseLatestTagFromGitOutput_EmptyInstaller(t *testing.T) { - // Test that parsing works even with a minimal installer setup - logChan := make(chan string, 10) - defer close(logChan) - - base := NewBaseDistribution(logChan) - installer := &ManualPackageInstaller{BaseDistribution: base} - - input := `abc123 refs/tags/v1.0.0 -def456 refs/tags/v0.9.0` - - result := installer.parseLatestTagFromGitOutput(input) - - if result != "v1.0.0" { - t.Errorf("Expected v1.0.0, got %s", result) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/minimal_install.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/minimal_install.go deleted file mode 100644 index 029f1ba..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/minimal_install.go +++ /dev/null @@ -1,44 +0,0 @@ -package distros - -type minimalInstallGroup struct { - packages []string - minimal bool -} - -func shouldPreferMinimalInstall(pkg string) bool { - switch pkg { - case "niri", "niri-git": - return true - default: - return false - } -} - -func splitMinimalInstallPackages(packages []string) (normal []string, minimal []string) { - for _, pkg := range packages { - if shouldPreferMinimalInstall(pkg) { - minimal = append(minimal, pkg) - continue - } - normal = append(normal, pkg) - } - return normal, minimal -} - -func orderedMinimalInstallGroups(packages []string) []minimalInstallGroup { - normal, minimal := splitMinimalInstallPackages(packages) - groups := make([]minimalInstallGroup, 0, 2) - if len(minimal) > 0 { - groups = append(groups, minimalInstallGroup{ - packages: minimal, - minimal: true, - }) - } - if len(normal) > 0 { - groups = append(groups, minimalInstallGroup{ - packages: normal, - minimal: false, - }) - } - return groups -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go deleted file mode 100644 index 62f40cd..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go +++ /dev/null @@ -1,856 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "slices" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -func init() { - Register("opensuse-tumbleweed", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) - Register("opensuse-leap", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) - Register("opensuse-slowroll", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) -} - -type OpenSUSEDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -const openSUSENiriWaylandServerPackage = "libwayland-server0" - -func NewOpenSUSEDistribution(config DistroConfig, logChan chan<- string) *OpenSUSEDistribution { - base := NewBaseDistribution(logChan) - return &OpenSUSEDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (o *OpenSUSEDistribution) GetID() string { - return o.config.ID -} - -func (o *OpenSUSEDistribution) GetColorHex() string { - return o.config.ColorHex -} - -func (o *OpenSUSEDistribution) GetFamily() DistroFamily { - return o.config.Family -} - -func (o *OpenSUSEDistribution) GetPackageManager() PackageManagerType { - return PackageManagerZypper -} - -func (o *OpenSUSEDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return o.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (o *OpenSUSEDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, o.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, o.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, o.detectGit()) - dependencies = append(dependencies, o.detectWindowManager(wm)) - dependencies = append(dependencies, o.detectQuickshell()) - dependencies = append(dependencies, o.detectDMSGreeter()) - dependencies = append(dependencies, o.detectXDGPortal()) - dependencies = append(dependencies, o.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, o.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, o.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, o.detectMatugen()) - dependencies = append(dependencies, o.detectDgop()) - - return dependencies, nil -} - -func (o *OpenSUSEDistribution) detectXDGPortal() deps.Dependency { - return o.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", o.packageInstalled("xdg-desktop-portal-gtk")) -} - -func (o *OpenSUSEDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("rpm", "-q", pkg) - err := cmd.Run() - return err == nil -} - -func (o *OpenSUSEDistribution) detectDMSGreeter() deps.Dependency { - return o.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", o.packageInstalled("dms-greeter")) -} - -func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return o.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - // Standard zypper packages - "git": {Name: "git", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - - // DMS packages from OBS - "dms (DankMaterialShell)": o.getDmsMapping(variants["dms (DankMaterialShell)"]), - "quickshell": o.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": {Name: "dms-greeter", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "ghostty": {Name: "ghostty", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "matugen": {Name: "matugen", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "dgop": {Name: "dgop", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = PackageMapping{Name: "hyprland", Repository: RepoTypeSystem} - packages["hyprctl"] = PackageMapping{Name: "hyprland", Repository: RepoTypeSystem} - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - // Niri stable has native package support on openSUSE - niriVariant := variants["niri"] - packages["niri"] = o.getNiriMapping(niriVariant) - packages["xwayland-satellite"] = o.getXwaylandSatelliteMapping(niriVariant) - } - - return packages -} - -func (o *OpenSUSEDistribution) getDmsMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "dms-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:dms-git"} - } - return PackageMapping{Name: "dms", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:dms"} -} - -func (o *OpenSUSEDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "quickshell-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "quickshell", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} -} - -func (o *OpenSUSEDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "niri-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "niri", Repository: RepoTypeSystem} -} - -func (o *OpenSUSEDistribution) getXwaylandSatelliteMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "xwayland-satellite-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem} -} - -func (o *OpenSUSEDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if o.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (o *OpenSUSEDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if o.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (o *OpenSUSEDistribution) getPrerequisites() []string { - return []string{} -} - -func (o *OpenSUSEDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - prerequisites := o.getPrerequisites() - var missingPkgs []string - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking prerequisites...", - IsComplete: false, - LogOutput: "Checking prerequisite packages", - } - - for _, pkg := range prerequisites { - checkCmd := exec.CommandContext(ctx, "rpm", "-q", pkg) - if err := checkCmd.Run(); err != nil { - missingPkgs = append(missingPkgs, pkg) - } - } - - _, err := exec.LookPath("go") - if err != nil { - o.log("go not found in PATH, will install go") - missingPkgs = append(missingPkgs, "go") - } else { - o.log("go already available in PATH") - } - - if len(missingPkgs) == 0 { - o.log("All prerequisites already installed") - return nil - } - - o.log(fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: fmt.Sprintf("Installing %d prerequisites...", len(missingPkgs)), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo zypper install -y %s", strings.Join(missingPkgs, " ")), - LogOutput: fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")), - } - - args := []string{"zypper", "install", "-y"} - args = append(args, missingPkgs...) - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - o.logError("failed to install prerequisites", err) - o.log(fmt.Sprintf("Prerequisites command output: %s", string(output))) - return fmt.Errorf("failed to install prerequisites: %w", err) - } - o.log(fmt.Sprintf("Prerequisites install output: %s", string(output))) - - return nil -} - -func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := o.disableInstallMediaRepos(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to disable install media repositories: %w", err) - } - - if err := o.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, obsPkgs, manualPkgs, variantMap := o.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) - - // Enable OBS repositories - if len(obsPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.15, - Step: "Enabling OBS repositories...", - IsComplete: false, - LogOutput: "Setting up OBS repositories for additional packages", - } - if err := o.enableOBSRepos(ctx, obsPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to enable OBS repositories: %w", err) - } - } - - // Phase 3: System Packages (Zypper) - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := o.installZypperPackages(ctx, systemPkgs, sudoPassword, progressChan, PhaseSystemPackages, "Installing system packages...", 0.40, 0.60); err != nil { - return fmt.Errorf("failed to install zypper packages: %w", err) - } - } - - // OBS Packages - obsPkgNames := o.extractPackageNames(obsPkgs) - if len(obsPkgNames) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: fmt.Sprintf("Installing %d OBS packages...", len(obsPkgNames)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing OBS packages: %s", strings.Join(obsPkgNames, ", ")), - } - if err := o.installZypperPackages(ctx, obsPkgNames, sudoPassword, progressChan, PhaseAURPackages, "Installing OBS packages...", 0.70, 0.85); err != nil { - return fmt.Errorf("failed to install OBS packages: %w", err) - } - } - - // Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := o.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - terminal := o.DetectTerminalFromDeps(dependencies) - if err := o.WriteEnvironmentConfig(terminal); err != nil { - o.log(fmt.Sprintf("Warning: failed to write environment config: %v", err)) - } - - if err := o.WriteWindowManagerConfig(wm); err != nil { - o.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) - } - - if err := o.EnableDMSService(ctx, wm); err != nil { - o.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) - } - - // Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) { - systemPkgs := []string{} - obsPkgs := []PackageMapping{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := o.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - if disabledFlags[dep.Name] { - continue - } - - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - o.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypeOBS: - obsPkgs = append(obsPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - systemPkgs = o.appendMissingSystemPackages(systemPkgs, openSUSENiriRuntimePackages(wm, disabledFlags)) - - return systemPkgs, obsPkgs, manualPkgs, variantMap -} - -func openSUSENiriRuntimePackages(wm deps.WindowManager, disabledFlags map[string]bool) []string { - if wm != deps.WindowManagerNiri || disabledFlags["niri"] { - return nil - } - - return []string{openSUSENiriWaylandServerPackage} -} - -func (o *OpenSUSEDistribution) appendMissingSystemPackages(systemPkgs []string, extraPkgs []string) []string { - for _, pkg := range extraPkgs { - if slices.Contains(systemPkgs, pkg) || o.packageInstalled(pkg) { - continue - } - - o.log(fmt.Sprintf("Adding openSUSE runtime package: %s", pkg)) - systemPkgs = append(systemPkgs, pkg) - } - - return systemPkgs -} - -func (o *OpenSUSEDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (o *OpenSUSEDistribution) enableOBSRepos(ctx context.Context, obsPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - enabledRepos := make(map[string]bool) - - osInfo, err := GetOSInfo() - if err != nil { - return fmt.Errorf("failed to get OS info: %w", err) - } - - obsDistroVersion := "openSUSE_Tumbleweed" - switch osInfo.Distribution.ID { - case "opensuse-leap": - obsDistroVersion = fmt.Sprintf("openSUSE_Leap_%s", osInfo.VersionID) - case "opensuse-slowroll": - obsDistroVersion = "openSUSE_Slowroll" - } - - for _, pkg := range obsPkgs { - if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] { - o.log(fmt.Sprintf("Enabling OBS repository: %s", pkg.RepoURL)) - - // RepoURL format: "home:AvengeMedia:danklinux" - repoPath := strings.ReplaceAll(pkg.RepoURL, ":", ":/") - repoName := strings.ReplaceAll(pkg.RepoURL, ":", "-") - repoURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/%s/%s.repo", - repoPath, obsDistroVersion, pkg.RepoURL) - - checkCmd := exec.CommandContext(ctx, "zypper", "repos", repoName) - if checkCmd.Run() == nil { - o.log(fmt.Sprintf("OBS repo %s already exists, skipping", pkg.RepoURL)) - enabledRepos[pkg.RepoURL] = true - continue - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.20, - Step: fmt.Sprintf("Enabling OBS repo %s...", pkg.RepoURL), - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo zypper addrepo %s", repoURL), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("zypper addrepo -f %s", repoURL)) - if err := o.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.20, 0.22); err != nil { - o.log(fmt.Sprintf("OBS repo %s add failed (may already exist): %v", pkg.RepoURL, err)) - } - - enabledRepos[pkg.RepoURL] = true - o.log(fmt.Sprintf("OBS repo %s enabled successfully", pkg.RepoURL)) - } - } - - // Refresh repositories with GPG auto-import - if len(enabledRepos) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.25, - Step: "Refreshing repositories...", - NeedsSudo: true, - CommandInfo: "sudo zypper --gpg-auto-import-keys refresh", - } - - refreshCmd := ExecSudoCommand(ctx, sudoPassword, "zypper --gpg-auto-import-keys refresh") - if err := o.runWithProgress(refreshCmd, progressChan, PhaseSystemPackages, 0.25, 0.27); err != nil { - return fmt.Errorf("failed to refresh repositories: %w", err) - } - } - - return nil -} - -func isOpenSUSEInstallMediaURI(uri string) bool { - normalizedURI := strings.ToLower(strings.TrimSpace(uri)) - - return strings.HasPrefix(normalizedURI, "cd:/") || - strings.HasPrefix(normalizedURI, "dvd:/") || - strings.HasPrefix(normalizedURI, "hd:/") || - strings.HasPrefix(normalizedURI, "iso:/") -} - -func parseZypperInstallMediaAliases(output string) []string { - var aliases []string - - for _, line := range strings.Split(output, "\n") { - line = strings.TrimSpace(line) - if line == "" || !strings.Contains(line, "|") { - continue - } - - parts := strings.Split(line, "|") - if len(parts) < 7 { - continue - } - - for i := range parts { - parts[i] = strings.TrimSpace(parts[i]) - } - - alias := parts[1] - enabled := strings.ToLower(parts[3]) - uri := parts[len(parts)-1] - - if alias == "" || strings.EqualFold(alias, "alias") { - continue - } - if enabled != "" && enabled != "yes" { - continue - } - if !isOpenSUSEInstallMediaURI(uri) { - continue - } - - aliases = append(aliases, alias) - } - - return aliases -} - -func (o *OpenSUSEDistribution) disableInstallMediaRepos(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - listCmd := exec.CommandContext(ctx, "zypper", "repos", "-u") - output, err := listCmd.CombinedOutput() - if err != nil { - o.log(fmt.Sprintf("Warning: failed to list zypper repositories: %s", strings.TrimSpace(string(output)))) - return fmt.Errorf("failed to list zypper repositories: %w", err) - } - - aliases := parseZypperInstallMediaAliases(string(output)) - if len(aliases) == 0 { - return nil - } - - o.log(fmt.Sprintf("Disabling install media repositories: %s", strings.Join(aliases, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.055, - Step: "Disabling install media repositories...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo zypper modifyrepo -d %s", strings.Join(aliases, " ")), - LogOutput: fmt.Sprintf("Disabling install media repositories: %s", strings.Join(aliases, ", ")), - } - - for _, alias := range aliases { - cmd := ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("zypper modifyrepo -d '%s'", escapeSingleQuotes(alias))) - repoOutput, err := cmd.CombinedOutput() - if err != nil { - o.log(fmt.Sprintf("Failed to disable install media repo %s: %s", alias, strings.TrimSpace(string(repoOutput)))) - return fmt.Errorf("failed to disable install media repo %s: %w", alias, err) - } - o.log(fmt.Sprintf("Disabled install media repo %s: %s", alias, strings.TrimSpace(string(repoOutput)))) - } - - return nil -} - -func (o *OpenSUSEDistribution) zypperInstallArgs(packages []string, minimal bool) []string { - args := []string{"zypper", "install", "-y"} - if minimal { - args = append(args, "--no-recommends") - } - return append(args, packages...) -} - -func (o *OpenSUSEDistribution) installZypperPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg, phase InstallPhase, step string, startProgress float64, endProgress float64) error { - if len(packages) == 0 { - return nil - } - - o.log(fmt.Sprintf("Installing zypper packages: %s", strings.Join(packages, ", "))) - - groups := orderedMinimalInstallGroups(packages) - totalGroups := len(groups) - - groupIndex := 0 - installGroup := func(groupPackages []string, minimal bool) error { - if len(groupPackages) == 0 { - return nil - } - - groupIndex++ - groupStart := startProgress - groupEnd := endProgress - if totalGroups > 1 { - midpoint := startProgress + ((endProgress - startProgress) / 2) - if groupIndex == 1 { - groupEnd = midpoint - } else { - groupStart = midpoint - } - } - - args := o.zypperInstallArgs(groupPackages, minimal) - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: groupStart, - Step: step, - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return o.runWithProgress(cmd, progressChan, phase, groupStart, groupEnd) - } - - for _, group := range groups { - if err := installGroup(group.packages, group.minimal); err != nil { - return err - } - } - return nil -} - -func (o *OpenSUSEDistribution) installQuickshell(ctx context.Context, variant deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - o.log("Installing quickshell from source (with openSUSE-specific build flags)...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "quickshell-build") - if err := os.MkdirAll(tmpDir, 0o755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning quickshell repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/quickshell-mirror/quickshell.git", - } - - var cloneCmd *exec.Cmd - if forceQuickshellGit || variant == deps.VariantGit { - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - latestTag := o.getLatestQuickshellTag(ctx) - if latestTag != "" { - o.log(fmt.Sprintf("Using latest quickshell tag: %s", latestTag)) - cloneCmd = exec.CommandContext(ctx, "git", "clone", "--branch", latestTag, "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - o.log("Warning: failed to fetch latest tag, using default branch") - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } - } - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone quickshell: %w", err) - } - - buildDir := tmpDir + "/build" - if err := os.MkdirAll(buildDir, 0o755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.3, - Step: "Configuring quickshell build (with openSUSE flags)...", - IsComplete: false, - CommandInfo: "cmake -B build -S . -G Ninja", - } - - // Get optflags from rpm - optflagsCmd := exec.CommandContext(ctx, "rpm", "--eval", "%{optflags}") - optflagsOutput, err := optflagsCmd.Output() - optflags := strings.TrimSpace(string(optflagsOutput)) - if err != nil || optflags == "" { - o.log("Warning: Could not get optflags from rpm, using default -O2 -g") - optflags = "-O2 -g" - } - - // Set openSUSE-specific CFLAGS - customCFLAGS := fmt.Sprintf("%s -I/usr/include/wayland", optflags) - - configureCmd := exec.CommandContext(ctx, "cmake", "-GNinja", "-B", "build", - "-DCMAKE_BUILD_TYPE=RelWithDebInfo", - "-DCRASH_REPORTER=off", - "-DCMAKE_CXX_STANDARD=20") - configureCmd.Dir = tmpDir - configureCmd.Env = append(os.Environ(), - "TMPDIR="+cacheDir, - "CFLAGS="+customCFLAGS, - "CXXFLAGS="+customCFLAGS) - - o.log(fmt.Sprintf("Using CFLAGS: %s", customCFLAGS)) - - output, err := configureCmd.CombinedOutput() - if err != nil { - o.log(fmt.Sprintf("cmake configure failed. Output:\n%s", string(output))) - return fmt.Errorf("failed to configure quickshell: %w\nCMake output:\n%s", err, string(output)) - } - - o.log(fmt.Sprintf("cmake configure successful. Output:\n%s", string(output))) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.4, - Step: "Building quickshell (this may take a while)...", - IsComplete: false, - CommandInfo: "cmake --build build", - } - - buildCmd := exec.CommandContext(ctx, "cmake", "--build", "build") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), - "TMPDIR="+cacheDir, - "CFLAGS="+customCFLAGS, - "CXXFLAGS="+customCFLAGS) - if err := o.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.4, 0.8, "Building quickshell..."); err != nil { - return fmt.Errorf("failed to build quickshell: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing quickshell...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cmake --install build", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install build") - installCmd.Dir = tmpDir - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - - o.log("quickshell installed successfully from source") - return nil -} - -func (o *OpenSUSEDistribution) installRust(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if o.commandExists("cargo") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.82, - Step: "Installing rustup...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo zypper install rustup", - } - - rustupInstallCmd := ExecSudoCommand(ctx, sudoPassword, "zypper install -y rustup") - if err := o.runWithProgress(rustupInstallCmd, progressChan, PhaseSystemPackages, 0.82, 0.83); err != nil { - return fmt.Errorf("failed to install rustup: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.83, - Step: "Installing stable Rust toolchain...", - IsComplete: false, - CommandInfo: "rustup install stable", - } - - rustInstallCmd := exec.CommandContext(ctx, "bash", "-c", "rustup install stable && rustup default stable") - if err := o.runWithProgress(rustInstallCmd, progressChan, PhaseSystemPackages, 0.83, 0.84); err != nil { - return fmt.Errorf("failed to install Rust toolchain: %w", err) - } - - if !o.commandExists("cargo") { - o.log("Warning: cargo not found in PATH after Rust installation, trying to source environment") - } - - return nil -} - -func (o *OpenSUSEDistribution) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - o.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - for _, pkg := range packages { - if pkg == "matugen" { - if err := o.installRust(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Rust: %w", err) - } - break - } - } - - for _, pkg := range packages { - variant := variantMap[pkg] - if pkg == "quickshell" { - if err := o.installQuickshell(ctx, variant, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - } else { - if err := o.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install %s: %w", pkg, err) - } - } - } - - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/osinfo.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/osinfo.go deleted file mode 100644 index 8fc4b25..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/osinfo.go +++ /dev/null @@ -1,122 +0,0 @@ -package distros - -import ( - "bufio" - "fmt" - "os" - "runtime" - "strconv" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" -) - -// DistroInfo contains basic information about a distribution -type DistroInfo struct { - ID string - HexColorCode string -} - -// OSInfo contains complete OS information -type OSInfo struct { - Distribution DistroInfo - Version string - VersionID string - VersionCodename string - PrettyName string - Architecture string -} - -// GetOSInfo detects the current OS and returns information about it -func GetOSInfo() (*OSInfo, error) { - if runtime.GOOS != "linux" { - return nil, errdefs.NewCustomError(errdefs.ErrTypeNotLinux, fmt.Sprintf("Only linux is supported, but I found %s", runtime.GOOS)) - } - - if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" { - return nil, errdefs.NewCustomError(errdefs.ErrTypeInvalidArchitecture, fmt.Sprintf("Only amd64 and arm64 are supported, but I found %s", runtime.GOARCH)) - } - - info := &OSInfo{ - Architecture: runtime.GOARCH, - } - - file, err := os.Open("/etc/os-release") - if err != nil { - return nil, err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - parts := strings.SplitN(line, "=", 2) - if len(parts) != 2 { - continue - } - - key := parts[0] - value := strings.Trim(parts[1], "\"") - - switch key { - case "ID": - config, exists := Registry[value] - if !exists { - return nil, errdefs.NewCustomError(errdefs.ErrTypeUnsupportedDistribution, fmt.Sprintf("Unsupported distribution: %s", value)) - } - - info.Distribution = DistroInfo{ - ID: value, // Use the actual ID from os-release - HexColorCode: config.ColorHex, - } - case "VERSION_ID", "BUILD_ID": - info.VersionID = value - case "VERSION": - info.Version = value - case "VERSION_CODENAME": - info.VersionCodename = value - case "PRETTY_NAME": - info.PrettyName = value - } - } - - return info, scanner.Err() -} - -// IsUnsupportedDistro checks if a distribution/version combination is supported -func IsUnsupportedDistro(distroID, versionID string) bool { - if !IsDistroSupported(distroID) { - return true - } - - if distroID == "ubuntu" { - parts := strings.Split(versionID, ".") - if len(parts) >= 2 { - major, err1 := strconv.Atoi(parts[0]) - minor, err2 := strconv.Atoi(parts[1]) - - if err1 == nil && err2 == nil { - return major < 25 || (major == 25 && minor < 4) - } - } - return true - } - - if distroID == "debian" { - // unstable/sid support - if versionID == "sid" { - return false - } - if versionID == "" { - // debian testing/sid have no version ID - return false - } - versionNum, err := strconv.Atoi(versionID) - if err == nil { - return versionNum < 12 - } - return true - } - - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/ubuntu.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/ubuntu.go deleted file mode 100644 index 37911f0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/ubuntu.go +++ /dev/null @@ -1,702 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -func init() { - Register("ubuntu", "#E95420", FamilyUbuntu, func(config DistroConfig, logChan chan<- string) Distribution { - return NewUbuntuDistribution(config, logChan) - }) -} - -type UbuntuDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -func NewUbuntuDistribution(config DistroConfig, logChan chan<- string) *UbuntuDistribution { - base := NewBaseDistribution(logChan) - return &UbuntuDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (u *UbuntuDistribution) GetID() string { - return u.config.ID -} - -func (u *UbuntuDistribution) GetColorHex() string { - return u.config.ColorHex -} - -func (u *UbuntuDistribution) GetFamily() DistroFamily { - return u.config.Family -} - -func (u *UbuntuDistribution) GetPackageManager() PackageManagerType { - return PackageManagerAPT -} - -func (u *UbuntuDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return u.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (u *UbuntuDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, u.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, u.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, u.detectGit()) - dependencies = append(dependencies, u.detectWindowManager(wm)) - dependencies = append(dependencies, u.detectQuickshell()) - dependencies = append(dependencies, u.detectDMSGreeter()) - dependencies = append(dependencies, u.detectXDGPortal()) - dependencies = append(dependencies, u.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, u.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, u.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, u.detectMatugen()) - dependencies = append(dependencies, u.detectDgop()) - - return dependencies, nil -} - -func (u *UbuntuDistribution) detectXDGPortal() deps.Dependency { - return u.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", u.packageInstalled("xdg-desktop-portal-gtk")) -} - -func (u *UbuntuDistribution) detectXwaylandSatellite() deps.Dependency { - return u.detectCommand("xwayland-satellite", "Xwayland support") -} - -func (u *UbuntuDistribution) detectAccountsService() deps.Dependency { - return u.detectPackage("accountsservice", "D-Bus interface for user account query and manipulation", u.packageInstalled("accountsservice")) -} - -func (u *UbuntuDistribution) detectDMSGreeter() deps.Dependency { - return u.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", u.packageInstalled("dms-greeter")) -} - -func (u *UbuntuDistribution) packageInstalled(pkg string) bool { - return debianPackageInstalledPrecisely(pkg) -} - -func (u *UbuntuDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return u.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (u *UbuntuDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - // Standard APT packages - "git": {Name: "git", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - - // DMS packages from PPAs - "dms (DankMaterialShell)": u.getDmsMapping(variants["dms (DankMaterialShell)"]), - "quickshell": u.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": {Name: "dms-greeter", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}, - "matugen": {Name: "matugen", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}, - "dgop": {Name: "dgop", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}, - "ghostty": {Name: "ghostty", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}, - } - - switch wm { - case deps.WindowManagerHyprland: - // Use the cppiber PPA for Hyprland - packages["hyprland"] = PackageMapping{Name: "hyprland", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"} - packages["hyprctl"] = PackageMapping{Name: "hyprland", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"} - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - niriVariant := variants["niri"] - packages["niri"] = u.getNiriMapping(niriVariant) - packages["xwayland-satellite"] = u.getXwaylandSatelliteMapping(niriVariant) - } - - return packages -} - -func (u *UbuntuDistribution) getDmsMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "dms-git", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/dms-git"} - } - return PackageMapping{Name: "dms", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/dms"} -} - -func (u *UbuntuDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "quickshell-git", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"} - } - return PackageMapping{Name: "quickshell", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"} -} - -func (u *UbuntuDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "niri-git", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"} - } - return PackageMapping{Name: "niri", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"} -} - -func (u *UbuntuDistribution) getXwaylandSatelliteMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "xwayland-satellite-git", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"} - } - return PackageMapping{Name: "xwayland-satellite", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"} -} - -func (u *UbuntuDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Updating package lists...", - IsComplete: false, - LogOutput: "Updating APT package lists", - } - - updateCmd := ExecSudoCommand(ctx, sudoPassword, "apt-get update") - if err := u.runWithProgress(updateCmd, progressChan, PhasePrerequisites, 0.06, 0.07); err != nil { - return fmt.Errorf("failed to update package lists: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: "Installing build-essential...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install -y build-essential", - LogOutput: "Installing build tools", - } - - checkCmd := exec.CommandContext(ctx, "dpkg", "-l", "build-essential") - if err := checkCmd.Run(); err != nil { - // Not installed, install it - cmd := ExecSudoCommand(ctx, sudoPassword, "apt-get install -y build-essential") - if err := u.runWithProgress(cmd, progressChan, PhasePrerequisites, 0.08, 0.09); err != nil { - return fmt.Errorf("failed to install build-essential: %w", err) - } - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.10, - Step: "Installing development dependencies...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install -y curl wget git cmake ninja-build pkg-config libglib2.0-dev libpolkit-agent-1-dev", - LogOutput: "Installing additional development tools", - } - - devToolsCmd := ExecSudoCommand(ctx, sudoPassword, - "apt-get install -y curl wget git cmake ninja-build pkg-config libglib2.0-dev libpolkit-agent-1-dev") - if err := u.runWithProgress(devToolsCmd, progressChan, PhasePrerequisites, 0.10, 0.12); err != nil { - return fmt.Errorf("failed to install development tools: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.12, - Step: "Prerequisites installation complete", - IsComplete: false, - LogOutput: "Prerequisites successfully installed", - } - - return nil -} - -func (u *UbuntuDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := u.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, ppaPkgs, manualPkgs, variantMap := u.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) - - // Phase 2: Enable PPA repositories - if len(ppaPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.15, - Step: "Enabling PPA repositories...", - IsComplete: false, - LogOutput: "Setting up PPA repositories for additional packages", - } - if err := u.enablePPARepos(ctx, ppaPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to enable PPA repositories: %w", err) - } - } - - // Phase 3: System Packages (APT) - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := u.installAPTPackages(ctx, systemPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install APT packages: %w", err) - } - } - - // Phase 4: PPA Packages - ppaPkgNames := u.extractPackageNames(ppaPkgs) - if len(ppaPkgNames) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, // Reusing AUR phase for PPA - Progress: 0.65, - Step: fmt.Sprintf("Installing %d PPA packages...", len(ppaPkgNames)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing PPA packages: %s", strings.Join(ppaPkgNames, ", ")), - } - if err := u.installPPAPackages(ctx, ppaPkgNames, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install PPA packages: %w", err) - } - } - - // Phase 5: Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.80, - Step: "Installing build dependencies...", - IsComplete: false, - LogOutput: "Installing build tools for manual compilation", - } - if err := u.installBuildDependencies(ctx, manualPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install build dependencies: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := u.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Phase 6: Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - terminal := u.DetectTerminalFromDeps(dependencies) - if err := u.WriteEnvironmentConfig(terminal); err != nil { - u.log(fmt.Sprintf("Warning: failed to write environment config: %v", err)) - } - - if err := u.WriteWindowManagerConfig(wm); err != nil { - u.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) - } - - if err := u.EnableDMSService(ctx, wm); err != nil { - u.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) - } - - // Phase 7: Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (u *UbuntuDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) { - systemPkgs := []string{} - ppaPkgs := []PackageMapping{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := u.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - if disabledFlags[dep.Name] { - continue - } - - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - u.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypePPA: - ppaPkgs = append(ppaPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - return systemPkgs, ppaPkgs, manualPkgs, variantMap -} - -func (u *UbuntuDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (u *UbuntuDistribution) enablePPARepos(ctx context.Context, ppaPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - enabledRepos := make(map[string]bool) - - installPPACmd := ExecSudoCommand(ctx, sudoPassword, - "apt-get install -y software-properties-common") - if err := u.runWithProgress(installPPACmd, progressChan, PhaseSystemPackages, 0.15, 0.17); err != nil { - return fmt.Errorf("failed to install software-properties-common: %w", err) - } - - for _, pkg := range ppaPkgs { - if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] { - u.log(fmt.Sprintf("Enabling PPA repository: %s", pkg.RepoURL)) - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.20, - Step: fmt.Sprintf("Enabling PPA repo %s...", pkg.RepoURL), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo add-apt-repository -y %s", pkg.RepoURL), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("add-apt-repository -y %s", pkg.RepoURL)) - if err := u.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.20, 0.22); err != nil { - u.logError(fmt.Sprintf("failed to enable PPA repo %s", pkg.RepoURL), err) - return fmt.Errorf("failed to enable PPA repo %s: %w", pkg.RepoURL, err) - } - u.log(fmt.Sprintf("PPA repo %s enabled successfully", pkg.RepoURL)) - enabledRepos[pkg.RepoURL] = true - } - } - - if len(enabledRepos) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.25, - Step: "Updating package lists...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get update", - } - - updateCmd := ExecSudoCommand(ctx, sudoPassword, "apt-get update") - if err := u.runWithProgress(updateCmd, progressChan, PhaseSystemPackages, 0.25, 0.27); err != nil { - return fmt.Errorf("failed to update package lists after adding PPAs: %w", err) - } - } - - return nil -} - -func (u *UbuntuDistribution) installAPTPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - u.log(fmt.Sprintf("Installing APT packages: %s", strings.Join(packages, ", "))) - return u.installAPTGroups(ctx, packages, sudoPassword, progressChan, PhaseSystemPackages, "Installing system packages...", 0.40, 0.60) -} - -func (u *UbuntuDistribution) installPPAPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - u.log(fmt.Sprintf("Installing PPA packages: %s", strings.Join(packages, ", "))) - return u.installAPTGroups(ctx, packages, sudoPassword, progressChan, PhaseAURPackages, "Installing PPA packages...", 0.70, 0.85) -} - -func (u *UbuntuDistribution) aptInstallArgs(packages []string, minimal bool) []string { - args := []string{"DEBIAN_FRONTEND=noninteractive", "apt-get", "install", "-y"} - if minimal { - args = append(args, "--no-install-recommends") - } - return append(args, packages...) -} - -func (u *UbuntuDistribution) installAPTGroups(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg, phase InstallPhase, step string, startProgress float64, endProgress float64) error { - groups := orderedMinimalInstallGroups(packages) - totalGroups := len(groups) - - groupIndex := 0 - installGroup := func(groupPackages []string, minimal bool) error { - if len(groupPackages) == 0 { - return nil - } - - groupIndex++ - groupStart := startProgress - groupEnd := endProgress - if totalGroups > 1 { - midpoint := startProgress + ((endProgress - startProgress) / 2) - if groupIndex == 1 { - groupEnd = midpoint - } else { - groupStart = midpoint - } - } - - args := u.aptInstallArgs(groupPackages, minimal) - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: groupStart, - Step: step, - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return u.runWithProgress(cmd, progressChan, phase, groupStart, groupEnd) - } - - for _, group := range groups { - if err := installGroup(group.packages, group.minimal); err != nil { - return err - } - } - return nil -} - -func (u *UbuntuDistribution) installBuildDependencies(ctx context.Context, manualPkgs []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - buildDeps := make(map[string]bool) - - for _, pkg := range manualPkgs { - switch pkg { - case "niri": - buildDeps["curl"] = true - buildDeps["libxkbcommon-dev"] = true - buildDeps["libwayland-dev"] = true - buildDeps["libudev-dev"] = true - buildDeps["libinput-dev"] = true - buildDeps["libdisplay-info-dev"] = true - buildDeps["libpango1.0-dev"] = true - buildDeps["libcairo-dev"] = true - buildDeps["libpipewire-0.3-dev"] = true - buildDeps["libc6-dev"] = true - buildDeps["clang"] = true - buildDeps["libseat-dev"] = true - buildDeps["libgbm-dev"] = true - buildDeps["alacritty"] = true - buildDeps["fuzzel"] = true - buildDeps["libxcb-cursor-dev"] = true - case "quickshell": - buildDeps["qt6-base-dev"] = true - buildDeps["qt6-base-private-dev"] = true - buildDeps["qt6-declarative-dev"] = true - buildDeps["qt6-declarative-private-dev"] = true - buildDeps["qt6-wayland-dev"] = true - buildDeps["qt6-wayland-private-dev"] = true - buildDeps["qt6-tools-dev"] = true - buildDeps["libqt6svg6-dev"] = true - buildDeps["qt6-shadertools-dev"] = true - buildDeps["spirv-tools"] = true - buildDeps["libcli11-dev"] = true - buildDeps["libjemalloc-dev"] = true - buildDeps["libwayland-dev"] = true - buildDeps["wayland-protocols"] = true - buildDeps["libdrm-dev"] = true - buildDeps["libgbm-dev"] = true - buildDeps["libegl-dev"] = true - buildDeps["libgles2-mesa-dev"] = true - buildDeps["libgl1-mesa-dev"] = true - buildDeps["libxcb1-dev"] = true - buildDeps["libpipewire-0.3-dev"] = true - buildDeps["libpam0g-dev"] = true - case "matugen": - buildDeps["curl"] = true - } - } - - for _, pkg := range manualPkgs { - switch pkg { - case "niri", "matugen": - if err := u.installRust(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Rust: %w", err) - } - case "dgop": - if err := u.installGo(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Go: %w", err) - } - } - } - - if len(buildDeps) == 0 { - return nil - } - - depList := make([]string, 0, len(buildDeps)) - for dep := range buildDeps { - depList = append(depList, dep) - } - - args := []string{"apt-get", "install", "-y"} - args = append(args, depList...) - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return u.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.80, 0.82) -} - -func (u *UbuntuDistribution) installRust(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if u.commandExists("cargo") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.82, - Step: "Installing rustup...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install rustup", - } - - rustupInstallCmd := ExecSudoCommand(ctx, sudoPassword, "apt-get install -y rustup") - if err := u.runWithProgress(rustupInstallCmd, progressChan, PhaseSystemPackages, 0.82, 0.83); err != nil { - return fmt.Errorf("failed to install rustup: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.83, - Step: "Installing stable Rust toolchain...", - IsComplete: false, - CommandInfo: "rustup install stable", - } - - rustInstallCmd := exec.CommandContext(ctx, "bash", "-c", "rustup install stable && rustup default stable") - if err := u.runWithProgress(rustInstallCmd, progressChan, PhaseSystemPackages, 0.83, 0.84); err != nil { - return fmt.Errorf("failed to install Rust toolchain: %w", err) - } - - // Verify cargo is now available - if !u.commandExists("cargo") { - u.log("Warning: cargo not found in PATH after Rust installation, trying to source environment") - } - - return nil -} - -func (u *UbuntuDistribution) installGo(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if u.commandExists("go") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.87, - Step: "Adding Go PPA repository...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo add-apt-repository ppa:longsleep/golang-backports", - } - - addPPACmd := ExecSudoCommand(ctx, sudoPassword, - "add-apt-repository -y ppa:longsleep/golang-backports") - if err := u.runWithProgress(addPPACmd, progressChan, PhaseSystemPackages, 0.87, 0.88); err != nil { - return fmt.Errorf("failed to add Go PPA: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.88, - Step: "Updating package lists...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get update", - } - - updateCmd := ExecSudoCommand(ctx, sudoPassword, "apt-get update") - if err := u.runWithProgress(updateCmd, progressChan, PhaseSystemPackages, 0.88, 0.89); err != nil { - return fmt.Errorf("failed to update package lists after adding Go PPA: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.89, - Step: "Installing Go...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo apt-get install golang-go", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, "apt-get install -y golang-go") - return u.runWithProgress(installCmd, progressChan, PhaseSystemPackages, 0.89, 0.90) -} - -func (u *UbuntuDistribution) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - u.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - for _, pkg := range packages { - switch pkg { - default: - if err := u.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install %s: %w", pkg, err) - } - } - } - - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/errdefs/errdefs.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/errdefs/errdefs.go deleted file mode 100644 index 3b5f159..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/errdefs/errdefs.go +++ /dev/null @@ -1,65 +0,0 @@ -package errdefs - -type ErrorType int - -const ( - ErrTypeNotLinux ErrorType = iota - ErrTypeInvalidArchitecture - ErrTypeUnsupportedDistribution - ErrTypeUnsupportedVersion - ErrTypeUpdateCancelled - ErrTypeNoUpdateNeeded - ErrTypeInvalidTemperature - ErrTypeInvalidGamma - ErrTypeInvalidLocation - ErrTypeInvalidManualTimes - ErrTypeNoWaylandDisplay - ErrTypeNoGammaControl - ErrTypeNotInitialized - ErrTypeSecretPromptCancelled - ErrTypeSecretPromptTimeout - ErrTypeSecretAgentFailed - ErrTypeGeneric -) - -type CustomError struct { - Type ErrorType - Message string -} - -func (e *CustomError) Error() string { - return e.Message -} - -func NewCustomError(errType ErrorType, message string) error { - return &CustomError{ - Type: errType, - Message: message, - } -} - -const ( - ErrBadCredentials = "bad-credentials" - ErrNoSuchSSID = "no-such-ssid" - ErrAssocTimeout = "assoc-timeout" - ErrDhcpTimeout = "dhcp-timeout" - ErrUserCanceled = "user-canceled" - ErrWifiDisabled = "wifi-disabled" - ErrAlreadyConnected = "already-connected" - ErrConnectionFailed = "connection-failed" -) - -var ( - ErrUpdateCancelled = NewCustomError(ErrTypeUpdateCancelled, "update cancelled by user") - ErrNoUpdateNeeded = NewCustomError(ErrTypeNoUpdateNeeded, "no update needed") - ErrInvalidTemperature = NewCustomError(ErrTypeInvalidTemperature, "temperature must be between 1000 and 10000") - ErrInvalidGamma = NewCustomError(ErrTypeInvalidGamma, "gamma must be between 0 and 10") - ErrInvalidLocation = NewCustomError(ErrTypeInvalidLocation, "invalid latitude/longitude") - ErrInvalidManualTimes = NewCustomError(ErrTypeInvalidManualTimes, "both sunrise and sunset must be set or neither") - ErrNoWaylandDisplay = NewCustomError(ErrTypeNoWaylandDisplay, "no wayland display available") - ErrNoGammaControl = NewCustomError(ErrTypeNoGammaControl, "compositor does not support gamma control") - ErrNotInitialized = NewCustomError(ErrTypeNotInitialized, "manager not initialized") - ErrSecretPromptCancelled = NewCustomError(ErrTypeSecretPromptCancelled, "secret prompt cancelled by user") - ErrSecretPromptTimeout = NewCustomError(ErrTypeSecretPromptTimeout, "secret prompt timed out") - ErrSecretAgentFailed = NewCustomError(ErrTypeSecretAgentFailed, "secret agent operation failed") -) diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client.go deleted file mode 100644 index 5b44121..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client.go +++ /dev/null @@ -1,42 +0,0 @@ -package geolocation - -import "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - -func NewClient() Client { - geoclueClient, err := newGeoClueClient() - if err != nil { - log.Warnf("GeoClue2 unavailable: %v", err) - return newSeededIpClient() - } - - loc, _ := geoclueClient.GetLocation() - if loc.Latitude != 0 || loc.Longitude != 0 { - log.Info("Using GeoClue2 location") - return geoclueClient - } - - log.Info("GeoClue2 has no fix yet, seeding with IP location") - ipLoc, err := fetchIPLocation() - if err != nil { - log.Warnf("IP location seed failed: %v", err) - return geoclueClient - } - - log.Info("Seeded GeoClue2 with IP location") - geoclueClient.SeedLocation(Location{Latitude: ipLoc.Latitude, Longitude: ipLoc.Longitude}) - return geoclueClient -} - -func newSeededIpClient() *IpClient { - client := newIpClient() - ipLoc, err := fetchIPLocation() - if err != nil { - log.Warnf("IP location also failed: %v", err) - return client - } - - log.Info("Using IP location") - client.currLocation.Latitude = ipLoc.Latitude - client.currLocation.Longitude = ipLoc.Longitude - return client -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client_geoclue.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client_geoclue.go deleted file mode 100644 index a06aadf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client_geoclue.go +++ /dev/null @@ -1,243 +0,0 @@ -package geolocation - -import ( - "fmt" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/godbus/dbus/v5" -) - -const ( - dbusGeoClueService = "org.freedesktop.GeoClue2" - dbusGeoCluePath = "/org/freedesktop/GeoClue2" - dbusGeoClueInterface = dbusGeoClueService - - dbusGeoClueManagerPath = dbusGeoCluePath + "/Manager" - dbusGeoClueManagerInterface = dbusGeoClueInterface + ".Manager" - dbusGeoClueManagerGetClient = dbusGeoClueManagerInterface + ".GetClient" - - dbusGeoClueClientInterface = dbusGeoClueInterface + ".Client" - dbusGeoClueClientDesktopId = dbusGeoClueClientInterface + ".DesktopId" - dbusGeoClueClientTimeThreshold = dbusGeoClueClientInterface + ".TimeThreshold" - dbusGeoClueClientTimeStart = dbusGeoClueClientInterface + ".Start" - dbusGeoClueClientTimeStop = dbusGeoClueClientInterface + ".Stop" - dbusGeoClueClientLocationUpdated = dbusGeoClueClientInterface + ".LocationUpdated" - - dbusGeoClueLocationInterface = dbusGeoClueInterface + ".Location" - dbusGeoClueLocationLatitude = dbusGeoClueLocationInterface + ".Latitude" - dbusGeoClueLocationLongitude = dbusGeoClueLocationInterface + ".Longitude" -) - -type GeoClueClient struct { - currLocation *Location - locationMutex sync.RWMutex - - dbusConn *dbus.Conn - clientPath dbus.ObjectPath - signals chan *dbus.Signal - - stopChan chan struct{} - sigWG sync.WaitGroup - - subscribers syncmap.Map[string, chan Location] -} - -func newGeoClueClient() (*GeoClueClient, error) { - dbusConn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("system bus connection failed: %w", err) - } - - c := &GeoClueClient{ - dbusConn: dbusConn, - stopChan: make(chan struct{}), - signals: make(chan *dbus.Signal, 256), - - currLocation: &Location{ - Latitude: 0.0, - Longitude: 0.0, - }, - } - - if err := c.setupClient(); err != nil { - dbusConn.Close() - return nil, err - } - - if err := c.startSignalPump(); err != nil { - return nil, err - } - - return c, nil -} - -func (c *GeoClueClient) Close() { - close(c.stopChan) - - c.sigWG.Wait() - - if c.signals != nil { - c.dbusConn.RemoveSignal(c.signals) - close(c.signals) - } - - c.subscribers.Range(func(key string, ch chan Location) bool { - close(ch) - c.subscribers.Delete(key) - return true - }) - - if c.dbusConn != nil { - c.dbusConn.Close() - } -} - -func (c *GeoClueClient) Subscribe(id string) chan Location { - ch := make(chan Location, 64) - c.subscribers.Store(id, ch) - return ch -} - -func (c *GeoClueClient) Unsubscribe(id string) { - if ch, ok := c.subscribers.LoadAndDelete(id); ok { - close(ch) - } -} - -func (c *GeoClueClient) setupClient() error { - managerObj := c.dbusConn.Object(dbusGeoClueService, dbusGeoClueManagerPath) - - if err := managerObj.Call(dbusGeoClueManagerGetClient, 0).Store(&c.clientPath); err != nil { - return fmt.Errorf("failed to create GeoClue2 client: %w", err) - } - - clientObj := c.dbusConn.Object(dbusGeoClueService, c.clientPath) - if err := clientObj.SetProperty(dbusGeoClueClientDesktopId, "dms"); err != nil { - return fmt.Errorf("failed to set desktop ID: %w", err) - } - - if err := clientObj.SetProperty(dbusGeoClueClientTimeThreshold, uint(10)); err != nil { - return fmt.Errorf("failed to set time threshold: %w", err) - } - - return nil -} - -func (c *GeoClueClient) startSignalPump() error { - c.dbusConn.Signal(c.signals) - - if err := c.dbusConn.AddMatchSignal( - dbus.WithMatchObjectPath(c.clientPath), - dbus.WithMatchInterface(dbusGeoClueClientInterface), - dbus.WithMatchSender(dbusGeoClueClientLocationUpdated), - ); err != nil { - return err - } - - c.sigWG.Add(1) - go func() { - defer c.sigWG.Done() - - clientObj := c.dbusConn.Object(dbusGeoClueService, c.clientPath) - clientObj.Call(dbusGeoClueClientTimeStart, 0) - defer clientObj.Call(dbusGeoClueClientTimeStop, 0) - - for { - select { - case <-c.stopChan: - return - case sig, ok := <-c.signals: - if !ok { - return - } - if sig == nil { - continue - } - - c.handleSignal(sig) - } - } - }() - - return nil -} - -func (c *GeoClueClient) handleSignal(sig *dbus.Signal) { - switch sig.Name { - case dbusGeoClueClientLocationUpdated: - if len(sig.Body) != 2 { - return - } - - newLocationPath, ok := sig.Body[1].(dbus.ObjectPath) - if !ok { - return - } - - if err := c.handleLocationUpdated(newLocationPath); err != nil { - log.Warn("GeoClue: Failed to handle location update: %v", err) - return - } - } -} - -func (c *GeoClueClient) handleLocationUpdated(path dbus.ObjectPath) error { - locationObj := c.dbusConn.Object(dbusGeoClueService, path) - - lat, err := locationObj.GetProperty(dbusGeoClueLocationLatitude) - if err != nil { - return err - } - - long, err := locationObj.GetProperty(dbusGeoClueLocationLongitude) - if err != nil { - return err - } - - c.locationMutex.Lock() - c.currLocation.Latitude = dbusutil.AsOr(lat, 0.0) - c.currLocation.Longitude = dbusutil.AsOr(long, 0.0) - c.locationMutex.Unlock() - - c.notifySubscribers() - return nil -} - -func (c *GeoClueClient) notifySubscribers() { - currentLocation, err := c.GetLocation() - if err != nil { - return - } - - c.subscribers.Range(func(key string, ch chan Location) bool { - select { - case ch <- currentLocation: - default: - log.Warn("GeoClue: subscriber channel full, dropping update") - } - return true - }) -} - -func (c *GeoClueClient) SeedLocation(loc Location) { - c.locationMutex.Lock() - defer c.locationMutex.Unlock() - c.currLocation.Latitude = loc.Latitude - c.currLocation.Longitude = loc.Longitude -} - -func (c *GeoClueClient) GetLocation() (Location, error) { - c.locationMutex.RLock() - defer c.locationMutex.RUnlock() - if c.currLocation == nil { - return Location{ - Latitude: 0.0, - Longitude: 0.0, - }, nil - } - stateCopy := *c.currLocation - return stateCopy, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client_ip.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client_ip.go deleted file mode 100644 index 0e68cae..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/client_ip.go +++ /dev/null @@ -1,91 +0,0 @@ -package geolocation - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "time" -) - -type IpClient struct { - currLocation *Location -} - -type ipLocationResult struct { - Location - City string -} - -type ipAPIResponse struct { - Status string `json:"status"` - Lat float64 `json:"lat"` - Lon float64 `json:"lon"` - City string `json:"city"` -} - -func newIpClient() *IpClient { - return &IpClient{ - currLocation: &Location{}, - } -} - -func (c *IpClient) Subscribe(id string) chan Location { - ch := make(chan Location, 1) - if location, err := c.GetLocation(); err == nil { - ch <- location - } - return ch -} - -func (c *IpClient) Unsubscribe(id string) {} - -func (c *IpClient) Close() {} - -func (c *IpClient) GetLocation() (Location, error) { - if c.currLocation.Latitude != 0 || c.currLocation.Longitude != 0 { - return *c.currLocation, nil - } - - result, err := fetchIPLocation() - if err != nil { - return Location{}, err - } - - c.currLocation.Latitude = result.Latitude - c.currLocation.Longitude = result.Longitude - return *c.currLocation, nil -} - -func fetchIPLocation() (ipLocationResult, error) { - client := &http.Client{Timeout: 10 * time.Second} - - resp, err := client.Get("http://ip-api.com/json/") - if err != nil { - return ipLocationResult{}, fmt.Errorf("failed to fetch IP location: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return ipLocationResult{}, fmt.Errorf("ip-api.com returned status %d", resp.StatusCode) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return ipLocationResult{}, fmt.Errorf("failed to read response: %w", err) - } - - var data ipAPIResponse - if err := json.Unmarshal(body, &data); err != nil { - return ipLocationResult{}, fmt.Errorf("failed to parse response: %w", err) - } - - if data.Status == "fail" || (data.Lat == 0 && data.Lon == 0) { - return ipLocationResult{}, fmt.Errorf("ip-api.com returned no location data") - } - - return ipLocationResult{ - Location: Location{Latitude: data.Lat, Longitude: data.Lon}, - City: data.City, - }, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/types.go deleted file mode 100644 index bf71041..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/geolocation/types.go +++ /dev/null @@ -1,15 +0,0 @@ -package geolocation - -type Location struct { - Latitude float64 - Longitude float64 -} - -type Client interface { - GetLocation() (Location, error) - - Subscribe(id string) chan Location - Unsubscribe(id string) - - Close() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/assets/apparmor/usr.bin.dms-greeter b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/assets/apparmor/usr.bin.dms-greeter deleted file mode 100644 index 7c42f9e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/assets/apparmor/usr.bin.dms-greeter +++ /dev/null @@ -1,91 +0,0 @@ -# AppArmor profile for dms-greeter -# -# Managed by DMS — regenerated on every `dms greeter install` / `dms greeter sync`. -# Manual edits will be overwritten on next sync. -# -# Mode: complain (denials are logged, nothing is blocked) -# To switch to enforce after validating with `aa-logprof`: -# sudo aa-enforce /etc/apparmor.d/usr.bin.dms-greeter -# -#include - -profile dms-greeter /usr/bin/dms-greeter flags=(complain) { - #include - #include - - # The launcher script itself - /usr/bin/dms-greeter r, - - # Cache directory — created by dms greeter sync/enable with greeter:greeter ownership - /var/cache/dms-greeter/ rw, - /var/cache/dms-greeter/** rwlk, - - # DMS config — packaged path - /usr/share/quickshell/dms-greeter/ r, - /usr/share/quickshell/dms-greeter/** r, - /usr/share/quickshell/ r, - /usr/share/quickshell/** r, - - # DMS config — system and user overrides - /etc/dms/ r, - /etc/dms/** r, - /usr/share/dms/ r, - /usr/share/dms/** r, - /home/*/.config/quickshell/ r, - /home/*/.config/quickshell/** r, - /root/.config/quickshell/ r, - /root/.config/quickshell/** r, - - # greetd / PAM — read-only for session setup - /etc/greetd/ r, - /etc/greetd/** r, - /etc/pam.d/ r, - /etc/pam.d/** r, - /usr/lib/pam.d/ r, - /usr/lib/pam.d/** r, - - # Compositor binaries — run unconfined so each compositor uses its own profile - /usr/bin/niri Ux, - /usr/bin/hyprland Ux, - /usr/bin/Hyprland Ux, - /usr/bin/sway Ux, - /usr/bin/labwc Ux, - /usr/bin/scroll Ux, - /usr/bin/miracle-wm Ux, - /usr/bin/mango Ux, - - # Quickshell — run unconfined (has its own compositor profile on some distros) - /usr/bin/qs Ux, - /usr/bin/quickshell Ux, - - # Wayland / XDG runtime (pipewire, wireplumber, wayland socket) - /run/user/[0-9]*/ rw, - /run/user/[0-9]*/** rw, - - # DRM / GPU devices (required for Wayland compositor startup) - /dev/dri/ r, - /dev/dri/* rw, - /dev/udmabuf rw, - - # Input devices - /dev/input/ r, - /dev/input/* r, - - # Systemd journal / logging - /run/systemd/journal/socket rw, - /dev/log rw, - - # Shell helper binaries invoked by the launcher script - /usr/bin/env ix, - /usr/bin/mkdir ix, - /usr/bin/cat ix, - /usr/bin/grep ix, - /usr/bin/dirname ix, - /usr/bin/basename ix, - /usr/bin/command ix, - /bin/env ix, - /bin/mkdir ix, - - # Signal management (compositor lifecycle) - signal (send, receive) set=("term", "int", "hup", "kill"), -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/installer.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/installer.go deleted file mode 100644 index ace72ce..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/installer.go +++ /dev/null @@ -1,2076 +0,0 @@ -package greeter - -import ( - "bufio" - "context" - _ "embed" - "encoding/json" - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/config" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/AvengeMedia/DankMaterialShell/core/internal/matugen" - sharedpam "github.com/AvengeMedia/DankMaterialShell/core/internal/pam" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/sblinch/kdl-go" - "github.com/sblinch/kdl-go/document" -) - -var appArmorProfileData []byte - -const appArmorProfileDest = "/etc/apparmor.d/usr.bin.dms-greeter" - -const GreeterCacheDir = "/var/cache/dms-greeter" - -func DetectDMSPath() (string, error) { - return config.LocateDMSConfig() -} - -// IsNixOS returns true when running on NixOS, which manages PAM configs through -// its module system. The DMS PAM managed block won't be written on NixOS. -func IsNixOS() bool { - _, err := os.Stat("/etc/NIXOS") - return err == nil -} - -func DetectGreeterGroup() string { - data, err := os.ReadFile("/etc/group") - if err != nil { - fmt.Fprintln(os.Stderr, "⚠ Warning: could not read /etc/group, defaulting to greeter") - return "greeter" - } - - if group, found := utils.FindGroupData(string(data), "greeter", "greetd", "_greeter"); found { - return group - } - - fmt.Fprintln(os.Stderr, "⚠ Warning: no greeter group found in /etc/group, defaulting to greeter") - return "greeter" -} - -func hasPasswdUser(passwdData, user string) bool { - prefix := user + ":" - for line := range strings.SplitSeq(passwdData, "\n") { - if strings.HasPrefix(line, prefix) { - return true - } - } - return false -} - -func findPasswdUser(passwdData string, candidates ...string) (string, bool) { - for _, candidate := range candidates { - if hasPasswdUser(passwdData, candidate) { - return candidate, true - } - } - return "", false -} - -func stripTomlComment(line string) string { - trimmed := strings.TrimSpace(line) - if idx := strings.Index(trimmed, "#"); idx >= 0 { - return strings.TrimSpace(trimmed[:idx]) - } - return trimmed -} - -func parseTomlSection(line string) (string, bool) { - trimmed := stripTomlComment(line) - if len(trimmed) < 3 || !strings.HasPrefix(trimmed, "[") || !strings.HasSuffix(trimmed, "]") { - return "", false - } - return strings.TrimSpace(trimmed[1 : len(trimmed)-1]), true -} - -func extractDefaultSessionUser(configContent string) string { - inDefaultSession := false - for line := range strings.SplitSeq(configContent, "\n") { - if section, ok := parseTomlSection(line); ok { - inDefaultSession = section == "default_session" - continue - } - - if !inDefaultSession { - continue - } - - trimmed := stripTomlComment(line) - if !strings.HasPrefix(trimmed, "user =") && !strings.HasPrefix(trimmed, "user=") { - continue - } - - parts := strings.SplitN(trimmed, "=", 2) - if len(parts) != 2 { - continue - } - user := strings.Trim(strings.TrimSpace(parts[1]), `"`) - if user != "" { - return user - } - } - - return "" -} - -func upsertDefaultSession(configContent, greeterUser, command string) string { - lines := strings.Split(configContent, "\n") - var out []string - - inDefaultSession := false - foundDefaultSession := false - defaultSessionUserSet := false - defaultSessionCommandSet := false - - appendDefaultSessionFields := func() { - if !defaultSessionUserSet { - out = append(out, fmt.Sprintf(`user = "%s"`, greeterUser)) - } - if !defaultSessionCommandSet { - out = append(out, command) - } - } - - for _, line := range lines { - if section, ok := parseTomlSection(line); ok { - if inDefaultSession { - appendDefaultSessionFields() - } - - inDefaultSession = section == "default_session" - if inDefaultSession { - foundDefaultSession = true - defaultSessionUserSet = false - defaultSessionCommandSet = false - } - - out = append(out, line) - continue - } - - if inDefaultSession { - trimmed := stripTomlComment(line) - if strings.HasPrefix(trimmed, "user =") || strings.HasPrefix(trimmed, "user=") { - out = append(out, fmt.Sprintf(`user = "%s"`, greeterUser)) - defaultSessionUserSet = true - continue - } - - if strings.HasPrefix(trimmed, "command =") || strings.HasPrefix(trimmed, "command=") { - if !defaultSessionCommandSet { - out = append(out, command) - defaultSessionCommandSet = true - } - continue - } - } - - out = append(out, line) - } - - if inDefaultSession { - appendDefaultSessionFields() - } - - if !foundDefaultSession { - if len(out) > 0 && strings.TrimSpace(out[len(out)-1]) != "" { - out = append(out, "") - } - out = append(out, "[default_session]") - out = append(out, fmt.Sprintf(`user = "%s"`, greeterUser)) - out = append(out, command) - } - - return strings.Join(out, "\n") -} - -func DetectGreeterUser() string { - passwdData, err := os.ReadFile("/etc/passwd") - if err == nil { - passwdContent := string(passwdData) - - if configData, cfgErr := os.ReadFile("/etc/greetd/config.toml"); cfgErr == nil { - if configured := extractDefaultSessionUser(string(configData)); configured != "" && hasPasswdUser(passwdContent, configured) { - return configured - } - } - - if user, found := findPasswdUser(passwdContent, "greeter", "greetd", "_greeter"); found { - return user - } - } else { - fmt.Fprintln(os.Stderr, "⚠ Warning: could not read /etc/passwd, defaulting greeter user to 'greeter'") - } - - if configData, cfgErr := os.ReadFile("/etc/greetd/config.toml"); cfgErr == nil { - if configured := extractDefaultSessionUser(string(configData)); configured != "" { - return configured - } - } - - fmt.Fprintln(os.Stderr, "⚠ Warning: no greeter user found, defaulting to 'greeter'") - return "greeter" -} - -func resolveGreeterWrapperPath() string { - if override := strings.TrimSpace(os.Getenv("DMS_GREETER_WRAPPER_CMD")); override != "" { - return override - } - - // Packaged installs only use the official wrapper; never fall back to /usr/local/bin. - if IsGreeterPackaged() { - packagedWrapper := "/usr/bin/dms-greeter" - if info, err := os.Stat(packagedWrapper); err == nil && !info.IsDir() && (info.Mode()&0o111) != 0 { - return packagedWrapper - } - fmt.Fprintln(os.Stderr, "⚠ Warning: packaged dms-greeter detected, but /usr/bin/dms-greeter is missing or not executable") - return packagedWrapper - } - - for _, candidate := range []string{"/usr/bin/dms-greeter", "/usr/local/bin/dms-greeter"} { - if info, err := os.Stat(candidate); err == nil && !info.IsDir() && (info.Mode()&0o111) != 0 { - return candidate - } - } - - if path, err := exec.LookPath("dms-greeter"); err == nil { - resolved := path - if realPath, realErr := filepath.EvalSymlinks(path); realErr == nil { - resolved = realPath - } - if strings.HasPrefix(resolved, "/home/") || strings.HasPrefix(resolved, "/tmp/") { - fmt.Fprintf(os.Stderr, "⚠ Warning: ignoring non-system dms-greeter on PATH: %s\n", path) - } else { - return path - } - } - - return "/usr/bin/dms-greeter" -} - -func DetectCompositors() []string { - var compositors []string - - if utils.CommandExists("niri") { - compositors = append(compositors, "niri") - } - if utils.CommandExists("Hyprland") { - compositors = append(compositors, "Hyprland") - } - - return compositors -} - -func PromptCompositorChoice(compositors []string) (string, error) { - fmt.Println("\nMultiple compositors detected:") - for i, comp := range compositors { - fmt.Printf("%d) %s\n", i+1, comp) - } - - reader := bufio.NewReader(os.Stdin) - fmt.Print("Choose compositor for greeter (1-2): ") - response, err := reader.ReadString('\n') - if err != nil { - return "", fmt.Errorf("error reading input: %w", err) - } - - response = strings.TrimSpace(response) - switch response { - case "1": - return compositors[0], nil - case "2": - if len(compositors) > 1 { - return compositors[1], nil - } - return "", fmt.Errorf("invalid choice") - default: - return "", fmt.Errorf("invalid choice") - } -} - -// EnsureGreetdInstalled checks if greetd is installed - greetd is a daemon in /usr/sbin on Debian/Ubuntu -func EnsureGreetdInstalled(logFunc func(string), sudoPassword string) error { - greetdFound := utils.CommandExists("greetd") - if !greetdFound { - for _, p := range []string{"/usr/sbin/greetd", "/sbin/greetd"} { - if _, err := os.Stat(p); err == nil { - greetdFound = true - break - } - } - } - if greetdFound { - logFunc("✓ greetd is already installed") - return nil - } - - logFunc("greetd is not installed. Installing...") - - osInfo, err := distros.GetOSInfo() - if err != nil { - return fmt.Errorf("failed to detect OS: %w", err) - } - - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - return fmt.Errorf("unsupported distribution for automatic greetd installation: %s", osInfo.Distribution.ID) - } - - ctx := context.Background() - var installCmd *exec.Cmd - - switch config.Family { - case distros.FamilyArch: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, - "pacman -S --needed --noconfirm greetd") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "pacman", "-S", "--needed", "--noconfirm", "greetd") - } - - case distros.FamilyFedora: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, - "dnf install -y greetd") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "dnf", "install", "-y", "greetd") - } - - case distros.FamilySUSE: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, - "zypper install -y greetd") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "greetd") - } - - case distros.FamilyUbuntu: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, - "apt-get install -y greetd") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "greetd") - } - - case distros.FamilyDebian: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, - "apt-get install -y greetd") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "greetd") - } - - case distros.FamilyGentoo: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, - "emerge --ask n sys-apps/greetd") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "emerge", "--ask", "n", "sys-apps/greetd") - } - - case distros.FamilyNix: - return fmt.Errorf("on NixOS, please add greetd to your configuration.nix") - - default: - return fmt.Errorf("unsupported distribution family for automatic greetd installation: %s", config.Family) - } - - installCmd.Stdout = os.Stdout - installCmd.Stderr = os.Stderr - - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install greetd: %w", err) - } - - logFunc("✓ greetd installed successfully") - return nil -} - -// IsGreeterPackaged returns true if dms-greeter was installed from a system package. -func IsGreeterPackaged() bool { - if !utils.CommandExists("dms-greeter") { - return false - } - packagedPath := "/usr/share/quickshell/dms-greeter" - info, err := os.Stat(packagedPath) - return err == nil && info.IsDir() -} - -// HasLegacyLocalGreeterWrapper returns true when a manually installed wrapper exists. -func HasLegacyLocalGreeterWrapper() bool { - info, err := os.Stat("/usr/local/bin/dms-greeter") - return err == nil && !info.IsDir() -} - -// TryInstallGreeterPackage attempts to install dms-greeter from the distro's official repo. -func TryInstallGreeterPackage(logFunc func(string), sudoPassword string) bool { - osInfo, err := distros.GetOSInfo() - if err != nil { - return false - } - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - return false - } - - if IsGreeterPackaged() { - logFunc("✓ dms-greeter package already installed") - return true - } - - ctx := context.Background() - var installCmd *exec.Cmd - var failHint string - - switch config.Family { - case distros.FamilyDebian: - obsSlug := getDebianOBSSlug(osInfo) - keyURL := fmt.Sprintf("https://download.opensuse.org/repositories/home:AvengeMedia:danklinux/%s/Release.key", obsSlug) - repoLine := fmt.Sprintf("deb [signed-by=/etc/apt/keyrings/danklinux.gpg] https://download.opensuse.org/repositories/home:/AvengeMedia:/danklinux/%s/ /", obsSlug) - failHint = fmt.Sprintf("⚠ dms-greeter install failed. Add OBS repo manually:\nsudo apt-get install -y gnupg\nsudo mkdir -p /etc/apt/keyrings\ncurl -fsSL %s | sudo gpg --dearmor -o /etc/apt/keyrings/danklinux.gpg\necho '%s' | sudo tee /etc/apt/sources.list.d/danklinux.list\nsudo apt update && sudo apt-get install -y dms-greeter", keyURL, repoLine) - logFunc(fmt.Sprintf("Adding DankLinux OBS repository (%s)...", obsSlug)) - if _, err := exec.LookPath("gpg"); err != nil { - logFunc("Installing gnupg for OBS repository key import...") - installGPGCmd := exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "gnupg") - installGPGCmd.Stdout = os.Stdout - installGPGCmd.Stderr = os.Stderr - if err := installGPGCmd.Run(); err != nil { - logFunc(fmt.Sprintf("⚠ Failed to install gnupg: %v", err)) - } - } - mkdirCmd := exec.CommandContext(ctx, "sudo", "mkdir", "-p", "/etc/apt/keyrings") - mkdirCmd.Stdout = os.Stdout - mkdirCmd.Stderr = os.Stderr - mkdirCmd.Run() - addKeyCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf(`curl -fsSL %s | sudo gpg --dearmor -o /etc/apt/keyrings/danklinux.gpg`, keyURL)) - addKeyCmd.Stdout = os.Stdout - addKeyCmd.Stderr = os.Stderr - addKeyCmd.Run() - addRepoCmd := exec.CommandContext(ctx, "bash", "-c", - fmt.Sprintf(`echo '%s' | sudo tee /etc/apt/sources.list.d/danklinux.list`, repoLine)) - addRepoCmd.Stdout = os.Stdout - addRepoCmd.Stderr = os.Stderr - addRepoCmd.Run() - exec.CommandContext(ctx, "sudo", "apt-get", "update").Run() - installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "dms-greeter") - case distros.FamilySUSE: - repoURL := getOpenSUSEOBSRepoURL(osInfo) - failHint = fmt.Sprintf("⚠ dms-greeter install failed. Add OBS repo manually:\nsudo zypper addrepo %s\nsudo zypper refresh && sudo zypper install dms-greeter", repoURL) - logFunc("Adding DankLinux OBS repository...") - addRepoCmd := exec.CommandContext(ctx, "sudo", "zypper", "addrepo", repoURL) - addRepoCmd.Stdout = os.Stdout - addRepoCmd.Stderr = os.Stderr - addRepoCmd.Run() - exec.CommandContext(ctx, "sudo", "zypper", "refresh").Run() - installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "dms-greeter") - case distros.FamilyUbuntu: - failHint = "⚠ dms-greeter install failed. Add PPA manually: sudo add-apt-repository ppa:avengemedia/danklinux && sudo apt-get update && sudo apt-get install -y dms-greeter" - logFunc("Enabling PPA ppa:avengemedia/danklinux...") - ppacmd := exec.CommandContext(ctx, "sudo", "add-apt-repository", "-y", "ppa:avengemedia/danklinux") - ppacmd.Stdout = os.Stdout - ppacmd.Stderr = os.Stderr - ppacmd.Run() - exec.CommandContext(ctx, "sudo", "apt-get", "update").Run() - installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "dms-greeter") - case distros.FamilyFedora: - failHint = "⚠ dms-greeter install failed. Enable COPR manually: sudo dnf copr enable avengemedia/danklinux && sudo dnf install dms-greeter" - logFunc("Enabling COPR avengemedia/danklinux...") - coprcmd := exec.CommandContext(ctx, "sudo", "dnf", "copr", "enable", "-y", "avengemedia/danklinux") - coprcmd.Stdout = os.Stdout - coprcmd.Stderr = os.Stderr - coprcmd.Run() - installCmd = exec.CommandContext(ctx, "sudo", "dnf", "install", "-y", "dms-greeter") - case distros.FamilyArch: - aurHelper := "" - for _, helper := range []string{"paru", "yay"} { - if _, err := exec.LookPath(helper); err == nil { - aurHelper = helper - break - } - } - if aurHelper == "" { - logFunc("⚠ No AUR helper found (paru/yay). Install greetd-dms-greeter-git from AUR: https://aur.archlinux.org/packages/greetd-dms-greeter-git") - return false - } - failHint = fmt.Sprintf("⚠ dms-greeter install failed. Install from AUR: %s -S greetd-dms-greeter-git", aurHelper) - installCmd = exec.CommandContext(ctx, aurHelper, "-S", "--noconfirm", "greetd-dms-greeter-git") - default: - return false - } - - logFunc("Installing dms-greeter from official repository...") - installCmd.Stdout = os.Stdout - installCmd.Stderr = os.Stderr - - if err := installCmd.Run(); err != nil { - logFunc(failHint) - return false - } - - logFunc("✓ dms-greeter package installed") - return true -} - -// CopyGreeterFiles installs the dms-greeter wrapper and sets up cache directory -func CopyGreeterFiles(dmsPath, compositor string, logFunc func(string), sudoPassword string) error { - if IsGreeterPackaged() { - logFunc("✓ dms-greeter package already installed") - } else { - if dmsPath == "" { - return fmt.Errorf("dms path is required for manual dms-greeter wrapper installs") - } - - assetsDir := filepath.Join(dmsPath, "Modules", "Greetd", "assets") - wrapperSrc := filepath.Join(assetsDir, "dms-greeter") - - if _, err := os.Stat(wrapperSrc); os.IsNotExist(err) { - return fmt.Errorf("dms-greeter wrapper not found at %s", wrapperSrc) - } - - wrapperDst := "/usr/local/bin/dms-greeter" - action := "Installed" - if info, err := os.Stat(wrapperDst); err == nil && !info.IsDir() { - action = "Updated" - } - if err := runSudoCmd(sudoPassword, "cp", wrapperSrc, wrapperDst); err != nil { - return fmt.Errorf("failed to copy dms-greeter wrapper: %w", err) - } - logFunc(fmt.Sprintf("✓ %s dms-greeter wrapper at %s", action, wrapperDst)) - - if err := runSudoCmd(sudoPassword, "chmod", "+x", wrapperDst); err != nil { - return fmt.Errorf("failed to make wrapper executable: %w", err) - } - - osInfo, err := distros.GetOSInfo() - if err == nil { - if config, exists := distros.Registry[osInfo.Distribution.ID]; exists && (config.Family == distros.FamilyFedora || config.Family == distros.FamilySUSE) { - if err := runSudoCmd(sudoPassword, "semanage", "fcontext", "-a", "-t", "bin_t", wrapperDst); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to set SELinux fcontext: %v", err)) - } else { - logFunc("✓ Set SELinux fcontext for dms-greeter") - } - - if err := runSudoCmd(sudoPassword, "restorecon", "-v", wrapperDst); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to restore SELinux context: %v", err)) - } else { - logFunc("✓ Restored SELinux context for dms-greeter") - } - } - } - } - - if err := EnsureGreeterCacheDir(logFunc, sudoPassword); err != nil { - return err - } - - return nil -} - -// EnsureGreeterCacheDir creates /var/cache/dms-greeter with correct ownership if it does not exist. -// It is safe to call multiple times (idempotent) and will repair ownership/mode -// when the directory already exists with stale permissions. -func EnsureGreeterCacheDir(logFunc func(string), sudoPassword string) error { - cacheDir := GreeterCacheDir - created := false - if info, err := os.Stat(cacheDir); err != nil { - if !os.IsNotExist(err) { - return fmt.Errorf("failed to stat cache directory: %w", err) - } - if err := runSudoCmd(sudoPassword, "mkdir", "-p", cacheDir); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - created = true - } else if !info.IsDir() { - return fmt.Errorf("cache path exists but is not a directory: %s", cacheDir) - } - - group := DetectGreeterGroup() - daemonUser := DetectGreeterUser() - preferredOwner := fmt.Sprintf("%s:%s", daemonUser, group) - owner := preferredOwner - if err := runSudoCmd(sudoPassword, "chown", owner, cacheDir); err != nil { - // Some setups may not have a matching daemon user at this moment; fall back - // to root: while still allowing group-writable greeter runtime access. - fallbackOwner := fmt.Sprintf("root:%s", group) - if fallbackErr := runSudoCmd(sudoPassword, "chown", fallbackOwner, cacheDir); fallbackErr != nil { - return fmt.Errorf("failed to set cache directory owner (preferred %s: %v; fallback %s: %w)", preferredOwner, err, fallbackOwner, fallbackErr) - } - owner = fallbackOwner - } - - if err := runSudoCmd(sudoPassword, "chmod", "2770", cacheDir); err != nil { - return fmt.Errorf("failed to set cache directory permissions: %w", err) - } - - runtimeDirs := []string{ - filepath.Join(cacheDir, ".local"), - filepath.Join(cacheDir, ".local", "state"), - filepath.Join(cacheDir, ".local", "share"), - filepath.Join(cacheDir, ".cache"), - } - for _, dir := range runtimeDirs { - if err := runSudoCmd(sudoPassword, "mkdir", "-p", dir); err != nil { - return fmt.Errorf("failed to create cache runtime directory %s: %w", dir, err) - } - if err := runSudoCmd(sudoPassword, "chown", owner, dir); err != nil { - return fmt.Errorf("failed to set owner for cache runtime directory %s: %w", dir, err) - } - if err := runSudoCmd(sudoPassword, "chmod", "2770", dir); err != nil { - return fmt.Errorf("failed to set permissions for cache runtime directory %s: %w", dir, err) - } - } - - legacyMemoryPath := filepath.Join(cacheDir, "memory.json") - stateMemoryPath := filepath.Join(cacheDir, ".local", "state", "memory.json") - if err := ensureGreeterMemoryCompatLink(logFunc, sudoPassword, legacyMemoryPath, stateMemoryPath); err != nil { - return err - } - - if isSELinuxEnforcing() && utils.CommandExists("restorecon") { - if err := runSudoCmd(sudoPassword, "restorecon", "-Rv", cacheDir); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to restore SELinux context for %s: %v", cacheDir, err)) - } - } - - if created { - logFunc(fmt.Sprintf("✓ Created cache directory %s (owner: %s, mode: 2770)", cacheDir, owner)) - } else { - logFunc(fmt.Sprintf("✓ Ensured cache directory %s permissions (owner: %s, mode: 2770)", cacheDir, owner)) - } - return nil -} - -func isSELinuxEnforcing() bool { - data, err := os.ReadFile("/sys/fs/selinux/enforce") - if err != nil { - return false - } - return strings.TrimSpace(string(data)) == "1" -} - -func ensureGreeterMemoryCompatLink(logFunc func(string), sudoPassword, legacyPath, statePath string) error { - info, err := os.Lstat(legacyPath) - if err == nil && info.Mode().IsRegular() { - if _, stateErr := os.Stat(statePath); os.IsNotExist(stateErr) { - if copyErr := runSudoCmd(sudoPassword, "cp", "-f", legacyPath, statePath); copyErr != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to migrate legacy greeter memory file to %s: %v", statePath, copyErr)) - } - } - } - - if err := runSudoCmd(sudoPassword, "ln", "-sfn", statePath, legacyPath); err != nil { - return fmt.Errorf("failed to create greeter memory compatibility symlink %s -> %s: %w", legacyPath, statePath, err) - } - - return nil -} - -// IsAppArmorEnabled reports whether AppArmor is active on the running kernel. -func IsAppArmorEnabled() bool { - data, err := os.ReadFile("/sys/module/apparmor/parameters/enabled") - if err != nil { - return false - } - return strings.HasPrefix(strings.TrimSpace(strings.ToLower(string(data))), "y") -} - -// InstallAppArmorProfile installs the bundled AppArmor profile and reloads it. No-op on NixOS or non-AppArmor systems. -func InstallAppArmorProfile(logFunc func(string), sudoPassword string) error { - if IsNixOS() { - logFunc(" ℹ Skipping AppArmor profile on NixOS (manage via security.apparmor.policies)") - return nil - } - - if !IsAppArmorEnabled() { - return nil - } - - if err := runSudoCmd(sudoPassword, "mkdir", "-p", "/etc/apparmor.d"); err != nil { - return fmt.Errorf("failed to create /etc/apparmor.d: %w", err) - } - - tmp, err := os.CreateTemp("", "dms-apparmor-*") - if err != nil { - return fmt.Errorf("failed to create temp file for AppArmor profile: %w", err) - } - tmpPath := tmp.Name() - defer os.Remove(tmpPath) - - if _, err := tmp.Write(appArmorProfileData); err != nil { - tmp.Close() - return fmt.Errorf("failed to write AppArmor profile: %w", err) - } - tmp.Close() - - if err := runSudoCmd(sudoPassword, "cp", tmpPath, appArmorProfileDest); err != nil { - return fmt.Errorf("failed to install AppArmor profile to %s: %w", appArmorProfileDest, err) - } - if err := runSudoCmd(sudoPassword, "chmod", "644", appArmorProfileDest); err != nil { - return fmt.Errorf("failed to set AppArmor profile permissions: %w", err) - } - - if utils.CommandExists("apparmor_parser") { - if err := runSudoCmd(sudoPassword, "apparmor_parser", "-r", appArmorProfileDest); err != nil { - logFunc(fmt.Sprintf(" ⚠ AppArmor profile installed but reload failed: %v", err)) - logFunc(" Run: sudo apparmor_parser -r " + appArmorProfileDest) - } else { - logFunc(" ✓ AppArmor profile installed and loaded (complain mode)") - } - } else { - logFunc(" ✓ AppArmor profile installed at " + appArmorProfileDest) - logFunc(" apparmor_parser not found — profile will be loaded on next boot") - } - - return nil -} - -// UninstallAppArmorProfile removes the DMS AppArmor profile and reloads AppArmor. -// It is a no-op when AppArmor is not active or the profile does not exist. -func UninstallAppArmorProfile(logFunc func(string), sudoPassword string) error { - if IsNixOS() { - return nil - } - if _, err := os.Stat("/sys/module/apparmor"); os.IsNotExist(err) { - return nil - } - if _, err := os.Stat(appArmorProfileDest); os.IsNotExist(err) { - return nil - } - - if utils.CommandExists("apparmor_parser") { - _ = runSudoCmd(sudoPassword, "apparmor_parser", "--remove", appArmorProfileDest) - } - if err := runSudoCmd(sudoPassword, "rm", "-f", appArmorProfileDest); err != nil { - return fmt.Errorf("failed to remove AppArmor profile: %w", err) - } - logFunc(" ✓ Removed DMS AppArmor profile") - return nil -} - -// EnsureACLInstalled installs the acl package (setfacl/getfacl) if not already present -func EnsureACLInstalled(logFunc func(string), sudoPassword string) error { - if utils.CommandExists("setfacl") { - return nil - } - - logFunc("setfacl not found – installing acl package...") - - osInfo, err := distros.GetOSInfo() - if err != nil { - return fmt.Errorf("failed to detect OS: %w", err) - } - - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - return fmt.Errorf("unsupported distribution for automatic acl installation: %s", osInfo.Distribution.ID) - } - - ctx := context.Background() - var installCmd *exec.Cmd - - switch config.Family { - case distros.FamilyArch: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "pacman -S --needed --noconfirm acl") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "pacman", "-S", "--needed", "--noconfirm", "acl") - } - - case distros.FamilyFedora: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "dnf install -y acl") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "dnf", "install", "-y", "acl") - } - - case distros.FamilySUSE: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "zypper install -y acl") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "zypper", "install", "-y", "acl") - } - - case distros.FamilyUbuntu: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "apt-get install -y acl") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "acl") - } - - case distros.FamilyDebian: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "apt-get install -y acl") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "apt-get", "install", "-y", "acl") - } - - case distros.FamilyGentoo: - if sudoPassword != "" { - installCmd = distros.ExecSudoCommand(ctx, sudoPassword, "emerge --ask n sys-fs/acl") - } else { - installCmd = exec.CommandContext(ctx, "sudo", "emerge", "--ask", "n", "sys-fs/acl") - } - - case distros.FamilyNix: - return fmt.Errorf("on NixOS, please add pkgs.acl to your configuration.nix") - - default: - return fmt.Errorf("unsupported distribution family for automatic acl installation: %s", config.Family) - } - - installCmd.Stdout = os.Stdout - installCmd.Stderr = os.Stderr - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install acl: %w", err) - } - - logFunc("✓ acl package installed") - return nil -} - -// SetupParentDirectoryACLs sets ACLs on parent directories to allow traversal -func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error { - if err := EnsureACLInstalled(logFunc, sudoPassword); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: could not install acl package: %v", err)) - logFunc(" ACL permissions will be skipped; theme sync may not work correctly.") - return nil - } - if !utils.CommandExists("setfacl") { - logFunc("⚠ Warning: setfacl still not available after install attempt; skipping ACL setup.") - return nil - } - - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - parentDirs := []struct { - path string - desc string - }{ - {homeDir, "home directory"}, - {filepath.Join(homeDir, ".config"), ".config directory"}, - {filepath.Join(homeDir, ".local"), ".local directory"}, - {filepath.Join(homeDir, ".cache"), ".cache directory"}, - {filepath.Join(homeDir, ".local", "state"), ".local/state directory"}, - {filepath.Join(homeDir, ".local", "share"), ".local/share directory"}, - } - - group := DetectGreeterGroup() - - logFunc("\nSetting up parent directory ACLs for greeter user access...") - - for _, dir := range parentDirs { - if _, err := os.Stat(dir.path); os.IsNotExist(err) { - if err := os.MkdirAll(dir.path, 0o755); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Could not create %s: %v", dir.desc, err)) - continue - } - } - - // Group ACL covers daemon users regardless of username (e.g. greetd ≠ greeter on Fedora). - if err := runSudoCmd(sudoPassword, "setfacl", "-m", fmt.Sprintf("g:%s:rX", group), dir.path); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to set ACL on %s: %v", dir.desc, err)) - logFunc(fmt.Sprintf(" You may need to run manually: setfacl -m g:%s:rX %s", group, dir.path)) - continue - } - - logFunc(fmt.Sprintf("✓ Set ACL on %s", dir.desc)) - } - - return nil -} - -// RemediateStaleACLs removes user-based ACLs left by older binary versions. Best-effort. -func RemediateStaleACLs(logFunc func(string), sudoPassword string) { - if !utils.CommandExists("setfacl") { - return - } - - homeDir, err := os.UserHomeDir() - if err != nil { - return - } - - passwdData, err := os.ReadFile("/etc/passwd") - if err != nil { - return - } - - dirs := []string{ - homeDir, - filepath.Join(homeDir, ".config"), - filepath.Join(homeDir, ".config", "DankMaterialShell"), - filepath.Join(homeDir, ".cache"), - filepath.Join(homeDir, ".cache", "DankMaterialShell"), - filepath.Join(homeDir, ".local"), - filepath.Join(homeDir, ".local", "state"), - filepath.Join(homeDir, ".local", "share"), - } - - passwdContent := string(passwdData) - staleUsers := []string{"greeter", "greetd", "_greeter"} - existingUsers := make([]string, 0, len(staleUsers)) - for _, user := range staleUsers { - if hasPasswdUser(passwdContent, user) { - existingUsers = append(existingUsers, user) - } - } - if len(existingUsers) == 0 { - return - } - - cleaned := false - for _, dir := range dirs { - if _, err := os.Stat(dir); err != nil { - continue - } - for _, user := range existingUsers { - _ = runSudoCmd(sudoPassword, "setfacl", "-x", fmt.Sprintf("u:%s", user), dir) - cleaned = true - } - } - if cleaned { - logFunc("✓ Cleaned up stale user-based ACLs from previous versions") - } -} - -// RemediateStaleAppArmor removes any AppArmor profile installed by an older binary on -// systems where AppArmor is not active. -func RemediateStaleAppArmor(logFunc func(string), sudoPassword string) { - if IsAppArmorEnabled() { - return - } - if _, err := os.Stat(appArmorProfileDest); os.IsNotExist(err) { - return - } - logFunc("ℹ Removing stale AppArmor profile (AppArmor is not active on this system)") - _ = UninstallAppArmorProfile(logFunc, sudoPassword) -} - -func SetupDMSGroup(logFunc func(string), sudoPassword string) error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - currentUser := os.Getenv("USER") - if currentUser == "" { - currentUser = os.Getenv("LOGNAME") - } - if currentUser == "" { - return fmt.Errorf("failed to determine current user") - } - - group := DetectGreeterGroup() - - // Create the group if it doesn't exist yet (e.g. before greetd package is installed). - if !utils.HasGroup(group) { - if err := runSudoCmd(sudoPassword, "groupadd", "-r", group); err != nil { - return fmt.Errorf("failed to create %s group: %w", group, err) - } - logFunc(fmt.Sprintf("✓ Created system group %s", group)) - } - - groupsCmd := exec.Command("groups", currentUser) - groupsOutput, err := groupsCmd.Output() - if err == nil && strings.Contains(string(groupsOutput), group) { - logFunc(fmt.Sprintf("✓ %s is already in %s group", currentUser, group)) - } else { - if err := runSudoCmd(sudoPassword, "usermod", "-aG", group, currentUser); err != nil { - return fmt.Errorf("failed to add %s to %s group: %w", currentUser, group, err) - } - logFunc(fmt.Sprintf("✓ Added %s to %s group (logout/login required for changes to take effect)", currentUser, group)) - } - - // Also add the daemon user (e.g. greetd on Fedora) so group ACLs apply to the running process. - daemonUser := DetectGreeterUser() - if daemonUser != currentUser { - daemonGroupsCmd := exec.Command("groups", daemonUser) - daemonGroupsOutput, daemonGroupsErr := daemonGroupsCmd.Output() - if daemonGroupsErr == nil { - if strings.Contains(string(daemonGroupsOutput), group) { - logFunc(fmt.Sprintf("✓ Greeter daemon user %s is already in %s group", daemonUser, group)) - } else { - if err := runSudoCmd(sudoPassword, "usermod", "-aG", group, daemonUser); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: could not add %s to %s group: %v", daemonUser, group, err)) - } else { - logFunc(fmt.Sprintf("✓ Added greeter daemon user %s to %s group", daemonUser, group)) - } - } - } - } - - configDirs := []struct { - path string - desc string - }{ - {filepath.Join(homeDir, ".config", "DankMaterialShell"), "DankMaterialShell config"}, - {filepath.Join(homeDir, ".local", "state", "DankMaterialShell"), "DankMaterialShell state"}, - {filepath.Join(homeDir, ".cache", "DankMaterialShell"), "DankMaterialShell cache"}, - {filepath.Join(homeDir, ".cache", "quickshell"), "quickshell cache"}, - {filepath.Join(homeDir, ".config", "quickshell"), "quickshell config"}, - {filepath.Join(homeDir, ".local", "share", "wayland-sessions"), "wayland sessions"}, - {filepath.Join(homeDir, ".local", "share", "xsessions"), "xsessions"}, - } - - for _, dir := range configDirs { - if _, err := os.Stat(dir.path); os.IsNotExist(err) { - if err := os.MkdirAll(dir.path, 0o755); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Could not create %s: %v", dir.path, err)) - continue - } - } - - if err := runSudoCmd(sudoPassword, "chgrp", "-R", group, dir.path); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to set group for %s: %v", dir.desc, err)) - continue - } - - if err := runSudoCmd(sudoPassword, "chmod", "-R", "g+rX", dir.path); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to set permissions for %s: %v", dir.desc, err)) - continue - } - - logFunc(fmt.Sprintf("✓ Set group permissions for %s", dir.desc)) - } - - if err := SetupParentDirectoryACLs(logFunc, sudoPassword); err != nil { - return fmt.Errorf("failed to setup parent directory ACLs: %w", err) - } - - return nil -} - -type GreeterColorSyncInfo struct { - SourcePath string - ThemeName string - UsesDynamicWallpaperOverride bool -} - -type greeterThemeSyncSettings struct { - CurrentThemeName string `json:"currentThemeName"` - GreeterWallpaperPath string `json:"greeterWallpaperPath"` - MatugenScheme string `json:"matugenScheme"` - IconTheme string `json:"iconTheme"` -} - -type greeterThemeSyncSession struct { - IsLightMode bool `json:"isLightMode"` -} - -type greeterThemeSyncState struct { - ThemeName string - GreeterWallpaperPath string - ResolvedGreeterWallpaperPath string - MatugenScheme string - IconTheme string - IsLightMode bool - UsesDynamicWallpaperOverride bool -} - -func defaultGreeterColorsSource(homeDir string) string { - return filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json") -} - -func greeterOverrideColorsStateDir(homeDir string) string { - return filepath.Join(homeDir, ".cache", "DankMaterialShell", "greeter-colors") -} - -func greeterOverrideColorsSource(homeDir string) string { - return filepath.Join(greeterOverrideColorsStateDir(homeDir), "dms-colors.json") -} - -func readOptionalJSONFile(path string, dst any) error { - data, err := os.ReadFile(path) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - if strings.TrimSpace(string(data)) == "" { - return nil - } - return json.Unmarshal(data, dst) -} - -func readGreeterThemeSyncSettings(homeDir string) (greeterThemeSyncSettings, error) { - settings := greeterThemeSyncSettings{ - CurrentThemeName: "purple", - MatugenScheme: "scheme-tonal-spot", - IconTheme: "System Default", - } - settingsPath := filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json") - if err := readOptionalJSONFile(settingsPath, &settings); err != nil { - return greeterThemeSyncSettings{}, fmt.Errorf("failed to parse settings at %s: %w", settingsPath, err) - } - return settings, nil -} - -func readGreeterThemeSyncSession(homeDir string) (greeterThemeSyncSession, error) { - session := greeterThemeSyncSession{} - sessionPath := filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json") - if err := readOptionalJSONFile(sessionPath, &session); err != nil { - return greeterThemeSyncSession{}, fmt.Errorf("failed to parse session at %s: %w", sessionPath, err) - } - return session, nil -} - -func resolveGreeterThemeSyncState(homeDir string) (greeterThemeSyncState, error) { - settings, err := readGreeterThemeSyncSettings(homeDir) - if err != nil { - return greeterThemeSyncState{}, err - } - session, err := readGreeterThemeSyncSession(homeDir) - if err != nil { - return greeterThemeSyncState{}, err - } - - resolvedWallpaperPath := "" - if settings.GreeterWallpaperPath != "" { - resolvedWallpaperPath = settings.GreeterWallpaperPath - if !filepath.IsAbs(resolvedWallpaperPath) { - resolvedWallpaperPath = filepath.Join(homeDir, resolvedWallpaperPath) - } - } - - usesDynamicWallpaperOverride := strings.EqualFold(strings.TrimSpace(settings.CurrentThemeName), "dynamic") && resolvedWallpaperPath != "" - - return greeterThemeSyncState{ - ThemeName: settings.CurrentThemeName, - GreeterWallpaperPath: settings.GreeterWallpaperPath, - ResolvedGreeterWallpaperPath: resolvedWallpaperPath, - MatugenScheme: settings.MatugenScheme, - IconTheme: settings.IconTheme, - IsLightMode: session.IsLightMode, - UsesDynamicWallpaperOverride: usesDynamicWallpaperOverride, - }, nil -} - -func (s greeterThemeSyncState) effectiveColorsSource(homeDir string) string { - if s.UsesDynamicWallpaperOverride { - return greeterOverrideColorsSource(homeDir) - } - return defaultGreeterColorsSource(homeDir) -} - -func ResolveGreeterColorSyncInfo(homeDir string) (GreeterColorSyncInfo, error) { - state, err := resolveGreeterThemeSyncState(homeDir) - if err != nil { - return GreeterColorSyncInfo{}, err - } - return GreeterColorSyncInfo{ - SourcePath: state.effectiveColorsSource(homeDir), - ThemeName: state.ThemeName, - UsesDynamicWallpaperOverride: state.UsesDynamicWallpaperOverride, - }, nil -} - -func ensureGreeterSyncSourceFile(path string) error { - sourceDir := filepath.Dir(path) - if err := os.MkdirAll(sourceDir, 0o755); err != nil { - return fmt.Errorf("failed to create source directory %s: %w", sourceDir, err) - } - - if _, err := os.Stat(path); os.IsNotExist(err) { - if err := os.WriteFile(path, []byte("{}"), 0o644); err != nil { - return fmt.Errorf("failed to create source file %s: %w", path, err) - } - } else if err != nil { - return fmt.Errorf("failed to inspect source file %s: %w", path, err) - } - - return nil -} - -func syncGreeterDynamicOverrideColors(dmsPath, homeDir string, state greeterThemeSyncState, logFunc func(string)) error { - if !state.UsesDynamicWallpaperOverride { - return nil - } - - st, err := os.Stat(state.ResolvedGreeterWallpaperPath) - if err != nil { - return fmt.Errorf("configured greeter wallpaper not found at %s: %w", state.ResolvedGreeterWallpaperPath, err) - } - if st.IsDir() { - return fmt.Errorf("configured greeter wallpaper path points to a directory: %s", state.ResolvedGreeterWallpaperPath) - } - - mode := matugen.ColorModeDark - if state.IsLightMode { - mode = matugen.ColorModeLight - } - - opts := matugen.Options{ - StateDir: greeterOverrideColorsStateDir(homeDir), - ShellDir: dmsPath, - ConfigDir: filepath.Join(homeDir, ".config"), - Kind: "image", - Value: state.ResolvedGreeterWallpaperPath, - Mode: mode, - IconTheme: state.IconTheme, - MatugenType: state.MatugenScheme, - RunUserTemplates: false, - ColorsOnly: true, - } - - err = matugen.Run(opts) - switch { - case errors.Is(err, matugen.ErrNoChanges): - logFunc("✓ Greeter dynamic override colors already up to date") - return nil - case err != nil: - return fmt.Errorf("failed to generate greeter dynamic colors from wallpaper override: %w", err) - default: - logFunc("✓ Generated greeter dynamic colors from wallpaper override") - return nil - } -} - -func syncGreeterColorSource(homeDir, cacheDir string, state greeterThemeSyncState, logFunc func(string), sudoPassword string) error { - source := state.effectiveColorsSource(homeDir) - if !state.UsesDynamicWallpaperOverride { - if err := ensureGreeterSyncSourceFile(source); err != nil { - return err - } - } else if _, err := os.Stat(source); err != nil { - return fmt.Errorf("expected generated greeter colors at %s: %w", source, err) - } - - target := filepath.Join(cacheDir, "colors.json") - _ = runSudoCmd(sudoPassword, "rm", "-f", target) - if err := runSudoCmd(sudoPassword, "ln", "-sf", source, target); err != nil { - return fmt.Errorf("failed to create symlink for wallpaper based theming (%s -> %s): %w", target, source, err) - } - - if state.UsesDynamicWallpaperOverride { - logFunc("✓ Synced wallpaper based theming (greeter wallpaper override)") - } else { - logFunc("✓ Synced wallpaper based theming") - } - - return nil -} - -func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPassword string) error { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - cacheDir := GreeterCacheDir - - symlinks := []struct { - source string - target string - desc string - }{ - { - source: filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json"), - target: filepath.Join(cacheDir, "settings.json"), - desc: "core settings (theme, clock formats, etc)", - }, - { - source: filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json"), - target: filepath.Join(cacheDir, "session.json"), - desc: "state (wallpaper configuration)", - }, - } - - for _, link := range symlinks { - sourceDir := filepath.Dir(link.source) - if _, err := os.Stat(sourceDir); os.IsNotExist(err) { - if err := os.MkdirAll(sourceDir, 0o755); err != nil { - return fmt.Errorf("failed to create source directory %s for %s: %w", sourceDir, link.desc, err) - } - } - - if _, err := os.Stat(link.source); os.IsNotExist(err) { - if err := os.WriteFile(link.source, []byte("{}"), 0o644); err != nil { - return fmt.Errorf("failed to create source file %s for %s: %w", link.source, link.desc, err) - } - } - - _ = runSudoCmd(sudoPassword, "rm", "-f", link.target) - - if err := runSudoCmd(sudoPassword, "ln", "-sf", link.source, link.target); err != nil { - return fmt.Errorf("failed to create symlink for %s (%s -> %s): %w", link.desc, link.target, link.source, err) - } - - logFunc(fmt.Sprintf("✓ Synced %s", link.desc)) - } - - state, err := resolveGreeterThemeSyncState(homeDir) - if err != nil { - return fmt.Errorf("failed to resolve greeter color source: %w", err) - } - - if err := syncGreeterDynamicOverrideColors(dmsPath, homeDir, state, logFunc); err != nil { - return err - } - - if err := syncGreeterColorSource(homeDir, cacheDir, state, logFunc, sudoPassword); err != nil { - return err - } - - if err := syncGreeterWallpaperOverride(cacheDir, logFunc, sudoPassword, state); err != nil { - return fmt.Errorf("greeter wallpaper override sync failed: %w", err) - } - - if strings.ToLower(compositor) != "niri" { - return nil - } - - if err := syncNiriGreeterConfig(logFunc, sudoPassword); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to sync niri greeter config: %v", err)) - } - - return nil -} - -func syncGreeterWallpaperOverride(cacheDir string, logFunc func(string), sudoPassword string, state greeterThemeSyncState) error { - destPath := filepath.Join(cacheDir, "greeter_wallpaper_override.jpg") - if state.ResolvedGreeterWallpaperPath == "" { - if err := runSudoCmd(sudoPassword, "rm", "-f", destPath); err != nil { - return fmt.Errorf("failed to clear override file %s: %w", destPath, err) - } - logFunc("✓ Cleared greeter wallpaper override") - return nil - } - if err := runSudoCmd(sudoPassword, "rm", "-f", destPath); err != nil { - return fmt.Errorf("failed to remove old override file %s: %w", destPath, err) - } - src := state.ResolvedGreeterWallpaperPath - st, err := os.Stat(src) - if err != nil { - return fmt.Errorf("configured greeter wallpaper not found at %s: %w", src, err) - } - if st.IsDir() { - return fmt.Errorf("configured greeter wallpaper path points to a directory: %s", src) - } - if err := runSudoCmd(sudoPassword, "cp", src, destPath); err != nil { - return fmt.Errorf("failed to copy override wallpaper to %s: %w", destPath, err) - } - greeterGroup := DetectGreeterGroup() - daemonUser := DetectGreeterUser() - if err := runSudoCmd(sudoPassword, "chown", daemonUser+":"+greeterGroup, destPath); err != nil { - if fallbackErr := runSudoCmd(sudoPassword, "chown", "root:"+greeterGroup, destPath); fallbackErr != nil { - return fmt.Errorf("failed to set override ownership on %s: %w", destPath, err) - } - } - if err := runSudoCmd(sudoPassword, "chmod", "644", destPath); err != nil { - return fmt.Errorf("failed to set override permissions on %s: %w", destPath, err) - } - logFunc("✓ Synced greeter wallpaper override") - return nil -} - -type niriGreeterSync struct { - processed map[string]bool - nodes []*document.Node - inputCount int - outputCount int - cursorCount int - debugCount int - cursorNode *document.Node - inputNode *document.Node - outputNodes map[string]*document.Node -} - -func syncNiriGreeterConfig(logFunc func(string), sudoPassword string) error { - configDir, err := os.UserConfigDir() - if err != nil { - return fmt.Errorf("failed to resolve user config directory: %w", err) - } - - configPath := filepath.Join(configDir, "niri", "config.kdl") - if _, err := os.Stat(configPath); os.IsNotExist(err) { - logFunc("ℹ Niri config not found; skipping greeter niri sync") - return nil - } else if err != nil { - return fmt.Errorf("failed to stat niri config: %w", err) - } - - extractor := &niriGreeterSync{ - processed: make(map[string]bool), - outputNodes: make(map[string]*document.Node), - } - - if err := extractor.processFile(configPath); err != nil { - return err - } - - if len(extractor.nodes) == 0 { - logFunc("ℹ No niri input/output sections found; skipping greeter niri sync") - return nil - } - - content := extractor.render() - if strings.TrimSpace(content) == "" { - logFunc("ℹ No niri input/output content to sync; skipping greeter niri sync") - return nil - } - - greeterDir := "/etc/greetd/niri" - greeterGroup := DetectGreeterGroup() - if err := runSudoCmd(sudoPassword, "mkdir", "-p", greeterDir); err != nil { - return fmt.Errorf("failed to create greetd niri directory: %w", err) - } - if err := runSudoCmd(sudoPassword, "chown", fmt.Sprintf("root:%s", greeterGroup), greeterDir); err != nil { - return fmt.Errorf("failed to set greetd niri directory ownership: %w", err) - } - if err := runSudoCmd(sudoPassword, "chmod", "755", greeterDir); err != nil { - return fmt.Errorf("failed to set greetd niri directory permissions: %w", err) - } - - dmsTemp, err := os.CreateTemp("", "dms-greeter-niri-dms-*.kdl") - if err != nil { - return fmt.Errorf("failed to create temp file: %w", err) - } - defer os.Remove(dmsTemp.Name()) - - if _, err := dmsTemp.WriteString(content); err != nil { - _ = dmsTemp.Close() - return fmt.Errorf("failed to write temp niri config: %w", err) - } - if err := dmsTemp.Close(); err != nil { - return fmt.Errorf("failed to close temp niri config: %w", err) - } - - dmsPath := filepath.Join(greeterDir, "dms.kdl") - if err := backupFileIfExists(sudoPassword, dmsPath, ".backup"); err != nil { - return fmt.Errorf("failed to backup %s: %w", dmsPath, err) - } - if err := runSudoCmd(sudoPassword, "install", "-o", "root", "-g", greeterGroup, "-m", "0644", dmsTemp.Name(), dmsPath); err != nil { - return fmt.Errorf("failed to install greetd niri dms config: %w", err) - } - - mainContent := fmt.Sprintf("%s\ninclude \"%s\"\n", config.NiriGreeterConfig, dmsPath) - mainTemp, err := os.CreateTemp("", "dms-greeter-niri-main-*.kdl") - if err != nil { - return fmt.Errorf("failed to create temp file: %w", err) - } - defer os.Remove(mainTemp.Name()) - - if _, err := mainTemp.WriteString(mainContent); err != nil { - _ = mainTemp.Close() - return fmt.Errorf("failed to write temp niri main config: %w", err) - } - if err := mainTemp.Close(); err != nil { - return fmt.Errorf("failed to close temp niri main config: %w", err) - } - - mainPath := filepath.Join(greeterDir, "config.kdl") - if err := backupFileIfExists(sudoPassword, mainPath, ".backup"); err != nil { - return fmt.Errorf("failed to backup %s: %w", mainPath, err) - } - if err := runSudoCmd(sudoPassword, "install", "-o", "root", "-g", greeterGroup, "-m", "0644", mainTemp.Name(), mainPath); err != nil { - return fmt.Errorf("failed to install greetd niri main config: %w", err) - } - - if err := ensureGreetdNiriConfig(logFunc, sudoPassword, mainPath); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to update greetd config for niri: %v", err)) - } - - logFunc(fmt.Sprintf("✓ Synced niri greeter config (%d input, %d output, %d cursor, %d debug) to %s", extractor.inputCount, extractor.outputCount, extractor.cursorCount, extractor.debugCount, dmsPath)) - return nil -} - -func ensureGreetdNiriConfig(logFunc func(string), sudoPassword string, niriConfigPath string) error { - configPath := "/etc/greetd/config.toml" - data, err := os.ReadFile(configPath) - if os.IsNotExist(err) { - logFunc("ℹ greetd config not found; skipping niri config wiring") - return nil - } - if err != nil { - return fmt.Errorf("failed to read greetd config: %w", err) - } - - lines := strings.Split(string(data), "\n") - updated := false - for i, line := range lines { - trimmed := strings.TrimSpace(line) - if !strings.HasPrefix(trimmed, "command") { - continue - } - - parts := strings.SplitN(trimmed, "=", 2) - if len(parts) != 2 { - continue - } - - command := strings.Trim(strings.TrimSpace(parts[1]), "\"") - if !strings.Contains(command, "dms-greeter") { - continue - } - if !strings.Contains(command, "--command niri") { - continue - } - command = stripConfigFlag(command) - command = stripCacheDirFlag(command) - command = strings.TrimSpace(command + " --cache-dir " + GreeterCacheDir) - - newCommand := fmt.Sprintf("%s -C %s", command, niriConfigPath) - idx := strings.Index(line, "command") - leading := "" - if idx > 0 { - leading = line[:idx] - } - lines[i] = fmt.Sprintf("%scommand = \"%s\"", leading, newCommand) - updated = true - break - } - - if !updated { - return nil - } - - tmpFile, err := os.CreateTemp("", "greetd-config-*.toml") - if err != nil { - return fmt.Errorf("failed to create temp greetd config: %w", err) - } - defer os.Remove(tmpFile.Name()) - - if _, err := tmpFile.WriteString(strings.Join(lines, "\n")); err != nil { - _ = tmpFile.Close() - return fmt.Errorf("failed to write temp greetd config: %w", err) - } - if err := tmpFile.Close(); err != nil { - return fmt.Errorf("failed to close temp greetd config: %w", err) - } - - if err := runSudoCmd(sudoPassword, "mv", tmpFile.Name(), configPath); err != nil { - return fmt.Errorf("failed to update greetd config: %w", err) - } - - logFunc(fmt.Sprintf("✓ Updated greetd config to use niri config %s", niriConfigPath)) - return nil -} - -func backupFileIfExists(sudoPassword string, path string, suffix string) error { - if _, err := os.Stat(path); os.IsNotExist(err) { - return nil - } else if err != nil { - return err - } - - backupPath := fmt.Sprintf("%s%s-%s", path, suffix, time.Now().Format("20060102-150405")) - if err := runSudoCmd(sudoPassword, "cp", path, backupPath); err != nil { - return err - } - return runSudoCmd(sudoPassword, "chmod", "644", backupPath) -} - -func (s *niriGreeterSync) processFile(filePath string) error { - absPath, err := filepath.Abs(filePath) - if err != nil { - return fmt.Errorf("failed to resolve path %s: %w", filePath, err) - } - - if s.processed[absPath] { - return nil - } - s.processed[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return fmt.Errorf("failed to read %s: %w", absPath, err) - } - - doc, err := kdl.Parse(strings.NewReader(string(data))) - if err != nil { - return fmt.Errorf("failed to parse KDL in %s: %w", absPath, err) - } - - baseDir := filepath.Dir(absPath) - for _, node := range doc.Nodes { - name := node.Name.String() - switch name { - case "include": - if err := s.handleInclude(node, baseDir); err != nil { - return err - } - case "input": - if s.inputNode == nil { - s.inputNode = node - s.inputNode.Children = dedupeCursorChildren(s.inputNode.Children) - s.nodes = append(s.nodes, node) - } else if len(node.Children) > 0 { - s.inputNode.Children = mergeInputChildren(s.inputNode.Children, node.Children) - } - s.inputCount++ - case "output": - key := outputNodeKey(node) - if existing, ok := s.outputNodes[key]; ok { - *existing = *node - } else { - s.outputNodes[key] = node - s.nodes = append(s.nodes, node) - } - s.outputCount++ - case "cursor": - if s.cursorNode == nil { - s.cursorNode = node - s.cursorNode.Children = dedupeCursorChildren(s.cursorNode.Children) - s.nodes = append(s.nodes, node) - s.cursorCount++ - } else if len(node.Children) > 0 { - s.cursorNode.Children = mergeCursorChildren(s.cursorNode.Children, node.Children) - } - case "debug": - s.nodes = append(s.nodes, node) - s.debugCount++ - } - } - - return nil -} - -func mergeCursorChildren(existing []*document.Node, incoming []*document.Node) []*document.Node { - if len(incoming) == 0 { - return existing - } - - indexByName := make(map[string]int, len(existing)) - for i, child := range existing { - indexByName[child.Name.String()] = i - } - - for _, child := range incoming { - name := child.Name.String() - if idx, ok := indexByName[name]; ok { - existing[idx] = child - continue - } - indexByName[name] = len(existing) - existing = append(existing, child) - } - - return existing -} - -func dedupeCursorChildren(children []*document.Node) []*document.Node { - if len(children) == 0 { - return children - } - - var result []*document.Node - indexByName := make(map[string]int, len(children)) - for _, child := range children { - name := child.Name.String() - if idx, ok := indexByName[name]; ok { - result[idx] = child - continue - } - indexByName[name] = len(result) - result = append(result, child) - } - - return result -} - -func mergeInputChildren(existing []*document.Node, incoming []*document.Node) []*document.Node { - if len(incoming) == 0 { - return existing - } - - indexByName := make(map[string]int, len(existing)) - for i, child := range existing { - indexByName[child.Name.String()] = i - } - - for _, child := range incoming { - name := child.Name.String() - if idx, ok := indexByName[name]; ok { - existing[idx] = child - continue - } - indexByName[name] = len(existing) - existing = append(existing, child) - } - - return existing -} - -func outputNodeKey(node *document.Node) string { - if len(node.Arguments) > 0 { - return strings.Trim(node.Arguments[0].String(), "\"") - } - return "" -} - -func (s *niriGreeterSync) handleInclude(node *document.Node, baseDir string) error { - if len(node.Arguments) == 0 { - return nil - } - - includePath := strings.Trim(node.Arguments[0].String(), "\"") - if includePath == "" { - return nil - } - - fullPath := includePath - if !filepath.IsAbs(includePath) { - fullPath = filepath.Join(baseDir, includePath) - } - - if _, err := os.Stat(fullPath); os.IsNotExist(err) { - return nil - } else if err != nil { - return fmt.Errorf("failed to stat include %s: %w", fullPath, err) - } - - return s.processFile(fullPath) -} - -func (s *niriGreeterSync) render() string { - if len(s.nodes) == 0 { - return "" - } - - var builder strings.Builder - for _, node := range s.nodes { - _, _ = node.WriteToOptions(&builder, document.NodeWriteOptions{ - LeadingTrailingSpace: true, - NameAndType: true, - Depth: 0, - Indent: []byte(" "), - IgnoreFlags: false, - }) - builder.WriteString("\n") - } - - return builder.String() -} - -func ConfigureGreetd(dmsPath, compositor string, logFunc func(string), sudoPassword string) error { - configPath := "/etc/greetd/config.toml" - - backupPath := fmt.Sprintf("%s.backup-%s", configPath, time.Now().Format("20060102-150405")) - if err := backupFileIfExists(sudoPassword, configPath, ".backup"); err != nil { - return fmt.Errorf("failed to backup greetd config: %w", err) - } - if _, err := os.Stat(configPath); err == nil { - logFunc(fmt.Sprintf("✓ Backed up existing config to %s", backupPath)) - } - - greeterUser := DetectGreeterUser() - - var configContent string - if data, err := os.ReadFile(configPath); err == nil { - configContent = string(data) - } else if os.IsNotExist(err) { - configContent = `[terminal] -vt = 1 - -[default_session] -` - } else { - return fmt.Errorf("failed to read greetd config: %w", err) - } - - wrapperCmd := resolveGreeterWrapperPath() - - compositorLower := strings.ToLower(compositor) - commandValue := fmt.Sprintf("%s --command %s --cache-dir %s", wrapperCmd, compositorLower, GreeterCacheDir) - if dmsPath != "" { - commandValue = fmt.Sprintf("%s -p %s", commandValue, dmsPath) - } - - commandLine := fmt.Sprintf(`command = "%s"`, commandValue) - newConfig := upsertDefaultSession(configContent, greeterUser, commandLine) - - tmpFile, err := os.CreateTemp("", "greetd-config-*.toml") - if err != nil { - return fmt.Errorf("failed to create temp greetd config: %w", err) - } - defer os.Remove(tmpFile.Name()) - - if _, err := tmpFile.WriteString(newConfig); err != nil { - _ = tmpFile.Close() - return fmt.Errorf("failed to write temp greetd config: %w", err) - } - if err := tmpFile.Close(); err != nil { - return fmt.Errorf("failed to close temp greetd config: %w", err) - } - - if err := runSudoCmd(sudoPassword, "mkdir", "-p", "/etc/greetd"); err != nil { - return fmt.Errorf("failed to create /etc/greetd: %w", err) - } - - if err := runSudoCmd(sudoPassword, "install", "-o", "root", "-g", "root", "-m", "0644", tmpFile.Name(), configPath); err != nil { - return fmt.Errorf("failed to install greetd config: %w", err) - } - - logFunc(fmt.Sprintf("✓ Updated greetd configuration (user: %s, command: %s)", greeterUser, commandValue)) - return nil -} - -func stripConfigFlag(command string) string { - for _, flag := range []string{" -C ", " --config "} { - idx := strings.Index(command, flag) - if idx == -1 { - continue - } - - before := command[:idx] - after := command[idx+len(flag):] - - switch { - case strings.HasPrefix(after, `"`): - if end := strings.Index(after[1:], `"`); end != -1 { - after = after[end+2:] - } else { - after = "" - } - default: - if space := strings.Index(after, " "); space != -1 { - after = after[space:] - } else { - after = "" - } - } - - command = strings.TrimSpace(before + after) - } - - return command -} - -func stripCacheDirFlag(command string) string { - fields := strings.Fields(command) - if len(fields) == 0 { - return strings.TrimSpace(command) - } - - filtered := make([]string, 0, len(fields)) - for i := 0; i < len(fields); i++ { - token := fields[i] - if token == "--cache-dir" { - if i+1 < len(fields) { - i++ - } - continue - } - if strings.HasPrefix(token, "--cache-dir=") { - continue - } - filtered = append(filtered, token) - } - - return strings.Join(filtered, " ") -} - -// getDebianOBSSlug returns the OBS repository slug for the running Debian version. -func getDebianOBSSlug(osInfo *distros.OSInfo) string { - versionID := strings.ToLower(osInfo.VersionID) - codename := strings.ToLower(osInfo.VersionCodename) - prettyName := strings.ToLower(osInfo.PrettyName) - - if strings.Contains(prettyName, "sid") || strings.Contains(prettyName, "unstable") || - codename == "sid" || versionID == "sid" { - return "Debian_Unstable" - } - if versionID == "testing" || codename == "testing" { - return "Debian_Testing" - } - if versionID != "" { - return "Debian_" + versionID // "Debian_13" - } - return "Debian_Unstable" -} - -// getOpenSUSEOBSRepoURL returns the OBS .repo file URL for the running openSUSE variant. -func getOpenSUSEOBSRepoURL(osInfo *distros.OSInfo) string { - const base = "https://download.opensuse.org/repositories/home:AvengeMedia:danklinux" - var slug string - switch osInfo.Distribution.ID { - case "opensuse-leap": - v := osInfo.VersionID - if v != "" && !strings.Contains(v, ".") { - v += ".0" // "16" → "16.0" - } - if v == "" { - v = "16.0" - } - slug = v - case "opensuse-slowroll": - slug = "openSUSE_Slowroll" - default: // opensuse-tumbleweed || unknown version - slug = "openSUSE_Tumbleweed" - } - return fmt.Sprintf("%s/%s/home:AvengeMedia:danklinux.repo", base, slug) -} - -func runSudoCmd(sudoPassword string, command string, args ...string) error { - var cmd *exec.Cmd - - if sudoPassword != "" { - fullArgs := append([]string{command}, args...) - quotedArgs := make([]string, len(fullArgs)) - for i, arg := range fullArgs { - quotedArgs[i] = "'" + strings.ReplaceAll(arg, "'", "'\\''") + "'" - } - cmdStr := strings.Join(quotedArgs, " ") - - cmd = distros.ExecSudoCommand(context.Background(), sudoPassword, cmdStr) - } else { - cmd = exec.Command("sudo", append([]string{command}, args...)...) - } - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} - -func checkSystemdEnabled(service string) (string, error) { - cmd := exec.Command("systemctl", "is-enabled", service) - output, _ := cmd.Output() - return strings.TrimSpace(string(output)), nil -} - -func DisableConflictingDisplayManagers(sudoPassword string, logFunc func(string)) error { - conflictingDMs := []string{"gdm", "gdm3", "lightdm", "sddm", "lxdm", "xdm", "cosmic-greeter"} - for _, dm := range conflictingDMs { - state, err := checkSystemdEnabled(dm) - if err != nil || state == "" || state == "not-found" { - continue - } - switch state { - case "enabled", "enabled-runtime", "static", "indirect", "alias": - logFunc(fmt.Sprintf("Disabling conflicting display manager: %s", dm)) - if err := runSudoCmd(sudoPassword, "systemctl", "disable", dm); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: Failed to disable %s: %v", dm, err)) - } else { - logFunc(fmt.Sprintf("✓ Disabled %s", dm)) - } - } - } - return nil -} - -// EnableGreetd unmasks and enables greetd, forcing it over any other DM. -func EnableGreetd(sudoPassword string, logFunc func(string)) error { - state, err := checkSystemdEnabled("greetd") - if err != nil { - return fmt.Errorf("failed to check greetd state: %w", err) - } - if state == "not-found" { - return fmt.Errorf("greetd service not found; ensure greetd is installed") - } - if state == "masked" || state == "masked-runtime" { - logFunc(" Unmasking greetd...") - if err := runSudoCmd(sudoPassword, "systemctl", "unmask", "greetd"); err != nil { - return fmt.Errorf("failed to unmask greetd: %w", err) - } - logFunc(" ✓ Unmasked greetd") - } - logFunc(" Enabling greetd service (--force)...") - if err := runSudoCmd(sudoPassword, "systemctl", "enable", "--force", "greetd"); err != nil { - return fmt.Errorf("failed to enable greetd: %w", err) - } - logFunc("✓ greetd enabled") - return nil -} - -func EnsureGraphicalTarget(sudoPassword string, logFunc func(string)) error { - cmd := exec.Command("systemctl", "get-default") - output, err := cmd.Output() - if err != nil { - logFunc(fmt.Sprintf("⚠ Warning: could not get default systemd target: %v", err)) - return nil - } - current := strings.TrimSpace(string(output)) - if current == "graphical.target" { - logFunc("✓ Default target is already graphical.target") - return nil - } - logFunc(fmt.Sprintf(" Setting default target to graphical.target (was: %s)...", current)) - if err := runSudoCmd(sudoPassword, "systemctl", "set-default", "graphical.target"); err != nil { - return fmt.Errorf("failed to set graphical target: %w", err) - } - logFunc("✓ Default target set to graphical.target") - return nil -} - -// AutoSetupGreeter performs the full non-interactive greeter setup -func AutoSetupGreeter(compositor, sudoPassword string, logFunc func(string)) error { - if IsGreeterPackaged() && HasLegacyLocalGreeterWrapper() { - return fmt.Errorf("legacy manual wrapper detected at /usr/local/bin/dms-greeter; " + - "remove it before using packaged dms-greeter: sudo rm -f /usr/local/bin/dms-greeter") - } - - logFunc("Ensuring greetd is installed...") - if err := EnsureGreetdInstalled(logFunc, sudoPassword); err != nil { - return fmt.Errorf("greetd install failed: %w", err) - } - - dmsPath := "" - if !IsGreeterPackaged() { - detected, err := DetectDMSPath() - if err != nil { - return fmt.Errorf("DMS installation not found: %w", err) - } - dmsPath = detected - logFunc(fmt.Sprintf("✓ Found DMS at: %s", dmsPath)) - } else { - logFunc("✓ Using packaged dms-greeter (/usr/share/quickshell/dms-greeter)") - } - - logFunc("Setting up dms-greeter group and permissions...") - if err := SetupDMSGroup(logFunc, sudoPassword); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: group/permissions setup error: %v", err)) - } - - logFunc("Copying greeter files...") - if err := CopyGreeterFiles(dmsPath, compositor, logFunc, sudoPassword); err != nil { - return fmt.Errorf("failed to copy greeter files: %w", err) - } - - logFunc("Configuring greetd...") - greeterPathForConfig := "" - if !IsGreeterPackaged() { - greeterPathForConfig = dmsPath - } - if err := ConfigureGreetd(greeterPathForConfig, compositor, logFunc, sudoPassword); err != nil { - return fmt.Errorf("failed to configure greetd: %w", err) - } - - logFunc("Synchronizing DMS configurations...") - if err := SyncDMSConfigs(dmsPath, compositor, logFunc, sudoPassword); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: config sync error: %v", err)) - } - - logFunc("Configuring authentication...") - if err := sharedpam.SyncAuthConfig(logFunc, sudoPassword, sharedpam.SyncAuthOptions{}); err != nil { - return fmt.Errorf("failed to sync authentication: %w", err) - } - - logFunc("Checking for conflicting display managers...") - if err := DisableConflictingDisplayManagers(sudoPassword, logFunc); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: %v", err)) - } - - logFunc("Enabling greetd service...") - if err := EnableGreetd(sudoPassword, logFunc); err != nil { - return fmt.Errorf("failed to enable greetd: %w", err) - } - - logFunc("Ensuring graphical.target as default...") - if err := EnsureGraphicalTarget(sudoPassword, logFunc); err != nil { - logFunc(fmt.Sprintf("⚠ Warning: %v", err)) - } - - logFunc("✓ DMS greeter setup complete") - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/installer_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/installer_test.go deleted file mode 100644 index 9acee57..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/greeter/installer_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package greeter - -import ( - "os" - "path/filepath" - "testing" -) - -func writeTestFile(t *testing.T, path string, content string) { - t.Helper() - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - t.Fatalf("failed to create parent dir for %s: %v", path, err) - } - if err := os.WriteFile(path, []byte(content), 0o644); err != nil { - t.Fatalf("failed to write %s: %v", path, err) - } -} - -func TestResolveGreeterThemeSyncState(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - settingsJSON string - sessionJSON string - wantSourcePath string - wantResolvedWallpaper string - wantDynamicOverrideUsed bool - }{ - { - name: "dynamic theme with greeter wallpaper override uses generated greeter colors", - settingsJSON: `{ - "currentThemeName": "dynamic", - "greeterWallpaperPath": "Pictures/blue.jpg", - "matugenScheme": "scheme-tonal-spot", - "iconTheme": "Papirus" -}`, - sessionJSON: `{"isLightMode":true}`, - wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "greeter-colors", "dms-colors.json"), - wantResolvedWallpaper: filepath.Join("Pictures", "blue.jpg"), - wantDynamicOverrideUsed: true, - }, - { - name: "dynamic theme without override uses desktop colors", - settingsJSON: `{ - "currentThemeName": "dynamic", - "greeterWallpaperPath": "" -}`, - sessionJSON: `{"isLightMode":false}`, - wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "dms-colors.json"), - wantResolvedWallpaper: "", - wantDynamicOverrideUsed: false, - }, - { - name: "non-dynamic theme keeps desktop colors even with override wallpaper", - settingsJSON: `{ - "currentThemeName": "purple", - "greeterWallpaperPath": "/tmp/blue.jpg" -}`, - sessionJSON: `{"isLightMode":false}`, - wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "dms-colors.json"), - wantResolvedWallpaper: "/tmp/blue.jpg", - wantDynamicOverrideUsed: false, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - homeDir := t.TempDir() - writeTestFile(t, filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json"), tt.settingsJSON) - writeTestFile(t, filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json"), tt.sessionJSON) - - state, err := resolveGreeterThemeSyncState(homeDir) - if err != nil { - t.Fatalf("resolveGreeterThemeSyncState returned error: %v", err) - } - - if got := state.effectiveColorsSource(homeDir); got != filepath.Join(homeDir, tt.wantSourcePath) { - t.Fatalf("effectiveColorsSource = %q, want %q", got, filepath.Join(homeDir, tt.wantSourcePath)) - } - - wantResolvedWallpaper := tt.wantResolvedWallpaper - if wantResolvedWallpaper != "" && !filepath.IsAbs(wantResolvedWallpaper) { - wantResolvedWallpaper = filepath.Join(homeDir, wantResolvedWallpaper) - } - if state.ResolvedGreeterWallpaperPath != wantResolvedWallpaper { - t.Fatalf("ResolvedGreeterWallpaperPath = %q, want %q", state.ResolvedGreeterWallpaperPath, wantResolvedWallpaper) - } - - if state.UsesDynamicWallpaperOverride != tt.wantDynamicOverrideUsed { - t.Fatalf("UsesDynamicWallpaperOverride = %v, want %v", state.UsesDynamicWallpaperOverride, tt.wantDynamicOverrideUsed) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/headless/runner.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/headless/runner.go deleted file mode 100644 index 7dae9fc..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/headless/runner.go +++ /dev/null @@ -1,418 +0,0 @@ -package headless - -import ( - "context" - "fmt" - "os" - "os/exec" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/config" - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/AvengeMedia/DankMaterialShell/core/internal/greeter" -) - -// ErrConfirmationRequired is returned when --yes is not set and the user -// must explicitly confirm the operation. -var ErrConfirmationRequired = fmt.Errorf("confirmation required: pass --yes to proceed") - -// validConfigNames maps lowercase CLI input to the deployer key used in -// replaceConfigs. Keep in sync with the config types checked by -// shouldReplaceConfig in deployer.go. -var validConfigNames = map[string]string{ - "niri": "Niri", - "hyprland": "Hyprland", - "ghostty": "Ghostty", - "kitty": "Kitty", - "alacritty": "Alacritty", -} - -// orderedConfigNames defines the canonical order for config names in output. -// Must be kept in sync with validConfigNames. -var orderedConfigNames = []string{"niri", "hyprland", "ghostty", "kitty", "alacritty"} - -// Config holds all CLI parameters for unattended installation. -type Config struct { - Compositor string // "niri" or "hyprland" - Terminal string // "ghostty", "kitty", or "alacritty" - IncludeDeps []string - ExcludeDeps []string - ReplaceConfigs []string // specific configs to deploy (e.g. "niri", "ghostty") - ReplaceConfigsAll bool // deploy/replace all configurations - Yes bool -} - -// Runner orchestrates unattended (headless) installation. -type Runner struct { - cfg Config - logChan chan string -} - -// NewRunner creates a new headless runner. -func NewRunner(cfg Config) *Runner { - return &Runner{ - cfg: cfg, - logChan: make(chan string, 1000), - } -} - -// GetLogChan returns the log channel for file logging. -func (r *Runner) GetLogChan() <-chan string { - return r.logChan -} - -// Run executes the full unattended installation flow. -func (r *Runner) Run() error { - r.log("Starting headless installation") - - // 1. Parse compositor and terminal selections - wm, err := r.parseWindowManager() - if err != nil { - return err - } - - terminal, err := r.parseTerminal() - if err != nil { - return err - } - - // 2. Build replace-configs map - replaceConfigs, err := r.buildReplaceConfigs() - if err != nil { - return err - } - - // 3. Detect OS - r.log("Detecting operating system...") - osInfo, err := distros.GetOSInfo() - if err != nil { - return fmt.Errorf("OS detection failed: %w", err) - } - - if distros.IsUnsupportedDistro(osInfo.Distribution.ID, osInfo.VersionID) { - return fmt.Errorf("unsupported distribution: %s %s", osInfo.PrettyName, osInfo.VersionID) - } - - fmt.Fprintf(os.Stdout, "Detected: %s (%s)\n", osInfo.PrettyName, osInfo.Architecture) - - // 4. Create distribution instance - distro, err := distros.NewDistribution(osInfo.Distribution.ID, r.logChan) - if err != nil { - return fmt.Errorf("failed to initialize distribution: %w", err) - } - - // 5. Detect dependencies - r.log("Detecting dependencies...") - fmt.Fprintln(os.Stdout, "Detecting dependencies...") - dependencies, err := distro.DetectDependenciesWithTerminal(context.Background(), wm, terminal) - if err != nil { - return fmt.Errorf("dependency detection failed: %w", err) - } - - // 5. Apply include/exclude filters and build the disabled-items map. - // Headless mode does not currently collect any explicit reinstall selections, - // so keep reinstallItems nil instead of constructing an always-empty map. - disabledItems, err := r.buildDisabledItems(dependencies) - if err != nil { - return err - } - var reinstallItems map[string]bool - - // Print dependency summary - fmt.Fprintln(os.Stdout, "\nDependencies:") - for _, dep := range dependencies { - marker := " " - status := "" - if disabledItems[dep.Name] { - marker = " SKIP " - status = "(disabled)" - } else { - switch dep.Status { - case deps.StatusInstalled: - marker = " OK " - status = "(installed)" - case deps.StatusMissing: - marker = " NEW " - status = "(will install)" - case deps.StatusNeedsUpdate: - marker = " UPD " - status = "(will update)" - case deps.StatusNeedsReinstall: - marker = " RE " - status = "(will reinstall)" - } - } - fmt.Fprintf(os.Stdout, "%s%-30s %s\n", marker, dep.Name, status) - } - fmt.Fprintln(os.Stdout) - - // 6b. Require explicit confirmation unless --yes is set - if !r.cfg.Yes { - if replaceConfigs == nil { - // --replace-configs-all - fmt.Fprintln(os.Stdout, "Packages will be installed and all configurations will be replaced.") - fmt.Fprintln(os.Stdout, "Existing config files will be backed up before replacement.") - } else if r.anyConfigEnabled(replaceConfigs) { - var names []string - for _, cliName := range orderedConfigNames { - deployerKey := validConfigNames[cliName] - if replaceConfigs[deployerKey] { - names = append(names, deployerKey) - } - } - fmt.Fprintf(os.Stdout, "Packages will be installed. The following configurations will be replaced (with backups): %s\n", strings.Join(names, ", ")) - } else { - fmt.Fprintln(os.Stdout, "Packages will be installed. No configurations will be deployed.") - } - fmt.Fprintln(os.Stdout, "Re-run with --yes (-y) to proceed.") - r.log("Aborted: --yes not set") - return ErrConfirmationRequired - } - - // 7. Authenticate sudo - sudoPassword, err := r.resolveSudoPassword() - if err != nil { - return err - } - - // 8. Install packages - fmt.Fprintln(os.Stdout, "Installing packages...") - r.log("Starting package installation") - - progressChan := make(chan distros.InstallProgressMsg, 100) - - installErr := make(chan error, 1) - go func() { - defer close(progressChan) - installErr <- distro.InstallPackages( - context.Background(), - dependencies, - wm, - sudoPassword, - reinstallItems, - disabledItems, - false, // skipGlobalUseFlags - progressChan, - ) - }() - - // Consume progress messages and print them - for msg := range progressChan { - if msg.Error != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", msg.Error) - } else if msg.Step != "" { - fmt.Fprintf(os.Stdout, " [%3.0f%%] %s\n", msg.Progress*100, msg.Step) - } - if msg.LogOutput != "" { - r.log(msg.LogOutput) - fmt.Fprintf(os.Stdout, " %s\n", msg.LogOutput) - } - } - - if err := <-installErr; err != nil { - return fmt.Errorf("package installation failed: %w", err) - } - - // 9. Greeter setup (if dms-greeter was included) - if !disabledItems["dms-greeter"] && r.depExists(dependencies, "dms-greeter") { - compositorName := "niri" - if wm == deps.WindowManagerHyprland { - compositorName = "Hyprland" - } - fmt.Fprintln(os.Stdout, "Configuring DMS greeter...") - logFunc := func(line string) { - r.log(line) - fmt.Fprintf(os.Stdout, " greeter: %s\n", line) - } - if err := greeter.AutoSetupGreeter(compositorName, sudoPassword, logFunc); err != nil { - // Non-fatal, matching TUI behavior - fmt.Fprintf(os.Stderr, "Warning: greeter setup issue (non-fatal): %v\n", err) - } - } - - // 10. Deploy configurations - fmt.Fprintln(os.Stdout, "Deploying configurations...") - r.log("Starting configuration deployment") - - deployer := config.NewConfigDeployer(r.logChan) - results, err := deployer.DeployConfigurationsSelectiveWithReinstalls( - context.Background(), - wm, - terminal, - dependencies, - replaceConfigs, - reinstallItems, - ) - if err != nil { - return fmt.Errorf("configuration deployment failed: %w", err) - } - - for _, result := range results { - if result.Deployed { - msg := fmt.Sprintf(" Deployed: %s", result.ConfigType) - if result.BackupPath != "" { - msg += fmt.Sprintf(" (backup: %s)", result.BackupPath) - } - fmt.Fprintln(os.Stdout, msg) - } - if result.Error != nil { - fmt.Fprintf(os.Stderr, " Error deploying %s: %v\n", result.ConfigType, result.Error) - } - } - - fmt.Fprintln(os.Stdout, "\nInstallation complete!") - r.log("Headless installation completed successfully") - return nil -} - -// buildDisabledItems computes the set of dependencies that should be skipped -// during installation, applying the --include-deps and --exclude-deps filters. -// dms-greeter is disabled by default (opt-in), matching TUI behavior. -func (r *Runner) buildDisabledItems(dependencies []deps.Dependency) (map[string]bool, error) { - disabledItems := make(map[string]bool) - - // dms-greeter is opt-in (disabled by default), matching TUI behavior - for i := range dependencies { - if dependencies[i].Name == "dms-greeter" { - disabledItems["dms-greeter"] = true - break - } - } - - // Process --include-deps (enable items that are disabled by default) - for _, name := range r.cfg.IncludeDeps { - name = strings.TrimSpace(name) - if name == "" { - continue - } - if !r.depExists(dependencies, name) { - return nil, fmt.Errorf("--include-deps: unknown dependency %q", name) - } - delete(disabledItems, name) - } - - // Process --exclude-deps (disable items) - for _, name := range r.cfg.ExcludeDeps { - name = strings.TrimSpace(name) - if name == "" { - continue - } - if !r.depExists(dependencies, name) { - return nil, fmt.Errorf("--exclude-deps: unknown dependency %q", name) - } - // Don't allow excluding DMS itself - if name == "dms (DankMaterialShell)" { - return nil, fmt.Errorf("--exclude-deps: cannot exclude required package %q", name) - } - disabledItems[name] = true - } - - return disabledItems, nil -} - -// buildReplaceConfigs converts the --replace-configs / --replace-configs-all -// flags into the map[string]bool consumed by the config deployer. -// -// Returns: -// - nil when --replace-configs-all is set (deployer treats nil as "replace all") -// - a map with all known configs set to false when neither flag is set (deploy only if config file is missing on disk) -// - a map with requested configs true, all others false for --replace-configs -// - an error when both flags are set or an invalid config name is given -func (r *Runner) buildReplaceConfigs() (map[string]bool, error) { - hasSpecific := len(r.cfg.ReplaceConfigs) > 0 - if hasSpecific && r.cfg.ReplaceConfigsAll { - return nil, fmt.Errorf("--replace-configs and --replace-configs-all are mutually exclusive") - } - - if r.cfg.ReplaceConfigsAll { - return nil, nil - } - - // Build a map with all known configs explicitly set to false - result := make(map[string]bool, len(validConfigNames)) - for _, cliName := range orderedConfigNames { - result[validConfigNames[cliName]] = false - } - - // Enable only the requested configs - for _, name := range r.cfg.ReplaceConfigs { - name = strings.TrimSpace(name) - if name == "" { - continue - } - deployerKey, ok := validConfigNames[strings.ToLower(name)] - if !ok { - return nil, fmt.Errorf("--replace-configs: unknown config %q; valid values: niri, hyprland, ghostty, kitty, alacritty", name) - } - result[deployerKey] = true - } - - return result, nil -} - -func (r *Runner) log(message string) { - select { - case r.logChan <- message: - default: - } -} - -func (r *Runner) parseWindowManager() (deps.WindowManager, error) { - switch strings.ToLower(r.cfg.Compositor) { - case "niri": - return deps.WindowManagerNiri, nil - case "hyprland": - return deps.WindowManagerHyprland, nil - default: - return 0, fmt.Errorf("invalid --compositor value %q: must be 'niri' or 'hyprland'", r.cfg.Compositor) - } -} - -func (r *Runner) parseTerminal() (deps.Terminal, error) { - switch strings.ToLower(r.cfg.Terminal) { - case "ghostty": - return deps.TerminalGhostty, nil - case "kitty": - return deps.TerminalKitty, nil - case "alacritty": - return deps.TerminalAlacritty, nil - default: - return 0, fmt.Errorf("invalid --term value %q: must be 'ghostty', 'kitty', or 'alacritty'", r.cfg.Terminal) - } -} - -func (r *Runner) resolveSudoPassword() (string, error) { - // Check if sudo credentials are cached (via sudo -v or NOPASSWD) - cmd := exec.Command("sudo", "-n", "true") - if err := cmd.Run(); err == nil { - r.log("sudo cache is valid, no password needed") - fmt.Fprintln(os.Stdout, "sudo: using cached credentials") - return "", nil - } - - return "", fmt.Errorf( - "sudo authentication required but no cached credentials found\n" + - "Options:\n" + - " 1. Run 'sudo -v' before dankinstall to cache credentials\n" + - " 2. Configure passwordless sudo for your user", - ) -} - -func (r *Runner) anyConfigEnabled(m map[string]bool) bool { - for _, v := range m { - if v { - return true - } - } - return false -} - -func (r *Runner) depExists(dependencies []deps.Dependency, name string) bool { - for _, dep := range dependencies { - if dep.Name == name { - return true - } - } - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/headless/runner_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/headless/runner_test.go deleted file mode 100644 index 79260ae..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/headless/runner_test.go +++ /dev/null @@ -1,459 +0,0 @@ -package headless - -import ( - "strings" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -func TestParseWindowManager(t *testing.T) { - tests := []struct { - name string - input string - want deps.WindowManager - wantErr bool - }{ - {"niri lowercase", "niri", deps.WindowManagerNiri, false}, - {"niri mixed case", "Niri", deps.WindowManagerNiri, false}, - {"hyprland lowercase", "hyprland", deps.WindowManagerHyprland, false}, - {"hyprland mixed case", "Hyprland", deps.WindowManagerHyprland, false}, - {"invalid", "sway", 0, true}, - {"empty", "", 0, true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := NewRunner(Config{Compositor: tt.input}) - got, err := r.parseWindowManager() - if (err != nil) != tt.wantErr { - t.Errorf("parseWindowManager() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !tt.wantErr && got != tt.want { - t.Errorf("parseWindowManager() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestParseTerminal(t *testing.T) { - tests := []struct { - name string - input string - want deps.Terminal - wantErr bool - }{ - {"ghostty lowercase", "ghostty", deps.TerminalGhostty, false}, - {"ghostty mixed case", "Ghostty", deps.TerminalGhostty, false}, - {"kitty lowercase", "kitty", deps.TerminalKitty, false}, - {"alacritty lowercase", "alacritty", deps.TerminalAlacritty, false}, - {"alacritty uppercase", "ALACRITTY", deps.TerminalAlacritty, false}, - {"invalid", "wezterm", 0, true}, - {"empty", "", 0, true}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := NewRunner(Config{Terminal: tt.input}) - got, err := r.parseTerminal() - if (err != nil) != tt.wantErr { - t.Errorf("parseTerminal() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !tt.wantErr && got != tt.want { - t.Errorf("parseTerminal() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestDepExists(t *testing.T) { - dependencies := []deps.Dependency{ - {Name: "niri", Status: deps.StatusInstalled}, - {Name: "ghostty", Status: deps.StatusMissing}, - {Name: "dms (DankMaterialShell)", Status: deps.StatusInstalled}, - {Name: "dms-greeter", Status: deps.StatusMissing}, - } - - tests := []struct { - name string - dep string - want bool - }{ - {"existing dep", "niri", true}, - {"existing dep with special chars", "dms (DankMaterialShell)", true}, - {"existing optional dep", "dms-greeter", true}, - {"non-existing dep", "firefox", false}, - {"empty name", "", false}, - } - - r := NewRunner(Config{}) - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := r.depExists(dependencies, tt.dep); got != tt.want { - t.Errorf("depExists(%q) = %v, want %v", tt.dep, got, tt.want) - } - }) - } -} - -func TestNewRunner(t *testing.T) { - cfg := Config{ - Compositor: "niri", - Terminal: "ghostty", - IncludeDeps: []string{"dms-greeter"}, - ExcludeDeps: []string{"some-pkg"}, - Yes: true, - } - r := NewRunner(cfg) - - if r == nil { - t.Fatal("NewRunner returned nil") - } - if r.cfg.Compositor != "niri" { - t.Errorf("cfg.Compositor = %q, want %q", r.cfg.Compositor, "niri") - } - if r.cfg.Terminal != "ghostty" { - t.Errorf("cfg.Terminal = %q, want %q", r.cfg.Terminal, "ghostty") - } - if !r.cfg.Yes { - t.Error("cfg.Yes = false, want true") - } - if r.logChan == nil { - t.Error("logChan is nil") - } -} - -func TestGetLogChan(t *testing.T) { - r := NewRunner(Config{}) - ch := r.GetLogChan() - if ch == nil { - t.Fatal("GetLogChan returned nil") - } - - // Verify the channel is readable by sending a message - go func() { - r.logChan <- "test message" - }() - msg := <-ch - if msg != "test message" { - t.Errorf("received %q, want %q", msg, "test message") - } -} - -func TestLog(t *testing.T) { - r := NewRunner(Config{}) - - // log should not block even if channel is full - for i := 0; i < 1100; i++ { - r.log("message") - } - // If we reach here without hanging, the non-blocking send works -} - -func TestRunRequiresYes(t *testing.T) { - // Verify that ErrConfirmationRequired is a distinct sentinel error - if ErrConfirmationRequired == nil { - t.Fatal("ErrConfirmationRequired should not be nil") - } - expected := "confirmation required: pass --yes to proceed" - if ErrConfirmationRequired.Error() != expected { - t.Errorf("ErrConfirmationRequired = %q, want %q", ErrConfirmationRequired.Error(), expected) - } -} - -func TestConfigYesStoredCorrectly(t *testing.T) { - // Yes=false (default) should be stored - rNo := NewRunner(Config{Compositor: "niri", Terminal: "ghostty", Yes: false}) - if rNo.cfg.Yes { - t.Error("cfg.Yes = true, want false") - } - - // Yes=true should be stored - rYes := NewRunner(Config{Compositor: "niri", Terminal: "ghostty", Yes: true}) - if !rYes.cfg.Yes { - t.Error("cfg.Yes = false, want true") - } -} - -func TestValidConfigNamesCompleteness(t *testing.T) { - // orderedConfigNames and validConfigNames must stay in sync. - if len(orderedConfigNames) != len(validConfigNames) { - t.Fatalf("orderedConfigNames has %d entries but validConfigNames has %d", - len(orderedConfigNames), len(validConfigNames)) - } - - // Every entry in orderedConfigNames must exist in validConfigNames. - for _, name := range orderedConfigNames { - if _, ok := validConfigNames[name]; !ok { - t.Errorf("orderedConfigNames contains %q which is missing from validConfigNames", name) - } - } - - // validConfigNames must have no extra keys not in orderedConfigNames. - ordered := make(map[string]bool, len(orderedConfigNames)) - for _, name := range orderedConfigNames { - ordered[name] = true - } - for key := range validConfigNames { - if !ordered[key] { - t.Errorf("validConfigNames contains %q which is missing from orderedConfigNames", key) - } - } -} - -func TestBuildReplaceConfigs(t *testing.T) { - allDeployerKeys := []string{"Niri", "Hyprland", "Ghostty", "Kitty", "Alacritty"} - - tests := []struct { - name string - replaceConfigs []string - replaceAll bool - wantNil bool // expect nil (replace all) - wantEnabled []string // deployer keys that should be true - wantErr bool - }{ - { - name: "neither flag set", - wantNil: false, - wantEnabled: nil, // all should be false - }, - { - name: "replace-configs-all", - replaceAll: true, - wantNil: true, - }, - { - name: "specific configs", - replaceConfigs: []string{"niri", "ghostty"}, - wantNil: false, - wantEnabled: []string{"Niri", "Ghostty"}, - }, - { - name: "both flags set", - replaceConfigs: []string{"niri"}, - replaceAll: true, - wantErr: true, - }, - { - name: "invalid config name", - replaceConfigs: []string{"foo"}, - wantErr: true, - }, - { - name: "case insensitive", - replaceConfigs: []string{"NIRI", "Ghostty"}, - wantNil: false, - wantEnabled: []string{"Niri", "Ghostty"}, - }, - { - name: "single config", - replaceConfigs: []string{"kitty"}, - wantNil: false, - wantEnabled: []string{"Kitty"}, - }, - { - name: "whitespace entry", - replaceConfigs: []string{" ", "niri"}, - wantNil: false, - wantEnabled: []string{"Niri"}, - }, - { - name: "duplicate entry", - replaceConfigs: []string{"niri", "niri"}, - wantNil: false, - wantEnabled: []string{"Niri"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := NewRunner(Config{ - ReplaceConfigs: tt.replaceConfigs, - ReplaceConfigsAll: tt.replaceAll, - }) - got, err := r.buildReplaceConfigs() - if (err != nil) != tt.wantErr { - t.Fatalf("buildReplaceConfigs() error = %v, wantErr %v", err, tt.wantErr) - } - if tt.wantErr { - return - } - if tt.wantNil { - if got != nil { - t.Fatalf("buildReplaceConfigs() = %v, want nil", got) - } - return - } - if got == nil { - t.Fatal("buildReplaceConfigs() = nil, want non-nil map") - } - - // All known deployer keys must be present - for _, key := range allDeployerKeys { - if _, exists := got[key]; !exists { - t.Errorf("missing deployer key %q in result map", key) - } - } - - // Build enabled set for easy lookup - enabledSet := make(map[string]bool) - for _, k := range tt.wantEnabled { - enabledSet[k] = true - } - - for _, key := range allDeployerKeys { - want := enabledSet[key] - if got[key] != want { - t.Errorf("replaceConfigs[%q] = %v, want %v", key, got[key], want) - } - } - }) - } -} - -func TestConfigReplaceConfigsStoredCorrectly(t *testing.T) { - r := NewRunner(Config{ - Compositor: "niri", - Terminal: "ghostty", - ReplaceConfigs: []string{"niri", "ghostty"}, - ReplaceConfigsAll: false, - }) - if len(r.cfg.ReplaceConfigs) != 2 { - t.Errorf("len(ReplaceConfigs) = %d, want 2", len(r.cfg.ReplaceConfigs)) - } - if r.cfg.ReplaceConfigsAll { - t.Error("ReplaceConfigsAll = true, want false") - } - - r2 := NewRunner(Config{ - Compositor: "niri", - Terminal: "ghostty", - ReplaceConfigsAll: true, - }) - if !r2.cfg.ReplaceConfigsAll { - t.Error("ReplaceConfigsAll = false, want true") - } - if len(r2.cfg.ReplaceConfigs) != 0 { - t.Errorf("len(ReplaceConfigs) = %d, want 0", len(r2.cfg.ReplaceConfigs)) - } -} - -func TestBuildDisabledItems(t *testing.T) { - dependencies := []deps.Dependency{ - {Name: "niri", Status: deps.StatusInstalled}, - {Name: "ghostty", Status: deps.StatusMissing}, - {Name: "dms (DankMaterialShell)", Status: deps.StatusInstalled}, - {Name: "dms-greeter", Status: deps.StatusMissing}, - {Name: "waybar", Status: deps.StatusMissing}, - } - - tests := []struct { - name string - includeDeps []string - excludeDeps []string - deps []deps.Dependency // nil means use the shared fixture - wantErr bool - errContains string // substring expected in error message - wantDisabled []string // dep names that should be in disabledItems - wantEnabled []string // dep names that should NOT be in disabledItems (extra check) - }{ - { - name: "no flags set, dms-greeter disabled by default", - wantDisabled: []string{"dms-greeter"}, - wantEnabled: []string{"niri", "ghostty", "waybar"}, - }, - { - name: "include dms-greeter enables it", - includeDeps: []string{"dms-greeter"}, - wantEnabled: []string{"dms-greeter"}, - }, - { - name: "exclude a regular dep", - excludeDeps: []string{"waybar"}, - wantDisabled: []string{"dms-greeter", "waybar"}, - }, - { - name: "include unknown dep returns error", - includeDeps: []string{"nonexistent"}, - wantErr: true, - errContains: "--include-deps", - }, - { - name: "exclude unknown dep returns error", - excludeDeps: []string{"nonexistent"}, - wantErr: true, - errContains: "--exclude-deps", - }, - { - name: "exclude DMS itself is forbidden", - excludeDeps: []string{"dms (DankMaterialShell)"}, - wantErr: true, - errContains: "cannot exclude required package", - }, - { - name: "include and exclude same dep", - includeDeps: []string{"dms-greeter"}, - excludeDeps: []string{"dms-greeter"}, - wantDisabled: []string{"dms-greeter"}, - }, - { - name: "whitespace entries are skipped", - includeDeps: []string{" ", "dms-greeter"}, - wantEnabled: []string{"dms-greeter"}, - }, - { - name: "no dms-greeter in deps, nothing disabled by default", - deps: []deps.Dependency{ - {Name: "niri", Status: deps.StatusInstalled}, - }, - wantEnabled: []string{"niri"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := NewRunner(Config{ - IncludeDeps: tt.includeDeps, - ExcludeDeps: tt.excludeDeps, - }) - d := tt.deps - if d == nil { - d = dependencies - } - got, err := r.buildDisabledItems(d) - if (err != nil) != tt.wantErr { - t.Fatalf("buildDisabledItems() error = %v, wantErr %v", err, tt.wantErr) - } - if tt.wantErr { - if tt.errContains != "" && !strings.Contains(err.Error(), tt.errContains) { - t.Errorf("error %q does not contain %q", err.Error(), tt.errContains) - } - return - } - if got == nil { - t.Fatal("buildDisabledItems() returned nil map, want non-nil") - } - - // Check expected disabled items - for _, name := range tt.wantDisabled { - if !got[name] { - t.Errorf("expected %q to be disabled, but it is not", name) - } - } - - // Check expected enabled items (should not be in the map or be false) - for _, name := range tt.wantEnabled { - if got[name] { - t.Errorf("expected %q to NOT be disabled, but it is", name) - } - } - - // If wantDisabled is empty, the map should have length 0 - if len(tt.wantDisabled) == 0 && len(got) != 0 { - t.Errorf("expected empty disabledItems map, got %v", got) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/discovery.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/discovery.go deleted file mode 100644 index 4990975..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/discovery.go +++ /dev/null @@ -1,107 +0,0 @@ -package keybinds - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -type DiscoveryConfig struct { - SearchPaths []string -} - -func DefaultDiscoveryConfig() *DiscoveryConfig { - var searchPaths []string - - configDir, err := os.UserConfigDir() - if err == nil && configDir != "" { - searchPaths = append(searchPaths, filepath.Join(configDir, "DankMaterialShell", "cheatsheets")) - } - - configDirs := os.Getenv("XDG_CONFIG_DIRS") - if configDirs != "" { - for dir := range strings.SplitSeq(configDirs, ":") { - if dir != "" { - searchPaths = append(searchPaths, filepath.Join(dir, "DankMaterialShell", "cheatsheets")) - } - } - } - - return &DiscoveryConfig{ - SearchPaths: searchPaths, - } -} - -func (d *DiscoveryConfig) FindJSONFiles() ([]string, error) { - var files []string - - for _, searchPath := range d.SearchPaths { - expandedPath, err := utils.ExpandPath(searchPath) - if err != nil { - continue - } - - if _, err := os.Stat(expandedPath); os.IsNotExist(err) { - continue - } - - entries, err := os.ReadDir(expandedPath) - if err != nil { - continue - } - - for _, entry := range entries { - if entry.IsDir() { - continue - } - - if !strings.HasSuffix(entry.Name(), ".json") { - continue - } - - fullPath := filepath.Join(expandedPath, entry.Name()) - files = append(files, fullPath) - } - } - - return files, nil -} - -type JSONProviderFactory func(filePath string) (Provider, error) - -var jsonProviderFactory JSONProviderFactory - -func SetJSONProviderFactory(factory JSONProviderFactory) { - jsonProviderFactory = factory -} - -func AutoDiscoverProviders(registry *Registry, config *DiscoveryConfig) error { - if config == nil { - config = DefaultDiscoveryConfig() - } - - if jsonProviderFactory == nil { - return nil - } - - files, err := config.FindJSONFiles() - if err != nil { - return fmt.Errorf("failed to discover JSON files: %w", err) - } - - for _, file := range files { - provider, err := jsonProviderFactory(file) - if err != nil { - continue - } - - if err := registry.Register(provider); err != nil { - continue - } - } - - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/discovery_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/discovery_test.go deleted file mode 100644 index a78724e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/discovery_test.go +++ /dev/null @@ -1,287 +0,0 @@ -package keybinds - -import ( - "os" - "path/filepath" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -func TestDefaultDiscoveryConfig(t *testing.T) { - oldConfigHome := os.Getenv("XDG_CONFIG_HOME") - oldConfigDirs := os.Getenv("XDG_CONFIG_DIRS") - defer func() { - os.Setenv("XDG_CONFIG_HOME", oldConfigHome) - os.Setenv("XDG_CONFIG_DIRS", oldConfigDirs) - }() - - tests := []struct { - name string - configHome string - configDirs string - expectedCount int - checkFirstPath bool - firstPath string - }{ - { - name: "default with no XDG vars", - configHome: "", - configDirs: "", - expectedCount: 1, - checkFirstPath: true, - }, - { - name: "with XDG_CONFIG_HOME set", - configHome: "/custom/config", - configDirs: "", - expectedCount: 1, - checkFirstPath: true, - firstPath: "/custom/config/DankMaterialShell/cheatsheets", - }, - { - name: "with XDG_CONFIG_DIRS set", - configHome: "/home/user/.config", - configDirs: "/etc/xdg:/opt/config", - expectedCount: 3, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - os.Setenv("XDG_CONFIG_HOME", tt.configHome) - os.Setenv("XDG_CONFIG_DIRS", tt.configDirs) - - config := DefaultDiscoveryConfig() - - if config == nil { - t.Fatal("DefaultDiscoveryConfig returned nil") - } - - if len(config.SearchPaths) != tt.expectedCount { - t.Errorf("SearchPaths count = %d, want %d", len(config.SearchPaths), tt.expectedCount) - } - - if tt.checkFirstPath && len(config.SearchPaths) > 0 { - if tt.firstPath != "" && config.SearchPaths[0] != tt.firstPath { - t.Errorf("SearchPaths[0] = %q, want %q", config.SearchPaths[0], tt.firstPath) - } - } - }) - } -} - -func TestFindJSONFiles(t *testing.T) { - tmpDir := t.TempDir() - - file1 := filepath.Join(tmpDir, "tmux.json") - file2 := filepath.Join(tmpDir, "vim.json") - txtFile := filepath.Join(tmpDir, "readme.txt") - subdir := filepath.Join(tmpDir, "subdir") - - if err := os.WriteFile(file1, []byte("{}"), 0o644); err != nil { - t.Fatalf("Failed to create file1: %v", err) - } - if err := os.WriteFile(file2, []byte("{}"), 0o644); err != nil { - t.Fatalf("Failed to create file2: %v", err) - } - if err := os.WriteFile(txtFile, []byte("text"), 0o644); err != nil { - t.Fatalf("Failed to create txt file: %v", err) - } - if err := os.MkdirAll(subdir, 0o755); err != nil { - t.Fatalf("Failed to create subdir: %v", err) - } - - config := &DiscoveryConfig{ - SearchPaths: []string{tmpDir}, - } - - files, err := config.FindJSONFiles() - if err != nil { - t.Fatalf("FindJSONFiles failed: %v", err) - } - - if len(files) != 2 { - t.Errorf("expected 2 JSON files, got %d", len(files)) - } - - found := make(map[string]bool) - for _, f := range files { - found[filepath.Base(f)] = true - } - - if !found["tmux.json"] { - t.Error("tmux.json not found") - } - if !found["vim.json"] { - t.Error("vim.json not found") - } - if found["readme.txt"] { - t.Error("readme.txt should not be included") - } -} - -func TestFindJSONFilesNonexistentPath(t *testing.T) { - config := &DiscoveryConfig{ - SearchPaths: []string{"/nonexistent/path"}, - } - - files, err := config.FindJSONFiles() - if err != nil { - t.Fatalf("FindJSONFiles failed: %v", err) - } - - if len(files) != 0 { - t.Errorf("expected 0 files for nonexistent path, got %d", len(files)) - } -} - -func TestFindJSONFilesMultiplePaths(t *testing.T) { - tmpDir1 := t.TempDir() - tmpDir2 := t.TempDir() - - file1 := filepath.Join(tmpDir1, "app1.json") - file2 := filepath.Join(tmpDir2, "app2.json") - - if err := os.WriteFile(file1, []byte("{}"), 0o644); err != nil { - t.Fatalf("Failed to create file1: %v", err) - } - if err := os.WriteFile(file2, []byte("{}"), 0o644); err != nil { - t.Fatalf("Failed to create file2: %v", err) - } - - config := &DiscoveryConfig{ - SearchPaths: []string{tmpDir1, tmpDir2}, - } - - files, err := config.FindJSONFiles() - if err != nil { - t.Fatalf("FindJSONFiles failed: %v", err) - } - - if len(files) != 2 { - t.Errorf("expected 2 JSON files from multiple paths, got %d", len(files)) - } -} - -func TestAutoDiscoverProviders(t *testing.T) { - tmpDir := t.TempDir() - - jsonContent := `{ - "title": "Test App", - "provider": "testapp", - "binds": {} -}` - - file := filepath.Join(tmpDir, "testapp.json") - if err := os.WriteFile(file, []byte(jsonContent), 0o644); err != nil { - t.Fatalf("Failed to create test file: %v", err) - } - - config := &DiscoveryConfig{ - SearchPaths: []string{tmpDir}, - } - - registry := NewRegistry() - - factoryCalled := false - SetJSONProviderFactory(func(filePath string) (Provider, error) { - factoryCalled = true - return &mockProvider{name: "testapp"}, nil - }) - - err := AutoDiscoverProviders(registry, config) - if err != nil { - t.Fatalf("AutoDiscoverProviders failed: %v", err) - } - - if !factoryCalled { - t.Error("factory was not called") - } - - provider, err := registry.Get("testapp") - if err != nil { - t.Fatalf("provider not registered: %v", err) - } - - if provider.Name() != "testapp" { - t.Errorf("provider name = %q, want %q", provider.Name(), "testapp") - } -} - -func TestAutoDiscoverProvidersNilConfig(t *testing.T) { - registry := NewRegistry() - - SetJSONProviderFactory(func(filePath string) (Provider, error) { - return &mockProvider{name: "test"}, nil - }) - - err := AutoDiscoverProviders(registry, nil) - if err != nil { - t.Fatalf("AutoDiscoverProviders with nil config failed: %v", err) - } -} - -func TestAutoDiscoverProvidersNoFactory(t *testing.T) { - tmpDir := t.TempDir() - - file := filepath.Join(tmpDir, "test.json") - if err := os.WriteFile(file, []byte("{}"), 0o644); err != nil { - t.Fatalf("Failed to create test file: %v", err) - } - - config := &DiscoveryConfig{ - SearchPaths: []string{tmpDir}, - } - - registry := NewRegistry() - - SetJSONProviderFactory(nil) - - err := AutoDiscoverProviders(registry, config) - if err != nil { - t.Fatalf("AutoDiscoverProviders should not fail without factory: %v", err) - } - - providers := registry.List() - if len(providers) != 0 { - t.Errorf("expected 0 providers without factory, got %d", len(providers)) - } -} - -func TestExpandPathInDiscovery(t *testing.T) { - home, err := os.UserHomeDir() - if err != nil { - t.Skip("Cannot get home directory") - } - - tests := []struct { - name string - input string - expected string - }{ - { - name: "tilde expansion", - input: "~/test", - expected: filepath.Join(home, "test"), - }, - { - name: "absolute path", - input: "/tmp/test", - expected: "/tmp/test", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := utils.ExpandPath(tt.input) - if err != nil { - t.Fatalf("expandPath failed: %v", err) - } - - if result != tt.expected { - t.Errorf("utils.ExpandPath(%q) = %q, want %q", tt.input, result, tt.expected) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland.go deleted file mode 100644 index e43a9eb..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland.go +++ /dev/null @@ -1,497 +0,0 @@ -package providers - -import ( - "fmt" - "os" - "path/filepath" - "sort" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -type HyprlandProvider struct { - configPath string - dmsBindsIncluded bool - parsed bool -} - -func NewHyprlandProvider(configPath string) *HyprlandProvider { - if configPath == "" { - configPath = defaultHyprlandConfigDir() - } - return &HyprlandProvider{ - configPath: configPath, - } -} - -func defaultHyprlandConfigDir() string { - configDir, err := os.UserConfigDir() - if err != nil { - return "" - } - return filepath.Join(configDir, "hypr") -} - -func (h *HyprlandProvider) Name() string { - return "hyprland" -} - -func (h *HyprlandProvider) GetCheatSheet() (*keybinds.CheatSheet, error) { - result, err := ParseHyprlandKeysWithDMS(h.configPath) - if err != nil { - return nil, fmt.Errorf("failed to parse hyprland config: %w", err) - } - - h.dmsBindsIncluded = result.DMSBindsIncluded - h.parsed = true - - categorizedBinds := make(map[string][]keybinds.Keybind) - h.convertSection(result.Section, "", categorizedBinds, result.ConflictingConfigs) - - sheet := &keybinds.CheatSheet{ - Title: "Hyprland Keybinds", - Provider: h.Name(), - Binds: categorizedBinds, - DMSBindsIncluded: result.DMSBindsIncluded, - } - - if result.DMSStatus != nil { - sheet.DMSStatus = &keybinds.DMSBindsStatus{ - Exists: result.DMSStatus.Exists, - Included: result.DMSStatus.Included, - IncludePosition: result.DMSStatus.IncludePosition, - TotalIncludes: result.DMSStatus.TotalIncludes, - BindsAfterDMS: result.DMSStatus.BindsAfterDMS, - Effective: result.DMSStatus.Effective, - OverriddenBy: result.DMSStatus.OverriddenBy, - StatusMessage: result.DMSStatus.StatusMessage, - } - } - - return sheet, nil -} - -func (h *HyprlandProvider) HasDMSBindsIncluded() bool { - if h.parsed { - return h.dmsBindsIncluded - } - - result, err := ParseHyprlandKeysWithDMS(h.configPath) - if err != nil { - return false - } - - h.dmsBindsIncluded = result.DMSBindsIncluded - h.parsed = true - return h.dmsBindsIncluded -} - -func (h *HyprlandProvider) convertSection(section *HyprlandSection, subcategory string, categorizedBinds map[string][]keybinds.Keybind, conflicts map[string]*HyprlandKeyBinding) { - currentSubcat := subcategory - if section.Name != "" { - currentSubcat = section.Name - } - - for _, kb := range section.Keybinds { - category := h.categorizeByDispatcher(kb.Dispatcher) - bind := h.convertKeybind(&kb, currentSubcat, conflicts) - categorizedBinds[category] = append(categorizedBinds[category], bind) - } - - for _, child := range section.Children { - h.convertSection(&child, currentSubcat, categorizedBinds, conflicts) - } -} - -func (h *HyprlandProvider) categorizeByDispatcher(dispatcher string) string { - switch { - case strings.Contains(dispatcher, "workspace"): - return "Workspace" - case strings.Contains(dispatcher, "monitor"): - return "Monitor" - case strings.Contains(dispatcher, "window") || - strings.Contains(dispatcher, "focus") || - strings.Contains(dispatcher, "move") || - strings.Contains(dispatcher, "swap") || - strings.Contains(dispatcher, "resize") || - dispatcher == "killactive" || - dispatcher == "fullscreen" || - dispatcher == "togglefloating" || - dispatcher == "pin" || - dispatcher == "fakefullscreen" || - dispatcher == "splitratio" || - dispatcher == "resizeactive": - return "Window" - case dispatcher == "exec": - return "Execute" - case dispatcher == "exit" || strings.Contains(dispatcher, "dpms"): - return "System" - default: - return "Other" - } -} - -func (h *HyprlandProvider) convertKeybind(kb *HyprlandKeyBinding, subcategory string, conflicts map[string]*HyprlandKeyBinding) keybinds.Keybind { - keyStr := h.formatKey(kb) - rawAction := h.formatRawAction(kb.Dispatcher, kb.Params) - desc := kb.Comment - - if desc == "" { - desc = rawAction - } - - source := "config" - if strings.Contains(kb.Source, "dms/binds.conf") { - source = "dms" - } - - bind := keybinds.Keybind{ - Key: keyStr, - Description: desc, - Action: rawAction, - Subcategory: subcategory, - Source: source, - Flags: kb.Flags, - } - - if source == "dms" && conflicts != nil { - normalizedKey := strings.ToLower(keyStr) - if conflictKb, ok := conflicts[normalizedKey]; ok { - bind.Conflict = &keybinds.Keybind{ - Key: keyStr, - Description: conflictKb.Comment, - Action: h.formatRawAction(conflictKb.Dispatcher, conflictKb.Params), - Source: "config", - } - } - } - - return bind -} - -func (h *HyprlandProvider) formatRawAction(dispatcher, params string) string { - if params != "" { - return dispatcher + " " + params - } - return dispatcher -} - -func (h *HyprlandProvider) formatKey(kb *HyprlandKeyBinding) string { - parts := make([]string, 0, len(kb.Mods)+1) - parts = append(parts, kb.Mods...) - parts = append(parts, kb.Key) - return strings.Join(parts, "+") -} - -func (h *HyprlandProvider) GetOverridePath() string { - expanded, err := utils.ExpandPath(h.configPath) - if err != nil { - return filepath.Join(h.configPath, "dms", "binds.conf") - } - return filepath.Join(expanded, "dms", "binds.conf") -} - -func (h *HyprlandProvider) validateAction(action string) error { - action = strings.TrimSpace(action) - switch { - case action == "": - return fmt.Errorf("action cannot be empty") - case action == "exec" || action == "exec ": - return fmt.Errorf("exec dispatcher requires arguments") - case strings.HasPrefix(action, "exec "): - rest := strings.TrimSpace(strings.TrimPrefix(action, "exec ")) - if rest == "" { - return fmt.Errorf("exec dispatcher requires arguments") - } - } - return nil -} - -func (h *HyprlandProvider) SetBind(key, action, description string, options map[string]any) error { - if err := h.validateAction(action); err != nil { - return err - } - - overridePath := h.GetOverridePath() - - if err := os.MkdirAll(filepath.Dir(overridePath), 0o755); err != nil { - return fmt.Errorf("failed to create dms directory: %w", err) - } - - existingBinds, err := h.loadOverrideBinds() - if err != nil { - existingBinds = make(map[string]*hyprlandOverrideBind) - } - - // Extract flags from options - var flags string - if options != nil { - if f, ok := options["flags"].(string); ok { - flags = f - } - } - - normalizedKey := strings.ToLower(key) - existingBinds[normalizedKey] = &hyprlandOverrideBind{ - Key: key, - Action: action, - Description: description, - Flags: flags, - Options: options, - } - - return h.writeOverrideBinds(existingBinds) -} - -func (h *HyprlandProvider) RemoveBind(key string) error { - existingBinds, err := h.loadOverrideBinds() - if err != nil { - return nil - } - - normalizedKey := strings.ToLower(key) - delete(existingBinds, normalizedKey) - return h.writeOverrideBinds(existingBinds) -} - -type hyprlandOverrideBind struct { - Key string - Action string - Description string - Flags string // Bind flags: l=locked, r=release, e=repeat, n=non-consuming, m=mouse, t=transparent, i=ignore-mods, s=separate, d=description, o=long-press - Options map[string]any -} - -func (h *HyprlandProvider) loadOverrideBinds() (map[string]*hyprlandOverrideBind, error) { - overridePath := h.GetOverridePath() - binds := make(map[string]*hyprlandOverrideBind) - - data, err := os.ReadFile(overridePath) - if os.IsNotExist(err) { - return binds, nil - } - if err != nil { - return nil, err - } - - lines := strings.Split(string(data), "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - if line == "" || strings.HasPrefix(line, "#") { - continue - } - - if !strings.HasPrefix(line, "bind") { - continue - } - - parts := strings.SplitN(line, "=", 2) - if len(parts) < 2 { - continue - } - - // Extract flags from bind type - bindType := strings.TrimSpace(parts[0]) - flags := extractBindFlags(bindType) - hasDescFlag := strings.Contains(flags, "d") - - content := strings.TrimSpace(parts[1]) - commentParts := strings.SplitN(content, "#", 2) - bindContent := strings.TrimSpace(commentParts[0]) - - var comment string - if len(commentParts) > 1 { - comment = strings.TrimSpace(commentParts[1]) - } - - // For bindd, format is: mods, key, description, dispatcher, params - var minFields, descIndex, dispatcherIndex int - if hasDescFlag { - minFields = 4 - descIndex = 2 - dispatcherIndex = 3 - } else { - minFields = 3 - dispatcherIndex = 2 - } - - fields := strings.SplitN(bindContent, ",", minFields+2) - if len(fields) < minFields { - continue - } - - mods := strings.TrimSpace(fields[0]) - keyName := strings.TrimSpace(fields[1]) - - var dispatcher, params string - if hasDescFlag { - if comment == "" { - comment = strings.TrimSpace(fields[descIndex]) - } - dispatcher = strings.TrimSpace(fields[dispatcherIndex]) - if len(fields) > dispatcherIndex+1 { - paramParts := fields[dispatcherIndex+1:] - params = strings.TrimSpace(strings.Join(paramParts, ",")) - } - } else { - dispatcher = strings.TrimSpace(fields[dispatcherIndex]) - if len(fields) > dispatcherIndex+1 { - paramParts := fields[dispatcherIndex+1:] - params = strings.TrimSpace(strings.Join(paramParts, ",")) - } - } - - keyStr := h.buildKeyString(mods, keyName) - normalizedKey := strings.ToLower(keyStr) - action := dispatcher - if params != "" { - action = dispatcher + " " + params - } - - binds[normalizedKey] = &hyprlandOverrideBind{ - Key: keyStr, - Action: action, - Description: comment, - Flags: flags, - } - } - - return binds, nil -} - -func (h *HyprlandProvider) buildKeyString(mods, key string) string { - if mods == "" { - return key - } - - modList := strings.FieldsFunc(mods, func(r rune) bool { - return r == '+' || r == ' ' - }) - - parts := append(modList, key) - return strings.Join(parts, "+") -} - -func (h *HyprlandProvider) getBindSortPriority(action string) int { - switch { - case strings.HasPrefix(action, "exec") && strings.Contains(action, "dms"): - return 0 - case strings.Contains(action, "workspace"): - return 1 - case strings.Contains(action, "window") || strings.Contains(action, "focus") || - strings.Contains(action, "move") || strings.Contains(action, "swap") || - strings.Contains(action, "resize"): - return 2 - case strings.Contains(action, "monitor"): - return 3 - case strings.HasPrefix(action, "exec"): - return 4 - case action == "exit" || strings.Contains(action, "dpms"): - return 5 - default: - return 6 - } -} - -func (h *HyprlandProvider) writeOverrideBinds(binds map[string]*hyprlandOverrideBind) error { - overridePath := h.GetOverridePath() - content := h.generateBindsContent(binds) - return os.WriteFile(overridePath, []byte(content), 0o644) -} - -func (h *HyprlandProvider) generateBindsContent(binds map[string]*hyprlandOverrideBind) string { - if len(binds) == 0 { - return "" - } - - bindList := make([]*hyprlandOverrideBind, 0, len(binds)) - for _, bind := range binds { - bindList = append(bindList, bind) - } - - sort.Slice(bindList, func(i, j int) bool { - pi, pj := h.getBindSortPriority(bindList[i].Action), h.getBindSortPriority(bindList[j].Action) - if pi != pj { - return pi < pj - } - return bindList[i].Key < bindList[j].Key - }) - - var sb strings.Builder - for _, bind := range bindList { - h.writeBindLine(&sb, bind) - } - - return sb.String() -} - -func (h *HyprlandProvider) writeBindLine(sb *strings.Builder, bind *hyprlandOverrideBind) { - mods, key := h.parseKeyString(bind.Key) - dispatcher, params := h.parseAction(bind.Action) - - // Write bind type with flags (e.g., "bind", "binde", "bindel") - sb.WriteString("bind") - if bind.Flags != "" { - sb.WriteString(bind.Flags) - } - sb.WriteString(" = ") - sb.WriteString(mods) - sb.WriteString(", ") - sb.WriteString(key) - sb.WriteString(", ") - - // For bindd (description flag), include description before dispatcher - if strings.Contains(bind.Flags, "d") && bind.Description != "" { - sb.WriteString(bind.Description) - sb.WriteString(", ") - } - - sb.WriteString(dispatcher) - - if params != "" { - sb.WriteString(", ") - sb.WriteString(params) - } - - // Only add comment if not using bindd (which has inline description) - if bind.Description != "" && !strings.Contains(bind.Flags, "d") { - sb.WriteString(" # ") - sb.WriteString(bind.Description) - } - - sb.WriteString("\n") -} - -func (h *HyprlandProvider) parseKeyString(keyStr string) (mods, key string) { - parts := strings.Split(keyStr, "+") - switch len(parts) { - case 0: - return "", keyStr - case 1: - return "", parts[0] - default: - return strings.Join(parts[:len(parts)-1], " "), parts[len(parts)-1] - } -} - -func (h *HyprlandProvider) parseAction(action string) (dispatcher, params string) { - parts := strings.SplitN(action, " ", 2) - switch len(parts) { - case 0: - return action, "" - case 1: - dispatcher = parts[0] - default: - dispatcher = parts[0] - params = parts[1] - } - - // Convert internal spawn format to Hyprland's exec - if dispatcher == "spawn" { - dispatcher = "exec" - } - - return dispatcher, params -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_parser.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_parser.go deleted file mode 100644 index 04b5ada..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_parser.go +++ /dev/null @@ -1,627 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -const ( - TitleRegex = "#+!" - HideComment = "[hidden]" - CommentBindPattern = "#/#" -) - -var ModSeparators = []rune{'+', ' '} - -type HyprlandKeyBinding struct { - Mods []string `json:"mods"` - Key string `json:"key"` - Dispatcher string `json:"dispatcher"` - Params string `json:"params"` - Comment string `json:"comment"` - Source string `json:"source"` - Flags string `json:"flags"` // Bind flags: l=locked, r=release, e=repeat, n=non-consuming, m=mouse, t=transparent, i=ignore-mods, s=separate, d=description, o=long-press -} - -type HyprlandSection struct { - Children []HyprlandSection `json:"children"` - Keybinds []HyprlandKeyBinding `json:"keybinds"` - Name string `json:"name"` -} - -type HyprlandParser struct { - contentLines []string - readingLine int - configDir string - currentSource string - dmsBindsExists bool - dmsBindsIncluded bool - includeCount int - dmsIncludePos int - bindsAfterDMS int - dmsBindKeys map[string]bool - configBindKeys map[string]bool - conflictingConfigs map[string]*HyprlandKeyBinding - bindMap map[string]*HyprlandKeyBinding - bindOrder []string - processedFiles map[string]bool - dmsProcessed bool -} - -func NewHyprlandParser(configDir string) *HyprlandParser { - return &HyprlandParser{ - contentLines: []string{}, - readingLine: 0, - configDir: configDir, - dmsIncludePos: -1, - dmsBindKeys: make(map[string]bool), - configBindKeys: make(map[string]bool), - conflictingConfigs: make(map[string]*HyprlandKeyBinding), - bindMap: make(map[string]*HyprlandKeyBinding), - bindOrder: []string{}, - processedFiles: make(map[string]bool), - } -} - -func (p *HyprlandParser) ReadContent(directory string) error { - expandedDir, err := utils.ExpandPath(directory) - if err != nil { - return err - } - - info, err := os.Stat(expandedDir) - if err != nil { - return err - } - if !info.IsDir() { - return os.ErrNotExist - } - - confFiles, err := filepath.Glob(filepath.Join(expandedDir, "*.conf")) - if err != nil { - return err - } - if len(confFiles) == 0 { - return os.ErrNotExist - } - - var combinedContent []string - for _, confFile := range confFiles { - if fileInfo, err := os.Stat(confFile); err == nil && fileInfo.Mode().IsRegular() { - data, err := os.ReadFile(confFile) - if err == nil { - combinedContent = append(combinedContent, string(data)) - } - } - } - - if len(combinedContent) == 0 { - return os.ErrNotExist - } - - fullContent := strings.Join(combinedContent, "\n") - p.contentLines = strings.Split(fullContent, "\n") - return nil -} - -func hyprlandAutogenerateComment(dispatcher, params string) string { - switch dispatcher { - case "resizewindow": - return "Resize window" - - case "movewindow": - if params == "" { - return "Move window" - } - dirMap := map[string]string{ - "l": "left", - "r": "right", - "u": "up", - "d": "down", - } - if dir, ok := dirMap[params]; ok { - return "move in " + dir + " direction" - } - return "move in null direction" - - case "pin": - return "pin (show on all workspaces)" - - case "splitratio": - return "Window split ratio " + params - - case "togglefloating": - return "Float/unfloat window" - - case "resizeactive": - return "Resize window by " + params - - case "killactive": - return "Close window" - - case "fullscreen": - fsMap := map[string]string{ - "0": "fullscreen", - "1": "maximization", - "2": "fullscreen on Hyprland's side", - } - if fs, ok := fsMap[params]; ok { - return "Toggle " + fs - } - return "Toggle null" - - case "fakefullscreen": - return "Toggle fake fullscreen" - - case "workspace": - switch params { - case "+1": - return "focus right" - case "-1": - return "focus left" - } - return "focus workspace " + params - case "movefocus": - dirMap := map[string]string{ - "l": "left", - "r": "right", - "u": "up", - "d": "down", - } - if dir, ok := dirMap[params]; ok { - return "move focus " + dir - } - return "move focus null" - - case "swapwindow": - dirMap := map[string]string{ - "l": "left", - "r": "right", - "u": "up", - "d": "down", - } - if dir, ok := dirMap[params]; ok { - return "swap in " + dir + " direction" - } - return "swap in null direction" - - case "movetoworkspace": - switch params { - case "+1": - return "move to right workspace (non-silent)" - case "-1": - return "move to left workspace (non-silent)" - } - return "move to workspace " + params + " (non-silent)" - case "movetoworkspacesilent": - switch params { - case "+1": - return "move to right workspace" - case "-1": - return "move to right workspace" - } - return "move to workspace " + params - - case "togglespecialworkspace": - return "toggle special" - - case "exec": - return params - - default: - return "" - } -} - -func (p *HyprlandParser) getKeybindAtLine(lineNumber int) *HyprlandKeyBinding { - line := p.contentLines[lineNumber] - return p.parseBindLine(line) -} - -func (p *HyprlandParser) getBindsRecursive(currentContent *HyprlandSection, scope int) *HyprlandSection { - titleRegex := regexp.MustCompile(TitleRegex) - - for p.readingLine < len(p.contentLines) { - line := p.contentLines[p.readingLine] - - loc := titleRegex.FindStringIndex(line) - if loc != nil && loc[0] == 0 { - headingScope := strings.Index(line, "!") - - if headingScope <= scope { - p.readingLine-- - return currentContent - } - - sectionName := strings.TrimSpace(line[headingScope+1:]) - p.readingLine++ - - childSection := &HyprlandSection{ - Children: []HyprlandSection{}, - Keybinds: []HyprlandKeyBinding{}, - Name: sectionName, - } - result := p.getBindsRecursive(childSection, headingScope) - currentContent.Children = append(currentContent.Children, *result) - - } else if strings.HasPrefix(line, CommentBindPattern) { - keybind := p.getKeybindAtLine(p.readingLine) - if keybind != nil { - currentContent.Keybinds = append(currentContent.Keybinds, *keybind) - } - - } else if line == "" || !strings.HasPrefix(strings.TrimSpace(line), "bind") { - - } else { - keybind := p.getKeybindAtLine(p.readingLine) - if keybind != nil { - currentContent.Keybinds = append(currentContent.Keybinds, *keybind) - } - } - - p.readingLine++ - } - - return currentContent -} - -func (p *HyprlandParser) ParseKeys() *HyprlandSection { - p.readingLine = 0 - rootSection := &HyprlandSection{ - Children: []HyprlandSection{}, - Keybinds: []HyprlandKeyBinding{}, - Name: "", - } - return p.getBindsRecursive(rootSection, 0) -} - -func ParseHyprlandKeys(path string) (*HyprlandSection, error) { - parser := NewHyprlandParser(path) - if err := parser.ReadContent(path); err != nil { - return nil, err - } - return parser.ParseKeys(), nil -} - -type HyprlandParseResult struct { - Section *HyprlandSection - DMSBindsIncluded bool - DMSStatus *HyprlandDMSStatus - ConflictingConfigs map[string]*HyprlandKeyBinding -} - -type HyprlandDMSStatus struct { - Exists bool - Included bool - IncludePosition int - TotalIncludes int - BindsAfterDMS int - Effective bool - OverriddenBy int - StatusMessage string -} - -func (p *HyprlandParser) buildDMSStatus() *HyprlandDMSStatus { - status := &HyprlandDMSStatus{ - Exists: p.dmsBindsExists, - Included: p.dmsBindsIncluded, - IncludePosition: p.dmsIncludePos, - TotalIncludes: p.includeCount, - BindsAfterDMS: p.bindsAfterDMS, - } - - switch { - case !p.dmsBindsExists: - status.Effective = false - status.StatusMessage = "dms/binds.conf does not exist" - case !p.dmsBindsIncluded: - status.Effective = false - status.StatusMessage = "dms/binds.conf is not sourced in config" - case p.bindsAfterDMS > 0: - status.Effective = true - status.OverriddenBy = p.bindsAfterDMS - status.StatusMessage = "Some DMS binds may be overridden by config binds" - default: - status.Effective = true - status.StatusMessage = "DMS binds are active" - } - - return status -} - -func (p *HyprlandParser) formatBindKey(kb *HyprlandKeyBinding) string { - parts := make([]string, 0, len(kb.Mods)+1) - parts = append(parts, kb.Mods...) - parts = append(parts, kb.Key) - return strings.Join(parts, "+") -} - -func (p *HyprlandParser) normalizeKey(key string) string { - return strings.ToLower(key) -} - -func (p *HyprlandParser) addBind(kb *HyprlandKeyBinding) bool { - key := p.formatBindKey(kb) - normalizedKey := p.normalizeKey(key) - isDMSBind := strings.Contains(kb.Source, "dms/binds.conf") - - if isDMSBind { - p.dmsBindKeys[normalizedKey] = true - } else if p.dmsBindKeys[normalizedKey] { - p.bindsAfterDMS++ - p.conflictingConfigs[normalizedKey] = kb - p.configBindKeys[normalizedKey] = true - return false - } else { - p.configBindKeys[normalizedKey] = true - } - - if _, exists := p.bindMap[normalizedKey]; !exists { - p.bindOrder = append(p.bindOrder, key) - } - p.bindMap[normalizedKey] = kb - return true -} - -func (p *HyprlandParser) ParseWithDMS() (*HyprlandSection, error) { - expandedDir, err := utils.ExpandPath(p.configDir) - if err != nil { - return nil, err - } - - dmsBindsPath := filepath.Join(expandedDir, "dms", "binds.conf") - if _, err := os.Stat(dmsBindsPath); err == nil { - p.dmsBindsExists = true - } - - mainConfig := filepath.Join(expandedDir, "hyprland.conf") - section, err := p.parseFileWithSource(mainConfig, "") - if err != nil { - return nil, err - } - - if p.dmsBindsExists && !p.dmsProcessed { - p.parseDMSBindsDirectly(dmsBindsPath, section) - } - - return section, nil -} - -func (p *HyprlandParser) parseFileWithSource(filePath, sectionName string) (*HyprlandSection, error) { - absPath, err := filepath.Abs(filePath) - if err != nil { - return nil, err - } - - if p.processedFiles[absPath] { - return &HyprlandSection{Name: sectionName}, nil - } - p.processedFiles[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return nil, err - } - - prevSource := p.currentSource - p.currentSource = absPath - - section := &HyprlandSection{Name: sectionName} - lines := strings.Split(string(data), "\n") - - for _, line := range lines { - trimmed := strings.TrimSpace(line) - - if strings.HasPrefix(trimmed, "source") { - p.handleSource(trimmed, section, filepath.Dir(absPath)) - continue - } - - if !strings.HasPrefix(trimmed, "bind") { - continue - } - - kb := p.parseBindLine(line) - if kb == nil { - continue - } - kb.Source = p.currentSource - if p.addBind(kb) { - section.Keybinds = append(section.Keybinds, *kb) - } - } - - p.currentSource = prevSource - return section, nil -} - -func (p *HyprlandParser) handleSource(line string, section *HyprlandSection, baseDir string) { - parts := strings.SplitN(line, "=", 2) - if len(parts) < 2 { - return - } - - sourcePath := strings.TrimSpace(parts[1]) - isDMSSource := sourcePath == "dms/binds.conf" || strings.HasSuffix(sourcePath, "/dms/binds.conf") - - p.includeCount++ - if isDMSSource { - p.dmsBindsIncluded = true - p.dmsIncludePos = p.includeCount - p.dmsProcessed = true - } - - fullPath := sourcePath - if !filepath.IsAbs(sourcePath) { - fullPath = filepath.Join(baseDir, sourcePath) - } - - expanded, err := utils.ExpandPath(fullPath) - if err != nil { - return - } - - includedSection, err := p.parseFileWithSource(expanded, "") - if err != nil { - return - } - - section.Children = append(section.Children, *includedSection) -} - -func (p *HyprlandParser) parseDMSBindsDirectly(dmsBindsPath string, section *HyprlandSection) { - data, err := os.ReadFile(dmsBindsPath) - if err != nil { - return - } - - prevSource := p.currentSource - p.currentSource = dmsBindsPath - - lines := strings.Split(string(data), "\n") - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if !strings.HasPrefix(trimmed, "bind") { - continue - } - - kb := p.parseBindLine(line) - if kb == nil { - continue - } - kb.Source = dmsBindsPath - if p.addBind(kb) { - section.Keybinds = append(section.Keybinds, *kb) - } - } - - p.currentSource = prevSource - p.dmsProcessed = true -} - -func (p *HyprlandParser) parseBindLine(line string) *HyprlandKeyBinding { - parts := strings.SplitN(line, "=", 2) - if len(parts) < 2 { - return nil - } - - // Extract bind type and flags from the left side of "=" - bindType := strings.TrimSpace(parts[0]) - flags := extractBindFlags(bindType) - hasDescFlag := strings.Contains(flags, "d") - - keys := parts[1] - keyParts := strings.SplitN(keys, "#", 2) - keys = keyParts[0] - - var comment string - if len(keyParts) > 1 { - comment = strings.TrimSpace(keyParts[1]) - } - - // For bindd, the format is: bindd = MODS, key, description, dispatcher, params - // For regular binds: bind = MODS, key, dispatcher, params - var minFields, descIndex, dispatcherIndex int - if hasDescFlag { - minFields = 4 // mods, key, description, dispatcher - descIndex = 2 - dispatcherIndex = 3 - } else { - minFields = 3 // mods, key, dispatcher - dispatcherIndex = 2 - } - - keyFields := strings.SplitN(keys, ",", minFields+2) // Allow for params - if len(keyFields) < minFields { - return nil - } - - mods := strings.TrimSpace(keyFields[0]) - key := strings.TrimSpace(keyFields[1]) - - var dispatcher, params string - if hasDescFlag { - // bindd format: description is in the bind itself - if comment == "" { - comment = strings.TrimSpace(keyFields[descIndex]) - } - dispatcher = strings.TrimSpace(keyFields[dispatcherIndex]) - if len(keyFields) > dispatcherIndex+1 { - paramParts := keyFields[dispatcherIndex+1:] - params = strings.TrimSpace(strings.Join(paramParts, ",")) - } - } else { - dispatcher = strings.TrimSpace(keyFields[dispatcherIndex]) - if len(keyFields) > dispatcherIndex+1 { - paramParts := keyFields[dispatcherIndex+1:] - params = strings.TrimSpace(strings.Join(paramParts, ",")) - } - } - - if comment != "" && strings.HasPrefix(comment, HideComment) { - return nil - } - - if comment == "" { - comment = hyprlandAutogenerateComment(dispatcher, params) - } - - var modList []string - if mods != "" { - modstring := mods + string(ModSeparators[0]) - idx := 0 - for index, char := range modstring { - isModSep := false - for _, sep := range ModSeparators { - if char == sep { - isModSep = true - break - } - } - if isModSep { - if index-idx > 1 { - modList = append(modList, modstring[idx:index]) - } - idx = index + 1 - } - } - } - - return &HyprlandKeyBinding{ - Mods: modList, - Key: key, - Dispatcher: dispatcher, - Params: params, - Comment: comment, - Flags: flags, - } -} - -// extractBindFlags extracts the flags from a bind type string -// e.g., "binde" -> "e", "bindel" -> "el", "bindd" -> "d" -func extractBindFlags(bindType string) string { - bindType = strings.TrimSpace(bindType) - if !strings.HasPrefix(bindType, "bind") { - return "" - } - return bindType[4:] // Everything after "bind" -} - -func ParseHyprlandKeysWithDMS(path string) (*HyprlandParseResult, error) { - parser := NewHyprlandParser(path) - section, err := parser.ParseWithDMS() - if err != nil { - return nil, err - } - - return &HyprlandParseResult{ - Section: section, - DMSBindsIncluded: parser.dmsBindsIncluded, - DMSStatus: parser.buildDMSStatus(), - ConflictingConfigs: parser.conflictingConfigs, - }, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_parser_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_parser_test.go deleted file mode 100644 index c2872e5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_parser_test.go +++ /dev/null @@ -1,519 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestHyprlandAutogenerateComment(t *testing.T) { - tests := []struct { - dispatcher string - params string - expected string - }{ - {"resizewindow", "", "Resize window"}, - {"movewindow", "", "Move window"}, - {"movewindow", "l", "move in left direction"}, - {"movewindow", "r", "move in right direction"}, - {"movewindow", "u", "move in up direction"}, - {"movewindow", "d", "move in down direction"}, - {"pin", "", "pin (show on all workspaces)"}, - {"splitratio", "0.5", "Window split ratio 0.5"}, - {"togglefloating", "", "Float/unfloat window"}, - {"resizeactive", "10 20", "Resize window by 10 20"}, - {"killactive", "", "Close window"}, - {"fullscreen", "0", "Toggle fullscreen"}, - {"fullscreen", "1", "Toggle maximization"}, - {"fullscreen", "2", "Toggle fullscreen on Hyprland's side"}, - {"fakefullscreen", "", "Toggle fake fullscreen"}, - {"workspace", "+1", "focus right"}, - {"workspace", "-1", "focus left"}, - {"workspace", "5", "focus workspace 5"}, - {"movefocus", "l", "move focus left"}, - {"movefocus", "r", "move focus right"}, - {"movefocus", "u", "move focus up"}, - {"movefocus", "d", "move focus down"}, - {"swapwindow", "l", "swap in left direction"}, - {"swapwindow", "r", "swap in right direction"}, - {"swapwindow", "u", "swap in up direction"}, - {"swapwindow", "d", "swap in down direction"}, - {"movetoworkspace", "+1", "move to right workspace (non-silent)"}, - {"movetoworkspace", "-1", "move to left workspace (non-silent)"}, - {"movetoworkspace", "3", "move to workspace 3 (non-silent)"}, - {"movetoworkspacesilent", "+1", "move to right workspace"}, - {"movetoworkspacesilent", "-1", "move to right workspace"}, - {"movetoworkspacesilent", "2", "move to workspace 2"}, - {"togglespecialworkspace", "", "toggle special"}, - {"exec", "firefox", "firefox"}, - {"unknown", "", ""}, - } - - for _, tt := range tests { - t.Run(tt.dispatcher+"_"+tt.params, func(t *testing.T) { - result := hyprlandAutogenerateComment(tt.dispatcher, tt.params) - if result != tt.expected { - t.Errorf("hyprlandAutogenerateComment(%q, %q) = %q, want %q", - tt.dispatcher, tt.params, result, tt.expected) - } - }) - } -} - -func TestHyprlandGetKeybindAtLine(t *testing.T) { - tests := []struct { - name string - line string - expected *HyprlandKeyBinding - }{ - { - name: "basic_keybind", - line: "bind = SUPER, Q, killactive", - expected: &HyprlandKeyBinding{ - Mods: []string{"SUPER"}, - Key: "Q", - Dispatcher: "killactive", - Params: "", - Comment: "Close window", - }, - }, - { - name: "keybind_with_params", - line: "bind = SUPER, left, movefocus, l", - expected: &HyprlandKeyBinding{ - Mods: []string{"SUPER"}, - Key: "left", - Dispatcher: "movefocus", - Params: "l", - Comment: "move focus left", - }, - }, - { - name: "keybind_with_comment", - line: "bind = SUPER, T, exec, kitty # Open terminal", - expected: &HyprlandKeyBinding{ - Mods: []string{"SUPER"}, - Key: "T", - Dispatcher: "exec", - Params: "kitty", - Comment: "Open terminal", - }, - }, - { - name: "keybind_hidden", - line: "bind = SUPER, H, exec, secret # [hidden]", - expected: nil, - }, - { - name: "keybind_multiple_mods", - line: "bind = SUPER+SHIFT, F, fullscreen, 0", - expected: &HyprlandKeyBinding{ - Mods: []string{"SUPER", "SHIFT"}, - Key: "F", - Dispatcher: "fullscreen", - Params: "0", - Comment: "Toggle fullscreen", - }, - }, - { - name: "keybind_no_mods", - line: "bind = , Print, exec, screenshot", - expected: &HyprlandKeyBinding{ - Mods: []string{}, - Key: "Print", - Dispatcher: "exec", - Params: "screenshot", - Comment: "screenshot", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - parser := NewHyprlandParser("") - parser.contentLines = []string{tt.line} - result := parser.getKeybindAtLine(0) - - if tt.expected == nil { - if result != nil { - t.Errorf("expected nil, got %+v", result) - } - return - } - - if result == nil { - t.Errorf("expected %+v, got nil", tt.expected) - return - } - - if result.Key != tt.expected.Key { - t.Errorf("Key = %q, want %q", result.Key, tt.expected.Key) - } - if result.Dispatcher != tt.expected.Dispatcher { - t.Errorf("Dispatcher = %q, want %q", result.Dispatcher, tt.expected.Dispatcher) - } - if result.Params != tt.expected.Params { - t.Errorf("Params = %q, want %q", result.Params, tt.expected.Params) - } - if result.Comment != tt.expected.Comment { - t.Errorf("Comment = %q, want %q", result.Comment, tt.expected.Comment) - } - if len(result.Mods) != len(tt.expected.Mods) { - t.Errorf("Mods length = %d, want %d", len(result.Mods), len(tt.expected.Mods)) - } else { - for i := range result.Mods { - if result.Mods[i] != tt.expected.Mods[i] { - t.Errorf("Mods[%d] = %q, want %q", i, result.Mods[i], tt.expected.Mods[i]) - } - } - } - }) - } -} - -func TestHyprlandParseKeysWithSections(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "hyprland.conf") - - content := `##! Window Management -bind = SUPER, Q, killactive -bind = SUPER, F, fullscreen, 0 - -###! Movement -bind = SUPER, left, movefocus, l -bind = SUPER, right, movefocus, r - -##! Applications -bind = SUPER, T, exec, kitty # Terminal -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseHyprlandKeys(tmpDir) - if err != nil { - t.Fatalf("ParseHyprlandKeys failed: %v", err) - } - - if len(section.Children) != 2 { - t.Errorf("Expected 2 top-level sections, got %d", len(section.Children)) - } - - if len(section.Children) >= 1 { - windowMgmt := section.Children[0] - if windowMgmt.Name != "Window Management" { - t.Errorf("First section name = %q, want %q", windowMgmt.Name, "Window Management") - } - if len(windowMgmt.Keybinds) != 2 { - t.Errorf("Window Management keybinds = %d, want 2", len(windowMgmt.Keybinds)) - } - - if len(windowMgmt.Children) != 1 { - t.Errorf("Window Management children = %d, want 1", len(windowMgmt.Children)) - } else { - movement := windowMgmt.Children[0] - if movement.Name != "Movement" { - t.Errorf("Movement section name = %q, want %q", movement.Name, "Movement") - } - if len(movement.Keybinds) != 2 { - t.Errorf("Movement keybinds = %d, want 2", len(movement.Keybinds)) - } - } - } - - if len(section.Children) >= 2 { - apps := section.Children[1] - if apps.Name != "Applications" { - t.Errorf("Second section name = %q, want %q", apps.Name, "Applications") - } - if len(apps.Keybinds) != 1 { - t.Errorf("Applications keybinds = %d, want 1", len(apps.Keybinds)) - } - if len(apps.Keybinds) > 0 && apps.Keybinds[0].Comment != "Terminal" { - t.Errorf("Applications keybind comment = %q, want %q", apps.Keybinds[0].Comment, "Terminal") - } - } -} - -func TestHyprlandParseKeysWithCommentBinds(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "test.conf") - - content := `#/# = SUPER, A, exec, app1 -bind = SUPER, B, exec, app2 -#/# = SUPER, C, exec, app3 # Custom comment -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseHyprlandKeys(tmpDir) - if err != nil { - t.Fatalf("ParseHyprlandKeys failed: %v", err) - } - - if len(section.Keybinds) != 3 { - t.Errorf("Expected 3 keybinds, got %d", len(section.Keybinds)) - } - - if len(section.Keybinds) > 0 && section.Keybinds[0].Key != "A" { - t.Errorf("First keybind key = %q, want %q", section.Keybinds[0].Key, "A") - } - if len(section.Keybinds) > 1 && section.Keybinds[1].Key != "B" { - t.Errorf("Second keybind key = %q, want %q", section.Keybinds[1].Key, "B") - } - if len(section.Keybinds) > 2 && section.Keybinds[2].Comment != "Custom comment" { - t.Errorf("Third keybind comment = %q, want %q", section.Keybinds[2].Comment, "Custom comment") - } -} - -func TestHyprlandReadContentMultipleFiles(t *testing.T) { - tmpDir := t.TempDir() - - file1 := filepath.Join(tmpDir, "a.conf") - file2 := filepath.Join(tmpDir, "b.conf") - - content1 := "bind = SUPER, Q, killactive\n" - content2 := "bind = SUPER, T, exec, kitty\n" - - if err := os.WriteFile(file1, []byte(content1), 0o644); err != nil { - t.Fatalf("Failed to write file1: %v", err) - } - if err := os.WriteFile(file2, []byte(content2), 0o644); err != nil { - t.Fatalf("Failed to write file2: %v", err) - } - - parser := NewHyprlandParser("") - if err := parser.ReadContent(tmpDir); err != nil { - t.Fatalf("ReadContent failed: %v", err) - } - - section := parser.ParseKeys() - if len(section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds from multiple files, got %d", len(section.Keybinds)) - } -} - -func TestHyprlandReadContentErrors(t *testing.T) { - tests := []struct { - name string - path string - }{ - { - name: "nonexistent_directory", - path: "/nonexistent/path/that/does/not/exist", - }, - { - name: "empty_directory", - path: t.TempDir(), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := ParseHyprlandKeys(tt.path) - if err == nil { - t.Error("Expected error, got nil") - } - }) - } -} - -func TestHyprlandReadContentWithTildeExpansion(t *testing.T) { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Skip("Cannot get home directory") - } - - tmpSubdir := filepath.Join(homeDir, ".config", "test-hypr-"+t.Name()) - if err := os.MkdirAll(tmpSubdir, 0o755); err != nil { - t.Skip("Cannot create test directory in home") - } - defer os.RemoveAll(tmpSubdir) - - configFile := filepath.Join(tmpSubdir, "test.conf") - if err := os.WriteFile(configFile, []byte("bind = SUPER, Q, killactive\n"), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - relPath, err := filepath.Rel(homeDir, tmpSubdir) - if err != nil { - t.Skip("Cannot create relative path") - } - - parser := NewHyprlandParser("") - tildePathMatch := "~/" + relPath - err = parser.ReadContent(tildePathMatch) - - if err != nil { - t.Errorf("ReadContent with tilde path failed: %v", err) - } -} - -func TestHyprlandKeybindWithParamsContainingCommas(t *testing.T) { - parser := NewHyprlandParser("") - parser.contentLines = []string{"bind = SUPER, R, exec, notify-send 'Title' 'Message, with comma'"} - - result := parser.getKeybindAtLine(0) - - if result == nil { - t.Fatal("Expected keybind, got nil") - } - - expected := "notify-send 'Title' 'Message, with comma'" - if result.Params != expected { - t.Errorf("Params = %q, want %q", result.Params, expected) - } -} - -func TestHyprlandEmptyAndCommentLines(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "test.conf") - - content := ` -# This is a comment -bind = SUPER, Q, killactive - -# Another comment - -bind = SUPER, T, exec, kitty -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseHyprlandKeys(tmpDir) - if err != nil { - t.Fatalf("ParseHyprlandKeys failed: %v", err) - } - - if len(section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds (comments ignored), got %d", len(section.Keybinds)) - } -} - -func TestExtractBindFlags(t *testing.T) { - tests := []struct { - bindType string - expected string - }{ - {"bind", ""}, - {"binde", "e"}, - {"bindl", "l"}, - {"bindr", "r"}, - {"bindd", "d"}, - {"bindo", "o"}, - {"bindel", "el"}, - {"bindler", "ler"}, - {"bindem", "em"}, - {" bind ", ""}, - {" binde ", "e"}, - {"notbind", ""}, - {"", ""}, - } - - for _, tt := range tests { - t.Run(tt.bindType, func(t *testing.T) { - result := extractBindFlags(tt.bindType) - if result != tt.expected { - t.Errorf("extractBindFlags(%q) = %q, want %q", tt.bindType, result, tt.expected) - } - }) - } -} - -func TestHyprlandBindFlags(t *testing.T) { - tests := []struct { - name string - line string - expectedFlags string - expectedKey string - expectedDisp string - expectedDesc string - }{ - { - name: "regular bind", - line: "bind = SUPER, Q, killactive", - expectedFlags: "", - expectedKey: "Q", - expectedDisp: "killactive", - expectedDesc: "Close window", - }, - { - name: "binde (repeat on hold)", - line: "binde = , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+", - expectedFlags: "e", - expectedKey: "XF86AudioRaiseVolume", - expectedDisp: "exec", - expectedDesc: "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+", - }, - { - name: "bindl (locked/inhibitor bypass)", - line: "bindl = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-", - expectedFlags: "l", - expectedKey: "XF86AudioLowerVolume", - expectedDisp: "exec", - expectedDesc: "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-", - }, - { - name: "bindr (release trigger)", - line: "bindr = SUPER, SUPER_L, exec, pkill wofi || wofi", - expectedFlags: "r", - expectedKey: "SUPER_L", - expectedDisp: "exec", - expectedDesc: "pkill wofi || wofi", - }, - { - name: "bindd (description)", - line: "bindd = SUPER, Q, Open my favourite terminal, exec, kitty", - expectedFlags: "d", - expectedKey: "Q", - expectedDisp: "exec", - expectedDesc: "Open my favourite terminal", - }, - { - name: "bindo (long press)", - line: "bindo = SUPER, XF86AudioNext, exec, playerctl next", - expectedFlags: "o", - expectedKey: "XF86AudioNext", - expectedDisp: "exec", - expectedDesc: "playerctl next", - }, - { - name: "bindel (combined flags)", - line: "bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+", - expectedFlags: "el", - expectedKey: "XF86AudioRaiseVolume", - expectedDisp: "exec", - expectedDesc: "wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - parser := NewHyprlandParser("") - parser.contentLines = []string{tt.line} - result := parser.getKeybindAtLine(0) - - if result == nil { - t.Fatal("Expected keybind, got nil") - } - - if result.Flags != tt.expectedFlags { - t.Errorf("Flags = %q, want %q", result.Flags, tt.expectedFlags) - } - if result.Key != tt.expectedKey { - t.Errorf("Key = %q, want %q", result.Key, tt.expectedKey) - } - if result.Dispatcher != tt.expectedDisp { - t.Errorf("Dispatcher = %q, want %q", result.Dispatcher, tt.expectedDisp) - } - if result.Comment != tt.expectedDesc { - t.Errorf("Comment = %q, want %q", result.Comment, tt.expectedDesc) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_test.go deleted file mode 100644 index 90dfe97..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/hyprland_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestNewHyprlandProvider(t *testing.T) { - t.Run("custom path", func(t *testing.T) { - p := NewHyprlandProvider("/custom/path") - if p == nil { - t.Fatal("NewHyprlandProvider returned nil") - } - if p.configPath != "/custom/path" { - t.Errorf("configPath = %q, want %q", p.configPath, "/custom/path") - } - }) - - t.Run("empty path defaults", func(t *testing.T) { - p := NewHyprlandProvider("") - if p == nil { - t.Fatal("NewHyprlandProvider returned nil") - } - configDir, err := os.UserConfigDir() - if err != nil { - t.Fatalf("UserConfigDir failed: %v", err) - } - expected := filepath.Join(configDir, "hypr") - if p.configPath != expected { - t.Errorf("configPath = %q, want %q", p.configPath, expected) - } - }) -} - -func TestHyprlandProviderName(t *testing.T) { - p := NewHyprlandProvider("") - if p.Name() != "hyprland" { - t.Errorf("Name() = %q, want %q", p.Name(), "hyprland") - } -} - -func TestHyprlandProviderGetCheatSheet(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "hyprland.conf") - - content := `##! Window Management -bind = SUPER, Q, killactive -bind = SUPER, F, fullscreen, 0 - -###! Movement -bind = SUPER, left, movefocus, l -bind = SUPER, right, movefocus, r - -##! Applications -bind = SUPER, T, exec, kitty # Terminal -bind = SUPER, 1, workspace, 1 -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - p := NewHyprlandProvider(tmpDir) - sheet, err := p.GetCheatSheet() - - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - if sheet.Title != "Hyprland Keybinds" { - t.Errorf("Title = %q, want %q", sheet.Title, "Hyprland Keybinds") - } - - if sheet.Provider != "hyprland" { - t.Errorf("Provider = %q, want %q", sheet.Provider, "hyprland") - } - - if len(sheet.Binds) == 0 { - t.Error("expected categorized bindings, got none") - } - - if windowBinds, ok := sheet.Binds["Window"]; !ok || len(windowBinds) == 0 { - t.Error("expected Window category with bindings") - } - - if execBinds, ok := sheet.Binds["Execute"]; !ok || len(execBinds) == 0 { - t.Error("expected Execute category with bindings") - } - - if wsBinds, ok := sheet.Binds["Workspace"]; !ok || len(wsBinds) == 0 { - t.Error("expected Workspace category with bindings") - } -} - -func TestHyprlandProviderGetCheatSheetError(t *testing.T) { - p := NewHyprlandProvider("/nonexistent/path") - _, err := p.GetCheatSheet() - - if err == nil { - t.Error("expected error for nonexistent path, got nil") - } -} - -func TestFormatKey(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "hyprland.conf") - - tests := []struct { - name string - content string - expected string - category string - }{ - { - name: "single mod", - content: "bind = SUPER, Q, killactive", - expected: "SUPER+Q", - category: "Window", - }, - { - name: "multiple mods", - content: "bind = SUPER+SHIFT, F, fullscreen, 0", - expected: "SUPER+SHIFT+F", - category: "Window", - }, - { - name: "no mods", - content: "bind = , Print, exec, screenshot", - expected: "Print", - category: "Execute", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := os.WriteFile(configFile, []byte(tt.content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - p := NewHyprlandProvider(tmpDir) - sheet, err := p.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - categoryBinds, ok := sheet.Binds[tt.category] - if !ok || len(categoryBinds) == 0 { - t.Fatalf("expected binds in category %q", tt.category) - } - - if categoryBinds[0].Key != tt.expected { - t.Errorf("Key = %q, want %q", categoryBinds[0].Key, tt.expected) - } - }) - } -} - -func TestDescriptionFallback(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "hyprland.conf") - - tests := []struct { - name string - content string - wantDesc string - }{ - { - name: "autogenerated description for known dispatcher", - content: "bind = SUPER, Q, killactive", - wantDesc: "Close window", - }, - { - name: "custom comment overrides autogeneration", - content: "bind = SUPER, T, exec, kitty # Open terminal", - wantDesc: "Open terminal", - }, - { - name: "fallback for unknown dispatcher without params", - content: "bind = SUPER, W, unknowndispatcher", - wantDesc: "unknowndispatcher", - }, - { - name: "fallback for unknown dispatcher with params", - content: "bind = SUPER, X, customdispatcher, arg1", - wantDesc: "customdispatcher arg1", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := os.WriteFile(configFile, []byte(tt.content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - p := NewHyprlandProvider(tmpDir) - sheet, err := p.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - found := false - for _, binds := range sheet.Binds { - for _, bind := range binds { - if bind.Description == tt.wantDesc { - found = true - break - } - } - if found { - break - } - } - - if !found { - t.Errorf("expected description %q not found in any bind", tt.wantDesc) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/jsonfile.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/jsonfile.go deleted file mode 100644 index 08cf449..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/jsonfile.go +++ /dev/null @@ -1,119 +0,0 @@ -package providers - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -type JSONFileProvider struct { - filePath string - name string -} - -func NewJSONFileProvider(filePath string) (*JSONFileProvider, error) { - if filePath == "" { - return nil, fmt.Errorf("file path cannot be empty") - } - - expandedPath, err := utils.ExpandPath(filePath) - if err != nil { - return nil, fmt.Errorf("failed to expand path: %w", err) - } - - name := filepath.Base(expandedPath) - name = name[:len(name)-len(filepath.Ext(name))] - - return &JSONFileProvider{ - filePath: expandedPath, - name: name, - }, nil -} - -func (j *JSONFileProvider) Name() string { - return j.name -} - -func (j *JSONFileProvider) GetCheatSheet() (*keybinds.CheatSheet, error) { - data, err := os.ReadFile(j.filePath) - if err != nil { - return nil, fmt.Errorf("failed to read file: %w", err) - } - - var rawData map[string]any - if err := json.Unmarshal(data, &rawData); err != nil { - return nil, fmt.Errorf("failed to parse JSON: %w", err) - } - - title, _ := rawData["title"].(string) - provider, _ := rawData["provider"].(string) - if provider == "" { - provider = j.name - } - - categorizedBinds := make(map[string][]keybinds.Keybind) - - bindsRaw, ok := rawData["binds"] - if !ok { - return nil, fmt.Errorf("missing 'binds' field") - } - - switch binds := bindsRaw.(type) { - case map[string]any: - for category, categoryBindsRaw := range binds { - categoryBindsList, ok := categoryBindsRaw.([]any) - if !ok { - continue - } - - var keybindsList []keybinds.Keybind - categoryBindsJSON, _ := json.Marshal(categoryBindsList) - if err := json.Unmarshal(categoryBindsJSON, &keybindsList); err != nil { - continue - } - - categorizedBinds[category] = keybindsList - } - - case []any: - flatBindsJSON, _ := json.Marshal(binds) - var flatBinds []struct { - Key string `json:"key"` - Description string `json:"desc"` - Action string `json:"action,omitempty"` - Category string `json:"cat,omitempty"` - Subcategory string `json:"subcat,omitempty"` - } - if err := json.Unmarshal(flatBindsJSON, &flatBinds); err != nil { - return nil, fmt.Errorf("failed to parse flat binds array: %w", err) - } - - for _, bind := range flatBinds { - category := bind.Category - if category == "" { - category = "Other" - } - - kb := keybinds.Keybind{ - Key: bind.Key, - Description: bind.Description, - Action: bind.Action, - Subcategory: bind.Subcategory, - } - categorizedBinds[category] = append(categorizedBinds[category], kb) - } - - default: - return nil, fmt.Errorf("'binds' must be either an object (categorized) or array (flat)") - } - - return &keybinds.CheatSheet{ - Title: title, - Provider: provider, - Binds: categorizedBinds, - }, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/jsonfile_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/jsonfile_test.go deleted file mode 100644 index ded4385..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/jsonfile_test.go +++ /dev/null @@ -1,281 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -func TestNewJSONFileProvider(t *testing.T) { - tmpDir := t.TempDir() - testFile := filepath.Join(tmpDir, "test.json") - - if err := os.WriteFile(testFile, []byte("{}"), 0o644); err != nil { - t.Fatalf("Failed to create test file: %v", err) - } - - tests := []struct { - name string - filePath string - expectError bool - wantName string - }{ - { - name: "valid file", - filePath: testFile, - expectError: false, - wantName: "test", - }, - { - name: "empty path", - filePath: "", - expectError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p, err := NewJSONFileProvider(tt.filePath) - - if tt.expectError { - if err == nil { - t.Error("expected error, got nil") - } - return - } - - if err != nil { - t.Errorf("unexpected error: %v", err) - return - } - - if p.Name() != tt.wantName { - t.Errorf("Name() = %q, want %q", p.Name(), tt.wantName) - } - }) - } -} - -func TestJSONFileProviderGetCheatSheet(t *testing.T) { - tmpDir := t.TempDir() - testFile := filepath.Join(tmpDir, "tmux.json") - - content := `{ - "title": "Tmux Binds", - "provider": "tmux", - "binds": { - "Pane": [ - { - "key": "Ctrl+Alt+J", - "desc": "Resize split downward", - "subcat": "Sizing" - }, - { - "key": "Ctrl+K", - "desc": "Move Focus Up", - "subcat": "Navigation" - } - ] - } -}` - - if err := os.WriteFile(testFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test file: %v", err) - } - - p, err := NewJSONFileProvider(testFile) - if err != nil { - t.Fatalf("NewJSONFileProvider failed: %v", err) - } - - sheet, err := p.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - if sheet.Title != "Tmux Binds" { - t.Errorf("Title = %q, want %q", sheet.Title, "Tmux Binds") - } - - if sheet.Provider != "tmux" { - t.Errorf("Provider = %q, want %q", sheet.Provider, "tmux") - } - - paneBinds, ok := sheet.Binds["Pane"] - if !ok { - t.Fatal("expected Pane category") - } - - if len(paneBinds) != 2 { - t.Errorf("len(Pane binds) = %d, want 2", len(paneBinds)) - } - - if len(paneBinds) > 0 { - bind := paneBinds[0] - if bind.Key != "Ctrl+Alt+J" { - t.Errorf("Pane[0].Key = %q, want %q", bind.Key, "Ctrl+Alt+J") - } - if bind.Description != "Resize split downward" { - t.Errorf("Pane[0].Description = %q, want %q", bind.Description, "Resize split downward") - } - if bind.Subcategory != "Sizing" { - t.Errorf("Pane[0].Subcategory = %q, want %q", bind.Subcategory, "Sizing") - } - } -} - -func TestJSONFileProviderGetCheatSheetNoProvider(t *testing.T) { - tmpDir := t.TempDir() - testFile := filepath.Join(tmpDir, "custom.json") - - content := `{ - "title": "Custom Binds", - "binds": {} -}` - - if err := os.WriteFile(testFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test file: %v", err) - } - - p, err := NewJSONFileProvider(testFile) - if err != nil { - t.Fatalf("NewJSONFileProvider failed: %v", err) - } - - sheet, err := p.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - if sheet.Provider != "custom" { - t.Errorf("Provider = %q, want %q (should default to filename)", sheet.Provider, "custom") - } -} - -func TestJSONFileProviderFlatArrayBackwardsCompat(t *testing.T) { - tmpDir := t.TempDir() - testFile := filepath.Join(tmpDir, "legacy.json") - - content := `{ - "title": "Legacy Format", - "provider": "legacy", - "binds": [ - { - "key": "Ctrl+S", - "desc": "Save file", - "cat": "File", - "subcat": "Operations" - }, - { - "key": "Ctrl+O", - "desc": "Open file", - "cat": "File" - }, - { - "key": "Ctrl+Q", - "desc": "Quit", - "subcat": "Exit" - } - ] -}` - - if err := os.WriteFile(testFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test file: %v", err) - } - - p, err := NewJSONFileProvider(testFile) - if err != nil { - t.Fatalf("NewJSONFileProvider failed: %v", err) - } - - sheet, err := p.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - fileBinds, ok := sheet.Binds["File"] - if !ok || len(fileBinds) != 2 { - t.Errorf("expected 2 binds in File category, got %d", len(fileBinds)) - } - - otherBinds, ok := sheet.Binds["Other"] - if !ok || len(otherBinds) != 1 { - t.Errorf("expected 1 bind in Other category (no cat specified), got %d", len(otherBinds)) - } - - if len(fileBinds) > 0 { - if fileBinds[0].Subcategory != "Operations" { - t.Errorf("expected subcategory %q, got %q", "Operations", fileBinds[0].Subcategory) - } - } -} - -func TestJSONFileProviderInvalidJSON(t *testing.T) { - tmpDir := t.TempDir() - testFile := filepath.Join(tmpDir, "invalid.json") - - if err := os.WriteFile(testFile, []byte("not valid json"), 0o644); err != nil { - t.Fatalf("Failed to write test file: %v", err) - } - - p, err := NewJSONFileProvider(testFile) - if err != nil { - t.Fatalf("NewJSONFileProvider failed: %v", err) - } - - _, err = p.GetCheatSheet() - if err == nil { - t.Error("expected error for invalid JSON, got nil") - } -} - -func TestJSONFileProviderNonexistentFile(t *testing.T) { - p, err := NewJSONFileProvider("/nonexistent/file.json") - if err != nil { - t.Fatalf("NewJSONFileProvider failed: %v", err) - } - - _, err = p.GetCheatSheet() - if err == nil { - t.Error("expected error for nonexistent file, got nil") - } -} - -func TestExpandPath(t *testing.T) { - home, err := os.UserHomeDir() - if err != nil { - t.Skip("Cannot get home directory") - } - - tests := []struct { - name string - input string - expected string - }{ - { - name: "tilde expansion", - input: "~/test", - expected: filepath.Join(home, "test"), - }, - { - name: "no expansion needed", - input: "/absolute/path", - expected: "/absolute/path", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := utils.ExpandPath(tt.input) - if err != nil { - t.Fatalf("expandPath failed: %v", err) - } - - if result != tt.expected { - t.Errorf("utils.ExpandPath(%q) = %q, want %q", tt.input, result, tt.expected) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc.go deleted file mode 100644 index 348ee7c..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc.go +++ /dev/null @@ -1,442 +0,0 @@ -package providers - -import ( - "fmt" - "os" - "path/filepath" - "sort" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -type MangoWCProvider struct { - configPath string - dmsBindsIncluded bool - parsed bool -} - -func NewMangoWCProvider(configPath string) *MangoWCProvider { - if configPath == "" { - configPath = defaultMangoWCConfigDir() - } - return &MangoWCProvider{ - configPath: configPath, - } -} - -func defaultMangoWCConfigDir() string { - configDir, err := os.UserConfigDir() - if err != nil { - return "" - } - return filepath.Join(configDir, "mango") -} - -func (m *MangoWCProvider) Name() string { - return "mangowc" -} - -func (m *MangoWCProvider) GetCheatSheet() (*keybinds.CheatSheet, error) { - result, err := ParseMangoWCKeysWithDMS(m.configPath) - if err != nil { - return nil, fmt.Errorf("failed to parse mangowc config: %w", err) - } - - m.dmsBindsIncluded = result.DMSBindsIncluded - m.parsed = true - - categorizedBinds := make(map[string][]keybinds.Keybind) - for _, kb := range result.Keybinds { - category := m.categorizeByCommand(kb.Command) - bind := m.convertKeybind(&kb, result.ConflictingConfigs) - categorizedBinds[category] = append(categorizedBinds[category], bind) - } - - sheet := &keybinds.CheatSheet{ - Title: "MangoWC Keybinds", - Provider: m.Name(), - Binds: categorizedBinds, - DMSBindsIncluded: result.DMSBindsIncluded, - } - - if result.DMSStatus != nil { - sheet.DMSStatus = &keybinds.DMSBindsStatus{ - Exists: result.DMSStatus.Exists, - Included: result.DMSStatus.Included, - IncludePosition: result.DMSStatus.IncludePosition, - TotalIncludes: result.DMSStatus.TotalIncludes, - BindsAfterDMS: result.DMSStatus.BindsAfterDMS, - Effective: result.DMSStatus.Effective, - OverriddenBy: result.DMSStatus.OverriddenBy, - StatusMessage: result.DMSStatus.StatusMessage, - } - } - - return sheet, nil -} - -func (m *MangoWCProvider) HasDMSBindsIncluded() bool { - if m.parsed { - return m.dmsBindsIncluded - } - - result, err := ParseMangoWCKeysWithDMS(m.configPath) - if err != nil { - return false - } - - m.dmsBindsIncluded = result.DMSBindsIncluded - m.parsed = true - return m.dmsBindsIncluded -} - -func (m *MangoWCProvider) categorizeByCommand(command string) string { - switch { - case strings.Contains(command, "mon"): - return "Monitor" - case command == "toggleoverview": - return "Overview" - case command == "toggle_scratchpad": - return "Scratchpad" - case strings.Contains(command, "layout") || strings.Contains(command, "proportion"): - return "Layout" - case strings.Contains(command, "gaps"): - return "Gaps" - case strings.Contains(command, "view") || strings.Contains(command, "tag"): - return "Tags" - case command == "focusstack" || - command == "focusdir" || - command == "exchange_client" || - command == "killclient" || - command == "togglefloating" || - command == "togglefullscreen" || - command == "togglefakefullscreen" || - command == "togglemaximizescreen" || - command == "toggleglobal" || - command == "toggleoverlay" || - command == "minimized" || - command == "restore_minimized" || - command == "movewin" || - command == "resizewin": - return "Window" - case command == "spawn" || command == "spawn_shell": - return "Execute" - case command == "quit" || command == "reload_config": - return "System" - default: - return "Other" - } -} - -func (m *MangoWCProvider) convertKeybind(kb *MangoWCKeyBinding, conflicts map[string]*MangoWCKeyBinding) keybinds.Keybind { - keyStr := m.formatKey(kb) - rawAction := m.formatRawAction(kb.Command, kb.Params) - desc := kb.Comment - - if desc == "" { - desc = rawAction - } - - source := "config" - if strings.Contains(kb.Source, "dms/binds.conf") || strings.Contains(kb.Source, "dms"+string(filepath.Separator)+"binds.conf") { - source = "dms" - } - - bind := keybinds.Keybind{ - Key: keyStr, - Description: desc, - Action: rawAction, - Source: source, - } - - if source == "dms" && conflicts != nil { - normalizedKey := strings.ToLower(keyStr) - if conflictKb, ok := conflicts[normalizedKey]; ok { - bind.Conflict = &keybinds.Keybind{ - Key: keyStr, - Description: conflictKb.Comment, - Action: m.formatRawAction(conflictKb.Command, conflictKb.Params), - Source: "config", - } - } - } - - return bind -} - -func (m *MangoWCProvider) formatRawAction(command, params string) string { - if params != "" { - return command + " " + params - } - return command -} - -func (m *MangoWCProvider) formatKey(kb *MangoWCKeyBinding) string { - parts := make([]string, 0, len(kb.Mods)+1) - parts = append(parts, kb.Mods...) - parts = append(parts, kb.Key) - return strings.Join(parts, "+") -} - -func (m *MangoWCProvider) GetOverridePath() string { - expanded, err := utils.ExpandPath(m.configPath) - if err != nil { - return filepath.Join(m.configPath, "dms", "binds.conf") - } - return filepath.Join(expanded, "dms", "binds.conf") -} - -func (m *MangoWCProvider) validateAction(action string) error { - action = strings.TrimSpace(action) - switch { - case action == "": - return fmt.Errorf("action cannot be empty") - case action == "spawn" || action == "spawn ": - return fmt.Errorf("spawn command requires arguments") - case action == "spawn_shell" || action == "spawn_shell ": - return fmt.Errorf("spawn_shell command requires arguments") - case strings.HasPrefix(action, "spawn "): - rest := strings.TrimSpace(strings.TrimPrefix(action, "spawn ")) - if rest == "" { - return fmt.Errorf("spawn command requires arguments") - } - case strings.HasPrefix(action, "spawn_shell "): - rest := strings.TrimSpace(strings.TrimPrefix(action, "spawn_shell ")) - if rest == "" { - return fmt.Errorf("spawn_shell command requires arguments") - } - } - return nil -} - -func (m *MangoWCProvider) SetBind(key, action, description string, options map[string]any) error { - if err := m.validateAction(action); err != nil { - return err - } - - overridePath := m.GetOverridePath() - - if err := os.MkdirAll(filepath.Dir(overridePath), 0o755); err != nil { - return fmt.Errorf("failed to create dms directory: %w", err) - } - - existingBinds, err := m.loadOverrideBinds() - if err != nil { - existingBinds = make(map[string]*mangowcOverrideBind) - } - - normalizedKey := strings.ToLower(key) - existingBinds[normalizedKey] = &mangowcOverrideBind{ - Key: key, - Action: action, - Description: description, - Options: options, - } - - return m.writeOverrideBinds(existingBinds) -} - -func (m *MangoWCProvider) RemoveBind(key string) error { - existingBinds, err := m.loadOverrideBinds() - if err != nil { - return nil - } - - normalizedKey := strings.ToLower(key) - delete(existingBinds, normalizedKey) - return m.writeOverrideBinds(existingBinds) -} - -type mangowcOverrideBind struct { - Key string - Action string - Description string - Options map[string]any -} - -func (m *MangoWCProvider) loadOverrideBinds() (map[string]*mangowcOverrideBind, error) { - overridePath := m.GetOverridePath() - binds := make(map[string]*mangowcOverrideBind) - - data, err := os.ReadFile(overridePath) - if os.IsNotExist(err) { - return binds, nil - } - if err != nil { - return nil, err - } - - lines := strings.Split(string(data), "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - if line == "" || strings.HasPrefix(line, "#") { - continue - } - - if !strings.HasPrefix(line, "bind") { - continue - } - - parts := strings.SplitN(line, "=", 2) - if len(parts) < 2 { - continue - } - - content := strings.TrimSpace(parts[1]) - commentParts := strings.SplitN(content, "#", 2) - bindContent := strings.TrimSpace(commentParts[0]) - - var comment string - if len(commentParts) > 1 { - comment = strings.TrimSpace(commentParts[1]) - } - - fields := strings.SplitN(bindContent, ",", 4) - if len(fields) < 3 { - continue - } - - mods := strings.TrimSpace(fields[0]) - keyName := strings.TrimSpace(fields[1]) - command := strings.TrimSpace(fields[2]) - - var params string - if len(fields) > 3 { - params = strings.TrimSpace(fields[3]) - } - - keyStr := m.buildKeyString(mods, keyName) - normalizedKey := strings.ToLower(keyStr) - action := command - if params != "" { - action = command + " " + params - } - - binds[normalizedKey] = &mangowcOverrideBind{ - Key: keyStr, - Action: action, - Description: comment, - } - } - - return binds, nil -} - -func (m *MangoWCProvider) buildKeyString(mods, key string) string { - if mods == "" || strings.EqualFold(mods, "none") { - return key - } - - modList := strings.FieldsFunc(mods, func(r rune) bool { - return r == '+' || r == ' ' - }) - - parts := append(modList, key) - return strings.Join(parts, "+") -} - -func (m *MangoWCProvider) getBindSortPriority(action string) int { - switch { - case strings.HasPrefix(action, "spawn") && strings.Contains(action, "dms"): - return 0 - case strings.Contains(action, "view") || strings.Contains(action, "tag"): - return 1 - case strings.Contains(action, "focus") || strings.Contains(action, "exchange") || - strings.Contains(action, "resize") || strings.Contains(action, "move"): - return 2 - case strings.Contains(action, "mon"): - return 3 - case strings.HasPrefix(action, "spawn"): - return 4 - case action == "quit" || action == "reload_config": - return 5 - default: - return 6 - } -} - -func (m *MangoWCProvider) writeOverrideBinds(binds map[string]*mangowcOverrideBind) error { - overridePath := m.GetOverridePath() - content := m.generateBindsContent(binds) - return os.WriteFile(overridePath, []byte(content), 0o644) -} - -func (m *MangoWCProvider) generateBindsContent(binds map[string]*mangowcOverrideBind) string { - if len(binds) == 0 { - return "" - } - - bindList := make([]*mangowcOverrideBind, 0, len(binds)) - for _, bind := range binds { - bindList = append(bindList, bind) - } - - sort.Slice(bindList, func(i, j int) bool { - pi, pj := m.getBindSortPriority(bindList[i].Action), m.getBindSortPriority(bindList[j].Action) - if pi != pj { - return pi < pj - } - return bindList[i].Key < bindList[j].Key - }) - - var sb strings.Builder - for _, bind := range bindList { - m.writeBindLine(&sb, bind) - } - - return sb.String() -} - -func (m *MangoWCProvider) writeBindLine(sb *strings.Builder, bind *mangowcOverrideBind) { - mods, key := m.parseKeyString(bind.Key) - command, params := m.parseAction(bind.Action) - - sb.WriteString("bind=") - if mods == "" { - sb.WriteString("none") - } else { - sb.WriteString(mods) - } - sb.WriteString(",") - sb.WriteString(key) - sb.WriteString(",") - sb.WriteString(command) - - if params != "" { - sb.WriteString(",") - sb.WriteString(params) - } - - if bind.Description != "" { - sb.WriteString(" # ") - sb.WriteString(bind.Description) - } - - sb.WriteString("\n") -} - -func (m *MangoWCProvider) parseKeyString(keyStr string) (mods, key string) { - parts := strings.Split(keyStr, "+") - switch len(parts) { - case 0: - return "", keyStr - case 1: - return "", parts[0] - default: - return strings.Join(parts[:len(parts)-1], "+"), parts[len(parts)-1] - } -} - -func (m *MangoWCProvider) parseAction(action string) (command, params string) { - parts := strings.SplitN(action, " ", 2) - switch len(parts) { - case 0: - return action, "" - case 1: - return parts[0], "" - default: - return parts[0], parts[1] - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_parser.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_parser.go deleted file mode 100644 index 5dac4b6..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_parser.go +++ /dev/null @@ -1,613 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -const ( - MangoWCHideComment = "[hidden]" -) - -var MangoWCModSeparators = []rune{'+', ' '} - -type MangoWCKeyBinding struct { - Mods []string `json:"mods"` - Key string `json:"key"` - Command string `json:"command"` - Params string `json:"params"` - Comment string `json:"comment"` - Source string `json:"source"` -} - -type MangoWCParser struct { - contentLines []string - readingLine int - configDir string - currentSource string - dmsBindsExists bool - dmsBindsIncluded bool - includeCount int - dmsIncludePos int - bindsAfterDMS int - dmsBindKeys map[string]bool - configBindKeys map[string]bool - conflictingConfigs map[string]*MangoWCKeyBinding - bindMap map[string]*MangoWCKeyBinding - bindOrder []string - processedFiles map[string]bool - dmsProcessed bool -} - -func NewMangoWCParser(configDir string) *MangoWCParser { - return &MangoWCParser{ - contentLines: []string{}, - readingLine: 0, - configDir: configDir, - dmsIncludePos: -1, - dmsBindKeys: make(map[string]bool), - configBindKeys: make(map[string]bool), - conflictingConfigs: make(map[string]*MangoWCKeyBinding), - bindMap: make(map[string]*MangoWCKeyBinding), - bindOrder: []string{}, - processedFiles: make(map[string]bool), - } -} - -func (p *MangoWCParser) ReadContent(path string) error { - expandedPath, err := utils.ExpandPath(path) - if err != nil { - return err - } - - info, err := os.Stat(expandedPath) - if err != nil { - return err - } - - var files []string - if info.IsDir() { - confFiles, err := filepath.Glob(filepath.Join(expandedPath, "*.conf")) - if err != nil { - return err - } - if len(confFiles) == 0 { - return os.ErrNotExist - } - files = confFiles - } else { - files = []string{expandedPath} - } - - var combinedContent []string - for _, file := range files { - if fileInfo, err := os.Stat(file); err == nil && fileInfo.Mode().IsRegular() { - data, err := os.ReadFile(file) - if err == nil { - combinedContent = append(combinedContent, string(data)) - } - } - } - - if len(combinedContent) == 0 { - return os.ErrNotExist - } - - fullContent := strings.Join(combinedContent, "\n") - p.contentLines = strings.Split(fullContent, "\n") - return nil -} - -func mangowcAutogenerateComment(command, params string) string { - switch command { - case "spawn", "spawn_shell": - return params - case "killclient": - return "Close window" - case "quit": - return "Exit MangoWC" - case "reload_config": - return "Reload configuration" - case "focusstack": - if params == "next" { - return "Focus next window" - } - if params == "prev" { - return "Focus previous window" - } - return "Focus stack " + params - case "focusdir": - dirMap := map[string]string{ - "left": "left", - "right": "right", - "up": "up", - "down": "down", - } - if dir, ok := dirMap[params]; ok { - return "Focus " + dir - } - return "Focus " + params - case "exchange_client": - dirMap := map[string]string{ - "left": "left", - "right": "right", - "up": "up", - "down": "down", - } - if dir, ok := dirMap[params]; ok { - return "Swap window " + dir - } - return "Swap window " + params - case "togglefloating": - return "Float/unfloat window" - case "togglefullscreen": - return "Toggle fullscreen" - case "togglefakefullscreen": - return "Toggle fake fullscreen" - case "togglemaximizescreen": - return "Toggle maximize" - case "toggleglobal": - return "Toggle global" - case "toggleoverview": - return "Toggle overview" - case "toggleoverlay": - return "Toggle overlay" - case "minimized": - return "Minimize window" - case "restore_minimized": - return "Restore minimized" - case "toggle_scratchpad": - return "Toggle scratchpad" - case "setlayout": - return "Set layout " + params - case "switch_layout": - return "Switch layout" - case "view": - parts := strings.Split(params, ",") - if len(parts) > 0 { - return "View tag " + parts[0] - } - return "View tag" - case "tag": - parts := strings.Split(params, ",") - if len(parts) > 0 { - return "Move to tag " + parts[0] - } - return "Move to tag" - case "toggleview": - parts := strings.Split(params, ",") - if len(parts) > 0 { - return "Toggle tag " + parts[0] - } - return "Toggle tag" - case "viewtoleft", "viewtoleft_have_client": - return "View left tag" - case "viewtoright", "viewtoright_have_client": - return "View right tag" - case "tagtoleft": - return "Move to left tag" - case "tagtoright": - return "Move to right tag" - case "focusmon": - return "Focus monitor " + params - case "tagmon": - return "Move to monitor " + params - case "incgaps": - if strings.HasPrefix(params, "-") { - return "Decrease gaps" - } - return "Increase gaps" - case "togglegaps": - return "Toggle gaps" - case "movewin": - return "Move window by " + params - case "resizewin": - return "Resize window by " + params - case "set_proportion": - return "Set proportion " + params - case "switch_proportion_preset": - return "Switch proportion preset" - default: - return "" - } -} - -func (p *MangoWCParser) getKeybindAtLine(lineNumber int) *MangoWCKeyBinding { - if lineNumber >= len(p.contentLines) { - return nil - } - - line := p.contentLines[lineNumber] - - bindMatch := regexp.MustCompile(`^(bind[lsr]*)\s*=\s*(.+)$`) - matches := bindMatch.FindStringSubmatch(line) - if len(matches) < 3 { - return nil - } - - bindType := matches[1] - content := matches[2] - - parts := strings.SplitN(content, "#", 2) - keys := parts[0] - - var comment string - if len(parts) > 1 { - comment = strings.TrimSpace(parts[1]) - } - - if strings.HasPrefix(comment, MangoWCHideComment) { - return nil - } - - keyFields := strings.SplitN(keys, ",", 4) - if len(keyFields) < 3 { - return nil - } - - mods := strings.TrimSpace(keyFields[0]) - key := strings.TrimSpace(keyFields[1]) - command := strings.TrimSpace(keyFields[2]) - - var params string - if len(keyFields) > 3 { - params = strings.TrimSpace(keyFields[3]) - } - - if comment == "" { - comment = mangowcAutogenerateComment(command, params) - } - - var modList []string - if mods != "" && !strings.EqualFold(mods, "none") { - modstring := mods + string(MangoWCModSeparators[0]) - p := 0 - for index, char := range modstring { - isModSep := false - for _, sep := range MangoWCModSeparators { - if char == sep { - isModSep = true - break - } - } - if isModSep { - if index-p > 1 { - modList = append(modList, modstring[p:index]) - } - p = index + 1 - } - } - } - - _ = bindType - - return &MangoWCKeyBinding{ - Mods: modList, - Key: key, - Command: command, - Params: params, - Comment: comment, - } -} - -func (p *MangoWCParser) ParseKeys() []MangoWCKeyBinding { - var keybinds []MangoWCKeyBinding - - for lineNumber := 0; lineNumber < len(p.contentLines); lineNumber++ { - line := p.contentLines[lineNumber] - if line == "" || strings.HasPrefix(strings.TrimSpace(line), "#") { - continue - } - - if !strings.HasPrefix(strings.TrimSpace(line), "bind") { - continue - } - - keybind := p.getKeybindAtLine(lineNumber) - if keybind != nil { - keybinds = append(keybinds, *keybind) - } - } - - return keybinds -} - -func ParseMangoWCKeys(path string) ([]MangoWCKeyBinding, error) { - parser := NewMangoWCParser(path) - if err := parser.ReadContent(path); err != nil { - return nil, err - } - return parser.ParseKeys(), nil -} - -type MangoWCParseResult struct { - Keybinds []MangoWCKeyBinding - DMSBindsIncluded bool - DMSStatus *MangoWCDMSStatus - ConflictingConfigs map[string]*MangoWCKeyBinding -} - -type MangoWCDMSStatus struct { - Exists bool - Included bool - IncludePosition int - TotalIncludes int - BindsAfterDMS int - Effective bool - OverriddenBy int - StatusMessage string -} - -func (p *MangoWCParser) buildDMSStatus() *MangoWCDMSStatus { - status := &MangoWCDMSStatus{ - Exists: p.dmsBindsExists, - Included: p.dmsBindsIncluded, - IncludePosition: p.dmsIncludePos, - TotalIncludes: p.includeCount, - BindsAfterDMS: p.bindsAfterDMS, - } - - switch { - case !p.dmsBindsExists: - status.Effective = false - status.StatusMessage = "dms/binds.conf does not exist" - case !p.dmsBindsIncluded: - status.Effective = false - status.StatusMessage = "dms/binds.conf is not sourced in config" - case p.bindsAfterDMS > 0: - status.Effective = true - status.OverriddenBy = p.bindsAfterDMS - status.StatusMessage = "Some DMS binds may be overridden by config binds" - default: - status.Effective = true - status.StatusMessage = "DMS binds are active" - } - - return status -} - -func (p *MangoWCParser) formatBindKey(kb *MangoWCKeyBinding) string { - parts := make([]string, 0, len(kb.Mods)+1) - parts = append(parts, kb.Mods...) - parts = append(parts, kb.Key) - return strings.Join(parts, "+") -} - -func (p *MangoWCParser) normalizeKey(key string) string { - return strings.ToLower(key) -} - -func (p *MangoWCParser) addBind(kb *MangoWCKeyBinding) { - key := p.formatBindKey(kb) - normalizedKey := p.normalizeKey(key) - isDMSBind := strings.Contains(kb.Source, "dms/binds.conf") || strings.Contains(kb.Source, "dms"+string(os.PathSeparator)+"binds.conf") - - if isDMSBind { - p.dmsBindKeys[normalizedKey] = true - } else if p.dmsBindKeys[normalizedKey] { - p.bindsAfterDMS++ - p.conflictingConfigs[normalizedKey] = kb - p.configBindKeys[normalizedKey] = true - return - } else { - p.configBindKeys[normalizedKey] = true - } - - if _, exists := p.bindMap[normalizedKey]; !exists { - p.bindOrder = append(p.bindOrder, key) - } - p.bindMap[normalizedKey] = kb -} - -func (p *MangoWCParser) ParseWithDMS() ([]MangoWCKeyBinding, error) { - expandedDir, err := utils.ExpandPath(p.configDir) - if err != nil { - return nil, err - } - - dmsBindsPath := filepath.Join(expandedDir, "dms", "binds.conf") - if _, err := os.Stat(dmsBindsPath); err == nil { - p.dmsBindsExists = true - } - - mainConfig := filepath.Join(expandedDir, "config.conf") - if _, err := os.Stat(mainConfig); os.IsNotExist(err) { - mainConfig = filepath.Join(expandedDir, "mango.conf") - } - - _, err = p.parseFileWithSource(mainConfig) - if err != nil { - return nil, err - } - - if p.dmsBindsExists && !p.dmsProcessed { - p.parseDMSBindsDirectly(dmsBindsPath) - } - - var keybinds []MangoWCKeyBinding - for _, key := range p.bindOrder { - normalizedKey := p.normalizeKey(key) - if kb, exists := p.bindMap[normalizedKey]; exists { - keybinds = append(keybinds, *kb) - } - } - - return keybinds, nil -} - -func (p *MangoWCParser) parseFileWithSource(filePath string) ([]MangoWCKeyBinding, error) { - absPath, err := filepath.Abs(filePath) - if err != nil { - return nil, err - } - - if p.processedFiles[absPath] { - return nil, nil - } - p.processedFiles[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return nil, err - } - - prevSource := p.currentSource - p.currentSource = absPath - - var keybinds []MangoWCKeyBinding - lines := strings.Split(string(data), "\n") - - for lineNum, line := range lines { - trimmed := strings.TrimSpace(line) - - if strings.HasPrefix(trimmed, "source") { - p.handleSource(trimmed, filepath.Dir(absPath), &keybinds) - continue - } - - if !strings.HasPrefix(trimmed, "bind") { - continue - } - - kb := p.getKeybindAtLineContent(line, lineNum) - if kb == nil { - continue - } - kb.Source = p.currentSource - p.addBind(kb) - keybinds = append(keybinds, *kb) - } - - p.currentSource = prevSource - return keybinds, nil -} - -func (p *MangoWCParser) handleSource(line, baseDir string, keybinds *[]MangoWCKeyBinding) { - parts := strings.SplitN(line, "=", 2) - if len(parts) < 2 { - return - } - - sourcePath := strings.TrimSpace(parts[1]) - isDMSSource := sourcePath == "dms/binds.conf" || sourcePath == "./dms/binds.conf" || strings.HasSuffix(sourcePath, "/dms/binds.conf") - - p.includeCount++ - if isDMSSource { - p.dmsBindsIncluded = true - p.dmsIncludePos = p.includeCount - p.dmsProcessed = true - } - - expanded, err := utils.ExpandPath(sourcePath) - if err != nil { - return - } - - fullPath := expanded - if !filepath.IsAbs(expanded) { - fullPath = filepath.Join(baseDir, expanded) - } - - includedBinds, err := p.parseFileWithSource(fullPath) - if err != nil { - return - } - - *keybinds = append(*keybinds, includedBinds...) -} - -func (p *MangoWCParser) parseDMSBindsDirectly(dmsBindsPath string) []MangoWCKeyBinding { - keybinds, err := p.parseFileWithSource(dmsBindsPath) - if err != nil { - return nil - } - p.dmsProcessed = true - return keybinds -} - -func (p *MangoWCParser) getKeybindAtLineContent(line string, _ int) *MangoWCKeyBinding { - bindMatch := regexp.MustCompile(`^(bind[lsr]*)\s*=\s*(.+)$`) - matches := bindMatch.FindStringSubmatch(line) - if len(matches) < 3 { - return nil - } - - content := matches[2] - parts := strings.SplitN(content, "#", 2) - keys := parts[0] - - var comment string - if len(parts) > 1 { - comment = strings.TrimSpace(parts[1]) - } - - if strings.HasPrefix(comment, MangoWCHideComment) { - return nil - } - - keyFields := strings.SplitN(keys, ",", 4) - if len(keyFields) < 3 { - return nil - } - - mods := strings.TrimSpace(keyFields[0]) - key := strings.TrimSpace(keyFields[1]) - command := strings.TrimSpace(keyFields[2]) - - var params string - if len(keyFields) > 3 { - params = strings.TrimSpace(keyFields[3]) - } - - if comment == "" { - comment = mangowcAutogenerateComment(command, params) - } - - var modList []string - if mods != "" && !strings.EqualFold(mods, "none") { - modstring := mods + string(MangoWCModSeparators[0]) - idx := 0 - for index, char := range modstring { - isModSep := false - for _, sep := range MangoWCModSeparators { - if char == sep { - isModSep = true - break - } - } - if isModSep { - if index-idx > 1 { - modList = append(modList, modstring[idx:index]) - } - idx = index + 1 - } - } - } - - return &MangoWCKeyBinding{ - Mods: modList, - Key: key, - Command: command, - Params: params, - Comment: comment, - } -} - -func ParseMangoWCKeysWithDMS(path string) (*MangoWCParseResult, error) { - parser := NewMangoWCParser(path) - keybinds, err := parser.ParseWithDMS() - if err != nil { - return nil, err - } - - return &MangoWCParseResult{ - Keybinds: keybinds, - DMSBindsIncluded: parser.dmsBindsIncluded, - DMSStatus: parser.buildDMSStatus(), - ConflictingConfigs: parser.conflictingConfigs, - }, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_parser_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_parser_test.go deleted file mode 100644 index 016f50e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_parser_test.go +++ /dev/null @@ -1,499 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestMangoWCAutogenerateComment(t *testing.T) { - tests := []struct { - command string - params string - expected string - }{ - {"spawn", "kitty", "kitty"}, - {"spawn_shell", "firefox", "firefox"}, - {"killclient", "", "Close window"}, - {"quit", "", "Exit MangoWC"}, - {"reload_config", "", "Reload configuration"}, - {"focusstack", "next", "Focus next window"}, - {"focusstack", "prev", "Focus previous window"}, - {"focusdir", "left", "Focus left"}, - {"focusdir", "right", "Focus right"}, - {"focusdir", "up", "Focus up"}, - {"focusdir", "down", "Focus down"}, - {"exchange_client", "left", "Swap window left"}, - {"exchange_client", "right", "Swap window right"}, - {"togglefloating", "", "Float/unfloat window"}, - {"togglefullscreen", "", "Toggle fullscreen"}, - {"togglefakefullscreen", "", "Toggle fake fullscreen"}, - {"togglemaximizescreen", "", "Toggle maximize"}, - {"toggleglobal", "", "Toggle global"}, - {"toggleoverview", "", "Toggle overview"}, - {"toggleoverlay", "", "Toggle overlay"}, - {"minimized", "", "Minimize window"}, - {"restore_minimized", "", "Restore minimized"}, - {"toggle_scratchpad", "", "Toggle scratchpad"}, - {"setlayout", "tile", "Set layout tile"}, - {"switch_layout", "", "Switch layout"}, - {"view", "1,0", "View tag 1"}, - {"tag", "2,0", "Move to tag 2"}, - {"toggleview", "3,0", "Toggle tag 3"}, - {"viewtoleft", "", "View left tag"}, - {"viewtoright", "", "View right tag"}, - {"viewtoleft_have_client", "", "View left tag"}, - {"viewtoright_have_client", "", "View right tag"}, - {"tagtoleft", "", "Move to left tag"}, - {"tagtoright", "", "Move to right tag"}, - {"focusmon", "left", "Focus monitor left"}, - {"tagmon", "right", "Move to monitor right"}, - {"incgaps", "1", "Increase gaps"}, - {"incgaps", "-1", "Decrease gaps"}, - {"togglegaps", "", "Toggle gaps"}, - {"movewin", "+0,-50", "Move window by +0,-50"}, - {"resizewin", "+0,+50", "Resize window by +0,+50"}, - {"set_proportion", "1.0", "Set proportion 1.0"}, - {"switch_proportion_preset", "", "Switch proportion preset"}, - {"unknown", "", ""}, - } - - for _, tt := range tests { - t.Run(tt.command+"_"+tt.params, func(t *testing.T) { - result := mangowcAutogenerateComment(tt.command, tt.params) - if result != tt.expected { - t.Errorf("mangowcAutogenerateComment(%q, %q) = %q, want %q", - tt.command, tt.params, result, tt.expected) - } - }) - } -} - -func TestMangoWCGetKeybindAtLine(t *testing.T) { - tests := []struct { - name string - line string - expected *MangoWCKeyBinding - }{ - { - name: "basic_keybind", - line: "bind=ALT,q,killclient,", - expected: &MangoWCKeyBinding{ - Mods: []string{"ALT"}, - Key: "q", - Command: "killclient", - Params: "", - Comment: "Close window", - }, - }, - { - name: "keybind_with_params", - line: "bind=ALT,Left,focusdir,left", - expected: &MangoWCKeyBinding{ - Mods: []string{"ALT"}, - Key: "Left", - Command: "focusdir", - Params: "left", - Comment: "Focus left", - }, - }, - { - name: "keybind_with_comment", - line: "bind=Alt,t,spawn,kitty # Open terminal", - expected: &MangoWCKeyBinding{ - Mods: []string{"Alt"}, - Key: "t", - Command: "spawn", - Params: "kitty", - Comment: "Open terminal", - }, - }, - { - name: "keybind_hidden", - line: "bind=SUPER,h,spawn,secret # [hidden]", - expected: nil, - }, - { - name: "keybind_multiple_mods", - line: "bind=SUPER+SHIFT,Up,exchange_client,up", - expected: &MangoWCKeyBinding{ - Mods: []string{"SUPER", "SHIFT"}, - Key: "Up", - Command: "exchange_client", - Params: "up", - Comment: "Swap window up", - }, - }, - { - name: "keybind_no_mods", - line: "bind=NONE,Print,spawn,screenshot", - expected: &MangoWCKeyBinding{ - Mods: []string{}, - Key: "Print", - Command: "spawn", - Params: "screenshot", - Comment: "screenshot", - }, - }, - { - name: "keybind_multiple_params", - line: "bind=Ctrl,1,view,1,0", - expected: &MangoWCKeyBinding{ - Mods: []string{"Ctrl"}, - Key: "1", - Command: "view", - Params: "1,0", - Comment: "View tag 1", - }, - }, - { - name: "bindl_flag", - line: "bindl=SUPER+ALT,l,spawn,dms ipc call lock lock", - expected: &MangoWCKeyBinding{ - Mods: []string{"SUPER", "ALT"}, - Key: "l", - Command: "spawn", - Params: "dms ipc call lock lock", - Comment: "dms ipc call lock lock", - }, - }, - { - name: "keybind_with_spaces", - line: "bind = SUPER, r, reload_config", - expected: &MangoWCKeyBinding{ - Mods: []string{"SUPER"}, - Key: "r", - Command: "reload_config", - Params: "", - Comment: "Reload configuration", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - parser := NewMangoWCParser("") - parser.contentLines = []string{tt.line} - result := parser.getKeybindAtLine(0) - - if tt.expected == nil { - if result != nil { - t.Errorf("expected nil, got %+v", result) - } - return - } - - if result == nil { - t.Errorf("expected %+v, got nil", tt.expected) - return - } - - if result.Key != tt.expected.Key { - t.Errorf("Key = %q, want %q", result.Key, tt.expected.Key) - } - if result.Command != tt.expected.Command { - t.Errorf("Command = %q, want %q", result.Command, tt.expected.Command) - } - if result.Params != tt.expected.Params { - t.Errorf("Params = %q, want %q", result.Params, tt.expected.Params) - } - if result.Comment != tt.expected.Comment { - t.Errorf("Comment = %q, want %q", result.Comment, tt.expected.Comment) - } - if len(result.Mods) != len(tt.expected.Mods) { - t.Errorf("Mods length = %d, want %d", len(result.Mods), len(tt.expected.Mods)) - } else { - for i := range result.Mods { - if result.Mods[i] != tt.expected.Mods[i] { - t.Errorf("Mods[%d] = %q, want %q", i, result.Mods[i], tt.expected.Mods[i]) - } - } - } - }) - } -} - -func TestMangoWCParseKeys(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.conf") - - content := `# MangoWC Configuration -blur=0 -border_radius=12 - -# Key Bindings -bind=SUPER,r,reload_config -bind=Alt,t,spawn,kitty # Terminal -bind=ALT,q,killclient, -bind=ALT,Left,focusdir,left - -# Hidden binding -bind=SUPER,h,spawn,secret # [hidden] - -# Multiple modifiers -bind=SUPER+SHIFT,Up,exchange_client,up - -# Workspace bindings -bind=Ctrl,1,view,1,0 -bind=Ctrl,2,view,2,0 -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - keybinds, err := ParseMangoWCKeys(configFile) - if err != nil { - t.Fatalf("ParseMangoWCKeys failed: %v", err) - } - - expectedCount := 7 - if len(keybinds) != expectedCount { - t.Errorf("Expected %d keybinds, got %d", expectedCount, len(keybinds)) - } - - if len(keybinds) > 0 && keybinds[0].Command != "reload_config" { - t.Errorf("First keybind command = %q, want %q", keybinds[0].Command, "reload_config") - } - - foundHidden := false - for _, kb := range keybinds { - if kb.Command == "spawn" && kb.Params == "secret" { - foundHidden = true - } - } - if foundHidden { - t.Error("Hidden keybind should not be included in results") - } -} - -func TestMangoWCReadContentMultipleFiles(t *testing.T) { - tmpDir := t.TempDir() - - file1 := filepath.Join(tmpDir, "a.conf") - file2 := filepath.Join(tmpDir, "b.conf") - - content1 := "bind=ALT,q,killclient,\n" - content2 := "bind=Alt,t,spawn,kitty\n" - - if err := os.WriteFile(file1, []byte(content1), 0o644); err != nil { - t.Fatalf("Failed to write file1: %v", err) - } - if err := os.WriteFile(file2, []byte(content2), 0o644); err != nil { - t.Fatalf("Failed to write file2: %v", err) - } - - parser := NewMangoWCParser("") - if err := parser.ReadContent(tmpDir); err != nil { - t.Fatalf("ReadContent failed: %v", err) - } - - keybinds := parser.ParseKeys() - if len(keybinds) != 2 { - t.Errorf("Expected 2 keybinds from multiple files, got %d", len(keybinds)) - } -} - -func TestMangoWCReadContentSingleFile(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.conf") - - content := "bind=ALT,q,killclient,\n" - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write config: %v", err) - } - - parser := NewMangoWCParser("") - if err := parser.ReadContent(configFile); err != nil { - t.Fatalf("ReadContent failed: %v", err) - } - - keybinds := parser.ParseKeys() - if len(keybinds) != 1 { - t.Errorf("Expected 1 keybind, got %d", len(keybinds)) - } -} - -func TestMangoWCReadContentErrors(t *testing.T) { - tests := []struct { - name string - path string - }{ - { - name: "nonexistent_directory", - path: "/nonexistent/path/that/does/not/exist", - }, - { - name: "empty_directory", - path: t.TempDir(), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := ParseMangoWCKeys(tt.path) - if err == nil { - t.Error("Expected error, got nil") - } - }) - } -} - -func TestMangoWCReadContentWithTildeExpansion(t *testing.T) { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Skip("Cannot get home directory") - } - - tmpSubdir := filepath.Join(homeDir, ".config", "test-mango-"+t.Name()) - if err := os.MkdirAll(tmpSubdir, 0o755); err != nil { - t.Skip("Cannot create test directory in home") - } - defer os.RemoveAll(tmpSubdir) - - configFile := filepath.Join(tmpSubdir, "config.conf") - if err := os.WriteFile(configFile, []byte("bind=ALT,q,killclient,\n"), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - relPath, err := filepath.Rel(homeDir, tmpSubdir) - if err != nil { - t.Skip("Cannot create relative path") - } - - parser := NewMangoWCParser("") - tildePathMatch := "~/" + relPath - err = parser.ReadContent(tildePathMatch) - - if err != nil { - t.Errorf("ReadContent with tilde path failed: %v", err) - } -} - -func TestMangoWCEmptyAndCommentLines(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.conf") - - content := ` -# This is a comment -bind=ALT,q,killclient, - -# Another comment - -bind=Alt,t,spawn,kitty -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - keybinds, err := ParseMangoWCKeys(configFile) - if err != nil { - t.Fatalf("ParseMangoWCKeys failed: %v", err) - } - - if len(keybinds) != 2 { - t.Errorf("Expected 2 keybinds (comments ignored), got %d", len(keybinds)) - } -} - -func TestMangoWCInvalidBindLines(t *testing.T) { - tests := []struct { - name string - line string - }{ - { - name: "missing_parts", - line: "bind=SUPER,q", - }, - { - name: "not_bind", - line: "blur=0", - }, - { - name: "empty_line", - line: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - parser := NewMangoWCParser("") - parser.contentLines = []string{tt.line} - result := parser.getKeybindAtLine(0) - - if result != nil { - t.Errorf("expected nil for invalid line, got %+v", result) - } - }) - } -} - -func TestMangoWCRealWorldConfig(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.conf") - - content := `# Application Launchers -bind=Alt,t,spawn,kitty -bind=Alt,space,spawn,dms ipc call spotlight toggle -bind=Alt,v,spawn,dms ipc call clipboard toggle - -# exit -bind=ALT+SHIFT,e,quit -bind=ALT,q,killclient, - -# switch window focus -bind=SUPER,Tab,focusstack,next -bind=ALT,Left,focusdir,left -bind=ALT,Right,focusdir,right - -# tag switch -bind=SUPER,Left,viewtoleft,0 -bind=CTRL,Left,viewtoleft_have_client,0 -bind=SUPER,Right,viewtoright,0 - -bind=Ctrl,1,view,1,0 -bind=Ctrl,2,view,2,0 -bind=Ctrl,3,view,3,0 -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - keybinds, err := ParseMangoWCKeys(configFile) - if err != nil { - t.Fatalf("ParseMangoWCKeys failed: %v", err) - } - - if len(keybinds) < 14 { - t.Errorf("Expected at least 14 keybinds, got %d", len(keybinds)) - } - - foundSpawn := false - foundQuit := false - foundView := false - - for _, kb := range keybinds { - if kb.Command == "spawn" && kb.Params == "kitty" { - foundSpawn = true - } - if kb.Command == "quit" { - foundQuit = true - } - if kb.Command == "view" && kb.Params == "1,0" { - foundView = true - } - } - - if !foundSpawn { - t.Error("Did not find spawn kitty keybind") - } - if !foundQuit { - t.Error("Did not find quit keybind") - } - if !foundView { - t.Error("Did not find view workspace 1 keybind") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_test.go deleted file mode 100644 index d417ce1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/mangowc_test.go +++ /dev/null @@ -1,320 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestMangoWCProviderName(t *testing.T) { - provider := NewMangoWCProvider("") - if provider.Name() != "mangowc" { - t.Errorf("Name() = %q, want %q", provider.Name(), "mangowc") - } -} - -func TestMangoWCProviderDefaultPath(t *testing.T) { - provider := NewMangoWCProvider("") - configDir, err := os.UserConfigDir() - if err != nil { - // Fall back to testing for non-empty path - if provider.configPath == "" { - t.Error("configPath should not be empty") - } - return - } - expected := filepath.Join(configDir, "mango") - if provider.configPath != expected { - t.Errorf("configPath = %q, want %q", provider.configPath, expected) - } -} - -func TestMangoWCProviderCustomPath(t *testing.T) { - customPath := "/custom/path" - provider := NewMangoWCProvider(customPath) - if provider.configPath != customPath { - t.Errorf("configPath = %q, want %q", provider.configPath, customPath) - } -} - -func TestMangoWCCategorizeByCommand(t *testing.T) { - tests := []struct { - command string - expected string - }{ - {"view", "Tags"}, - {"tag", "Tags"}, - {"toggleview", "Tags"}, - {"viewtoleft", "Tags"}, - {"viewtoright", "Tags"}, - {"viewtoleft_have_client", "Tags"}, - {"tagtoleft", "Tags"}, - {"tagtoright", "Tags"}, - {"focusmon", "Monitor"}, - {"tagmon", "Monitor"}, - {"focusstack", "Window"}, - {"focusdir", "Window"}, - {"exchange_client", "Window"}, - {"killclient", "Window"}, - {"togglefloating", "Window"}, - {"togglefullscreen", "Window"}, - {"togglefakefullscreen", "Window"}, - {"togglemaximizescreen", "Window"}, - {"toggleglobal", "Window"}, - {"toggleoverlay", "Window"}, - {"minimized", "Window"}, - {"restore_minimized", "Window"}, - {"movewin", "Window"}, - {"resizewin", "Window"}, - {"toggleoverview", "Overview"}, - {"toggle_scratchpad", "Scratchpad"}, - {"setlayout", "Layout"}, - {"switch_layout", "Layout"}, - {"set_proportion", "Layout"}, - {"switch_proportion_preset", "Layout"}, - {"incgaps", "Gaps"}, - {"togglegaps", "Gaps"}, - {"spawn", "Execute"}, - {"spawn_shell", "Execute"}, - {"quit", "System"}, - {"reload_config", "System"}, - {"unknown_command", "Other"}, - } - - provider := NewMangoWCProvider("") - for _, tt := range tests { - t.Run(tt.command, func(t *testing.T) { - result := provider.categorizeByCommand(tt.command) - if result != tt.expected { - t.Errorf("categorizeByCommand(%q) = %q, want %q", tt.command, result, tt.expected) - } - }) - } -} - -func TestMangoWCFormatKey(t *testing.T) { - tests := []struct { - name string - keybind *MangoWCKeyBinding - expected string - }{ - { - name: "single_mod", - keybind: &MangoWCKeyBinding{ - Mods: []string{"ALT"}, - Key: "q", - }, - expected: "ALT+q", - }, - { - name: "multiple_mods", - keybind: &MangoWCKeyBinding{ - Mods: []string{"SUPER", "SHIFT"}, - Key: "Up", - }, - expected: "SUPER+SHIFT+Up", - }, - { - name: "no_mods", - keybind: &MangoWCKeyBinding{ - Mods: []string{}, - Key: "Print", - }, - expected: "Print", - }, - } - - provider := NewMangoWCProvider("") - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := provider.formatKey(tt.keybind) - if result != tt.expected { - t.Errorf("formatKey() = %q, want %q", result, tt.expected) - } - }) - } -} - -func TestMangoWCConvertKeybind(t *testing.T) { - tests := []struct { - name string - keybind *MangoWCKeyBinding - wantKey string - wantDesc string - }{ - { - name: "with_comment", - keybind: &MangoWCKeyBinding{ - Mods: []string{"ALT"}, - Key: "t", - Command: "spawn", - Params: "kitty", - Comment: "Open terminal", - }, - wantKey: "ALT+t", - wantDesc: "Open terminal", - }, - { - name: "without_comment", - keybind: &MangoWCKeyBinding{ - Mods: []string{"SUPER"}, - Key: "r", - Command: "reload_config", - Params: "", - Comment: "", - }, - wantKey: "SUPER+r", - wantDesc: "reload_config", - }, - { - name: "with_params_no_comment", - keybind: &MangoWCKeyBinding{ - Mods: []string{"CTRL"}, - Key: "1", - Command: "view", - Params: "1,0", - Comment: "", - }, - wantKey: "CTRL+1", - wantDesc: "view 1,0", - }, - } - - provider := NewMangoWCProvider("") - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := provider.convertKeybind(tt.keybind, nil) - if result.Key != tt.wantKey { - t.Errorf("convertKeybind().Key = %q, want %q", result.Key, tt.wantKey) - } - if result.Description != tt.wantDesc { - t.Errorf("convertKeybind().Description = %q, want %q", result.Description, tt.wantDesc) - } - }) - } -} - -func TestMangoWCGetCheatSheet(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.conf") - - content := `# MangoWC Configuration -blur=0 - -# Key Bindings -bind=SUPER,r,reload_config -bind=Alt,t,spawn,kitty # Terminal -bind=ALT,q,killclient, - -# Window management -bind=ALT,Left,focusdir,left -bind=ALT,Right,focusdir,right -bind=SUPER+SHIFT,Up,exchange_client,up - -# Tags -bind=Ctrl,1,view,1,0 -bind=Ctrl,2,view,2,0 -bind=Alt,1,tag,1,0 - -# Layout -bind=SUPER,n,switch_layout - -# Gaps -bind=ALT+SHIFT,X,incgaps,1 -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - provider := NewMangoWCProvider(tmpDir) - sheet, err := provider.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - if sheet == nil { - t.Fatal("Expected non-nil CheatSheet") - } - - if sheet.Title != "MangoWC Keybinds" { - t.Errorf("Title = %q, want %q", sheet.Title, "MangoWC Keybinds") - } - - if sheet.Provider != "mangowc" { - t.Errorf("Provider = %q, want %q", sheet.Provider, "mangowc") - } - - categories := []string{"System", "Execute", "Window", "Tags", "Layout", "Gaps"} - for _, category := range categories { - if _, exists := sheet.Binds[category]; !exists { - t.Errorf("Expected category %q to exist", category) - } - } - - if len(sheet.Binds["System"]) < 1 { - t.Error("Expected at least 1 System keybind") - } - if len(sheet.Binds["Execute"]) < 1 { - t.Error("Expected at least 1 Execute keybind") - } - if len(sheet.Binds["Window"]) < 3 { - t.Error("Expected at least 3 Window keybinds") - } - if len(sheet.Binds["Tags"]) < 3 { - t.Error("Expected at least 3 Tags keybinds") - } -} - -func TestMangoWCGetCheatSheetError(t *testing.T) { - provider := NewMangoWCProvider("/nonexistent/path") - _, err := provider.GetCheatSheet() - if err == nil { - t.Error("Expected error for nonexistent path, got nil") - } -} - -func TestMangoWCIntegration(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.conf") - - content := `bind=Alt,t,spawn,kitty # Open terminal -bind=ALT,q,killclient, -bind=SUPER,r,reload_config # Reload config -bind=ALT,Left,focusdir,left -bind=Ctrl,1,view,1,0 -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - provider := NewMangoWCProvider(tmpDir) - sheet, err := provider.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - totalBinds := 0 - for _, binds := range sheet.Binds { - totalBinds += len(binds) - } - - expectedBinds := 5 - if totalBinds != expectedBinds { - t.Errorf("Expected %d total keybinds, got %d", expectedBinds, totalBinds) - } - - foundTerminal := false - for _, binds := range sheet.Binds { - for _, bind := range binds { - if bind.Description == "Open terminal" && bind.Key == "Alt+t" { - foundTerminal = true - } - } - } - - if !foundTerminal { - t.Error("Did not find terminal keybind with correct key and description") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/miracle.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/miracle.go deleted file mode 100644 index 43fc58e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/miracle.go +++ /dev/null @@ -1,95 +0,0 @@ -package providers - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -type MiracleProvider struct { - configPath string -} - -func NewMiracleProvider(configPath string) *MiracleProvider { - if configPath == "" { - configDir, err := os.UserConfigDir() - if err == nil { - configPath = filepath.Join(configDir, "miracle-wm") - } - } - return &MiracleProvider{configPath: configPath} -} - -func (m *MiracleProvider) Name() string { - return "miracle" -} - -func (m *MiracleProvider) GetCheatSheet() (*keybinds.CheatSheet, error) { - config, err := ParseMiracleConfig(m.configPath) - if err != nil { - return nil, fmt.Errorf("failed to parse miracle-wm config: %w", err) - } - - bindings := MiracleConfigToBindings(config) - categorizedBinds := make(map[string][]keybinds.Keybind) - - for _, kb := range bindings { - category := m.categorizeAction(kb.Action) - bind := keybinds.Keybind{ - Key: m.formatKey(kb), - Description: kb.Comment, - Action: kb.Action, - } - categorizedBinds[category] = append(categorizedBinds[category], bind) - } - - return &keybinds.CheatSheet{ - Title: "Miracle WM Keybinds", - Provider: m.Name(), - Binds: categorizedBinds, - }, nil -} - -func (m *MiracleProvider) GetOverridePath() string { - expanded, err := utils.ExpandPath(m.configPath) - if err != nil { - return filepath.Join(m.configPath, "config.yaml") - } - return filepath.Join(expanded, "config.yaml") -} - -func (m *MiracleProvider) formatKey(kb MiracleKeyBinding) string { - parts := make([]string, 0, len(kb.Mods)+1) - parts = append(parts, kb.Mods...) - parts = append(parts, kb.Key) - return strings.Join(parts, "+") -} - -func (m *MiracleProvider) categorizeAction(action string) string { - switch { - case strings.HasPrefix(action, "select_workspace_") || strings.HasPrefix(action, "move_to_workspace_"): - return "Workspace" - case strings.Contains(action, "select_") || strings.Contains(action, "move_"): - return "Window" - case action == "toggle_resize" || strings.HasPrefix(action, "resize_"): - return "Window" - case action == "fullscreen" || action == "toggle_floating" || action == "quit_active_window" || action == "toggle_pinned_to_workspace": - return "Window" - case action == "toggle_tabbing" || action == "toggle_stacking" || action == "request_vertical" || action == "request_horizontal": - return "Layout" - case action == "quit_compositor": - return "System" - case action == "terminal": - return "Execute" - case strings.HasPrefix(action, "magnifier_"): - return "Accessibility" - case strings.HasPrefix(action, "dms ") || strings.Contains(action, "dms ipc"): - return "Execute" - default: - return "Execute" - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/miracle_parser.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/miracle_parser.go deleted file mode 100644 index 425c64a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/miracle_parser.go +++ /dev/null @@ -1,320 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "gopkg.in/yaml.v3" -) - -type MiracleConfig struct { - Terminal string `yaml:"terminal"` - ActionKey string `yaml:"action_key"` - DefaultActionOverrides []MiracleActionOverride `yaml:"default_action_overrides"` - CustomActions []MiracleCustomAction `yaml:"custom_actions"` -} - -type MiracleActionOverride struct { - Name string `yaml:"name"` - Action string `yaml:"action"` - Modifiers []string `yaml:"modifiers"` - Key string `yaml:"key"` -} - -type MiracleCustomAction struct { - Command string `yaml:"command"` - Action string `yaml:"action"` - Modifiers []string `yaml:"modifiers"` - Key string `yaml:"key"` -} - -type MiracleKeyBinding struct { - Mods []string - Key string - Action string - Comment string -} - -var miracleDefaultBinds = []MiracleKeyBinding{ - {Mods: []string{"Super"}, Key: "Return", Action: "terminal", Comment: "Open terminal"}, - {Mods: []string{"Super"}, Key: "v", Action: "request_vertical", Comment: "Layout windows vertically"}, - {Mods: []string{"Super"}, Key: "h", Action: "request_horizontal", Comment: "Layout windows horizontally"}, - {Mods: []string{"Super"}, Key: "Up", Action: "select_up", Comment: "Select window above"}, - {Mods: []string{"Super"}, Key: "Down", Action: "select_down", Comment: "Select window below"}, - {Mods: []string{"Super"}, Key: "Left", Action: "select_left", Comment: "Select window left"}, - {Mods: []string{"Super"}, Key: "Right", Action: "select_right", Comment: "Select window right"}, - {Mods: []string{"Super", "Shift"}, Key: "Up", Action: "move_up", Comment: "Move window up"}, - {Mods: []string{"Super", "Shift"}, Key: "Down", Action: "move_down", Comment: "Move window down"}, - {Mods: []string{"Super", "Shift"}, Key: "Left", Action: "move_left", Comment: "Move window left"}, - {Mods: []string{"Super", "Shift"}, Key: "Right", Action: "move_right", Comment: "Move window right"}, - {Mods: []string{"Super"}, Key: "r", Action: "toggle_resize", Comment: "Toggle resize mode"}, - {Mods: []string{"Super"}, Key: "f", Action: "fullscreen", Comment: "Toggle fullscreen"}, - {Mods: []string{"Super", "Shift"}, Key: "q", Action: "quit_active_window", Comment: "Close window"}, - {Mods: []string{"Super", "Shift"}, Key: "e", Action: "quit_compositor", Comment: "Exit compositor"}, - {Mods: []string{"Super"}, Key: "Space", Action: "toggle_floating", Comment: "Toggle floating"}, - {Mods: []string{"Super", "Shift"}, Key: "p", Action: "toggle_pinned_to_workspace", Comment: "Toggle pinned to workspace"}, - {Mods: []string{"Super"}, Key: "w", Action: "toggle_tabbing", Comment: "Toggle tabbing layout"}, - {Mods: []string{"Super"}, Key: "s", Action: "toggle_stacking", Comment: "Toggle stacking layout"}, - {Mods: []string{"Super"}, Key: "1", Action: "select_workspace_0", Comment: "Workspace 1"}, - {Mods: []string{"Super"}, Key: "2", Action: "select_workspace_1", Comment: "Workspace 2"}, - {Mods: []string{"Super"}, Key: "3", Action: "select_workspace_2", Comment: "Workspace 3"}, - {Mods: []string{"Super"}, Key: "4", Action: "select_workspace_3", Comment: "Workspace 4"}, - {Mods: []string{"Super"}, Key: "5", Action: "select_workspace_4", Comment: "Workspace 5"}, - {Mods: []string{"Super"}, Key: "6", Action: "select_workspace_5", Comment: "Workspace 6"}, - {Mods: []string{"Super"}, Key: "7", Action: "select_workspace_6", Comment: "Workspace 7"}, - {Mods: []string{"Super"}, Key: "8", Action: "select_workspace_7", Comment: "Workspace 8"}, - {Mods: []string{"Super"}, Key: "9", Action: "select_workspace_8", Comment: "Workspace 9"}, - {Mods: []string{"Super"}, Key: "0", Action: "select_workspace_9", Comment: "Workspace 10"}, - {Mods: []string{"Super", "Shift"}, Key: "1", Action: "move_to_workspace_0", Comment: "Move to workspace 1"}, - {Mods: []string{"Super", "Shift"}, Key: "2", Action: "move_to_workspace_1", Comment: "Move to workspace 2"}, - {Mods: []string{"Super", "Shift"}, Key: "3", Action: "move_to_workspace_2", Comment: "Move to workspace 3"}, - {Mods: []string{"Super", "Shift"}, Key: "4", Action: "move_to_workspace_3", Comment: "Move to workspace 4"}, - {Mods: []string{"Super", "Shift"}, Key: "5", Action: "move_to_workspace_4", Comment: "Move to workspace 5"}, - {Mods: []string{"Super", "Shift"}, Key: "6", Action: "move_to_workspace_5", Comment: "Move to workspace 6"}, - {Mods: []string{"Super", "Shift"}, Key: "7", Action: "move_to_workspace_6", Comment: "Move to workspace 7"}, - {Mods: []string{"Super", "Shift"}, Key: "8", Action: "move_to_workspace_7", Comment: "Move to workspace 8"}, - {Mods: []string{"Super", "Shift"}, Key: "9", Action: "move_to_workspace_8", Comment: "Move to workspace 9"}, - {Mods: []string{"Super", "Shift"}, Key: "0", Action: "move_to_workspace_9", Comment: "Move to workspace 10"}, -} - -func ParseMiracleConfig(configPath string) (*MiracleConfig, error) { - expanded, err := utils.ExpandPath(configPath) - if err != nil { - return nil, err - } - - info, err := os.Stat(expanded) - if err != nil { - return nil, err - } - - var configFile string - if info.IsDir() { - configFile = filepath.Join(expanded, "config.yaml") - } else { - configFile = expanded - } - - data, err := os.ReadFile(configFile) - if err != nil { - return nil, err - } - - var config MiracleConfig - if err := yaml.Unmarshal(data, &config); err != nil { - return nil, err - } - - if config.ActionKey == "" { - config.ActionKey = "meta" - } - - return &config, nil -} - -func resolveMiracleModifier(mod, actionKey string) string { - switch mod { - case "primary": - return resolveActionKey(actionKey) - case "alt", "alt_left", "alt_right": - return "Alt" - case "shift", "shift_left", "shift_right": - return "Shift" - case "ctrl", "ctrl_left", "ctrl_right": - return "Ctrl" - case "meta", "meta_left", "meta_right": - return "Super" - default: - return mod - } -} - -func resolveActionKey(actionKey string) string { - switch actionKey { - case "meta": - return "Super" - case "alt": - return "Alt" - case "ctrl": - return "Ctrl" - default: - return "Super" - } -} - -func miracleKeyCodeToName(keyCode string) string { - name := strings.TrimPrefix(keyCode, "KEY_") - name = strings.ToLower(name) - - switch name { - case "enter": - return "Return" - case "space": - return "Space" - case "up": - return "Up" - case "down": - return "Down" - case "left": - return "Left" - case "right": - return "Right" - case "tab": - return "Tab" - case "escape", "esc": - return "Escape" - case "delete": - return "Delete" - case "backspace": - return "BackSpace" - case "home": - return "Home" - case "end": - return "End" - case "pageup": - return "Page_Up" - case "pagedown": - return "Page_Down" - case "print": - return "Print" - case "pause": - return "Pause" - case "volumeup": - return "XF86AudioRaiseVolume" - case "volumedown": - return "XF86AudioLowerVolume" - case "mute": - return "XF86AudioMute" - case "micmute": - return "XF86AudioMicMute" - case "brightnessup": - return "XF86MonBrightnessUp" - case "brightnessdown": - return "XF86MonBrightnessDown" - case "kbdillumup": - return "XF86KbdBrightnessUp" - case "kbdillumdown": - return "XF86KbdBrightnessDown" - case "comma": - return "comma" - case "minus": - return "minus" - case "equal": - return "equal" - } - - if len(name) == 1 { - return name - } - - return name -} - -func MiracleConfigToBindings(config *MiracleConfig) []MiracleKeyBinding { - overridden := make(map[string]bool) - var bindings []MiracleKeyBinding - - for _, override := range config.DefaultActionOverrides { - mods := make([]string, 0, len(override.Modifiers)) - for _, mod := range override.Modifiers { - mods = append(mods, resolveMiracleModifier(mod, config.ActionKey)) - } - - bindings = append(bindings, MiracleKeyBinding{ - Mods: mods, - Key: miracleKeyCodeToName(override.Key), - Action: override.Name, - Comment: miracleActionDescription(override.Name), - }) - overridden[override.Name] = true - } - - for _, def := range miracleDefaultBinds { - if overridden[def.Action] { - continue - } - bindings = append(bindings, def) - } - - for _, custom := range config.CustomActions { - mods := make([]string, 0, len(custom.Modifiers)) - for _, mod := range custom.Modifiers { - mods = append(mods, resolveMiracleModifier(mod, config.ActionKey)) - } - - bindings = append(bindings, MiracleKeyBinding{ - Mods: mods, - Key: miracleKeyCodeToName(custom.Key), - Action: custom.Command, - Comment: custom.Command, - }) - } - - return bindings -} - -func miracleActionDescription(action string) string { - switch action { - case "terminal": - return "Open terminal" - case "request_vertical": - return "Layout windows vertically" - case "request_horizontal": - return "Layout windows horizontally" - case "select_up": - return "Select window above" - case "select_down": - return "Select window below" - case "select_left": - return "Select window left" - case "select_right": - return "Select window right" - case "move_up": - return "Move window up" - case "move_down": - return "Move window down" - case "move_left": - return "Move window left" - case "move_right": - return "Move window right" - case "toggle_resize": - return "Toggle resize mode" - case "fullscreen": - return "Toggle fullscreen" - case "quit_active_window": - return "Close window" - case "quit_compositor": - return "Exit compositor" - case "toggle_floating": - return "Toggle floating" - case "toggle_pinned_to_workspace": - return "Toggle pinned to workspace" - case "toggle_tabbing": - return "Toggle tabbing layout" - case "toggle_stacking": - return "Toggle stacking layout" - case "magnifier_on": - return "Enable magnifier" - case "magnifier_off": - return "Disable magnifier" - case "magnifier_increase_size": - return "Increase magnifier area" - case "magnifier_decrease_size": - return "Decrease magnifier area" - case "magnifier_increase_scale": - return "Increase magnifier scale" - case "magnifier_decrease_scale": - return "Decrease magnifier scale" - } - - if num, ok := strings.CutPrefix(action, "select_workspace_"); ok { - return "Workspace " + num - } - if num, ok := strings.CutPrefix(action, "move_to_workspace_"); ok { - return "Move to workspace " + num - } - - return action -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri.go deleted file mode 100644 index aec20b5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri.go +++ /dev/null @@ -1,650 +0,0 @@ -package providers - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "sort" - "strconv" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" - "github.com/sblinch/kdl-go" - "github.com/sblinch/kdl-go/document" -) - -type NiriProvider struct { - configDir string - dmsBindsIncluded bool - parsed bool -} - -func NewNiriProvider(configDir string) *NiriProvider { - if configDir == "" { - configDir = defaultNiriConfigDir() - } - return &NiriProvider{ - configDir: configDir, - } -} - -func defaultNiriConfigDir() string { - configDir, err := os.UserConfigDir() - if err != nil { - return "" - } - return filepath.Join(configDir, "niri") -} - -func (n *NiriProvider) Name() string { - return "niri" -} - -func (n *NiriProvider) GetCheatSheet() (*keybinds.CheatSheet, error) { - result, err := ParseNiriKeys(n.configDir) - if err != nil { - return nil, fmt.Errorf("failed to parse niri config: %w", err) - } - - n.dmsBindsIncluded = result.DMSBindsIncluded - n.parsed = true - - categorizedBinds := make(map[string][]keybinds.Keybind) - n.convertSection(result.Section, "", categorizedBinds, result.ConflictingConfigs) - - sheet := &keybinds.CheatSheet{ - Title: "Niri Keybinds", - Provider: n.Name(), - Binds: categorizedBinds, - DMSBindsIncluded: result.DMSBindsIncluded, - } - - if result.DMSStatus != nil { - sheet.DMSStatus = &keybinds.DMSBindsStatus{ - Exists: result.DMSStatus.Exists, - Included: result.DMSStatus.Included, - IncludePosition: result.DMSStatus.IncludePosition, - TotalIncludes: result.DMSStatus.TotalIncludes, - BindsAfterDMS: result.DMSStatus.BindsAfterDMS, - Effective: result.DMSStatus.Effective, - OverriddenBy: result.DMSStatus.OverriddenBy, - StatusMessage: result.DMSStatus.StatusMessage, - } - } - - return sheet, nil -} - -func (n *NiriProvider) HasDMSBindsIncluded() bool { - if n.parsed { - return n.dmsBindsIncluded - } - - result, err := ParseNiriKeys(n.configDir) - if err != nil { - return false - } - - n.dmsBindsIncluded = result.DMSBindsIncluded - n.parsed = true - return n.dmsBindsIncluded -} - -func (n *NiriProvider) convertSection(section *NiriSection, subcategory string, categorizedBinds map[string][]keybinds.Keybind, conflicts map[string]*NiriKeyBinding) { - currentSubcat := subcategory - if section.Name != "" { - currentSubcat = section.Name - } - - for _, kb := range section.Keybinds { - category := n.categorizeByAction(kb.Action) - bind := n.convertKeybind(&kb, currentSubcat, conflicts) - categorizedBinds[category] = append(categorizedBinds[category], bind) - } - - for _, child := range section.Children { - n.convertSection(&child, currentSubcat, categorizedBinds, conflicts) - } -} - -func (n *NiriProvider) categorizeByAction(action string) string { - switch { - case action == "next-window" || action == "previous-window": - return "Alt-Tab" - case strings.Contains(action, "screenshot"): - return "Screenshot" - case action == "show-hotkey-overlay" || action == "toggle-overview": - return "Overview" - case action == "quit" || - action == "power-off-monitors" || - action == "power-on-monitors" || - action == "suspend" || - action == "do-screen-transition" || - action == "toggle-keyboard-shortcuts-inhibit" || - strings.Contains(action, "dpms"): - return "System" - case action == "spawn": - return "Execute" - case strings.Contains(action, "workspace"): - return "Workspace" - case strings.HasPrefix(action, "focus-monitor") || - strings.HasPrefix(action, "move-column-to-monitor") || - strings.HasPrefix(action, "move-window-to-monitor"): - return "Monitor" - case strings.Contains(action, "window") || - strings.Contains(action, "focus") || - strings.Contains(action, "move") || - strings.Contains(action, "swap") || - strings.Contains(action, "resize") || - strings.Contains(action, "column"): - return "Window" - default: - return "Other" - } -} - -func (n *NiriProvider) convertKeybind(kb *NiriKeyBinding, subcategory string, conflicts map[string]*NiriKeyBinding) keybinds.Keybind { - rawAction := n.formatRawAction(kb.Action, kb.Args) - keyStr := n.formatKey(kb) - - source := "config" - if strings.Contains(kb.Source, "dms/binds.kdl") { - source = "dms" - } - - bind := keybinds.Keybind{ - Key: keyStr, - Description: kb.Description, - Action: rawAction, - Subcategory: subcategory, - Source: source, - HideOnOverlay: kb.HideOnOverlay, - CooldownMs: kb.CooldownMs, - AllowWhenLocked: kb.AllowWhenLocked, - AllowInhibiting: kb.AllowInhibiting, - Repeat: kb.Repeat, - } - - if source == "dms" && conflicts != nil { - if conflictKb, ok := conflicts[keyStr]; ok { - bind.Conflict = &keybinds.Keybind{ - Key: keyStr, - Description: conflictKb.Description, - Action: n.formatRawAction(conflictKb.Action, conflictKb.Args), - Source: "config", - } - } - } - - return bind -} - -func (n *NiriProvider) formatRawAction(action string, args []string) string { - if len(args) == 0 { - return action - } - - if action == "spawn" && len(args) >= 3 && args[1] == "-c" { - switch args[0] { - case "sh", "bash": - cmd := strings.Join(args[2:], " ") - return fmt.Sprintf("spawn %s -c \"%s\"", args[0], strings.ReplaceAll(cmd, "\"", "\\\"")) - } - } - - quotedArgs := make([]string, len(args)) - for i, arg := range args { - if arg == "" { - quotedArgs[i] = `""` - } else { - quotedArgs[i] = arg - } - } - return action + " " + strings.Join(quotedArgs, " ") -} - -func (n *NiriProvider) formatKey(kb *NiriKeyBinding) string { - parts := make([]string, 0, len(kb.Mods)+1) - parts = append(parts, kb.Mods...) - parts = append(parts, kb.Key) - return strings.Join(parts, "+") -} - -func (n *NiriProvider) GetOverridePath() string { - return filepath.Join(n.configDir, "dms", "binds.kdl") -} - -func (n *NiriProvider) validateAction(action string) error { - action = strings.TrimSpace(action) - switch { - case action == "": - return fmt.Errorf("action cannot be empty") - case action == "spawn" || action == "spawn ": - return fmt.Errorf("spawn command requires arguments") - case strings.HasPrefix(action, "spawn "): - rest := strings.TrimSpace(strings.TrimPrefix(action, "spawn ")) - switch rest { - case "": - return fmt.Errorf("spawn command requires arguments") - case "sh -c \"\"", "sh -c ''", "bash -c \"\"", "bash -c ''": - return fmt.Errorf("shell command cannot be empty") - } - } - return nil -} - -func (n *NiriProvider) SetBind(key, action, description string, options map[string]any) error { - if err := n.validateAction(action); err != nil { - return err - } - - overridePath := n.GetOverridePath() - - if err := os.MkdirAll(filepath.Dir(overridePath), 0o755); err != nil { - return fmt.Errorf("failed to create dms directory: %w", err) - } - - existingBinds, err := n.loadOverrideBinds() - if err != nil { - existingBinds = make(map[string]*overrideBind) - } - - existingBinds[key] = &overrideBind{ - Key: key, - Action: action, - Description: description, - Options: options, - } - - return n.writeOverrideBinds(existingBinds) -} - -func (n *NiriProvider) RemoveBind(key string) error { - existingBinds, err := n.loadOverrideBinds() - if err != nil { - return nil - } - - delete(existingBinds, key) - return n.writeOverrideBinds(existingBinds) -} - -type overrideBind struct { - Key string - Action string - Description string - Options map[string]any -} - -func (n *NiriProvider) loadOverrideBinds() (map[string]*overrideBind, error) { - overridePath := n.GetOverridePath() - binds := make(map[string]*overrideBind) - - data, err := os.ReadFile(overridePath) - if os.IsNotExist(err) { - return binds, nil - } - if err != nil { - return nil, err - } - - parser := NewNiriParser(filepath.Dir(overridePath)) - parser.currentSource = overridePath - - doc, err := kdl.Parse(strings.NewReader(string(data))) - if err != nil { - return nil, err - } - - for _, node := range doc.Nodes { - if node.Name.String() != "binds" || node.Children == nil { - continue - } - for _, child := range node.Children { - kb := parser.parseKeybindNode(child, "") - if kb == nil { - continue - } - keyStr := parser.formatBindKey(kb) - - action := n.buildActionFromNode(child) - if action == "" { - action = n.formatRawAction(kb.Action, kb.Args) - } - - binds[keyStr] = &overrideBind{ - Key: keyStr, - Action: action, - Description: kb.Description, - Options: n.extractOptions(child), - } - } - } - - return binds, nil -} - -func (n *NiriProvider) buildActionFromNode(bindNode *document.Node) string { - if len(bindNode.Children) == 0 { - return "" - } - - actionNode := bindNode.Children[0] - actionName := actionNode.Name.String() - if actionName == "" { - return "" - } - - parts := []string{actionName} - for _, arg := range actionNode.Arguments { - val := arg.ValueString() - if val == "" { - parts = append(parts, `""`) - } else if strings.ContainsAny(val, " \t") { - parts = append(parts, `"`+strings.ReplaceAll(val, `"`, `\"`)+`"`) - } else { - parts = append(parts, val) - } - } - - if actionNode.Properties != nil { - for _, propName := range []string{"focus", "show-pointer", "write-to-disk", "skip-confirmation", "delay-ms"} { - if val, ok := actionNode.Properties.Get(propName); ok { - parts = append(parts, propName+"="+val.String()) - } - } - } - - return strings.Join(parts, " ") -} - -func (n *NiriProvider) extractOptions(node *document.Node) map[string]any { - if node.Properties == nil { - return make(map[string]any) - } - - opts := make(map[string]any) - if val, ok := node.Properties.Get("repeat"); ok { - opts["repeat"] = val.String() == "true" - } - if val, ok := node.Properties.Get("cooldown-ms"); ok { - if ms, err := strconv.Atoi(val.String()); err == nil { - opts["cooldown-ms"] = ms - } - } - if val, ok := node.Properties.Get("allow-when-locked"); ok { - opts["allow-when-locked"] = val.String() == "true" - } - if val, ok := node.Properties.Get("allow-inhibiting"); ok { - opts["allow-inhibiting"] = val.String() == "true" - } - return opts -} - -func (n *NiriProvider) isRecentWindowsAction(action string) bool { - switch action { - case "next-window", "previous-window": - return true - default: - return false - } -} - -func (n *NiriProvider) buildBindNode(bind *overrideBind) *document.Node { - node := document.NewNode() - node.SetName(bind.Key) - - if bind.Options != nil { - if v, ok := bind.Options["repeat"]; ok && v == false { - node.AddProperty("repeat", false, "") - } - if v, ok := bind.Options["cooldown-ms"]; ok { - switch val := v.(type) { - case int: - node.AddProperty("cooldown-ms", val, "") - case string: - if ms, err := strconv.Atoi(val); err == nil { - node.AddProperty("cooldown-ms", ms, "") - } - } - } - if v, ok := bind.Options["allow-when-locked"]; ok && v == true { - node.AddProperty("allow-when-locked", true, "") - } - if v, ok := bind.Options["allow-inhibiting"]; ok && v == false { - node.AddProperty("allow-inhibiting", false, "") - } - } - - if bind.Description != "" { - node.AddProperty("hotkey-overlay-title", bind.Description, "") - } - - actionNode := n.buildActionNode(bind.Action) - node.AddNode(actionNode) - - return node -} - -func (n *NiriProvider) buildActionNode(action string) *document.Node { - action = strings.TrimSpace(action) - node := document.NewNode() - - parts := n.parseActionParts(action) - if len(parts) == 0 { - node.SetName(action) - return node - } - - node.SetName(parts[0]) - for _, arg := range parts[1:] { - if strings.Contains(arg, "=") { - kv := strings.SplitN(arg, "=", 2) - switch kv[1] { - case "true": - node.AddProperty(kv[0], true, "") - case "false": - node.AddProperty(kv[0], false, "") - default: - node.AddProperty(kv[0], kv[1], "") - } - continue - } - node.AddArgument(arg, "") - } - return node -} - -func (n *NiriProvider) parseActionParts(action string) []string { - var parts []string - var current strings.Builder - var inQuote, escaped, wasQuoted bool - - for _, r := range action { - switch { - case escaped: - current.WriteRune(r) - escaped = false - case r == '\\': - escaped = true - case r == '"': - wasQuoted = true - inQuote = !inQuote - case r == ' ' && !inQuote: - if current.Len() > 0 || wasQuoted { - parts = append(parts, current.String()) - current.Reset() - wasQuoted = false - } - default: - current.WriteRune(r) - } - } - if current.Len() > 0 || wasQuoted { - parts = append(parts, current.String()) - } - return parts -} - -func (n *NiriProvider) writeOverrideBinds(binds map[string]*overrideBind) error { - overridePath := n.GetOverridePath() - content := n.generateBindsContent(binds) - - if err := n.validateBindsContent(content); err != nil { - return err - } - - return os.WriteFile(overridePath, []byte(content), 0o644) -} - -func (n *NiriProvider) getBindSortPriority(action string) int { - switch { - case strings.HasPrefix(action, "spawn") && strings.Contains(action, "dms"): - return 0 - case strings.Contains(action, "workspace"): - return 1 - case strings.Contains(action, "window") || strings.Contains(action, "column") || - strings.Contains(action, "focus") || strings.Contains(action, "move") || - strings.Contains(action, "swap") || strings.Contains(action, "resize"): - return 2 - case strings.HasPrefix(action, "focus-monitor") || strings.Contains(action, "monitor"): - return 3 - case strings.Contains(action, "screenshot"): - return 4 - case action == "quit" || action == "power-off-monitors" || strings.Contains(action, "dpms"): - return 5 - case strings.HasPrefix(action, "spawn"): - return 6 - default: - return 7 - } -} - -func (n *NiriProvider) generateBindsContent(binds map[string]*overrideBind) string { - if len(binds) == 0 { - return "binds {}\n" - } - - var regularBinds, recentWindowsBinds []*overrideBind - for _, bind := range binds { - switch { - case n.isRecentWindowsAction(bind.Action): - recentWindowsBinds = append(recentWindowsBinds, bind) - default: - regularBinds = append(regularBinds, bind) - } - } - - sort.Slice(regularBinds, func(i, j int) bool { - pi, pj := n.getBindSortPriority(regularBinds[i].Action), n.getBindSortPriority(regularBinds[j].Action) - if pi != pj { - return pi < pj - } - return regularBinds[i].Key < regularBinds[j].Key - }) - - sort.Slice(recentWindowsBinds, func(i, j int) bool { - return recentWindowsBinds[i].Key < recentWindowsBinds[j].Key - }) - - var sb strings.Builder - - sb.WriteString("binds {\n") - for _, bind := range regularBinds { - n.writeBindNode(&sb, bind, " ") - } - sb.WriteString("}\n") - - if len(recentWindowsBinds) > 0 { - sb.WriteString("\nrecent-windows {\n") - sb.WriteString(" binds {\n") - for _, bind := range recentWindowsBinds { - n.writeBindNode(&sb, bind, " ") - } - sb.WriteString(" }\n") - sb.WriteString("}\n") - } - - return sb.String() -} - -func (n *NiriProvider) writeBindNode(sb *strings.Builder, bind *overrideBind, indent string) { - node := n.buildBindNode(bind) - - sb.WriteString(indent) - sb.WriteString(node.Name.String()) - - if node.Properties.Exist() { - sb.WriteString(" ") - sb.WriteString(strings.TrimLeft(node.Properties.String(), " ")) - } - - sb.WriteString(" { ") - if len(node.Children) > 0 { - child := node.Children[0] - actionName := child.Name.String() - sb.WriteString(actionName) - forceQuote := actionName == "spawn" - for _, arg := range child.Arguments { - sb.WriteString(" ") - n.writeArg(sb, arg.ValueString(), forceQuote) - } - if child.Properties.Exist() { - sb.WriteString(" ") - sb.WriteString(strings.TrimLeft(child.Properties.String(), " ")) - } - } - sb.WriteString("; }\n") -} - -func (n *NiriProvider) writeArg(sb *strings.Builder, val string, forceQuote bool) { - if !forceQuote && n.isNumericArg(val) { - sb.WriteString(val) - return - } - sb.WriteString("\"") - sb.WriteString(strings.ReplaceAll(val, "\"", "\\\"")) - sb.WriteString("\"") -} - -func (n *NiriProvider) isNumericArg(val string) bool { - if val == "" { - return false - } - start := 0 - if val[0] == '-' || val[0] == '+' { - if len(val) == 1 { - return false - } - start = 1 - } - for i := start; i < len(val); i++ { - if val[i] < '0' || val[i] > '9' { - return false - } - } - return true -} - -func (n *NiriProvider) validateBindsContent(content string) error { - tmpFile, err := os.CreateTemp("", "dms-binds-*.kdl") - if err != nil { - return fmt.Errorf("failed to create temp file: %w", err) - } - defer os.Remove(tmpFile.Name()) - - if _, err := tmpFile.WriteString(content); err != nil { - tmpFile.Close() - return fmt.Errorf("failed to write temp file: %w", err) - } - tmpFile.Close() - - cmd := exec.Command("niri", "validate", "-c", tmpFile.Name()) - output, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("invalid config: %s", strings.TrimSpace(string(output))) - } - - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_parser.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_parser.go deleted file mode 100644 index 46520ff..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_parser.go +++ /dev/null @@ -1,400 +0,0 @@ -package providers - -import ( - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/sblinch/kdl-go" - "github.com/sblinch/kdl-go/document" -) - -type NiriKeyBinding struct { - Mods []string - Key string - Action string - Args []string - Description string - HideOnOverlay bool - CooldownMs int - AllowWhenLocked bool - AllowInhibiting *bool - Repeat *bool - Source string -} - -type NiriSection struct { - Name string - Keybinds []NiriKeyBinding - Children []NiriSection -} - -type NiriParser struct { - configDir string - processedFiles map[string]bool - bindMap map[string]*NiriKeyBinding - bindOrder []string - currentSource string - dmsBindsIncluded bool - dmsBindsExists bool - includeCount int - dmsIncludePos int - bindsBeforeDMS int - bindsAfterDMS int - dmsBindKeys map[string]bool - configBindKeys map[string]bool - dmsProcessed bool - dmsBindMap map[string]*NiriKeyBinding - conflictingConfigs map[string]*NiriKeyBinding -} - -func NewNiriParser(configDir string) *NiriParser { - return &NiriParser{ - configDir: configDir, - processedFiles: make(map[string]bool), - bindMap: make(map[string]*NiriKeyBinding), - bindOrder: []string{}, - currentSource: "", - dmsIncludePos: -1, - dmsBindKeys: make(map[string]bool), - configBindKeys: make(map[string]bool), - dmsBindMap: make(map[string]*NiriKeyBinding), - conflictingConfigs: make(map[string]*NiriKeyBinding), - } -} - -func (p *NiriParser) Parse() (*NiriSection, error) { - dmsBindsPath := filepath.Join(p.configDir, "dms", "binds.kdl") - if _, err := os.Stat(dmsBindsPath); err == nil { - p.dmsBindsExists = true - } - - configPath := filepath.Join(p.configDir, "config.kdl") - section, err := p.parseFile(configPath, "") - if err != nil { - return nil, err - } - - if p.dmsBindsExists && !p.dmsProcessed { - p.parseDMSBindsDirectly(dmsBindsPath, section) - } - - section.Keybinds = p.finalizeBinds() - return section, nil -} - -func (p *NiriParser) parseDMSBindsDirectly(dmsBindsPath string, section *NiriSection) { - data, err := os.ReadFile(dmsBindsPath) - if err != nil { - return - } - - doc, err := kdl.Parse(strings.NewReader(string(data))) - if err != nil { - return - } - - prevSource := p.currentSource - p.currentSource = dmsBindsPath - baseDir := filepath.Dir(dmsBindsPath) - p.processNodes(doc.Nodes, section, baseDir) - p.currentSource = prevSource - p.dmsProcessed = true -} - -func (p *NiriParser) finalizeBinds() []NiriKeyBinding { - binds := make([]NiriKeyBinding, 0, len(p.bindOrder)) - for _, key := range p.bindOrder { - if kb, ok := p.bindMap[key]; ok { - binds = append(binds, *kb) - } - } - return binds -} - -func (p *NiriParser) addBind(kb *NiriKeyBinding) { - key := p.formatBindKey(kb) - isDMSBind := strings.Contains(kb.Source, "dms/binds.kdl") - - if isDMSBind { - p.dmsBindKeys[key] = true - p.dmsBindMap[key] = kb - } else if p.dmsBindKeys[key] { - p.bindsAfterDMS++ - p.conflictingConfigs[key] = kb - p.configBindKeys[key] = true - return - } else { - p.configBindKeys[key] = true - } - - if _, exists := p.bindMap[key]; !exists { - p.bindOrder = append(p.bindOrder, key) - } - p.bindMap[key] = kb -} - -func (p *NiriParser) formatBindKey(kb *NiriKeyBinding) string { - parts := make([]string, 0, len(kb.Mods)+1) - parts = append(parts, kb.Mods...) - parts = append(parts, kb.Key) - return strings.Join(parts, "+") -} - -func (p *NiriParser) parseFile(filePath, sectionName string) (*NiriSection, error) { - absPath, err := filepath.Abs(filePath) - if err != nil { - return nil, fmt.Errorf("failed to resolve path %s: %w", filePath, err) - } - - if p.processedFiles[absPath] { - return &NiriSection{Name: sectionName}, nil - } - p.processedFiles[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return nil, fmt.Errorf("failed to read %s: %w", absPath, err) - } - - doc, err := kdl.Parse(strings.NewReader(string(data))) - if err != nil { - return nil, fmt.Errorf("failed to parse KDL in %s: %w", absPath, err) - } - - section := &NiriSection{ - Name: sectionName, - } - - prevSource := p.currentSource - p.currentSource = absPath - baseDir := filepath.Dir(absPath) - p.processNodes(doc.Nodes, section, baseDir) - p.currentSource = prevSource - - return section, nil -} - -func (p *NiriParser) processNodes(nodes []*document.Node, section *NiriSection, baseDir string) { - for _, node := range nodes { - name := node.Name.String() - - switch name { - case "include": - p.handleInclude(node, section, baseDir) - case "binds": - p.extractBinds(node, section, "") - case "recent-windows": - p.handleRecentWindows(node, section) - } - } -} - -func (p *NiriParser) handleInclude(node *document.Node, section *NiriSection, baseDir string) { - if len(node.Arguments) == 0 { - return - } - - includePath := strings.Trim(node.Arguments[0].String(), "\"") - isDMSInclude := includePath == "dms/binds.kdl" || strings.HasSuffix(includePath, "/dms/binds.kdl") - - p.includeCount++ - if isDMSInclude { - p.dmsBindsIncluded = true - p.dmsIncludePos = p.includeCount - p.bindsBeforeDMS = len(p.bindMap) - } - - fullPath := filepath.Join(baseDir, includePath) - if filepath.IsAbs(includePath) { - fullPath = includePath - } - - if isDMSInclude { - p.dmsProcessed = true - } - - includedSection, err := p.parseFile(fullPath, "") - if err != nil { - return - } - - section.Children = append(section.Children, includedSection.Children...) -} - -func (p *NiriParser) HasDMSBindsIncluded() bool { - return p.dmsBindsIncluded -} - -func (p *NiriParser) handleRecentWindows(node *document.Node, section *NiriSection) { - if node.Children == nil { - return - } - - for _, child := range node.Children { - if child.Name.String() != "binds" { - continue - } - p.extractBinds(child, section, "Alt-Tab") - } -} - -func (p *NiriParser) extractBinds(node *document.Node, section *NiriSection, subcategory string) { - if node.Children == nil { - return - } - - for _, child := range node.Children { - kb := p.parseKeybindNode(child, subcategory) - if kb == nil { - continue - } - p.addBind(kb) - } -} - -func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBinding { - keyCombo := node.Name.String() - if keyCombo == "" { - return nil - } - - mods, key := p.parseKeyCombo(keyCombo) - - var action string - var args []string - if len(node.Children) > 0 { - actionNode := node.Children[0] - action = actionNode.Name.String() - for _, arg := range actionNode.Arguments { - args = append(args, arg.ValueString()) - } - if actionNode.Properties != nil { - for _, propName := range []string{"focus", "show-pointer", "write-to-disk", "skip-confirmation", "delay-ms"} { - if val, ok := actionNode.Properties.Get(propName); ok { - args = append(args, propName+"="+val.String()) - } - } - } - } - - var description string - var hideOnOverlay bool - var cooldownMs int - var allowWhenLocked bool - var allowInhibiting *bool - var repeat *bool - if node.Properties != nil { - if val, ok := node.Properties.Get("hotkey-overlay-title"); ok { - switch val.ValueString() { - case "null", "": - hideOnOverlay = true - default: - description = val.ValueString() - } - } - if val, ok := node.Properties.Get("cooldown-ms"); ok { - cooldownMs, _ = strconv.Atoi(val.String()) - } - if val, ok := node.Properties.Get("allow-when-locked"); ok { - allowWhenLocked = val.String() == "true" - } - if val, ok := node.Properties.Get("allow-inhibiting"); ok { - v := val.String() == "true" - allowInhibiting = &v - } - if val, ok := node.Properties.Get("repeat"); ok { - v := val.String() == "true" - repeat = &v - } - } - - return &NiriKeyBinding{ - Mods: mods, - Key: key, - Action: action, - Args: args, - Description: description, - HideOnOverlay: hideOnOverlay, - CooldownMs: cooldownMs, - AllowWhenLocked: allowWhenLocked, - AllowInhibiting: allowInhibiting, - Repeat: repeat, - Source: p.currentSource, - } -} - -func (p *NiriParser) parseKeyCombo(combo string) ([]string, string) { - parts := strings.Split(combo, "+") - - switch len(parts) { - case 0: - return nil, combo - case 1: - return nil, parts[0] - default: - return parts[:len(parts)-1], parts[len(parts)-1] - } -} - -type NiriParseResult struct { - Section *NiriSection - DMSBindsIncluded bool - DMSStatus *DMSBindsStatusInfo - ConflictingConfigs map[string]*NiriKeyBinding -} - -type DMSBindsStatusInfo struct { - Exists bool - Included bool - IncludePosition int - TotalIncludes int - BindsAfterDMS int - Effective bool - OverriddenBy int - StatusMessage string -} - -func (p *NiriParser) buildDMSStatus() *DMSBindsStatusInfo { - status := &DMSBindsStatusInfo{ - Exists: p.dmsBindsExists, - Included: p.dmsBindsIncluded, - IncludePosition: p.dmsIncludePos, - TotalIncludes: p.includeCount, - BindsAfterDMS: p.bindsAfterDMS, - } - - switch { - case !p.dmsBindsExists: - status.Effective = false - status.StatusMessage = "dms/binds.kdl does not exist" - case !p.dmsBindsIncluded: - status.Effective = false - status.StatusMessage = "dms/binds.kdl is not included in config.kdl" - case p.bindsAfterDMS > 0: - status.Effective = true - status.OverriddenBy = p.bindsAfterDMS - status.StatusMessage = "Some DMS binds may be overridden by config binds" - default: - status.Effective = true - status.StatusMessage = "DMS binds are active" - } - - return status -} - -func ParseNiriKeys(configDir string) (*NiriParseResult, error) { - parser := NewNiriParser(configDir) - section, err := parser.Parse() - if err != nil { - return nil, err - } - return &NiriParseResult{ - Section: section, - DMSBindsIncluded: parser.HasDMSBindsIncluded(), - DMSStatus: parser.buildDMSStatus(), - ConflictingConfigs: parser.conflictingConfigs, - }, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_parser_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_parser_test.go deleted file mode 100644 index e21fde5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_parser_test.go +++ /dev/null @@ -1,630 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestNiriParseKeyCombo(t *testing.T) { - tests := []struct { - combo string - expectedMods []string - expectedKey string - }{ - {"Mod+Q", []string{"Mod"}, "Q"}, - {"Mod+Shift+F", []string{"Mod", "Shift"}, "F"}, - {"Ctrl+Alt+Delete", []string{"Ctrl", "Alt"}, "Delete"}, - {"Print", nil, "Print"}, - {"XF86AudioMute", nil, "XF86AudioMute"}, - {"Super+Tab", []string{"Super"}, "Tab"}, - {"Mod+Shift+Ctrl+H", []string{"Mod", "Shift", "Ctrl"}, "H"}, - } - - parser := NewNiriParser("") - for _, tt := range tests { - t.Run(tt.combo, func(t *testing.T) { - mods, key := parser.parseKeyCombo(tt.combo) - - if len(mods) != len(tt.expectedMods) { - t.Errorf("Mods length = %d, want %d", len(mods), len(tt.expectedMods)) - } else { - for i := range mods { - if mods[i] != tt.expectedMods[i] { - t.Errorf("Mods[%d] = %q, want %q", i, mods[i], tt.expectedMods[i]) - } - } - } - - if key != tt.expectedKey { - t.Errorf("Key = %q, want %q", key, tt.expectedKey) - } - }) - } -} - -func TestNiriParseBasicBinds(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Mod+Q { close-window; } - Mod+F { fullscreen-window; } - Mod+T hotkey-overlay-title="Open Terminal" { spawn "kitty"; } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 3 { - t.Errorf("Expected 3 keybinds, got %d", len(result.Section.Keybinds)) - } - - foundClose := false - foundFullscreen := false - foundTerminal := false - - for _, kb := range result.Section.Keybinds { - switch kb.Action { - case "close-window": - foundClose = true - if kb.Key != "Q" || len(kb.Mods) != 1 || kb.Mods[0] != "Mod" { - t.Errorf("close-window keybind mismatch: %+v", kb) - } - case "fullscreen-window": - foundFullscreen = true - case "spawn": - foundTerminal = true - if kb.Description != "Open Terminal" { - t.Errorf("spawn description = %q, want %q", kb.Description, "Open Terminal") - } - if len(kb.Args) != 1 || kb.Args[0] != "kitty" { - t.Errorf("spawn args = %v, want [kitty]", kb.Args) - } - } - } - - if !foundClose { - t.Error("close-window keybind not found") - } - if !foundFullscreen { - t.Error("fullscreen-window keybind not found") - } - if !foundTerminal { - t.Error("spawn keybind not found") - } -} - -func TestNiriParseRecentWindows(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `recent-windows { - binds { - Alt+Tab { next-window scope="output"; } - Alt+Shift+Tab { previous-window scope="output"; } - } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds from recent-windows, got %d", len(result.Section.Keybinds)) - } - - foundNext := false - foundPrev := false - - for _, kb := range result.Section.Keybinds { - switch kb.Action { - case "next-window": - foundNext = true - case "previous-window": - foundPrev = true - } - } - - if !foundNext { - t.Error("next-window keybind not found") - } - if !foundPrev { - t.Error("previous-window keybind not found") - } -} - -func TestNiriParseInclude(t *testing.T) { - tmpDir := t.TempDir() - subDir := filepath.Join(tmpDir, "dms") - if err := os.MkdirAll(subDir, 0o755); err != nil { - t.Fatalf("Failed to create subdir: %v", err) - } - - mainConfig := filepath.Join(tmpDir, "config.kdl") - includeConfig := filepath.Join(subDir, "binds.kdl") - - mainContent := `binds { - Mod+Q { close-window; } -} -include "dms/binds.kdl" -` - includeContent := `binds { - Mod+T hotkey-overlay-title="Terminal" { spawn "kitty"; } -} -` - - if err := os.WriteFile(mainConfig, []byte(mainContent), 0o644); err != nil { - t.Fatalf("Failed to write main config: %v", err) - } - if err := os.WriteFile(includeConfig, []byte(includeContent), 0o644); err != nil { - t.Fatalf("Failed to write include config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds (1 main + 1 include), got %d", len(result.Section.Keybinds)) - } -} - -func TestNiriParseIncludeOverride(t *testing.T) { - tmpDir := t.TempDir() - subDir := filepath.Join(tmpDir, "dms") - if err := os.MkdirAll(subDir, 0o755); err != nil { - t.Fatalf("Failed to create subdir: %v", err) - } - - mainConfig := filepath.Join(tmpDir, "config.kdl") - includeConfig := filepath.Join(subDir, "binds.kdl") - - mainContent := `binds { - Mod+T hotkey-overlay-title="Main Terminal" { spawn "alacritty"; } -} -include "dms/binds.kdl" -` - includeContent := `binds { - Mod+T hotkey-overlay-title="Override Terminal" { spawn "kitty"; } -} -` - - if err := os.WriteFile(mainConfig, []byte(mainContent), 0o644); err != nil { - t.Fatalf("Failed to write main config: %v", err) - } - if err := os.WriteFile(includeConfig, []byte(includeContent), 0o644); err != nil { - t.Fatalf("Failed to write include config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 1 { - t.Errorf("Expected 1 keybind (later overrides earlier), got %d", len(result.Section.Keybinds)) - } - - if len(result.Section.Keybinds) > 0 { - kb := result.Section.Keybinds[0] - if kb.Description != "Override Terminal" { - t.Errorf("Expected description 'Override Terminal' (from include), got %q", kb.Description) - } - if len(kb.Args) != 1 || kb.Args[0] != "kitty" { - t.Errorf("Expected args [kitty] (from include), got %v", kb.Args) - } - } -} - -func TestNiriParseCircularInclude(t *testing.T) { - tmpDir := t.TempDir() - - mainConfig := filepath.Join(tmpDir, "config.kdl") - otherConfig := filepath.Join(tmpDir, "other.kdl") - - mainContent := `binds { - Mod+Q { close-window; } -} -include "other.kdl" -` - otherContent := `binds { - Mod+T { spawn "kitty"; } -} -include "config.kdl" -` - - if err := os.WriteFile(mainConfig, []byte(mainContent), 0o644); err != nil { - t.Fatalf("Failed to write main config: %v", err) - } - if err := os.WriteFile(otherConfig, []byte(otherContent), 0o644); err != nil { - t.Fatalf("Failed to write other config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed (should handle circular includes): %v", err) - } - - if len(result.Section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds (circular include handled), got %d", len(result.Section.Keybinds)) - } -} - -func TestNiriParseMissingInclude(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Mod+Q { close-window; } -} -include "nonexistent/file.kdl" -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed (should skip missing include): %v", err) - } - - if len(result.Section.Keybinds) != 1 { - t.Errorf("Expected 1 keybind (missing include skipped), got %d", len(result.Section.Keybinds)) - } -} - -func TestNiriParseNoBinds(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `cursor { - xcursor-theme "Bibata" - xcursor-size 24 -} - -input { - keyboard { - numlock - } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 0 { - t.Errorf("Expected 0 keybinds, got %d", len(result.Section.Keybinds)) - } -} - -func TestNiriParseErrors(t *testing.T) { - tests := []struct { - name string - path string - }{ - { - name: "nonexistent_directory", - path: "/nonexistent/path/that/does/not/exist", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := ParseNiriKeys(tt.path) - if err == nil { - t.Error("Expected error, got nil") - } - }) - } -} - -func TestNiriBindOverrideBehavior(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Mod+T hotkey-overlay-title="First" { spawn "first"; } - Mod+Q { close-window; } - Mod+T hotkey-overlay-title="Second" { spawn "second"; } - Mod+F { fullscreen-window; } - Mod+T hotkey-overlay-title="Third" { spawn "third"; } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 3 { - t.Fatalf("Expected 3 unique keybinds, got %d", len(result.Section.Keybinds)) - } - - var modT *NiriKeyBinding - for i := range result.Section.Keybinds { - kb := &result.Section.Keybinds[i] - if len(kb.Mods) == 1 && kb.Mods[0] == "Mod" && kb.Key == "T" { - modT = kb - break - } - } - - if modT == nil { - t.Fatal("Mod+T keybind not found") - } - - if modT.Description != "Third" { - t.Errorf("Mod+T description = %q, want 'Third' (last definition wins)", modT.Description) - } - - if len(modT.Args) != 1 || modT.Args[0] != "third" { - t.Errorf("Mod+T args = %v, want [third] (last definition wins)", modT.Args) - } -} - -func TestNiriBindOverrideWithIncludes(t *testing.T) { - tmpDir := t.TempDir() - subDir := filepath.Join(tmpDir, "custom") - if err := os.MkdirAll(subDir, 0o755); err != nil { - t.Fatalf("Failed to create subdir: %v", err) - } - - mainConfig := filepath.Join(tmpDir, "config.kdl") - includeConfig := filepath.Join(subDir, "overrides.kdl") - - mainContent := `binds { - Mod+1 { focus-workspace 1; } - Mod+2 { focus-workspace 2; } - Mod+T hotkey-overlay-title="Default Terminal" { spawn "xterm"; } -} -include "custom/overrides.kdl" -binds { - Mod+3 { focus-workspace 3; } -} -` - includeContent := `binds { - Mod+T hotkey-overlay-title="Custom Terminal" { spawn "kitty"; } - Mod+2 { focus-workspace 22; } -} -` - - if err := os.WriteFile(mainConfig, []byte(mainContent), 0o644); err != nil { - t.Fatalf("Failed to write main config: %v", err) - } - if err := os.WriteFile(includeConfig, []byte(includeContent), 0o644); err != nil { - t.Fatalf("Failed to write include config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 4 { - t.Errorf("Expected 4 unique keybinds, got %d", len(result.Section.Keybinds)) - } - - bindMap := make(map[string]*NiriKeyBinding) - for i := range result.Section.Keybinds { - kb := &result.Section.Keybinds[i] - key := "" - for _, m := range kb.Mods { - key += m + "+" - } - key += kb.Key - bindMap[key] = kb - } - - if kb, ok := bindMap["Mod+T"]; ok { - if kb.Description != "Custom Terminal" { - t.Errorf("Mod+T should be overridden by include, got description %q", kb.Description) - } - } else { - t.Error("Mod+T not found") - } - - if kb, ok := bindMap["Mod+2"]; ok { - if len(kb.Args) != 1 || kb.Args[0] != "22" { - t.Errorf("Mod+2 should be overridden by include with workspace 22, got args %v", kb.Args) - } - } else { - t.Error("Mod+2 not found") - } - - if _, ok := bindMap["Mod+1"]; !ok { - t.Error("Mod+1 should exist (not overridden)") - } - - if _, ok := bindMap["Mod+3"]; !ok { - t.Error("Mod+3 should exist (added after include)") - } -} - -func TestNiriParseMultipleArgs(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Mod+Space hotkey-overlay-title="Application Launcher" { - spawn "dms" "ipc" "call" "spotlight" "toggle"; - } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 1 { - t.Fatalf("Expected 1 keybind, got %d", len(result.Section.Keybinds)) - } - - kb := result.Section.Keybinds[0] - if len(kb.Args) != 5 { - t.Errorf("Expected 5 args, got %d: %v", len(kb.Args), kb.Args) - } - - expectedArgs := []string{"dms", "ipc", "call", "spotlight", "toggle"} - for i, arg := range expectedArgs { - if i < len(kb.Args) && kb.Args[i] != arg { - t.Errorf("Args[%d] = %q, want %q", i, kb.Args[i], arg) - } - } -} - -func TestNiriParseNumericWorkspaceBinds(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Mod+1 hotkey-overlay-title="Focus Workspace 1" { focus-workspace 1; } - Mod+2 hotkey-overlay-title="Focus Workspace 2" { focus-workspace 2; } - Mod+0 hotkey-overlay-title="Focus Workspace 10" { focus-workspace 10; } - Mod+Shift+1 hotkey-overlay-title="Move to Workspace 1" { move-column-to-workspace 1; } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 4 { - t.Errorf("Expected 4 keybinds, got %d", len(result.Section.Keybinds)) - } - - for _, kb := range result.Section.Keybinds { - switch kb.Key { - case "1": - if len(kb.Mods) == 1 && kb.Mods[0] == "Mod" { - if kb.Action != "focus-workspace" || len(kb.Args) != 1 || kb.Args[0] != "1" { - t.Errorf("Mod+1 action/args mismatch: %+v", kb) - } - if kb.Description != "Focus Workspace 1" { - t.Errorf("Mod+1 description = %q, want 'Focus Workspace 1'", kb.Description) - } - } - case "0": - if kb.Action != "focus-workspace" || len(kb.Args) != 1 || kb.Args[0] != "10" { - t.Errorf("Mod+0 action/args mismatch: %+v", kb) - } - } - } -} - -func TestNiriParseQuotedStringArgs(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Super+Minus hotkey-overlay-title="Adjust Column Width -10%" { set-column-width "-10%"; } - Super+Equal hotkey-overlay-title="Adjust Column Width +10%" { set-column-width "+10%"; } - Super+Shift+Minus hotkey-overlay-title="Adjust Window Height -10%" { set-window-height "-10%"; } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 3 { - t.Errorf("Expected 3 keybinds, got %d", len(result.Section.Keybinds)) - } - - for _, kb := range result.Section.Keybinds { - if kb.Action == "set-column-width" { - if len(kb.Args) != 1 { - t.Errorf("set-column-width should have 1 arg, got %d", len(kb.Args)) - continue - } - if kb.Args[0] != "-10%" && kb.Args[0] != "+10%" { - t.Errorf("set-column-width arg = %q, want -10%% or +10%%", kb.Args[0]) - } - } - } -} - -func TestNiriParseActionWithProperties(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Mod+Shift+1 hotkey-overlay-title="Move to Workspace 1" { move-column-to-workspace 1 focus=false; } - Mod+Shift+2 hotkey-overlay-title="Move to Workspace 2" { move-column-to-workspace 2 focus=false; } - Alt+Tab { next-window scope="output"; } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("ParseNiriKeys failed: %v", err) - } - - if len(result.Section.Keybinds) != 3 { - t.Errorf("Expected 3 keybinds, got %d", len(result.Section.Keybinds)) - } - - for _, kb := range result.Section.Keybinds { - switch kb.Action { - case "move-column-to-workspace": - if len(kb.Args) != 2 { - t.Errorf("move-column-to-workspace should have 2 args (index + focus), got %d", len(kb.Args)) - } - hasIndex := false - hasFocus := false - for _, arg := range kb.Args { - if arg == "1" || arg == "2" { - hasIndex = true - } - if arg == "focus=false" { - hasFocus = true - } - } - if !hasIndex { - t.Errorf("move-column-to-workspace missing index arg") - } - if !hasFocus { - t.Errorf("move-column-to-workspace missing focus=false arg") - } - case "next-window": - if kb.Key != "Tab" { - t.Errorf("next-window key = %q, want 'Tab'", kb.Key) - } - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_test.go deleted file mode 100644 index b04c5b2..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/niri_test.go +++ /dev/null @@ -1,661 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestNiriProviderName(t *testing.T) { - provider := NewNiriProvider("") - if provider.Name() != "niri" { - t.Errorf("Name() = %q, want %q", provider.Name(), "niri") - } -} - -func TestNiriProviderGetCheatSheet(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Mod+Q { close-window; } - Mod+F { fullscreen-window; } - Mod+T hotkey-overlay-title="Open Terminal" { spawn "kitty"; } - Mod+1 { focus-workspace 1; } - Mod+Shift+1 { move-column-to-workspace 1; } - Print { screenshot; } - Mod+Shift+E { quit; } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - provider := NewNiriProvider(tmpDir) - cheatSheet, err := provider.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - if cheatSheet.Title != "Niri Keybinds" { - t.Errorf("Title = %q, want %q", cheatSheet.Title, "Niri Keybinds") - } - - if cheatSheet.Provider != "niri" { - t.Errorf("Provider = %q, want %q", cheatSheet.Provider, "niri") - } - - windowBinds := cheatSheet.Binds["Window"] - if len(windowBinds) < 2 { - t.Errorf("Expected at least 2 Window binds, got %d", len(windowBinds)) - } - - execBinds := cheatSheet.Binds["Execute"] - if len(execBinds) < 1 { - t.Errorf("Expected at least 1 Execute bind, got %d", len(execBinds)) - } - - workspaceBinds := cheatSheet.Binds["Workspace"] - if len(workspaceBinds) < 2 { - t.Errorf("Expected at least 2 Workspace binds, got %d", len(workspaceBinds)) - } - - screenshotBinds := cheatSheet.Binds["Screenshot"] - if len(screenshotBinds) < 1 { - t.Errorf("Expected at least 1 Screenshot bind, got %d", len(screenshotBinds)) - } - - systemBinds := cheatSheet.Binds["System"] - if len(systemBinds) < 1 { - t.Errorf("Expected at least 1 System bind, got %d", len(systemBinds)) - } -} - -func TestNiriCategorizeByAction(t *testing.T) { - provider := NewNiriProvider("") - - tests := []struct { - action string - expected string - }{ - {"focus-workspace", "Workspace"}, - {"focus-workspace-up", "Workspace"}, - {"move-column-to-workspace", "Workspace"}, - {"focus-monitor-left", "Monitor"}, - {"move-column-to-monitor-right", "Monitor"}, - {"close-window", "Window"}, - {"fullscreen-window", "Window"}, - {"maximize-column", "Window"}, - {"toggle-window-floating", "Window"}, - {"focus-column-left", "Window"}, - {"move-column-right", "Window"}, - {"spawn", "Execute"}, - {"quit", "System"}, - {"power-off-monitors", "System"}, - {"screenshot", "Screenshot"}, - {"screenshot-window", "Screenshot"}, - {"toggle-overview", "Overview"}, - {"show-hotkey-overlay", "Overview"}, - {"next-window", "Alt-Tab"}, - {"previous-window", "Alt-Tab"}, - {"unknown-action", "Other"}, - } - - for _, tt := range tests { - t.Run(tt.action, func(t *testing.T) { - result := provider.categorizeByAction(tt.action) - if result != tt.expected { - t.Errorf("categorizeByAction(%q) = %q, want %q", tt.action, result, tt.expected) - } - }) - } -} - -func TestNiriFormatRawAction(t *testing.T) { - provider := NewNiriProvider("") - - tests := []struct { - action string - args []string - expected string - }{ - {"spawn", []string{"kitty"}, "spawn kitty"}, - {"spawn", []string{"dms", "ipc", "call"}, "spawn dms ipc call"}, - {"spawn", []string{"dms", "ipc", "call", "brightness", "increment", "5", ""}, `spawn dms ipc call brightness increment 5 ""`}, - {"spawn", []string{"dms", "ipc", "call", "dash", "toggle", ""}, `spawn dms ipc call dash toggle ""`}, - {"close-window", nil, "close-window"}, - {"fullscreen-window", nil, "fullscreen-window"}, - {"focus-workspace", []string{"1"}, "focus-workspace 1"}, - {"move-column-to-workspace", []string{"5"}, "move-column-to-workspace 5"}, - {"set-column-width", []string{"+10%"}, "set-column-width +10%"}, - } - - for _, tt := range tests { - t.Run(tt.action, func(t *testing.T) { - result := provider.formatRawAction(tt.action, tt.args) - if result != tt.expected { - t.Errorf("formatRawAction(%q, %v) = %q, want %q", tt.action, tt.args, result, tt.expected) - } - }) - } -} - -func TestNiriFormatKey(t *testing.T) { - provider := NewNiriProvider("") - - tests := []struct { - mods []string - key string - expected string - }{ - {[]string{"Mod"}, "Q", "Mod+Q"}, - {[]string{"Mod", "Shift"}, "F", "Mod+Shift+F"}, - {[]string{"Ctrl", "Alt"}, "Delete", "Ctrl+Alt+Delete"}, - {nil, "Print", "Print"}, - {[]string{}, "XF86AudioMute", "XF86AudioMute"}, - } - - for _, tt := range tests { - t.Run(tt.expected, func(t *testing.T) { - kb := &NiriKeyBinding{ - Mods: tt.mods, - Key: tt.key, - } - result := provider.formatKey(kb) - if result != tt.expected { - t.Errorf("formatKey(%v) = %q, want %q", kb, result, tt.expected) - } - }) - } -} - -func TestNiriDefaultConfigDir(t *testing.T) { - originalXDG := os.Getenv("XDG_CONFIG_HOME") - defer os.Setenv("XDG_CONFIG_HOME", originalXDG) - - os.Setenv("XDG_CONFIG_HOME", "/custom/config") - dir := defaultNiriConfigDir() - if dir != "/custom/config/niri" { - t.Errorf("With XDG_CONFIG_HOME set, got %q, want %q", dir, "/custom/config/niri") - } - - os.Unsetenv("XDG_CONFIG_HOME") - dir = defaultNiriConfigDir() - home, _ := os.UserHomeDir() - expected := filepath.Join(home, ".config", "niri") - if dir != expected { - t.Errorf("Without XDG_CONFIG_HOME, got %q, want %q", dir, expected) - } -} - -func TestNiriGenerateBindsContent(t *testing.T) { - provider := NewNiriProvider("") - - tests := []struct { - name string - binds map[string]*overrideBind - expected string - }{ - { - name: "empty binds", - binds: map[string]*overrideBind{}, - expected: "binds {}\n", - }, - { - name: "simple spawn bind", - binds: map[string]*overrideBind{ - "Mod+T": { - Key: "Mod+T", - Action: "spawn kitty", - Description: "Open Terminal", - }, - }, - expected: `binds { - Mod+T hotkey-overlay-title="Open Terminal" { spawn "kitty"; } -} -`, - }, - { - name: "spawn with multiple args", - binds: map[string]*overrideBind{ - "Mod+Space": { - Key: "Mod+Space", - Action: `spawn "dms" "ipc" "call" "spotlight" "toggle"`, - Description: "Application Launcher", - }, - }, - expected: `binds { - Mod+Space hotkey-overlay-title="Application Launcher" { spawn "dms" "ipc" "call" "spotlight" "toggle"; } -} -`, - }, - { - name: "bind with allow-when-locked", - binds: map[string]*overrideBind{ - "XF86AudioMute": { - Key: "XF86AudioMute", - Action: `spawn "dms" "ipc" "call" "audio" "mute"`, - Options: map[string]any{"allow-when-locked": true}, - }, - }, - expected: `binds { - XF86AudioMute allow-when-locked=true { spawn "dms" "ipc" "call" "audio" "mute"; } -} -`, - }, - { - name: "simple action without args", - binds: map[string]*overrideBind{ - "Mod+Q": { - Key: "Mod+Q", - Action: "close-window", - Description: "Close Window", - }, - }, - expected: `binds { - Mod+Q hotkey-overlay-title="Close Window" { close-window; } -} -`, - }, - { - name: "recent-windows action", - binds: map[string]*overrideBind{ - "Alt+Tab": { - Key: "Alt+Tab", - Action: "next-window", - }, - }, - expected: `binds { -} - -recent-windows { - binds { - Alt+Tab { next-window; } - } -} -`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := provider.generateBindsContent(tt.binds) - if result != tt.expected { - t.Errorf("generateBindsContent() =\n%q\nwant:\n%q", result, tt.expected) - } - }) - } -} - -func TestNiriGenerateBindsContentRoundTrip(t *testing.T) { - provider := NewNiriProvider("") - - binds := map[string]*overrideBind{ - "Mod+Space": { - Key: "Mod+Space", - Action: `spawn "dms" "ipc" "call" "spotlight" "toggle"`, - Description: "Application Launcher", - }, - "XF86AudioMute": { - Key: "XF86AudioMute", - Action: `spawn "dms" "ipc" "call" "audio" "mute"`, - Options: map[string]any{"allow-when-locked": true}, - }, - "Mod+Q": { - Key: "Mod+Q", - Action: "close-window", - Description: "Close Window", - }, - } - - content := provider.generateBindsContent(binds) - - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write temp file: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("Failed to parse generated content: %v\nContent was:\n%s", err, content) - } - - if len(result.Section.Keybinds) != 3 { - t.Errorf("Expected 3 keybinds after round-trip, got %d", len(result.Section.Keybinds)) - } -} - -func TestNiriEmptyArgsPreservation(t *testing.T) { - provider := NewNiriProvider("") - - binds := map[string]*overrideBind{ - "XF86MonBrightnessUp": { - Key: "XF86MonBrightnessUp", - Action: `spawn dms ipc call brightness increment 5 ""`, - Description: "Brightness Up", - }, - "XF86MonBrightnessDown": { - Key: "XF86MonBrightnessDown", - Action: `spawn dms ipc call brightness decrement 5 ""`, - Description: "Brightness Down", - }, - "Super+Alt+Page_Up": { - Key: "Super+Alt+Page_Up", - Action: `spawn dms ipc call dash toggle ""`, - Description: "Dashboard Toggle", - }, - } - - content := provider.generateBindsContent(binds) - - tmpDir := t.TempDir() - dmsDir := filepath.Join(tmpDir, "dms") - if err := os.MkdirAll(dmsDir, 0o755); err != nil { - t.Fatalf("Failed to create dms directory: %v", err) - } - - bindsFile := filepath.Join(dmsDir, "binds.kdl") - if err := os.WriteFile(bindsFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write binds file: %v", err) - } - - testProvider := NewNiriProvider(tmpDir) - loadedBinds, err := testProvider.loadOverrideBinds() - if err != nil { - t.Fatalf("Failed to load binds: %v\nContent was:\n%s", err, content) - } - - for key, expected := range binds { - loaded, ok := loadedBinds[key] - if !ok { - t.Errorf("Missing bind for key %s", key) - continue - } - if loaded.Action != expected.Action { - t.Errorf("Action mismatch for %s:\n got: %q\n want: %q", key, loaded.Action, expected.Action) - } - } -} - -func TestNiriProviderWithRealWorldConfig(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - - content := `binds { - Mod+Shift+Ctrl+D { debug-toggle-damage; } - Super+D { spawn "niri" "msg" "action" "toggle-overview"; } - Super+Tab repeat=false { toggle-overview; } - Mod+Shift+Slash { show-hotkey-overlay; } - - Mod+T hotkey-overlay-title="Open Terminal" { spawn "kitty"; } - Mod+Space hotkey-overlay-title="Application Launcher" { - spawn "dms" "ipc" "call" "spotlight" "toggle"; - } - - XF86AudioRaiseVolume allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "increment" "3"; - } - XF86AudioLowerVolume allow-when-locked=true { - spawn "dms" "ipc" "call" "audio" "decrement" "3"; - } - - Mod+Q repeat=false { close-window; } - Mod+F { maximize-column; } - Mod+Shift+F { fullscreen-window; } - - Mod+Left { focus-column-left; } - Mod+Down { focus-window-down; } - Mod+Up { focus-window-up; } - Mod+Right { focus-column-right; } - - Mod+1 { focus-workspace 1; } - Mod+2 { focus-workspace 2; } - Mod+Shift+1 { move-column-to-workspace 1; } - Mod+Shift+2 { move-column-to-workspace 2; } - - Print { screenshot; } - Ctrl+Print { screenshot-screen; } - Alt+Print { screenshot-window; } - - Mod+Shift+E { quit; } -} - -recent-windows { - binds { - Alt+Tab { next-window scope="output"; } - Alt+Shift+Tab { previous-window scope="output"; } - } -} -` - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - provider := NewNiriProvider(tmpDir) - cheatSheet, err := provider.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - totalBinds := 0 - for _, binds := range cheatSheet.Binds { - totalBinds += len(binds) - } - - if totalBinds < 20 { - t.Errorf("Expected at least 20 keybinds, got %d", totalBinds) - } - - if len(cheatSheet.Binds["Alt-Tab"]) < 2 { - t.Errorf("Expected at least 2 Alt-Tab binds, got %d", len(cheatSheet.Binds["Alt-Tab"])) - } -} - -func TestNiriGenerateBindsContentNumericArgs(t *testing.T) { - provider := NewNiriProvider("") - - tests := []struct { - name string - binds map[string]*overrideBind - expected string - }{ - { - name: "workspace with numeric arg", - binds: map[string]*overrideBind{ - "Mod+1": { - Key: "Mod+1", - Action: "focus-workspace 1", - Description: "Focus Workspace 1", - }, - }, - expected: `binds { - Mod+1 hotkey-overlay-title="Focus Workspace 1" { focus-workspace 1; } -} -`, - }, - { - name: "workspace with large numeric arg", - binds: map[string]*overrideBind{ - "Mod+0": { - Key: "Mod+0", - Action: "focus-workspace 10", - Description: "Focus Workspace 10", - }, - }, - expected: `binds { - Mod+0 hotkey-overlay-title="Focus Workspace 10" { focus-workspace 10; } -} -`, - }, - { - name: "percentage string arg (should be quoted)", - binds: map[string]*overrideBind{ - "Super+Minus": { - Key: "Super+Minus", - Action: `set-column-width "-10%"`, - Description: "Adjust Column Width -10%", - }, - }, - expected: `binds { - Super+Minus hotkey-overlay-title="Adjust Column Width -10%" { set-column-width "-10%"; } -} -`, - }, - { - name: "positive percentage string arg", - binds: map[string]*overrideBind{ - "Super+Equal": { - Key: "Super+Equal", - Action: `set-column-width "+10%"`, - Description: "Adjust Column Width +10%", - }, - }, - expected: `binds { - Super+Equal hotkey-overlay-title="Adjust Column Width +10%" { set-column-width "+10%"; } -} -`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := provider.generateBindsContent(tt.binds) - if result != tt.expected { - t.Errorf("generateBindsContent() =\n%q\nwant:\n%q", result, tt.expected) - } - }) - } -} - -func TestNiriGenerateActionWithUnquotedPercentArg(t *testing.T) { - provider := NewNiriProvider("") - - binds := map[string]*overrideBind{ - "Super+Equal": { - Key: "Super+Equal", - Action: "set-window-height +10%", - Description: "Adjust Window Height +10%", - }, - } - - content := provider.generateBindsContent(binds) - expected := `binds { - Super+Equal hotkey-overlay-title="Adjust Window Height +10%" { set-window-height "+10%"; } -} -` - if content != expected { - t.Errorf("Content mismatch.\nGot:\n%s\nWant:\n%s", content, expected) - } -} - -func TestNiriGenerateSpawnWithNumericArgs(t *testing.T) { - provider := NewNiriProvider("") - - binds := map[string]*overrideBind{ - "XF86AudioLowerVolume": { - Key: "XF86AudioLowerVolume", - Action: `spawn "dms" "ipc" "call" "audio" "decrement" "3"`, - Options: map[string]any{"allow-when-locked": true}, - }, - } - - content := provider.generateBindsContent(binds) - expected := `binds { - XF86AudioLowerVolume allow-when-locked=true { spawn "dms" "ipc" "call" "audio" "decrement" "3"; } -} -` - if content != expected { - t.Errorf("Content mismatch.\nGot:\n%s\nWant:\n%s", content, expected) - } -} - -func TestNiriGenerateSpawnNumericArgFromCLI(t *testing.T) { - provider := NewNiriProvider("") - - binds := map[string]*overrideBind{ - "XF86AudioLowerVolume": { - Key: "XF86AudioLowerVolume", - Action: "spawn dms ipc call audio decrement 3", - Options: map[string]any{"allow-when-locked": true}, - }, - } - - content := provider.generateBindsContent(binds) - expected := `binds { - XF86AudioLowerVolume allow-when-locked=true { spawn "dms" "ipc" "call" "audio" "decrement" "3"; } -} -` - if content != expected { - t.Errorf("Content mismatch.\nGot:\n%s\nWant:\n%s", content, expected) - } -} - -func TestNiriGenerateWorkspaceBindsRoundTrip(t *testing.T) { - provider := NewNiriProvider("") - - binds := map[string]*overrideBind{ - "Mod+1": { - Key: "Mod+1", - Action: "focus-workspace 1", - Description: "Focus Workspace 1", - }, - "Mod+2": { - Key: "Mod+2", - Action: "focus-workspace 2", - Description: "Focus Workspace 2", - }, - "Mod+Shift+1": { - Key: "Mod+Shift+1", - Action: "move-column-to-workspace 1", - Description: "Move to Workspace 1", - }, - "Super+Minus": { - Key: "Super+Minus", - Action: "set-column-width -10%", - Description: "Adjust Column Width -10%", - }, - } - - content := provider.generateBindsContent(binds) - - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config.kdl") - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write temp file: %v", err) - } - - result, err := ParseNiriKeys(tmpDir) - if err != nil { - t.Fatalf("Failed to parse generated content: %v\nContent was:\n%s", err, content) - } - - if len(result.Section.Keybinds) != 4 { - t.Errorf("Expected 4 keybinds after round-trip, got %d", len(result.Section.Keybinds)) - } - - foundFocusWS1 := false - foundMoveWS1 := false - foundSetWidth := false - - for _, kb := range result.Section.Keybinds { - switch { - case kb.Action == "focus-workspace" && len(kb.Args) > 0 && kb.Args[0] == "1": - foundFocusWS1 = true - case kb.Action == "move-column-to-workspace" && len(kb.Args) > 0 && kb.Args[0] == "1": - foundMoveWS1 = true - case kb.Action == "set-column-width" && len(kb.Args) > 0 && kb.Args[0] == "-10%": - foundSetWidth = true - } - } - - if !foundFocusWS1 { - t.Error("focus-workspace 1 not found after round-trip") - } - if !foundMoveWS1 { - t.Error("move-column-to-workspace 1 not found after round-trip") - } - if !foundSetWidth { - t.Error("set-column-width -10% not found after round-trip") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway.go deleted file mode 100644 index 93ceb73..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway.go +++ /dev/null @@ -1,150 +0,0 @@ -package providers - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds" -) - -type SwayProvider struct { - configPath string - isScroll bool -} - -func NewSwayProvider(configPath string) *SwayProvider { - isScroll := false - _, scrollEnvSet := os.LookupEnv("SCROLLSOCK") - - if configPath == "" { - configDir, err := os.UserConfigDir() - if err != nil { - configDir = "" - } - if scrollEnvSet { - if configDir != "" { - configPath = filepath.Join(configDir, "scroll") - } - isScroll = true - } else { - if configDir != "" { - configPath = filepath.Join(configDir, "sway") - } - } - } else { - isScroll = strings.Contains(configPath, "scroll") - } - - return &SwayProvider{ - configPath: configPath, - isScroll: isScroll, - } -} - -func (s *SwayProvider) Name() string { - if s == nil { - if os.Getenv("SCROLLSOCK") != "" { - return "scroll" - } - return "sway" - } - - if s.isScroll { - return "scroll" - } - return "sway" -} - -func (s *SwayProvider) GetCheatSheet() (*keybinds.CheatSheet, error) { - section, err := ParseSwayKeys(s.configPath) - if err != nil { - return nil, fmt.Errorf("failed to parse sway config: %w", err) - } - - categorizedBinds := make(map[string][]keybinds.Keybind) - s.convertSection(section, "", categorizedBinds) - - cheatSheetTitle := "Sway Keybinds" - if s != nil && s.isScroll { - cheatSheetTitle = "Scroll Keybinds" - } - - return &keybinds.CheatSheet{ - Title: cheatSheetTitle, - Provider: s.Name(), - Binds: categorizedBinds, - }, nil -} - -func (s *SwayProvider) convertSection(section *SwaySection, subcategory string, categorizedBinds map[string][]keybinds.Keybind) { - currentSubcat := subcategory - if section.Name != "" { - currentSubcat = section.Name - } - - for _, kb := range section.Keybinds { - category := s.categorizeByCommand(kb.Command) - bind := s.convertKeybind(&kb, currentSubcat) - categorizedBinds[category] = append(categorizedBinds[category], bind) - } - - for _, child := range section.Children { - s.convertSection(&child, currentSubcat, categorizedBinds) - } -} - -func (s *SwayProvider) categorizeByCommand(command string) string { - command = strings.ToLower(command) - - switch { - case strings.Contains(command, "scratchpad"): - return "Scratchpad" - case strings.Contains(command, "workspace") && strings.Contains(command, "output"): - return "Monitor" - case strings.Contains(command, "workspace"): - return "Workspace" - case strings.Contains(command, "output"): - return "Monitor" - case strings.Contains(command, "layout"): - return "Layout" - case command == "kill" || - command == "fullscreen" || strings.Contains(command, "fullscreen") || - command == "floating toggle" || strings.Contains(command, "floating") || - strings.Contains(command, "focus") || - strings.Contains(command, "move") || - strings.Contains(command, "resize") || - strings.Contains(command, "split"): - return "Window" - case strings.HasPrefix(command, "exec"): - return "Execute" - case command == "exit" || command == "reload": - return "System" - default: - return "Other" - } -} - -func (s *SwayProvider) convertKeybind(kb *SwayKeyBinding, subcategory string) keybinds.Keybind { - key := s.formatKey(kb) - desc := kb.Comment - - if desc == "" { - desc = kb.Command - } - - return keybinds.Keybind{ - Key: key, - Description: desc, - Action: kb.Command, - Subcategory: subcategory, - } -} - -func (s *SwayProvider) formatKey(kb *SwayKeyBinding) string { - parts := make([]string, 0, len(kb.Mods)+1) - parts = append(parts, kb.Mods...) - parts = append(parts, kb.Key) - return strings.Join(parts, "+") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_parser.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_parser.go deleted file mode 100644 index 647c9f7..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_parser.go +++ /dev/null @@ -1,364 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -const ( - SwayTitleRegex = "#+!" - SwayHideComment = "[hidden]" -) - -var SwayModSeparators = []rune{'+', ' '} - -type SwayKeyBinding struct { - Mods []string `json:"mods"` - Key string `json:"key"` - Command string `json:"command"` - Comment string `json:"comment"` -} - -type SwaySection struct { - Children []SwaySection `json:"children"` - Keybinds []SwayKeyBinding `json:"keybinds"` - Name string `json:"name"` -} - -type SwayParser struct { - contentLines []string - readingLine int - variables map[string]string -} - -func NewSwayParser() *SwayParser { - return &SwayParser{ - contentLines: []string{}, - readingLine: 0, - variables: make(map[string]string), - } -} - -func (p *SwayParser) ReadContent(path string) error { - expandedPath, err := utils.ExpandPath(path) - if err != nil { - return err - } - - info, err := os.Stat(expandedPath) - if err != nil { - return err - } - - var files []string - if info.IsDir() { - mainConfig := filepath.Join(expandedPath, "config") - if fileInfo, err := os.Stat(mainConfig); err == nil && fileInfo.Mode().IsRegular() { - files = []string{mainConfig} - } else { - return os.ErrNotExist - } - } else { - files = []string{expandedPath} - } - - var combinedContent []string - for _, file := range files { - data, err := os.ReadFile(file) - if err != nil { - return err - } - combinedContent = append(combinedContent, string(data)) - } - - if len(combinedContent) == 0 { - return os.ErrNotExist - } - - fullContent := strings.Join(combinedContent, "\n") - p.contentLines = strings.Split(fullContent, "\n") - p.parseVariables() - return nil -} - -func (p *SwayParser) parseVariables() { - setRegex := regexp.MustCompile(`^\s*set\s+\$(\w+)\s+(.+)$`) - for _, line := range p.contentLines { - matches := setRegex.FindStringSubmatch(line) - if len(matches) == 3 { - varName := matches[1] - varValue := strings.TrimSpace(matches[2]) - p.variables[varName] = varValue - } - } -} - -func (p *SwayParser) expandVariables(text string) string { - result := text - for varName, varValue := range p.variables { - result = strings.ReplaceAll(result, "$"+varName, varValue) - } - return result -} - -func swayAutogenerateComment(command string) string { - command = strings.TrimSpace(command) - - if strings.HasPrefix(command, "exec ") { - cmdPart := strings.TrimPrefix(command, "exec ") - cmdPart = strings.TrimPrefix(cmdPart, "--no-startup-id ") - return cmdPart - } - - switch { - case command == "kill": - return "Close window" - case command == "exit": - return "Exit Sway" - case command == "reload": - return "Reload configuration" - case strings.HasPrefix(command, "fullscreen"): - return "Toggle fullscreen" - case strings.HasPrefix(command, "floating toggle"): - return "Float/unfloat window" - case strings.HasPrefix(command, "focus mode_toggle"): - return "Toggle focus mode" - case strings.HasPrefix(command, "focus parent"): - return "Focus parent container" - case strings.HasPrefix(command, "focus left"): - return "Focus left" - case strings.HasPrefix(command, "focus right"): - return "Focus right" - case strings.HasPrefix(command, "focus up"): - return "Focus up" - case strings.HasPrefix(command, "focus down"): - return "Focus down" - case strings.HasPrefix(command, "focus output"): - return "Focus monitor" - case strings.HasPrefix(command, "move left"): - return "Move window left" - case strings.HasPrefix(command, "move right"): - return "Move window right" - case strings.HasPrefix(command, "move up"): - return "Move window up" - case strings.HasPrefix(command, "move down"): - return "Move window down" - case strings.HasPrefix(command, "move container to workspace"): - if strings.Contains(command, "prev") { - return "Move to previous workspace" - } - if strings.Contains(command, "next") { - return "Move to next workspace" - } - parts := strings.Fields(command) - if len(parts) > 4 { - return "Move to workspace " + parts[len(parts)-1] - } - return "Move to workspace" - case strings.HasPrefix(command, "move workspace to output"): - return "Move workspace to monitor" - case strings.HasPrefix(command, "workspace"): - if strings.Contains(command, "prev") { - return "Previous workspace" - } - if strings.Contains(command, "next") { - return "Next workspace" - } - parts := strings.Fields(command) - if len(parts) > 1 { - wsNum := parts[len(parts)-1] - return "Workspace " + wsNum - } - return "Switch workspace" - case strings.HasPrefix(command, "layout"): - parts := strings.Fields(command) - if len(parts) > 1 { - return "Layout " + parts[1] - } - return "Change layout" - case strings.HasPrefix(command, "split"): - if strings.Contains(command, "h") { - return "Split horizontal" - } - if strings.Contains(command, "v") { - return "Split vertical" - } - return "Split container" - case strings.HasPrefix(command, "resize"): - return "Resize window" - case strings.Contains(command, "scratchpad"): - return "Toggle scratchpad" - default: - return command - } -} - -func (p *SwayParser) getKeybindAtLine(lineNumber int) *SwayKeyBinding { - if lineNumber >= len(p.contentLines) { - return nil - } - - line := p.contentLines[lineNumber] - - bindMatch := regexp.MustCompile(`^\s*(bindsym|bindcode)\s+(.+)$`) - matches := bindMatch.FindStringSubmatch(line) - if len(matches) < 3 { - return nil - } - - content := matches[2] - - parts := strings.SplitN(content, "#", 2) - keys := strings.TrimSpace(parts[0]) - - var comment string - if len(parts) > 1 { - comment = strings.TrimSpace(parts[1]) - } - - if strings.HasPrefix(comment, SwayHideComment) { - return nil - } - - flags := "" - if strings.HasPrefix(keys, "--") { - spaceIdx := strings.Index(keys, " ") - if spaceIdx > 0 { - flags = keys[:spaceIdx] - keys = strings.TrimSpace(keys[spaceIdx+1:]) - } - } - - keyParts := strings.Fields(keys) - if len(keyParts) < 2 { - return nil - } - - keyCombo := keyParts[0] - keyCombo = p.expandVariables(keyCombo) - command := strings.Join(keyParts[1:], " ") - command = p.expandVariables(command) - - var modList []string - var key string - - modstring := keyCombo + string(SwayModSeparators[0]) - pos := 0 - for index, char := range modstring { - isModSep := false - for _, sep := range SwayModSeparators { - if char == sep { - isModSep = true - break - } - } - if isModSep { - if index-pos > 0 { - part := modstring[pos:index] - if swayIsMod(part) { - modList = append(modList, part) - } else { - key = part - } - } - pos = index + 1 - } - } - - if comment == "" { - comment = swayAutogenerateComment(command) - } - - _ = flags - - return &SwayKeyBinding{ - Mods: modList, - Key: key, - Command: command, - Comment: comment, - } -} - -func swayIsMod(s string) bool { - s = strings.ToLower(s) - if s == "mod1" || s == "mod2" || s == "mod3" || s == "mod4" || s == "mod5" || - s == "shift" || s == "control" || s == "ctrl" || s == "alt" || s == "super" || - strings.HasPrefix(s, "$") { - return true - } - - isNumeric := true - for _, c := range s { - if c < '0' || c > '9' { - isNumeric = false - break - } - } - if isNumeric && len(s) >= 2 { - return true - } - return false -} - -func (p *SwayParser) getBindsRecursive(currentContent *SwaySection, scope int) *SwaySection { - titleRegex := regexp.MustCompile(SwayTitleRegex) - - for p.readingLine < len(p.contentLines) { - line := p.contentLines[p.readingLine] - - loc := titleRegex.FindStringIndex(line) - if loc != nil && loc[0] == 0 { - headingScope := strings.Index(line, "!") - - if headingScope <= scope { - p.readingLine-- - return currentContent - } - - sectionName := strings.TrimSpace(line[headingScope+1:]) - p.readingLine++ - - childSection := &SwaySection{ - Children: []SwaySection{}, - Keybinds: []SwayKeyBinding{}, - Name: sectionName, - } - result := p.getBindsRecursive(childSection, headingScope) - currentContent.Children = append(currentContent.Children, *result) - - } else if line == "" || (!strings.Contains(line, "bindsym") && !strings.Contains(line, "bindcode")) { - - } else { - keybind := p.getKeybindAtLine(p.readingLine) - if keybind != nil { - currentContent.Keybinds = append(currentContent.Keybinds, *keybind) - } - } - - p.readingLine++ - } - - return currentContent -} - -func (p *SwayParser) ParseKeys() *SwaySection { - p.readingLine = 0 - rootSection := &SwaySection{ - Children: []SwaySection{}, - Keybinds: []SwayKeyBinding{}, - Name: "", - } - return p.getBindsRecursive(rootSection, 0) -} - -func ParseSwayKeys(path string) (*SwaySection, error) { - parser := NewSwayParser() - if err := parser.ReadContent(path); err != nil { - return nil, err - } - return parser.ParseKeys(), nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_parser_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_parser_test.go deleted file mode 100644 index 6bb5ecf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_parser_test.go +++ /dev/null @@ -1,471 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestSwayAutogenerateComment(t *testing.T) { - tests := []struct { - command string - expected string - }{ - {"exec kitty", "kitty"}, - {"exec --no-startup-id firefox", "firefox"}, - {"kill", "Close window"}, - {"exit", "Exit Sway"}, - {"reload", "Reload configuration"}, - {"fullscreen toggle", "Toggle fullscreen"}, - {"floating toggle", "Float/unfloat window"}, - {"focus mode_toggle", "Toggle focus mode"}, - {"focus parent", "Focus parent container"}, - {"focus left", "Focus left"}, - {"focus right", "Focus right"}, - {"focus up", "Focus up"}, - {"focus down", "Focus down"}, - {"focus output left", "Focus monitor"}, - {"move left", "Move window left"}, - {"move right", "Move window right"}, - {"move up", "Move window up"}, - {"move down", "Move window down"}, - {"move container to workspace number 1", "Move to workspace 1"}, - {"move container to workspace prev", "Move to previous workspace"}, - {"move container to workspace next", "Move to next workspace"}, - {"move workspace to output left", "Move workspace to monitor"}, - {"workspace number 1", "Workspace 1"}, - {"workspace prev", "Previous workspace"}, - {"workspace next", "Next workspace"}, - {"layout tabbed", "Layout tabbed"}, - {"layout stacking", "Layout stacking"}, - {"splith", "Split horizontal"}, - {"splitv", "Split vertical"}, - {"resize grow width 10 ppt", "Resize window"}, - {"move scratchpad", "Toggle scratchpad"}, - } - - for _, tt := range tests { - t.Run(tt.command, func(t *testing.T) { - result := swayAutogenerateComment(tt.command) - if result != tt.expected { - t.Errorf("swayAutogenerateComment(%q) = %q, want %q", - tt.command, result, tt.expected) - } - }) - } -} - -func TestSwayGetKeybindAtLine(t *testing.T) { - tests := []struct { - name string - line string - expected *SwayKeyBinding - }{ - { - name: "basic_keybind", - line: "bindsym Mod4+q kill", - expected: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "q", - Command: "kill", - Comment: "Close window", - }, - }, - { - name: "keybind_with_exec", - line: "bindsym Mod4+t exec kitty", - expected: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "t", - Command: "exec kitty", - Comment: "kitty", - }, - }, - { - name: "keybind_with_comment", - line: "bindsym Mod4+Space exec dms ipc call spotlight toggle # Open launcher", - expected: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "Space", - Command: "exec dms ipc call spotlight toggle", - Comment: "Open launcher", - }, - }, - { - name: "keybind_hidden", - line: "bindsym Mod4+h exec secret # [hidden]", - expected: nil, - }, - { - name: "keybind_multiple_mods", - line: "bindsym Mod4+Shift+e exit", - expected: &SwayKeyBinding{ - Mods: []string{"Mod4", "Shift"}, - Key: "e", - Command: "exit", - Comment: "Exit Sway", - }, - }, - { - name: "keybind_no_mods", - line: "bindsym Print exec grim screenshot.png", - expected: &SwayKeyBinding{ - Mods: []string{}, - Key: "Print", - Command: "exec grim screenshot.png", - Comment: "grim screenshot.png", - }, - }, - { - name: "keybind_with_flags", - line: "bindsym --release Mod4+x exec notify-send released", - expected: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "x", - Command: "exec notify-send released", - Comment: "notify-send released", - }, - }, - { - name: "keybind_focus_direction", - line: "bindsym Mod4+Left focus left", - expected: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "Left", - Command: "focus left", - Comment: "Focus left", - }, - }, - { - name: "keybind_workspace", - line: "bindsym Mod4+1 workspace number 1", - expected: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "1", - Command: "workspace number 1", - Comment: "Workspace 1", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - parser := NewSwayParser() - parser.contentLines = []string{tt.line} - result := parser.getKeybindAtLine(0) - - if tt.expected == nil { - if result != nil { - t.Errorf("expected nil, got %+v", result) - } - return - } - - if result == nil { - t.Errorf("expected %+v, got nil", tt.expected) - return - } - - if result.Key != tt.expected.Key { - t.Errorf("Key = %q, want %q", result.Key, tt.expected.Key) - } - if result.Command != tt.expected.Command { - t.Errorf("Command = %q, want %q", result.Command, tt.expected.Command) - } - if result.Comment != tt.expected.Comment { - t.Errorf("Comment = %q, want %q", result.Comment, tt.expected.Comment) - } - if len(result.Mods) != len(tt.expected.Mods) { - t.Errorf("Mods length = %d, want %d", len(result.Mods), len(tt.expected.Mods)) - } else { - for i := range result.Mods { - if result.Mods[i] != tt.expected.Mods[i] { - t.Errorf("Mods[%d] = %q, want %q", i, result.Mods[i], tt.expected.Mods[i]) - } - } - } - }) - } -} - -func TestSwayVariableExpansion(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config") - - content := `set $mod Mod4 -set $term kitty -set $menu rofi - -bindsym $mod+t exec $term -bindsym $mod+d exec $menu -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseSwayKeys(configFile) - if err != nil { - t.Fatalf("ParseSwayKeys failed: %v", err) - } - - if len(section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds, got %d", len(section.Keybinds)) - } - - if len(section.Keybinds) > 0 { - if section.Keybinds[0].Mods[0] != "Mod4" { - t.Errorf("Expected Mod4, got %q", section.Keybinds[0].Mods[0]) - } - if section.Keybinds[0].Command != "exec kitty" { - t.Errorf("Expected 'exec kitty', got %q", section.Keybinds[0].Command) - } - } - - if len(section.Keybinds) > 1 { - if section.Keybinds[1].Command != "exec rofi" { - t.Errorf("Expected 'exec rofi', got %q", section.Keybinds[1].Command) - } - } -} - -func TestSwayParseKeysWithSections(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config") - - content := `set $mod Mod4 - -##! Window Management -bindsym $mod+q kill -bindsym $mod+f fullscreen toggle - -###! Focus -bindsym $mod+Left focus left -bindsym $mod+Right focus right - -##! Applications -bindsym $mod+t exec kitty # Terminal -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseSwayKeys(tmpDir) - if err != nil { - t.Fatalf("ParseSwayKeys failed: %v", err) - } - - if len(section.Children) != 2 { - t.Errorf("Expected 2 top-level sections, got %d", len(section.Children)) - } - - if len(section.Children) >= 1 { - windowMgmt := section.Children[0] - if windowMgmt.Name != "Window Management" { - t.Errorf("First section name = %q, want %q", windowMgmt.Name, "Window Management") - } - if len(windowMgmt.Keybinds) != 2 { - t.Errorf("Window Management keybinds = %d, want 2", len(windowMgmt.Keybinds)) - } - - if len(windowMgmt.Children) != 1 { - t.Errorf("Window Management children = %d, want 1", len(windowMgmt.Children)) - } else { - focus := windowMgmt.Children[0] - if focus.Name != "Focus" { - t.Errorf("Focus section name = %q, want %q", focus.Name, "Focus") - } - if len(focus.Keybinds) != 2 { - t.Errorf("Focus keybinds = %d, want 2", len(focus.Keybinds)) - } - } - } - - if len(section.Children) >= 2 { - apps := section.Children[1] - if apps.Name != "Applications" { - t.Errorf("Second section name = %q, want %q", apps.Name, "Applications") - } - if len(apps.Keybinds) != 1 { - t.Errorf("Applications keybinds = %d, want 1", len(apps.Keybinds)) - } - if len(apps.Keybinds) > 0 && apps.Keybinds[0].Comment != "Terminal" { - t.Errorf("Applications keybind comment = %q, want %q", apps.Keybinds[0].Comment, "Terminal") - } - } -} - -func TestSwayReadContentErrors(t *testing.T) { - tests := []struct { - name string - path string - }{ - { - name: "nonexistent_directory", - path: "/nonexistent/path/that/does/not/exist", - }, - { - name: "empty_directory", - path: t.TempDir(), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := ParseSwayKeys(tt.path) - if err == nil { - t.Error("Expected error, got nil") - } - }) - } -} - -func TestSwayReadContentWithTildeExpansion(t *testing.T) { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Skip("Cannot get home directory") - } - - tmpSubdir := filepath.Join(homeDir, ".config", "test-sway-"+t.Name()) - if err := os.MkdirAll(tmpSubdir, 0o755); err != nil { - t.Skip("Cannot create test directory in home") - } - defer os.RemoveAll(tmpSubdir) - - configFile := filepath.Join(tmpSubdir, "config") - if err := os.WriteFile(configFile, []byte("bindsym Mod4+q kill\n"), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - relPath, err := filepath.Rel(homeDir, tmpSubdir) - if err != nil { - t.Skip("Cannot create relative path") - } - - parser := NewSwayParser() - tildePathMatch := "~/" + relPath - err = parser.ReadContent(tildePathMatch) - - if err != nil { - t.Errorf("ReadContent with tilde path failed: %v", err) - } -} - -func TestSwayEmptyAndCommentLines(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config") - - content := ` -# This is a comment -bindsym Mod4+q kill - -# Another comment - -bindsym Mod4+t exec kitty -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseSwayKeys(configFile) - if err != nil { - t.Fatalf("ParseSwayKeys failed: %v", err) - } - - if len(section.Keybinds) != 2 { - t.Errorf("Expected 2 keybinds (comments ignored), got %d", len(section.Keybinds)) - } -} - -func TestSwayRealWorldConfig(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config") - - content := `set $mod Mod4 -set $term kitty - -## Application Launchers -bindsym $mod+t exec $term -bindsym $mod+Space exec rofi - -## Window Management -bindsym $mod+q kill -bindsym $mod+f fullscreen toggle - -## Focus Navigation -bindsym $mod+Left focus left -bindsym $mod+Right focus right - -## Workspace Navigation -bindsym $mod+1 workspace number 1 -bindsym $mod+2 workspace number 2 -bindsym $mod+Shift+1 move container to workspace number 1 -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - section, err := ParseSwayKeys(configFile) - if err != nil { - t.Fatalf("ParseSwayKeys failed: %v", err) - } - - if len(section.Keybinds) < 9 { - t.Errorf("Expected at least 9 keybinds, got %d", len(section.Keybinds)) - } - - foundExec := false - foundKill := false - foundWorkspace := false - - for _, kb := range section.Keybinds { - if kb.Command == "exec kitty" { - foundExec = true - } - if kb.Command == "kill" { - foundKill = true - } - if kb.Command == "workspace number 1" { - foundWorkspace = true - } - } - - if !foundExec { - t.Error("Did not find exec kitty keybind") - } - if !foundKill { - t.Error("Did not find kill keybind") - } - if !foundWorkspace { - t.Error("Did not find workspace 1 keybind") - } -} - -func TestSwayIsMod(t *testing.T) { - tests := []struct { - input string - expected bool - }{ - {"Mod4", true}, - {"Shift", true}, - {"Control", true}, - {"Alt", true}, - {"Super", true}, - {"$mod", true}, - {"Left", false}, - {"q", false}, - {"1", false}, - } - - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - result := swayIsMod(tt.input) - if result != tt.expected { - t.Errorf("swayIsMod(%q) = %v, want %v", tt.input, result, tt.expected) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_test.go deleted file mode 100644 index fd476d8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/providers/sway_test.go +++ /dev/null @@ -1,293 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestSwayProviderName(t *testing.T) { - provider := NewSwayProvider("") - if provider.Name() != "sway" { - t.Errorf("Name() = %q, want %q", provider.Name(), "sway") - } -} - -func TestSwayProviderDefaultPath(t *testing.T) { - provider := NewSwayProvider("") - configDir, err := os.UserConfigDir() - if err != nil { - t.Skip("UserConfigDir not available") - } - expected := filepath.Join(configDir, "sway") - if provider.configPath != expected { - t.Errorf("configPath = %q, want %q", provider.configPath, expected) - } -} - -func TestSwayProviderCustomPath(t *testing.T) { - customPath := "/custom/path" - provider := NewSwayProvider(customPath) - if provider.configPath != customPath { - t.Errorf("configPath = %q, want %q", provider.configPath, customPath) - } -} - -func TestSwayCategorizeByCommand(t *testing.T) { - tests := []struct { - command string - expected string - }{ - {"workspace number 1", "Workspace"}, - {"workspace prev", "Workspace"}, - {"workspace next", "Workspace"}, - {"move container to workspace number 1", "Workspace"}, - {"focus output left", "Monitor"}, - {"move workspace to output right", "Monitor"}, - {"kill", "Window"}, - {"fullscreen toggle", "Window"}, - {"floating toggle", "Window"}, - {"focus left", "Window"}, - {"focus right", "Window"}, - {"move left", "Window"}, - {"move right", "Window"}, - {"resize grow width 10px", "Window"}, - {"splith", "Window"}, - {"splitv", "Window"}, - {"layout tabbed", "Layout"}, - {"layout stacking", "Layout"}, - {"move scratchpad", "Scratchpad"}, - {"scratchpad show", "Scratchpad"}, - {"exec kitty", "Execute"}, - {"exec --no-startup-id firefox", "Execute"}, - {"exit", "System"}, - {"reload", "System"}, - {"unknown command", "Other"}, - } - - provider := NewSwayProvider("") - for _, tt := range tests { - t.Run(tt.command, func(t *testing.T) { - result := provider.categorizeByCommand(tt.command) - if result != tt.expected { - t.Errorf("categorizeByCommand(%q) = %q, want %q", tt.command, result, tt.expected) - } - }) - } -} - -func TestSwayFormatKey(t *testing.T) { - tests := []struct { - name string - keybind *SwayKeyBinding - expected string - }{ - { - name: "single_mod", - keybind: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "q", - }, - expected: "Mod4+q", - }, - { - name: "multiple_mods", - keybind: &SwayKeyBinding{ - Mods: []string{"Mod4", "Shift"}, - Key: "e", - }, - expected: "Mod4+Shift+e", - }, - { - name: "no_mods", - keybind: &SwayKeyBinding{ - Mods: []string{}, - Key: "Print", - }, - expected: "Print", - }, - } - - provider := NewSwayProvider("") - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := provider.formatKey(tt.keybind) - if result != tt.expected { - t.Errorf("formatKey() = %q, want %q", result, tt.expected) - } - }) - } -} - -func TestSwayConvertKeybind(t *testing.T) { - tests := []struct { - name string - keybind *SwayKeyBinding - wantKey string - wantDesc string - }{ - { - name: "with_comment", - keybind: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "t", - Command: "exec kitty", - Comment: "Open terminal", - }, - wantKey: "Mod4+t", - wantDesc: "Open terminal", - }, - { - name: "without_comment", - keybind: &SwayKeyBinding{ - Mods: []string{"Mod4"}, - Key: "r", - Command: "reload", - Comment: "", - }, - wantKey: "Mod4+r", - wantDesc: "reload", - }, - } - - provider := NewSwayProvider("") - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := provider.convertKeybind(tt.keybind, "") - if result.Key != tt.wantKey { - t.Errorf("convertKeybind().Key = %q, want %q", result.Key, tt.wantKey) - } - if result.Description != tt.wantDesc { - t.Errorf("convertKeybind().Description = %q, want %q", result.Description, tt.wantDesc) - } - }) - } -} - -func TestSwayGetCheatSheet(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config") - - content := `set $mod Mod4 -set $term kitty - -# System -bindsym $mod+Shift+c reload -bindsym $mod+Shift+e exit - -# Applications -bindsym $mod+t exec $term -bindsym $mod+Space exec rofi - -# Window Management -bindsym $mod+q kill -bindsym $mod+f fullscreen toggle -bindsym $mod+Left focus left -bindsym $mod+Right focus right - -# Workspace -bindsym $mod+1 workspace number 1 -bindsym $mod+2 workspace number 2 -bindsym $mod+Shift+1 move container to workspace number 1 - -# Layout -bindsym $mod+s layout stacking -bindsym $mod+w layout tabbed -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - provider := NewSwayProvider(tmpDir) - sheet, err := provider.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - if sheet == nil { - t.Fatal("Expected non-nil CheatSheet") - } - - if sheet.Title != "Sway Keybinds" { - t.Errorf("Title = %q, want %q", sheet.Title, "Sway Keybinds") - } - - if sheet.Provider != "sway" { - t.Errorf("Provider = %q, want %q", sheet.Provider, "sway") - } - - categories := []string{"System", "Execute", "Window", "Workspace", "Layout"} - for _, category := range categories { - if _, exists := sheet.Binds[category]; !exists { - t.Errorf("Expected category %q to exist", category) - } - } - - if len(sheet.Binds["System"]) < 2 { - t.Error("Expected at least 2 System keybinds") - } - if len(sheet.Binds["Execute"]) < 2 { - t.Error("Expected at least 2 Execute keybinds") - } - if len(sheet.Binds["Window"]) < 4 { - t.Error("Expected at least 4 Window keybinds") - } - if len(sheet.Binds["Workspace"]) < 3 { - t.Error("Expected at least 3 Workspace keybinds") - } -} - -func TestSwayGetCheatSheetError(t *testing.T) { - provider := NewSwayProvider("/nonexistent/path") - _, err := provider.GetCheatSheet() - if err == nil { - t.Error("Expected error for nonexistent path, got nil") - } -} - -func TestSwayIntegration(t *testing.T) { - tmpDir := t.TempDir() - configFile := filepath.Join(tmpDir, "config") - - content := `set $mod Mod4 - -bindsym $mod+t exec kitty # Terminal -bindsym $mod+q kill -bindsym $mod+f fullscreen toggle -bindsym $mod+1 workspace number 1 -` - - if err := os.WriteFile(configFile, []byte(content), 0o644); err != nil { - t.Fatalf("Failed to write test config: %v", err) - } - - provider := NewSwayProvider(tmpDir) - sheet, err := provider.GetCheatSheet() - if err != nil { - t.Fatalf("GetCheatSheet failed: %v", err) - } - - totalBinds := 0 - for _, binds := range sheet.Binds { - totalBinds += len(binds) - } - - expectedBinds := 4 - if totalBinds != expectedBinds { - t.Errorf("Expected %d total keybinds, got %d", expectedBinds, totalBinds) - } - - foundTerminal := false - for _, binds := range sheet.Binds { - for _, bind := range binds { - if bind.Description == "Terminal" && bind.Key == "Mod4+t" { - foundTerminal = true - } - } - } - - if !foundTerminal { - t.Error("Did not find terminal keybind with correct key and description") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/registry.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/registry.go deleted file mode 100644 index d726838..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/registry.go +++ /dev/null @@ -1,79 +0,0 @@ -package keybinds - -import ( - "fmt" - "sync" -) - -type Registry struct { - mu sync.RWMutex - providers map[string]Provider -} - -func NewRegistry() *Registry { - return &Registry{ - providers: make(map[string]Provider), - } -} - -func (r *Registry) Register(provider Provider) error { - if provider == nil { - return fmt.Errorf("cannot register nil provider") - } - - name := provider.Name() - if name == "" { - return fmt.Errorf("provider name cannot be empty") - } - - r.mu.Lock() - defer r.mu.Unlock() - - if _, exists := r.providers[name]; exists { - return fmt.Errorf("provider %q already registered", name) - } - - r.providers[name] = provider - return nil -} - -func (r *Registry) Get(name string) (Provider, error) { - r.mu.RLock() - defer r.mu.RUnlock() - - provider, exists := r.providers[name] - if !exists { - return nil, fmt.Errorf("provider %q not found", name) - } - - return provider, nil -} - -func (r *Registry) List() []string { - r.mu.RLock() - defer r.mu.RUnlock() - - names := make([]string, 0, len(r.providers)) - for name := range r.providers { - names = append(names, name) - } - return names -} - -var defaultRegistry = NewRegistry() - -func GetDefaultRegistry() *Registry { - return defaultRegistry -} - -func Register(provider Provider) error { - return defaultRegistry.Register(provider) -} - -func Get(name string) (Provider, error) { - return defaultRegistry.Get(name) -} - -func List() []string { - return defaultRegistry.List() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/registry_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/registry_test.go deleted file mode 100644 index c3ddfee..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/registry_test.go +++ /dev/null @@ -1,183 +0,0 @@ -package keybinds - -import ( - "testing" -) - -type mockProvider struct { - name string - err error -} - -func (m *mockProvider) Name() string { - return m.name -} - -func (m *mockProvider) GetCheatSheet() (*CheatSheet, error) { - if m.err != nil { - return nil, m.err - } - return &CheatSheet{ - Title: "Test", - Provider: m.name, - Binds: make(map[string][]Keybind), - }, nil -} - -func TestNewRegistry(t *testing.T) { - r := NewRegistry() - if r == nil { - t.Fatal("NewRegistry returned nil") - } - - if r.providers == nil { - t.Error("providers map is nil") - } -} - -func TestRegisterProvider(t *testing.T) { - tests := []struct { - name string - provider Provider - expectError bool - errorMsg string - }{ - { - name: "valid provider", - provider: &mockProvider{name: "test"}, - expectError: false, - }, - { - name: "nil provider", - provider: nil, - expectError: true, - errorMsg: "cannot register nil provider", - }, - { - name: "empty name", - provider: &mockProvider{name: ""}, - expectError: true, - errorMsg: "provider name cannot be empty", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := NewRegistry() - err := r.Register(tt.provider) - - if tt.expectError { - if err == nil { - t.Error("expected error, got nil") - } - return - } - - if err != nil { - t.Errorf("unexpected error: %v", err) - } - }) - } -} - -func TestRegisterDuplicate(t *testing.T) { - r := NewRegistry() - p := &mockProvider{name: "test"} - - if err := r.Register(p); err != nil { - t.Fatalf("first registration failed: %v", err) - } - - err := r.Register(p) - if err == nil { - t.Error("expected error when registering duplicate, got nil") - } -} - -func TestGetProvider(t *testing.T) { - r := NewRegistry() - p := &mockProvider{name: "test"} - - if err := r.Register(p); err != nil { - t.Fatalf("registration failed: %v", err) - } - - retrieved, err := r.Get("test") - if err != nil { - t.Fatalf("Get failed: %v", err) - } - - if retrieved.Name() != "test" { - t.Errorf("Got provider name %q, want %q", retrieved.Name(), "test") - } -} - -func TestGetNonexistent(t *testing.T) { - r := NewRegistry() - - _, err := r.Get("nonexistent") - if err == nil { - t.Error("expected error for nonexistent provider, got nil") - } -} - -func TestListProviders(t *testing.T) { - r := NewRegistry() - - p1 := &mockProvider{name: "test1"} - p2 := &mockProvider{name: "test2"} - p3 := &mockProvider{name: "test3"} - - r.Register(p1) - r.Register(p2) - r.Register(p3) - - list := r.List() - - if len(list) != 3 { - t.Errorf("expected 3 providers, got %d", len(list)) - } - - found := make(map[string]bool) - for _, name := range list { - found[name] = true - } - - expected := []string{"test1", "test2", "test3"} - for _, name := range expected { - if !found[name] { - t.Errorf("expected provider %q not found in list", name) - } - } -} - -func TestDefaultRegistry(t *testing.T) { - p := &mockProvider{name: "default-test"} - - err := Register(p) - if err != nil { - t.Fatalf("Register failed: %v", err) - } - - retrieved, err := Get("default-test") - if err != nil { - t.Fatalf("Get failed: %v", err) - } - - if retrieved.Name() != "default-test" { - t.Errorf("Got provider name %q, want %q", retrieved.Name(), "default-test") - } - - list := List() - found := false - for _, name := range list { - if name == "default-test" { - found = true - break - } - } - - if !found { - t.Error("provider not found in default registry list") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/types.go deleted file mode 100644 index 109b53b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/keybinds/types.go +++ /dev/null @@ -1,47 +0,0 @@ -package keybinds - -type Keybind struct { - Key string `json:"key"` - Description string `json:"desc"` - Action string `json:"action,omitempty"` - Subcategory string `json:"subcat,omitempty"` - Source string `json:"source,omitempty"` - HideOnOverlay bool `json:"hideOnOverlay,omitempty"` - CooldownMs int `json:"cooldownMs,omitempty"` - Flags string `json:"flags,omitempty"` // Hyprland bind flags: e=repeat, l=locked, r=release, o=long-press - AllowWhenLocked bool `json:"allowWhenLocked,omitempty"` - AllowInhibiting *bool `json:"allowInhibiting,omitempty"` // nil=default(true), false=explicitly disabled - Repeat *bool `json:"repeat,omitempty"` // nil=default(true), false=explicitly disabled - Conflict *Keybind `json:"conflict,omitempty"` -} - -type DMSBindsStatus struct { - Exists bool `json:"exists"` - Included bool `json:"included"` - IncludePosition int `json:"includePosition"` - TotalIncludes int `json:"totalIncludes"` - BindsAfterDMS int `json:"bindsAfterDms"` - Effective bool `json:"effective"` - OverriddenBy int `json:"overriddenBy"` - StatusMessage string `json:"statusMessage"` -} - -type CheatSheet struct { - Title string `json:"title"` - Provider string `json:"provider"` - Binds map[string][]Keybind `json:"binds"` - DMSBindsIncluded bool `json:"dmsBindsIncluded"` - DMSStatus *DMSBindsStatus `json:"dmsStatus,omitempty"` -} - -type Provider interface { - Name() string - GetCheatSheet() (*CheatSheet, error) -} - -type WritableProvider interface { - Provider - SetBind(key, action, description string, options map[string]any) error - RemoveBind(key string) error - GetOverridePath() string -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/log/filelogger.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/log/filelogger.go deleted file mode 100644 index e3ff42b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/log/filelogger.go +++ /dev/null @@ -1,114 +0,0 @@ -package log - -import ( - "bufio" - "fmt" - "os" - "path/filepath" - "regexp" - "sync" - "time" -) - -type FileLogger struct { - file *os.File - writer *bufio.Writer - logPath string - mu sync.Mutex - stopChan chan struct{} - doneChan chan struct{} - passwordRe *regexp.Regexp -} - -func NewFileLogger() (*FileLogger, error) { - timestamp := time.Now().Unix() - - // Use DANKINSTALL_LOG_DIR if set, otherwise fall back to /tmp. - logDir := os.Getenv("DANKINSTALL_LOG_DIR") - if logDir == "" { - logDir = "/tmp" - } - if err := os.MkdirAll(logDir, 0o755); err != nil { - return nil, fmt.Errorf("failed to create log directory: %w", err) - } - logPath := filepath.Join(logDir, fmt.Sprintf("dankinstall-%d.log", timestamp)) - - file, err := os.Create(logPath) - if err != nil { - return nil, fmt.Errorf("failed to create log file: %w", err) - } - - passwordRe := regexp.MustCompile(`(?i)(password[:\s=]+)[^\s]+`) - - logger := &FileLogger{ - file: file, - writer: bufio.NewWriter(file), - logPath: logPath, - stopChan: make(chan struct{}), - doneChan: make(chan struct{}), - passwordRe: passwordRe, - } - - header := fmt.Sprintf("=== DankInstall Log ===\nStarted: %s\n\n", time.Now().Format(time.RFC3339)) - logger.writeToFile(header) - - return logger, nil -} - -func (l *FileLogger) GetLogPath() string { - return l.logPath -} - -func (l *FileLogger) redactPassword(message string) string { - redacted := l.passwordRe.ReplaceAllString(message, "${1}[REDACTED]") - - redacted = regexp.MustCompile(`echo\s+'[^']+'`).ReplaceAllString(redacted, "echo '[REDACTED]'") - - return redacted -} - -func (l *FileLogger) writeToFile(message string) { - l.mu.Lock() - defer l.mu.Unlock() - - redacted := l.redactPassword(message) - timestamp := time.Now().Format("15:04:05.000") - - fmt.Fprintf(l.writer, "[%s] %s\n", timestamp, redacted) - l.writer.Flush() -} - -func (l *FileLogger) StartListening(logChan <-chan string) { - go func() { - defer close(l.doneChan) - for { - select { - case msg, ok := <-logChan: - if !ok { - return - } - l.writeToFile(msg) - case <-l.stopChan: - return - } - } - }() -} - -func (l *FileLogger) Close() error { - close(l.stopChan) - <-l.doneChan - - l.mu.Lock() - defer l.mu.Unlock() - - footer := fmt.Sprintf("\n=== DankInstall Log End ===\nCompleted: %s\n", time.Now().Format(time.RFC3339)) - l.writer.WriteString(footer) //nolint:errcheck - l.writer.Flush() - - if err := l.file.Sync(); err != nil { - return err - } - - return l.file.Close() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/log/log.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/log/log.go deleted file mode 100644 index 6b3ede3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/log/log.go +++ /dev/null @@ -1,116 +0,0 @@ -package log - -import ( - "os" - "strings" - "sync" - - "github.com/charmbracelet/lipgloss" - cblog "github.com/charmbracelet/log" -) - -// Logger embeds the Charm Logger and adds Printf/Fatalf -type Logger struct{ *cblog.Logger } - -// Printf routes goose/info-style logs through Infof. -func (l *Logger) Printf(format string, v ...any) { l.Infof(format, v...) } - -// Fatalf keeps goose’s contract of exiting the program. -func (l *Logger) Fatalf(format string, v ...any) { l.Logger.Fatalf(format, v...) } - -var ( - logger *Logger - initLogger sync.Once -) - -func parseLogLevel(level string) cblog.Level { - switch strings.ToLower(level) { - case "debug": - return cblog.DebugLevel - case "info": - return cblog.InfoLevel - case "warn", "warning": - return cblog.WarnLevel - case "error": - return cblog.ErrorLevel - case "fatal": - return cblog.FatalLevel - default: - return cblog.InfoLevel - } -} - -func GetQtLoggingRules() string { - level := os.Getenv("DMS_LOG_LEVEL") - if level == "" { - level = "info" - } - - var rules []string - switch strings.ToLower(level) { - case "fatal": - rules = []string{"*.debug=false", "*.info=false", "*.warning=false", "*.critical=false"} - case "error": - rules = []string{"*.debug=false", "*.info=false", "*.warning=false"} - case "warn", "warning": - rules = []string{"*.debug=false", "*.info=false"} - case "info": - rules = []string{"*.debug=false"} - case "debug": - return "" - default: - rules = []string{"*.debug=false"} - } - - return strings.Join(rules, ";") -} - -// GetLogger returns a logger instance -func GetLogger() *Logger { - initLogger.Do(func() { - styles := cblog.DefaultStyles() - // Attempt to match the colors used by qml/quickshell logs - styles.Levels[cblog.FatalLevel] = lipgloss.NewStyle(). - SetString(" FATAL"). - Foreground(lipgloss.Color("1")) - styles.Levels[cblog.ErrorLevel] = lipgloss.NewStyle(). - SetString(" ERROR"). - Foreground(lipgloss.Color("9")) - styles.Levels[cblog.WarnLevel] = lipgloss.NewStyle(). - SetString(" WARN"). - Foreground(lipgloss.Color("3")) - styles.Levels[cblog.InfoLevel] = lipgloss.NewStyle(). - SetString(" INFO"). - Foreground(lipgloss.Color("2")) - styles.Levels[cblog.DebugLevel] = lipgloss.NewStyle(). - SetString(" DEBUG"). - Foreground(lipgloss.Color("4")) - - base := cblog.New(os.Stderr) - base.SetStyles(styles) - base.SetReportTimestamp(false) - - level := cblog.InfoLevel - if envLevel := os.Getenv("DMS_LOG_LEVEL"); envLevel != "" { - level = parseLogLevel(envLevel) - } - base.SetLevel(level) - base.SetPrefix(" go") - - logger = &Logger{base} - }) - return logger -} - -// * Convenience wrappers - -func Debug(msg any, keyvals ...any) { GetLogger().Debug(msg, keyvals...) } -func Debugf(format string, v ...any) { GetLogger().Debugf(format, v...) } -func Info(msg any, keyvals ...any) { GetLogger().Info(msg, keyvals...) } -func Infof(format string, v ...any) { GetLogger().Infof(format, v...) } -func Warn(msg any, keyvals ...any) { GetLogger().Warn(msg, keyvals...) } -func Warnf(format string, v ...any) { GetLogger().Warnf(format, v...) } -func Error(msg any, keyvals ...any) { GetLogger().Error(msg, keyvals...) } -func Errorf(format string, v ...any) { GetLogger().Errorf(format, v...) } -func Fatal(msg any, keyvals ...any) { GetLogger().Fatal(msg, keyvals...) } -func Fatalf(format string, v ...any) { GetLogger().Fatalf(format, v...) } diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/matugen.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/matugen.go deleted file mode 100644 index a29d324..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/matugen.go +++ /dev/null @@ -1,978 +0,0 @@ -package matugen - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "math" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "sync" - "syscall" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/dank16" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/lucasb-eyer/go-colorful" -) - -var ErrNoChanges = errors.New("no color changes") - -type ColorMode string - -const ( - ColorModeDark ColorMode = "dark" - ColorModeLight ColorMode = "light" -) - -type TemplateKind int - -const ( - TemplateKindNormal TemplateKind = iota - TemplateKindTerminal - TemplateKindGTK - TemplateKindVSCode - TemplateKindEmacs -) - -type TemplateDef struct { - ID string - Commands []string - Flatpaks []string - ConfigFile string - Kind TemplateKind - RunUnconditionally bool -} - -var templateRegistry = []TemplateDef{ - {ID: "gtk", Kind: TemplateKindGTK, RunUnconditionally: true}, - {ID: "niri", Commands: []string{"niri"}, ConfigFile: "niri.toml"}, - {ID: "hyprland", Commands: []string{"Hyprland"}, ConfigFile: "hyprland.toml"}, - {ID: "mangowc", Commands: []string{"mango"}, ConfigFile: "mangowc.toml"}, - {ID: "qt5ct", Commands: []string{"qt5ct"}, ConfigFile: "qt5ct.toml"}, - {ID: "qt6ct", Commands: []string{"qt6ct"}, ConfigFile: "qt6ct.toml"}, - {ID: "firefox", Commands: []string{"firefox"}, ConfigFile: "firefox.toml"}, - {ID: "pywalfox", Commands: []string{"pywalfox"}, ConfigFile: "pywalfox.toml"}, - {ID: "zenbrowser", Commands: []string{"zen", "zen-browser", "zen-beta", "zen-twilight"}, Flatpaks: []string{"app.zen_browser.zen"}, ConfigFile: "zenbrowser.toml"}, - {ID: "vesktop", Commands: []string{"vesktop"}, Flatpaks: []string{"dev.vencord.Vesktop"}, ConfigFile: "vesktop.toml"}, - {ID: "equibop", Commands: []string{"equibop"}, ConfigFile: "equibop.toml"}, - {ID: "ghostty", Commands: []string{"ghostty"}, ConfigFile: "ghostty.toml", Kind: TemplateKindTerminal}, - {ID: "kitty", Commands: []string{"kitty"}, ConfigFile: "kitty.toml", Kind: TemplateKindTerminal}, - {ID: "foot", Commands: []string{"foot"}, ConfigFile: "foot.toml", Kind: TemplateKindTerminal}, - {ID: "alacritty", Commands: []string{"alacritty"}, ConfigFile: "alacritty.toml", Kind: TemplateKindTerminal}, - {ID: "wezterm", Commands: []string{"wezterm"}, ConfigFile: "wezterm.toml", Kind: TemplateKindTerminal}, - {ID: "nvim", Commands: []string{"nvim"}, ConfigFile: "neovim.toml", Kind: TemplateKindTerminal}, - {ID: "dgop", Commands: []string{"dgop"}, ConfigFile: "dgop.toml"}, - {ID: "kcolorscheme", ConfigFile: "kcolorscheme.toml", RunUnconditionally: true}, - {ID: "vscode", Kind: TemplateKindVSCode}, - {ID: "emacs", Commands: []string{"emacs"}, ConfigFile: "emacs.toml", Kind: TemplateKindEmacs}, - {ID: "zed", Commands: []string{"zed", "zeditor", "zedit"}, ConfigFile: "zed.toml"}, -} - -func (c *ColorMode) GTKTheme() string { - switch *c { - case ColorModeDark: - return "adw-gtk3-dark" - default: - return "adw-gtk3" - } -} - -var ( - matugenVersionMu sync.Mutex - matugenVersionOK bool - matugenSupportsCOE bool - matugenIsV4 bool -) - -type Options struct { - StateDir string - ShellDir string - ConfigDir string - Kind string - Value string - Mode ColorMode - IconTheme string - MatugenType string - Contrast float64 - RunUserTemplates bool - ColorsOnly bool - StockColors string - SyncModeWithPortal bool - TerminalsAlwaysDark bool - SkipTemplates string - AppChecker utils.AppChecker -} - -type ColorsOutput struct { - Colors struct { - Dark map[string]string `json:"dark"` - Light map[string]string `json:"light"` - } `json:"colors"` -} - -func (o *Options) ColorsOutput() string { - return filepath.Join(o.StateDir, "dms-colors.json") -} - -func (o *Options) ShouldSkipTemplate(name string) bool { - if o.SkipTemplates == "" { - return false - } - for _, skip := range strings.Split(o.SkipTemplates, ",") { - if strings.TrimSpace(skip) == name { - return true - } - } - return false -} - -func Run(opts Options) error { - if opts.StateDir == "" { - return fmt.Errorf("state-dir is required") - } - if opts.ShellDir == "" { - return fmt.Errorf("shell-dir is required") - } - if opts.ConfigDir == "" { - return fmt.Errorf("config-dir is required") - } - if opts.Kind == "" { - return fmt.Errorf("kind is required") - } - if opts.Value == "" { - return fmt.Errorf("value is required") - } - if opts.Mode == "" { - opts.Mode = ColorModeDark - } - if opts.MatugenType == "" { - opts.MatugenType = "scheme-tonal-spot" - } - if opts.IconTheme == "" { - opts.IconTheme = "System Default" - } - if opts.AppChecker == nil { - opts.AppChecker = utils.DefaultAppChecker{} - } - - if err := os.MkdirAll(opts.StateDir, 0o755); err != nil { - return fmt.Errorf("failed to create state dir: %w", err) - } - - log.Infof("Building theme: %s %s (%s)", opts.Kind, opts.Value, opts.Mode) - - changed, buildErr := buildOnce(&opts) - if buildErr != nil { - return buildErr - } - - if !changed { - log.Info("No color changes detected, skipping refresh") - return ErrNoChanges - } - - if opts.SyncModeWithPortal { - syncColorScheme(opts.Mode) - } - - log.Info("Done") - return nil -} - -func buildOnce(opts *Options) (bool, error) { - cfgFile, err := os.CreateTemp("", "matugen-config-*.toml") - if err != nil { - return false, fmt.Errorf("failed to create temp config: %w", err) - } - defer os.Remove(cfgFile.Name()) - defer cfgFile.Close() - - tmpDir, err := os.MkdirTemp("", "matugen-templates-*") - if err != nil { - return false, fmt.Errorf("failed to create temp dir: %w", err) - } - defer os.RemoveAll(tmpDir) - - if err := buildMergedConfig(opts, cfgFile, tmpDir); err != nil { - return false, fmt.Errorf("failed to build config: %w", err) - } - cfgFile.Close() - - oldColors, _ := os.ReadFile(opts.ColorsOutput()) - - var primaryDark, primaryLight, surface string - var dank16JSON string - var importArgs []string - - if opts.StockColors != "" { - log.Info("Using stock/custom theme colors with matugen base") - primaryDark = extractNestedColor(opts.StockColors, "primary", "dark") - primaryLight = extractNestedColor(opts.StockColors, "primary", "light") - surface = extractNestedColor(opts.StockColors, "surface", "dark") - - if primaryDark == "" { - return false, fmt.Errorf("failed to extract primary dark from stock colors") - } - if primaryLight == "" { - primaryLight = primaryDark - } - - dank16JSON = generateDank16Variants(primaryDark, primaryLight, surface, opts.Mode) - importData := fmt.Sprintf(`{"colors": %s, "dank16": %s}`, opts.StockColors, dank16JSON) - importArgs = []string{"--import-json-string", importData} - - log.Info("Running matugen color hex with stock color overrides") - args := []string{"color", "hex", primaryDark, "-m", string(opts.Mode), "-t", opts.MatugenType, "-c", cfgFile.Name()} - args = appendContrastArg(args, opts.Contrast) - args = append(args, importArgs...) - if err := runMatugen(args); err != nil { - return false, err - } - } else { - log.Infof("Using dynamic theme from %s: %s", opts.Kind, opts.Value) - - matJSON, err := runMatugenDryRun(opts) - if err != nil { - return false, fmt.Errorf("matugen dry-run failed: %w", err) - } - - primaryDark = extractMatugenColor(matJSON, "primary", "dark") - primaryLight = extractMatugenColor(matJSON, "primary", "light") - surface = extractMatugenColor(matJSON, "surface", "dark") - - if primaryDark == "" { - return false, fmt.Errorf("failed to extract primary color") - } - if primaryLight == "" { - primaryLight = primaryDark - } - - dank16JSON = generateDank16Variants(primaryDark, primaryLight, surface, opts.Mode) - importData := fmt.Sprintf(`{"dank16": %s}`, dank16JSON) - importArgs = []string{"--import-json-string", importData} - - log.Infof("Running matugen %s with dank16 injection", opts.Kind) - var args []string - switch opts.Kind { - case "hex": - args = []string{"color", "hex", opts.Value} - default: - args = []string{opts.Kind, opts.Value} - } - args = append(args, "-m", string(opts.Mode), "-t", opts.MatugenType, "-c", cfgFile.Name()) - args = appendContrastArg(args, opts.Contrast) - args = append(args, importArgs...) - if err := runMatugen(args); err != nil { - return false, err - } - } - - newColors, _ := os.ReadFile(opts.ColorsOutput()) - if bytes.Equal(oldColors, newColors) && len(oldColors) > 0 { - return false, nil - } - - if opts.ColorsOnly { - return true, nil - } - - if isDMSGTKActive(opts.ConfigDir) { - switch opts.Mode { - case ColorModeLight: - syncAccentColor(primaryLight) - default: - syncAccentColor(primaryDark) - } - refreshGTK(opts.Mode) - refreshGTK4() - } - - if !opts.ShouldSkipTemplate("qt6ct") && appExists(opts.AppChecker, []string{"qt6ct"}, nil) { - refreshQt6ct() - } - - signalTerminals(opts) - - return true, nil -} - -func appendContrastArg(args []string, contrast float64) []string { - if contrast == 0 { - return args - } - return append(args, "--contrast", strconv.FormatFloat(contrast, 'f', -1, 64)) -} - -func buildMergedConfig(opts *Options, cfgFile *os.File, tmpDir string) error { - userConfigPath := filepath.Join(opts.ConfigDir, "matugen", "config.toml") - - wroteConfig := false - if opts.RunUserTemplates { - if data, err := os.ReadFile(userConfigPath); err == nil { - configSection := extractTOMLSection(string(data), "[config]", "[templates]") - if configSection != "" { - cfgFile.WriteString(configSection) - cfgFile.WriteString("\n") - wroteConfig = true - } - } - } - if !wroteConfig { - cfgFile.WriteString("[config]\n\n") - } - - baseConfigPath := filepath.Join(opts.ShellDir, "matugen", "configs", "base.toml") - if data, err := os.ReadFile(baseConfigPath); err == nil { - content := string(data) - lines := strings.Split(content, "\n") - for _, line := range lines { - if strings.TrimSpace(line) == "[config]" { - continue - } - cfgFile.WriteString(substituteVars(line, opts.ShellDir) + "\n") - } - cfgFile.WriteString("\n") - } - - fmt.Fprintf(cfgFile, `[templates.dank] -input_path = '%s/matugen/templates/dank.json' -output_path = '%s' - -`, opts.ShellDir, opts.ColorsOutput()) - - if opts.ColorsOnly { - return nil - } - - homeDir, _ := os.UserHomeDir() - for _, tmpl := range templateRegistry { - if opts.ShouldSkipTemplate(tmpl.ID) { - continue - } - - switch tmpl.Kind { - case TemplateKindGTK: - switch opts.Mode { - case ColorModeLight: - appendConfig(opts, cfgFile, nil, nil, "gtk3-light.toml") - default: - appendConfig(opts, cfgFile, nil, nil, "gtk3-dark.toml") - } - case TemplateKindTerminal: - appendTerminalConfig(opts, cfgFile, tmpDir, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile) - case TemplateKindVSCode: - appendVSCodeConfig(cfgFile, "vscode", filepath.Join(homeDir, ".vscode/extensions"), opts.ShellDir) - appendVSCodeConfig(cfgFile, "codium", filepath.Join(homeDir, ".vscode-oss/extensions"), opts.ShellDir) - appendVSCodeConfig(cfgFile, "codeoss", filepath.Join(homeDir, ".config/Code - OSS/extensions"), opts.ShellDir) - appendVSCodeConfig(cfgFile, "cursor", filepath.Join(homeDir, ".cursor/extensions"), opts.ShellDir) - appendVSCodeConfig(cfgFile, "windsurf", filepath.Join(homeDir, ".windsurf/extensions"), opts.ShellDir) - appendVSCodeConfig(cfgFile, "vscode-insiders", filepath.Join(homeDir, ".vscode-insiders/extensions"), opts.ShellDir) - case TemplateKindEmacs: - if utils.EmacsConfigDir() != "" { - appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile) - } - default: - appendConfig(opts, cfgFile, tmpl.Commands, tmpl.Flatpaks, tmpl.ConfigFile) - } - } - - if opts.RunUserTemplates { - if data, err := os.ReadFile(userConfigPath); err == nil { - templatesSection := extractTOMLSection(string(data), "[templates]", "") - if templatesSection != "" { - cfgFile.WriteString(templatesSection) - cfgFile.WriteString("\n") - } - } - } - - userPluginConfigDir := filepath.Join(opts.ConfigDir, "matugen", "dms", "configs") - if entries, err := os.ReadDir(userPluginConfigDir); err == nil { - for _, entry := range entries { - if !strings.HasSuffix(entry.Name(), ".toml") { - continue - } - if data, err := os.ReadFile(filepath.Join(userPluginConfigDir, entry.Name())); err == nil { - cfgFile.WriteString(string(data)) - cfgFile.WriteString("\n") - } - } - } - - return nil -} - -func appendConfig( - opts *Options, - cfgFile *os.File, - checkCmd []string, - checkFlatpaks []string, - fileName string, -) { - configPath := filepath.Join(opts.ShellDir, "matugen", "configs", fileName) - if _, err := os.Stat(configPath); err != nil { - return - } - if !appExists(opts.AppChecker, checkCmd, checkFlatpaks) { - return - } - data, err := os.ReadFile(configPath) - if err != nil { - return - } - cfgFile.WriteString(substituteVars(string(data), opts.ShellDir)) - cfgFile.WriteString("\n") -} - -func appendTerminalConfig(opts *Options, cfgFile *os.File, tmpDir string, checkCmd []string, checkFlatpaks []string, fileName string) { - configPath := filepath.Join(opts.ShellDir, "matugen", "configs", fileName) - if _, err := os.Stat(configPath); err != nil { - return - } - if !appExists(opts.AppChecker, checkCmd, checkFlatpaks) { - return - } - data, err := os.ReadFile(configPath) - if err != nil { - return - } - - content := string(data) - - if !opts.TerminalsAlwaysDark { - cfgFile.WriteString(substituteVars(content, opts.ShellDir)) - cfgFile.WriteString("\n") - return - } - - lines := strings.Split(content, "\n") - for _, line := range lines { - if !strings.Contains(line, "input_path") || !strings.Contains(line, "SHELL_DIR/matugen/templates/") { - continue - } - - start := strings.Index(line, "'SHELL_DIR/matugen/templates/") - if start == -1 { - continue - } - end := strings.Index(line[start+1:], "'") - if end == -1 { - continue - } - templateName := line[start+len("'SHELL_DIR/matugen/templates/") : start+1+end] - origPath := filepath.Join(opts.ShellDir, "matugen", "templates", templateName) - - origData, err := os.ReadFile(origPath) - if err != nil { - continue - } - - modified := strings.ReplaceAll(string(origData), ".default.", ".dark.") - tmpPath := filepath.Join(tmpDir, templateName) - if err := os.WriteFile(tmpPath, []byte(modified), 0o644); err != nil { - continue - } - - content = strings.ReplaceAll(content, - fmt.Sprintf("'SHELL_DIR/matugen/templates/%s'", templateName), - fmt.Sprintf("'%s'", tmpPath)) - } - - cfgFile.WriteString(substituteVars(content, opts.ShellDir)) - cfgFile.WriteString("\n") -} - -func appExists(checker utils.AppChecker, checkCmd []string, checkFlatpaks []string) bool { - // Both nil is treated as "skip check" / unconditionally run - if checkCmd == nil && checkFlatpaks == nil { - return true - } - if checkCmd != nil && checker.AnyCommandExists(checkCmd...) { - return true - } - if checkFlatpaks != nil && checker.AnyFlatpakExists(checkFlatpaks...) { - return true - } - return false -} - -func appendVSCodeConfig(cfgFile *os.File, name, extBaseDir, shellDir string) { - pattern := filepath.Join(extBaseDir, "danklinux.dms-theme-*") - matches, err := filepath.Glob(pattern) - if err != nil || len(matches) == 0 { - return - } - - extDir := matches[0] - templateDir := filepath.Join(shellDir, "matugen", "templates") - fmt.Fprintf(cfgFile, `[templates.dms%sdefault] -input_path = '%s/vscode-color-theme-default.json' -output_path = '%s/themes/dankshell-default.json' - -[templates.dms%sdark] -input_path = '%s/vscode-color-theme-dark.json' -output_path = '%s/themes/dankshell-dark.json' - -[templates.dms%slight] -input_path = '%s/vscode-color-theme-light.json' -output_path = '%s/themes/dankshell-light.json' - -`, name, templateDir, extDir, - name, templateDir, extDir, - name, templateDir, extDir) - log.Infof("Added %s theme config (extension found at %s)", name, extDir) -} - -func substituteVars(content, shellDir string) string { - result := strings.ReplaceAll(content, "'SHELL_DIR/", "'"+shellDir+"/") - result = strings.ReplaceAll(result, "'CONFIG_DIR/", "'"+utils.XDGConfigHome()+"/") - result = strings.ReplaceAll(result, "'DATA_DIR/", "'"+utils.XDGDataHome()+"/") - result = strings.ReplaceAll(result, "'CACHE_DIR/", "'"+utils.XDGCacheHome()+"/") - if emacsDir := utils.EmacsConfigDir(); emacsDir != "" { - result = strings.ReplaceAll(result, "'EMACS_DIR/", "'"+emacsDir+"/") - } - return result -} - -func extractTOMLSection(content, startMarker, endMarker string) string { - startIdx := strings.Index(content, startMarker) - if startIdx == -1 { - return "" - } - - if endMarker == "" { - return content[startIdx:] - } - - endIdx := strings.Index(content[startIdx:], endMarker) - if endIdx == -1 { - return content[startIdx:] - } - return content[startIdx : startIdx+endIdx] -} - -type matugenFlags struct { - supportsCOE bool - isV4 bool -} - -func detectMatugenVersion() (matugenFlags, error) { - matugenVersionMu.Lock() - defer matugenVersionMu.Unlock() - - if matugenVersionOK { - return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil - } - - return detectMatugenVersionLocked() -} - -func redetectMatugenVersion(old matugenFlags) (matugenFlags, bool) { - matugenVersionMu.Lock() - defer matugenVersionMu.Unlock() - - matugenVersionOK = false - flags, err := detectMatugenVersionLocked() - if err != nil { - return old, false - } - changed := flags.supportsCOE != old.supportsCOE || flags.isV4 != old.isV4 - return flags, changed -} - -func detectMatugenVersionLocked() (matugenFlags, error) { - cmd := exec.Command("matugen", "--version") - output, err := cmd.Output() - if err != nil { - return matugenFlags{}, fmt.Errorf("failed to get matugen version: %w", err) - } - - versionStr := strings.TrimSpace(string(output)) - versionStr = strings.TrimPrefix(versionStr, "matugen ") - - parts := strings.Split(versionStr, ".") - if len(parts) < 2 { - return matugenFlags{}, fmt.Errorf("unexpected matugen version format: %q", versionStr) - } - - major, err := strconv.Atoi(parts[0]) - if err != nil { - return matugenFlags{}, fmt.Errorf("failed to parse matugen major version %q: %w", parts[0], err) - } - - minor, err := strconv.Atoi(parts[1]) - if err != nil { - return matugenFlags{}, fmt.Errorf("failed to parse matugen minor version %q: %w", parts[1], err) - } - - matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1) - matugenIsV4 = major >= 4 - matugenVersionOK = true - - if matugenSupportsCOE { - log.Debugf("Matugen %s detected: continue-on-error support enabled", versionStr) - } - if matugenIsV4 { - log.Debugf("Matugen %s detected: using v4 compatibility flags", versionStr) - } - return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil -} - -func buildMatugenArgs(baseArgs []string, flags matugenFlags) []string { - args := make([]string, 0, len(baseArgs)+4) - if flags.supportsCOE { - args = append(args, "--continue-on-error") - } - args = append(args, baseArgs...) - if flags.isV4 { - args = append(args, "--source-color-index", "0") - } - return args -} - -func runMatugen(baseArgs []string) error { - flags, err := detectMatugenVersion() - if err != nil { - return err - } - - args := buildMatugenArgs(baseArgs, flags) - cmd := exec.Command("matugen", args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - runErr := cmd.Run() - if runErr == nil { - return nil - } - - log.Warnf("Matugen failed (v4=%v): %v", flags.isV4, runErr) - - newFlags, changed := redetectMatugenVersion(flags) - if !changed { - return runErr - } - - log.Warnf("Matugen version changed (v4: %v -> %v), retrying", flags.isV4, newFlags.isV4) - args = buildMatugenArgs(baseArgs, newFlags) - retryCmd := exec.Command("matugen", args...) - retryCmd.Stdout = os.Stdout - retryCmd.Stderr = os.Stderr - return retryCmd.Run() -} - -func runMatugenDryRun(opts *Options) (string, error) { - flags, err := detectMatugenVersion() - if err != nil { - return "", err - } - - output, dryErr := execDryRun(opts, flags) - if dryErr == nil { - return output, nil - } - - log.Warnf("Matugen dry-run failed (v4=%v): %v", flags.isV4, dryErr) - - newFlags, changed := redetectMatugenVersion(flags) - if !changed { - return "", dryErr - } - - log.Warnf("Matugen version changed (v4: %v -> %v), retrying dry-run", flags.isV4, newFlags.isV4) - return execDryRun(opts, newFlags) -} - -func execDryRun(opts *Options, flags matugenFlags) (string, error) { - var baseArgs []string - switch opts.Kind { - case "hex": - baseArgs = []string{"color", "hex", opts.Value} - default: - baseArgs = []string{opts.Kind, opts.Value} - } - baseArgs = append(baseArgs, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run") - baseArgs = appendContrastArg(baseArgs, opts.Contrast) - if flags.isV4 { - baseArgs = append(baseArgs, "--source-color-index", "0", "--old-json-output") - } - - cmd := exec.Command("matugen", baseArgs...) - var stderr strings.Builder - cmd.Stderr = &stderr - output, err := cmd.Output() - if err != nil { - if stderr.Len() > 0 { - return "", fmt.Errorf("matugen %v failed (v4=%v): %s", baseArgs, flags.isV4, strings.TrimSpace(stderr.String())) - } - return "", fmt.Errorf("matugen %v failed (v4=%v): %w", baseArgs, flags.isV4, err) - } - return strings.ReplaceAll(string(output), "\n", ""), nil -} - -func extractMatugenColor(jsonStr, colorName, variant string) string { - var data map[string]any - if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { - return "" - } - - colors, ok := data["colors"].(map[string]any) - if !ok { - return "" - } - - colorData, ok := colors[colorName].(map[string]any) - if !ok { - return "" - } - - variantData, ok := colorData[variant].(string) - if !ok { - return "" - } - - return variantData -} - -func extractNestedColor(jsonStr, colorName, variant string) string { - var data map[string]any - if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { - return "" - } - - colorData, ok := data[colorName].(map[string]any) - if !ok { - return "" - } - - variantData, ok := colorData[variant].(map[string]any) - if !ok { - return "" - } - - color, ok := variantData["color"].(string) - if !ok { - return "" - } - - return color -} - -func generateDank16Variants(primaryDark, primaryLight, surface string, mode ColorMode) string { - variantOpts := dank16.VariantOptions{ - PrimaryDark: primaryDark, - PrimaryLight: primaryLight, - Background: surface, - UseDPS: true, - IsLightMode: mode == ColorModeLight, - } - variantColors := dank16.GenerateVariantPalette(variantOpts) - return dank16.GenerateVariantJSON(variantColors) -} - -func isDMSGTKActive(configDir string) bool { - gtkCSS := filepath.Join(configDir, "gtk-3.0", "gtk.css") - - info, err := os.Lstat(gtkCSS) - if err != nil { - return false - } - - if info.Mode()&os.ModeSymlink != 0 { - target, err := os.Readlink(gtkCSS) - return err == nil && strings.Contains(target, "dank-colors.css") - } - - data, err := os.ReadFile(gtkCSS) - return err == nil && strings.Contains(string(data), "dank-colors.css") -} - -func refreshGTK(mode ColorMode) { - if err := utils.GsettingsSet("org.gnome.desktop.interface", "gtk-theme", ""); err != nil { - log.Warnf("Failed to reset gtk-theme: %v", err) - } - if err := utils.GsettingsSet("org.gnome.desktop.interface", "gtk-theme", mode.GTKTheme()); err != nil { - log.Warnf("Failed to set gtk-theme: %v", err) - } -} - -func refreshGTK4() { - output, err := utils.GsettingsGet("org.gnome.desktop.interface", "color-scheme") - if err != nil { - return - } - current := strings.Trim(output, "'") - - var toggle string - if current == "prefer-dark" { - toggle = "default" - } else { - toggle = "prefer-dark" - } - - if err := utils.GsettingsSet("org.gnome.desktop.interface", "color-scheme", toggle); err != nil { - log.Warnf("Failed to toggle color-scheme for GTK4 refresh: %v", err) - return - } - time.Sleep(50 * time.Millisecond) - if err := utils.GsettingsSet("org.gnome.desktop.interface", "color-scheme", current); err != nil { - log.Warnf("Failed to restore color-scheme for GTK4 refresh: %v", err) - } -} - -func refreshQt6ct() { - confPath := filepath.Join(utils.XDGConfigHome(), "qt6ct", "qt6ct.conf") - now := time.Now() - if err := os.Chtimes(confPath, now, now); err != nil { - log.Warnf("Failed to touch qt6ct.conf: %v", err) - } -} - -func signalTerminals(opts *Options) { - if !opts.ShouldSkipTemplate("kitty") && appExists(opts.AppChecker, []string{"kitty"}, nil) { - signalByName("kitty", syscall.SIGUSR1) - signalByName(".kitty-wrapped", syscall.SIGUSR1) - } - if !opts.ShouldSkipTemplate("ghostty") && appExists(opts.AppChecker, []string{"ghostty"}, nil) { - signalByName("ghostty", syscall.SIGUSR2) - signalByName(".ghostty-wrappe", syscall.SIGUSR2) - } -} - -func signalByName(name string, sig syscall.Signal) { - entries, err := os.ReadDir("/proc") - if err != nil { - return - } - for _, entry := range entries { - pid, err := strconv.Atoi(entry.Name()) - if err != nil { - continue - } - comm, err := os.ReadFile(filepath.Join("/proc", entry.Name(), "comm")) - if err != nil { - continue - } - if strings.TrimSpace(string(comm)) == name { - syscall.Kill(pid, sig) - } - } -} - -func syncColorScheme(mode ColorMode) { - scheme := "prefer-dark" - if mode == ColorModeLight { - scheme = "default" - } - - if err := utils.GsettingsSet("org.gnome.desktop.interface", "color-scheme", scheme); err != nil { - log.Warnf("Failed to sync color-scheme: %v", err) - } -} - -var adwaitaAccents = []struct { - name string - colors []colorful.Color -}{ - {"blue", hexColors("#3f8ae5", "#438de6", "#a4caee")}, - {"green", hexColors("#26a269", "#39ac76", "#81d5ad")}, - {"orange", hexColors("#f17738", "#ff7800", "#ffc994")}, - {"pink", hexColors("#e4358a", "#e64392", "#f9b3d5")}, - {"purple", hexColors("#954ab5", "#9c46b9", "#d099d6")}, - {"red", hexColors("#e84053", "#e01b24", "#f2a1a5")}, - {"slate", hexColors("#557b9f", "#6a8daf", "#b4c6d6")}, - {"teal", hexColors("#129eb0", "#2190a4", "#7bdff4")}, - {"yellow", hexColors("#cbac10", "#d4b411", "#f5c211")}, -} - -func hexColors(hexes ...string) []colorful.Color { - out := make([]colorful.Color, len(hexes)) - for i, h := range hexes { - out[i], _ = colorful.Hex(h) - } - return out -} - -func closestAdwaitaAccent(primaryHex string) string { - c, err := colorful.Hex(primaryHex) - if err != nil { - return "blue" - } - - best := "blue" - bestDist := math.MaxFloat64 - for _, a := range adwaitaAccents { - for _, ref := range a.colors { - d := c.DistanceCIEDE2000(ref) - if d < bestDist { - bestDist = d - best = a.name - } - } - } - return best -} - -func syncAccentColor(primaryHex string) { - accent := closestAdwaitaAccent(primaryHex) - log.Infof("Setting GNOME accent color: %s", accent) - if err := utils.GsettingsSet("org.gnome.desktop.interface", "accent-color", accent); err != nil { - log.Warnf("Failed to set accent-color: %v", err) - } -} - -type TemplateCheck struct { - ID string `json:"id"` - Detected bool `json:"detected"` -} - -func CheckTemplates(checker utils.AppChecker) []TemplateCheck { - if checker == nil { - checker = utils.DefaultAppChecker{} - } - - homeDir, _ := os.UserHomeDir() - checks := make([]TemplateCheck, 0, len(templateRegistry)) - - for _, tmpl := range templateRegistry { - detected := false - - switch { - case tmpl.RunUnconditionally: - detected = true - case tmpl.Kind == TemplateKindVSCode: - detected = checkVSCodeExtension(homeDir) - case tmpl.Kind == TemplateKindEmacs: - detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks) && utils.EmacsConfigDir() != "" - default: - detected = appExists(checker, tmpl.Commands, tmpl.Flatpaks) - } - - checks = append(checks, TemplateCheck{ID: tmpl.ID, Detected: detected}) - } - - return checks -} - -func checkVSCodeExtension(homeDir string) bool { - extDirs := []string{ - filepath.Join(homeDir, ".vscode/extensions"), - filepath.Join(homeDir, ".vscode-oss/extensions"), - filepath.Join(homeDir, ".config/Code - OSS/extensions"), - filepath.Join(homeDir, ".cursor/extensions"), - filepath.Join(homeDir, ".windsurf/extensions"), - } - - for _, extDir := range extDirs { - pattern := filepath.Join(extDir, "danklinux.dms-theme-*") - if matches, err := filepath.Glob(pattern); err == nil && len(matches) > 0 { - return true - } - } - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/matugen_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/matugen_test.go deleted file mode 100644 index 48880df..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/matugen_test.go +++ /dev/null @@ -1,443 +0,0 @@ -package matugen - -import ( - "os" - "path/filepath" - "strings" - "testing" - - mocks_utils "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/utils" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/stretchr/testify/assert" -) - -func TestAppendConfigBinaryExists(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - testConfig := "test config content" - configPath := filepath.Join(configsDir, "test.toml") - if err := os.WriteFile(configPath, []byte(testConfig), 0o644); err != nil { - t.Fatalf("failed to write config: %v", err) - } - - outFile := filepath.Join(tempDir, "output.toml") - cfgFile, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed to create output file: %v", err) - } - defer cfgFile.Close() - - mockChecker := mocks_utils.NewMockAppChecker(t) - mockChecker.EXPECT().AnyCommandExists("sh").Return(true) - - opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} - - appendConfig(opts, cfgFile, []string{"sh"}, nil, "test.toml") - - cfgFile.Close() - output, err := os.ReadFile(outFile) - if err != nil { - t.Fatalf("failed to read output: %v", err) - } - - if len(output) == 0 { - t.Errorf("expected config to be written when binary exists") - } - if string(output) != testConfig+"\n" { - t.Errorf("expected %q, got %q", testConfig+"\n", string(output)) - } -} - -func TestAppendConfigBinaryDoesNotExist(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - testConfig := "test config content" - configPath := filepath.Join(configsDir, "test.toml") - if err := os.WriteFile(configPath, []byte(testConfig), 0o644); err != nil { - t.Fatalf("failed to write config: %v", err) - } - - outFile := filepath.Join(tempDir, "output.toml") - cfgFile, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed to create output file: %v", err) - } - defer cfgFile.Close() - - mockChecker := mocks_utils.NewMockAppChecker(t) - mockChecker.EXPECT().AnyCommandExists("nonexistent-binary-12345").Return(false) - mockChecker.EXPECT().AnyFlatpakExists().Return(false) - - opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} - - appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{}, "test.toml") - - cfgFile.Close() - output, err := os.ReadFile(outFile) - if err != nil { - t.Fatalf("failed to read output: %v", err) - } - - if len(output) != 0 { - t.Errorf("expected no config when binary doesn't exist, got: %q", string(output)) - } -} - -func TestAppendConfigFlatpakExists(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - testConfig := "zen config content" - configPath := filepath.Join(configsDir, "test.toml") - if err := os.WriteFile(configPath, []byte(testConfig), 0o644); err != nil { - t.Fatalf("failed to write config: %v", err) - } - - outFile := filepath.Join(tempDir, "output.toml") - cfgFile, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed to create output file: %v", err) - } - defer cfgFile.Close() - - mockChecker := mocks_utils.NewMockAppChecker(t) - mockChecker.EXPECT().AnyFlatpakExists("app.zen_browser.zen").Return(true) - - opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} - - appendConfig(opts, cfgFile, nil, []string{"app.zen_browser.zen"}, "test.toml") - - cfgFile.Close() - output, err := os.ReadFile(outFile) - if err != nil { - t.Fatalf("failed to read output: %v", err) - } - - if len(output) == 0 { - t.Errorf("expected config to be written when flatpak exists") - } -} - -func TestAppendConfigFlatpakDoesNotExist(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - testConfig := "test config content" - configPath := filepath.Join(configsDir, "test.toml") - if err := os.WriteFile(configPath, []byte(testConfig), 0o644); err != nil { - t.Fatalf("failed to write config: %v", err) - } - - outFile := filepath.Join(tempDir, "output.toml") - cfgFile, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed to create output file: %v", err) - } - defer cfgFile.Close() - - mockChecker := mocks_utils.NewMockAppChecker(t) - mockChecker.EXPECT().AnyCommandExists().Return(false) - mockChecker.EXPECT().AnyFlatpakExists("com.nonexistent.flatpak").Return(false) - - opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} - - appendConfig(opts, cfgFile, []string{}, []string{"com.nonexistent.flatpak"}, "test.toml") - - cfgFile.Close() - output, err := os.ReadFile(outFile) - if err != nil { - t.Fatalf("failed to read output: %v", err) - } - - if len(output) != 0 { - t.Errorf("expected no config when flatpak doesn't exist, got: %q", string(output)) - } -} - -func TestAppendConfigBothExist(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - testConfig := "zen config content" - configPath := filepath.Join(configsDir, "test.toml") - if err := os.WriteFile(configPath, []byte(testConfig), 0o644); err != nil { - t.Fatalf("failed to write config: %v", err) - } - - outFile := filepath.Join(tempDir, "output.toml") - cfgFile, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed to create output file: %v", err) - } - defer cfgFile.Close() - - mockChecker := mocks_utils.NewMockAppChecker(t) - mockChecker.EXPECT().AnyCommandExists("sh").Return(true) - - opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} - - appendConfig(opts, cfgFile, []string{"sh"}, []string{"app.zen_browser.zen"}, "test.toml") - - cfgFile.Close() - output, err := os.ReadFile(outFile) - if err != nil { - t.Fatalf("failed to read output: %v", err) - } - - if len(output) == 0 { - t.Errorf("expected config to be written when both binary and flatpak exist") - } -} - -func TestAppendConfigNeitherExists(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - testConfig := "test config content" - configPath := filepath.Join(configsDir, "test.toml") - if err := os.WriteFile(configPath, []byte(testConfig), 0o644); err != nil { - t.Fatalf("failed to write config: %v", err) - } - - outFile := filepath.Join(tempDir, "output.toml") - cfgFile, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed to create output file: %v", err) - } - defer cfgFile.Close() - - mockChecker := mocks_utils.NewMockAppChecker(t) - mockChecker.EXPECT().AnyCommandExists("nonexistent-binary-12345").Return(false) - mockChecker.EXPECT().AnyFlatpakExists("com.nonexistent.flatpak").Return(false) - - opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} - - appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{"com.nonexistent.flatpak"}, "test.toml") - - cfgFile.Close() - output, err := os.ReadFile(outFile) - if err != nil { - t.Fatalf("failed to read output: %v", err) - } - - if len(output) != 0 { - t.Errorf("expected no config when neither exists, got: %q", string(output)) - } -} - -func TestAppendConfigNoChecks(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - testConfig := "always include" - configPath := filepath.Join(configsDir, "test.toml") - if err := os.WriteFile(configPath, []byte(testConfig), 0o644); err != nil { - t.Fatalf("failed to write config: %v", err) - } - - outFile := filepath.Join(tempDir, "output.toml") - cfgFile, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed to create output file: %v", err) - } - defer cfgFile.Close() - - opts := &Options{ShellDir: shellDir} - - appendConfig(opts, cfgFile, nil, nil, "test.toml") - - cfgFile.Close() - output, err := os.ReadFile(outFile) - if err != nil { - t.Fatalf("failed to read output: %v", err) - } - - if len(output) == 0 { - t.Errorf("expected config to be written when no checks specified") - } -} - -func TestAppendConfigFileDoesNotExist(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - outFile := filepath.Join(tempDir, "output.toml") - cfgFile, err := os.Create(outFile) - if err != nil { - t.Fatalf("failed to create output file: %v", err) - } - defer cfgFile.Close() - - opts := &Options{ShellDir: shellDir} - - appendConfig(opts, cfgFile, nil, nil, "nonexistent.toml") - - cfgFile.Close() - output, err := os.ReadFile(outFile) - if err != nil { - t.Fatalf("failed to read output: %v", err) - } - - if len(output) != 0 { - t.Errorf("expected no config when file doesn't exist, got: %q", string(output)) - } -} - -func TestSubstituteVars(t *testing.T) { - configDir := utils.XDGConfigHome() - dataDir := utils.XDGDataHome() - cacheDir := utils.XDGCacheHome() - - tests := []struct { - name string - input string - shellDir string - expected string - }{ - { - name: "substitutes SHELL_DIR", - input: "input_path = 'SHELL_DIR/matugen/templates/foo.conf'", - shellDir: "/home/user/shell", - expected: "input_path = '/home/user/shell/matugen/templates/foo.conf'", - }, - { - name: "substitutes CONFIG_DIR", - input: "output_path = 'CONFIG_DIR/kitty/theme.conf'", - shellDir: "/home/user/shell", - expected: "output_path = '" + configDir + "/kitty/theme.conf'", - }, - { - name: "substitutes DATA_DIR", - input: "output_path = 'DATA_DIR/color-schemes/theme.colors'", - shellDir: "/home/user/shell", - expected: "output_path = '" + dataDir + "/color-schemes/theme.colors'", - }, - { - name: "substitutes CACHE_DIR", - input: "output_path = 'CACHE_DIR/wal/colors.json'", - shellDir: "/home/user/shell", - expected: "output_path = '" + cacheDir + "/wal/colors.json'", - }, - { - name: "substitutes all dir types", - input: "'SHELL_DIR/a' 'CONFIG_DIR/b' 'DATA_DIR/c' 'CACHE_DIR/d'", - shellDir: "/shell", - expected: "'/shell/a' '" + configDir + "/b' '" + dataDir + "/c' '" + cacheDir + "/d'", - }, - { - name: "no substitution when no placeholders", - input: "input_path = '/absolute/path/foo.conf'", - shellDir: "/home/user/shell", - expected: "input_path = '/absolute/path/foo.conf'", - }, - { - name: "multiple SHELL_DIR occurrences", - input: "'SHELL_DIR/a' and 'SHELL_DIR/b'", - shellDir: "/shell", - expected: "'/shell/a' and '/shell/b'", - }, - { - name: "only substitutes quoted paths", - input: "SHELL_DIR/unquoted and 'SHELL_DIR/quoted'", - shellDir: "/shell", - expected: "SHELL_DIR/unquoted and '/shell/quoted'", - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - result := substituteVars(tc.input, tc.shellDir) - assert.Equal(t, tc.expected, result) - }) - } -} - -func TestBuildMergedConfigColorsOnly(t *testing.T) { - tempDir := t.TempDir() - - shellDir := filepath.Join(tempDir, "shell") - configsDir := filepath.Join(shellDir, "matugen", "configs") - if err := os.MkdirAll(configsDir, 0o755); err != nil { - t.Fatalf("failed to create configs dir: %v", err) - } - - baseConfig := "[config]\ncustom_keywords = []\n" - if err := os.WriteFile(filepath.Join(configsDir, "base.toml"), []byte(baseConfig), 0o644); err != nil { - t.Fatalf("failed to write base config: %v", err) - } - - cfgFile, err := os.CreateTemp(tempDir, "merged-*.toml") - if err != nil { - t.Fatalf("failed to create temp config: %v", err) - } - defer os.Remove(cfgFile.Name()) - defer cfgFile.Close() - - opts := &Options{ - ShellDir: shellDir, - ConfigDir: filepath.Join(tempDir, "config"), - StateDir: filepath.Join(tempDir, "state"), - ColorsOnly: true, - } - - if err := buildMergedConfig(opts, cfgFile, filepath.Join(tempDir, "templates")); err != nil { - t.Fatalf("buildMergedConfig failed: %v", err) - } - - if err := cfgFile.Close(); err != nil { - t.Fatalf("failed to close merged config: %v", err) - } - - output, err := os.ReadFile(cfgFile.Name()) - if err != nil { - t.Fatalf("failed to read merged config: %v", err) - } - - content := string(output) - assert.Contains(t, content, "[templates.dank]") - assert.Contains(t, content, "output_path = '"+filepath.Join(opts.StateDir, "dms-colors.json")+"'") - assert.NotContains(t, content, "[templates.gtk]") - assert.False(t, strings.Contains(content, "output_path = 'CONFIG_DIR/"), "colors-only config should not emit app template outputs") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/queue.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/queue.go deleted file mode 100644 index 38cf72a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/matugen/queue.go +++ /dev/null @@ -1,143 +0,0 @@ -package matugen - -import ( - "context" - "errors" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -type Result struct { - Success bool - Error error -} - -type QueuedJob struct { - Options Options - Done chan Result - Ctx context.Context - Cancel context.CancelFunc -} - -type Queue struct { - mu sync.Mutex - current *QueuedJob - pending *QueuedJob - jobDone chan struct{} -} - -var globalQueue *Queue -var queueOnce sync.Once - -func GetQueue() *Queue { - queueOnce.Do(func() { - globalQueue = &Queue{ - jobDone: make(chan struct{}, 1), - } - }) - return globalQueue -} - -func (q *Queue) Submit(opts Options) <-chan Result { - result := make(chan Result, 1) - ctx, cancel := context.WithCancel(context.Background()) - - job := &QueuedJob{ - Options: opts, - Done: result, - Ctx: ctx, - Cancel: cancel, - } - - q.mu.Lock() - - if q.pending != nil { - log.Info("Cancelling pending theme request") - q.pending.Cancel() - q.pending.Done <- Result{Success: false, Error: context.Canceled} - close(q.pending.Done) - } - - if q.current != nil { - q.pending = job - q.mu.Unlock() - log.Info("Theme request queued (worker running)") - return result - } - - q.current = job - q.mu.Unlock() - - go q.runWorker() - return result -} - -func (q *Queue) runWorker() { - for { - q.mu.Lock() - job := q.current - if job == nil { - q.mu.Unlock() - return - } - q.mu.Unlock() - - select { - case <-job.Ctx.Done(): - q.finishJob(Result{Success: false, Error: context.Canceled}) - continue - default: - } - - log.Infof("Processing theme: %s %s (%s)", job.Options.Kind, job.Options.Value, job.Options.Mode) - err := Run(job.Options) - - var result Result - switch { - case err == nil: - result = Result{Success: true} - case errors.Is(err, ErrNoChanges): - result = Result{Success: true} - default: - result = Result{Success: false, Error: err} - } - - q.finishJob(result) - } -} - -func (q *Queue) finishJob(result Result) { - q.mu.Lock() - defer q.mu.Unlock() - - if q.current != nil { - select { - case q.current.Done <- result: - default: - } - close(q.current.Done) - } - - q.current = q.pending - q.pending = nil - - if q.current == nil { - select { - case q.jobDone <- struct{}{}: - default: - } - } -} - -func (q *Queue) IsRunning() bool { - q.mu.Lock() - defer q.mu.Unlock() - return q.current != nil -} - -func (q *Queue) HasPending() bool { - q.mu.Lock() - defer q.mu.Unlock() - return q.pending != nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/brightness/mock_DBusConn.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/brightness/mock_DBusConn.go deleted file mode 100644 index ce9472d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/brightness/mock_DBusConn.go +++ /dev/null @@ -1,130 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_brightness - -import ( - dbus "github.com/godbus/dbus/v5" - mock "github.com/stretchr/testify/mock" -) - -// MockDBusConn is an autogenerated mock type for the DBusConn type -type MockDBusConn struct { - mock.Mock -} - -type MockDBusConn_Expecter struct { - mock *mock.Mock -} - -func (_m *MockDBusConn) EXPECT() *MockDBusConn_Expecter { - return &MockDBusConn_Expecter{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *MockDBusConn) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDBusConn_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockDBusConn_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockDBusConn_Expecter) Close() *MockDBusConn_Close_Call { - return &MockDBusConn_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockDBusConn_Close_Call) Run(run func()) *MockDBusConn_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDBusConn_Close_Call) Return(_a0 error) *MockDBusConn_Close_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDBusConn_Close_Call) RunAndReturn(run func() error) *MockDBusConn_Close_Call { - _c.Call.Return(run) - return _c -} - -// Object provides a mock function with given fields: dest, path -func (_m *MockDBusConn) Object(dest string, path dbus.ObjectPath) dbus.BusObject { - ret := _m.Called(dest, path) - - if len(ret) == 0 { - panic("no return value specified for Object") - } - - var r0 dbus.BusObject - if rf, ok := ret.Get(0).(func(string, dbus.ObjectPath) dbus.BusObject); ok { - r0 = rf(dest, path) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(dbus.BusObject) - } - } - - return r0 -} - -// MockDBusConn_Object_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Object' -type MockDBusConn_Object_Call struct { - *mock.Call -} - -// Object is a helper method to define mock.On call -// - dest string -// - path dbus.ObjectPath -func (_e *MockDBusConn_Expecter) Object(dest any, path any) *MockDBusConn_Object_Call { - return &MockDBusConn_Object_Call{Call: _e.mock.On("Object", dest, path)} -} - -func (_c *MockDBusConn_Object_Call) Run(run func(dest string, path dbus.ObjectPath)) *MockDBusConn_Object_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(dbus.ObjectPath)) - }) - return _c -} - -func (_c *MockDBusConn_Object_Call) Return(_a0 dbus.BusObject) *MockDBusConn_Object_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDBusConn_Object_Call) RunAndReturn(run func(string, dbus.ObjectPath) dbus.BusObject) *MockDBusConn_Object_Call { - _c.Call.Return(run) - return _c -} - -// NewMockDBusConn creates a new instance of MockDBusConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockDBusConn(t interface { - mock.TestingT - Cleanup(func()) -}, -) *MockDBusConn { - mock := &MockDBusConn{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/cups/mock_CUPSClientInterface.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/cups/mock_CUPSClientInterface.go deleted file mode 100644 index 3c98074..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/cups/mock_CUPSClientInterface.go +++ /dev/null @@ -1,1246 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_cups - -import ( - io "io" - - ipp "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" - mock "github.com/stretchr/testify/mock" -) - -// MockCUPSClientInterface is an autogenerated mock type for the CUPSClientInterface type -type MockCUPSClientInterface struct { - mock.Mock -} - -type MockCUPSClientInterface_Expecter struct { - mock *mock.Mock -} - -func (_m *MockCUPSClientInterface) EXPECT() *MockCUPSClientInterface_Expecter { - return &MockCUPSClientInterface_Expecter{mock: &_m.Mock} -} - -// AcceptJobs provides a mock function with given fields: printer -func (_m *MockCUPSClientInterface) AcceptJobs(printer string) error { - ret := _m.Called(printer) - - if len(ret) == 0 { - panic("no return value specified for AcceptJobs") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_AcceptJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AcceptJobs' -type MockCUPSClientInterface_AcceptJobs_Call struct { - *mock.Call -} - -// AcceptJobs is a helper method to define mock.On call -// - printer string -func (_e *MockCUPSClientInterface_Expecter) AcceptJobs(printer interface{}) *MockCUPSClientInterface_AcceptJobs_Call { - return &MockCUPSClientInterface_AcceptJobs_Call{Call: _e.mock.On("AcceptJobs", printer)} -} - -func (_c *MockCUPSClientInterface_AcceptJobs_Call) Run(run func(printer string)) *MockCUPSClientInterface_AcceptJobs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_AcceptJobs_Call) Return(_a0 error) *MockCUPSClientInterface_AcceptJobs_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_AcceptJobs_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_AcceptJobs_Call { - _c.Call.Return(run) - return _c -} - -// AddPrinterToClass provides a mock function with given fields: class, printer -func (_m *MockCUPSClientInterface) AddPrinterToClass(class string, printer string) error { - ret := _m.Called(class, printer) - - if len(ret) == 0 { - panic("no return value specified for AddPrinterToClass") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(class, printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_AddPrinterToClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddPrinterToClass' -type MockCUPSClientInterface_AddPrinterToClass_Call struct { - *mock.Call -} - -// AddPrinterToClass is a helper method to define mock.On call -// - class string -// - printer string -func (_e *MockCUPSClientInterface_Expecter) AddPrinterToClass(class interface{}, printer interface{}) *MockCUPSClientInterface_AddPrinterToClass_Call { - return &MockCUPSClientInterface_AddPrinterToClass_Call{Call: _e.mock.On("AddPrinterToClass", class, printer)} -} - -func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) Run(run func(class string, printer string)) *MockCUPSClientInterface_AddPrinterToClass_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) Return(_a0 error) *MockCUPSClientInterface_AddPrinterToClass_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_AddPrinterToClass_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_AddPrinterToClass_Call { - _c.Call.Return(run) - return _c -} - -// CancelAllJob provides a mock function with given fields: printer, purge -func (_m *MockCUPSClientInterface) CancelAllJob(printer string, purge bool) error { - ret := _m.Called(printer, purge) - - if len(ret) == 0 { - panic("no return value specified for CancelAllJob") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(printer, purge) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_CancelAllJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelAllJob' -type MockCUPSClientInterface_CancelAllJob_Call struct { - *mock.Call -} - -// CancelAllJob is a helper method to define mock.On call -// - printer string -// - purge bool -func (_e *MockCUPSClientInterface_Expecter) CancelAllJob(printer interface{}, purge interface{}) *MockCUPSClientInterface_CancelAllJob_Call { - return &MockCUPSClientInterface_CancelAllJob_Call{Call: _e.mock.On("CancelAllJob", printer, purge)} -} - -func (_c *MockCUPSClientInterface_CancelAllJob_Call) Run(run func(printer string, purge bool)) *MockCUPSClientInterface_CancelAllJob_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_CancelAllJob_Call) Return(_a0 error) *MockCUPSClientInterface_CancelAllJob_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_CancelAllJob_Call) RunAndReturn(run func(string, bool) error) *MockCUPSClientInterface_CancelAllJob_Call { - _c.Call.Return(run) - return _c -} - -// CancelJob provides a mock function with given fields: jobID, purge -func (_m *MockCUPSClientInterface) CancelJob(jobID int, purge bool) error { - ret := _m.Called(jobID, purge) - - if len(ret) == 0 { - panic("no return value specified for CancelJob") - } - - var r0 error - if rf, ok := ret.Get(0).(func(int, bool) error); ok { - r0 = rf(jobID, purge) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_CancelJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelJob' -type MockCUPSClientInterface_CancelJob_Call struct { - *mock.Call -} - -// CancelJob is a helper method to define mock.On call -// - jobID int -// - purge bool -func (_e *MockCUPSClientInterface_Expecter) CancelJob(jobID interface{}, purge interface{}) *MockCUPSClientInterface_CancelJob_Call { - return &MockCUPSClientInterface_CancelJob_Call{Call: _e.mock.On("CancelJob", jobID, purge)} -} - -func (_c *MockCUPSClientInterface_CancelJob_Call) Run(run func(jobID int, purge bool)) *MockCUPSClientInterface_CancelJob_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int), args[1].(bool)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_CancelJob_Call) Return(_a0 error) *MockCUPSClientInterface_CancelJob_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_CancelJob_Call) RunAndReturn(run func(int, bool) error) *MockCUPSClientInterface_CancelJob_Call { - _c.Call.Return(run) - return _c -} - -// CreatePrinter provides a mock function with given fields: name, deviceURI, ppd, shared, errorPolicy, information, location -func (_m *MockCUPSClientInterface) CreatePrinter(name string, deviceURI string, ppd string, shared bool, errorPolicy string, information string, location string) error { - ret := _m.Called(name, deviceURI, ppd, shared, errorPolicy, information, location) - - if len(ret) == 0 { - panic("no return value specified for CreatePrinter") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string, string, bool, string, string, string) error); ok { - r0 = rf(name, deviceURI, ppd, shared, errorPolicy, information, location) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_CreatePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePrinter' -type MockCUPSClientInterface_CreatePrinter_Call struct { - *mock.Call -} - -// CreatePrinter is a helper method to define mock.On call -// - name string -// - deviceURI string -// - ppd string -// - shared bool -// - errorPolicy string -// - information string -// - location string -func (_e *MockCUPSClientInterface_Expecter) CreatePrinter(name interface{}, deviceURI interface{}, ppd interface{}, shared interface{}, errorPolicy interface{}, information interface{}, location interface{}) *MockCUPSClientInterface_CreatePrinter_Call { - return &MockCUPSClientInterface_CreatePrinter_Call{Call: _e.mock.On("CreatePrinter", name, deviceURI, ppd, shared, errorPolicy, information, location)} -} - -func (_c *MockCUPSClientInterface_CreatePrinter_Call) Run(run func(name string, deviceURI string, ppd string, shared bool, errorPolicy string, information string, location string)) *MockCUPSClientInterface_CreatePrinter_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string), args[2].(string), args[3].(bool), args[4].(string), args[5].(string), args[6].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_CreatePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_CreatePrinter_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_CreatePrinter_Call) RunAndReturn(run func(string, string, string, bool, string, string, string) error) *MockCUPSClientInterface_CreatePrinter_Call { - _c.Call.Return(run) - return _c -} - -// DeleteClass provides a mock function with given fields: class -func (_m *MockCUPSClientInterface) DeleteClass(class string) error { - ret := _m.Called(class) - - if len(ret) == 0 { - panic("no return value specified for DeleteClass") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(class) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_DeleteClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteClass' -type MockCUPSClientInterface_DeleteClass_Call struct { - *mock.Call -} - -// DeleteClass is a helper method to define mock.On call -// - class string -func (_e *MockCUPSClientInterface_Expecter) DeleteClass(class interface{}) *MockCUPSClientInterface_DeleteClass_Call { - return &MockCUPSClientInterface_DeleteClass_Call{Call: _e.mock.On("DeleteClass", class)} -} - -func (_c *MockCUPSClientInterface_DeleteClass_Call) Run(run func(class string)) *MockCUPSClientInterface_DeleteClass_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_DeleteClass_Call) Return(_a0 error) *MockCUPSClientInterface_DeleteClass_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_DeleteClass_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_DeleteClass_Call { - _c.Call.Return(run) - return _c -} - -// DeletePrinter provides a mock function with given fields: printer -func (_m *MockCUPSClientInterface) DeletePrinter(printer string) error { - ret := _m.Called(printer) - - if len(ret) == 0 { - panic("no return value specified for DeletePrinter") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_DeletePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeletePrinter' -type MockCUPSClientInterface_DeletePrinter_Call struct { - *mock.Call -} - -// DeletePrinter is a helper method to define mock.On call -// - printer string -func (_e *MockCUPSClientInterface_Expecter) DeletePrinter(printer interface{}) *MockCUPSClientInterface_DeletePrinter_Call { - return &MockCUPSClientInterface_DeletePrinter_Call{Call: _e.mock.On("DeletePrinter", printer)} -} - -func (_c *MockCUPSClientInterface_DeletePrinter_Call) Run(run func(printer string)) *MockCUPSClientInterface_DeletePrinter_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_DeletePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_DeletePrinter_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_DeletePrinter_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_DeletePrinter_Call { - _c.Call.Return(run) - return _c -} - -// DeletePrinterFromClass provides a mock function with given fields: class, printer -func (_m *MockCUPSClientInterface) DeletePrinterFromClass(class string, printer string) error { - ret := _m.Called(class, printer) - - if len(ret) == 0 { - panic("no return value specified for DeletePrinterFromClass") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(class, printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_DeletePrinterFromClass_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeletePrinterFromClass' -type MockCUPSClientInterface_DeletePrinterFromClass_Call struct { - *mock.Call -} - -// DeletePrinterFromClass is a helper method to define mock.On call -// - class string -// - printer string -func (_e *MockCUPSClientInterface_Expecter) DeletePrinterFromClass(class interface{}, printer interface{}) *MockCUPSClientInterface_DeletePrinterFromClass_Call { - return &MockCUPSClientInterface_DeletePrinterFromClass_Call{Call: _e.mock.On("DeletePrinterFromClass", class, printer)} -} - -func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) Run(run func(class string, printer string)) *MockCUPSClientInterface_DeletePrinterFromClass_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) Return(_a0 error) *MockCUPSClientInterface_DeletePrinterFromClass_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_DeletePrinterFromClass_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_DeletePrinterFromClass_Call { - _c.Call.Return(run) - return _c -} - -// GetClasses provides a mock function with given fields: attributes -func (_m *MockCUPSClientInterface) GetClasses(attributes []string) (map[string]ipp.Attributes, error) { - ret := _m.Called(attributes) - - if len(ret) == 0 { - panic("no return value specified for GetClasses") - } - - var r0 map[string]ipp.Attributes - var r1 error - if rf, ok := ret.Get(0).(func([]string) (map[string]ipp.Attributes, error)); ok { - return rf(attributes) - } - if rf, ok := ret.Get(0).(func([]string) map[string]ipp.Attributes); ok { - r0 = rf(attributes) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]ipp.Attributes) - } - } - - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(attributes) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_GetClasses_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetClasses' -type MockCUPSClientInterface_GetClasses_Call struct { - *mock.Call -} - -// GetClasses is a helper method to define mock.On call -// - attributes []string -func (_e *MockCUPSClientInterface_Expecter) GetClasses(attributes interface{}) *MockCUPSClientInterface_GetClasses_Call { - return &MockCUPSClientInterface_GetClasses_Call{Call: _e.mock.On("GetClasses", attributes)} -} - -func (_c *MockCUPSClientInterface_GetClasses_Call) Run(run func(attributes []string)) *MockCUPSClientInterface_GetClasses_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_GetClasses_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetClasses_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_GetClasses_Call) RunAndReturn(run func([]string) (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetClasses_Call { - _c.Call.Return(run) - return _c -} - -// GetDevices provides a mock function with no fields -func (_m *MockCUPSClientInterface) GetDevices() (map[string]ipp.Attributes, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetDevices") - } - - var r0 map[string]ipp.Attributes - var r1 error - if rf, ok := ret.Get(0).(func() (map[string]ipp.Attributes, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() map[string]ipp.Attributes); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]ipp.Attributes) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_GetDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDevices' -type MockCUPSClientInterface_GetDevices_Call struct { - *mock.Call -} - -// GetDevices is a helper method to define mock.On call -func (_e *MockCUPSClientInterface_Expecter) GetDevices() *MockCUPSClientInterface_GetDevices_Call { - return &MockCUPSClientInterface_GetDevices_Call{Call: _e.mock.On("GetDevices")} -} - -func (_c *MockCUPSClientInterface_GetDevices_Call) Run(run func()) *MockCUPSClientInterface_GetDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockCUPSClientInterface_GetDevices_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_GetDevices_Call) RunAndReturn(run func() (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetJobs provides a mock function with given fields: printer, class, whichJobs, myJobs, firstJobId, limit, attributes -func (_m *MockCUPSClientInterface) GetJobs(printer string, class string, whichJobs string, myJobs bool, firstJobId int, limit int, attributes []string) (map[int]ipp.Attributes, error) { - ret := _m.Called(printer, class, whichJobs, myJobs, firstJobId, limit, attributes) - - if len(ret) == 0 { - panic("no return value specified for GetJobs") - } - - var r0 map[int]ipp.Attributes - var r1 error - if rf, ok := ret.Get(0).(func(string, string, string, bool, int, int, []string) (map[int]ipp.Attributes, error)); ok { - return rf(printer, class, whichJobs, myJobs, firstJobId, limit, attributes) - } - if rf, ok := ret.Get(0).(func(string, string, string, bool, int, int, []string) map[int]ipp.Attributes); ok { - r0 = rf(printer, class, whichJobs, myJobs, firstJobId, limit, attributes) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[int]ipp.Attributes) - } - } - - if rf, ok := ret.Get(1).(func(string, string, string, bool, int, int, []string) error); ok { - r1 = rf(printer, class, whichJobs, myJobs, firstJobId, limit, attributes) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_GetJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetJobs' -type MockCUPSClientInterface_GetJobs_Call struct { - *mock.Call -} - -// GetJobs is a helper method to define mock.On call -// - printer string -// - class string -// - whichJobs string -// - myJobs bool -// - firstJobId int -// - limit int -// - attributes []string -func (_e *MockCUPSClientInterface_Expecter) GetJobs(printer interface{}, class interface{}, whichJobs interface{}, myJobs interface{}, firstJobId interface{}, limit interface{}, attributes interface{}) *MockCUPSClientInterface_GetJobs_Call { - return &MockCUPSClientInterface_GetJobs_Call{Call: _e.mock.On("GetJobs", printer, class, whichJobs, myJobs, firstJobId, limit, attributes)} -} - -func (_c *MockCUPSClientInterface_GetJobs_Call) Run(run func(printer string, class string, whichJobs string, myJobs bool, firstJobId int, limit int, attributes []string)) *MockCUPSClientInterface_GetJobs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string), args[2].(string), args[3].(bool), args[4].(int), args[5].(int), args[6].([]string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_GetJobs_Call) Return(_a0 map[int]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetJobs_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_GetJobs_Call) RunAndReturn(run func(string, string, string, bool, int, int, []string) (map[int]ipp.Attributes, error)) *MockCUPSClientInterface_GetJobs_Call { - _c.Call.Return(run) - return _c -} - -// GetPPDs provides a mock function with no fields -func (_m *MockCUPSClientInterface) GetPPDs() (map[string]ipp.Attributes, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPPDs") - } - - var r0 map[string]ipp.Attributes - var r1 error - if rf, ok := ret.Get(0).(func() (map[string]ipp.Attributes, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() map[string]ipp.Attributes); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]ipp.Attributes) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_GetPPDs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPPDs' -type MockCUPSClientInterface_GetPPDs_Call struct { - *mock.Call -} - -// GetPPDs is a helper method to define mock.On call -func (_e *MockCUPSClientInterface_Expecter) GetPPDs() *MockCUPSClientInterface_GetPPDs_Call { - return &MockCUPSClientInterface_GetPPDs_Call{Call: _e.mock.On("GetPPDs")} -} - -func (_c *MockCUPSClientInterface_GetPPDs_Call) Run(run func()) *MockCUPSClientInterface_GetPPDs_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockCUPSClientInterface_GetPPDs_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetPPDs_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_GetPPDs_Call) RunAndReturn(run func() (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetPPDs_Call { - _c.Call.Return(run) - return _c -} - -// GetPrinters provides a mock function with given fields: attributes -func (_m *MockCUPSClientInterface) GetPrinters(attributes []string) (map[string]ipp.Attributes, error) { - ret := _m.Called(attributes) - - if len(ret) == 0 { - panic("no return value specified for GetPrinters") - } - - var r0 map[string]ipp.Attributes - var r1 error - if rf, ok := ret.Get(0).(func([]string) (map[string]ipp.Attributes, error)); ok { - return rf(attributes) - } - if rf, ok := ret.Get(0).(func([]string) map[string]ipp.Attributes); ok { - r0 = rf(attributes) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]ipp.Attributes) - } - } - - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(attributes) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_GetPrinters_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrinters' -type MockCUPSClientInterface_GetPrinters_Call struct { - *mock.Call -} - -// GetPrinters is a helper method to define mock.On call -// - attributes []string -func (_e *MockCUPSClientInterface_Expecter) GetPrinters(attributes interface{}) *MockCUPSClientInterface_GetPrinters_Call { - return &MockCUPSClientInterface_GetPrinters_Call{Call: _e.mock.On("GetPrinters", attributes)} -} - -func (_c *MockCUPSClientInterface_GetPrinters_Call) Run(run func(attributes []string)) *MockCUPSClientInterface_GetPrinters_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_GetPrinters_Call) Return(_a0 map[string]ipp.Attributes, _a1 error) *MockCUPSClientInterface_GetPrinters_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_GetPrinters_Call) RunAndReturn(run func([]string) (map[string]ipp.Attributes, error)) *MockCUPSClientInterface_GetPrinters_Call { - _c.Call.Return(run) - return _c -} - -// HoldJobUntil provides a mock function with given fields: jobID, holdUntil -func (_m *MockCUPSClientInterface) HoldJobUntil(jobID int, holdUntil string) error { - ret := _m.Called(jobID, holdUntil) - - if len(ret) == 0 { - panic("no return value specified for HoldJobUntil") - } - - var r0 error - if rf, ok := ret.Get(0).(func(int, string) error); ok { - r0 = rf(jobID, holdUntil) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_HoldJobUntil_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HoldJobUntil' -type MockCUPSClientInterface_HoldJobUntil_Call struct { - *mock.Call -} - -// HoldJobUntil is a helper method to define mock.On call -// - jobID int -// - holdUntil string -func (_e *MockCUPSClientInterface_Expecter) HoldJobUntil(jobID interface{}, holdUntil interface{}) *MockCUPSClientInterface_HoldJobUntil_Call { - return &MockCUPSClientInterface_HoldJobUntil_Call{Call: _e.mock.On("HoldJobUntil", jobID, holdUntil)} -} - -func (_c *MockCUPSClientInterface_HoldJobUntil_Call) Run(run func(jobID int, holdUntil string)) *MockCUPSClientInterface_HoldJobUntil_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int), args[1].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_HoldJobUntil_Call) Return(_a0 error) *MockCUPSClientInterface_HoldJobUntil_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_HoldJobUntil_Call) RunAndReturn(run func(int, string) error) *MockCUPSClientInterface_HoldJobUntil_Call { - _c.Call.Return(run) - return _c -} - -// MoveJob provides a mock function with given fields: jobID, destPrinter -func (_m *MockCUPSClientInterface) MoveJob(jobID int, destPrinter string) error { - ret := _m.Called(jobID, destPrinter) - - if len(ret) == 0 { - panic("no return value specified for MoveJob") - } - - var r0 error - if rf, ok := ret.Get(0).(func(int, string) error); ok { - r0 = rf(jobID, destPrinter) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_MoveJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MoveJob' -type MockCUPSClientInterface_MoveJob_Call struct { - *mock.Call -} - -// MoveJob is a helper method to define mock.On call -// - jobID int -// - destPrinter string -func (_e *MockCUPSClientInterface_Expecter) MoveJob(jobID interface{}, destPrinter interface{}) *MockCUPSClientInterface_MoveJob_Call { - return &MockCUPSClientInterface_MoveJob_Call{Call: _e.mock.On("MoveJob", jobID, destPrinter)} -} - -func (_c *MockCUPSClientInterface_MoveJob_Call) Run(run func(jobID int, destPrinter string)) *MockCUPSClientInterface_MoveJob_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int), args[1].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_MoveJob_Call) Return(_a0 error) *MockCUPSClientInterface_MoveJob_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_MoveJob_Call) RunAndReturn(run func(int, string) error) *MockCUPSClientInterface_MoveJob_Call { - _c.Call.Return(run) - return _c -} - -// PausePrinter provides a mock function with given fields: printer -func (_m *MockCUPSClientInterface) PausePrinter(printer string) error { - ret := _m.Called(printer) - - if len(ret) == 0 { - panic("no return value specified for PausePrinter") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_PausePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PausePrinter' -type MockCUPSClientInterface_PausePrinter_Call struct { - *mock.Call -} - -// PausePrinter is a helper method to define mock.On call -// - printer string -func (_e *MockCUPSClientInterface_Expecter) PausePrinter(printer interface{}) *MockCUPSClientInterface_PausePrinter_Call { - return &MockCUPSClientInterface_PausePrinter_Call{Call: _e.mock.On("PausePrinter", printer)} -} - -func (_c *MockCUPSClientInterface_PausePrinter_Call) Run(run func(printer string)) *MockCUPSClientInterface_PausePrinter_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_PausePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_PausePrinter_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_PausePrinter_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_PausePrinter_Call { - _c.Call.Return(run) - return _c -} - -// PrintTestPage provides a mock function with given fields: printer, testPageData, size -func (_m *MockCUPSClientInterface) PrintTestPage(printer string, testPageData io.Reader, size int) (int, error) { - ret := _m.Called(printer, testPageData, size) - - if len(ret) == 0 { - panic("no return value specified for PrintTestPage") - } - - var r0 int - var r1 error - if rf, ok := ret.Get(0).(func(string, io.Reader, int) (int, error)); ok { - return rf(printer, testPageData, size) - } - if rf, ok := ret.Get(0).(func(string, io.Reader, int) int); ok { - r0 = rf(printer, testPageData, size) - } else { - r0 = ret.Get(0).(int) - } - - if rf, ok := ret.Get(1).(func(string, io.Reader, int) error); ok { - r1 = rf(printer, testPageData, size) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_PrintTestPage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrintTestPage' -type MockCUPSClientInterface_PrintTestPage_Call struct { - *mock.Call -} - -// PrintTestPage is a helper method to define mock.On call -// - printer string -// - testPageData io.Reader -// - size int -func (_e *MockCUPSClientInterface_Expecter) PrintTestPage(printer interface{}, testPageData interface{}, size interface{}) *MockCUPSClientInterface_PrintTestPage_Call { - return &MockCUPSClientInterface_PrintTestPage_Call{Call: _e.mock.On("PrintTestPage", printer, testPageData, size)} -} - -func (_c *MockCUPSClientInterface_PrintTestPage_Call) Run(run func(printer string, testPageData io.Reader, size int)) *MockCUPSClientInterface_PrintTestPage_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(io.Reader), args[2].(int)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_PrintTestPage_Call) Return(_a0 int, _a1 error) *MockCUPSClientInterface_PrintTestPage_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_PrintTestPage_Call) RunAndReturn(run func(string, io.Reader, int) (int, error)) *MockCUPSClientInterface_PrintTestPage_Call { - _c.Call.Return(run) - return _c -} - -// RejectJobs provides a mock function with given fields: printer -func (_m *MockCUPSClientInterface) RejectJobs(printer string) error { - ret := _m.Called(printer) - - if len(ret) == 0 { - panic("no return value specified for RejectJobs") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_RejectJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RejectJobs' -type MockCUPSClientInterface_RejectJobs_Call struct { - *mock.Call -} - -// RejectJobs is a helper method to define mock.On call -// - printer string -func (_e *MockCUPSClientInterface_Expecter) RejectJobs(printer interface{}) *MockCUPSClientInterface_RejectJobs_Call { - return &MockCUPSClientInterface_RejectJobs_Call{Call: _e.mock.On("RejectJobs", printer)} -} - -func (_c *MockCUPSClientInterface_RejectJobs_Call) Run(run func(printer string)) *MockCUPSClientInterface_RejectJobs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_RejectJobs_Call) Return(_a0 error) *MockCUPSClientInterface_RejectJobs_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_RejectJobs_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_RejectJobs_Call { - _c.Call.Return(run) - return _c -} - -// RestartJob provides a mock function with given fields: jobID -func (_m *MockCUPSClientInterface) RestartJob(jobID int) error { - ret := _m.Called(jobID) - - if len(ret) == 0 { - panic("no return value specified for RestartJob") - } - - var r0 error - if rf, ok := ret.Get(0).(func(int) error); ok { - r0 = rf(jobID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_RestartJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RestartJob' -type MockCUPSClientInterface_RestartJob_Call struct { - *mock.Call -} - -// RestartJob is a helper method to define mock.On call -// - jobID int -func (_e *MockCUPSClientInterface_Expecter) RestartJob(jobID interface{}) *MockCUPSClientInterface_RestartJob_Call { - return &MockCUPSClientInterface_RestartJob_Call{Call: _e.mock.On("RestartJob", jobID)} -} - -func (_c *MockCUPSClientInterface_RestartJob_Call) Run(run func(jobID int)) *MockCUPSClientInterface_RestartJob_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_RestartJob_Call) Return(_a0 error) *MockCUPSClientInterface_RestartJob_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_RestartJob_Call) RunAndReturn(run func(int) error) *MockCUPSClientInterface_RestartJob_Call { - _c.Call.Return(run) - return _c -} - -// ResumePrinter provides a mock function with given fields: printer -func (_m *MockCUPSClientInterface) ResumePrinter(printer string) error { - ret := _m.Called(printer) - - if len(ret) == 0 { - panic("no return value specified for ResumePrinter") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(printer) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_ResumePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResumePrinter' -type MockCUPSClientInterface_ResumePrinter_Call struct { - *mock.Call -} - -// ResumePrinter is a helper method to define mock.On call -// - printer string -func (_e *MockCUPSClientInterface_Expecter) ResumePrinter(printer interface{}) *MockCUPSClientInterface_ResumePrinter_Call { - return &MockCUPSClientInterface_ResumePrinter_Call{Call: _e.mock.On("ResumePrinter", printer)} -} - -func (_c *MockCUPSClientInterface_ResumePrinter_Call) Run(run func(printer string)) *MockCUPSClientInterface_ResumePrinter_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_ResumePrinter_Call) Return(_a0 error) *MockCUPSClientInterface_ResumePrinter_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_ResumePrinter_Call) RunAndReturn(run func(string) error) *MockCUPSClientInterface_ResumePrinter_Call { - _c.Call.Return(run) - return _c -} - -// SendRequest provides a mock function with given fields: url, req, additionalResponseData -func (_m *MockCUPSClientInterface) SendRequest(url string, req *ipp.Request, additionalResponseData io.Writer) (*ipp.Response, error) { - ret := _m.Called(url, req, additionalResponseData) - - if len(ret) == 0 { - panic("no return value specified for SendRequest") - } - - var r0 *ipp.Response - var r1 error - if rf, ok := ret.Get(0).(func(string, *ipp.Request, io.Writer) (*ipp.Response, error)); ok { - return rf(url, req, additionalResponseData) - } - if rf, ok := ret.Get(0).(func(string, *ipp.Request, io.Writer) *ipp.Response); ok { - r0 = rf(url, req, additionalResponseData) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ipp.Response) - } - } - - if rf, ok := ret.Get(1).(func(string, *ipp.Request, io.Writer) error); ok { - r1 = rf(url, req, additionalResponseData) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockCUPSClientInterface_SendRequest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendRequest' -type MockCUPSClientInterface_SendRequest_Call struct { - *mock.Call -} - -// SendRequest is a helper method to define mock.On call -// - url string -// - req *ipp.Request -// - additionalResponseData io.Writer -func (_e *MockCUPSClientInterface_Expecter) SendRequest(url interface{}, req interface{}, additionalResponseData interface{}) *MockCUPSClientInterface_SendRequest_Call { - return &MockCUPSClientInterface_SendRequest_Call{Call: _e.mock.On("SendRequest", url, req, additionalResponseData)} -} - -func (_c *MockCUPSClientInterface_SendRequest_Call) Run(run func(url string, req *ipp.Request, additionalResponseData io.Writer)) *MockCUPSClientInterface_SendRequest_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(*ipp.Request), args[2].(io.Writer)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_SendRequest_Call) Return(_a0 *ipp.Response, _a1 error) *MockCUPSClientInterface_SendRequest_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockCUPSClientInterface_SendRequest_Call) RunAndReturn(run func(string, *ipp.Request, io.Writer) (*ipp.Response, error)) *MockCUPSClientInterface_SendRequest_Call { - _c.Call.Return(run) - return _c -} - -// SetPrinterInformation provides a mock function with given fields: printer, information -func (_m *MockCUPSClientInterface) SetPrinterInformation(printer string, information string) error { - ret := _m.Called(printer, information) - - if len(ret) == 0 { - panic("no return value specified for SetPrinterInformation") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(printer, information) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_SetPrinterInformation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterInformation' -type MockCUPSClientInterface_SetPrinterInformation_Call struct { - *mock.Call -} - -// SetPrinterInformation is a helper method to define mock.On call -// - printer string -// - information string -func (_e *MockCUPSClientInterface_Expecter) SetPrinterInformation(printer interface{}, information interface{}) *MockCUPSClientInterface_SetPrinterInformation_Call { - return &MockCUPSClientInterface_SetPrinterInformation_Call{Call: _e.mock.On("SetPrinterInformation", printer, information)} -} - -func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) Run(run func(printer string, information string)) *MockCUPSClientInterface_SetPrinterInformation_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterInformation_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_SetPrinterInformation_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_SetPrinterInformation_Call { - _c.Call.Return(run) - return _c -} - -// SetPrinterIsShared provides a mock function with given fields: printer, shared -func (_m *MockCUPSClientInterface) SetPrinterIsShared(printer string, shared bool) error { - ret := _m.Called(printer, shared) - - if len(ret) == 0 { - panic("no return value specified for SetPrinterIsShared") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(printer, shared) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_SetPrinterIsShared_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterIsShared' -type MockCUPSClientInterface_SetPrinterIsShared_Call struct { - *mock.Call -} - -// SetPrinterIsShared is a helper method to define mock.On call -// - printer string -// - shared bool -func (_e *MockCUPSClientInterface_Expecter) SetPrinterIsShared(printer interface{}, shared interface{}) *MockCUPSClientInterface_SetPrinterIsShared_Call { - return &MockCUPSClientInterface_SetPrinterIsShared_Call{Call: _e.mock.On("SetPrinterIsShared", printer, shared)} -} - -func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) Run(run func(printer string, shared bool)) *MockCUPSClientInterface_SetPrinterIsShared_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterIsShared_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_SetPrinterIsShared_Call) RunAndReturn(run func(string, bool) error) *MockCUPSClientInterface_SetPrinterIsShared_Call { - _c.Call.Return(run) - return _c -} - -// SetPrinterLocation provides a mock function with given fields: printer, location -func (_m *MockCUPSClientInterface) SetPrinterLocation(printer string, location string) error { - ret := _m.Called(printer, location) - - if len(ret) == 0 { - panic("no return value specified for SetPrinterLocation") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(printer, location) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockCUPSClientInterface_SetPrinterLocation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrinterLocation' -type MockCUPSClientInterface_SetPrinterLocation_Call struct { - *mock.Call -} - -// SetPrinterLocation is a helper method to define mock.On call -// - printer string -// - location string -func (_e *MockCUPSClientInterface_Expecter) SetPrinterLocation(printer interface{}, location interface{}) *MockCUPSClientInterface_SetPrinterLocation_Call { - return &MockCUPSClientInterface_SetPrinterLocation_Call{Call: _e.mock.On("SetPrinterLocation", printer, location)} -} - -func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) Run(run func(printer string, location string)) *MockCUPSClientInterface_SetPrinterLocation_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) Return(_a0 error) *MockCUPSClientInterface_SetPrinterLocation_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockCUPSClientInterface_SetPrinterLocation_Call) RunAndReturn(run func(string, string) error) *MockCUPSClientInterface_SetPrinterLocation_Call { - _c.Call.Return(run) - return _c -} - -// NewMockCUPSClientInterface creates a new instance of MockCUPSClientInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockCUPSClientInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *MockCUPSClientInterface { - mock := &MockCUPSClientInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/cups_pkhelper/mock_PkHelper.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/cups_pkhelper/mock_PkHelper.go deleted file mode 100644 index cad5bfc..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/cups_pkhelper/mock_PkHelper.go +++ /dev/null @@ -1,708 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_cups_pkhelper - -import ( - cups "github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups" - mock "github.com/stretchr/testify/mock" -) - -// MockPkHelper is an autogenerated mock type for the PkHelper type -type MockPkHelper struct { - mock.Mock -} - -type MockPkHelper_Expecter struct { - mock *mock.Mock -} - -func (_m *MockPkHelper) EXPECT() *MockPkHelper_Expecter { - return &MockPkHelper_Expecter{mock: &_m.Mock} -} - -// ClassAddPrinter provides a mock function with given fields: className, printerName -func (_m *MockPkHelper) ClassAddPrinter(className string, printerName string) error { - ret := _m.Called(className, printerName) - - if len(ret) == 0 { - panic("no return value specified for ClassAddPrinter") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(className, printerName) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_ClassAddPrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassAddPrinter' -type MockPkHelper_ClassAddPrinter_Call struct { - *mock.Call -} - -// ClassAddPrinter is a helper method to define mock.On call -// - className string -// - printerName string -func (_e *MockPkHelper_Expecter) ClassAddPrinter(className interface{}, printerName interface{}) *MockPkHelper_ClassAddPrinter_Call { - return &MockPkHelper_ClassAddPrinter_Call{Call: _e.mock.On("ClassAddPrinter", className, printerName)} -} - -func (_c *MockPkHelper_ClassAddPrinter_Call) Run(run func(className string, printerName string)) *MockPkHelper_ClassAddPrinter_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockPkHelper_ClassAddPrinter_Call) Return(_a0 error) *MockPkHelper_ClassAddPrinter_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_ClassAddPrinter_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_ClassAddPrinter_Call { - _c.Call.Return(run) - return _c -} - -// ClassDelete provides a mock function with given fields: className -func (_m *MockPkHelper) ClassDelete(className string) error { - ret := _m.Called(className) - - if len(ret) == 0 { - panic("no return value specified for ClassDelete") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(className) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_ClassDelete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassDelete' -type MockPkHelper_ClassDelete_Call struct { - *mock.Call -} - -// ClassDelete is a helper method to define mock.On call -// - className string -func (_e *MockPkHelper_Expecter) ClassDelete(className interface{}) *MockPkHelper_ClassDelete_Call { - return &MockPkHelper_ClassDelete_Call{Call: _e.mock.On("ClassDelete", className)} -} - -func (_c *MockPkHelper_ClassDelete_Call) Run(run func(className string)) *MockPkHelper_ClassDelete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockPkHelper_ClassDelete_Call) Return(_a0 error) *MockPkHelper_ClassDelete_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_ClassDelete_Call) RunAndReturn(run func(string) error) *MockPkHelper_ClassDelete_Call { - _c.Call.Return(run) - return _c -} - -// ClassDeletePrinter provides a mock function with given fields: className, printerName -func (_m *MockPkHelper) ClassDeletePrinter(className string, printerName string) error { - ret := _m.Called(className, printerName) - - if len(ret) == 0 { - panic("no return value specified for ClassDeletePrinter") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(className, printerName) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_ClassDeletePrinter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClassDeletePrinter' -type MockPkHelper_ClassDeletePrinter_Call struct { - *mock.Call -} - -// ClassDeletePrinter is a helper method to define mock.On call -// - className string -// - printerName string -func (_e *MockPkHelper_Expecter) ClassDeletePrinter(className interface{}, printerName interface{}) *MockPkHelper_ClassDeletePrinter_Call { - return &MockPkHelper_ClassDeletePrinter_Call{Call: _e.mock.On("ClassDeletePrinter", className, printerName)} -} - -func (_c *MockPkHelper_ClassDeletePrinter_Call) Run(run func(className string, printerName string)) *MockPkHelper_ClassDeletePrinter_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockPkHelper_ClassDeletePrinter_Call) Return(_a0 error) *MockPkHelper_ClassDeletePrinter_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_ClassDeletePrinter_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_ClassDeletePrinter_Call { - _c.Call.Return(run) - return _c -} - -// DevicesGet provides a mock function with given fields: timeout, limit, includeSchemes, excludeSchemes -func (_m *MockPkHelper) DevicesGet(timeout int, limit int, includeSchemes []string, excludeSchemes []string) ([]cups.Device, error) { - ret := _m.Called(timeout, limit, includeSchemes, excludeSchemes) - - if len(ret) == 0 { - panic("no return value specified for DevicesGet") - } - - var r0 []cups.Device - var r1 error - if rf, ok := ret.Get(0).(func(int, int, []string, []string) ([]cups.Device, error)); ok { - return rf(timeout, limit, includeSchemes, excludeSchemes) - } - if rf, ok := ret.Get(0).(func(int, int, []string, []string) []cups.Device); ok { - r0 = rf(timeout, limit, includeSchemes, excludeSchemes) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]cups.Device) - } - } - - if rf, ok := ret.Get(1).(func(int, int, []string, []string) error); ok { - r1 = rf(timeout, limit, includeSchemes, excludeSchemes) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockPkHelper_DevicesGet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DevicesGet' -type MockPkHelper_DevicesGet_Call struct { - *mock.Call -} - -// DevicesGet is a helper method to define mock.On call -// - timeout int -// - limit int -// - includeSchemes []string -// - excludeSchemes []string -func (_e *MockPkHelper_Expecter) DevicesGet(timeout interface{}, limit interface{}, includeSchemes interface{}, excludeSchemes interface{}) *MockPkHelper_DevicesGet_Call { - return &MockPkHelper_DevicesGet_Call{Call: _e.mock.On("DevicesGet", timeout, limit, includeSchemes, excludeSchemes)} -} - -func (_c *MockPkHelper_DevicesGet_Call) Run(run func(timeout int, limit int, includeSchemes []string, excludeSchemes []string)) *MockPkHelper_DevicesGet_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int), args[1].(int), args[2].([]string), args[3].([]string)) - }) - return _c -} - -func (_c *MockPkHelper_DevicesGet_Call) Return(_a0 []cups.Device, _a1 error) *MockPkHelper_DevicesGet_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockPkHelper_DevicesGet_Call) RunAndReturn(run func(int, int, []string, []string) ([]cups.Device, error)) *MockPkHelper_DevicesGet_Call { - _c.Call.Return(run) - return _c -} - -// JobCancelPurge provides a mock function with given fields: jobID, purge -func (_m *MockPkHelper) JobCancelPurge(jobID int, purge bool) error { - ret := _m.Called(jobID, purge) - - if len(ret) == 0 { - panic("no return value specified for JobCancelPurge") - } - - var r0 error - if rf, ok := ret.Get(0).(func(int, bool) error); ok { - r0 = rf(jobID, purge) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_JobCancelPurge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobCancelPurge' -type MockPkHelper_JobCancelPurge_Call struct { - *mock.Call -} - -// JobCancelPurge is a helper method to define mock.On call -// - jobID int -// - purge bool -func (_e *MockPkHelper_Expecter) JobCancelPurge(jobID interface{}, purge interface{}) *MockPkHelper_JobCancelPurge_Call { - return &MockPkHelper_JobCancelPurge_Call{Call: _e.mock.On("JobCancelPurge", jobID, purge)} -} - -func (_c *MockPkHelper_JobCancelPurge_Call) Run(run func(jobID int, purge bool)) *MockPkHelper_JobCancelPurge_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int), args[1].(bool)) - }) - return _c -} - -func (_c *MockPkHelper_JobCancelPurge_Call) Return(_a0 error) *MockPkHelper_JobCancelPurge_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_JobCancelPurge_Call) RunAndReturn(run func(int, bool) error) *MockPkHelper_JobCancelPurge_Call { - _c.Call.Return(run) - return _c -} - -// JobRestart provides a mock function with given fields: jobID -func (_m *MockPkHelper) JobRestart(jobID int) error { - ret := _m.Called(jobID) - - if len(ret) == 0 { - panic("no return value specified for JobRestart") - } - - var r0 error - if rf, ok := ret.Get(0).(func(int) error); ok { - r0 = rf(jobID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_JobRestart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobRestart' -type MockPkHelper_JobRestart_Call struct { - *mock.Call -} - -// JobRestart is a helper method to define mock.On call -// - jobID int -func (_e *MockPkHelper_Expecter) JobRestart(jobID interface{}) *MockPkHelper_JobRestart_Call { - return &MockPkHelper_JobRestart_Call{Call: _e.mock.On("JobRestart", jobID)} -} - -func (_c *MockPkHelper_JobRestart_Call) Run(run func(jobID int)) *MockPkHelper_JobRestart_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int)) - }) - return _c -} - -func (_c *MockPkHelper_JobRestart_Call) Return(_a0 error) *MockPkHelper_JobRestart_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_JobRestart_Call) RunAndReturn(run func(int) error) *MockPkHelper_JobRestart_Call { - _c.Call.Return(run) - return _c -} - -// JobSetHoldUntil provides a mock function with given fields: jobID, holdUntil -func (_m *MockPkHelper) JobSetHoldUntil(jobID int, holdUntil string) error { - ret := _m.Called(jobID, holdUntil) - - if len(ret) == 0 { - panic("no return value specified for JobSetHoldUntil") - } - - var r0 error - if rf, ok := ret.Get(0).(func(int, string) error); ok { - r0 = rf(jobID, holdUntil) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_JobSetHoldUntil_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'JobSetHoldUntil' -type MockPkHelper_JobSetHoldUntil_Call struct { - *mock.Call -} - -// JobSetHoldUntil is a helper method to define mock.On call -// - jobID int -// - holdUntil string -func (_e *MockPkHelper_Expecter) JobSetHoldUntil(jobID interface{}, holdUntil interface{}) *MockPkHelper_JobSetHoldUntil_Call { - return &MockPkHelper_JobSetHoldUntil_Call{Call: _e.mock.On("JobSetHoldUntil", jobID, holdUntil)} -} - -func (_c *MockPkHelper_JobSetHoldUntil_Call) Run(run func(jobID int, holdUntil string)) *MockPkHelper_JobSetHoldUntil_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int), args[1].(string)) - }) - return _c -} - -func (_c *MockPkHelper_JobSetHoldUntil_Call) Return(_a0 error) *MockPkHelper_JobSetHoldUntil_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_JobSetHoldUntil_Call) RunAndReturn(run func(int, string) error) *MockPkHelper_JobSetHoldUntil_Call { - _c.Call.Return(run) - return _c -} - -// PrinterAdd provides a mock function with given fields: name, uri, ppd, info, location -func (_m *MockPkHelper) PrinterAdd(name string, uri string, ppd string, info string, location string) error { - ret := _m.Called(name, uri, ppd, info, location) - - if len(ret) == 0 { - panic("no return value specified for PrinterAdd") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string, string, string, string) error); ok { - r0 = rf(name, uri, ppd, info, location) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_PrinterAdd_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterAdd' -type MockPkHelper_PrinterAdd_Call struct { - *mock.Call -} - -// PrinterAdd is a helper method to define mock.On call -// - name string -// - uri string -// - ppd string -// - info string -// - location string -func (_e *MockPkHelper_Expecter) PrinterAdd(name interface{}, uri interface{}, ppd interface{}, info interface{}, location interface{}) *MockPkHelper_PrinterAdd_Call { - return &MockPkHelper_PrinterAdd_Call{Call: _e.mock.On("PrinterAdd", name, uri, ppd, info, location)} -} - -func (_c *MockPkHelper_PrinterAdd_Call) Run(run func(name string, uri string, ppd string, info string, location string)) *MockPkHelper_PrinterAdd_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string), args[2].(string), args[3].(string), args[4].(string)) - }) - return _c -} - -func (_c *MockPkHelper_PrinterAdd_Call) Return(_a0 error) *MockPkHelper_PrinterAdd_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_PrinterAdd_Call) RunAndReturn(run func(string, string, string, string, string) error) *MockPkHelper_PrinterAdd_Call { - _c.Call.Return(run) - return _c -} - -// PrinterDelete provides a mock function with given fields: name -func (_m *MockPkHelper) PrinterDelete(name string) error { - ret := _m.Called(name) - - if len(ret) == 0 { - panic("no return value specified for PrinterDelete") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(name) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_PrinterDelete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterDelete' -type MockPkHelper_PrinterDelete_Call struct { - *mock.Call -} - -// PrinterDelete is a helper method to define mock.On call -// - name string -func (_e *MockPkHelper_Expecter) PrinterDelete(name interface{}) *MockPkHelper_PrinterDelete_Call { - return &MockPkHelper_PrinterDelete_Call{Call: _e.mock.On("PrinterDelete", name)} -} - -func (_c *MockPkHelper_PrinterDelete_Call) Run(run func(name string)) *MockPkHelper_PrinterDelete_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockPkHelper_PrinterDelete_Call) Return(_a0 error) *MockPkHelper_PrinterDelete_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_PrinterDelete_Call) RunAndReturn(run func(string) error) *MockPkHelper_PrinterDelete_Call { - _c.Call.Return(run) - return _c -} - -// PrinterSetAcceptJobs provides a mock function with given fields: name, enabled, reason -func (_m *MockPkHelper) PrinterSetAcceptJobs(name string, enabled bool, reason string) error { - ret := _m.Called(name, enabled, reason) - - if len(ret) == 0 { - panic("no return value specified for PrinterSetAcceptJobs") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool, string) error); ok { - r0 = rf(name, enabled, reason) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_PrinterSetAcceptJobs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetAcceptJobs' -type MockPkHelper_PrinterSetAcceptJobs_Call struct { - *mock.Call -} - -// PrinterSetAcceptJobs is a helper method to define mock.On call -// - name string -// - enabled bool -// - reason string -func (_e *MockPkHelper_Expecter) PrinterSetAcceptJobs(name interface{}, enabled interface{}, reason interface{}) *MockPkHelper_PrinterSetAcceptJobs_Call { - return &MockPkHelper_PrinterSetAcceptJobs_Call{Call: _e.mock.On("PrinterSetAcceptJobs", name, enabled, reason)} -} - -func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) Run(run func(name string, enabled bool, reason string)) *MockPkHelper_PrinterSetAcceptJobs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool), args[2].(string)) - }) - return _c -} - -func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) Return(_a0 error) *MockPkHelper_PrinterSetAcceptJobs_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_PrinterSetAcceptJobs_Call) RunAndReturn(run func(string, bool, string) error) *MockPkHelper_PrinterSetAcceptJobs_Call { - _c.Call.Return(run) - return _c -} - -// PrinterSetEnabled provides a mock function with given fields: name, enabled -func (_m *MockPkHelper) PrinterSetEnabled(name string, enabled bool) error { - ret := _m.Called(name, enabled) - - if len(ret) == 0 { - panic("no return value specified for PrinterSetEnabled") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(name, enabled) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_PrinterSetEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetEnabled' -type MockPkHelper_PrinterSetEnabled_Call struct { - *mock.Call -} - -// PrinterSetEnabled is a helper method to define mock.On call -// - name string -// - enabled bool -func (_e *MockPkHelper_Expecter) PrinterSetEnabled(name interface{}, enabled interface{}) *MockPkHelper_PrinterSetEnabled_Call { - return &MockPkHelper_PrinterSetEnabled_Call{Call: _e.mock.On("PrinterSetEnabled", name, enabled)} -} - -func (_c *MockPkHelper_PrinterSetEnabled_Call) Run(run func(name string, enabled bool)) *MockPkHelper_PrinterSetEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockPkHelper_PrinterSetEnabled_Call) Return(_a0 error) *MockPkHelper_PrinterSetEnabled_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_PrinterSetEnabled_Call) RunAndReturn(run func(string, bool) error) *MockPkHelper_PrinterSetEnabled_Call { - _c.Call.Return(run) - return _c -} - -// PrinterSetInfo provides a mock function with given fields: name, info -func (_m *MockPkHelper) PrinterSetInfo(name string, info string) error { - ret := _m.Called(name, info) - - if len(ret) == 0 { - panic("no return value specified for PrinterSetInfo") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(name, info) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_PrinterSetInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetInfo' -type MockPkHelper_PrinterSetInfo_Call struct { - *mock.Call -} - -// PrinterSetInfo is a helper method to define mock.On call -// - name string -// - info string -func (_e *MockPkHelper_Expecter) PrinterSetInfo(name interface{}, info interface{}) *MockPkHelper_PrinterSetInfo_Call { - return &MockPkHelper_PrinterSetInfo_Call{Call: _e.mock.On("PrinterSetInfo", name, info)} -} - -func (_c *MockPkHelper_PrinterSetInfo_Call) Run(run func(name string, info string)) *MockPkHelper_PrinterSetInfo_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockPkHelper_PrinterSetInfo_Call) Return(_a0 error) *MockPkHelper_PrinterSetInfo_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_PrinterSetInfo_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_PrinterSetInfo_Call { - _c.Call.Return(run) - return _c -} - -// PrinterSetLocation provides a mock function with given fields: name, location -func (_m *MockPkHelper) PrinterSetLocation(name string, location string) error { - ret := _m.Called(name, location) - - if len(ret) == 0 { - panic("no return value specified for PrinterSetLocation") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(name, location) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_PrinterSetLocation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetLocation' -type MockPkHelper_PrinterSetLocation_Call struct { - *mock.Call -} - -// PrinterSetLocation is a helper method to define mock.On call -// - name string -// - location string -func (_e *MockPkHelper_Expecter) PrinterSetLocation(name interface{}, location interface{}) *MockPkHelper_PrinterSetLocation_Call { - return &MockPkHelper_PrinterSetLocation_Call{Call: _e.mock.On("PrinterSetLocation", name, location)} -} - -func (_c *MockPkHelper_PrinterSetLocation_Call) Run(run func(name string, location string)) *MockPkHelper_PrinterSetLocation_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockPkHelper_PrinterSetLocation_Call) Return(_a0 error) *MockPkHelper_PrinterSetLocation_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_PrinterSetLocation_Call) RunAndReturn(run func(string, string) error) *MockPkHelper_PrinterSetLocation_Call { - _c.Call.Return(run) - return _c -} - -// PrinterSetShared provides a mock function with given fields: name, shared -func (_m *MockPkHelper) PrinterSetShared(name string, shared bool) error { - ret := _m.Called(name, shared) - - if len(ret) == 0 { - panic("no return value specified for PrinterSetShared") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(name, shared) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockPkHelper_PrinterSetShared_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrinterSetShared' -type MockPkHelper_PrinterSetShared_Call struct { - *mock.Call -} - -// PrinterSetShared is a helper method to define mock.On call -// - name string -// - shared bool -func (_e *MockPkHelper_Expecter) PrinterSetShared(name interface{}, shared interface{}) *MockPkHelper_PrinterSetShared_Call { - return &MockPkHelper_PrinterSetShared_Call{Call: _e.mock.On("PrinterSetShared", name, shared)} -} - -func (_c *MockPkHelper_PrinterSetShared_Call) Run(run func(name string, shared bool)) *MockPkHelper_PrinterSetShared_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockPkHelper_PrinterSetShared_Call) Return(_a0 error) *MockPkHelper_PrinterSetShared_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockPkHelper_PrinterSetShared_Call) RunAndReturn(run func(string, bool) error) *MockPkHelper_PrinterSetShared_Call { - _c.Call.Return(run) - return _c -} - -// NewMockPkHelper creates a new instance of MockPkHelper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockPkHelper(t interface { - mock.TestingT - Cleanup(func()) -}) *MockPkHelper { - mock := &MockPkHelper{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/evdev/mock_EvdevDevice.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/evdev/mock_EvdevDevice.go deleted file mode 100644 index 4d54e3e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/evdev/mock_EvdevDevice.go +++ /dev/null @@ -1,295 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_evdev - -import ( - go_evdev "github.com/holoplot/go-evdev" - mock "github.com/stretchr/testify/mock" -) - -// MockEvdevDevice is an autogenerated mock type for the EvdevDevice type -type MockEvdevDevice struct { - mock.Mock -} - -type MockEvdevDevice_Expecter struct { - mock *mock.Mock -} - -func (_m *MockEvdevDevice) EXPECT() *MockEvdevDevice_Expecter { - return &MockEvdevDevice_Expecter{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *MockEvdevDevice) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockEvdevDevice_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockEvdevDevice_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockEvdevDevice_Expecter) Close() *MockEvdevDevice_Close_Call { - return &MockEvdevDevice_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockEvdevDevice_Close_Call) Run(run func()) *MockEvdevDevice_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockEvdevDevice_Close_Call) Return(_a0 error) *MockEvdevDevice_Close_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockEvdevDevice_Close_Call) RunAndReturn(run func() error) *MockEvdevDevice_Close_Call { - _c.Call.Return(run) - return _c -} - -// Name provides a mock function with no fields -func (_m *MockEvdevDevice) Name() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Name") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockEvdevDevice_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' -type MockEvdevDevice_Name_Call struct { - *mock.Call -} - -// Name is a helper method to define mock.On call -func (_e *MockEvdevDevice_Expecter) Name() *MockEvdevDevice_Name_Call { - return &MockEvdevDevice_Name_Call{Call: _e.mock.On("Name")} -} - -func (_c *MockEvdevDevice_Name_Call) Run(run func()) *MockEvdevDevice_Name_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockEvdevDevice_Name_Call) Return(_a0 string, _a1 error) *MockEvdevDevice_Name_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockEvdevDevice_Name_Call) RunAndReturn(run func() (string, error)) *MockEvdevDevice_Name_Call { - _c.Call.Return(run) - return _c -} - -// Path provides a mock function with no fields -func (_m *MockEvdevDevice) Path() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Path") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// MockEvdevDevice_Path_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Path' -type MockEvdevDevice_Path_Call struct { - *mock.Call -} - -// Path is a helper method to define mock.On call -func (_e *MockEvdevDevice_Expecter) Path() *MockEvdevDevice_Path_Call { - return &MockEvdevDevice_Path_Call{Call: _e.mock.On("Path")} -} - -func (_c *MockEvdevDevice_Path_Call) Run(run func()) *MockEvdevDevice_Path_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockEvdevDevice_Path_Call) Return(_a0 string) *MockEvdevDevice_Path_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockEvdevDevice_Path_Call) RunAndReturn(run func() string) *MockEvdevDevice_Path_Call { - _c.Call.Return(run) - return _c -} - -// ReadOne provides a mock function with no fields -func (_m *MockEvdevDevice) ReadOne() (*go_evdev.InputEvent, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ReadOne") - } - - var r0 *go_evdev.InputEvent - var r1 error - if rf, ok := ret.Get(0).(func() (*go_evdev.InputEvent, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *go_evdev.InputEvent); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*go_evdev.InputEvent) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockEvdevDevice_ReadOne_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadOne' -type MockEvdevDevice_ReadOne_Call struct { - *mock.Call -} - -// ReadOne is a helper method to define mock.On call -func (_e *MockEvdevDevice_Expecter) ReadOne() *MockEvdevDevice_ReadOne_Call { - return &MockEvdevDevice_ReadOne_Call{Call: _e.mock.On("ReadOne")} -} - -func (_c *MockEvdevDevice_ReadOne_Call) Run(run func()) *MockEvdevDevice_ReadOne_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockEvdevDevice_ReadOne_Call) Return(_a0 *go_evdev.InputEvent, _a1 error) *MockEvdevDevice_ReadOne_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockEvdevDevice_ReadOne_Call) RunAndReturn(run func() (*go_evdev.InputEvent, error)) *MockEvdevDevice_ReadOne_Call { - _c.Call.Return(run) - return _c -} - -// State provides a mock function with given fields: t -func (_m *MockEvdevDevice) State(t go_evdev.EvType) (go_evdev.StateMap, error) { - ret := _m.Called(t) - - if len(ret) == 0 { - panic("no return value specified for State") - } - - var r0 go_evdev.StateMap - var r1 error - if rf, ok := ret.Get(0).(func(go_evdev.EvType) (go_evdev.StateMap, error)); ok { - return rf(t) - } - if rf, ok := ret.Get(0).(func(go_evdev.EvType) go_evdev.StateMap); ok { - r0 = rf(t) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(go_evdev.StateMap) - } - } - - if rf, ok := ret.Get(1).(func(go_evdev.EvType) error); ok { - r1 = rf(t) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockEvdevDevice_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type MockEvdevDevice_State_Call struct { - *mock.Call -} - -// State is a helper method to define mock.On call -// - t go_evdev.EvType -func (_e *MockEvdevDevice_Expecter) State(t interface{}) *MockEvdevDevice_State_Call { - return &MockEvdevDevice_State_Call{Call: _e.mock.On("State", t)} -} - -func (_c *MockEvdevDevice_State_Call) Run(run func(t go_evdev.EvType)) *MockEvdevDevice_State_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(go_evdev.EvType)) - }) - return _c -} - -func (_c *MockEvdevDevice_State_Call) Return(_a0 go_evdev.StateMap, _a1 error) *MockEvdevDevice_State_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockEvdevDevice_State_Call) RunAndReturn(run func(go_evdev.EvType) (go_evdev.StateMap, error)) *MockEvdevDevice_State_Call { - _c.Call.Return(run) - return _c -} - -// NewMockEvdevDevice creates a new instance of MockEvdevDevice. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockEvdevDevice(t interface { - mock.TestingT - Cleanup(func()) -}) *MockEvdevDevice { - mock := &MockEvdevDevice{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/geolocation/mock_Client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/geolocation/mock_Client.go deleted file mode 100644 index bf097cb..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/geolocation/mock_Client.go +++ /dev/null @@ -1,203 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_geolocation - -import ( - geolocation "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - mock "github.com/stretchr/testify/mock" -) - -// MockClient is an autogenerated mock type for the Client type -type MockClient struct { - mock.Mock -} - -type MockClient_Expecter struct { - mock *mock.Mock -} - -func (_m *MockClient) EXPECT() *MockClient_Expecter { - return &MockClient_Expecter{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *MockClient) Close() { - _m.Called() -} - -// MockClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockClient_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockClient_Expecter) Close() *MockClient_Close_Call { - return &MockClient_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockClient_Close_Call) Run(run func()) *MockClient_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockClient_Close_Call) Return() *MockClient_Close_Call { - _c.Call.Return() - return _c -} - -func (_c *MockClient_Close_Call) RunAndReturn(run func()) *MockClient_Close_Call { - _c.Run(run) - return _c -} - -// GetLocation provides a mock function with no fields -func (_m *MockClient) GetLocation() (geolocation.Location, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetLocation") - } - - var r0 geolocation.Location - var r1 error - if rf, ok := ret.Get(0).(func() (geolocation.Location, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() geolocation.Location); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(geolocation.Location) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockClient_GetLocation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLocation' -type MockClient_GetLocation_Call struct { - *mock.Call -} - -// GetLocation is a helper method to define mock.On call -func (_e *MockClient_Expecter) GetLocation() *MockClient_GetLocation_Call { - return &MockClient_GetLocation_Call{Call: _e.mock.On("GetLocation")} -} - -func (_c *MockClient_GetLocation_Call) Run(run func()) *MockClient_GetLocation_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockClient_GetLocation_Call) Return(_a0 geolocation.Location, _a1 error) *MockClient_GetLocation_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockClient_GetLocation_Call) RunAndReturn(run func() (geolocation.Location, error)) *MockClient_GetLocation_Call { - _c.Call.Return(run) - return _c -} - -// Subscribe provides a mock function with given fields: id -func (_m *MockClient) Subscribe(id string) chan geolocation.Location { - ret := _m.Called(id) - - if len(ret) == 0 { - panic("no return value specified for Subscribe") - } - - var r0 chan geolocation.Location - if rf, ok := ret.Get(0).(func(string) chan geolocation.Location); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(chan geolocation.Location) - } - } - - return r0 -} - -// MockClient_Subscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Subscribe' -type MockClient_Subscribe_Call struct { - *mock.Call -} - -// Subscribe is a helper method to define mock.On call -// - id string -func (_e *MockClient_Expecter) Subscribe(id interface{}) *MockClient_Subscribe_Call { - return &MockClient_Subscribe_Call{Call: _e.mock.On("Subscribe", id)} -} - -func (_c *MockClient_Subscribe_Call) Run(run func(id string)) *MockClient_Subscribe_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockClient_Subscribe_Call) Return(_a0 chan geolocation.Location) *MockClient_Subscribe_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockClient_Subscribe_Call) RunAndReturn(run func(string) chan geolocation.Location) *MockClient_Subscribe_Call { - _c.Call.Return(run) - return _c -} - -// Unsubscribe provides a mock function with given fields: id -func (_m *MockClient) Unsubscribe(id string) { - _m.Called(id) -} - -// MockClient_Unsubscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Unsubscribe' -type MockClient_Unsubscribe_Call struct { - *mock.Call -} - -// Unsubscribe is a helper method to define mock.On call -// - id string -func (_e *MockClient_Expecter) Unsubscribe(id interface{}) *MockClient_Unsubscribe_Call { - return &MockClient_Unsubscribe_Call{Call: _e.mock.On("Unsubscribe", id)} -} - -func (_c *MockClient_Unsubscribe_Call) Run(run func(id string)) *MockClient_Unsubscribe_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockClient_Unsubscribe_Call) Return() *MockClient_Unsubscribe_Call { - _c.Call.Return() - return _c -} - -func (_c *MockClient_Unsubscribe_Call) RunAndReturn(run func(string)) *MockClient_Unsubscribe_Call { - _c.Run(run) - return _c -} - -// NewMockClient creates a new instance of MockClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockClient(t interface { - mock.TestingT - Cleanup(func()) -}) *MockClient { - mock := &MockClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_AccessPoint.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_AccessPoint.go deleted file mode 100644 index ae5124d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_AccessPoint.go +++ /dev/null @@ -1,689 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockAccessPoint is an autogenerated mock type for the AccessPoint type -type MockAccessPoint struct { - mock.Mock -} - -type MockAccessPoint_Expecter struct { - mock *mock.Mock -} - -func (_m *MockAccessPoint) EXPECT() *MockAccessPoint_Expecter { - return &MockAccessPoint_Expecter{mock: &_m.Mock} -} - -// GetPath provides a mock function with no fields -func (_m *MockAccessPoint) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockAccessPoint_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockAccessPoint_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPath() *MockAccessPoint_GetPath_Call { - return &MockAccessPoint_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockAccessPoint_GetPath_Call) Run(run func()) *MockAccessPoint_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockAccessPoint_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockAccessPoint_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockAccessPoint_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFlags provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFlags' -type MockAccessPoint_GetPropertyFlags_Call struct { - *mock.Call -} - -// GetPropertyFlags is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyFlags() *MockAccessPoint_GetPropertyFlags_Call { - return &MockAccessPoint_GetPropertyFlags_Call{Call: _e.mock.On("GetPropertyFlags")} -} - -func (_c *MockAccessPoint_GetPropertyFlags_Call) Run(run func()) *MockAccessPoint_GetPropertyFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyFlags_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyFlags_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyFlags_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFrequency provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyFrequency() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFrequency") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyFrequency_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFrequency' -type MockAccessPoint_GetPropertyFrequency_Call struct { - *mock.Call -} - -// GetPropertyFrequency is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyFrequency() *MockAccessPoint_GetPropertyFrequency_Call { - return &MockAccessPoint_GetPropertyFrequency_Call{Call: _e.mock.On("GetPropertyFrequency")} -} - -func (_c *MockAccessPoint_GetPropertyFrequency_Call) Run(run func()) *MockAccessPoint_GetPropertyFrequency_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyFrequency_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyFrequency_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyFrequency_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyFrequency_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyHWAddress provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyHWAddress() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyHWAddress") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyHWAddress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyHWAddress' -type MockAccessPoint_GetPropertyHWAddress_Call struct { - *mock.Call -} - -// GetPropertyHWAddress is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyHWAddress() *MockAccessPoint_GetPropertyHWAddress_Call { - return &MockAccessPoint_GetPropertyHWAddress_Call{Call: _e.mock.On("GetPropertyHWAddress")} -} - -func (_c *MockAccessPoint_GetPropertyHWAddress_Call) Run(run func()) *MockAccessPoint_GetPropertyHWAddress_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyHWAddress_Call) Return(_a0 string, _a1 error) *MockAccessPoint_GetPropertyHWAddress_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyHWAddress_Call) RunAndReturn(run func() (string, error)) *MockAccessPoint_GetPropertyHWAddress_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyLastSeen provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyLastSeen() (int32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyLastSeen") - } - - var r0 int32 - var r1 error - if rf, ok := ret.Get(0).(func() (int32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyLastSeen_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyLastSeen' -type MockAccessPoint_GetPropertyLastSeen_Call struct { - *mock.Call -} - -// GetPropertyLastSeen is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyLastSeen() *MockAccessPoint_GetPropertyLastSeen_Call { - return &MockAccessPoint_GetPropertyLastSeen_Call{Call: _e.mock.On("GetPropertyLastSeen")} -} - -func (_c *MockAccessPoint_GetPropertyLastSeen_Call) Run(run func()) *MockAccessPoint_GetPropertyLastSeen_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyLastSeen_Call) Return(_a0 int32, _a1 error) *MockAccessPoint_GetPropertyLastSeen_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyLastSeen_Call) RunAndReturn(run func() (int32, error)) *MockAccessPoint_GetPropertyLastSeen_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMaxBitrate provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyMaxBitrate() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMaxBitrate") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyMaxBitrate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMaxBitrate' -type MockAccessPoint_GetPropertyMaxBitrate_Call struct { - *mock.Call -} - -// GetPropertyMaxBitrate is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyMaxBitrate() *MockAccessPoint_GetPropertyMaxBitrate_Call { - return &MockAccessPoint_GetPropertyMaxBitrate_Call{Call: _e.mock.On("GetPropertyMaxBitrate")} -} - -func (_c *MockAccessPoint_GetPropertyMaxBitrate_Call) Run(run func()) *MockAccessPoint_GetPropertyMaxBitrate_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyMaxBitrate_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyMaxBitrate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyMaxBitrate_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyMaxBitrate_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMode provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyMode() (gonetworkmanager.Nm80211Mode, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMode") - } - - var r0 gonetworkmanager.Nm80211Mode - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.Nm80211Mode, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.Nm80211Mode); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.Nm80211Mode) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyMode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMode' -type MockAccessPoint_GetPropertyMode_Call struct { - *mock.Call -} - -// GetPropertyMode is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyMode() *MockAccessPoint_GetPropertyMode_Call { - return &MockAccessPoint_GetPropertyMode_Call{Call: _e.mock.On("GetPropertyMode")} -} - -func (_c *MockAccessPoint_GetPropertyMode_Call) Run(run func()) *MockAccessPoint_GetPropertyMode_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyMode_Call) Return(_a0 gonetworkmanager.Nm80211Mode, _a1 error) *MockAccessPoint_GetPropertyMode_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyMode_Call) RunAndReturn(run func() (gonetworkmanager.Nm80211Mode, error)) *MockAccessPoint_GetPropertyMode_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyRSNFlags provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyRSNFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyRSNFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyRSNFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyRSNFlags' -type MockAccessPoint_GetPropertyRSNFlags_Call struct { - *mock.Call -} - -// GetPropertyRSNFlags is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyRSNFlags() *MockAccessPoint_GetPropertyRSNFlags_Call { - return &MockAccessPoint_GetPropertyRSNFlags_Call{Call: _e.mock.On("GetPropertyRSNFlags")} -} - -func (_c *MockAccessPoint_GetPropertyRSNFlags_Call) Run(run func()) *MockAccessPoint_GetPropertyRSNFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyRSNFlags_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyRSNFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyRSNFlags_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyRSNFlags_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertySSID provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertySSID() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertySSID") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertySSID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertySSID' -type MockAccessPoint_GetPropertySSID_Call struct { - *mock.Call -} - -// GetPropertySSID is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertySSID() *MockAccessPoint_GetPropertySSID_Call { - return &MockAccessPoint_GetPropertySSID_Call{Call: _e.mock.On("GetPropertySSID")} -} - -func (_c *MockAccessPoint_GetPropertySSID_Call) Run(run func()) *MockAccessPoint_GetPropertySSID_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertySSID_Call) Return(_a0 string, _a1 error) *MockAccessPoint_GetPropertySSID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertySSID_Call) RunAndReturn(run func() (string, error)) *MockAccessPoint_GetPropertySSID_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyStrength provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyStrength() (uint8, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyStrength") - } - - var r0 uint8 - var r1 error - if rf, ok := ret.Get(0).(func() (uint8, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint8); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint8) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyStrength_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyStrength' -type MockAccessPoint_GetPropertyStrength_Call struct { - *mock.Call -} - -// GetPropertyStrength is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyStrength() *MockAccessPoint_GetPropertyStrength_Call { - return &MockAccessPoint_GetPropertyStrength_Call{Call: _e.mock.On("GetPropertyStrength")} -} - -func (_c *MockAccessPoint_GetPropertyStrength_Call) Run(run func()) *MockAccessPoint_GetPropertyStrength_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyStrength_Call) Return(_a0 uint8, _a1 error) *MockAccessPoint_GetPropertyStrength_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyStrength_Call) RunAndReturn(run func() (uint8, error)) *MockAccessPoint_GetPropertyStrength_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWPAFlags provides a mock function with no fields -func (_m *MockAccessPoint) GetPropertyWPAFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWPAFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_GetPropertyWPAFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWPAFlags' -type MockAccessPoint_GetPropertyWPAFlags_Call struct { - *mock.Call -} - -// GetPropertyWPAFlags is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) GetPropertyWPAFlags() *MockAccessPoint_GetPropertyWPAFlags_Call { - return &MockAccessPoint_GetPropertyWPAFlags_Call{Call: _e.mock.On("GetPropertyWPAFlags")} -} - -func (_c *MockAccessPoint_GetPropertyWPAFlags_Call) Run(run func()) *MockAccessPoint_GetPropertyWPAFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_GetPropertyWPAFlags_Call) Return(_a0 uint32, _a1 error) *MockAccessPoint_GetPropertyWPAFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_GetPropertyWPAFlags_Call) RunAndReturn(run func() (uint32, error)) *MockAccessPoint_GetPropertyWPAFlags_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockAccessPoint) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockAccessPoint_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockAccessPoint_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockAccessPoint_Expecter) MarshalJSON() *MockAccessPoint_MarshalJSON_Call { - return &MockAccessPoint_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockAccessPoint_MarshalJSON_Call) Run(run func()) *MockAccessPoint_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockAccessPoint_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockAccessPoint_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockAccessPoint_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockAccessPoint_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// NewMockAccessPoint creates a new instance of MockAccessPoint. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockAccessPoint(t interface { - mock.TestingT - Cleanup(func()) -}) *MockAccessPoint { - mock := &MockAccessPoint{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_ActiveConnection.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_ActiveConnection.go deleted file mode 100644 index a4e3344..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_ActiveConnection.go +++ /dev/null @@ -1,1025 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockActiveConnection is an autogenerated mock type for the ActiveConnection type -type MockActiveConnection struct { - mock.Mock -} - -type MockActiveConnection_Expecter struct { - mock *mock.Mock -} - -func (_m *MockActiveConnection) EXPECT() *MockActiveConnection_Expecter { - return &MockActiveConnection_Expecter{mock: &_m.Mock} -} - -// GetPath provides a mock function with no fields -func (_m *MockActiveConnection) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockActiveConnection_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockActiveConnection_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPath() *MockActiveConnection_GetPath_Call { - return &MockActiveConnection_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockActiveConnection_GetPath_Call) Run(run func()) *MockActiveConnection_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockActiveConnection_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockActiveConnection_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockActiveConnection_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyConnection provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyConnection() (gonetworkmanager.Connection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyConnection") - } - - var r0 gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.Connection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.Connection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyConnection' -type MockActiveConnection_GetPropertyConnection_Call struct { - *mock.Call -} - -// GetPropertyConnection is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyConnection() *MockActiveConnection_GetPropertyConnection_Call { - return &MockActiveConnection_GetPropertyConnection_Call{Call: _e.mock.On("GetPropertyConnection")} -} - -func (_c *MockActiveConnection_GetPropertyConnection_Call) Run(run func()) *MockActiveConnection_GetPropertyConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyConnection_Call) Return(_a0 gonetworkmanager.Connection, _a1 error) *MockActiveConnection_GetPropertyConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyConnection_Call) RunAndReturn(run func() (gonetworkmanager.Connection, error)) *MockActiveConnection_GetPropertyConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP4Config provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDHCP4Config() (gonetworkmanager.DHCP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP4Config") - } - - var r0 gonetworkmanager.DHCP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDHCP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP4Config' -type MockActiveConnection_GetPropertyDHCP4Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP4Config is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDHCP4Config() *MockActiveConnection_GetPropertyDHCP4Config_Call { - return &MockActiveConnection_GetPropertyDHCP4Config_Call{Call: _e.mock.On("GetPropertyDHCP4Config")} -} - -func (_c *MockActiveConnection_GetPropertyDHCP4Config_Call) Run(run func()) *MockActiveConnection_GetPropertyDHCP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDHCP4Config_Call) Return(_a0 gonetworkmanager.DHCP4Config, _a1 error) *MockActiveConnection_GetPropertyDHCP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDHCP4Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP4Config, error)) *MockActiveConnection_GetPropertyDHCP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP6Config provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDHCP6Config() (gonetworkmanager.DHCP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP6Config") - } - - var r0 gonetworkmanager.DHCP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDHCP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP6Config' -type MockActiveConnection_GetPropertyDHCP6Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP6Config is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDHCP6Config() *MockActiveConnection_GetPropertyDHCP6Config_Call { - return &MockActiveConnection_GetPropertyDHCP6Config_Call{Call: _e.mock.On("GetPropertyDHCP6Config")} -} - -func (_c *MockActiveConnection_GetPropertyDHCP6Config_Call) Run(run func()) *MockActiveConnection_GetPropertyDHCP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDHCP6Config_Call) Return(_a0 gonetworkmanager.DHCP6Config, _a1 error) *MockActiveConnection_GetPropertyDHCP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDHCP6Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP6Config, error)) *MockActiveConnection_GetPropertyDHCP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDefault provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDefault() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDefault") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDefault_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDefault' -type MockActiveConnection_GetPropertyDefault_Call struct { - *mock.Call -} - -// GetPropertyDefault is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDefault() *MockActiveConnection_GetPropertyDefault_Call { - return &MockActiveConnection_GetPropertyDefault_Call{Call: _e.mock.On("GetPropertyDefault")} -} - -func (_c *MockActiveConnection_GetPropertyDefault_Call) Run(run func()) *MockActiveConnection_GetPropertyDefault_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDefault_Call) Return(_a0 bool, _a1 error) *MockActiveConnection_GetPropertyDefault_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDefault_Call) RunAndReturn(run func() (bool, error)) *MockActiveConnection_GetPropertyDefault_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDefault6 provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDefault6() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDefault6") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDefault6_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDefault6' -type MockActiveConnection_GetPropertyDefault6_Call struct { - *mock.Call -} - -// GetPropertyDefault6 is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDefault6() *MockActiveConnection_GetPropertyDefault6_Call { - return &MockActiveConnection_GetPropertyDefault6_Call{Call: _e.mock.On("GetPropertyDefault6")} -} - -func (_c *MockActiveConnection_GetPropertyDefault6_Call) Run(run func()) *MockActiveConnection_GetPropertyDefault6_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDefault6_Call) Return(_a0 bool, _a1 error) *MockActiveConnection_GetPropertyDefault6_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDefault6_Call) RunAndReturn(run func() (bool, error)) *MockActiveConnection_GetPropertyDefault6_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDevices provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDevices' -type MockActiveConnection_GetPropertyDevices_Call struct { - *mock.Call -} - -// GetPropertyDevices is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyDevices() *MockActiveConnection_GetPropertyDevices_Call { - return &MockActiveConnection_GetPropertyDevices_Call{Call: _e.mock.On("GetPropertyDevices")} -} - -func (_c *MockActiveConnection_GetPropertyDevices_Call) Run(run func()) *MockActiveConnection_GetPropertyDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockActiveConnection_GetPropertyDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockActiveConnection_GetPropertyDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyID provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyID() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyID") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyID' -type MockActiveConnection_GetPropertyID_Call struct { - *mock.Call -} - -// GetPropertyID is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyID() *MockActiveConnection_GetPropertyID_Call { - return &MockActiveConnection_GetPropertyID_Call{Call: _e.mock.On("GetPropertyID")} -} - -func (_c *MockActiveConnection_GetPropertyID_Call) Run(run func()) *MockActiveConnection_GetPropertyID_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyID_Call) Return(_a0 string, _a1 error) *MockActiveConnection_GetPropertyID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyID_Call) RunAndReturn(run func() (string, error)) *MockActiveConnection_GetPropertyID_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP4Config provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyIP4Config() (gonetworkmanager.IP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP4Config") - } - - var r0 gonetworkmanager.IP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyIP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP4Config' -type MockActiveConnection_GetPropertyIP4Config_Call struct { - *mock.Call -} - -// GetPropertyIP4Config is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyIP4Config() *MockActiveConnection_GetPropertyIP4Config_Call { - return &MockActiveConnection_GetPropertyIP4Config_Call{Call: _e.mock.On("GetPropertyIP4Config")} -} - -func (_c *MockActiveConnection_GetPropertyIP4Config_Call) Run(run func()) *MockActiveConnection_GetPropertyIP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyIP4Config_Call) Return(_a0 gonetworkmanager.IP4Config, _a1 error) *MockActiveConnection_GetPropertyIP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyIP4Config_Call) RunAndReturn(run func() (gonetworkmanager.IP4Config, error)) *MockActiveConnection_GetPropertyIP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP6Config provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyIP6Config() (gonetworkmanager.IP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP6Config") - } - - var r0 gonetworkmanager.IP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyIP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP6Config' -type MockActiveConnection_GetPropertyIP6Config_Call struct { - *mock.Call -} - -// GetPropertyIP6Config is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyIP6Config() *MockActiveConnection_GetPropertyIP6Config_Call { - return &MockActiveConnection_GetPropertyIP6Config_Call{Call: _e.mock.On("GetPropertyIP6Config")} -} - -func (_c *MockActiveConnection_GetPropertyIP6Config_Call) Run(run func()) *MockActiveConnection_GetPropertyIP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyIP6Config_Call) Return(_a0 gonetworkmanager.IP6Config, _a1 error) *MockActiveConnection_GetPropertyIP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyIP6Config_Call) RunAndReturn(run func() (gonetworkmanager.IP6Config, error)) *MockActiveConnection_GetPropertyIP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMaster provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyMaster() (gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMaster") - } - - var r0 gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyMaster_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMaster' -type MockActiveConnection_GetPropertyMaster_Call struct { - *mock.Call -} - -// GetPropertyMaster is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyMaster() *MockActiveConnection_GetPropertyMaster_Call { - return &MockActiveConnection_GetPropertyMaster_Call{Call: _e.mock.On("GetPropertyMaster")} -} - -func (_c *MockActiveConnection_GetPropertyMaster_Call) Run(run func()) *MockActiveConnection_GetPropertyMaster_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyMaster_Call) Return(_a0 gonetworkmanager.Device, _a1 error) *MockActiveConnection_GetPropertyMaster_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyMaster_Call) RunAndReturn(run func() (gonetworkmanager.Device, error)) *MockActiveConnection_GetPropertyMaster_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertySpecificObject provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertySpecificObject() (gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertySpecificObject") - } - - var r0 gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertySpecificObject_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertySpecificObject' -type MockActiveConnection_GetPropertySpecificObject_Call struct { - *mock.Call -} - -// GetPropertySpecificObject is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertySpecificObject() *MockActiveConnection_GetPropertySpecificObject_Call { - return &MockActiveConnection_GetPropertySpecificObject_Call{Call: _e.mock.On("GetPropertySpecificObject")} -} - -func (_c *MockActiveConnection_GetPropertySpecificObject_Call) Run(run func()) *MockActiveConnection_GetPropertySpecificObject_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertySpecificObject_Call) Return(_a0 gonetworkmanager.AccessPoint, _a1 error) *MockActiveConnection_GetPropertySpecificObject_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertySpecificObject_Call) RunAndReturn(run func() (gonetworkmanager.AccessPoint, error)) *MockActiveConnection_GetPropertySpecificObject_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyState provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyState() (gonetworkmanager.NmActiveConnectionState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyState") - } - - var r0 gonetworkmanager.NmActiveConnectionState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmActiveConnectionState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmActiveConnectionState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmActiveConnectionState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyState' -type MockActiveConnection_GetPropertyState_Call struct { - *mock.Call -} - -// GetPropertyState is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyState() *MockActiveConnection_GetPropertyState_Call { - return &MockActiveConnection_GetPropertyState_Call{Call: _e.mock.On("GetPropertyState")} -} - -func (_c *MockActiveConnection_GetPropertyState_Call) Run(run func()) *MockActiveConnection_GetPropertyState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyState_Call) Return(_a0 gonetworkmanager.NmActiveConnectionState, _a1 error) *MockActiveConnection_GetPropertyState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyState_Call) RunAndReturn(run func() (gonetworkmanager.NmActiveConnectionState, error)) *MockActiveConnection_GetPropertyState_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyStateFlags provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyStateFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyStateFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyStateFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyStateFlags' -type MockActiveConnection_GetPropertyStateFlags_Call struct { - *mock.Call -} - -// GetPropertyStateFlags is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyStateFlags() *MockActiveConnection_GetPropertyStateFlags_Call { - return &MockActiveConnection_GetPropertyStateFlags_Call{Call: _e.mock.On("GetPropertyStateFlags")} -} - -func (_c *MockActiveConnection_GetPropertyStateFlags_Call) Run(run func()) *MockActiveConnection_GetPropertyStateFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyStateFlags_Call) Return(_a0 uint32, _a1 error) *MockActiveConnection_GetPropertyStateFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyStateFlags_Call) RunAndReturn(run func() (uint32, error)) *MockActiveConnection_GetPropertyStateFlags_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyType provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyType() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyType") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyType' -type MockActiveConnection_GetPropertyType_Call struct { - *mock.Call -} - -// GetPropertyType is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyType() *MockActiveConnection_GetPropertyType_Call { - return &MockActiveConnection_GetPropertyType_Call{Call: _e.mock.On("GetPropertyType")} -} - -func (_c *MockActiveConnection_GetPropertyType_Call) Run(run func()) *MockActiveConnection_GetPropertyType_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyType_Call) Return(_a0 string, _a1 error) *MockActiveConnection_GetPropertyType_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyType_Call) RunAndReturn(run func() (string, error)) *MockActiveConnection_GetPropertyType_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyUUID provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyUUID() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyUUID") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyUUID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyUUID' -type MockActiveConnection_GetPropertyUUID_Call struct { - *mock.Call -} - -// GetPropertyUUID is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyUUID() *MockActiveConnection_GetPropertyUUID_Call { - return &MockActiveConnection_GetPropertyUUID_Call{Call: _e.mock.On("GetPropertyUUID")} -} - -func (_c *MockActiveConnection_GetPropertyUUID_Call) Run(run func()) *MockActiveConnection_GetPropertyUUID_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyUUID_Call) Return(_a0 string, _a1 error) *MockActiveConnection_GetPropertyUUID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyUUID_Call) RunAndReturn(run func() (string, error)) *MockActiveConnection_GetPropertyUUID_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyVPN provides a mock function with no fields -func (_m *MockActiveConnection) GetPropertyVPN() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyVPN") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockActiveConnection_GetPropertyVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyVPN' -type MockActiveConnection_GetPropertyVPN_Call struct { - *mock.Call -} - -// GetPropertyVPN is a helper method to define mock.On call -func (_e *MockActiveConnection_Expecter) GetPropertyVPN() *MockActiveConnection_GetPropertyVPN_Call { - return &MockActiveConnection_GetPropertyVPN_Call{Call: _e.mock.On("GetPropertyVPN")} -} - -func (_c *MockActiveConnection_GetPropertyVPN_Call) Run(run func()) *MockActiveConnection_GetPropertyVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockActiveConnection_GetPropertyVPN_Call) Return(_a0 bool, _a1 error) *MockActiveConnection_GetPropertyVPN_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockActiveConnection_GetPropertyVPN_Call) RunAndReturn(run func() (bool, error)) *MockActiveConnection_GetPropertyVPN_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeState provides a mock function with given fields: receiver, exit -func (_m *MockActiveConnection) SubscribeState(receiver chan gonetworkmanager.StateChange, exit chan struct{}) error { - ret := _m.Called(receiver, exit) - - if len(ret) == 0 { - panic("no return value specified for SubscribeState") - } - - var r0 error - if rf, ok := ret.Get(0).(func(chan gonetworkmanager.StateChange, chan struct{}) error); ok { - r0 = rf(receiver, exit) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockActiveConnection_SubscribeState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeState' -type MockActiveConnection_SubscribeState_Call struct { - *mock.Call -} - -// SubscribeState is a helper method to define mock.On call -// - receiver chan gonetworkmanager.StateChange -// - exit chan struct{} -func (_e *MockActiveConnection_Expecter) SubscribeState(receiver interface{}, exit interface{}) *MockActiveConnection_SubscribeState_Call { - return &MockActiveConnection_SubscribeState_Call{Call: _e.mock.On("SubscribeState", receiver, exit)} -} - -func (_c *MockActiveConnection_SubscribeState_Call) Run(run func(receiver chan gonetworkmanager.StateChange, exit chan struct{})) *MockActiveConnection_SubscribeState_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(chan gonetworkmanager.StateChange), args[1].(chan struct{})) - }) - return _c -} - -func (_c *MockActiveConnection_SubscribeState_Call) Return(err error) *MockActiveConnection_SubscribeState_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockActiveConnection_SubscribeState_Call) RunAndReturn(run func(chan gonetworkmanager.StateChange, chan struct{}) error) *MockActiveConnection_SubscribeState_Call { - _c.Call.Return(run) - return _c -} - -// NewMockActiveConnection creates a new instance of MockActiveConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockActiveConnection(t interface { - mock.TestingT - Cleanup(func()) -}) *MockActiveConnection { - mock := &MockActiveConnection{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Connection.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Connection.go deleted file mode 100644 index 84f59e1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Connection.go +++ /dev/null @@ -1,646 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockConnection is an autogenerated mock type for the Connection type -type MockConnection struct { - mock.Mock -} - -type MockConnection_Expecter struct { - mock *mock.Mock -} - -func (_m *MockConnection) EXPECT() *MockConnection_Expecter { - return &MockConnection_Expecter{mock: &_m.Mock} -} - -// ClearSecrets provides a mock function with no fields -func (_m *MockConnection) ClearSecrets() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ClearSecrets") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_ClearSecrets_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClearSecrets' -type MockConnection_ClearSecrets_Call struct { - *mock.Call -} - -// ClearSecrets is a helper method to define mock.On call -func (_e *MockConnection_Expecter) ClearSecrets() *MockConnection_ClearSecrets_Call { - return &MockConnection_ClearSecrets_Call{Call: _e.mock.On("ClearSecrets")} -} - -func (_c *MockConnection_ClearSecrets_Call) Run(run func()) *MockConnection_ClearSecrets_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_ClearSecrets_Call) Return(_a0 error) *MockConnection_ClearSecrets_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_ClearSecrets_Call) RunAndReturn(run func() error) *MockConnection_ClearSecrets_Call { - _c.Call.Return(run) - return _c -} - -// Delete provides a mock function with no fields -func (_m *MockConnection) Delete() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Delete") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type MockConnection_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -func (_e *MockConnection_Expecter) Delete() *MockConnection_Delete_Call { - return &MockConnection_Delete_Call{Call: _e.mock.On("Delete")} -} - -func (_c *MockConnection_Delete_Call) Run(run func()) *MockConnection_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_Delete_Call) Return(_a0 error) *MockConnection_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_Delete_Call) RunAndReturn(run func() error) *MockConnection_Delete_Call { - _c.Call.Return(run) - return _c -} - -// GetPath provides a mock function with no fields -func (_m *MockConnection) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockConnection_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockConnection_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetPath() *MockConnection_GetPath_Call { - return &MockConnection_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockConnection_GetPath_Call) Run(run func()) *MockConnection_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockConnection_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockConnection_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFilename provides a mock function with no fields -func (_m *MockConnection) GetPropertyFilename() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFilename") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetPropertyFilename_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFilename' -type MockConnection_GetPropertyFilename_Call struct { - *mock.Call -} - -// GetPropertyFilename is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetPropertyFilename() *MockConnection_GetPropertyFilename_Call { - return &MockConnection_GetPropertyFilename_Call{Call: _e.mock.On("GetPropertyFilename")} -} - -func (_c *MockConnection_GetPropertyFilename_Call) Run(run func()) *MockConnection_GetPropertyFilename_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetPropertyFilename_Call) Return(_a0 string, _a1 error) *MockConnection_GetPropertyFilename_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetPropertyFilename_Call) RunAndReturn(run func() (string, error)) *MockConnection_GetPropertyFilename_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFlags provides a mock function with no fields -func (_m *MockConnection) GetPropertyFlags() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFlags") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetPropertyFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFlags' -type MockConnection_GetPropertyFlags_Call struct { - *mock.Call -} - -// GetPropertyFlags is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetPropertyFlags() *MockConnection_GetPropertyFlags_Call { - return &MockConnection_GetPropertyFlags_Call{Call: _e.mock.On("GetPropertyFlags")} -} - -func (_c *MockConnection_GetPropertyFlags_Call) Run(run func()) *MockConnection_GetPropertyFlags_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetPropertyFlags_Call) Return(_a0 uint32, _a1 error) *MockConnection_GetPropertyFlags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetPropertyFlags_Call) RunAndReturn(run func() (uint32, error)) *MockConnection_GetPropertyFlags_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyUnsaved provides a mock function with no fields -func (_m *MockConnection) GetPropertyUnsaved() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyUnsaved") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetPropertyUnsaved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyUnsaved' -type MockConnection_GetPropertyUnsaved_Call struct { - *mock.Call -} - -// GetPropertyUnsaved is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetPropertyUnsaved() *MockConnection_GetPropertyUnsaved_Call { - return &MockConnection_GetPropertyUnsaved_Call{Call: _e.mock.On("GetPropertyUnsaved")} -} - -func (_c *MockConnection_GetPropertyUnsaved_Call) Run(run func()) *MockConnection_GetPropertyUnsaved_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetPropertyUnsaved_Call) Return(_a0 bool, _a1 error) *MockConnection_GetPropertyUnsaved_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetPropertyUnsaved_Call) RunAndReturn(run func() (bool, error)) *MockConnection_GetPropertyUnsaved_Call { - _c.Call.Return(run) - return _c -} - -// GetSecrets provides a mock function with given fields: settingName -func (_m *MockConnection) GetSecrets(settingName string) (gonetworkmanager.ConnectionSettings, error) { - ret := _m.Called(settingName) - - if len(ret) == 0 { - panic("no return value specified for GetSecrets") - } - - var r0 gonetworkmanager.ConnectionSettings - var r1 error - if rf, ok := ret.Get(0).(func(string) (gonetworkmanager.ConnectionSettings, error)); ok { - return rf(settingName) - } - if rf, ok := ret.Get(0).(func(string) gonetworkmanager.ConnectionSettings); ok { - r0 = rf(settingName) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ConnectionSettings) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(settingName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetSecrets_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSecrets' -type MockConnection_GetSecrets_Call struct { - *mock.Call -} - -// GetSecrets is a helper method to define mock.On call -// - settingName string -func (_e *MockConnection_Expecter) GetSecrets(settingName interface{}) *MockConnection_GetSecrets_Call { - return &MockConnection_GetSecrets_Call{Call: _e.mock.On("GetSecrets", settingName)} -} - -func (_c *MockConnection_GetSecrets_Call) Run(run func(settingName string)) *MockConnection_GetSecrets_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockConnection_GetSecrets_Call) Return(_a0 gonetworkmanager.ConnectionSettings, _a1 error) *MockConnection_GetSecrets_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetSecrets_Call) RunAndReturn(run func(string) (gonetworkmanager.ConnectionSettings, error)) *MockConnection_GetSecrets_Call { - _c.Call.Return(run) - return _c -} - -// GetSettings provides a mock function with no fields -func (_m *MockConnection) GetSettings() (gonetworkmanager.ConnectionSettings, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetSettings") - } - - var r0 gonetworkmanager.ConnectionSettings - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ConnectionSettings, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ConnectionSettings); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ConnectionSettings) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_GetSettings_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSettings' -type MockConnection_GetSettings_Call struct { - *mock.Call -} - -// GetSettings is a helper method to define mock.On call -func (_e *MockConnection_Expecter) GetSettings() *MockConnection_GetSettings_Call { - return &MockConnection_GetSettings_Call{Call: _e.mock.On("GetSettings")} -} - -func (_c *MockConnection_GetSettings_Call) Run(run func()) *MockConnection_GetSettings_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_GetSettings_Call) Return(_a0 gonetworkmanager.ConnectionSettings, _a1 error) *MockConnection_GetSettings_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_GetSettings_Call) RunAndReturn(run func() (gonetworkmanager.ConnectionSettings, error)) *MockConnection_GetSettings_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockConnection) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConnection_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockConnection_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockConnection_Expecter) MarshalJSON() *MockConnection_MarshalJSON_Call { - return &MockConnection_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockConnection_MarshalJSON_Call) Run(run func()) *MockConnection_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockConnection_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockConnection_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockConnection_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// Save provides a mock function with no fields -func (_m *MockConnection) Save() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Save") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_Save_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Save' -type MockConnection_Save_Call struct { - *mock.Call -} - -// Save is a helper method to define mock.On call -func (_e *MockConnection_Expecter) Save() *MockConnection_Save_Call { - return &MockConnection_Save_Call{Call: _e.mock.On("Save")} -} - -func (_c *MockConnection_Save_Call) Run(run func()) *MockConnection_Save_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConnection_Save_Call) Return(_a0 error) *MockConnection_Save_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_Save_Call) RunAndReturn(run func() error) *MockConnection_Save_Call { - _c.Call.Return(run) - return _c -} - -// Update provides a mock function with given fields: settings -func (_m *MockConnection) Update(settings gonetworkmanager.ConnectionSettings) error { - ret := _m.Called(settings) - - if len(ret) == 0 { - panic("no return value specified for Update") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) error); ok { - r0 = rf(settings) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' -type MockConnection_Update_Call struct { - *mock.Call -} - -// Update is a helper method to define mock.On call -// - settings gonetworkmanager.ConnectionSettings -func (_e *MockConnection_Expecter) Update(settings interface{}) *MockConnection_Update_Call { - return &MockConnection_Update_Call{Call: _e.mock.On("Update", settings)} -} - -func (_c *MockConnection_Update_Call) Run(run func(settings gonetworkmanager.ConnectionSettings)) *MockConnection_Update_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ConnectionSettings)) - }) - return _c -} - -func (_c *MockConnection_Update_Call) Return(_a0 error) *MockConnection_Update_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_Update_Call) RunAndReturn(run func(gonetworkmanager.ConnectionSettings) error) *MockConnection_Update_Call { - _c.Call.Return(run) - return _c -} - -// UpdateUnsaved provides a mock function with given fields: settings -func (_m *MockConnection) UpdateUnsaved(settings gonetworkmanager.ConnectionSettings) error { - ret := _m.Called(settings) - - if len(ret) == 0 { - panic("no return value specified for UpdateUnsaved") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) error); ok { - r0 = rf(settings) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConnection_UpdateUnsaved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUnsaved' -type MockConnection_UpdateUnsaved_Call struct { - *mock.Call -} - -// UpdateUnsaved is a helper method to define mock.On call -// - settings gonetworkmanager.ConnectionSettings -func (_e *MockConnection_Expecter) UpdateUnsaved(settings interface{}) *MockConnection_UpdateUnsaved_Call { - return &MockConnection_UpdateUnsaved_Call{Call: _e.mock.On("UpdateUnsaved", settings)} -} - -func (_c *MockConnection_UpdateUnsaved_Call) Run(run func(settings gonetworkmanager.ConnectionSettings)) *MockConnection_UpdateUnsaved_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ConnectionSettings)) - }) - return _c -} - -func (_c *MockConnection_UpdateUnsaved_Call) Return(_a0 error) *MockConnection_UpdateUnsaved_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConnection_UpdateUnsaved_Call) RunAndReturn(run func(gonetworkmanager.ConnectionSettings) error) *MockConnection_UpdateUnsaved_Call { - _c.Call.Return(run) - return _c -} - -// NewMockConnection creates a new instance of MockConnection. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockConnection(t interface { - mock.TestingT - Cleanup(func()) -}) *MockConnection { - mock := &MockConnection{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Device.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Device.go deleted file mode 100644 index b2fc0b5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Device.go +++ /dev/null @@ -1,1638 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockDevice is an autogenerated mock type for the Device type -type MockDevice struct { - mock.Mock -} - -type MockDevice_Expecter struct { - mock *mock.Mock -} - -func (_m *MockDevice) EXPECT() *MockDevice_Expecter { - return &MockDevice_Expecter{mock: &_m.Mock} -} - -// Delete provides a mock function with no fields -func (_m *MockDevice) Delete() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Delete") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type MockDevice_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -func (_e *MockDevice_Expecter) Delete() *MockDevice_Delete_Call { - return &MockDevice_Delete_Call{Call: _e.mock.On("Delete")} -} - -func (_c *MockDevice_Delete_Call) Run(run func()) *MockDevice_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_Delete_Call) Return(_a0 error) *MockDevice_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_Delete_Call) RunAndReturn(run func() error) *MockDevice_Delete_Call { - _c.Call.Return(run) - return _c -} - -// Disconnect provides a mock function with no fields -func (_m *MockDevice) Disconnect() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Disconnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_Disconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Disconnect' -type MockDevice_Disconnect_Call struct { - *mock.Call -} - -// Disconnect is a helper method to define mock.On call -func (_e *MockDevice_Expecter) Disconnect() *MockDevice_Disconnect_Call { - return &MockDevice_Disconnect_Call{Call: _e.mock.On("Disconnect")} -} - -func (_c *MockDevice_Disconnect_Call) Run(run func()) *MockDevice_Disconnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_Disconnect_Call) Return(_a0 error) *MockDevice_Disconnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_Disconnect_Call) RunAndReturn(run func() error) *MockDevice_Disconnect_Call { - _c.Call.Return(run) - return _c -} - -// GetPath provides a mock function with no fields -func (_m *MockDevice) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockDevice_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockDevice_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPath() *MockDevice_GetPath_Call { - return &MockDevice_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockDevice_GetPath_Call) Run(run func()) *MockDevice_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockDevice_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockDevice_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActiveConnection provides a mock function with no fields -func (_m *MockDevice) GetPropertyActiveConnection() (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActiveConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyActiveConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActiveConnection' -type MockDevice_GetPropertyActiveConnection_Call struct { - *mock.Call -} - -// GetPropertyActiveConnection is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyActiveConnection() *MockDevice_GetPropertyActiveConnection_Call { - return &MockDevice_GetPropertyActiveConnection_Call{Call: _e.mock.On("GetPropertyActiveConnection")} -} - -func (_c *MockDevice_GetPropertyActiveConnection_Call) Run(run func()) *MockDevice_GetPropertyActiveConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyActiveConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockDevice_GetPropertyActiveConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyActiveConnection_Call) RunAndReturn(run func() (gonetworkmanager.ActiveConnection, error)) *MockDevice_GetPropertyActiveConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAutoConnect provides a mock function with no fields -func (_m *MockDevice) GetPropertyAutoConnect() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAutoConnect") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyAutoConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAutoConnect' -type MockDevice_GetPropertyAutoConnect_Call struct { - *mock.Call -} - -// GetPropertyAutoConnect is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyAutoConnect() *MockDevice_GetPropertyAutoConnect_Call { - return &MockDevice_GetPropertyAutoConnect_Call{Call: _e.mock.On("GetPropertyAutoConnect")} -} - -func (_c *MockDevice_GetPropertyAutoConnect_Call) Run(run func()) *MockDevice_GetPropertyAutoConnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyAutoConnect_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyAutoConnect_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyAutoConnect_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyAutoConnect_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAvailableConnections provides a mock function with no fields -func (_m *MockDevice) GetPropertyAvailableConnections() ([]gonetworkmanager.Connection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAvailableConnections") - } - - var r0 []gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Connection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Connection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyAvailableConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAvailableConnections' -type MockDevice_GetPropertyAvailableConnections_Call struct { - *mock.Call -} - -// GetPropertyAvailableConnections is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyAvailableConnections() *MockDevice_GetPropertyAvailableConnections_Call { - return &MockDevice_GetPropertyAvailableConnections_Call{Call: _e.mock.On("GetPropertyAvailableConnections")} -} - -func (_c *MockDevice_GetPropertyAvailableConnections_Call) Run(run func()) *MockDevice_GetPropertyAvailableConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyAvailableConnections_Call) Return(_a0 []gonetworkmanager.Connection, _a1 error) *MockDevice_GetPropertyAvailableConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyAvailableConnections_Call) RunAndReturn(run func() ([]gonetworkmanager.Connection, error)) *MockDevice_GetPropertyAvailableConnections_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP4Config provides a mock function with no fields -func (_m *MockDevice) GetPropertyDHCP4Config() (gonetworkmanager.DHCP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP4Config") - } - - var r0 gonetworkmanager.DHCP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDHCP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP4Config' -type MockDevice_GetPropertyDHCP4Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP4Config is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDHCP4Config() *MockDevice_GetPropertyDHCP4Config_Call { - return &MockDevice_GetPropertyDHCP4Config_Call{Call: _e.mock.On("GetPropertyDHCP4Config")} -} - -func (_c *MockDevice_GetPropertyDHCP4Config_Call) Run(run func()) *MockDevice_GetPropertyDHCP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDHCP4Config_Call) Return(_a0 gonetworkmanager.DHCP4Config, _a1 error) *MockDevice_GetPropertyDHCP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDHCP4Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP4Config, error)) *MockDevice_GetPropertyDHCP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP6Config provides a mock function with no fields -func (_m *MockDevice) GetPropertyDHCP6Config() (gonetworkmanager.DHCP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP6Config") - } - - var r0 gonetworkmanager.DHCP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDHCP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP6Config' -type MockDevice_GetPropertyDHCP6Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP6Config is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDHCP6Config() *MockDevice_GetPropertyDHCP6Config_Call { - return &MockDevice_GetPropertyDHCP6Config_Call{Call: _e.mock.On("GetPropertyDHCP6Config")} -} - -func (_c *MockDevice_GetPropertyDHCP6Config_Call) Run(run func()) *MockDevice_GetPropertyDHCP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDHCP6Config_Call) Return(_a0 gonetworkmanager.DHCP6Config, _a1 error) *MockDevice_GetPropertyDHCP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDHCP6Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP6Config, error)) *MockDevice_GetPropertyDHCP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDeviceType provides a mock function with no fields -func (_m *MockDevice) GetPropertyDeviceType() (gonetworkmanager.NmDeviceType, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDeviceType") - } - - var r0 gonetworkmanager.NmDeviceType - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmDeviceType, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmDeviceType); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmDeviceType) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDeviceType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDeviceType' -type MockDevice_GetPropertyDeviceType_Call struct { - *mock.Call -} - -// GetPropertyDeviceType is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDeviceType() *MockDevice_GetPropertyDeviceType_Call { - return &MockDevice_GetPropertyDeviceType_Call{Call: _e.mock.On("GetPropertyDeviceType")} -} - -func (_c *MockDevice_GetPropertyDeviceType_Call) Run(run func()) *MockDevice_GetPropertyDeviceType_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDeviceType_Call) Return(_a0 gonetworkmanager.NmDeviceType, _a1 error) *MockDevice_GetPropertyDeviceType_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDeviceType_Call) RunAndReturn(run func() (gonetworkmanager.NmDeviceType, error)) *MockDevice_GetPropertyDeviceType_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDriver provides a mock function with no fields -func (_m *MockDevice) GetPropertyDriver() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDriver") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDriver_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDriver' -type MockDevice_GetPropertyDriver_Call struct { - *mock.Call -} - -// GetPropertyDriver is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDriver() *MockDevice_GetPropertyDriver_Call { - return &MockDevice_GetPropertyDriver_Call{Call: _e.mock.On("GetPropertyDriver")} -} - -func (_c *MockDevice_GetPropertyDriver_Call) Run(run func()) *MockDevice_GetPropertyDriver_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDriver_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyDriver_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDriver_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyDriver_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDriverVersion provides a mock function with no fields -func (_m *MockDevice) GetPropertyDriverVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDriverVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyDriverVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDriverVersion' -type MockDevice_GetPropertyDriverVersion_Call struct { - *mock.Call -} - -// GetPropertyDriverVersion is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyDriverVersion() *MockDevice_GetPropertyDriverVersion_Call { - return &MockDevice_GetPropertyDriverVersion_Call{Call: _e.mock.On("GetPropertyDriverVersion")} -} - -func (_c *MockDevice_GetPropertyDriverVersion_Call) Run(run func()) *MockDevice_GetPropertyDriverVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyDriverVersion_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyDriverVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyDriverVersion_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyDriverVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFirmwareMissing provides a mock function with no fields -func (_m *MockDevice) GetPropertyFirmwareMissing() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFirmwareMissing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyFirmwareMissing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFirmwareMissing' -type MockDevice_GetPropertyFirmwareMissing_Call struct { - *mock.Call -} - -// GetPropertyFirmwareMissing is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyFirmwareMissing() *MockDevice_GetPropertyFirmwareMissing_Call { - return &MockDevice_GetPropertyFirmwareMissing_Call{Call: _e.mock.On("GetPropertyFirmwareMissing")} -} - -func (_c *MockDevice_GetPropertyFirmwareMissing_Call) Run(run func()) *MockDevice_GetPropertyFirmwareMissing_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyFirmwareMissing_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyFirmwareMissing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyFirmwareMissing_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyFirmwareMissing_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFirmwareVersion provides a mock function with no fields -func (_m *MockDevice) GetPropertyFirmwareVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFirmwareVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyFirmwareVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFirmwareVersion' -type MockDevice_GetPropertyFirmwareVersion_Call struct { - *mock.Call -} - -// GetPropertyFirmwareVersion is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyFirmwareVersion() *MockDevice_GetPropertyFirmwareVersion_Call { - return &MockDevice_GetPropertyFirmwareVersion_Call{Call: _e.mock.On("GetPropertyFirmwareVersion")} -} - -func (_c *MockDevice_GetPropertyFirmwareVersion_Call) Run(run func()) *MockDevice_GetPropertyFirmwareVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyFirmwareVersion_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyFirmwareVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyFirmwareVersion_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyFirmwareVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP4Config provides a mock function with no fields -func (_m *MockDevice) GetPropertyIP4Config() (gonetworkmanager.IP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP4Config") - } - - var r0 gonetworkmanager.IP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyIP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP4Config' -type MockDevice_GetPropertyIP4Config_Call struct { - *mock.Call -} - -// GetPropertyIP4Config is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyIP4Config() *MockDevice_GetPropertyIP4Config_Call { - return &MockDevice_GetPropertyIP4Config_Call{Call: _e.mock.On("GetPropertyIP4Config")} -} - -func (_c *MockDevice_GetPropertyIP4Config_Call) Run(run func()) *MockDevice_GetPropertyIP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyIP4Config_Call) Return(_a0 gonetworkmanager.IP4Config, _a1 error) *MockDevice_GetPropertyIP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyIP4Config_Call) RunAndReturn(run func() (gonetworkmanager.IP4Config, error)) *MockDevice_GetPropertyIP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP6Config provides a mock function with no fields -func (_m *MockDevice) GetPropertyIP6Config() (gonetworkmanager.IP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP6Config") - } - - var r0 gonetworkmanager.IP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyIP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP6Config' -type MockDevice_GetPropertyIP6Config_Call struct { - *mock.Call -} - -// GetPropertyIP6Config is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyIP6Config() *MockDevice_GetPropertyIP6Config_Call { - return &MockDevice_GetPropertyIP6Config_Call{Call: _e.mock.On("GetPropertyIP6Config")} -} - -func (_c *MockDevice_GetPropertyIP6Config_Call) Run(run func()) *MockDevice_GetPropertyIP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyIP6Config_Call) Return(_a0 gonetworkmanager.IP6Config, _a1 error) *MockDevice_GetPropertyIP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyIP6Config_Call) RunAndReturn(run func() (gonetworkmanager.IP6Config, error)) *MockDevice_GetPropertyIP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyInterface provides a mock function with no fields -func (_m *MockDevice) GetPropertyInterface() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyInterface") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyInterface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyInterface' -type MockDevice_GetPropertyInterface_Call struct { - *mock.Call -} - -// GetPropertyInterface is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyInterface() *MockDevice_GetPropertyInterface_Call { - return &MockDevice_GetPropertyInterface_Call{Call: _e.mock.On("GetPropertyInterface")} -} - -func (_c *MockDevice_GetPropertyInterface_Call) Run(run func()) *MockDevice_GetPropertyInterface_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyInterface_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyInterface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyInterface_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyInterface_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIp4Connectivity provides a mock function with no fields -func (_m *MockDevice) GetPropertyIp4Connectivity() (gonetworkmanager.NmConnectivity, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIp4Connectivity") - } - - var r0 gonetworkmanager.NmConnectivity - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmConnectivity, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmConnectivity); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmConnectivity) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyIp4Connectivity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIp4Connectivity' -type MockDevice_GetPropertyIp4Connectivity_Call struct { - *mock.Call -} - -// GetPropertyIp4Connectivity is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyIp4Connectivity() *MockDevice_GetPropertyIp4Connectivity_Call { - return &MockDevice_GetPropertyIp4Connectivity_Call{Call: _e.mock.On("GetPropertyIp4Connectivity")} -} - -func (_c *MockDevice_GetPropertyIp4Connectivity_Call) Run(run func()) *MockDevice_GetPropertyIp4Connectivity_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyIp4Connectivity_Call) Return(_a0 gonetworkmanager.NmConnectivity, _a1 error) *MockDevice_GetPropertyIp4Connectivity_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyIp4Connectivity_Call) RunAndReturn(run func() (gonetworkmanager.NmConnectivity, error)) *MockDevice_GetPropertyIp4Connectivity_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIpInterface provides a mock function with no fields -func (_m *MockDevice) GetPropertyIpInterface() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIpInterface") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyIpInterface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIpInterface' -type MockDevice_GetPropertyIpInterface_Call struct { - *mock.Call -} - -// GetPropertyIpInterface is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyIpInterface() *MockDevice_GetPropertyIpInterface_Call { - return &MockDevice_GetPropertyIpInterface_Call{Call: _e.mock.On("GetPropertyIpInterface")} -} - -func (_c *MockDevice_GetPropertyIpInterface_Call) Run(run func()) *MockDevice_GetPropertyIpInterface_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyIpInterface_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyIpInterface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyIpInterface_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyIpInterface_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyManaged provides a mock function with no fields -func (_m *MockDevice) GetPropertyManaged() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyManaged") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyManaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyManaged' -type MockDevice_GetPropertyManaged_Call struct { - *mock.Call -} - -// GetPropertyManaged is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyManaged() *MockDevice_GetPropertyManaged_Call { - return &MockDevice_GetPropertyManaged_Call{Call: _e.mock.On("GetPropertyManaged")} -} - -func (_c *MockDevice_GetPropertyManaged_Call) Run(run func()) *MockDevice_GetPropertyManaged_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyManaged_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyManaged_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyManaged_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyManaged_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMtu provides a mock function with no fields -func (_m *MockDevice) GetPropertyMtu() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMtu") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyMtu_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMtu' -type MockDevice_GetPropertyMtu_Call struct { - *mock.Call -} - -// GetPropertyMtu is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyMtu() *MockDevice_GetPropertyMtu_Call { - return &MockDevice_GetPropertyMtu_Call{Call: _e.mock.On("GetPropertyMtu")} -} - -func (_c *MockDevice_GetPropertyMtu_Call) Run(run func()) *MockDevice_GetPropertyMtu_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyMtu_Call) Return(_a0 uint32, _a1 error) *MockDevice_GetPropertyMtu_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyMtu_Call) RunAndReturn(run func() (uint32, error)) *MockDevice_GetPropertyMtu_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNmPluginMissing provides a mock function with no fields -func (_m *MockDevice) GetPropertyNmPluginMissing() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNmPluginMissing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyNmPluginMissing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNmPluginMissing' -type MockDevice_GetPropertyNmPluginMissing_Call struct { - *mock.Call -} - -// GetPropertyNmPluginMissing is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyNmPluginMissing() *MockDevice_GetPropertyNmPluginMissing_Call { - return &MockDevice_GetPropertyNmPluginMissing_Call{Call: _e.mock.On("GetPropertyNmPluginMissing")} -} - -func (_c *MockDevice_GetPropertyNmPluginMissing_Call) Run(run func()) *MockDevice_GetPropertyNmPluginMissing_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyNmPluginMissing_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyNmPluginMissing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyNmPluginMissing_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyNmPluginMissing_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPhysicalPortId provides a mock function with no fields -func (_m *MockDevice) GetPropertyPhysicalPortId() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPhysicalPortId") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyPhysicalPortId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPhysicalPortId' -type MockDevice_GetPropertyPhysicalPortId_Call struct { - *mock.Call -} - -// GetPropertyPhysicalPortId is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyPhysicalPortId() *MockDevice_GetPropertyPhysicalPortId_Call { - return &MockDevice_GetPropertyPhysicalPortId_Call{Call: _e.mock.On("GetPropertyPhysicalPortId")} -} - -func (_c *MockDevice_GetPropertyPhysicalPortId_Call) Run(run func()) *MockDevice_GetPropertyPhysicalPortId_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyPhysicalPortId_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyPhysicalPortId_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyPhysicalPortId_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyPhysicalPortId_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyReal provides a mock function with no fields -func (_m *MockDevice) GetPropertyReal() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyReal") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyReal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyReal' -type MockDevice_GetPropertyReal_Call struct { - *mock.Call -} - -// GetPropertyReal is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyReal() *MockDevice_GetPropertyReal_Call { - return &MockDevice_GetPropertyReal_Call{Call: _e.mock.On("GetPropertyReal")} -} - -func (_c *MockDevice_GetPropertyReal_Call) Run(run func()) *MockDevice_GetPropertyReal_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyReal_Call) Return(_a0 bool, _a1 error) *MockDevice_GetPropertyReal_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyReal_Call) RunAndReturn(run func() (bool, error)) *MockDevice_GetPropertyReal_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyState provides a mock function with no fields -func (_m *MockDevice) GetPropertyState() (gonetworkmanager.NmDeviceState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyState") - } - - var r0 gonetworkmanager.NmDeviceState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmDeviceState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmDeviceState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmDeviceState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyState' -type MockDevice_GetPropertyState_Call struct { - *mock.Call -} - -// GetPropertyState is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyState() *MockDevice_GetPropertyState_Call { - return &MockDevice_GetPropertyState_Call{Call: _e.mock.On("GetPropertyState")} -} - -func (_c *MockDevice_GetPropertyState_Call) Run(run func()) *MockDevice_GetPropertyState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyState_Call) Return(_a0 gonetworkmanager.NmDeviceState, _a1 error) *MockDevice_GetPropertyState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyState_Call) RunAndReturn(run func() (gonetworkmanager.NmDeviceState, error)) *MockDevice_GetPropertyState_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyUdi provides a mock function with no fields -func (_m *MockDevice) GetPropertyUdi() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyUdi") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_GetPropertyUdi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyUdi' -type MockDevice_GetPropertyUdi_Call struct { - *mock.Call -} - -// GetPropertyUdi is a helper method to define mock.On call -func (_e *MockDevice_Expecter) GetPropertyUdi() *MockDevice_GetPropertyUdi_Call { - return &MockDevice_GetPropertyUdi_Call{Call: _e.mock.On("GetPropertyUdi")} -} - -func (_c *MockDevice_GetPropertyUdi_Call) Run(run func()) *MockDevice_GetPropertyUdi_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_GetPropertyUdi_Call) Return(_a0 string, _a1 error) *MockDevice_GetPropertyUdi_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_GetPropertyUdi_Call) RunAndReturn(run func() (string, error)) *MockDevice_GetPropertyUdi_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockDevice) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDevice_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockDevice_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockDevice_Expecter) MarshalJSON() *MockDevice_MarshalJSON_Call { - return &MockDevice_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockDevice_MarshalJSON_Call) Run(run func()) *MockDevice_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDevice_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockDevice_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDevice_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockDevice_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// Reapply provides a mock function with given fields: connection, versionId, flags -func (_m *MockDevice) Reapply(connection gonetworkmanager.Connection, versionId uint64, flags uint32) error { - ret := _m.Called(connection, versionId, flags) - - if len(ret) == 0 { - panic("no return value specified for Reapply") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, uint64, uint32) error); ok { - r0 = rf(connection, versionId, flags) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_Reapply_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reapply' -type MockDevice_Reapply_Call struct { - *mock.Call -} - -// Reapply is a helper method to define mock.On call -// - connection gonetworkmanager.Connection -// - versionId uint64 -// - flags uint32 -func (_e *MockDevice_Expecter) Reapply(connection interface{}, versionId interface{}, flags interface{}) *MockDevice_Reapply_Call { - return &MockDevice_Reapply_Call{Call: _e.mock.On("Reapply", connection, versionId, flags)} -} - -func (_c *MockDevice_Reapply_Call) Run(run func(connection gonetworkmanager.Connection, versionId uint64, flags uint32)) *MockDevice_Reapply_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Connection), args[1].(uint64), args[2].(uint32)) - }) - return _c -} - -func (_c *MockDevice_Reapply_Call) Return(_a0 error) *MockDevice_Reapply_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_Reapply_Call) RunAndReturn(run func(gonetworkmanager.Connection, uint64, uint32) error) *MockDevice_Reapply_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyAutoConnect provides a mock function with given fields: _a0 -func (_m *MockDevice) SetPropertyAutoConnect(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyAutoConnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_SetPropertyAutoConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyAutoConnect' -type MockDevice_SetPropertyAutoConnect_Call struct { - *mock.Call -} - -// SetPropertyAutoConnect is a helper method to define mock.On call -// - _a0 bool -func (_e *MockDevice_Expecter) SetPropertyAutoConnect(_a0 interface{}) *MockDevice_SetPropertyAutoConnect_Call { - return &MockDevice_SetPropertyAutoConnect_Call{Call: _e.mock.On("SetPropertyAutoConnect", _a0)} -} - -func (_c *MockDevice_SetPropertyAutoConnect_Call) Run(run func(_a0 bool)) *MockDevice_SetPropertyAutoConnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockDevice_SetPropertyAutoConnect_Call) Return(_a0 error) *MockDevice_SetPropertyAutoConnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_SetPropertyAutoConnect_Call) RunAndReturn(run func(bool) error) *MockDevice_SetPropertyAutoConnect_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyManaged provides a mock function with given fields: _a0 -func (_m *MockDevice) SetPropertyManaged(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyManaged") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_SetPropertyManaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyManaged' -type MockDevice_SetPropertyManaged_Call struct { - *mock.Call -} - -// SetPropertyManaged is a helper method to define mock.On call -// - _a0 bool -func (_e *MockDevice_Expecter) SetPropertyManaged(_a0 interface{}) *MockDevice_SetPropertyManaged_Call { - return &MockDevice_SetPropertyManaged_Call{Call: _e.mock.On("SetPropertyManaged", _a0)} -} - -func (_c *MockDevice_SetPropertyManaged_Call) Run(run func(_a0 bool)) *MockDevice_SetPropertyManaged_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockDevice_SetPropertyManaged_Call) Return(_a0 error) *MockDevice_SetPropertyManaged_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDevice_SetPropertyManaged_Call) RunAndReturn(run func(bool) error) *MockDevice_SetPropertyManaged_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeState provides a mock function with given fields: receiver, exit -func (_m *MockDevice) SubscribeState(receiver chan gonetworkmanager.DeviceStateChange, exit chan struct{}) error { - ret := _m.Called(receiver, exit) - - if len(ret) == 0 { - panic("no return value specified for SubscribeState") - } - - var r0 error - if rf, ok := ret.Get(0).(func(chan gonetworkmanager.DeviceStateChange, chan struct{}) error); ok { - r0 = rf(receiver, exit) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDevice_SubscribeState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeState' -type MockDevice_SubscribeState_Call struct { - *mock.Call -} - -// SubscribeState is a helper method to define mock.On call -// - receiver chan gonetworkmanager.DeviceStateChange -// - exit chan struct{} -func (_e *MockDevice_Expecter) SubscribeState(receiver interface{}, exit interface{}) *MockDevice_SubscribeState_Call { - return &MockDevice_SubscribeState_Call{Call: _e.mock.On("SubscribeState", receiver, exit)} -} - -func (_c *MockDevice_SubscribeState_Call) Run(run func(receiver chan gonetworkmanager.DeviceStateChange, exit chan struct{})) *MockDevice_SubscribeState_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(chan gonetworkmanager.DeviceStateChange), args[1].(chan struct{})) - }) - return _c -} - -func (_c *MockDevice_SubscribeState_Call) Return(err error) *MockDevice_SubscribeState_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockDevice_SubscribeState_Call) RunAndReturn(run func(chan gonetworkmanager.DeviceStateChange, chan struct{}) error) *MockDevice_SubscribeState_Call { - _c.Call.Return(run) - return _c -} - -// NewMockDevice creates a new instance of MockDevice. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockDevice(t interface { - mock.TestingT - Cleanup(func()) -}) *MockDevice { - mock := &MockDevice{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_DeviceWireless.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_DeviceWireless.go deleted file mode 100644 index d2d3f2d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_DeviceWireless.go +++ /dev/null @@ -1,2241 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockDeviceWireless is an autogenerated mock type for the DeviceWireless type -type MockDeviceWireless struct { - mock.Mock -} - -type MockDeviceWireless_Expecter struct { - mock *mock.Mock -} - -func (_m *MockDeviceWireless) EXPECT() *MockDeviceWireless_Expecter { - return &MockDeviceWireless_Expecter{mock: &_m.Mock} -} - -// Delete provides a mock function with no fields -func (_m *MockDeviceWireless) Delete() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Delete") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type MockDeviceWireless_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) Delete() *MockDeviceWireless_Delete_Call { - return &MockDeviceWireless_Delete_Call{Call: _e.mock.On("Delete")} -} - -func (_c *MockDeviceWireless_Delete_Call) Run(run func()) *MockDeviceWireless_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_Delete_Call) Return(_a0 error) *MockDeviceWireless_Delete_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_Delete_Call) RunAndReturn(run func() error) *MockDeviceWireless_Delete_Call { - _c.Call.Return(run) - return _c -} - -// Disconnect provides a mock function with no fields -func (_m *MockDeviceWireless) Disconnect() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Disconnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_Disconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Disconnect' -type MockDeviceWireless_Disconnect_Call struct { - *mock.Call -} - -// Disconnect is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) Disconnect() *MockDeviceWireless_Disconnect_Call { - return &MockDeviceWireless_Disconnect_Call{Call: _e.mock.On("Disconnect")} -} - -func (_c *MockDeviceWireless_Disconnect_Call) Run(run func()) *MockDeviceWireless_Disconnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_Disconnect_Call) Return(_a0 error) *MockDeviceWireless_Disconnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_Disconnect_Call) RunAndReturn(run func() error) *MockDeviceWireless_Disconnect_Call { - _c.Call.Return(run) - return _c -} - -// GetAccessPoints provides a mock function with no fields -func (_m *MockDeviceWireless) GetAccessPoints() ([]gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetAccessPoints") - } - - var r0 []gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetAccessPoints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAccessPoints' -type MockDeviceWireless_GetAccessPoints_Call struct { - *mock.Call -} - -// GetAccessPoints is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetAccessPoints() *MockDeviceWireless_GetAccessPoints_Call { - return &MockDeviceWireless_GetAccessPoints_Call{Call: _e.mock.On("GetAccessPoints")} -} - -func (_c *MockDeviceWireless_GetAccessPoints_Call) Run(run func()) *MockDeviceWireless_GetAccessPoints_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetAccessPoints_Call) Return(_a0 []gonetworkmanager.AccessPoint, _a1 error) *MockDeviceWireless_GetAccessPoints_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetAccessPoints_Call) RunAndReturn(run func() ([]gonetworkmanager.AccessPoint, error)) *MockDeviceWireless_GetAccessPoints_Call { - _c.Call.Return(run) - return _c -} - -// GetAllAccessPoints provides a mock function with no fields -func (_m *MockDeviceWireless) GetAllAccessPoints() ([]gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetAllAccessPoints") - } - - var r0 []gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetAllAccessPoints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAllAccessPoints' -type MockDeviceWireless_GetAllAccessPoints_Call struct { - *mock.Call -} - -// GetAllAccessPoints is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetAllAccessPoints() *MockDeviceWireless_GetAllAccessPoints_Call { - return &MockDeviceWireless_GetAllAccessPoints_Call{Call: _e.mock.On("GetAllAccessPoints")} -} - -func (_c *MockDeviceWireless_GetAllAccessPoints_Call) Run(run func()) *MockDeviceWireless_GetAllAccessPoints_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetAllAccessPoints_Call) Return(_a0 []gonetworkmanager.AccessPoint, _a1 error) *MockDeviceWireless_GetAllAccessPoints_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetAllAccessPoints_Call) RunAndReturn(run func() ([]gonetworkmanager.AccessPoint, error)) *MockDeviceWireless_GetAllAccessPoints_Call { - _c.Call.Return(run) - return _c -} - -// GetPath provides a mock function with no fields -func (_m *MockDeviceWireless) GetPath() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPath") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockDeviceWireless_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath' -type MockDeviceWireless_GetPath_Call struct { - *mock.Call -} - -// GetPath is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPath() *MockDeviceWireless_GetPath_Call { - return &MockDeviceWireless_GetPath_Call{Call: _e.mock.On("GetPath")} -} - -func (_c *MockDeviceWireless_GetPath_Call) Run(run func()) *MockDeviceWireless_GetPath_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPath_Call) Return(_a0 dbus.ObjectPath) *MockDeviceWireless_GetPath_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_GetPath_Call) RunAndReturn(run func() dbus.ObjectPath) *MockDeviceWireless_GetPath_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAccessPoints provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyAccessPoints() ([]gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAccessPoints") - } - - var r0 []gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyAccessPoints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAccessPoints' -type MockDeviceWireless_GetPropertyAccessPoints_Call struct { - *mock.Call -} - -// GetPropertyAccessPoints is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyAccessPoints() *MockDeviceWireless_GetPropertyAccessPoints_Call { - return &MockDeviceWireless_GetPropertyAccessPoints_Call{Call: _e.mock.On("GetPropertyAccessPoints")} -} - -func (_c *MockDeviceWireless_GetPropertyAccessPoints_Call) Run(run func()) *MockDeviceWireless_GetPropertyAccessPoints_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAccessPoints_Call) Return(_a0 []gonetworkmanager.AccessPoint, _a1 error) *MockDeviceWireless_GetPropertyAccessPoints_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAccessPoints_Call) RunAndReturn(run func() ([]gonetworkmanager.AccessPoint, error)) *MockDeviceWireless_GetPropertyAccessPoints_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActiveAccessPoint provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyActiveAccessPoint() (gonetworkmanager.AccessPoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActiveAccessPoint") - } - - var r0 gonetworkmanager.AccessPoint - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.AccessPoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.AccessPoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.AccessPoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyActiveAccessPoint_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActiveAccessPoint' -type MockDeviceWireless_GetPropertyActiveAccessPoint_Call struct { - *mock.Call -} - -// GetPropertyActiveAccessPoint is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyActiveAccessPoint() *MockDeviceWireless_GetPropertyActiveAccessPoint_Call { - return &MockDeviceWireless_GetPropertyActiveAccessPoint_Call{Call: _e.mock.On("GetPropertyActiveAccessPoint")} -} - -func (_c *MockDeviceWireless_GetPropertyActiveAccessPoint_Call) Run(run func()) *MockDeviceWireless_GetPropertyActiveAccessPoint_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyActiveAccessPoint_Call) Return(_a0 gonetworkmanager.AccessPoint, _a1 error) *MockDeviceWireless_GetPropertyActiveAccessPoint_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyActiveAccessPoint_Call) RunAndReturn(run func() (gonetworkmanager.AccessPoint, error)) *MockDeviceWireless_GetPropertyActiveAccessPoint_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActiveConnection provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyActiveConnection() (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActiveConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyActiveConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActiveConnection' -type MockDeviceWireless_GetPropertyActiveConnection_Call struct { - *mock.Call -} - -// GetPropertyActiveConnection is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyActiveConnection() *MockDeviceWireless_GetPropertyActiveConnection_Call { - return &MockDeviceWireless_GetPropertyActiveConnection_Call{Call: _e.mock.On("GetPropertyActiveConnection")} -} - -func (_c *MockDeviceWireless_GetPropertyActiveConnection_Call) Run(run func()) *MockDeviceWireless_GetPropertyActiveConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyActiveConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockDeviceWireless_GetPropertyActiveConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyActiveConnection_Call) RunAndReturn(run func() (gonetworkmanager.ActiveConnection, error)) *MockDeviceWireless_GetPropertyActiveConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAutoConnect provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyAutoConnect() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAutoConnect") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyAutoConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAutoConnect' -type MockDeviceWireless_GetPropertyAutoConnect_Call struct { - *mock.Call -} - -// GetPropertyAutoConnect is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyAutoConnect() *MockDeviceWireless_GetPropertyAutoConnect_Call { - return &MockDeviceWireless_GetPropertyAutoConnect_Call{Call: _e.mock.On("GetPropertyAutoConnect")} -} - -func (_c *MockDeviceWireless_GetPropertyAutoConnect_Call) Run(run func()) *MockDeviceWireless_GetPropertyAutoConnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAutoConnect_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyAutoConnect_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAutoConnect_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyAutoConnect_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAvailableConnections provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyAvailableConnections() ([]gonetworkmanager.Connection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAvailableConnections") - } - - var r0 []gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Connection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Connection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyAvailableConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAvailableConnections' -type MockDeviceWireless_GetPropertyAvailableConnections_Call struct { - *mock.Call -} - -// GetPropertyAvailableConnections is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyAvailableConnections() *MockDeviceWireless_GetPropertyAvailableConnections_Call { - return &MockDeviceWireless_GetPropertyAvailableConnections_Call{Call: _e.mock.On("GetPropertyAvailableConnections")} -} - -func (_c *MockDeviceWireless_GetPropertyAvailableConnections_Call) Run(run func()) *MockDeviceWireless_GetPropertyAvailableConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAvailableConnections_Call) Return(_a0 []gonetworkmanager.Connection, _a1 error) *MockDeviceWireless_GetPropertyAvailableConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyAvailableConnections_Call) RunAndReturn(run func() ([]gonetworkmanager.Connection, error)) *MockDeviceWireless_GetPropertyAvailableConnections_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyBitrate provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyBitrate() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyBitrate") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyBitrate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyBitrate' -type MockDeviceWireless_GetPropertyBitrate_Call struct { - *mock.Call -} - -// GetPropertyBitrate is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyBitrate() *MockDeviceWireless_GetPropertyBitrate_Call { - return &MockDeviceWireless_GetPropertyBitrate_Call{Call: _e.mock.On("GetPropertyBitrate")} -} - -func (_c *MockDeviceWireless_GetPropertyBitrate_Call) Run(run func()) *MockDeviceWireless_GetPropertyBitrate_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyBitrate_Call) Return(_a0 uint32, _a1 error) *MockDeviceWireless_GetPropertyBitrate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyBitrate_Call) RunAndReturn(run func() (uint32, error)) *MockDeviceWireless_GetPropertyBitrate_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP4Config provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDHCP4Config() (gonetworkmanager.DHCP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP4Config") - } - - var r0 gonetworkmanager.DHCP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDHCP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP4Config' -type MockDeviceWireless_GetPropertyDHCP4Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP4Config is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDHCP4Config() *MockDeviceWireless_GetPropertyDHCP4Config_Call { - return &MockDeviceWireless_GetPropertyDHCP4Config_Call{Call: _e.mock.On("GetPropertyDHCP4Config")} -} - -func (_c *MockDeviceWireless_GetPropertyDHCP4Config_Call) Run(run func()) *MockDeviceWireless_GetPropertyDHCP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDHCP4Config_Call) Return(_a0 gonetworkmanager.DHCP4Config, _a1 error) *MockDeviceWireless_GetPropertyDHCP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDHCP4Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP4Config, error)) *MockDeviceWireless_GetPropertyDHCP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDHCP6Config provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDHCP6Config() (gonetworkmanager.DHCP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDHCP6Config") - } - - var r0 gonetworkmanager.DHCP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.DHCP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.DHCP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.DHCP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDHCP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDHCP6Config' -type MockDeviceWireless_GetPropertyDHCP6Config_Call struct { - *mock.Call -} - -// GetPropertyDHCP6Config is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDHCP6Config() *MockDeviceWireless_GetPropertyDHCP6Config_Call { - return &MockDeviceWireless_GetPropertyDHCP6Config_Call{Call: _e.mock.On("GetPropertyDHCP6Config")} -} - -func (_c *MockDeviceWireless_GetPropertyDHCP6Config_Call) Run(run func()) *MockDeviceWireless_GetPropertyDHCP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDHCP6Config_Call) Return(_a0 gonetworkmanager.DHCP6Config, _a1 error) *MockDeviceWireless_GetPropertyDHCP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDHCP6Config_Call) RunAndReturn(run func() (gonetworkmanager.DHCP6Config, error)) *MockDeviceWireless_GetPropertyDHCP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDeviceType provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDeviceType() (gonetworkmanager.NmDeviceType, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDeviceType") - } - - var r0 gonetworkmanager.NmDeviceType - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmDeviceType, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmDeviceType); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmDeviceType) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDeviceType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDeviceType' -type MockDeviceWireless_GetPropertyDeviceType_Call struct { - *mock.Call -} - -// GetPropertyDeviceType is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDeviceType() *MockDeviceWireless_GetPropertyDeviceType_Call { - return &MockDeviceWireless_GetPropertyDeviceType_Call{Call: _e.mock.On("GetPropertyDeviceType")} -} - -func (_c *MockDeviceWireless_GetPropertyDeviceType_Call) Run(run func()) *MockDeviceWireless_GetPropertyDeviceType_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDeviceType_Call) Return(_a0 gonetworkmanager.NmDeviceType, _a1 error) *MockDeviceWireless_GetPropertyDeviceType_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDeviceType_Call) RunAndReturn(run func() (gonetworkmanager.NmDeviceType, error)) *MockDeviceWireless_GetPropertyDeviceType_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDriver provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDriver() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDriver") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDriver_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDriver' -type MockDeviceWireless_GetPropertyDriver_Call struct { - *mock.Call -} - -// GetPropertyDriver is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDriver() *MockDeviceWireless_GetPropertyDriver_Call { - return &MockDeviceWireless_GetPropertyDriver_Call{Call: _e.mock.On("GetPropertyDriver")} -} - -func (_c *MockDeviceWireless_GetPropertyDriver_Call) Run(run func()) *MockDeviceWireless_GetPropertyDriver_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDriver_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyDriver_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDriver_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyDriver_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDriverVersion provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyDriverVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDriverVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyDriverVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDriverVersion' -type MockDeviceWireless_GetPropertyDriverVersion_Call struct { - *mock.Call -} - -// GetPropertyDriverVersion is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyDriverVersion() *MockDeviceWireless_GetPropertyDriverVersion_Call { - return &MockDeviceWireless_GetPropertyDriverVersion_Call{Call: _e.mock.On("GetPropertyDriverVersion")} -} - -func (_c *MockDeviceWireless_GetPropertyDriverVersion_Call) Run(run func()) *MockDeviceWireless_GetPropertyDriverVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDriverVersion_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyDriverVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyDriverVersion_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyDriverVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFirmwareMissing provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyFirmwareMissing() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFirmwareMissing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyFirmwareMissing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFirmwareMissing' -type MockDeviceWireless_GetPropertyFirmwareMissing_Call struct { - *mock.Call -} - -// GetPropertyFirmwareMissing is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyFirmwareMissing() *MockDeviceWireless_GetPropertyFirmwareMissing_Call { - return &MockDeviceWireless_GetPropertyFirmwareMissing_Call{Call: _e.mock.On("GetPropertyFirmwareMissing")} -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareMissing_Call) Run(run func()) *MockDeviceWireless_GetPropertyFirmwareMissing_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareMissing_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyFirmwareMissing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareMissing_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyFirmwareMissing_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyFirmwareVersion provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyFirmwareVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyFirmwareVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyFirmwareVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyFirmwareVersion' -type MockDeviceWireless_GetPropertyFirmwareVersion_Call struct { - *mock.Call -} - -// GetPropertyFirmwareVersion is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyFirmwareVersion() *MockDeviceWireless_GetPropertyFirmwareVersion_Call { - return &MockDeviceWireless_GetPropertyFirmwareVersion_Call{Call: _e.mock.On("GetPropertyFirmwareVersion")} -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareVersion_Call) Run(run func()) *MockDeviceWireless_GetPropertyFirmwareVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareVersion_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyFirmwareVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyFirmwareVersion_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyFirmwareVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyHwAddress provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyHwAddress() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyHwAddress") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyHwAddress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyHwAddress' -type MockDeviceWireless_GetPropertyHwAddress_Call struct { - *mock.Call -} - -// GetPropertyHwAddress is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyHwAddress() *MockDeviceWireless_GetPropertyHwAddress_Call { - return &MockDeviceWireless_GetPropertyHwAddress_Call{Call: _e.mock.On("GetPropertyHwAddress")} -} - -func (_c *MockDeviceWireless_GetPropertyHwAddress_Call) Run(run func()) *MockDeviceWireless_GetPropertyHwAddress_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyHwAddress_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyHwAddress_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyHwAddress_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyHwAddress_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP4Config provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyIP4Config() (gonetworkmanager.IP4Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP4Config") - } - - var r0 gonetworkmanager.IP4Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP4Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP4Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP4Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyIP4Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP4Config' -type MockDeviceWireless_GetPropertyIP4Config_Call struct { - *mock.Call -} - -// GetPropertyIP4Config is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyIP4Config() *MockDeviceWireless_GetPropertyIP4Config_Call { - return &MockDeviceWireless_GetPropertyIP4Config_Call{Call: _e.mock.On("GetPropertyIP4Config")} -} - -func (_c *MockDeviceWireless_GetPropertyIP4Config_Call) Run(run func()) *MockDeviceWireless_GetPropertyIP4Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIP4Config_Call) Return(_a0 gonetworkmanager.IP4Config, _a1 error) *MockDeviceWireless_GetPropertyIP4Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIP4Config_Call) RunAndReturn(run func() (gonetworkmanager.IP4Config, error)) *MockDeviceWireless_GetPropertyIP4Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIP6Config provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyIP6Config() (gonetworkmanager.IP6Config, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIP6Config") - } - - var r0 gonetworkmanager.IP6Config - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.IP6Config, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.IP6Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.IP6Config) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyIP6Config_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIP6Config' -type MockDeviceWireless_GetPropertyIP6Config_Call struct { - *mock.Call -} - -// GetPropertyIP6Config is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyIP6Config() *MockDeviceWireless_GetPropertyIP6Config_Call { - return &MockDeviceWireless_GetPropertyIP6Config_Call{Call: _e.mock.On("GetPropertyIP6Config")} -} - -func (_c *MockDeviceWireless_GetPropertyIP6Config_Call) Run(run func()) *MockDeviceWireless_GetPropertyIP6Config_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIP6Config_Call) Return(_a0 gonetworkmanager.IP6Config, _a1 error) *MockDeviceWireless_GetPropertyIP6Config_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIP6Config_Call) RunAndReturn(run func() (gonetworkmanager.IP6Config, error)) *MockDeviceWireless_GetPropertyIP6Config_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyInterface provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyInterface() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyInterface") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyInterface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyInterface' -type MockDeviceWireless_GetPropertyInterface_Call struct { - *mock.Call -} - -// GetPropertyInterface is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyInterface() *MockDeviceWireless_GetPropertyInterface_Call { - return &MockDeviceWireless_GetPropertyInterface_Call{Call: _e.mock.On("GetPropertyInterface")} -} - -func (_c *MockDeviceWireless_GetPropertyInterface_Call) Run(run func()) *MockDeviceWireless_GetPropertyInterface_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyInterface_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyInterface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyInterface_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyInterface_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIp4Connectivity provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyIp4Connectivity() (gonetworkmanager.NmConnectivity, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIp4Connectivity") - } - - var r0 gonetworkmanager.NmConnectivity - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmConnectivity, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmConnectivity); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmConnectivity) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyIp4Connectivity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIp4Connectivity' -type MockDeviceWireless_GetPropertyIp4Connectivity_Call struct { - *mock.Call -} - -// GetPropertyIp4Connectivity is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyIp4Connectivity() *MockDeviceWireless_GetPropertyIp4Connectivity_Call { - return &MockDeviceWireless_GetPropertyIp4Connectivity_Call{Call: _e.mock.On("GetPropertyIp4Connectivity")} -} - -func (_c *MockDeviceWireless_GetPropertyIp4Connectivity_Call) Run(run func()) *MockDeviceWireless_GetPropertyIp4Connectivity_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIp4Connectivity_Call) Return(_a0 gonetworkmanager.NmConnectivity, _a1 error) *MockDeviceWireless_GetPropertyIp4Connectivity_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIp4Connectivity_Call) RunAndReturn(run func() (gonetworkmanager.NmConnectivity, error)) *MockDeviceWireless_GetPropertyIp4Connectivity_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyIpInterface provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyIpInterface() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyIpInterface") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyIpInterface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyIpInterface' -type MockDeviceWireless_GetPropertyIpInterface_Call struct { - *mock.Call -} - -// GetPropertyIpInterface is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyIpInterface() *MockDeviceWireless_GetPropertyIpInterface_Call { - return &MockDeviceWireless_GetPropertyIpInterface_Call{Call: _e.mock.On("GetPropertyIpInterface")} -} - -func (_c *MockDeviceWireless_GetPropertyIpInterface_Call) Run(run func()) *MockDeviceWireless_GetPropertyIpInterface_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIpInterface_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyIpInterface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyIpInterface_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyIpInterface_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyLastScan provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyLastScan() (int64, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyLastScan") - } - - var r0 int64 - var r1 error - if rf, ok := ret.Get(0).(func() (int64, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() int64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int64) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyLastScan_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyLastScan' -type MockDeviceWireless_GetPropertyLastScan_Call struct { - *mock.Call -} - -// GetPropertyLastScan is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyLastScan() *MockDeviceWireless_GetPropertyLastScan_Call { - return &MockDeviceWireless_GetPropertyLastScan_Call{Call: _e.mock.On("GetPropertyLastScan")} -} - -func (_c *MockDeviceWireless_GetPropertyLastScan_Call) Run(run func()) *MockDeviceWireless_GetPropertyLastScan_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyLastScan_Call) Return(_a0 int64, _a1 error) *MockDeviceWireless_GetPropertyLastScan_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyLastScan_Call) RunAndReturn(run func() (int64, error)) *MockDeviceWireless_GetPropertyLastScan_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyManaged provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyManaged() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyManaged") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyManaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyManaged' -type MockDeviceWireless_GetPropertyManaged_Call struct { - *mock.Call -} - -// GetPropertyManaged is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyManaged() *MockDeviceWireless_GetPropertyManaged_Call { - return &MockDeviceWireless_GetPropertyManaged_Call{Call: _e.mock.On("GetPropertyManaged")} -} - -func (_c *MockDeviceWireless_GetPropertyManaged_Call) Run(run func()) *MockDeviceWireless_GetPropertyManaged_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyManaged_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyManaged_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyManaged_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyManaged_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMode provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyMode() (gonetworkmanager.Nm80211Mode, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMode") - } - - var r0 gonetworkmanager.Nm80211Mode - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.Nm80211Mode, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.Nm80211Mode); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.Nm80211Mode) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyMode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMode' -type MockDeviceWireless_GetPropertyMode_Call struct { - *mock.Call -} - -// GetPropertyMode is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyMode() *MockDeviceWireless_GetPropertyMode_Call { - return &MockDeviceWireless_GetPropertyMode_Call{Call: _e.mock.On("GetPropertyMode")} -} - -func (_c *MockDeviceWireless_GetPropertyMode_Call) Run(run func()) *MockDeviceWireless_GetPropertyMode_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyMode_Call) Return(_a0 gonetworkmanager.Nm80211Mode, _a1 error) *MockDeviceWireless_GetPropertyMode_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyMode_Call) RunAndReturn(run func() (gonetworkmanager.Nm80211Mode, error)) *MockDeviceWireless_GetPropertyMode_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMtu provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyMtu() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMtu") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyMtu_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMtu' -type MockDeviceWireless_GetPropertyMtu_Call struct { - *mock.Call -} - -// GetPropertyMtu is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyMtu() *MockDeviceWireless_GetPropertyMtu_Call { - return &MockDeviceWireless_GetPropertyMtu_Call{Call: _e.mock.On("GetPropertyMtu")} -} - -func (_c *MockDeviceWireless_GetPropertyMtu_Call) Run(run func()) *MockDeviceWireless_GetPropertyMtu_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyMtu_Call) Return(_a0 uint32, _a1 error) *MockDeviceWireless_GetPropertyMtu_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyMtu_Call) RunAndReturn(run func() (uint32, error)) *MockDeviceWireless_GetPropertyMtu_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNmPluginMissing provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyNmPluginMissing() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNmPluginMissing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyNmPluginMissing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNmPluginMissing' -type MockDeviceWireless_GetPropertyNmPluginMissing_Call struct { - *mock.Call -} - -// GetPropertyNmPluginMissing is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyNmPluginMissing() *MockDeviceWireless_GetPropertyNmPluginMissing_Call { - return &MockDeviceWireless_GetPropertyNmPluginMissing_Call{Call: _e.mock.On("GetPropertyNmPluginMissing")} -} - -func (_c *MockDeviceWireless_GetPropertyNmPluginMissing_Call) Run(run func()) *MockDeviceWireless_GetPropertyNmPluginMissing_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyNmPluginMissing_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyNmPluginMissing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyNmPluginMissing_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyNmPluginMissing_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPermHwAddress provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyPermHwAddress() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPermHwAddress") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyPermHwAddress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPermHwAddress' -type MockDeviceWireless_GetPropertyPermHwAddress_Call struct { - *mock.Call -} - -// GetPropertyPermHwAddress is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyPermHwAddress() *MockDeviceWireless_GetPropertyPermHwAddress_Call { - return &MockDeviceWireless_GetPropertyPermHwAddress_Call{Call: _e.mock.On("GetPropertyPermHwAddress")} -} - -func (_c *MockDeviceWireless_GetPropertyPermHwAddress_Call) Run(run func()) *MockDeviceWireless_GetPropertyPermHwAddress_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyPermHwAddress_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyPermHwAddress_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyPermHwAddress_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyPermHwAddress_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPhysicalPortId provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyPhysicalPortId() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPhysicalPortId") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyPhysicalPortId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPhysicalPortId' -type MockDeviceWireless_GetPropertyPhysicalPortId_Call struct { - *mock.Call -} - -// GetPropertyPhysicalPortId is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyPhysicalPortId() *MockDeviceWireless_GetPropertyPhysicalPortId_Call { - return &MockDeviceWireless_GetPropertyPhysicalPortId_Call{Call: _e.mock.On("GetPropertyPhysicalPortId")} -} - -func (_c *MockDeviceWireless_GetPropertyPhysicalPortId_Call) Run(run func()) *MockDeviceWireless_GetPropertyPhysicalPortId_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyPhysicalPortId_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyPhysicalPortId_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyPhysicalPortId_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyPhysicalPortId_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyReal provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyReal() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyReal") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyReal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyReal' -type MockDeviceWireless_GetPropertyReal_Call struct { - *mock.Call -} - -// GetPropertyReal is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyReal() *MockDeviceWireless_GetPropertyReal_Call { - return &MockDeviceWireless_GetPropertyReal_Call{Call: _e.mock.On("GetPropertyReal")} -} - -func (_c *MockDeviceWireless_GetPropertyReal_Call) Run(run func()) *MockDeviceWireless_GetPropertyReal_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyReal_Call) Return(_a0 bool, _a1 error) *MockDeviceWireless_GetPropertyReal_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyReal_Call) RunAndReturn(run func() (bool, error)) *MockDeviceWireless_GetPropertyReal_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyState provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyState() (gonetworkmanager.NmDeviceState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyState") - } - - var r0 gonetworkmanager.NmDeviceState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmDeviceState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmDeviceState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmDeviceState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyState' -type MockDeviceWireless_GetPropertyState_Call struct { - *mock.Call -} - -// GetPropertyState is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyState() *MockDeviceWireless_GetPropertyState_Call { - return &MockDeviceWireless_GetPropertyState_Call{Call: _e.mock.On("GetPropertyState")} -} - -func (_c *MockDeviceWireless_GetPropertyState_Call) Run(run func()) *MockDeviceWireless_GetPropertyState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyState_Call) Return(_a0 gonetworkmanager.NmDeviceState, _a1 error) *MockDeviceWireless_GetPropertyState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyState_Call) RunAndReturn(run func() (gonetworkmanager.NmDeviceState, error)) *MockDeviceWireless_GetPropertyState_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyUdi provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyUdi() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyUdi") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyUdi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyUdi' -type MockDeviceWireless_GetPropertyUdi_Call struct { - *mock.Call -} - -// GetPropertyUdi is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyUdi() *MockDeviceWireless_GetPropertyUdi_Call { - return &MockDeviceWireless_GetPropertyUdi_Call{Call: _e.mock.On("GetPropertyUdi")} -} - -func (_c *MockDeviceWireless_GetPropertyUdi_Call) Run(run func()) *MockDeviceWireless_GetPropertyUdi_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyUdi_Call) Return(_a0 string, _a1 error) *MockDeviceWireless_GetPropertyUdi_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyUdi_Call) RunAndReturn(run func() (string, error)) *MockDeviceWireless_GetPropertyUdi_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWirelessCapabilities provides a mock function with no fields -func (_m *MockDeviceWireless) GetPropertyWirelessCapabilities() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWirelessCapabilities") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_GetPropertyWirelessCapabilities_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWirelessCapabilities' -type MockDeviceWireless_GetPropertyWirelessCapabilities_Call struct { - *mock.Call -} - -// GetPropertyWirelessCapabilities is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) GetPropertyWirelessCapabilities() *MockDeviceWireless_GetPropertyWirelessCapabilities_Call { - return &MockDeviceWireless_GetPropertyWirelessCapabilities_Call{Call: _e.mock.On("GetPropertyWirelessCapabilities")} -} - -func (_c *MockDeviceWireless_GetPropertyWirelessCapabilities_Call) Run(run func()) *MockDeviceWireless_GetPropertyWirelessCapabilities_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyWirelessCapabilities_Call) Return(_a0 uint32, _a1 error) *MockDeviceWireless_GetPropertyWirelessCapabilities_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_GetPropertyWirelessCapabilities_Call) RunAndReturn(run func() (uint32, error)) *MockDeviceWireless_GetPropertyWirelessCapabilities_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockDeviceWireless) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockDeviceWireless_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockDeviceWireless_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) MarshalJSON() *MockDeviceWireless_MarshalJSON_Call { - return &MockDeviceWireless_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockDeviceWireless_MarshalJSON_Call) Run(run func()) *MockDeviceWireless_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockDeviceWireless_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockDeviceWireless_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockDeviceWireless_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// Reapply provides a mock function with given fields: connection, versionId, flags -func (_m *MockDeviceWireless) Reapply(connection gonetworkmanager.Connection, versionId uint64, flags uint32) error { - ret := _m.Called(connection, versionId, flags) - - if len(ret) == 0 { - panic("no return value specified for Reapply") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, uint64, uint32) error); ok { - r0 = rf(connection, versionId, flags) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_Reapply_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reapply' -type MockDeviceWireless_Reapply_Call struct { - *mock.Call -} - -// Reapply is a helper method to define mock.On call -// - connection gonetworkmanager.Connection -// - versionId uint64 -// - flags uint32 -func (_e *MockDeviceWireless_Expecter) Reapply(connection interface{}, versionId interface{}, flags interface{}) *MockDeviceWireless_Reapply_Call { - return &MockDeviceWireless_Reapply_Call{Call: _e.mock.On("Reapply", connection, versionId, flags)} -} - -func (_c *MockDeviceWireless_Reapply_Call) Run(run func(connection gonetworkmanager.Connection, versionId uint64, flags uint32)) *MockDeviceWireless_Reapply_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Connection), args[1].(uint64), args[2].(uint32)) - }) - return _c -} - -func (_c *MockDeviceWireless_Reapply_Call) Return(_a0 error) *MockDeviceWireless_Reapply_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_Reapply_Call) RunAndReturn(run func(gonetworkmanager.Connection, uint64, uint32) error) *MockDeviceWireless_Reapply_Call { - _c.Call.Return(run) - return _c -} - -// RequestScan provides a mock function with no fields -func (_m *MockDeviceWireless) RequestScan() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for RequestScan") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_RequestScan_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RequestScan' -type MockDeviceWireless_RequestScan_Call struct { - *mock.Call -} - -// RequestScan is a helper method to define mock.On call -func (_e *MockDeviceWireless_Expecter) RequestScan() *MockDeviceWireless_RequestScan_Call { - return &MockDeviceWireless_RequestScan_Call{Call: _e.mock.On("RequestScan")} -} - -func (_c *MockDeviceWireless_RequestScan_Call) Run(run func()) *MockDeviceWireless_RequestScan_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockDeviceWireless_RequestScan_Call) Return(_a0 error) *MockDeviceWireless_RequestScan_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_RequestScan_Call) RunAndReturn(run func() error) *MockDeviceWireless_RequestScan_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyAutoConnect provides a mock function with given fields: _a0 -func (_m *MockDeviceWireless) SetPropertyAutoConnect(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyAutoConnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_SetPropertyAutoConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyAutoConnect' -type MockDeviceWireless_SetPropertyAutoConnect_Call struct { - *mock.Call -} - -// SetPropertyAutoConnect is a helper method to define mock.On call -// - _a0 bool -func (_e *MockDeviceWireless_Expecter) SetPropertyAutoConnect(_a0 interface{}) *MockDeviceWireless_SetPropertyAutoConnect_Call { - return &MockDeviceWireless_SetPropertyAutoConnect_Call{Call: _e.mock.On("SetPropertyAutoConnect", _a0)} -} - -func (_c *MockDeviceWireless_SetPropertyAutoConnect_Call) Run(run func(_a0 bool)) *MockDeviceWireless_SetPropertyAutoConnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockDeviceWireless_SetPropertyAutoConnect_Call) Return(_a0 error) *MockDeviceWireless_SetPropertyAutoConnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_SetPropertyAutoConnect_Call) RunAndReturn(run func(bool) error) *MockDeviceWireless_SetPropertyAutoConnect_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyManaged provides a mock function with given fields: _a0 -func (_m *MockDeviceWireless) SetPropertyManaged(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyManaged") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_SetPropertyManaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyManaged' -type MockDeviceWireless_SetPropertyManaged_Call struct { - *mock.Call -} - -// SetPropertyManaged is a helper method to define mock.On call -// - _a0 bool -func (_e *MockDeviceWireless_Expecter) SetPropertyManaged(_a0 interface{}) *MockDeviceWireless_SetPropertyManaged_Call { - return &MockDeviceWireless_SetPropertyManaged_Call{Call: _e.mock.On("SetPropertyManaged", _a0)} -} - -func (_c *MockDeviceWireless_SetPropertyManaged_Call) Run(run func(_a0 bool)) *MockDeviceWireless_SetPropertyManaged_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockDeviceWireless_SetPropertyManaged_Call) Return(_a0 error) *MockDeviceWireless_SetPropertyManaged_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockDeviceWireless_SetPropertyManaged_Call) RunAndReturn(run func(bool) error) *MockDeviceWireless_SetPropertyManaged_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeState provides a mock function with given fields: receiver, exit -func (_m *MockDeviceWireless) SubscribeState(receiver chan gonetworkmanager.DeviceStateChange, exit chan struct{}) error { - ret := _m.Called(receiver, exit) - - if len(ret) == 0 { - panic("no return value specified for SubscribeState") - } - - var r0 error - if rf, ok := ret.Get(0).(func(chan gonetworkmanager.DeviceStateChange, chan struct{}) error); ok { - r0 = rf(receiver, exit) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockDeviceWireless_SubscribeState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeState' -type MockDeviceWireless_SubscribeState_Call struct { - *mock.Call -} - -// SubscribeState is a helper method to define mock.On call -// - receiver chan gonetworkmanager.DeviceStateChange -// - exit chan struct{} -func (_e *MockDeviceWireless_Expecter) SubscribeState(receiver interface{}, exit interface{}) *MockDeviceWireless_SubscribeState_Call { - return &MockDeviceWireless_SubscribeState_Call{Call: _e.mock.On("SubscribeState", receiver, exit)} -} - -func (_c *MockDeviceWireless_SubscribeState_Call) Run(run func(receiver chan gonetworkmanager.DeviceStateChange, exit chan struct{})) *MockDeviceWireless_SubscribeState_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(chan gonetworkmanager.DeviceStateChange), args[1].(chan struct{})) - }) - return _c -} - -func (_c *MockDeviceWireless_SubscribeState_Call) Return(err error) *MockDeviceWireless_SubscribeState_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockDeviceWireless_SubscribeState_Call) RunAndReturn(run func(chan gonetworkmanager.DeviceStateChange, chan struct{}) error) *MockDeviceWireless_SubscribeState_Call { - _c.Call.Return(run) - return _c -} - -// NewMockDeviceWireless creates a new instance of MockDeviceWireless. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockDeviceWireless(t interface { - mock.TestingT - Cleanup(func()) -}) *MockDeviceWireless { - mock := &MockDeviceWireless{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_IP4Config.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_IP4Config.go deleted file mode 100644 index 235be86..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_IP4Config.go +++ /dev/null @@ -1,772 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - mock "github.com/stretchr/testify/mock" -) - -// MockIP4Config is an autogenerated mock type for the IP4Config type -type MockIP4Config struct { - mock.Mock -} - -type MockIP4Config_Expecter struct { - mock *mock.Mock -} - -func (_m *MockIP4Config) EXPECT() *MockIP4Config_Expecter { - return &MockIP4Config_Expecter{mock: &_m.Mock} -} - -// GetPropertyAddressData provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyAddressData() ([]gonetworkmanager.IP4AddressData, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAddressData") - } - - var r0 []gonetworkmanager.IP4AddressData - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4AddressData, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4AddressData); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4AddressData) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyAddressData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAddressData' -type MockIP4Config_GetPropertyAddressData_Call struct { - *mock.Call -} - -// GetPropertyAddressData is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyAddressData() *MockIP4Config_GetPropertyAddressData_Call { - return &MockIP4Config_GetPropertyAddressData_Call{Call: _e.mock.On("GetPropertyAddressData")} -} - -func (_c *MockIP4Config_GetPropertyAddressData_Call) Run(run func()) *MockIP4Config_GetPropertyAddressData_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyAddressData_Call) Return(_a0 []gonetworkmanager.IP4AddressData, _a1 error) *MockIP4Config_GetPropertyAddressData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyAddressData_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4AddressData, error)) *MockIP4Config_GetPropertyAddressData_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAddresses provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyAddresses() ([]gonetworkmanager.IP4Address, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAddresses") - } - - var r0 []gonetworkmanager.IP4Address - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4Address, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4Address); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4Address) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyAddresses_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAddresses' -type MockIP4Config_GetPropertyAddresses_Call struct { - *mock.Call -} - -// GetPropertyAddresses is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyAddresses() *MockIP4Config_GetPropertyAddresses_Call { - return &MockIP4Config_GetPropertyAddresses_Call{Call: _e.mock.On("GetPropertyAddresses")} -} - -func (_c *MockIP4Config_GetPropertyAddresses_Call) Run(run func()) *MockIP4Config_GetPropertyAddresses_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyAddresses_Call) Return(_a0 []gonetworkmanager.IP4Address, _a1 error) *MockIP4Config_GetPropertyAddresses_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyAddresses_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4Address, error)) *MockIP4Config_GetPropertyAddresses_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDnsOptions provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyDnsOptions() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDnsOptions") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyDnsOptions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDnsOptions' -type MockIP4Config_GetPropertyDnsOptions_Call struct { - *mock.Call -} - -// GetPropertyDnsOptions is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyDnsOptions() *MockIP4Config_GetPropertyDnsOptions_Call { - return &MockIP4Config_GetPropertyDnsOptions_Call{Call: _e.mock.On("GetPropertyDnsOptions")} -} - -func (_c *MockIP4Config_GetPropertyDnsOptions_Call) Run(run func()) *MockIP4Config_GetPropertyDnsOptions_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyDnsOptions_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertyDnsOptions_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyDnsOptions_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertyDnsOptions_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDnsPriority provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyDnsPriority() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDnsPriority") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyDnsPriority_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDnsPriority' -type MockIP4Config_GetPropertyDnsPriority_Call struct { - *mock.Call -} - -// GetPropertyDnsPriority is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyDnsPriority() *MockIP4Config_GetPropertyDnsPriority_Call { - return &MockIP4Config_GetPropertyDnsPriority_Call{Call: _e.mock.On("GetPropertyDnsPriority")} -} - -func (_c *MockIP4Config_GetPropertyDnsPriority_Call) Run(run func()) *MockIP4Config_GetPropertyDnsPriority_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyDnsPriority_Call) Return(_a0 uint32, _a1 error) *MockIP4Config_GetPropertyDnsPriority_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyDnsPriority_Call) RunAndReturn(run func() (uint32, error)) *MockIP4Config_GetPropertyDnsPriority_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDomains provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyDomains() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDomains") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyDomains_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDomains' -type MockIP4Config_GetPropertyDomains_Call struct { - *mock.Call -} - -// GetPropertyDomains is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyDomains() *MockIP4Config_GetPropertyDomains_Call { - return &MockIP4Config_GetPropertyDomains_Call{Call: _e.mock.On("GetPropertyDomains")} -} - -func (_c *MockIP4Config_GetPropertyDomains_Call) Run(run func()) *MockIP4Config_GetPropertyDomains_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyDomains_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertyDomains_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyDomains_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertyDomains_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyGateway provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyGateway() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyGateway") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyGateway_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyGateway' -type MockIP4Config_GetPropertyGateway_Call struct { - *mock.Call -} - -// GetPropertyGateway is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyGateway() *MockIP4Config_GetPropertyGateway_Call { - return &MockIP4Config_GetPropertyGateway_Call{Call: _e.mock.On("GetPropertyGateway")} -} - -func (_c *MockIP4Config_GetPropertyGateway_Call) Run(run func()) *MockIP4Config_GetPropertyGateway_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyGateway_Call) Return(_a0 string, _a1 error) *MockIP4Config_GetPropertyGateway_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyGateway_Call) RunAndReturn(run func() (string, error)) *MockIP4Config_GetPropertyGateway_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNameserverData provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyNameserverData() ([]gonetworkmanager.IP4NameserverData, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNameserverData") - } - - var r0 []gonetworkmanager.IP4NameserverData - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4NameserverData, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4NameserverData); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4NameserverData) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyNameserverData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNameserverData' -type MockIP4Config_GetPropertyNameserverData_Call struct { - *mock.Call -} - -// GetPropertyNameserverData is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyNameserverData() *MockIP4Config_GetPropertyNameserverData_Call { - return &MockIP4Config_GetPropertyNameserverData_Call{Call: _e.mock.On("GetPropertyNameserverData")} -} - -func (_c *MockIP4Config_GetPropertyNameserverData_Call) Run(run func()) *MockIP4Config_GetPropertyNameserverData_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyNameserverData_Call) Return(_a0 []gonetworkmanager.IP4NameserverData, _a1 error) *MockIP4Config_GetPropertyNameserverData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyNameserverData_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4NameserverData, error)) *MockIP4Config_GetPropertyNameserverData_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNameservers provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyNameservers() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNameservers") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyNameservers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNameservers' -type MockIP4Config_GetPropertyNameservers_Call struct { - *mock.Call -} - -// GetPropertyNameservers is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyNameservers() *MockIP4Config_GetPropertyNameservers_Call { - return &MockIP4Config_GetPropertyNameservers_Call{Call: _e.mock.On("GetPropertyNameservers")} -} - -func (_c *MockIP4Config_GetPropertyNameservers_Call) Run(run func()) *MockIP4Config_GetPropertyNameservers_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyNameservers_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertyNameservers_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyNameservers_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertyNameservers_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyRouteData provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyRouteData() ([]gonetworkmanager.IP4RouteData, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyRouteData") - } - - var r0 []gonetworkmanager.IP4RouteData - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4RouteData, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4RouteData); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4RouteData) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyRouteData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyRouteData' -type MockIP4Config_GetPropertyRouteData_Call struct { - *mock.Call -} - -// GetPropertyRouteData is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyRouteData() *MockIP4Config_GetPropertyRouteData_Call { - return &MockIP4Config_GetPropertyRouteData_Call{Call: _e.mock.On("GetPropertyRouteData")} -} - -func (_c *MockIP4Config_GetPropertyRouteData_Call) Run(run func()) *MockIP4Config_GetPropertyRouteData_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyRouteData_Call) Return(_a0 []gonetworkmanager.IP4RouteData, _a1 error) *MockIP4Config_GetPropertyRouteData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyRouteData_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4RouteData, error)) *MockIP4Config_GetPropertyRouteData_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyRoutes provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyRoutes() ([]gonetworkmanager.IP4Route, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyRoutes") - } - - var r0 []gonetworkmanager.IP4Route - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.IP4Route, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.IP4Route); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.IP4Route) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyRoutes_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyRoutes' -type MockIP4Config_GetPropertyRoutes_Call struct { - *mock.Call -} - -// GetPropertyRoutes is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyRoutes() *MockIP4Config_GetPropertyRoutes_Call { - return &MockIP4Config_GetPropertyRoutes_Call{Call: _e.mock.On("GetPropertyRoutes")} -} - -func (_c *MockIP4Config_GetPropertyRoutes_Call) Run(run func()) *MockIP4Config_GetPropertyRoutes_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyRoutes_Call) Return(_a0 []gonetworkmanager.IP4Route, _a1 error) *MockIP4Config_GetPropertyRoutes_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyRoutes_Call) RunAndReturn(run func() ([]gonetworkmanager.IP4Route, error)) *MockIP4Config_GetPropertyRoutes_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertySearches provides a mock function with no fields -func (_m *MockIP4Config) GetPropertySearches() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertySearches") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertySearches_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertySearches' -type MockIP4Config_GetPropertySearches_Call struct { - *mock.Call -} - -// GetPropertySearches is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertySearches() *MockIP4Config_GetPropertySearches_Call { - return &MockIP4Config_GetPropertySearches_Call{Call: _e.mock.On("GetPropertySearches")} -} - -func (_c *MockIP4Config_GetPropertySearches_Call) Run(run func()) *MockIP4Config_GetPropertySearches_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertySearches_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertySearches_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertySearches_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertySearches_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWinsServerData provides a mock function with no fields -func (_m *MockIP4Config) GetPropertyWinsServerData() ([]string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWinsServerData") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func() ([]string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_GetPropertyWinsServerData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWinsServerData' -type MockIP4Config_GetPropertyWinsServerData_Call struct { - *mock.Call -} - -// GetPropertyWinsServerData is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) GetPropertyWinsServerData() *MockIP4Config_GetPropertyWinsServerData_Call { - return &MockIP4Config_GetPropertyWinsServerData_Call{Call: _e.mock.On("GetPropertyWinsServerData")} -} - -func (_c *MockIP4Config_GetPropertyWinsServerData_Call) Run(run func()) *MockIP4Config_GetPropertyWinsServerData_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_GetPropertyWinsServerData_Call) Return(_a0 []string, _a1 error) *MockIP4Config_GetPropertyWinsServerData_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_GetPropertyWinsServerData_Call) RunAndReturn(run func() ([]string, error)) *MockIP4Config_GetPropertyWinsServerData_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockIP4Config) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockIP4Config_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockIP4Config_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockIP4Config_Expecter) MarshalJSON() *MockIP4Config_MarshalJSON_Call { - return &MockIP4Config_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockIP4Config_MarshalJSON_Call) Run(run func()) *MockIP4Config_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockIP4Config_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockIP4Config_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockIP4Config_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockIP4Config_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// NewMockIP4Config creates a new instance of MockIP4Config. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockIP4Config(t interface { - mock.TestingT - Cleanup(func()) -}) *MockIP4Config { - mock := &MockIP4Config{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_NetworkManager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_NetworkManager.go deleted file mode 100644 index e74ffb0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_NetworkManager.go +++ /dev/null @@ -1,2349 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - dbus "github.com/godbus/dbus/v5" - - mock "github.com/stretchr/testify/mock" -) - -// MockNetworkManager is an autogenerated mock type for the NetworkManager type -type MockNetworkManager struct { - mock.Mock -} - -type MockNetworkManager_Expecter struct { - mock *mock.Mock -} - -func (_m *MockNetworkManager) EXPECT() *MockNetworkManager_Expecter { - return &MockNetworkManager_Expecter{mock: &_m.Mock} -} - -// ActivateConnection provides a mock function with given fields: connection, device, specificObject -func (_m *MockNetworkManager) ActivateConnection(connection gonetworkmanager.Connection, device gonetworkmanager.Device, specificObject *dbus.Object) (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called(connection, device, specificObject) - - if len(ret) == 0 { - panic("no return value specified for ActivateConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, gonetworkmanager.Device, *dbus.Object) (gonetworkmanager.ActiveConnection, error)); ok { - return rf(connection, device, specificObject) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, gonetworkmanager.Device, *dbus.Object) gonetworkmanager.ActiveConnection); ok { - r0 = rf(connection, device, specificObject) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.Connection, gonetworkmanager.Device, *dbus.Object) error); ok { - r1 = rf(connection, device, specificObject) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_ActivateConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ActivateConnection' -type MockNetworkManager_ActivateConnection_Call struct { - *mock.Call -} - -// ActivateConnection is a helper method to define mock.On call -// - connection gonetworkmanager.Connection -// - device gonetworkmanager.Device -// - specificObject *dbus.Object -func (_e *MockNetworkManager_Expecter) ActivateConnection(connection interface{}, device interface{}, specificObject interface{}) *MockNetworkManager_ActivateConnection_Call { - return &MockNetworkManager_ActivateConnection_Call{Call: _e.mock.On("ActivateConnection", connection, device, specificObject)} -} - -func (_c *MockNetworkManager_ActivateConnection_Call) Run(run func(connection gonetworkmanager.Connection, device gonetworkmanager.Device, specificObject *dbus.Object)) *MockNetworkManager_ActivateConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Connection), args[1].(gonetworkmanager.Device), args[2].(*dbus.Object)) - }) - return _c -} - -func (_c *MockNetworkManager_ActivateConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_ActivateConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_ActivateConnection_Call) RunAndReturn(run func(gonetworkmanager.Connection, gonetworkmanager.Device, *dbus.Object) (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_ActivateConnection_Call { - _c.Call.Return(run) - return _c -} - -// ActivateWirelessConnection provides a mock function with given fields: connection, device, accessPoint -func (_m *MockNetworkManager) ActivateWirelessConnection(connection gonetworkmanager.Connection, device gonetworkmanager.Device, accessPoint gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called(connection, device, accessPoint) - - if len(ret) == 0 { - panic("no return value specified for ActivateWirelessConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, gonetworkmanager.Device, gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error)); ok { - return rf(connection, device, accessPoint) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.Connection, gonetworkmanager.Device, gonetworkmanager.AccessPoint) gonetworkmanager.ActiveConnection); ok { - r0 = rf(connection, device, accessPoint) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.Connection, gonetworkmanager.Device, gonetworkmanager.AccessPoint) error); ok { - r1 = rf(connection, device, accessPoint) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_ActivateWirelessConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ActivateWirelessConnection' -type MockNetworkManager_ActivateWirelessConnection_Call struct { - *mock.Call -} - -// ActivateWirelessConnection is a helper method to define mock.On call -// - connection gonetworkmanager.Connection -// - device gonetworkmanager.Device -// - accessPoint gonetworkmanager.AccessPoint -func (_e *MockNetworkManager_Expecter) ActivateWirelessConnection(connection interface{}, device interface{}, accessPoint interface{}) *MockNetworkManager_ActivateWirelessConnection_Call { - return &MockNetworkManager_ActivateWirelessConnection_Call{Call: _e.mock.On("ActivateWirelessConnection", connection, device, accessPoint)} -} - -func (_c *MockNetworkManager_ActivateWirelessConnection_Call) Run(run func(connection gonetworkmanager.Connection, device gonetworkmanager.Device, accessPoint gonetworkmanager.AccessPoint)) *MockNetworkManager_ActivateWirelessConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Connection), args[1].(gonetworkmanager.Device), args[2].(gonetworkmanager.AccessPoint)) - }) - return _c -} - -func (_c *MockNetworkManager_ActivateWirelessConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_ActivateWirelessConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_ActivateWirelessConnection_Call) RunAndReturn(run func(gonetworkmanager.Connection, gonetworkmanager.Device, gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_ActivateWirelessConnection_Call { - _c.Call.Return(run) - return _c -} - -// AddAndActivateConnection provides a mock function with given fields: connection, device -func (_m *MockNetworkManager) AddAndActivateConnection(connection map[string]map[string]interface{}, device gonetworkmanager.Device) (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called(connection, device) - - if len(ret) == 0 { - panic("no return value specified for AddAndActivateConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func(map[string]map[string]interface{}, gonetworkmanager.Device) (gonetworkmanager.ActiveConnection, error)); ok { - return rf(connection, device) - } - if rf, ok := ret.Get(0).(func(map[string]map[string]interface{}, gonetworkmanager.Device) gonetworkmanager.ActiveConnection); ok { - r0 = rf(connection, device) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func(map[string]map[string]interface{}, gonetworkmanager.Device) error); ok { - r1 = rf(connection, device) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_AddAndActivateConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddAndActivateConnection' -type MockNetworkManager_AddAndActivateConnection_Call struct { - *mock.Call -} - -// AddAndActivateConnection is a helper method to define mock.On call -// - connection map[string]map[string]interface{} -// - device gonetworkmanager.Device -func (_e *MockNetworkManager_Expecter) AddAndActivateConnection(connection interface{}, device interface{}) *MockNetworkManager_AddAndActivateConnection_Call { - return &MockNetworkManager_AddAndActivateConnection_Call{Call: _e.mock.On("AddAndActivateConnection", connection, device)} -} - -func (_c *MockNetworkManager_AddAndActivateConnection_Call) Run(run func(connection map[string]map[string]interface{}, device gonetworkmanager.Device)) *MockNetworkManager_AddAndActivateConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]map[string]interface{}), args[1].(gonetworkmanager.Device)) - }) - return _c -} - -func (_c *MockNetworkManager_AddAndActivateConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_AddAndActivateConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_AddAndActivateConnection_Call) RunAndReturn(run func(map[string]map[string]interface{}, gonetworkmanager.Device) (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_AddAndActivateConnection_Call { - _c.Call.Return(run) - return _c -} - -// AddAndActivateWirelessConnection provides a mock function with given fields: connection, device, accessPoint -func (_m *MockNetworkManager) AddAndActivateWirelessConnection(connection map[string]map[string]interface{}, device gonetworkmanager.Device, accessPoint gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called(connection, device, accessPoint) - - if len(ret) == 0 { - panic("no return value specified for AddAndActivateWirelessConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func(map[string]map[string]interface{}, gonetworkmanager.Device, gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error)); ok { - return rf(connection, device, accessPoint) - } - if rf, ok := ret.Get(0).(func(map[string]map[string]interface{}, gonetworkmanager.Device, gonetworkmanager.AccessPoint) gonetworkmanager.ActiveConnection); ok { - r0 = rf(connection, device, accessPoint) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func(map[string]map[string]interface{}, gonetworkmanager.Device, gonetworkmanager.AccessPoint) error); ok { - r1 = rf(connection, device, accessPoint) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_AddAndActivateWirelessConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddAndActivateWirelessConnection' -type MockNetworkManager_AddAndActivateWirelessConnection_Call struct { - *mock.Call -} - -// AddAndActivateWirelessConnection is a helper method to define mock.On call -// - connection map[string]map[string]interface{} -// - device gonetworkmanager.Device -// - accessPoint gonetworkmanager.AccessPoint -func (_e *MockNetworkManager_Expecter) AddAndActivateWirelessConnection(connection interface{}, device interface{}, accessPoint interface{}) *MockNetworkManager_AddAndActivateWirelessConnection_Call { - return &MockNetworkManager_AddAndActivateWirelessConnection_Call{Call: _e.mock.On("AddAndActivateWirelessConnection", connection, device, accessPoint)} -} - -func (_c *MockNetworkManager_AddAndActivateWirelessConnection_Call) Run(run func(connection map[string]map[string]interface{}, device gonetworkmanager.Device, accessPoint gonetworkmanager.AccessPoint)) *MockNetworkManager_AddAndActivateWirelessConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(map[string]map[string]interface{}), args[1].(gonetworkmanager.Device), args[2].(gonetworkmanager.AccessPoint)) - }) - return _c -} - -func (_c *MockNetworkManager_AddAndActivateWirelessConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_AddAndActivateWirelessConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_AddAndActivateWirelessConnection_Call) RunAndReturn(run func(map[string]map[string]interface{}, gonetworkmanager.Device, gonetworkmanager.AccessPoint) (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_AddAndActivateWirelessConnection_Call { - _c.Call.Return(run) - return _c -} - -// CheckConnectivity provides a mock function with no fields -func (_m *MockNetworkManager) CheckConnectivity() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for CheckConnectivity") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_CheckConnectivity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckConnectivity' -type MockNetworkManager_CheckConnectivity_Call struct { - *mock.Call -} - -// CheckConnectivity is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) CheckConnectivity() *MockNetworkManager_CheckConnectivity_Call { - return &MockNetworkManager_CheckConnectivity_Call{Call: _e.mock.On("CheckConnectivity")} -} - -func (_c *MockNetworkManager_CheckConnectivity_Call) Run(run func()) *MockNetworkManager_CheckConnectivity_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_CheckConnectivity_Call) Return(_a0 error) *MockNetworkManager_CheckConnectivity_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_CheckConnectivity_Call) RunAndReturn(run func() error) *MockNetworkManager_CheckConnectivity_Call { - _c.Call.Return(run) - return _c -} - -// CheckpointAdjustRollbackTimeout provides a mock function with given fields: checkpoint, addTimeout -func (_m *MockNetworkManager) CheckpointAdjustRollbackTimeout(checkpoint gonetworkmanager.Checkpoint, addTimeout uint32) error { - ret := _m.Called(checkpoint, addTimeout) - - if len(ret) == 0 { - panic("no return value specified for CheckpointAdjustRollbackTimeout") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Checkpoint, uint32) error); ok { - r0 = rf(checkpoint, addTimeout) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_CheckpointAdjustRollbackTimeout_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckpointAdjustRollbackTimeout' -type MockNetworkManager_CheckpointAdjustRollbackTimeout_Call struct { - *mock.Call -} - -// CheckpointAdjustRollbackTimeout is a helper method to define mock.On call -// - checkpoint gonetworkmanager.Checkpoint -// - addTimeout uint32 -func (_e *MockNetworkManager_Expecter) CheckpointAdjustRollbackTimeout(checkpoint interface{}, addTimeout interface{}) *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call { - return &MockNetworkManager_CheckpointAdjustRollbackTimeout_Call{Call: _e.mock.On("CheckpointAdjustRollbackTimeout", checkpoint, addTimeout)} -} - -func (_c *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call) Run(run func(checkpoint gonetworkmanager.Checkpoint, addTimeout uint32)) *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Checkpoint), args[1].(uint32)) - }) - return _c -} - -func (_c *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call) Return(_a0 error) *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call) RunAndReturn(run func(gonetworkmanager.Checkpoint, uint32) error) *MockNetworkManager_CheckpointAdjustRollbackTimeout_Call { - _c.Call.Return(run) - return _c -} - -// CheckpointCreate provides a mock function with given fields: devices, rollbackTimeout, flags -func (_m *MockNetworkManager) CheckpointCreate(devices []gonetworkmanager.Device, rollbackTimeout uint32, flags uint32) (gonetworkmanager.Checkpoint, error) { - ret := _m.Called(devices, rollbackTimeout, flags) - - if len(ret) == 0 { - panic("no return value specified for CheckpointCreate") - } - - var r0 gonetworkmanager.Checkpoint - var r1 error - if rf, ok := ret.Get(0).(func([]gonetworkmanager.Device, uint32, uint32) (gonetworkmanager.Checkpoint, error)); ok { - return rf(devices, rollbackTimeout, flags) - } - if rf, ok := ret.Get(0).(func([]gonetworkmanager.Device, uint32, uint32) gonetworkmanager.Checkpoint); ok { - r0 = rf(devices, rollbackTimeout, flags) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Checkpoint) - } - } - - if rf, ok := ret.Get(1).(func([]gonetworkmanager.Device, uint32, uint32) error); ok { - r1 = rf(devices, rollbackTimeout, flags) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_CheckpointCreate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckpointCreate' -type MockNetworkManager_CheckpointCreate_Call struct { - *mock.Call -} - -// CheckpointCreate is a helper method to define mock.On call -// - devices []gonetworkmanager.Device -// - rollbackTimeout uint32 -// - flags uint32 -func (_e *MockNetworkManager_Expecter) CheckpointCreate(devices interface{}, rollbackTimeout interface{}, flags interface{}) *MockNetworkManager_CheckpointCreate_Call { - return &MockNetworkManager_CheckpointCreate_Call{Call: _e.mock.On("CheckpointCreate", devices, rollbackTimeout, flags)} -} - -func (_c *MockNetworkManager_CheckpointCreate_Call) Run(run func(devices []gonetworkmanager.Device, rollbackTimeout uint32, flags uint32)) *MockNetworkManager_CheckpointCreate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]gonetworkmanager.Device), args[1].(uint32), args[2].(uint32)) - }) - return _c -} - -func (_c *MockNetworkManager_CheckpointCreate_Call) Return(_a0 gonetworkmanager.Checkpoint, _a1 error) *MockNetworkManager_CheckpointCreate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_CheckpointCreate_Call) RunAndReturn(run func([]gonetworkmanager.Device, uint32, uint32) (gonetworkmanager.Checkpoint, error)) *MockNetworkManager_CheckpointCreate_Call { - _c.Call.Return(run) - return _c -} - -// CheckpointDestroy provides a mock function with given fields: checkpoint -func (_m *MockNetworkManager) CheckpointDestroy(checkpoint gonetworkmanager.Checkpoint) error { - ret := _m.Called(checkpoint) - - if len(ret) == 0 { - panic("no return value specified for CheckpointDestroy") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Checkpoint) error); ok { - r0 = rf(checkpoint) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_CheckpointDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckpointDestroy' -type MockNetworkManager_CheckpointDestroy_Call struct { - *mock.Call -} - -// CheckpointDestroy is a helper method to define mock.On call -// - checkpoint gonetworkmanager.Checkpoint -func (_e *MockNetworkManager_Expecter) CheckpointDestroy(checkpoint interface{}) *MockNetworkManager_CheckpointDestroy_Call { - return &MockNetworkManager_CheckpointDestroy_Call{Call: _e.mock.On("CheckpointDestroy", checkpoint)} -} - -func (_c *MockNetworkManager_CheckpointDestroy_Call) Run(run func(checkpoint gonetworkmanager.Checkpoint)) *MockNetworkManager_CheckpointDestroy_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Checkpoint)) - }) - return _c -} - -func (_c *MockNetworkManager_CheckpointDestroy_Call) Return(_a0 error) *MockNetworkManager_CheckpointDestroy_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_CheckpointDestroy_Call) RunAndReturn(run func(gonetworkmanager.Checkpoint) error) *MockNetworkManager_CheckpointDestroy_Call { - _c.Call.Return(run) - return _c -} - -// CheckpointRollback provides a mock function with given fields: checkpoint -func (_m *MockNetworkManager) CheckpointRollback(checkpoint gonetworkmanager.Checkpoint) (map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult, error) { - ret := _m.Called(checkpoint) - - if len(ret) == 0 { - panic("no return value specified for CheckpointRollback") - } - - var r0 map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.Checkpoint) (map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult, error)); ok { - return rf(checkpoint) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.Checkpoint) map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult); ok { - r0 = rf(checkpoint) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.Checkpoint) error); ok { - r1 = rf(checkpoint) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_CheckpointRollback_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckpointRollback' -type MockNetworkManager_CheckpointRollback_Call struct { - *mock.Call -} - -// CheckpointRollback is a helper method to define mock.On call -// - checkpoint gonetworkmanager.Checkpoint -func (_e *MockNetworkManager_Expecter) CheckpointRollback(checkpoint interface{}) *MockNetworkManager_CheckpointRollback_Call { - return &MockNetworkManager_CheckpointRollback_Call{Call: _e.mock.On("CheckpointRollback", checkpoint)} -} - -func (_c *MockNetworkManager_CheckpointRollback_Call) Run(run func(checkpoint gonetworkmanager.Checkpoint)) *MockNetworkManager_CheckpointRollback_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.Checkpoint)) - }) - return _c -} - -func (_c *MockNetworkManager_CheckpointRollback_Call) Return(result map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult, err error) *MockNetworkManager_CheckpointRollback_Call { - _c.Call.Return(result, err) - return _c -} - -func (_c *MockNetworkManager_CheckpointRollback_Call) RunAndReturn(run func(gonetworkmanager.Checkpoint) (map[dbus.ObjectPath]gonetworkmanager.NmRollbackResult, error)) *MockNetworkManager_CheckpointRollback_Call { - _c.Call.Return(run) - return _c -} - -// DeactivateConnection provides a mock function with given fields: connection -func (_m *MockNetworkManager) DeactivateConnection(connection gonetworkmanager.ActiveConnection) error { - ret := _m.Called(connection) - - if len(ret) == 0 { - panic("no return value specified for DeactivateConnection") - } - - var r0 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ActiveConnection) error); ok { - r0 = rf(connection) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_DeactivateConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeactivateConnection' -type MockNetworkManager_DeactivateConnection_Call struct { - *mock.Call -} - -// DeactivateConnection is a helper method to define mock.On call -// - connection gonetworkmanager.ActiveConnection -func (_e *MockNetworkManager_Expecter) DeactivateConnection(connection interface{}) *MockNetworkManager_DeactivateConnection_Call { - return &MockNetworkManager_DeactivateConnection_Call{Call: _e.mock.On("DeactivateConnection", connection)} -} - -func (_c *MockNetworkManager_DeactivateConnection_Call) Run(run func(connection gonetworkmanager.ActiveConnection)) *MockNetworkManager_DeactivateConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ActiveConnection)) - }) - return _c -} - -func (_c *MockNetworkManager_DeactivateConnection_Call) Return(_a0 error) *MockNetworkManager_DeactivateConnection_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_DeactivateConnection_Call) RunAndReturn(run func(gonetworkmanager.ActiveConnection) error) *MockNetworkManager_DeactivateConnection_Call { - _c.Call.Return(run) - return _c -} - -// Enable provides a mock function with given fields: enableNDisable -func (_m *MockNetworkManager) Enable(enableNDisable bool) error { - ret := _m.Called(enableNDisable) - - if len(ret) == 0 { - panic("no return value specified for Enable") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(enableNDisable) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_Enable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Enable' -type MockNetworkManager_Enable_Call struct { - *mock.Call -} - -// Enable is a helper method to define mock.On call -// - enableNDisable bool -func (_e *MockNetworkManager_Expecter) Enable(enableNDisable interface{}) *MockNetworkManager_Enable_Call { - return &MockNetworkManager_Enable_Call{Call: _e.mock.On("Enable", enableNDisable)} -} - -func (_c *MockNetworkManager_Enable_Call) Run(run func(enableNDisable bool)) *MockNetworkManager_Enable_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockNetworkManager_Enable_Call) Return(_a0 error) *MockNetworkManager_Enable_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_Enable_Call) RunAndReturn(run func(bool) error) *MockNetworkManager_Enable_Call { - _c.Call.Return(run) - return _c -} - -// GetAllDevices provides a mock function with no fields -func (_m *MockNetworkManager) GetAllDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetAllDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetAllDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAllDevices' -type MockNetworkManager_GetAllDevices_Call struct { - *mock.Call -} - -// GetAllDevices is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetAllDevices() *MockNetworkManager_GetAllDevices_Call { - return &MockNetworkManager_GetAllDevices_Call{Call: _e.mock.On("GetAllDevices")} -} - -func (_c *MockNetworkManager_GetAllDevices_Call) Run(run func()) *MockNetworkManager_GetAllDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetAllDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetAllDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetAllDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockNetworkManager_GetAllDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetDeviceByIpIface provides a mock function with given fields: interfaceId -func (_m *MockNetworkManager) GetDeviceByIpIface(interfaceId string) (gonetworkmanager.Device, error) { - ret := _m.Called(interfaceId) - - if len(ret) == 0 { - panic("no return value specified for GetDeviceByIpIface") - } - - var r0 gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func(string) (gonetworkmanager.Device, error)); ok { - return rf(interfaceId) - } - if rf, ok := ret.Get(0).(func(string) gonetworkmanager.Device); ok { - r0 = rf(interfaceId) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(interfaceId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetDeviceByIpIface_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDeviceByIpIface' -type MockNetworkManager_GetDeviceByIpIface_Call struct { - *mock.Call -} - -// GetDeviceByIpIface is a helper method to define mock.On call -// - interfaceId string -func (_e *MockNetworkManager_Expecter) GetDeviceByIpIface(interfaceId interface{}) *MockNetworkManager_GetDeviceByIpIface_Call { - return &MockNetworkManager_GetDeviceByIpIface_Call{Call: _e.mock.On("GetDeviceByIpIface", interfaceId)} -} - -func (_c *MockNetworkManager_GetDeviceByIpIface_Call) Run(run func(interfaceId string)) *MockNetworkManager_GetDeviceByIpIface_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockNetworkManager_GetDeviceByIpIface_Call) Return(_a0 gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetDeviceByIpIface_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetDeviceByIpIface_Call) RunAndReturn(run func(string) (gonetworkmanager.Device, error)) *MockNetworkManager_GetDeviceByIpIface_Call { - _c.Call.Return(run) - return _c -} - -// GetDevices provides a mock function with no fields -func (_m *MockNetworkManager) GetDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDevices' -type MockNetworkManager_GetDevices_Call struct { - *mock.Call -} - -// GetDevices is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetDevices() *MockNetworkManager_GetDevices_Call { - return &MockNetworkManager_GetDevices_Call{Call: _e.mock.On("GetDevices")} -} - -func (_c *MockNetworkManager_GetDevices_Call) Run(run func()) *MockNetworkManager_GetDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockNetworkManager_GetDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActivatingConnection provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyActivatingConnection() (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActivatingConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyActivatingConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActivatingConnection' -type MockNetworkManager_GetPropertyActivatingConnection_Call struct { - *mock.Call -} - -// GetPropertyActivatingConnection is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyActivatingConnection() *MockNetworkManager_GetPropertyActivatingConnection_Call { - return &MockNetworkManager_GetPropertyActivatingConnection_Call{Call: _e.mock.On("GetPropertyActivatingConnection")} -} - -func (_c *MockNetworkManager_GetPropertyActivatingConnection_Call) Run(run func()) *MockNetworkManager_GetPropertyActivatingConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyActivatingConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_GetPropertyActivatingConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyActivatingConnection_Call) RunAndReturn(run func() (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_GetPropertyActivatingConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyActiveConnections provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyActiveConnections() ([]gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyActiveConnections") - } - - var r0 []gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyActiveConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyActiveConnections' -type MockNetworkManager_GetPropertyActiveConnections_Call struct { - *mock.Call -} - -// GetPropertyActiveConnections is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyActiveConnections() *MockNetworkManager_GetPropertyActiveConnections_Call { - return &MockNetworkManager_GetPropertyActiveConnections_Call{Call: _e.mock.On("GetPropertyActiveConnections")} -} - -func (_c *MockNetworkManager_GetPropertyActiveConnections_Call) Run(run func()) *MockNetworkManager_GetPropertyActiveConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyActiveConnections_Call) Return(_a0 []gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_GetPropertyActiveConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyActiveConnections_Call) RunAndReturn(run func() ([]gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_GetPropertyActiveConnections_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyAllDevices provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyAllDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyAllDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyAllDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyAllDevices' -type MockNetworkManager_GetPropertyAllDevices_Call struct { - *mock.Call -} - -// GetPropertyAllDevices is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyAllDevices() *MockNetworkManager_GetPropertyAllDevices_Call { - return &MockNetworkManager_GetPropertyAllDevices_Call{Call: _e.mock.On("GetPropertyAllDevices")} -} - -func (_c *MockNetworkManager_GetPropertyAllDevices_Call) Run(run func()) *MockNetworkManager_GetPropertyAllDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyAllDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetPropertyAllDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyAllDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockNetworkManager_GetPropertyAllDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyCapabilities provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyCapabilities() ([]gonetworkmanager.NmCapability, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyCapabilities") - } - - var r0 []gonetworkmanager.NmCapability - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.NmCapability, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.NmCapability); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.NmCapability) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyCapabilities_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyCapabilities' -type MockNetworkManager_GetPropertyCapabilities_Call struct { - *mock.Call -} - -// GetPropertyCapabilities is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyCapabilities() *MockNetworkManager_GetPropertyCapabilities_Call { - return &MockNetworkManager_GetPropertyCapabilities_Call{Call: _e.mock.On("GetPropertyCapabilities")} -} - -func (_c *MockNetworkManager_GetPropertyCapabilities_Call) Run(run func()) *MockNetworkManager_GetPropertyCapabilities_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyCapabilities_Call) Return(_a0 []gonetworkmanager.NmCapability, _a1 error) *MockNetworkManager_GetPropertyCapabilities_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyCapabilities_Call) RunAndReturn(run func() ([]gonetworkmanager.NmCapability, error)) *MockNetworkManager_GetPropertyCapabilities_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyCheckpoints provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyCheckpoints() ([]gonetworkmanager.Checkpoint, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyCheckpoints") - } - - var r0 []gonetworkmanager.Checkpoint - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Checkpoint, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Checkpoint); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Checkpoint) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyCheckpoints_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyCheckpoints' -type MockNetworkManager_GetPropertyCheckpoints_Call struct { - *mock.Call -} - -// GetPropertyCheckpoints is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyCheckpoints() *MockNetworkManager_GetPropertyCheckpoints_Call { - return &MockNetworkManager_GetPropertyCheckpoints_Call{Call: _e.mock.On("GetPropertyCheckpoints")} -} - -func (_c *MockNetworkManager_GetPropertyCheckpoints_Call) Run(run func()) *MockNetworkManager_GetPropertyCheckpoints_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyCheckpoints_Call) Return(_a0 []gonetworkmanager.Checkpoint, _a1 error) *MockNetworkManager_GetPropertyCheckpoints_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyCheckpoints_Call) RunAndReturn(run func() ([]gonetworkmanager.Checkpoint, error)) *MockNetworkManager_GetPropertyCheckpoints_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyConnectivity provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyConnectivity() (gonetworkmanager.NmConnectivity, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyConnectivity") - } - - var r0 gonetworkmanager.NmConnectivity - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmConnectivity, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmConnectivity); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmConnectivity) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyConnectivity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyConnectivity' -type MockNetworkManager_GetPropertyConnectivity_Call struct { - *mock.Call -} - -// GetPropertyConnectivity is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyConnectivity() *MockNetworkManager_GetPropertyConnectivity_Call { - return &MockNetworkManager_GetPropertyConnectivity_Call{Call: _e.mock.On("GetPropertyConnectivity")} -} - -func (_c *MockNetworkManager_GetPropertyConnectivity_Call) Run(run func()) *MockNetworkManager_GetPropertyConnectivity_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivity_Call) Return(_a0 gonetworkmanager.NmConnectivity, _a1 error) *MockNetworkManager_GetPropertyConnectivity_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivity_Call) RunAndReturn(run func() (gonetworkmanager.NmConnectivity, error)) *MockNetworkManager_GetPropertyConnectivity_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyConnectivityCheckAvailable provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyConnectivityCheckAvailable() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyConnectivityCheckAvailable") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyConnectivityCheckAvailable' -type MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call struct { - *mock.Call -} - -// GetPropertyConnectivityCheckAvailable is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyConnectivityCheckAvailable() *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call { - return &MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call{Call: _e.mock.On("GetPropertyConnectivityCheckAvailable")} -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call) Run(run func()) *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyConnectivityCheckAvailable_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyConnectivityCheckEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyConnectivityCheckEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyConnectivityCheckEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyConnectivityCheckEnabled' -type MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call struct { - *mock.Call -} - -// GetPropertyConnectivityCheckEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyConnectivityCheckEnabled() *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call { - return &MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call{Call: _e.mock.On("GetPropertyConnectivityCheckEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyConnectivityCheckEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyDevices provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyDevices() ([]gonetworkmanager.Device, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyDevices") - } - - var r0 []gonetworkmanager.Device - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Device, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Device); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Device) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyDevices' -type MockNetworkManager_GetPropertyDevices_Call struct { - *mock.Call -} - -// GetPropertyDevices is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyDevices() *MockNetworkManager_GetPropertyDevices_Call { - return &MockNetworkManager_GetPropertyDevices_Call{Call: _e.mock.On("GetPropertyDevices")} -} - -func (_c *MockNetworkManager_GetPropertyDevices_Call) Run(run func()) *MockNetworkManager_GetPropertyDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyDevices_Call) Return(_a0 []gonetworkmanager.Device, _a1 error) *MockNetworkManager_GetPropertyDevices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyDevices_Call) RunAndReturn(run func() ([]gonetworkmanager.Device, error)) *MockNetworkManager_GetPropertyDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyMetered provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyMetered() (gonetworkmanager.NmMetered, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyMetered") - } - - var r0 gonetworkmanager.NmMetered - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmMetered, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmMetered); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmMetered) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyMetered_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyMetered' -type MockNetworkManager_GetPropertyMetered_Call struct { - *mock.Call -} - -// GetPropertyMetered is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyMetered() *MockNetworkManager_GetPropertyMetered_Call { - return &MockNetworkManager_GetPropertyMetered_Call{Call: _e.mock.On("GetPropertyMetered")} -} - -func (_c *MockNetworkManager_GetPropertyMetered_Call) Run(run func()) *MockNetworkManager_GetPropertyMetered_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyMetered_Call) Return(_a0 gonetworkmanager.NmMetered, _a1 error) *MockNetworkManager_GetPropertyMetered_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyMetered_Call) RunAndReturn(run func() (gonetworkmanager.NmMetered, error)) *MockNetworkManager_GetPropertyMetered_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyNetworkingEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyNetworkingEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyNetworkingEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyNetworkingEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyNetworkingEnabled' -type MockNetworkManager_GetPropertyNetworkingEnabled_Call struct { - *mock.Call -} - -// GetPropertyNetworkingEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyNetworkingEnabled() *MockNetworkManager_GetPropertyNetworkingEnabled_Call { - return &MockNetworkManager_GetPropertyNetworkingEnabled_Call{Call: _e.mock.On("GetPropertyNetworkingEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyNetworkingEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyNetworkingEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyNetworkingEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyNetworkingEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyNetworkingEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyNetworkingEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPrimaryConnection provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyPrimaryConnection() (gonetworkmanager.ActiveConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPrimaryConnection") - } - - var r0 gonetworkmanager.ActiveConnection - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.ActiveConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.ActiveConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.ActiveConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyPrimaryConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPrimaryConnection' -type MockNetworkManager_GetPropertyPrimaryConnection_Call struct { - *mock.Call -} - -// GetPropertyPrimaryConnection is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyPrimaryConnection() *MockNetworkManager_GetPropertyPrimaryConnection_Call { - return &MockNetworkManager_GetPropertyPrimaryConnection_Call{Call: _e.mock.On("GetPropertyPrimaryConnection")} -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnection_Call) Run(run func()) *MockNetworkManager_GetPropertyPrimaryConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnection_Call) Return(_a0 gonetworkmanager.ActiveConnection, _a1 error) *MockNetworkManager_GetPropertyPrimaryConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnection_Call) RunAndReturn(run func() (gonetworkmanager.ActiveConnection, error)) *MockNetworkManager_GetPropertyPrimaryConnection_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyPrimaryConnectionType provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyPrimaryConnectionType() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyPrimaryConnectionType") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyPrimaryConnectionType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyPrimaryConnectionType' -type MockNetworkManager_GetPropertyPrimaryConnectionType_Call struct { - *mock.Call -} - -// GetPropertyPrimaryConnectionType is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyPrimaryConnectionType() *MockNetworkManager_GetPropertyPrimaryConnectionType_Call { - return &MockNetworkManager_GetPropertyPrimaryConnectionType_Call{Call: _e.mock.On("GetPropertyPrimaryConnectionType")} -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnectionType_Call) Run(run func()) *MockNetworkManager_GetPropertyPrimaryConnectionType_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnectionType_Call) Return(_a0 string, _a1 error) *MockNetworkManager_GetPropertyPrimaryConnectionType_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyPrimaryConnectionType_Call) RunAndReturn(run func() (string, error)) *MockNetworkManager_GetPropertyPrimaryConnectionType_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyStartup provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyStartup() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyStartup") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyStartup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyStartup' -type MockNetworkManager_GetPropertyStartup_Call struct { - *mock.Call -} - -// GetPropertyStartup is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyStartup() *MockNetworkManager_GetPropertyStartup_Call { - return &MockNetworkManager_GetPropertyStartup_Call{Call: _e.mock.On("GetPropertyStartup")} -} - -func (_c *MockNetworkManager_GetPropertyStartup_Call) Run(run func()) *MockNetworkManager_GetPropertyStartup_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyStartup_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyStartup_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyStartup_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyStartup_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyState provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyState() (gonetworkmanager.NmState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyState") - } - - var r0 gonetworkmanager.NmState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyState' -type MockNetworkManager_GetPropertyState_Call struct { - *mock.Call -} - -// GetPropertyState is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyState() *MockNetworkManager_GetPropertyState_Call { - return &MockNetworkManager_GetPropertyState_Call{Call: _e.mock.On("GetPropertyState")} -} - -func (_c *MockNetworkManager_GetPropertyState_Call) Run(run func()) *MockNetworkManager_GetPropertyState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyState_Call) Return(_a0 gonetworkmanager.NmState, _a1 error) *MockNetworkManager_GetPropertyState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyState_Call) RunAndReturn(run func() (gonetworkmanager.NmState, error)) *MockNetworkManager_GetPropertyState_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyVersion provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyVersion() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyVersion' -type MockNetworkManager_GetPropertyVersion_Call struct { - *mock.Call -} - -// GetPropertyVersion is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyVersion() *MockNetworkManager_GetPropertyVersion_Call { - return &MockNetworkManager_GetPropertyVersion_Call{Call: _e.mock.On("GetPropertyVersion")} -} - -func (_c *MockNetworkManager_GetPropertyVersion_Call) Run(run func()) *MockNetworkManager_GetPropertyVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyVersion_Call) Return(_a0 string, _a1 error) *MockNetworkManager_GetPropertyVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyVersion_Call) RunAndReturn(run func() (string, error)) *MockNetworkManager_GetPropertyVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWimaxEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWimaxEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWimaxEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWimaxEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWimaxEnabled' -type MockNetworkManager_GetPropertyWimaxEnabled_Call struct { - *mock.Call -} - -// GetPropertyWimaxEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWimaxEnabled() *MockNetworkManager_GetPropertyWimaxEnabled_Call { - return &MockNetworkManager_GetPropertyWimaxEnabled_Call{Call: _e.mock.On("GetPropertyWimaxEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWimaxEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWimaxEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWimaxEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWimaxEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWimaxEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWimaxEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWimaxHardwareEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWimaxHardwareEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWimaxHardwareEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWimaxHardwareEnabled' -type MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call struct { - *mock.Call -} - -// GetPropertyWimaxHardwareEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWimaxHardwareEnabled() *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call { - return &MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call{Call: _e.mock.On("GetPropertyWimaxHardwareEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWimaxHardwareEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWirelessEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWirelessEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWirelessEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWirelessEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWirelessEnabled' -type MockNetworkManager_GetPropertyWirelessEnabled_Call struct { - *mock.Call -} - -// GetPropertyWirelessEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWirelessEnabled() *MockNetworkManager_GetPropertyWirelessEnabled_Call { - return &MockNetworkManager_GetPropertyWirelessEnabled_Call{Call: _e.mock.On("GetPropertyWirelessEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWirelessEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWirelessEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWirelessEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWirelessEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWirelessEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWirelessEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWirelessHardwareEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWirelessHardwareEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWirelessHardwareEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWirelessHardwareEnabled' -type MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call struct { - *mock.Call -} - -// GetPropertyWirelessHardwareEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWirelessHardwareEnabled() *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call { - return &MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call{Call: _e.mock.On("GetPropertyWirelessHardwareEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWirelessHardwareEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWwanEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWwanEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWwanEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWwanEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWwanEnabled' -type MockNetworkManager_GetPropertyWwanEnabled_Call struct { - *mock.Call -} - -// GetPropertyWwanEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWwanEnabled() *MockNetworkManager_GetPropertyWwanEnabled_Call { - return &MockNetworkManager_GetPropertyWwanEnabled_Call{Call: _e.mock.On("GetPropertyWwanEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWwanEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWwanEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWwanEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWwanEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWwanEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWwanEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyWwanHardwareEnabled provides a mock function with no fields -func (_m *MockNetworkManager) GetPropertyWwanHardwareEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyWwanHardwareEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_GetPropertyWwanHardwareEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyWwanHardwareEnabled' -type MockNetworkManager_GetPropertyWwanHardwareEnabled_Call struct { - *mock.Call -} - -// GetPropertyWwanHardwareEnabled is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) GetPropertyWwanHardwareEnabled() *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call { - return &MockNetworkManager_GetPropertyWwanHardwareEnabled_Call{Call: _e.mock.On("GetPropertyWwanHardwareEnabled")} -} - -func (_c *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call) Run(run func()) *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call) Return(_a0 bool, _a1 error) *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call) RunAndReturn(run func() (bool, error)) *MockNetworkManager_GetPropertyWwanHardwareEnabled_Call { - _c.Call.Return(run) - return _c -} - -// MarshalJSON provides a mock function with no fields -func (_m *MockNetworkManager) MarshalJSON() ([]byte, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for MarshalJSON") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_MarshalJSON_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarshalJSON' -type MockNetworkManager_MarshalJSON_Call struct { - *mock.Call -} - -// MarshalJSON is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) MarshalJSON() *MockNetworkManager_MarshalJSON_Call { - return &MockNetworkManager_MarshalJSON_Call{Call: _e.mock.On("MarshalJSON")} -} - -func (_c *MockNetworkManager_MarshalJSON_Call) Run(run func()) *MockNetworkManager_MarshalJSON_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_MarshalJSON_Call) Return(_a0 []byte, _a1 error) *MockNetworkManager_MarshalJSON_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_MarshalJSON_Call) RunAndReturn(run func() ([]byte, error)) *MockNetworkManager_MarshalJSON_Call { - _c.Call.Return(run) - return _c -} - -// Reload provides a mock function with given fields: flags -func (_m *MockNetworkManager) Reload(flags uint32) error { - ret := _m.Called(flags) - - if len(ret) == 0 { - panic("no return value specified for Reload") - } - - var r0 error - if rf, ok := ret.Get(0).(func(uint32) error); ok { - r0 = rf(flags) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_Reload_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reload' -type MockNetworkManager_Reload_Call struct { - *mock.Call -} - -// Reload is a helper method to define mock.On call -// - flags uint32 -func (_e *MockNetworkManager_Expecter) Reload(flags interface{}) *MockNetworkManager_Reload_Call { - return &MockNetworkManager_Reload_Call{Call: _e.mock.On("Reload", flags)} -} - -func (_c *MockNetworkManager_Reload_Call) Run(run func(flags uint32)) *MockNetworkManager_Reload_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(uint32)) - }) - return _c -} - -func (_c *MockNetworkManager_Reload_Call) Return(_a0 error) *MockNetworkManager_Reload_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_Reload_Call) RunAndReturn(run func(uint32) error) *MockNetworkManager_Reload_Call { - _c.Call.Return(run) - return _c -} - -// SetPropertyWirelessEnabled provides a mock function with given fields: _a0 -func (_m *MockNetworkManager) SetPropertyWirelessEnabled(_a0 bool) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SetPropertyWirelessEnabled") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_SetPropertyWirelessEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPropertyWirelessEnabled' -type MockNetworkManager_SetPropertyWirelessEnabled_Call struct { - *mock.Call -} - -// SetPropertyWirelessEnabled is a helper method to define mock.On call -// - _a0 bool -func (_e *MockNetworkManager_Expecter) SetPropertyWirelessEnabled(_a0 interface{}) *MockNetworkManager_SetPropertyWirelessEnabled_Call { - return &MockNetworkManager_SetPropertyWirelessEnabled_Call{Call: _e.mock.On("SetPropertyWirelessEnabled", _a0)} -} - -func (_c *MockNetworkManager_SetPropertyWirelessEnabled_Call) Run(run func(_a0 bool)) *MockNetworkManager_SetPropertyWirelessEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockNetworkManager_SetPropertyWirelessEnabled_Call) Return(_a0 error) *MockNetworkManager_SetPropertyWirelessEnabled_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_SetPropertyWirelessEnabled_Call) RunAndReturn(run func(bool) error) *MockNetworkManager_SetPropertyWirelessEnabled_Call { - _c.Call.Return(run) - return _c -} - -// Sleep provides a mock function with given fields: sleepNWake -func (_m *MockNetworkManager) Sleep(sleepNWake bool) error { - ret := _m.Called(sleepNWake) - - if len(ret) == 0 { - panic("no return value specified for Sleep") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(sleepNWake) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockNetworkManager_Sleep_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sleep' -type MockNetworkManager_Sleep_Call struct { - *mock.Call -} - -// Sleep is a helper method to define mock.On call -// - sleepNWake bool -func (_e *MockNetworkManager_Expecter) Sleep(sleepNWake interface{}) *MockNetworkManager_Sleep_Call { - return &MockNetworkManager_Sleep_Call{Call: _e.mock.On("Sleep", sleepNWake)} -} - -func (_c *MockNetworkManager_Sleep_Call) Run(run func(sleepNWake bool)) *MockNetworkManager_Sleep_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockNetworkManager_Sleep_Call) Return(_a0 error) *MockNetworkManager_Sleep_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_Sleep_Call) RunAndReturn(run func(bool) error) *MockNetworkManager_Sleep_Call { - _c.Call.Return(run) - return _c -} - -// State provides a mock function with no fields -func (_m *MockNetworkManager) State() (gonetworkmanager.NmState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for State") - } - - var r0 gonetworkmanager.NmState - var r1 error - if rf, ok := ret.Get(0).(func() (gonetworkmanager.NmState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() gonetworkmanager.NmState); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(gonetworkmanager.NmState) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockNetworkManager_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type MockNetworkManager_State_Call struct { - *mock.Call -} - -// State is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) State() *MockNetworkManager_State_Call { - return &MockNetworkManager_State_Call{Call: _e.mock.On("State")} -} - -func (_c *MockNetworkManager_State_Call) Run(run func()) *MockNetworkManager_State_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_State_Call) Return(_a0 gonetworkmanager.NmState, _a1 error) *MockNetworkManager_State_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockNetworkManager_State_Call) RunAndReturn(run func() (gonetworkmanager.NmState, error)) *MockNetworkManager_State_Call { - _c.Call.Return(run) - return _c -} - -// Subscribe provides a mock function with no fields -func (_m *MockNetworkManager) Subscribe() <-chan *dbus.Signal { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Subscribe") - } - - var r0 <-chan *dbus.Signal - if rf, ok := ret.Get(0).(func() <-chan *dbus.Signal); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan *dbus.Signal) - } - } - - return r0 -} - -// MockNetworkManager_Subscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Subscribe' -type MockNetworkManager_Subscribe_Call struct { - *mock.Call -} - -// Subscribe is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) Subscribe() *MockNetworkManager_Subscribe_Call { - return &MockNetworkManager_Subscribe_Call{Call: _e.mock.On("Subscribe")} -} - -func (_c *MockNetworkManager_Subscribe_Call) Run(run func()) *MockNetworkManager_Subscribe_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_Subscribe_Call) Return(_a0 <-chan *dbus.Signal) *MockNetworkManager_Subscribe_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockNetworkManager_Subscribe_Call) RunAndReturn(run func() <-chan *dbus.Signal) *MockNetworkManager_Subscribe_Call { - _c.Call.Return(run) - return _c -} - -// Unsubscribe provides a mock function with no fields -func (_m *MockNetworkManager) Unsubscribe() { - _m.Called() -} - -// MockNetworkManager_Unsubscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Unsubscribe' -type MockNetworkManager_Unsubscribe_Call struct { - *mock.Call -} - -// Unsubscribe is a helper method to define mock.On call -func (_e *MockNetworkManager_Expecter) Unsubscribe() *MockNetworkManager_Unsubscribe_Call { - return &MockNetworkManager_Unsubscribe_Call{Call: _e.mock.On("Unsubscribe")} -} - -func (_c *MockNetworkManager_Unsubscribe_Call) Run(run func()) *MockNetworkManager_Unsubscribe_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockNetworkManager_Unsubscribe_Call) Return() *MockNetworkManager_Unsubscribe_Call { - _c.Call.Return() - return _c -} - -func (_c *MockNetworkManager_Unsubscribe_Call) RunAndReturn(run func()) *MockNetworkManager_Unsubscribe_Call { - _c.Run(run) - return _c -} - -// NewMockNetworkManager creates a new instance of MockNetworkManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockNetworkManager(t interface { - mock.TestingT - Cleanup(func()) -}) *MockNetworkManager { - mock := &MockNetworkManager{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Settings.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Settings.go deleted file mode 100644 index 20f488e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2/mock_Settings.go +++ /dev/null @@ -1,467 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package gonetworkmanager - -import ( - gonetworkmanager "github.com/Wifx/gonetworkmanager/v2" - mock "github.com/stretchr/testify/mock" -) - -// MockSettings is an autogenerated mock type for the Settings type -type MockSettings struct { - mock.Mock -} - -type MockSettings_Expecter struct { - mock *mock.Mock -} - -func (_m *MockSettings) EXPECT() *MockSettings_Expecter { - return &MockSettings_Expecter{mock: &_m.Mock} -} - -// AddConnection provides a mock function with given fields: settings -func (_m *MockSettings) AddConnection(settings gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error) { - ret := _m.Called(settings) - - if len(ret) == 0 { - panic("no return value specified for AddConnection") - } - - var r0 gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error)); ok { - return rf(settings) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) gonetworkmanager.Connection); ok { - r0 = rf(settings) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.ConnectionSettings) error); ok { - r1 = rf(settings) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_AddConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddConnection' -type MockSettings_AddConnection_Call struct { - *mock.Call -} - -// AddConnection is a helper method to define mock.On call -// - settings gonetworkmanager.ConnectionSettings -func (_e *MockSettings_Expecter) AddConnection(settings interface{}) *MockSettings_AddConnection_Call { - return &MockSettings_AddConnection_Call{Call: _e.mock.On("AddConnection", settings)} -} - -func (_c *MockSettings_AddConnection_Call) Run(run func(settings gonetworkmanager.ConnectionSettings)) *MockSettings_AddConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ConnectionSettings)) - }) - return _c -} - -func (_c *MockSettings_AddConnection_Call) Return(_a0 gonetworkmanager.Connection, _a1 error) *MockSettings_AddConnection_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_AddConnection_Call) RunAndReturn(run func(gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error)) *MockSettings_AddConnection_Call { - _c.Call.Return(run) - return _c -} - -// AddConnectionUnsaved provides a mock function with given fields: settings -func (_m *MockSettings) AddConnectionUnsaved(settings gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error) { - ret := _m.Called(settings) - - if len(ret) == 0 { - panic("no return value specified for AddConnectionUnsaved") - } - - var r0 gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error)); ok { - return rf(settings) - } - if rf, ok := ret.Get(0).(func(gonetworkmanager.ConnectionSettings) gonetworkmanager.Connection); ok { - r0 = rf(settings) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func(gonetworkmanager.ConnectionSettings) error); ok { - r1 = rf(settings) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_AddConnectionUnsaved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddConnectionUnsaved' -type MockSettings_AddConnectionUnsaved_Call struct { - *mock.Call -} - -// AddConnectionUnsaved is a helper method to define mock.On call -// - settings gonetworkmanager.ConnectionSettings -func (_e *MockSettings_Expecter) AddConnectionUnsaved(settings interface{}) *MockSettings_AddConnectionUnsaved_Call { - return &MockSettings_AddConnectionUnsaved_Call{Call: _e.mock.On("AddConnectionUnsaved", settings)} -} - -func (_c *MockSettings_AddConnectionUnsaved_Call) Run(run func(settings gonetworkmanager.ConnectionSettings)) *MockSettings_AddConnectionUnsaved_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(gonetworkmanager.ConnectionSettings)) - }) - return _c -} - -func (_c *MockSettings_AddConnectionUnsaved_Call) Return(_a0 gonetworkmanager.Connection, _a1 error) *MockSettings_AddConnectionUnsaved_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_AddConnectionUnsaved_Call) RunAndReturn(run func(gonetworkmanager.ConnectionSettings) (gonetworkmanager.Connection, error)) *MockSettings_AddConnectionUnsaved_Call { - _c.Call.Return(run) - return _c -} - -// GetConnectionByUUID provides a mock function with given fields: uuid -func (_m *MockSettings) GetConnectionByUUID(uuid string) (gonetworkmanager.Connection, error) { - ret := _m.Called(uuid) - - if len(ret) == 0 { - panic("no return value specified for GetConnectionByUUID") - } - - var r0 gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func(string) (gonetworkmanager.Connection, error)); ok { - return rf(uuid) - } - if rf, ok := ret.Get(0).(func(string) gonetworkmanager.Connection); ok { - r0 = rf(uuid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(uuid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_GetConnectionByUUID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetConnectionByUUID' -type MockSettings_GetConnectionByUUID_Call struct { - *mock.Call -} - -// GetConnectionByUUID is a helper method to define mock.On call -// - uuid string -func (_e *MockSettings_Expecter) GetConnectionByUUID(uuid interface{}) *MockSettings_GetConnectionByUUID_Call { - return &MockSettings_GetConnectionByUUID_Call{Call: _e.mock.On("GetConnectionByUUID", uuid)} -} - -func (_c *MockSettings_GetConnectionByUUID_Call) Run(run func(uuid string)) *MockSettings_GetConnectionByUUID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockSettings_GetConnectionByUUID_Call) Return(_a0 gonetworkmanager.Connection, _a1 error) *MockSettings_GetConnectionByUUID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_GetConnectionByUUID_Call) RunAndReturn(run func(string) (gonetworkmanager.Connection, error)) *MockSettings_GetConnectionByUUID_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyCanModify provides a mock function with no fields -func (_m *MockSettings) GetPropertyCanModify() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyCanModify") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_GetPropertyCanModify_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyCanModify' -type MockSettings_GetPropertyCanModify_Call struct { - *mock.Call -} - -// GetPropertyCanModify is a helper method to define mock.On call -func (_e *MockSettings_Expecter) GetPropertyCanModify() *MockSettings_GetPropertyCanModify_Call { - return &MockSettings_GetPropertyCanModify_Call{Call: _e.mock.On("GetPropertyCanModify")} -} - -func (_c *MockSettings_GetPropertyCanModify_Call) Run(run func()) *MockSettings_GetPropertyCanModify_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockSettings_GetPropertyCanModify_Call) Return(_a0 bool, _a1 error) *MockSettings_GetPropertyCanModify_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_GetPropertyCanModify_Call) RunAndReturn(run func() (bool, error)) *MockSettings_GetPropertyCanModify_Call { - _c.Call.Return(run) - return _c -} - -// GetPropertyHostname provides a mock function with no fields -func (_m *MockSettings) GetPropertyHostname() (string, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPropertyHostname") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func() (string, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_GetPropertyHostname_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPropertyHostname' -type MockSettings_GetPropertyHostname_Call struct { - *mock.Call -} - -// GetPropertyHostname is a helper method to define mock.On call -func (_e *MockSettings_Expecter) GetPropertyHostname() *MockSettings_GetPropertyHostname_Call { - return &MockSettings_GetPropertyHostname_Call{Call: _e.mock.On("GetPropertyHostname")} -} - -func (_c *MockSettings_GetPropertyHostname_Call) Run(run func()) *MockSettings_GetPropertyHostname_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockSettings_GetPropertyHostname_Call) Return(_a0 string, _a1 error) *MockSettings_GetPropertyHostname_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_GetPropertyHostname_Call) RunAndReturn(run func() (string, error)) *MockSettings_GetPropertyHostname_Call { - _c.Call.Return(run) - return _c -} - -// ListConnections provides a mock function with no fields -func (_m *MockSettings) ListConnections() ([]gonetworkmanager.Connection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ListConnections") - } - - var r0 []gonetworkmanager.Connection - var r1 error - if rf, ok := ret.Get(0).(func() ([]gonetworkmanager.Connection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []gonetworkmanager.Connection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]gonetworkmanager.Connection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockSettings_ListConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListConnections' -type MockSettings_ListConnections_Call struct { - *mock.Call -} - -// ListConnections is a helper method to define mock.On call -func (_e *MockSettings_Expecter) ListConnections() *MockSettings_ListConnections_Call { - return &MockSettings_ListConnections_Call{Call: _e.mock.On("ListConnections")} -} - -func (_c *MockSettings_ListConnections_Call) Run(run func()) *MockSettings_ListConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockSettings_ListConnections_Call) Return(_a0 []gonetworkmanager.Connection, _a1 error) *MockSettings_ListConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockSettings_ListConnections_Call) RunAndReturn(run func() ([]gonetworkmanager.Connection, error)) *MockSettings_ListConnections_Call { - _c.Call.Return(run) - return _c -} - -// ReloadConnections provides a mock function with no fields -func (_m *MockSettings) ReloadConnections() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ReloadConnections") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockSettings_ReloadConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReloadConnections' -type MockSettings_ReloadConnections_Call struct { - *mock.Call -} - -// ReloadConnections is a helper method to define mock.On call -func (_e *MockSettings_Expecter) ReloadConnections() *MockSettings_ReloadConnections_Call { - return &MockSettings_ReloadConnections_Call{Call: _e.mock.On("ReloadConnections")} -} - -func (_c *MockSettings_ReloadConnections_Call) Run(run func()) *MockSettings_ReloadConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockSettings_ReloadConnections_Call) Return(_a0 error) *MockSettings_ReloadConnections_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockSettings_ReloadConnections_Call) RunAndReturn(run func() error) *MockSettings_ReloadConnections_Call { - _c.Call.Return(run) - return _c -} - -// SaveHostname provides a mock function with given fields: hostname -func (_m *MockSettings) SaveHostname(hostname string) error { - ret := _m.Called(hostname) - - if len(ret) == 0 { - panic("no return value specified for SaveHostname") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(hostname) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockSettings_SaveHostname_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveHostname' -type MockSettings_SaveHostname_Call struct { - *mock.Call -} - -// SaveHostname is a helper method to define mock.On call -// - hostname string -func (_e *MockSettings_Expecter) SaveHostname(hostname interface{}) *MockSettings_SaveHostname_Call { - return &MockSettings_SaveHostname_Call{Call: _e.mock.On("SaveHostname", hostname)} -} - -func (_c *MockSettings_SaveHostname_Call) Run(run func(hostname string)) *MockSettings_SaveHostname_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockSettings_SaveHostname_Call) Return(_a0 error) *MockSettings_SaveHostname_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockSettings_SaveHostname_Call) RunAndReturn(run func(string) error) *MockSettings_SaveHostname_Call { - _c.Call.Return(run) - return _c -} - -// NewMockSettings creates a new instance of MockSettings. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockSettings(t interface { - mock.TestingT - Cleanup(func()) -}) *MockSettings { - mock := &MockSettings{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5/mock_BusObject.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5/mock_BusObject.go deleted file mode 100644 index c0d1a3f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5/mock_BusObject.go +++ /dev/null @@ -1,649 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package dbus - -import ( - context "context" - - dbus "github.com/godbus/dbus/v5" - mock "github.com/stretchr/testify/mock" -) - -// MockBusObject is an autogenerated mock type for the BusObject type -type MockBusObject struct { - mock.Mock -} - -type MockBusObject_Expecter struct { - mock *mock.Mock -} - -func (_m *MockBusObject) EXPECT() *MockBusObject_Expecter { - return &MockBusObject_Expecter{mock: &_m.Mock} -} - -// AddMatchSignal provides a mock function with given fields: iface, member, options -func (_m *MockBusObject) AddMatchSignal(iface string, member string, options ...dbus.MatchOption) *dbus.Call { - _va := make([]interface{}, len(options)) - for _i := range options { - _va[_i] = options[_i] - } - var _ca []interface{} - _ca = append(_ca, iface, member) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for AddMatchSignal") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(string, string, ...dbus.MatchOption) *dbus.Call); ok { - r0 = rf(iface, member, options...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_AddMatchSignal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddMatchSignal' -type MockBusObject_AddMatchSignal_Call struct { - *mock.Call -} - -// AddMatchSignal is a helper method to define mock.On call -// - iface string -// - member string -// - options ...dbus.MatchOption -func (_e *MockBusObject_Expecter) AddMatchSignal(iface interface{}, member interface{}, options ...interface{}) *MockBusObject_AddMatchSignal_Call { - return &MockBusObject_AddMatchSignal_Call{Call: _e.mock.On("AddMatchSignal", - append([]interface{}{iface, member}, options...)...)} -} - -func (_c *MockBusObject_AddMatchSignal_Call) Run(run func(iface string, member string, options ...dbus.MatchOption)) *MockBusObject_AddMatchSignal_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]dbus.MatchOption, len(args)-2) - for i, a := range args[2:] { - if a != nil { - variadicArgs[i] = a.(dbus.MatchOption) - } - } - run(args[0].(string), args[1].(string), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_AddMatchSignal_Call) Return(_a0 *dbus.Call) *MockBusObject_AddMatchSignal_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_AddMatchSignal_Call) RunAndReturn(run func(string, string, ...dbus.MatchOption) *dbus.Call) *MockBusObject_AddMatchSignal_Call { - _c.Call.Return(run) - return _c -} - -// Call provides a mock function with given fields: method, flags, args -func (_m *MockBusObject) Call(method string, flags dbus.Flags, args ...interface{}) *dbus.Call { - var _ca []interface{} - _ca = append(_ca, method, flags) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for Call") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(string, dbus.Flags, ...interface{}) *dbus.Call); ok { - r0 = rf(method, flags, args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_Call_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Call' -type MockBusObject_Call_Call struct { - *mock.Call -} - -// Call is a helper method to define mock.On call -// - method string -// - flags dbus.Flags -// - args ...interface{} -func (_e *MockBusObject_Expecter) Call(method interface{}, flags interface{}, args ...interface{}) *MockBusObject_Call_Call { - return &MockBusObject_Call_Call{Call: _e.mock.On("Call", - append([]interface{}{method, flags}, args...)...)} -} - -func (_c *MockBusObject_Call_Call) Run(run func(method string, flags dbus.Flags, args ...interface{})) *MockBusObject_Call_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-2) - for i, a := range args[2:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(string), args[1].(dbus.Flags), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_Call_Call) Return(_a0 *dbus.Call) *MockBusObject_Call_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_Call_Call) RunAndReturn(run func(string, dbus.Flags, ...interface{}) *dbus.Call) *MockBusObject_Call_Call { - _c.Call.Return(run) - return _c -} - -// CallWithContext provides a mock function with given fields: ctx, method, flags, args -func (_m *MockBusObject) CallWithContext(ctx context.Context, method string, flags dbus.Flags, args ...interface{}) *dbus.Call { - var _ca []interface{} - _ca = append(_ca, ctx, method, flags) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for CallWithContext") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(context.Context, string, dbus.Flags, ...interface{}) *dbus.Call); ok { - r0 = rf(ctx, method, flags, args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_CallWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallWithContext' -type MockBusObject_CallWithContext_Call struct { - *mock.Call -} - -// CallWithContext is a helper method to define mock.On call -// - ctx context.Context -// - method string -// - flags dbus.Flags -// - args ...interface{} -func (_e *MockBusObject_Expecter) CallWithContext(ctx interface{}, method interface{}, flags interface{}, args ...interface{}) *MockBusObject_CallWithContext_Call { - return &MockBusObject_CallWithContext_Call{Call: _e.mock.On("CallWithContext", - append([]interface{}{ctx, method, flags}, args...)...)} -} - -func (_c *MockBusObject_CallWithContext_Call) Run(run func(ctx context.Context, method string, flags dbus.Flags, args ...interface{})) *MockBusObject_CallWithContext_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-3) - for i, a := range args[3:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(context.Context), args[1].(string), args[2].(dbus.Flags), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_CallWithContext_Call) Return(_a0 *dbus.Call) *MockBusObject_CallWithContext_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_CallWithContext_Call) RunAndReturn(run func(context.Context, string, dbus.Flags, ...interface{}) *dbus.Call) *MockBusObject_CallWithContext_Call { - _c.Call.Return(run) - return _c -} - -// Destination provides a mock function with no fields -func (_m *MockBusObject) Destination() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Destination") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// MockBusObject_Destination_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Destination' -type MockBusObject_Destination_Call struct { - *mock.Call -} - -// Destination is a helper method to define mock.On call -func (_e *MockBusObject_Expecter) Destination() *MockBusObject_Destination_Call { - return &MockBusObject_Destination_Call{Call: _e.mock.On("Destination")} -} - -func (_c *MockBusObject_Destination_Call) Run(run func()) *MockBusObject_Destination_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBusObject_Destination_Call) Return(_a0 string) *MockBusObject_Destination_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_Destination_Call) RunAndReturn(run func() string) *MockBusObject_Destination_Call { - _c.Call.Return(run) - return _c -} - -// GetProperty provides a mock function with given fields: p -func (_m *MockBusObject) GetProperty(p string) (dbus.Variant, error) { - ret := _m.Called(p) - - if len(ret) == 0 { - panic("no return value specified for GetProperty") - } - - var r0 dbus.Variant - var r1 error - if rf, ok := ret.Get(0).(func(string) (dbus.Variant, error)); ok { - return rf(p) - } - if rf, ok := ret.Get(0).(func(string) dbus.Variant); ok { - r0 = rf(p) - } else { - r0 = ret.Get(0).(dbus.Variant) - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(p) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBusObject_GetProperty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetProperty' -type MockBusObject_GetProperty_Call struct { - *mock.Call -} - -// GetProperty is a helper method to define mock.On call -// - p string -func (_e *MockBusObject_Expecter) GetProperty(p interface{}) *MockBusObject_GetProperty_Call { - return &MockBusObject_GetProperty_Call{Call: _e.mock.On("GetProperty", p)} -} - -func (_c *MockBusObject_GetProperty_Call) Run(run func(p string)) *MockBusObject_GetProperty_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBusObject_GetProperty_Call) Return(_a0 dbus.Variant, _a1 error) *MockBusObject_GetProperty_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBusObject_GetProperty_Call) RunAndReturn(run func(string) (dbus.Variant, error)) *MockBusObject_GetProperty_Call { - _c.Call.Return(run) - return _c -} - -// Go provides a mock function with given fields: method, flags, ch, args -func (_m *MockBusObject) Go(method string, flags dbus.Flags, ch chan *dbus.Call, args ...interface{}) *dbus.Call { - var _ca []interface{} - _ca = append(_ca, method, flags, ch) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for Go") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(string, dbus.Flags, chan *dbus.Call, ...interface{}) *dbus.Call); ok { - r0 = rf(method, flags, ch, args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_Go_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Go' -type MockBusObject_Go_Call struct { - *mock.Call -} - -// Go is a helper method to define mock.On call -// - method string -// - flags dbus.Flags -// - ch chan *dbus.Call -// - args ...interface{} -func (_e *MockBusObject_Expecter) Go(method interface{}, flags interface{}, ch interface{}, args ...interface{}) *MockBusObject_Go_Call { - return &MockBusObject_Go_Call{Call: _e.mock.On("Go", - append([]interface{}{method, flags, ch}, args...)...)} -} - -func (_c *MockBusObject_Go_Call) Run(run func(method string, flags dbus.Flags, ch chan *dbus.Call, args ...interface{})) *MockBusObject_Go_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-3) - for i, a := range args[3:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(string), args[1].(dbus.Flags), args[2].(chan *dbus.Call), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_Go_Call) Return(_a0 *dbus.Call) *MockBusObject_Go_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_Go_Call) RunAndReturn(run func(string, dbus.Flags, chan *dbus.Call, ...interface{}) *dbus.Call) *MockBusObject_Go_Call { - _c.Call.Return(run) - return _c -} - -// GoWithContext provides a mock function with given fields: ctx, method, flags, ch, args -func (_m *MockBusObject) GoWithContext(ctx context.Context, method string, flags dbus.Flags, ch chan *dbus.Call, args ...interface{}) *dbus.Call { - var _ca []interface{} - _ca = append(_ca, ctx, method, flags, ch) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for GoWithContext") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(context.Context, string, dbus.Flags, chan *dbus.Call, ...interface{}) *dbus.Call); ok { - r0 = rf(ctx, method, flags, ch, args...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_GoWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GoWithContext' -type MockBusObject_GoWithContext_Call struct { - *mock.Call -} - -// GoWithContext is a helper method to define mock.On call -// - ctx context.Context -// - method string -// - flags dbus.Flags -// - ch chan *dbus.Call -// - args ...interface{} -func (_e *MockBusObject_Expecter) GoWithContext(ctx interface{}, method interface{}, flags interface{}, ch interface{}, args ...interface{}) *MockBusObject_GoWithContext_Call { - return &MockBusObject_GoWithContext_Call{Call: _e.mock.On("GoWithContext", - append([]interface{}{ctx, method, flags, ch}, args...)...)} -} - -func (_c *MockBusObject_GoWithContext_Call) Run(run func(ctx context.Context, method string, flags dbus.Flags, ch chan *dbus.Call, args ...interface{})) *MockBusObject_GoWithContext_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-4) - for i, a := range args[4:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(context.Context), args[1].(string), args[2].(dbus.Flags), args[3].(chan *dbus.Call), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_GoWithContext_Call) Return(_a0 *dbus.Call) *MockBusObject_GoWithContext_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_GoWithContext_Call) RunAndReturn(run func(context.Context, string, dbus.Flags, chan *dbus.Call, ...interface{}) *dbus.Call) *MockBusObject_GoWithContext_Call { - _c.Call.Return(run) - return _c -} - -// Path provides a mock function with no fields -func (_m *MockBusObject) Path() dbus.ObjectPath { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Path") - } - - var r0 dbus.ObjectPath - if rf, ok := ret.Get(0).(func() dbus.ObjectPath); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(dbus.ObjectPath) - } - - return r0 -} - -// MockBusObject_Path_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Path' -type MockBusObject_Path_Call struct { - *mock.Call -} - -// Path is a helper method to define mock.On call -func (_e *MockBusObject_Expecter) Path() *MockBusObject_Path_Call { - return &MockBusObject_Path_Call{Call: _e.mock.On("Path")} -} - -func (_c *MockBusObject_Path_Call) Run(run func()) *MockBusObject_Path_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBusObject_Path_Call) Return(_a0 dbus.ObjectPath) *MockBusObject_Path_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_Path_Call) RunAndReturn(run func() dbus.ObjectPath) *MockBusObject_Path_Call { - _c.Call.Return(run) - return _c -} - -// RemoveMatchSignal provides a mock function with given fields: iface, member, options -func (_m *MockBusObject) RemoveMatchSignal(iface string, member string, options ...dbus.MatchOption) *dbus.Call { - _va := make([]interface{}, len(options)) - for _i := range options { - _va[_i] = options[_i] - } - var _ca []interface{} - _ca = append(_ca, iface, member) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for RemoveMatchSignal") - } - - var r0 *dbus.Call - if rf, ok := ret.Get(0).(func(string, string, ...dbus.MatchOption) *dbus.Call); ok { - r0 = rf(iface, member, options...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*dbus.Call) - } - } - - return r0 -} - -// MockBusObject_RemoveMatchSignal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveMatchSignal' -type MockBusObject_RemoveMatchSignal_Call struct { - *mock.Call -} - -// RemoveMatchSignal is a helper method to define mock.On call -// - iface string -// - member string -// - options ...dbus.MatchOption -func (_e *MockBusObject_Expecter) RemoveMatchSignal(iface interface{}, member interface{}, options ...interface{}) *MockBusObject_RemoveMatchSignal_Call { - return &MockBusObject_RemoveMatchSignal_Call{Call: _e.mock.On("RemoveMatchSignal", - append([]interface{}{iface, member}, options...)...)} -} - -func (_c *MockBusObject_RemoveMatchSignal_Call) Run(run func(iface string, member string, options ...dbus.MatchOption)) *MockBusObject_RemoveMatchSignal_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]dbus.MatchOption, len(args)-2) - for i, a := range args[2:] { - if a != nil { - variadicArgs[i] = a.(dbus.MatchOption) - } - } - run(args[0].(string), args[1].(string), variadicArgs...) - }) - return _c -} - -func (_c *MockBusObject_RemoveMatchSignal_Call) Return(_a0 *dbus.Call) *MockBusObject_RemoveMatchSignal_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_RemoveMatchSignal_Call) RunAndReturn(run func(string, string, ...dbus.MatchOption) *dbus.Call) *MockBusObject_RemoveMatchSignal_Call { - _c.Call.Return(run) - return _c -} - -// SetProperty provides a mock function with given fields: p, v -func (_m *MockBusObject) SetProperty(p string, v interface{}) error { - ret := _m.Called(p, v) - - if len(ret) == 0 { - panic("no return value specified for SetProperty") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { - r0 = rf(p, v) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBusObject_SetProperty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetProperty' -type MockBusObject_SetProperty_Call struct { - *mock.Call -} - -// SetProperty is a helper method to define mock.On call -// - p string -// - v interface{} -func (_e *MockBusObject_Expecter) SetProperty(p interface{}, v interface{}) *MockBusObject_SetProperty_Call { - return &MockBusObject_SetProperty_Call{Call: _e.mock.On("SetProperty", p, v)} -} - -func (_c *MockBusObject_SetProperty_Call) Run(run func(p string, v interface{})) *MockBusObject_SetProperty_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(interface{})) - }) - return _c -} - -func (_c *MockBusObject_SetProperty_Call) Return(_a0 error) *MockBusObject_SetProperty_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_SetProperty_Call) RunAndReturn(run func(string, interface{}) error) *MockBusObject_SetProperty_Call { - _c.Call.Return(run) - return _c -} - -// StoreProperty provides a mock function with given fields: p, value -func (_m *MockBusObject) StoreProperty(p string, value interface{}) error { - ret := _m.Called(p, value) - - if len(ret) == 0 { - panic("no return value specified for StoreProperty") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { - r0 = rf(p, value) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBusObject_StoreProperty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StoreProperty' -type MockBusObject_StoreProperty_Call struct { - *mock.Call -} - -// StoreProperty is a helper method to define mock.On call -// - p string -// - value interface{} -func (_e *MockBusObject_Expecter) StoreProperty(p interface{}, value interface{}) *MockBusObject_StoreProperty_Call { - return &MockBusObject_StoreProperty_Call{Call: _e.mock.On("StoreProperty", p, value)} -} - -func (_c *MockBusObject_StoreProperty_Call) Run(run func(p string, value interface{})) *MockBusObject_StoreProperty_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(interface{})) - }) - return _c -} - -func (_c *MockBusObject_StoreProperty_Call) Return(_a0 error) *MockBusObject_StoreProperty_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBusObject_StoreProperty_Call) RunAndReturn(run func(string, interface{}) error) *MockBusObject_StoreProperty_Call { - _c.Call.Return(run) - return _c -} - -// NewMockBusObject creates a new instance of MockBusObject. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockBusObject(t interface { - mock.TestingT - Cleanup(func()) -}) *MockBusObject { - mock := &MockBusObject{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/internal/plugins/mock_GitClient.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/internal/plugins/mock_GitClient.go deleted file mode 100644 index 23e34f3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/internal/plugins/mock_GitClient.go +++ /dev/null @@ -1,181 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package plugins - -import mock "github.com/stretchr/testify/mock" - -// MockGitClient is an autogenerated mock type for the GitClient type -type MockGitClient struct { - mock.Mock -} - -type MockGitClient_Expecter struct { - mock *mock.Mock -} - -func (_m *MockGitClient) EXPECT() *MockGitClient_Expecter { - return &MockGitClient_Expecter{mock: &_m.Mock} -} - -// HasUpdates provides a mock function with given fields: path -func (_m *MockGitClient) HasUpdates(path string) (bool, error) { - ret := _m.Called(path) - - if len(ret) == 0 { - panic("no return value specified for HasUpdates") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(string) (bool, error)); ok { - return rf(path) - } - if rf, ok := ret.Get(0).(func(string) bool); ok { - r0 = rf(path) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(path) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockGitClient_HasUpdates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HasUpdates' -type MockGitClient_HasUpdates_Call struct { - *mock.Call -} - -// HasUpdates is a helper method to define mock.On call -// - path string -func (_e *MockGitClient_Expecter) HasUpdates(path any) *MockGitClient_HasUpdates_Call { - return &MockGitClient_HasUpdates_Call{Call: _e.mock.On("HasUpdates", path)} -} - -func (_c *MockGitClient_HasUpdates_Call) Run(run func(path string)) *MockGitClient_HasUpdates_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockGitClient_HasUpdates_Call) Return(_a0 bool, _a1 error) *MockGitClient_HasUpdates_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockGitClient_HasUpdates_Call) RunAndReturn(run func(string) (bool, error)) *MockGitClient_HasUpdates_Call { - _c.Call.Return(run) - return _c -} - -// PlainClone provides a mock function with given fields: path, url -func (_m *MockGitClient) PlainClone(path string, url string) error { - ret := _m.Called(path, url) - - if len(ret) == 0 { - panic("no return value specified for PlainClone") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string) error); ok { - r0 = rf(path, url) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockGitClient_PlainClone_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PlainClone' -type MockGitClient_PlainClone_Call struct { - *mock.Call -} - -// PlainClone is a helper method to define mock.On call -// - path string -// - url string -func (_e *MockGitClient_Expecter) PlainClone(path any, url any) *MockGitClient_PlainClone_Call { - return &MockGitClient_PlainClone_Call{Call: _e.mock.On("PlainClone", path, url)} -} - -func (_c *MockGitClient_PlainClone_Call) Run(run func(path string, url string)) *MockGitClient_PlainClone_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockGitClient_PlainClone_Call) Return(_a0 error) *MockGitClient_PlainClone_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockGitClient_PlainClone_Call) RunAndReturn(run func(string, string) error) *MockGitClient_PlainClone_Call { - _c.Call.Return(run) - return _c -} - -// Pull provides a mock function with given fields: path -func (_m *MockGitClient) Pull(path string) error { - ret := _m.Called(path) - - if len(ret) == 0 { - panic("no return value specified for Pull") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(path) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockGitClient_Pull_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Pull' -type MockGitClient_Pull_Call struct { - *mock.Call -} - -// Pull is a helper method to define mock.On call -// - path string -func (_e *MockGitClient_Expecter) Pull(path any) *MockGitClient_Pull_Call { - return &MockGitClient_Pull_Call{Call: _e.mock.On("Pull", path)} -} - -func (_c *MockGitClient_Pull_Call) Run(run func(path string)) *MockGitClient_Pull_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockGitClient_Pull_Call) Return(_a0 error) *MockGitClient_Pull_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockGitClient_Pull_Call) RunAndReturn(run func(string) error) *MockGitClient_Pull_Call { - _c.Call.Return(run) - return _c -} - -// NewMockGitClient creates a new instance of MockGitClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockGitClient(t interface { - mock.TestingT - Cleanup(func()) -}) *MockGitClient { - mock := &MockGitClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/net/mock_Conn.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/net/mock_Conn.go deleted file mode 100644 index 248a0d8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/net/mock_Conn.go +++ /dev/null @@ -1,427 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package net - -import ( - net "net" - - mock "github.com/stretchr/testify/mock" - - time "time" -) - -// MockConn is an autogenerated mock type for the Conn type -type MockConn struct { - mock.Mock -} - -type MockConn_Expecter struct { - mock *mock.Mock -} - -func (_m *MockConn) EXPECT() *MockConn_Expecter { - return &MockConn_Expecter{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *MockConn) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConn_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockConn_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockConn_Expecter) Close() *MockConn_Close_Call { - return &MockConn_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockConn_Close_Call) Run(run func()) *MockConn_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConn_Close_Call) Return(_a0 error) *MockConn_Close_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_Close_Call) RunAndReturn(run func() error) *MockConn_Close_Call { - _c.Call.Return(run) - return _c -} - -// LocalAddr provides a mock function with no fields -func (_m *MockConn) LocalAddr() net.Addr { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for LocalAddr") - } - - var r0 net.Addr - if rf, ok := ret.Get(0).(func() net.Addr); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(net.Addr) - } - } - - return r0 -} - -// MockConn_LocalAddr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalAddr' -type MockConn_LocalAddr_Call struct { - *mock.Call -} - -// LocalAddr is a helper method to define mock.On call -func (_e *MockConn_Expecter) LocalAddr() *MockConn_LocalAddr_Call { - return &MockConn_LocalAddr_Call{Call: _e.mock.On("LocalAddr")} -} - -func (_c *MockConn_LocalAddr_Call) Run(run func()) *MockConn_LocalAddr_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConn_LocalAddr_Call) Return(_a0 net.Addr) *MockConn_LocalAddr_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_LocalAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_LocalAddr_Call { - _c.Call.Return(run) - return _c -} - -// Read provides a mock function with given fields: b -func (_m *MockConn) Read(b []byte) (int, error) { - ret := _m.Called(b) - - if len(ret) == 0 { - panic("no return value specified for Read") - } - - var r0 int - var r1 error - if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { - return rf(b) - } - if rf, ok := ret.Get(0).(func([]byte) int); ok { - r0 = rf(b) - } else { - r0 = ret.Get(0).(int) - } - - if rf, ok := ret.Get(1).(func([]byte) error); ok { - r1 = rf(b) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConn_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' -type MockConn_Read_Call struct { - *mock.Call -} - -// Read is a helper method to define mock.On call -// - b []byte -func (_e *MockConn_Expecter) Read(b interface{}) *MockConn_Read_Call { - return &MockConn_Read_Call{Call: _e.mock.On("Read", b)} -} - -func (_c *MockConn_Read_Call) Run(run func(b []byte)) *MockConn_Read_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]byte)) - }) - return _c -} - -func (_c *MockConn_Read_Call) Return(n int, err error) *MockConn_Read_Call { - _c.Call.Return(n, err) - return _c -} - -func (_c *MockConn_Read_Call) RunAndReturn(run func([]byte) (int, error)) *MockConn_Read_Call { - _c.Call.Return(run) - return _c -} - -// RemoteAddr provides a mock function with no fields -func (_m *MockConn) RemoteAddr() net.Addr { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for RemoteAddr") - } - - var r0 net.Addr - if rf, ok := ret.Get(0).(func() net.Addr); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(net.Addr) - } - } - - return r0 -} - -// MockConn_RemoteAddr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteAddr' -type MockConn_RemoteAddr_Call struct { - *mock.Call -} - -// RemoteAddr is a helper method to define mock.On call -func (_e *MockConn_Expecter) RemoteAddr() *MockConn_RemoteAddr_Call { - return &MockConn_RemoteAddr_Call{Call: _e.mock.On("RemoteAddr")} -} - -func (_c *MockConn_RemoteAddr_Call) Run(run func()) *MockConn_RemoteAddr_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockConn_RemoteAddr_Call) Return(_a0 net.Addr) *MockConn_RemoteAddr_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_RemoteAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_RemoteAddr_Call { - _c.Call.Return(run) - return _c -} - -// SetDeadline provides a mock function with given fields: t -func (_m *MockConn) SetDeadline(t time.Time) error { - ret := _m.Called(t) - - if len(ret) == 0 { - panic("no return value specified for SetDeadline") - } - - var r0 error - if rf, ok := ret.Get(0).(func(time.Time) error); ok { - r0 = rf(t) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConn_SetDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetDeadline' -type MockConn_SetDeadline_Call struct { - *mock.Call -} - -// SetDeadline is a helper method to define mock.On call -// - t time.Time -func (_e *MockConn_Expecter) SetDeadline(t interface{}) *MockConn_SetDeadline_Call { - return &MockConn_SetDeadline_Call{Call: _e.mock.On("SetDeadline", t)} -} - -func (_c *MockConn_SetDeadline_Call) Run(run func(t time.Time)) *MockConn_SetDeadline_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(time.Time)) - }) - return _c -} - -func (_c *MockConn_SetDeadline_Call) Return(_a0 error) *MockConn_SetDeadline_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_SetDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetDeadline_Call { - _c.Call.Return(run) - return _c -} - -// SetReadDeadline provides a mock function with given fields: t -func (_m *MockConn) SetReadDeadline(t time.Time) error { - ret := _m.Called(t) - - if len(ret) == 0 { - panic("no return value specified for SetReadDeadline") - } - - var r0 error - if rf, ok := ret.Get(0).(func(time.Time) error); ok { - r0 = rf(t) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConn_SetReadDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetReadDeadline' -type MockConn_SetReadDeadline_Call struct { - *mock.Call -} - -// SetReadDeadline is a helper method to define mock.On call -// - t time.Time -func (_e *MockConn_Expecter) SetReadDeadline(t interface{}) *MockConn_SetReadDeadline_Call { - return &MockConn_SetReadDeadline_Call{Call: _e.mock.On("SetReadDeadline", t)} -} - -func (_c *MockConn_SetReadDeadline_Call) Run(run func(t time.Time)) *MockConn_SetReadDeadline_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(time.Time)) - }) - return _c -} - -func (_c *MockConn_SetReadDeadline_Call) Return(_a0 error) *MockConn_SetReadDeadline_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_SetReadDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetReadDeadline_Call { - _c.Call.Return(run) - return _c -} - -// SetWriteDeadline provides a mock function with given fields: t -func (_m *MockConn) SetWriteDeadline(t time.Time) error { - ret := _m.Called(t) - - if len(ret) == 0 { - panic("no return value specified for SetWriteDeadline") - } - - var r0 error - if rf, ok := ret.Get(0).(func(time.Time) error); ok { - r0 = rf(t) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockConn_SetWriteDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWriteDeadline' -type MockConn_SetWriteDeadline_Call struct { - *mock.Call -} - -// SetWriteDeadline is a helper method to define mock.On call -// - t time.Time -func (_e *MockConn_Expecter) SetWriteDeadline(t interface{}) *MockConn_SetWriteDeadline_Call { - return &MockConn_SetWriteDeadline_Call{Call: _e.mock.On("SetWriteDeadline", t)} -} - -func (_c *MockConn_SetWriteDeadline_Call) Run(run func(t time.Time)) *MockConn_SetWriteDeadline_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(time.Time)) - }) - return _c -} - -func (_c *MockConn_SetWriteDeadline_Call) Return(_a0 error) *MockConn_SetWriteDeadline_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockConn_SetWriteDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetWriteDeadline_Call { - _c.Call.Return(run) - return _c -} - -// Write provides a mock function with given fields: b -func (_m *MockConn) Write(b []byte) (int, error) { - ret := _m.Called(b) - - if len(ret) == 0 { - panic("no return value specified for Write") - } - - var r0 int - var r1 error - if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { - return rf(b) - } - if rf, ok := ret.Get(0).(func([]byte) int); ok { - r0 = rf(b) - } else { - r0 = ret.Get(0).(int) - } - - if rf, ok := ret.Get(1).(func([]byte) error); ok { - r1 = rf(b) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockConn_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write' -type MockConn_Write_Call struct { - *mock.Call -} - -// Write is a helper method to define mock.On call -// - b []byte -func (_e *MockConn_Expecter) Write(b interface{}) *MockConn_Write_Call { - return &MockConn_Write_Call{Call: _e.mock.On("Write", b)} -} - -func (_c *MockConn_Write_Call) Run(run func(b []byte)) *MockConn_Write_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]byte)) - }) - return _c -} - -func (_c *MockConn_Write_Call) Return(n int, err error) *MockConn_Write_Call { - _c.Call.Return(n, err) - return _c -} - -func (_c *MockConn_Write_Call) RunAndReturn(run func([]byte) (int, error)) *MockConn_Write_Call { - _c.Call.Return(run) - return _c -} - -// NewMockConn creates a new instance of MockConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockConn(t interface { - mock.TestingT - Cleanup(func()) -}) *MockConn { - mock := &MockConn{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/network/mock_Backend.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/network/mock_Backend.go deleted file mode 100644 index d7eb2df..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/network/mock_Backend.go +++ /dev/null @@ -1,1975 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_network - -import ( - network "github.com/AvengeMedia/DankMaterialShell/core/internal/server/network" - mock "github.com/stretchr/testify/mock" -) - -// MockBackend is an autogenerated mock type for the Backend type -type MockBackend struct { - mock.Mock -} - -type MockBackend_Expecter struct { - mock *mock.Mock -} - -func (_m *MockBackend) EXPECT() *MockBackend_Expecter { - return &MockBackend_Expecter{mock: &_m.Mock} -} - -// ActivateWiredConnection provides a mock function with given fields: uuid -func (_m *MockBackend) ActivateWiredConnection(uuid string) error { - ret := _m.Called(uuid) - - if len(ret) == 0 { - panic("no return value specified for ActivateWiredConnection") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(uuid) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ActivateWiredConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ActivateWiredConnection' -type MockBackend_ActivateWiredConnection_Call struct { - *mock.Call -} - -// ActivateWiredConnection is a helper method to define mock.On call -// - uuid string -func (_e *MockBackend_Expecter) ActivateWiredConnection(uuid interface{}) *MockBackend_ActivateWiredConnection_Call { - return &MockBackend_ActivateWiredConnection_Call{Call: _e.mock.On("ActivateWiredConnection", uuid)} -} - -func (_c *MockBackend_ActivateWiredConnection_Call) Run(run func(uuid string)) *MockBackend_ActivateWiredConnection_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_ActivateWiredConnection_Call) Return(_a0 error) *MockBackend_ActivateWiredConnection_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ActivateWiredConnection_Call) RunAndReturn(run func(string) error) *MockBackend_ActivateWiredConnection_Call { - _c.Call.Return(run) - return _c -} - -// CancelCredentials provides a mock function with given fields: token -func (_m *MockBackend) CancelCredentials(token string) error { - ret := _m.Called(token) - - if len(ret) == 0 { - panic("no return value specified for CancelCredentials") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(token) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_CancelCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelCredentials' -type MockBackend_CancelCredentials_Call struct { - *mock.Call -} - -// CancelCredentials is a helper method to define mock.On call -// - token string -func (_e *MockBackend_Expecter) CancelCredentials(token interface{}) *MockBackend_CancelCredentials_Call { - return &MockBackend_CancelCredentials_Call{Call: _e.mock.On("CancelCredentials", token)} -} - -func (_c *MockBackend_CancelCredentials_Call) Run(run func(token string)) *MockBackend_CancelCredentials_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_CancelCredentials_Call) Return(_a0 error) *MockBackend_CancelCredentials_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_CancelCredentials_Call) RunAndReturn(run func(string) error) *MockBackend_CancelCredentials_Call { - _c.Call.Return(run) - return _c -} - -// ClearVPNCredentials provides a mock function with given fields: uuidOrName -func (_m *MockBackend) ClearVPNCredentials(uuidOrName string) error { - ret := _m.Called(uuidOrName) - - if len(ret) == 0 { - panic("no return value specified for ClearVPNCredentials") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(uuidOrName) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ClearVPNCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClearVPNCredentials' -type MockBackend_ClearVPNCredentials_Call struct { - *mock.Call -} - -// ClearVPNCredentials is a helper method to define mock.On call -// - uuidOrName string -func (_e *MockBackend_Expecter) ClearVPNCredentials(uuidOrName interface{}) *MockBackend_ClearVPNCredentials_Call { - return &MockBackend_ClearVPNCredentials_Call{Call: _e.mock.On("ClearVPNCredentials", uuidOrName)} -} - -func (_c *MockBackend_ClearVPNCredentials_Call) Run(run func(uuidOrName string)) *MockBackend_ClearVPNCredentials_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_ClearVPNCredentials_Call) Return(_a0 error) *MockBackend_ClearVPNCredentials_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ClearVPNCredentials_Call) RunAndReturn(run func(string) error) *MockBackend_ClearVPNCredentials_Call { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with no fields -func (_m *MockBackend) Close() { - _m.Called() -} - -// MockBackend_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockBackend_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockBackend_Expecter) Close() *MockBackend_Close_Call { - return &MockBackend_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockBackend_Close_Call) Run(run func()) *MockBackend_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_Close_Call) Return() *MockBackend_Close_Call { - _c.Call.Return() - return _c -} - -func (_c *MockBackend_Close_Call) RunAndReturn(run func()) *MockBackend_Close_Call { - _c.Run(run) - return _c -} - -// ConnectEthernet provides a mock function with no fields -func (_m *MockBackend) ConnectEthernet() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ConnectEthernet") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ConnectEthernet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConnectEthernet' -type MockBackend_ConnectEthernet_Call struct { - *mock.Call -} - -// ConnectEthernet is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ConnectEthernet() *MockBackend_ConnectEthernet_Call { - return &MockBackend_ConnectEthernet_Call{Call: _e.mock.On("ConnectEthernet")} -} - -func (_c *MockBackend_ConnectEthernet_Call) Run(run func()) *MockBackend_ConnectEthernet_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ConnectEthernet_Call) Return(_a0 error) *MockBackend_ConnectEthernet_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ConnectEthernet_Call) RunAndReturn(run func() error) *MockBackend_ConnectEthernet_Call { - _c.Call.Return(run) - return _c -} - -// ConnectVPN provides a mock function with given fields: uuidOrName, singleActive -func (_m *MockBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - ret := _m.Called(uuidOrName, singleActive) - - if len(ret) == 0 { - panic("no return value specified for ConnectVPN") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(uuidOrName, singleActive) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ConnectVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConnectVPN' -type MockBackend_ConnectVPN_Call struct { - *mock.Call -} - -// ConnectVPN is a helper method to define mock.On call -// - uuidOrName string -// - singleActive bool -func (_e *MockBackend_Expecter) ConnectVPN(uuidOrName interface{}, singleActive interface{}) *MockBackend_ConnectVPN_Call { - return &MockBackend_ConnectVPN_Call{Call: _e.mock.On("ConnectVPN", uuidOrName, singleActive)} -} - -func (_c *MockBackend_ConnectVPN_Call) Run(run func(uuidOrName string, singleActive bool)) *MockBackend_ConnectVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockBackend_ConnectVPN_Call) Return(_a0 error) *MockBackend_ConnectVPN_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ConnectVPN_Call) RunAndReturn(run func(string, bool) error) *MockBackend_ConnectVPN_Call { - _c.Call.Return(run) - return _c -} - -// ConnectWiFi provides a mock function with given fields: req -func (_m *MockBackend) ConnectWiFi(req network.ConnectionRequest) error { - ret := _m.Called(req) - - if len(ret) == 0 { - panic("no return value specified for ConnectWiFi") - } - - var r0 error - if rf, ok := ret.Get(0).(func(network.ConnectionRequest) error); ok { - r0 = rf(req) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ConnectWiFi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConnectWiFi' -type MockBackend_ConnectWiFi_Call struct { - *mock.Call -} - -// ConnectWiFi is a helper method to define mock.On call -// - req network.ConnectionRequest -func (_e *MockBackend_Expecter) ConnectWiFi(req interface{}) *MockBackend_ConnectWiFi_Call { - return &MockBackend_ConnectWiFi_Call{Call: _e.mock.On("ConnectWiFi", req)} -} - -func (_c *MockBackend_ConnectWiFi_Call) Run(run func(req network.ConnectionRequest)) *MockBackend_ConnectWiFi_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(network.ConnectionRequest)) - }) - return _c -} - -func (_c *MockBackend_ConnectWiFi_Call) Return(_a0 error) *MockBackend_ConnectWiFi_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ConnectWiFi_Call) RunAndReturn(run func(network.ConnectionRequest) error) *MockBackend_ConnectWiFi_Call { - _c.Call.Return(run) - return _c -} - -// DeleteVPN provides a mock function with given fields: uuidOrName -func (_m *MockBackend) DeleteVPN(uuidOrName string) error { - ret := _m.Called(uuidOrName) - - if len(ret) == 0 { - panic("no return value specified for DeleteVPN") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(uuidOrName) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DeleteVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteVPN' -type MockBackend_DeleteVPN_Call struct { - *mock.Call -} - -// DeleteVPN is a helper method to define mock.On call -// - uuidOrName string -func (_e *MockBackend_Expecter) DeleteVPN(uuidOrName interface{}) *MockBackend_DeleteVPN_Call { - return &MockBackend_DeleteVPN_Call{Call: _e.mock.On("DeleteVPN", uuidOrName)} -} - -func (_c *MockBackend_DeleteVPN_Call) Run(run func(uuidOrName string)) *MockBackend_DeleteVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_DeleteVPN_Call) Return(_a0 error) *MockBackend_DeleteVPN_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DeleteVPN_Call) RunAndReturn(run func(string) error) *MockBackend_DeleteVPN_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectAllVPN provides a mock function with no fields -func (_m *MockBackend) DisconnectAllVPN() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DisconnectAllVPN") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectAllVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectAllVPN' -type MockBackend_DisconnectAllVPN_Call struct { - *mock.Call -} - -// DisconnectAllVPN is a helper method to define mock.On call -func (_e *MockBackend_Expecter) DisconnectAllVPN() *MockBackend_DisconnectAllVPN_Call { - return &MockBackend_DisconnectAllVPN_Call{Call: _e.mock.On("DisconnectAllVPN")} -} - -func (_c *MockBackend_DisconnectAllVPN_Call) Run(run func()) *MockBackend_DisconnectAllVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_DisconnectAllVPN_Call) Return(_a0 error) *MockBackend_DisconnectAllVPN_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectAllVPN_Call) RunAndReturn(run func() error) *MockBackend_DisconnectAllVPN_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectEthernet provides a mock function with no fields -func (_m *MockBackend) DisconnectEthernet() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DisconnectEthernet") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectEthernet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectEthernet' -type MockBackend_DisconnectEthernet_Call struct { - *mock.Call -} - -// DisconnectEthernet is a helper method to define mock.On call -func (_e *MockBackend_Expecter) DisconnectEthernet() *MockBackend_DisconnectEthernet_Call { - return &MockBackend_DisconnectEthernet_Call{Call: _e.mock.On("DisconnectEthernet")} -} - -func (_c *MockBackend_DisconnectEthernet_Call) Run(run func()) *MockBackend_DisconnectEthernet_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_DisconnectEthernet_Call) Return(_a0 error) *MockBackend_DisconnectEthernet_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectEthernet_Call) RunAndReturn(run func() error) *MockBackend_DisconnectEthernet_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectEthernetDevice provides a mock function with given fields: device -func (_m *MockBackend) DisconnectEthernetDevice(device string) error { - ret := _m.Called(device) - - if len(ret) == 0 { - panic("no return value specified for DisconnectEthernetDevice") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(device) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectEthernetDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectEthernetDevice' -type MockBackend_DisconnectEthernetDevice_Call struct { - *mock.Call -} - -// DisconnectEthernetDevice is a helper method to define mock.On call -// - device string -func (_e *MockBackend_Expecter) DisconnectEthernetDevice(device interface{}) *MockBackend_DisconnectEthernetDevice_Call { - return &MockBackend_DisconnectEthernetDevice_Call{Call: _e.mock.On("DisconnectEthernetDevice", device)} -} - -func (_c *MockBackend_DisconnectEthernetDevice_Call) Run(run func(device string)) *MockBackend_DisconnectEthernetDevice_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_DisconnectEthernetDevice_Call) Return(_a0 error) *MockBackend_DisconnectEthernetDevice_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectEthernetDevice_Call) RunAndReturn(run func(string) error) *MockBackend_DisconnectEthernetDevice_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectVPN provides a mock function with given fields: uuidOrName -func (_m *MockBackend) DisconnectVPN(uuidOrName string) error { - ret := _m.Called(uuidOrName) - - if len(ret) == 0 { - panic("no return value specified for DisconnectVPN") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(uuidOrName) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectVPN' -type MockBackend_DisconnectVPN_Call struct { - *mock.Call -} - -// DisconnectVPN is a helper method to define mock.On call -// - uuidOrName string -func (_e *MockBackend_Expecter) DisconnectVPN(uuidOrName interface{}) *MockBackend_DisconnectVPN_Call { - return &MockBackend_DisconnectVPN_Call{Call: _e.mock.On("DisconnectVPN", uuidOrName)} -} - -func (_c *MockBackend_DisconnectVPN_Call) Run(run func(uuidOrName string)) *MockBackend_DisconnectVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_DisconnectVPN_Call) Return(_a0 error) *MockBackend_DisconnectVPN_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectVPN_Call) RunAndReturn(run func(string) error) *MockBackend_DisconnectVPN_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectWiFi provides a mock function with no fields -func (_m *MockBackend) DisconnectWiFi() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DisconnectWiFi") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectWiFi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectWiFi' -type MockBackend_DisconnectWiFi_Call struct { - *mock.Call -} - -// DisconnectWiFi is a helper method to define mock.On call -func (_e *MockBackend_Expecter) DisconnectWiFi() *MockBackend_DisconnectWiFi_Call { - return &MockBackend_DisconnectWiFi_Call{Call: _e.mock.On("DisconnectWiFi")} -} - -func (_c *MockBackend_DisconnectWiFi_Call) Run(run func()) *MockBackend_DisconnectWiFi_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_DisconnectWiFi_Call) Return(_a0 error) *MockBackend_DisconnectWiFi_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectWiFi_Call) RunAndReturn(run func() error) *MockBackend_DisconnectWiFi_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectWiFiDevice provides a mock function with given fields: device -func (_m *MockBackend) DisconnectWiFiDevice(device string) error { - ret := _m.Called(device) - - if len(ret) == 0 { - panic("no return value specified for DisconnectWiFiDevice") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(device) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_DisconnectWiFiDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectWiFiDevice' -type MockBackend_DisconnectWiFiDevice_Call struct { - *mock.Call -} - -// DisconnectWiFiDevice is a helper method to define mock.On call -// - device string -func (_e *MockBackend_Expecter) DisconnectWiFiDevice(device interface{}) *MockBackend_DisconnectWiFiDevice_Call { - return &MockBackend_DisconnectWiFiDevice_Call{Call: _e.mock.On("DisconnectWiFiDevice", device)} -} - -func (_c *MockBackend_DisconnectWiFiDevice_Call) Run(run func(device string)) *MockBackend_DisconnectWiFiDevice_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_DisconnectWiFiDevice_Call) Return(_a0 error) *MockBackend_DisconnectWiFiDevice_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_DisconnectWiFiDevice_Call) RunAndReturn(run func(string) error) *MockBackend_DisconnectWiFiDevice_Call { - _c.Call.Return(run) - return _c -} - -// ForgetWiFiNetwork provides a mock function with given fields: ssid -func (_m *MockBackend) ForgetWiFiNetwork(ssid string) error { - ret := _m.Called(ssid) - - if len(ret) == 0 { - panic("no return value specified for ForgetWiFiNetwork") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(ssid) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ForgetWiFiNetwork_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ForgetWiFiNetwork' -type MockBackend_ForgetWiFiNetwork_Call struct { - *mock.Call -} - -// ForgetWiFiNetwork is a helper method to define mock.On call -// - ssid string -func (_e *MockBackend_Expecter) ForgetWiFiNetwork(ssid interface{}) *MockBackend_ForgetWiFiNetwork_Call { - return &MockBackend_ForgetWiFiNetwork_Call{Call: _e.mock.On("ForgetWiFiNetwork", ssid)} -} - -func (_c *MockBackend_ForgetWiFiNetwork_Call) Run(run func(ssid string)) *MockBackend_ForgetWiFiNetwork_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_ForgetWiFiNetwork_Call) Return(_a0 error) *MockBackend_ForgetWiFiNetwork_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ForgetWiFiNetwork_Call) RunAndReturn(run func(string) error) *MockBackend_ForgetWiFiNetwork_Call { - _c.Call.Return(run) - return _c -} - -// GetCurrentState provides a mock function with no fields -func (_m *MockBackend) GetCurrentState() (*network.BackendState, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetCurrentState") - } - - var r0 *network.BackendState - var r1 error - if rf, ok := ret.Get(0).(func() (*network.BackendState, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *network.BackendState); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*network.BackendState) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetCurrentState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCurrentState' -type MockBackend_GetCurrentState_Call struct { - *mock.Call -} - -// GetCurrentState is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetCurrentState() *MockBackend_GetCurrentState_Call { - return &MockBackend_GetCurrentState_Call{Call: _e.mock.On("GetCurrentState")} -} - -func (_c *MockBackend_GetCurrentState_Call) Run(run func()) *MockBackend_GetCurrentState_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetCurrentState_Call) Return(_a0 *network.BackendState, _a1 error) *MockBackend_GetCurrentState_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetCurrentState_Call) RunAndReturn(run func() (*network.BackendState, error)) *MockBackend_GetCurrentState_Call { - _c.Call.Return(run) - return _c -} - -// GetEthernetDevices provides a mock function with no fields -func (_m *MockBackend) GetEthernetDevices() []network.EthernetDevice { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetEthernetDevices") - } - - var r0 []network.EthernetDevice - if rf, ok := ret.Get(0).(func() []network.EthernetDevice); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.EthernetDevice) - } - } - - return r0 -} - -// MockBackend_GetEthernetDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEthernetDevices' -type MockBackend_GetEthernetDevices_Call struct { - *mock.Call -} - -// GetEthernetDevices is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetEthernetDevices() *MockBackend_GetEthernetDevices_Call { - return &MockBackend_GetEthernetDevices_Call{Call: _e.mock.On("GetEthernetDevices")} -} - -func (_c *MockBackend_GetEthernetDevices_Call) Run(run func()) *MockBackend_GetEthernetDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetEthernetDevices_Call) Return(_a0 []network.EthernetDevice) *MockBackend_GetEthernetDevices_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_GetEthernetDevices_Call) RunAndReturn(run func() []network.EthernetDevice) *MockBackend_GetEthernetDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetPromptBroker provides a mock function with no fields -func (_m *MockBackend) GetPromptBroker() network.PromptBroker { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetPromptBroker") - } - - var r0 network.PromptBroker - if rf, ok := ret.Get(0).(func() network.PromptBroker); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(network.PromptBroker) - } - } - - return r0 -} - -// MockBackend_GetPromptBroker_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPromptBroker' -type MockBackend_GetPromptBroker_Call struct { - *mock.Call -} - -// GetPromptBroker is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetPromptBroker() *MockBackend_GetPromptBroker_Call { - return &MockBackend_GetPromptBroker_Call{Call: _e.mock.On("GetPromptBroker")} -} - -func (_c *MockBackend_GetPromptBroker_Call) Run(run func()) *MockBackend_GetPromptBroker_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetPromptBroker_Call) Return(_a0 network.PromptBroker) *MockBackend_GetPromptBroker_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_GetPromptBroker_Call) RunAndReturn(run func() network.PromptBroker) *MockBackend_GetPromptBroker_Call { - _c.Call.Return(run) - return _c -} - -// GetVPNConfig provides a mock function with given fields: uuidOrName -func (_m *MockBackend) GetVPNConfig(uuidOrName string) (*network.VPNConfig, error) { - ret := _m.Called(uuidOrName) - - if len(ret) == 0 { - panic("no return value specified for GetVPNConfig") - } - - var r0 *network.VPNConfig - var r1 error - if rf, ok := ret.Get(0).(func(string) (*network.VPNConfig, error)); ok { - return rf(uuidOrName) - } - if rf, ok := ret.Get(0).(func(string) *network.VPNConfig); ok { - r0 = rf(uuidOrName) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*network.VPNConfig) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(uuidOrName) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetVPNConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetVPNConfig' -type MockBackend_GetVPNConfig_Call struct { - *mock.Call -} - -// GetVPNConfig is a helper method to define mock.On call -// - uuidOrName string -func (_e *MockBackend_Expecter) GetVPNConfig(uuidOrName interface{}) *MockBackend_GetVPNConfig_Call { - return &MockBackend_GetVPNConfig_Call{Call: _e.mock.On("GetVPNConfig", uuidOrName)} -} - -func (_c *MockBackend_GetVPNConfig_Call) Run(run func(uuidOrName string)) *MockBackend_GetVPNConfig_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_GetVPNConfig_Call) Return(_a0 *network.VPNConfig, _a1 error) *MockBackend_GetVPNConfig_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetVPNConfig_Call) RunAndReturn(run func(string) (*network.VPNConfig, error)) *MockBackend_GetVPNConfig_Call { - _c.Call.Return(run) - return _c -} - -// GetWiFiDevices provides a mock function with no fields -func (_m *MockBackend) GetWiFiDevices() []network.WiFiDevice { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetWiFiDevices") - } - - var r0 []network.WiFiDevice - if rf, ok := ret.Get(0).(func() []network.WiFiDevice); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.WiFiDevice) - } - } - - return r0 -} - -// MockBackend_GetWiFiDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiDevices' -type MockBackend_GetWiFiDevices_Call struct { - *mock.Call -} - -// GetWiFiDevices is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetWiFiDevices() *MockBackend_GetWiFiDevices_Call { - return &MockBackend_GetWiFiDevices_Call{Call: _e.mock.On("GetWiFiDevices")} -} - -func (_c *MockBackend_GetWiFiDevices_Call) Run(run func()) *MockBackend_GetWiFiDevices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetWiFiDevices_Call) Return(_a0 []network.WiFiDevice) *MockBackend_GetWiFiDevices_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_GetWiFiDevices_Call) RunAndReturn(run func() []network.WiFiDevice) *MockBackend_GetWiFiDevices_Call { - _c.Call.Return(run) - return _c -} - -// GetWiFiEnabled provides a mock function with no fields -func (_m *MockBackend) GetWiFiEnabled() (bool, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetWiFiEnabled") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func() (bool, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiFiEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiEnabled' -type MockBackend_GetWiFiEnabled_Call struct { - *mock.Call -} - -// GetWiFiEnabled is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetWiFiEnabled() *MockBackend_GetWiFiEnabled_Call { - return &MockBackend_GetWiFiEnabled_Call{Call: _e.mock.On("GetWiFiEnabled")} -} - -func (_c *MockBackend_GetWiFiEnabled_Call) Run(run func()) *MockBackend_GetWiFiEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetWiFiEnabled_Call) Return(_a0 bool, _a1 error) *MockBackend_GetWiFiEnabled_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiFiEnabled_Call) RunAndReturn(run func() (bool, error)) *MockBackend_GetWiFiEnabled_Call { - _c.Call.Return(run) - return _c -} - -// GetWiFiNetworkDetails provides a mock function with given fields: ssid -func (_m *MockBackend) GetWiFiNetworkDetails(ssid string) (*network.NetworkInfoResponse, error) { - ret := _m.Called(ssid) - - if len(ret) == 0 { - panic("no return value specified for GetWiFiNetworkDetails") - } - - var r0 *network.NetworkInfoResponse - var r1 error - if rf, ok := ret.Get(0).(func(string) (*network.NetworkInfoResponse, error)); ok { - return rf(ssid) - } - if rf, ok := ret.Get(0).(func(string) *network.NetworkInfoResponse); ok { - r0 = rf(ssid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*network.NetworkInfoResponse) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(ssid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiFiNetworkDetails_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiNetworkDetails' -type MockBackend_GetWiFiNetworkDetails_Call struct { - *mock.Call -} - -// GetWiFiNetworkDetails is a helper method to define mock.On call -// - ssid string -func (_e *MockBackend_Expecter) GetWiFiNetworkDetails(ssid interface{}) *MockBackend_GetWiFiNetworkDetails_Call { - return &MockBackend_GetWiFiNetworkDetails_Call{Call: _e.mock.On("GetWiFiNetworkDetails", ssid)} -} - -func (_c *MockBackend_GetWiFiNetworkDetails_Call) Run(run func(ssid string)) *MockBackend_GetWiFiNetworkDetails_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_GetWiFiNetworkDetails_Call) Return(_a0 *network.NetworkInfoResponse, _a1 error) *MockBackend_GetWiFiNetworkDetails_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiFiNetworkDetails_Call) RunAndReturn(run func(string) (*network.NetworkInfoResponse, error)) *MockBackend_GetWiFiNetworkDetails_Call { - _c.Call.Return(run) - return _c -} - -// GetWiFiQRCodeContent provides a mock function with given fields: ssid -func (_m *MockBackend) GetWiFiQRCodeContent(ssid string) (string, error) { - ret := _m.Called(ssid) - - if len(ret) == 0 { - panic("no return value specified for GetWiFiQRCodeContent") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(string) (string, error)); ok { - return rf(ssid) - } - if rf, ok := ret.Get(0).(func(string) string); ok { - r0 = rf(ssid) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(ssid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiFiQRCodeContent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiQRCodeContent' -type MockBackend_GetWiFiQRCodeContent_Call struct { - *mock.Call -} - -// GetWiFiQRCodeContent is a helper method to define mock.On call -// - ssid string -func (_e *MockBackend_Expecter) GetWiFiQRCodeContent(ssid interface{}) *MockBackend_GetWiFiQRCodeContent_Call { - return &MockBackend_GetWiFiQRCodeContent_Call{Call: _e.mock.On("GetWiFiQRCodeContent", ssid)} -} - -func (_c *MockBackend_GetWiFiQRCodeContent_Call) Run(run func(ssid string)) *MockBackend_GetWiFiQRCodeContent_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_GetWiFiQRCodeContent_Call) Return(_a0 string, _a1 error) *MockBackend_GetWiFiQRCodeContent_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiFiQRCodeContent_Call) RunAndReturn(run func(string) (string, error)) *MockBackend_GetWiFiQRCodeContent_Call { - _c.Call.Return(run) - return _c -} - -// GetWiredConnections provides a mock function with no fields -func (_m *MockBackend) GetWiredConnections() ([]network.WiredConnection, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetWiredConnections") - } - - var r0 []network.WiredConnection - var r1 error - if rf, ok := ret.Get(0).(func() ([]network.WiredConnection, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []network.WiredConnection); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.WiredConnection) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiredConnections_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiredConnections' -type MockBackend_GetWiredConnections_Call struct { - *mock.Call -} - -// GetWiredConnections is a helper method to define mock.On call -func (_e *MockBackend_Expecter) GetWiredConnections() *MockBackend_GetWiredConnections_Call { - return &MockBackend_GetWiredConnections_Call{Call: _e.mock.On("GetWiredConnections")} -} - -func (_c *MockBackend_GetWiredConnections_Call) Run(run func()) *MockBackend_GetWiredConnections_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_GetWiredConnections_Call) Return(_a0 []network.WiredConnection, _a1 error) *MockBackend_GetWiredConnections_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiredConnections_Call) RunAndReturn(run func() ([]network.WiredConnection, error)) *MockBackend_GetWiredConnections_Call { - _c.Call.Return(run) - return _c -} - -// GetWiredNetworkDetails provides a mock function with given fields: uuid -func (_m *MockBackend) GetWiredNetworkDetails(uuid string) (*network.WiredNetworkInfoResponse, error) { - ret := _m.Called(uuid) - - if len(ret) == 0 { - panic("no return value specified for GetWiredNetworkDetails") - } - - var r0 *network.WiredNetworkInfoResponse - var r1 error - if rf, ok := ret.Get(0).(func(string) (*network.WiredNetworkInfoResponse, error)); ok { - return rf(uuid) - } - if rf, ok := ret.Get(0).(func(string) *network.WiredNetworkInfoResponse); ok { - r0 = rf(uuid) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*network.WiredNetworkInfoResponse) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(uuid) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_GetWiredNetworkDetails_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiredNetworkDetails' -type MockBackend_GetWiredNetworkDetails_Call struct { - *mock.Call -} - -// GetWiredNetworkDetails is a helper method to define mock.On call -// - uuid string -func (_e *MockBackend_Expecter) GetWiredNetworkDetails(uuid interface{}) *MockBackend_GetWiredNetworkDetails_Call { - return &MockBackend_GetWiredNetworkDetails_Call{Call: _e.mock.On("GetWiredNetworkDetails", uuid)} -} - -func (_c *MockBackend_GetWiredNetworkDetails_Call) Run(run func(uuid string)) *MockBackend_GetWiredNetworkDetails_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_GetWiredNetworkDetails_Call) Return(_a0 *network.WiredNetworkInfoResponse, _a1 error) *MockBackend_GetWiredNetworkDetails_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_GetWiredNetworkDetails_Call) RunAndReturn(run func(string) (*network.WiredNetworkInfoResponse, error)) *MockBackend_GetWiredNetworkDetails_Call { - _c.Call.Return(run) - return _c -} - -// ImportVPN provides a mock function with given fields: filePath, name -func (_m *MockBackend) ImportVPN(filePath string, name string) (*network.VPNImportResult, error) { - ret := _m.Called(filePath, name) - - if len(ret) == 0 { - panic("no return value specified for ImportVPN") - } - - var r0 *network.VPNImportResult - var r1 error - if rf, ok := ret.Get(0).(func(string, string) (*network.VPNImportResult, error)); ok { - return rf(filePath, name) - } - if rf, ok := ret.Get(0).(func(string, string) *network.VPNImportResult); ok { - r0 = rf(filePath, name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*network.VPNImportResult) - } - } - - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(filePath, name) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_ImportVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ImportVPN' -type MockBackend_ImportVPN_Call struct { - *mock.Call -} - -// ImportVPN is a helper method to define mock.On call -// - filePath string -// - name string -func (_e *MockBackend_Expecter) ImportVPN(filePath interface{}, name interface{}) *MockBackend_ImportVPN_Call { - return &MockBackend_ImportVPN_Call{Call: _e.mock.On("ImportVPN", filePath, name)} -} - -func (_c *MockBackend_ImportVPN_Call) Run(run func(filePath string, name string)) *MockBackend_ImportVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockBackend_ImportVPN_Call) Return(_a0 *network.VPNImportResult, _a1 error) *MockBackend_ImportVPN_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_ImportVPN_Call) RunAndReturn(run func(string, string) (*network.VPNImportResult, error)) *MockBackend_ImportVPN_Call { - _c.Call.Return(run) - return _c -} - -// Initialize provides a mock function with no fields -func (_m *MockBackend) Initialize() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Initialize") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_Initialize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Initialize' -type MockBackend_Initialize_Call struct { - *mock.Call -} - -// Initialize is a helper method to define mock.On call -func (_e *MockBackend_Expecter) Initialize() *MockBackend_Initialize_Call { - return &MockBackend_Initialize_Call{Call: _e.mock.On("Initialize")} -} - -func (_c *MockBackend_Initialize_Call) Run(run func()) *MockBackend_Initialize_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_Initialize_Call) Return(_a0 error) *MockBackend_Initialize_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_Initialize_Call) RunAndReturn(run func() error) *MockBackend_Initialize_Call { - _c.Call.Return(run) - return _c -} - -// ListActiveVPN provides a mock function with no fields -func (_m *MockBackend) ListActiveVPN() ([]network.VPNActive, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ListActiveVPN") - } - - var r0 []network.VPNActive - var r1 error - if rf, ok := ret.Get(0).(func() ([]network.VPNActive, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []network.VPNActive); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.VPNActive) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_ListActiveVPN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListActiveVPN' -type MockBackend_ListActiveVPN_Call struct { - *mock.Call -} - -// ListActiveVPN is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ListActiveVPN() *MockBackend_ListActiveVPN_Call { - return &MockBackend_ListActiveVPN_Call{Call: _e.mock.On("ListActiveVPN")} -} - -func (_c *MockBackend_ListActiveVPN_Call) Run(run func()) *MockBackend_ListActiveVPN_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ListActiveVPN_Call) Return(_a0 []network.VPNActive, _a1 error) *MockBackend_ListActiveVPN_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_ListActiveVPN_Call) RunAndReturn(run func() ([]network.VPNActive, error)) *MockBackend_ListActiveVPN_Call { - _c.Call.Return(run) - return _c -} - -// ListVPNPlugins provides a mock function with no fields -func (_m *MockBackend) ListVPNPlugins() ([]network.VPNPlugin, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ListVPNPlugins") - } - - var r0 []network.VPNPlugin - var r1 error - if rf, ok := ret.Get(0).(func() ([]network.VPNPlugin, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []network.VPNPlugin); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.VPNPlugin) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_ListVPNPlugins_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListVPNPlugins' -type MockBackend_ListVPNPlugins_Call struct { - *mock.Call -} - -// ListVPNPlugins is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ListVPNPlugins() *MockBackend_ListVPNPlugins_Call { - return &MockBackend_ListVPNPlugins_Call{Call: _e.mock.On("ListVPNPlugins")} -} - -func (_c *MockBackend_ListVPNPlugins_Call) Run(run func()) *MockBackend_ListVPNPlugins_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ListVPNPlugins_Call) Return(_a0 []network.VPNPlugin, _a1 error) *MockBackend_ListVPNPlugins_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_ListVPNPlugins_Call) RunAndReturn(run func() ([]network.VPNPlugin, error)) *MockBackend_ListVPNPlugins_Call { - _c.Call.Return(run) - return _c -} - -// ListVPNProfiles provides a mock function with no fields -func (_m *MockBackend) ListVPNProfiles() ([]network.VPNProfile, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ListVPNProfiles") - } - - var r0 []network.VPNProfile - var r1 error - if rf, ok := ret.Get(0).(func() ([]network.VPNProfile, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() []network.VPNProfile); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]network.VPNProfile) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBackend_ListVPNProfiles_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListVPNProfiles' -type MockBackend_ListVPNProfiles_Call struct { - *mock.Call -} - -// ListVPNProfiles is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ListVPNProfiles() *MockBackend_ListVPNProfiles_Call { - return &MockBackend_ListVPNProfiles_Call{Call: _e.mock.On("ListVPNProfiles")} -} - -func (_c *MockBackend_ListVPNProfiles_Call) Run(run func()) *MockBackend_ListVPNProfiles_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ListVPNProfiles_Call) Return(_a0 []network.VPNProfile, _a1 error) *MockBackend_ListVPNProfiles_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBackend_ListVPNProfiles_Call) RunAndReturn(run func() ([]network.VPNProfile, error)) *MockBackend_ListVPNProfiles_Call { - _c.Call.Return(run) - return _c -} - -// ScanWiFi provides a mock function with no fields -func (_m *MockBackend) ScanWiFi() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ScanWiFi") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ScanWiFi_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ScanWiFi' -type MockBackend_ScanWiFi_Call struct { - *mock.Call -} - -// ScanWiFi is a helper method to define mock.On call -func (_e *MockBackend_Expecter) ScanWiFi() *MockBackend_ScanWiFi_Call { - return &MockBackend_ScanWiFi_Call{Call: _e.mock.On("ScanWiFi")} -} - -func (_c *MockBackend_ScanWiFi_Call) Run(run func()) *MockBackend_ScanWiFi_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_ScanWiFi_Call) Return(_a0 error) *MockBackend_ScanWiFi_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ScanWiFi_Call) RunAndReturn(run func() error) *MockBackend_ScanWiFi_Call { - _c.Call.Return(run) - return _c -} - -// ScanWiFiDevice provides a mock function with given fields: device -func (_m *MockBackend) ScanWiFiDevice(device string) error { - ret := _m.Called(device) - - if len(ret) == 0 { - panic("no return value specified for ScanWiFiDevice") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { - r0 = rf(device) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_ScanWiFiDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ScanWiFiDevice' -type MockBackend_ScanWiFiDevice_Call struct { - *mock.Call -} - -// ScanWiFiDevice is a helper method to define mock.On call -// - device string -func (_e *MockBackend_Expecter) ScanWiFiDevice(device interface{}) *MockBackend_ScanWiFiDevice_Call { - return &MockBackend_ScanWiFiDevice_Call{Call: _e.mock.On("ScanWiFiDevice", device)} -} - -func (_c *MockBackend_ScanWiFiDevice_Call) Run(run func(device string)) *MockBackend_ScanWiFiDevice_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockBackend_ScanWiFiDevice_Call) Return(_a0 error) *MockBackend_ScanWiFiDevice_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_ScanWiFiDevice_Call) RunAndReturn(run func(string) error) *MockBackend_ScanWiFiDevice_Call { - _c.Call.Return(run) - return _c -} - -// SetPromptBroker provides a mock function with given fields: broker -func (_m *MockBackend) SetPromptBroker(broker network.PromptBroker) error { - ret := _m.Called(broker) - - if len(ret) == 0 { - panic("no return value specified for SetPromptBroker") - } - - var r0 error - if rf, ok := ret.Get(0).(func(network.PromptBroker) error); ok { - r0 = rf(broker) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SetPromptBroker_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPromptBroker' -type MockBackend_SetPromptBroker_Call struct { - *mock.Call -} - -// SetPromptBroker is a helper method to define mock.On call -// - broker network.PromptBroker -func (_e *MockBackend_Expecter) SetPromptBroker(broker interface{}) *MockBackend_SetPromptBroker_Call { - return &MockBackend_SetPromptBroker_Call{Call: _e.mock.On("SetPromptBroker", broker)} -} - -func (_c *MockBackend_SetPromptBroker_Call) Run(run func(broker network.PromptBroker)) *MockBackend_SetPromptBroker_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(network.PromptBroker)) - }) - return _c -} - -func (_c *MockBackend_SetPromptBroker_Call) Return(_a0 error) *MockBackend_SetPromptBroker_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SetPromptBroker_Call) RunAndReturn(run func(network.PromptBroker) error) *MockBackend_SetPromptBroker_Call { - _c.Call.Return(run) - return _c -} - -// SetVPNCredentials provides a mock function with given fields: uuid, username, password, save -func (_m *MockBackend) SetVPNCredentials(uuid string, username string, password string, save bool) error { - ret := _m.Called(uuid, username, password, save) - - if len(ret) == 0 { - panic("no return value specified for SetVPNCredentials") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, string, string, bool) error); ok { - r0 = rf(uuid, username, password, save) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SetVPNCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetVPNCredentials' -type MockBackend_SetVPNCredentials_Call struct { - *mock.Call -} - -// SetVPNCredentials is a helper method to define mock.On call -// - uuid string -// - username string -// - password string -// - save bool -func (_e *MockBackend_Expecter) SetVPNCredentials(uuid interface{}, username interface{}, password interface{}, save interface{}) *MockBackend_SetVPNCredentials_Call { - return &MockBackend_SetVPNCredentials_Call{Call: _e.mock.On("SetVPNCredentials", uuid, username, password, save)} -} - -func (_c *MockBackend_SetVPNCredentials_Call) Run(run func(uuid string, username string, password string, save bool)) *MockBackend_SetVPNCredentials_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string), args[2].(string), args[3].(bool)) - }) - return _c -} - -func (_c *MockBackend_SetVPNCredentials_Call) Return(_a0 error) *MockBackend_SetVPNCredentials_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SetVPNCredentials_Call) RunAndReturn(run func(string, string, string, bool) error) *MockBackend_SetVPNCredentials_Call { - _c.Call.Return(run) - return _c -} - -// SetWiFiAutoconnect provides a mock function with given fields: ssid, autoconnect -func (_m *MockBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - ret := _m.Called(ssid, autoconnect) - - if len(ret) == 0 { - panic("no return value specified for SetWiFiAutoconnect") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, bool) error); ok { - r0 = rf(ssid, autoconnect) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SetWiFiAutoconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWiFiAutoconnect' -type MockBackend_SetWiFiAutoconnect_Call struct { - *mock.Call -} - -// SetWiFiAutoconnect is a helper method to define mock.On call -// - ssid string -// - autoconnect bool -func (_e *MockBackend_Expecter) SetWiFiAutoconnect(ssid interface{}, autoconnect interface{}) *MockBackend_SetWiFiAutoconnect_Call { - return &MockBackend_SetWiFiAutoconnect_Call{Call: _e.mock.On("SetWiFiAutoconnect", ssid, autoconnect)} -} - -func (_c *MockBackend_SetWiFiAutoconnect_Call) Run(run func(ssid string, autoconnect bool)) *MockBackend_SetWiFiAutoconnect_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockBackend_SetWiFiAutoconnect_Call) Return(_a0 error) *MockBackend_SetWiFiAutoconnect_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SetWiFiAutoconnect_Call) RunAndReturn(run func(string, bool) error) *MockBackend_SetWiFiAutoconnect_Call { - _c.Call.Return(run) - return _c -} - -// SetWiFiEnabled provides a mock function with given fields: enabled -func (_m *MockBackend) SetWiFiEnabled(enabled bool) error { - ret := _m.Called(enabled) - - if len(ret) == 0 { - panic("no return value specified for SetWiFiEnabled") - } - - var r0 error - if rf, ok := ret.Get(0).(func(bool) error); ok { - r0 = rf(enabled) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SetWiFiEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWiFiEnabled' -type MockBackend_SetWiFiEnabled_Call struct { - *mock.Call -} - -// SetWiFiEnabled is a helper method to define mock.On call -// - enabled bool -func (_e *MockBackend_Expecter) SetWiFiEnabled(enabled interface{}) *MockBackend_SetWiFiEnabled_Call { - return &MockBackend_SetWiFiEnabled_Call{Call: _e.mock.On("SetWiFiEnabled", enabled)} -} - -func (_c *MockBackend_SetWiFiEnabled_Call) Run(run func(enabled bool)) *MockBackend_SetWiFiEnabled_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bool)) - }) - return _c -} - -func (_c *MockBackend_SetWiFiEnabled_Call) Return(_a0 error) *MockBackend_SetWiFiEnabled_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SetWiFiEnabled_Call) RunAndReturn(run func(bool) error) *MockBackend_SetWiFiEnabled_Call { - _c.Call.Return(run) - return _c -} - -// StartMonitoring provides a mock function with given fields: onStateChange -func (_m *MockBackend) StartMonitoring(onStateChange func()) error { - ret := _m.Called(onStateChange) - - if len(ret) == 0 { - panic("no return value specified for StartMonitoring") - } - - var r0 error - if rf, ok := ret.Get(0).(func(func()) error); ok { - r0 = rf(onStateChange) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_StartMonitoring_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartMonitoring' -type MockBackend_StartMonitoring_Call struct { - *mock.Call -} - -// StartMonitoring is a helper method to define mock.On call -// - onStateChange func() -func (_e *MockBackend_Expecter) StartMonitoring(onStateChange interface{}) *MockBackend_StartMonitoring_Call { - return &MockBackend_StartMonitoring_Call{Call: _e.mock.On("StartMonitoring", onStateChange)} -} - -func (_c *MockBackend_StartMonitoring_Call) Run(run func(onStateChange func())) *MockBackend_StartMonitoring_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(func())) - }) - return _c -} - -func (_c *MockBackend_StartMonitoring_Call) Return(_a0 error) *MockBackend_StartMonitoring_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_StartMonitoring_Call) RunAndReturn(run func(func()) error) *MockBackend_StartMonitoring_Call { - _c.Call.Return(run) - return _c -} - -// StopMonitoring provides a mock function with no fields -func (_m *MockBackend) StopMonitoring() { - _m.Called() -} - -// MockBackend_StopMonitoring_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StopMonitoring' -type MockBackend_StopMonitoring_Call struct { - *mock.Call -} - -// StopMonitoring is a helper method to define mock.On call -func (_e *MockBackend_Expecter) StopMonitoring() *MockBackend_StopMonitoring_Call { - return &MockBackend_StopMonitoring_Call{Call: _e.mock.On("StopMonitoring")} -} - -func (_c *MockBackend_StopMonitoring_Call) Run(run func()) *MockBackend_StopMonitoring_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockBackend_StopMonitoring_Call) Return() *MockBackend_StopMonitoring_Call { - _c.Call.Return() - return _c -} - -func (_c *MockBackend_StopMonitoring_Call) RunAndReturn(run func()) *MockBackend_StopMonitoring_Call { - _c.Run(run) - return _c -} - -// SubmitCredentials provides a mock function with given fields: token, secrets, save -func (_m *MockBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - ret := _m.Called(token, secrets, save) - - if len(ret) == 0 { - panic("no return value specified for SubmitCredentials") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, map[string]string, bool) error); ok { - r0 = rf(token, secrets, save) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_SubmitCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubmitCredentials' -type MockBackend_SubmitCredentials_Call struct { - *mock.Call -} - -// SubmitCredentials is a helper method to define mock.On call -// - token string -// - secrets map[string]string -// - save bool -func (_e *MockBackend_Expecter) SubmitCredentials(token interface{}, secrets interface{}, save interface{}) *MockBackend_SubmitCredentials_Call { - return &MockBackend_SubmitCredentials_Call{Call: _e.mock.On("SubmitCredentials", token, secrets, save)} -} - -func (_c *MockBackend_SubmitCredentials_Call) Run(run func(token string, secrets map[string]string, save bool)) *MockBackend_SubmitCredentials_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(map[string]string), args[2].(bool)) - }) - return _c -} - -func (_c *MockBackend_SubmitCredentials_Call) Return(_a0 error) *MockBackend_SubmitCredentials_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_SubmitCredentials_Call) RunAndReturn(run func(string, map[string]string, bool) error) *MockBackend_SubmitCredentials_Call { - _c.Call.Return(run) - return _c -} - -// UpdateVPNConfig provides a mock function with given fields: uuid, updates -func (_m *MockBackend) UpdateVPNConfig(uuid string, updates map[string]interface{}) error { - ret := _m.Called(uuid, updates) - - if len(ret) == 0 { - panic("no return value specified for UpdateVPNConfig") - } - - var r0 error - if rf, ok := ret.Get(0).(func(string, map[string]interface{}) error); ok { - r0 = rf(uuid, updates) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockBackend_UpdateVPNConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateVPNConfig' -type MockBackend_UpdateVPNConfig_Call struct { - *mock.Call -} - -// UpdateVPNConfig is a helper method to define mock.On call -// - uuid string -// - updates map[string]interface{} -func (_e *MockBackend_Expecter) UpdateVPNConfig(uuid interface{}, updates interface{}) *MockBackend_UpdateVPNConfig_Call { - return &MockBackend_UpdateVPNConfig_Call{Call: _e.mock.On("UpdateVPNConfig", uuid, updates)} -} - -func (_c *MockBackend_UpdateVPNConfig_Call) Run(run func(uuid string, updates map[string]interface{})) *MockBackend_UpdateVPNConfig_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(map[string]interface{})) - }) - return _c -} - -func (_c *MockBackend_UpdateVPNConfig_Call) Return(_a0 error) *MockBackend_UpdateVPNConfig_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockBackend_UpdateVPNConfig_Call) RunAndReturn(run func(string, map[string]interface{}) error) *MockBackend_UpdateVPNConfig_Call { - _c.Call.Return(run) - return _c -} - -// NewMockBackend creates a new instance of MockBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockBackend(t interface { - mock.TestingT - Cleanup(func()) -}) *MockBackend { - mock := &MockBackend{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/utils/mock_AppChecker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/utils/mock_AppChecker.go deleted file mode 100644 index be220c4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/utils/mock_AppChecker.go +++ /dev/null @@ -1,242 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_utils - -import mock "github.com/stretchr/testify/mock" - -// MockAppChecker is an autogenerated mock type for the AppChecker type -type MockAppChecker struct { - mock.Mock -} - -type MockAppChecker_Expecter struct { - mock *mock.Mock -} - -func (_m *MockAppChecker) EXPECT() *MockAppChecker_Expecter { - return &MockAppChecker_Expecter{mock: &_m.Mock} -} - -// AnyCommandExists provides a mock function with given fields: cmds -func (_m *MockAppChecker) AnyCommandExists(cmds ...string) bool { - _va := make([]interface{}, len(cmds)) - for _i := range cmds { - _va[_i] = cmds[_i] - } - var _ca []interface{} - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for AnyCommandExists") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(...string) bool); ok { - r0 = rf(cmds...) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// MockAppChecker_AnyCommandExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AnyCommandExists' -type MockAppChecker_AnyCommandExists_Call struct { - *mock.Call -} - -// AnyCommandExists is a helper method to define mock.On call -// - cmds ...string -func (_e *MockAppChecker_Expecter) AnyCommandExists(cmds ...interface{}) *MockAppChecker_AnyCommandExists_Call { - return &MockAppChecker_AnyCommandExists_Call{Call: _e.mock.On("AnyCommandExists", - append([]interface{}{}, cmds...)...)} -} - -func (_c *MockAppChecker_AnyCommandExists_Call) Run(run func(cmds ...string)) *MockAppChecker_AnyCommandExists_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]string, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(string) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *MockAppChecker_AnyCommandExists_Call) Return(_a0 bool) *MockAppChecker_AnyCommandExists_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockAppChecker_AnyCommandExists_Call) RunAndReturn(run func(...string) bool) *MockAppChecker_AnyCommandExists_Call { - _c.Call.Return(run) - return _c -} - -// AnyFlatpakExists provides a mock function with given fields: flatpaks -func (_m *MockAppChecker) AnyFlatpakExists(flatpaks ...string) bool { - _va := make([]interface{}, len(flatpaks)) - for _i := range flatpaks { - _va[_i] = flatpaks[_i] - } - var _ca []interface{} - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for AnyFlatpakExists") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(...string) bool); ok { - r0 = rf(flatpaks...) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// MockAppChecker_AnyFlatpakExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AnyFlatpakExists' -type MockAppChecker_AnyFlatpakExists_Call struct { - *mock.Call -} - -// AnyFlatpakExists is a helper method to define mock.On call -// - flatpaks ...string -func (_e *MockAppChecker_Expecter) AnyFlatpakExists(flatpaks ...interface{}) *MockAppChecker_AnyFlatpakExists_Call { - return &MockAppChecker_AnyFlatpakExists_Call{Call: _e.mock.On("AnyFlatpakExists", - append([]interface{}{}, flatpaks...)...)} -} - -func (_c *MockAppChecker_AnyFlatpakExists_Call) Run(run func(flatpaks ...string)) *MockAppChecker_AnyFlatpakExists_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]string, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(string) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *MockAppChecker_AnyFlatpakExists_Call) Return(_a0 bool) *MockAppChecker_AnyFlatpakExists_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockAppChecker_AnyFlatpakExists_Call) RunAndReturn(run func(...string) bool) *MockAppChecker_AnyFlatpakExists_Call { - _c.Call.Return(run) - return _c -} - -// CommandExists provides a mock function with given fields: cmd -func (_m *MockAppChecker) CommandExists(cmd string) bool { - ret := _m.Called(cmd) - - if len(ret) == 0 { - panic("no return value specified for CommandExists") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(string) bool); ok { - r0 = rf(cmd) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// MockAppChecker_CommandExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommandExists' -type MockAppChecker_CommandExists_Call struct { - *mock.Call -} - -// CommandExists is a helper method to define mock.On call -// - cmd string -func (_e *MockAppChecker_Expecter) CommandExists(cmd interface{}) *MockAppChecker_CommandExists_Call { - return &MockAppChecker_CommandExists_Call{Call: _e.mock.On("CommandExists", cmd)} -} - -func (_c *MockAppChecker_CommandExists_Call) Run(run func(cmd string)) *MockAppChecker_CommandExists_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockAppChecker_CommandExists_Call) Return(_a0 bool) *MockAppChecker_CommandExists_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockAppChecker_CommandExists_Call) RunAndReturn(run func(string) bool) *MockAppChecker_CommandExists_Call { - _c.Call.Return(run) - return _c -} - -// FlatpakExists provides a mock function with given fields: name -func (_m *MockAppChecker) FlatpakExists(name string) bool { - ret := _m.Called(name) - - if len(ret) == 0 { - panic("no return value specified for FlatpakExists") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(string) bool); ok { - r0 = rf(name) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// MockAppChecker_FlatpakExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FlatpakExists' -type MockAppChecker_FlatpakExists_Call struct { - *mock.Call -} - -// FlatpakExists is a helper method to define mock.On call -// - name string -func (_e *MockAppChecker_Expecter) FlatpakExists(name interface{}) *MockAppChecker_FlatpakExists_Call { - return &MockAppChecker_FlatpakExists_Call{Call: _e.mock.On("FlatpakExists", name)} -} - -func (_c *MockAppChecker_FlatpakExists_Call) Run(run func(name string)) *MockAppChecker_FlatpakExists_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockAppChecker_FlatpakExists_Call) Return(_a0 bool) *MockAppChecker_FlatpakExists_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockAppChecker_FlatpakExists_Call) RunAndReturn(run func(string) bool) *MockAppChecker_FlatpakExists_Call { - _c.Call.Return(run) - return _c -} - -// NewMockAppChecker creates a new instance of MockAppChecker. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockAppChecker(t interface { - mock.TestingT - Cleanup(func()) -}) *MockAppChecker { - mock := &MockAppChecker{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/version/mock_VersionFetcher.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/version/mock_VersionFetcher.go deleted file mode 100644 index a6fa27c..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/version/mock_VersionFetcher.go +++ /dev/null @@ -1,144 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_version - -import mock "github.com/stretchr/testify/mock" - -// MockVersionFetcher is an autogenerated mock type for the VersionFetcher type -type MockVersionFetcher struct { - mock.Mock -} - -type MockVersionFetcher_Expecter struct { - mock *mock.Mock -} - -func (_m *MockVersionFetcher) EXPECT() *MockVersionFetcher_Expecter { - return &MockVersionFetcher_Expecter{mock: &_m.Mock} -} - -// GetCurrentVersion provides a mock function with given fields: dmsPath -func (_m *MockVersionFetcher) GetCurrentVersion(dmsPath string) (string, error) { - ret := _m.Called(dmsPath) - - if len(ret) == 0 { - panic("no return value specified for GetCurrentVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(string) (string, error)); ok { - return rf(dmsPath) - } - if rf, ok := ret.Get(0).(func(string) string); ok { - r0 = rf(dmsPath) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(dmsPath) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockVersionFetcher_GetCurrentVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCurrentVersion' -type MockVersionFetcher_GetCurrentVersion_Call struct { - *mock.Call -} - -// GetCurrentVersion is a helper method to define mock.On call -// - dmsPath string -func (_e *MockVersionFetcher_Expecter) GetCurrentVersion(dmsPath interface{}) *MockVersionFetcher_GetCurrentVersion_Call { - return &MockVersionFetcher_GetCurrentVersion_Call{Call: _e.mock.On("GetCurrentVersion", dmsPath)} -} - -func (_c *MockVersionFetcher_GetCurrentVersion_Call) Run(run func(dmsPath string)) *MockVersionFetcher_GetCurrentVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockVersionFetcher_GetCurrentVersion_Call) Return(_a0 string, _a1 error) *MockVersionFetcher_GetCurrentVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockVersionFetcher_GetCurrentVersion_Call) RunAndReturn(run func(string) (string, error)) *MockVersionFetcher_GetCurrentVersion_Call { - _c.Call.Return(run) - return _c -} - -// GetLatestVersion provides a mock function with given fields: dmsPath -func (_m *MockVersionFetcher) GetLatestVersion(dmsPath string) (string, error) { - ret := _m.Called(dmsPath) - - if len(ret) == 0 { - panic("no return value specified for GetLatestVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(string) (string, error)); ok { - return rf(dmsPath) - } - if rf, ok := ret.Get(0).(func(string) string); ok { - r0 = rf(dmsPath) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(dmsPath) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockVersionFetcher_GetLatestVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLatestVersion' -type MockVersionFetcher_GetLatestVersion_Call struct { - *mock.Call -} - -// GetLatestVersion is a helper method to define mock.On call -// - dmsPath string -func (_e *MockVersionFetcher_Expecter) GetLatestVersion(dmsPath interface{}) *MockVersionFetcher_GetLatestVersion_Call { - return &MockVersionFetcher_GetLatestVersion_Call{Call: _e.mock.On("GetLatestVersion", dmsPath)} -} - -func (_c *MockVersionFetcher_GetLatestVersion_Call) Run(run func(dmsPath string)) *MockVersionFetcher_GetLatestVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *MockVersionFetcher_GetLatestVersion_Call) Return(_a0 string, _a1 error) *MockVersionFetcher_GetLatestVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockVersionFetcher_GetLatestVersion_Call) RunAndReturn(run func(string) (string, error)) *MockVersionFetcher_GetLatestVersion_Call { - _c.Call.Return(run) - return _c -} - -// NewMockVersionFetcher creates a new instance of MockVersionFetcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockVersionFetcher(t interface { - mock.TestingT - Cleanup(func()) -}) *MockVersionFetcher { - mock := &MockVersionFetcher{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/wlclient/mock_WaylandDisplay.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/wlclient/mock_WaylandDisplay.go deleted file mode 100644 index 45d944b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/wlclient/mock_WaylandDisplay.go +++ /dev/null @@ -1,229 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_wlclient - -import ( - client "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - mock "github.com/stretchr/testify/mock" -) - -// MockWaylandDisplay is an autogenerated mock type for the WaylandDisplay type -type MockWaylandDisplay struct { - mock.Mock -} - -type MockWaylandDisplay_Expecter struct { - mock *mock.Mock -} - -func (_m *MockWaylandDisplay) EXPECT() *MockWaylandDisplay_Expecter { - return &MockWaylandDisplay_Expecter{mock: &_m.Mock} -} - -// Context provides a mock function with no fields -func (_m *MockWaylandDisplay) Context() *client.Context { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Context") - } - - var r0 *client.Context - if rf, ok := ret.Get(0).(func() *client.Context); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*client.Context) - } - } - - return r0 -} - -// MockWaylandDisplay_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context' -type MockWaylandDisplay_Context_Call struct { - *mock.Call -} - -// Context is a helper method to define mock.On call -func (_e *MockWaylandDisplay_Expecter) Context() *MockWaylandDisplay_Context_Call { - return &MockWaylandDisplay_Context_Call{Call: _e.mock.On("Context")} -} - -func (_c *MockWaylandDisplay_Context_Call) Run(run func()) *MockWaylandDisplay_Context_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockWaylandDisplay_Context_Call) Return(_a0 *client.Context) *MockWaylandDisplay_Context_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockWaylandDisplay_Context_Call) RunAndReturn(run func() *client.Context) *MockWaylandDisplay_Context_Call { - _c.Call.Return(run) - return _c -} - -// Destroy provides a mock function with no fields -func (_m *MockWaylandDisplay) Destroy() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Destroy") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockWaylandDisplay_Destroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Destroy' -type MockWaylandDisplay_Destroy_Call struct { - *mock.Call -} - -// Destroy is a helper method to define mock.On call -func (_e *MockWaylandDisplay_Expecter) Destroy() *MockWaylandDisplay_Destroy_Call { - return &MockWaylandDisplay_Destroy_Call{Call: _e.mock.On("Destroy")} -} - -func (_c *MockWaylandDisplay_Destroy_Call) Run(run func()) *MockWaylandDisplay_Destroy_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockWaylandDisplay_Destroy_Call) Return(_a0 error) *MockWaylandDisplay_Destroy_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockWaylandDisplay_Destroy_Call) RunAndReturn(run func() error) *MockWaylandDisplay_Destroy_Call { - _c.Call.Return(run) - return _c -} - -// GetRegistry provides a mock function with no fields -func (_m *MockWaylandDisplay) GetRegistry() (*client.Registry, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetRegistry") - } - - var r0 *client.Registry - var r1 error - if rf, ok := ret.Get(0).(func() (*client.Registry, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *client.Registry); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*client.Registry) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockWaylandDisplay_GetRegistry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRegistry' -type MockWaylandDisplay_GetRegistry_Call struct { - *mock.Call -} - -// GetRegistry is a helper method to define mock.On call -func (_e *MockWaylandDisplay_Expecter) GetRegistry() *MockWaylandDisplay_GetRegistry_Call { - return &MockWaylandDisplay_GetRegistry_Call{Call: _e.mock.On("GetRegistry")} -} - -func (_c *MockWaylandDisplay_GetRegistry_Call) Run(run func()) *MockWaylandDisplay_GetRegistry_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockWaylandDisplay_GetRegistry_Call) Return(_a0 *client.Registry, _a1 error) *MockWaylandDisplay_GetRegistry_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockWaylandDisplay_GetRegistry_Call) RunAndReturn(run func() (*client.Registry, error)) *MockWaylandDisplay_GetRegistry_Call { - _c.Call.Return(run) - return _c -} - -// Roundtrip provides a mock function with no fields -func (_m *MockWaylandDisplay) Roundtrip() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Roundtrip") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockWaylandDisplay_Roundtrip_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Roundtrip' -type MockWaylandDisplay_Roundtrip_Call struct { - *mock.Call -} - -// Roundtrip is a helper method to define mock.On call -func (_e *MockWaylandDisplay_Expecter) Roundtrip() *MockWaylandDisplay_Roundtrip_Call { - return &MockWaylandDisplay_Roundtrip_Call{Call: _e.mock.On("Roundtrip")} -} - -func (_c *MockWaylandDisplay_Roundtrip_Call) Run(run func()) *MockWaylandDisplay_Roundtrip_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockWaylandDisplay_Roundtrip_Call) Return(_a0 error) *MockWaylandDisplay_Roundtrip_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockWaylandDisplay_Roundtrip_Call) RunAndReturn(run func() error) *MockWaylandDisplay_Roundtrip_Call { - _c.Call.Return(run) - return _c -} - -// NewMockWaylandDisplay creates a new instance of MockWaylandDisplay. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockWaylandDisplay(t interface { - mock.TestingT - Cleanup(func()) -}) *MockWaylandDisplay { - mock := &MockWaylandDisplay{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/wlcontext/mock_WaylandContext.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/wlcontext/mock_WaylandContext.go deleted file mode 100644 index f5b0c35..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/mocks/wlcontext/mock_WaylandContext.go +++ /dev/null @@ -1,226 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks_wlcontext - -import ( - client "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - mock "github.com/stretchr/testify/mock" -) - -// MockWaylandContext is an autogenerated mock type for the WaylandContext type -type MockWaylandContext struct { - mock.Mock -} - -type MockWaylandContext_Expecter struct { - mock *mock.Mock -} - -func (_m *MockWaylandContext) EXPECT() *MockWaylandContext_Expecter { - return &MockWaylandContext_Expecter{mock: &_m.Mock} -} - -// Close provides a mock function with no fields -func (_m *MockWaylandContext) Close() { - _m.Called() -} - -// MockWaylandContext_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockWaylandContext_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockWaylandContext_Expecter) Close() *MockWaylandContext_Close_Call { - return &MockWaylandContext_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockWaylandContext_Close_Call) Run(run func()) *MockWaylandContext_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockWaylandContext_Close_Call) Return() *MockWaylandContext_Close_Call { - _c.Call.Return() - return _c -} - -func (_c *MockWaylandContext_Close_Call) RunAndReturn(run func()) *MockWaylandContext_Close_Call { - _c.Run(run) - return _c -} - -// Display provides a mock function with no fields -func (_m *MockWaylandContext) Display() *client.Display { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Display") - } - - var r0 *client.Display - if rf, ok := ret.Get(0).(func() *client.Display); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*client.Display) - } - } - - return r0 -} - -// MockWaylandContext_Display_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Display' -type MockWaylandContext_Display_Call struct { - *mock.Call -} - -// Display is a helper method to define mock.On call -func (_e *MockWaylandContext_Expecter) Display() *MockWaylandContext_Display_Call { - return &MockWaylandContext_Display_Call{Call: _e.mock.On("Display")} -} - -func (_c *MockWaylandContext_Display_Call) Run(run func()) *MockWaylandContext_Display_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockWaylandContext_Display_Call) Return(_a0 *client.Display) *MockWaylandContext_Display_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockWaylandContext_Display_Call) RunAndReturn(run func() *client.Display) *MockWaylandContext_Display_Call { - _c.Call.Return(run) - return _c -} - -// FatalError provides a mock function with no fields -func (_m *MockWaylandContext) FatalError() <-chan error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for FatalError") - } - - var r0 <-chan error - if rf, ok := ret.Get(0).(func() <-chan error); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan error) - } - } - - return r0 -} - -// MockWaylandContext_FatalError_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FatalError' -type MockWaylandContext_FatalError_Call struct { - *mock.Call -} - -// FatalError is a helper method to define mock.On call -func (_e *MockWaylandContext_Expecter) FatalError() *MockWaylandContext_FatalError_Call { - return &MockWaylandContext_FatalError_Call{Call: _e.mock.On("FatalError")} -} - -func (_c *MockWaylandContext_FatalError_Call) Run(run func()) *MockWaylandContext_FatalError_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockWaylandContext_FatalError_Call) Return(_a0 <-chan error) *MockWaylandContext_FatalError_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockWaylandContext_FatalError_Call) RunAndReturn(run func() <-chan error) *MockWaylandContext_FatalError_Call { - _c.Call.Return(run) - return _c -} - -// Post provides a mock function with given fields: fn -func (_m *MockWaylandContext) Post(fn func()) { - _m.Called(fn) -} - -// MockWaylandContext_Post_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Post' -type MockWaylandContext_Post_Call struct { - *mock.Call -} - -// Post is a helper method to define mock.On call -// - fn func() -func (_e *MockWaylandContext_Expecter) Post(fn interface{}) *MockWaylandContext_Post_Call { - return &MockWaylandContext_Post_Call{Call: _e.mock.On("Post", fn)} -} - -func (_c *MockWaylandContext_Post_Call) Run(run func(fn func())) *MockWaylandContext_Post_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(func())) - }) - return _c -} - -func (_c *MockWaylandContext_Post_Call) Return() *MockWaylandContext_Post_Call { - _c.Call.Return() - return _c -} - -func (_c *MockWaylandContext_Post_Call) RunAndReturn(run func(func())) *MockWaylandContext_Post_Call { - _c.Run(run) - return _c -} - -// Start provides a mock function with no fields -func (_m *MockWaylandContext) Start() { - _m.Called() -} - -// MockWaylandContext_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type MockWaylandContext_Start_Call struct { - *mock.Call -} - -// Start is a helper method to define mock.On call -func (_e *MockWaylandContext_Expecter) Start() *MockWaylandContext_Start_Call { - return &MockWaylandContext_Start_Call{Call: _e.mock.On("Start")} -} - -func (_c *MockWaylandContext_Start_Call) Run(run func()) *MockWaylandContext_Start_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockWaylandContext_Start_Call) Return() *MockWaylandContext_Start_Call { - _c.Call.Return() - return _c -} - -func (_c *MockWaylandContext_Start_Call) RunAndReturn(run func()) *MockWaylandContext_Start_Call { - _c.Run(run) - return _c -} - -// NewMockWaylandContext creates a new instance of MockWaylandContext. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockWaylandContext(t interface { - mock.TestingT - Cleanup(func()) -}) *MockWaylandContext { - mock := &MockWaylandContext{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/notify/notify.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/notify/notify.go deleted file mode 100644 index 1e42b16..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/notify/notify.go +++ /dev/null @@ -1,185 +0,0 @@ -package notify - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "syscall" - - "github.com/godbus/dbus/v5" -) - -const ( - notifyDest = "org.freedesktop.Notifications" - notifyPath = "/org/freedesktop/Notifications" - notifyInterface = "org.freedesktop.Notifications" - - maxSummaryLen = 29 - maxBodyLen = 80 -) - -type Notification struct { - AppName string - Icon string - Summary string - Body string - FilePath string - Timeout int32 -} - -func Send(n Notification) error { - conn, err := dbus.SessionBus() - if err != nil { - return fmt.Errorf("dbus session failed: %w", err) - } - - if n.AppName == "" { - n.AppName = "DMS" - } - if n.Timeout == 0 { - n.Timeout = 5000 - } - - if len(n.Summary) > maxSummaryLen { - n.Summary = n.Summary[:maxSummaryLen-3] + "..." - } - if len(n.Body) > maxBodyLen { - n.Body = n.Body[:maxBodyLen-3] + "..." - } - - var actions []string - if n.FilePath != "" { - actions = []string{ - "open", "Open", - "folder", "Open Folder", - } - } - - hints := map[string]dbus.Variant{} - if n.FilePath != "" { - imgPath := n.FilePath - if !strings.HasPrefix(imgPath, "file://") { - imgPath = "file://" + imgPath - } - hints["image_path"] = dbus.MakeVariant(imgPath) - } - - obj := conn.Object(notifyDest, notifyPath) - call := obj.Call( - notifyInterface+".Notify", - 0, - n.AppName, - uint32(0), - n.Icon, - n.Summary, - n.Body, - actions, - hints, - n.Timeout, - ) - - if call.Err != nil { - return fmt.Errorf("notify call failed: %w", call.Err) - } - - var notificationID uint32 - if err := call.Store(¬ificationID); err != nil { - return fmt.Errorf("failed to get notification id: %w", err) - } - - if len(actions) > 0 && n.FilePath != "" { - spawnActionListener(notificationID, n.FilePath) - } - - return nil -} - -func spawnActionListener(notificationID uint32, filePath string) { - exe, err := os.Executable() - if err != nil { - return - } - - cmd := exec.Command(exe, "notify-action-generic", fmt.Sprintf("%d", notificationID), filePath) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - cmd.Start() -} - -func RunActionListener(args []string) { - if len(args) < 2 { - return - } - - notificationID, err := strconv.ParseUint(args[0], 10, 32) - if err != nil { - return - } - - filePath := args[1] - - conn, err := dbus.SessionBus() - if err != nil { - return - } - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(notifyPath), - dbus.WithMatchInterface(notifyInterface), - ); err != nil { - return - } - - signals := make(chan *dbus.Signal, 10) - conn.Signal(signals) - - for sig := range signals { - switch sig.Name { - case notifyInterface + ".ActionInvoked": - if len(sig.Body) < 2 { - continue - } - id, ok := sig.Body[0].(uint32) - if !ok || id != uint32(notificationID) { - continue - } - action, ok := sig.Body[1].(string) - if !ok { - continue - } - handleAction(action, filePath) - return - - case notifyInterface + ".NotificationClosed": - if len(sig.Body) < 1 { - continue - } - id, ok := sig.Body[0].(uint32) - if !ok || id != uint32(notificationID) { - continue - } - return - } - } -} - -func handleAction(action, filePath string) { - switch action { - case "open", "default": - openPath(filePath) - case "folder": - openPath(filepath.Dir(filePath)) - } -} - -func openPath(path string) { - cmd := exec.Command("xdg-open", path) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - cmd.Start() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/pam/pam.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/pam/pam.go deleted file mode 100644 index 16ca3f9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/pam/pam.go +++ /dev/null @@ -1,892 +0,0 @@ -package pam - -import ( - "context" - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" -) - -const ( - GreeterPamManagedBlockStart = "# BEGIN DMS GREETER AUTH (managed by dms greeter sync)" - GreeterPamManagedBlockEnd = "# END DMS GREETER AUTH" - - LockscreenPamManagedBlockStart = "# BEGIN DMS LOCKSCREEN AUTH (managed by dms greeter sync)" - LockscreenPamManagedBlockEnd = "# END DMS LOCKSCREEN AUTH" - - LockscreenU2FPamManagedBlockStart = "# BEGIN DMS LOCKSCREEN U2F AUTH (managed by dms auth sync)" - LockscreenU2FPamManagedBlockEnd = "# END DMS LOCKSCREEN U2F AUTH" - - legacyGreeterPamFprintComment = "# DMS greeter fingerprint" - legacyGreeterPamU2FComment = "# DMS greeter U2F" - - GreetdPamPath = "/etc/pam.d/greetd" - DankshellPamPath = "/etc/pam.d/dankshell" - DankshellU2FPamPath = "/etc/pam.d/dankshell-u2f" -) - -var includedPamAuthFiles = []string{ - "system-auth", - "common-auth", - "password-auth", - "system-login", - "system-local-login", - "common-auth-pc", - "login", -} - -type AuthSettings struct { - EnableFprint bool `json:"enableFprint"` - EnableU2f bool `json:"enableU2f"` - GreeterEnableFprint bool `json:"greeterEnableFprint"` - GreeterEnableU2f bool `json:"greeterEnableU2f"` -} - -type SyncAuthOptions struct { - HomeDir string - ForceGreeterAuth bool -} - -type syncDeps struct { - pamDir string - greetdPath string - dankshellPath string - dankshellU2fPath string - isNixOS func() bool - readFile func(string) ([]byte, error) - stat func(string) (os.FileInfo, error) - createTemp func(string, string) (*os.File, error) - removeFile func(string) error - runSudoCmd func(string, string, ...string) error - pamModuleExists func(string) bool - fingerprintAvailableForCurrentUser func() bool -} - -type lockscreenPamIncludeDirective struct { - target string - filterType string -} - -type lockscreenPamResolver struct { - pamDir string - readFile func(string) ([]byte, error) -} - -func defaultSyncDeps() syncDeps { - return syncDeps{ - pamDir: "/etc/pam.d", - greetdPath: GreetdPamPath, - dankshellPath: DankshellPamPath, - dankshellU2fPath: DankshellU2FPamPath, - isNixOS: IsNixOS, - readFile: os.ReadFile, - stat: os.Stat, - createTemp: os.CreateTemp, - removeFile: os.Remove, - runSudoCmd: runSudoCmd, - pamModuleExists: pamModuleExists, - fingerprintAvailableForCurrentUser: FingerprintAuthAvailableForCurrentUser, - } -} - -func IsNixOS() bool { - _, err := os.Stat("/etc/NIXOS") - return err == nil -} - -func ReadAuthSettings(homeDir string) (AuthSettings, error) { - settingsPath := filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json") - data, err := os.ReadFile(settingsPath) - if err != nil { - if os.IsNotExist(err) { - return AuthSettings{}, nil - } - return AuthSettings{}, fmt.Errorf("failed to read settings at %s: %w", settingsPath, err) - } - if strings.TrimSpace(string(data)) == "" { - return AuthSettings{}, nil - } - - var settings AuthSettings - if err := json.Unmarshal(data, &settings); err != nil { - return AuthSettings{}, fmt.Errorf("failed to parse settings at %s: %w", settingsPath, err) - } - return settings, nil -} - -func ReadGreeterAuthToggles(homeDir string) (enableFprint bool, enableU2f bool, err error) { - settings, err := ReadAuthSettings(homeDir) - if err != nil { - return false, false, err - } - return settings.GreeterEnableFprint, settings.GreeterEnableU2f, nil -} - -func SyncAuthConfig(logFunc func(string), sudoPassword string, options SyncAuthOptions) error { - return syncAuthConfigWithDeps(logFunc, sudoPassword, options, defaultSyncDeps()) -} - -func RemoveManagedGreeterPamBlock(logFunc func(string), sudoPassword string) error { - return removeManagedGreeterPamBlockWithDeps(logFunc, sudoPassword, defaultSyncDeps()) -} - -func syncAuthConfigWithDeps(logFunc func(string), sudoPassword string, options SyncAuthOptions, deps syncDeps) error { - homeDir := strings.TrimSpace(options.HomeDir) - if homeDir == "" { - var err error - homeDir, err = os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - } - - settings, err := ReadAuthSettings(homeDir) - if err != nil { - return err - } - - if err := syncLockscreenPamConfigWithDeps(logFunc, sudoPassword, deps); err != nil { - return err - } - if err := syncLockscreenU2FPamConfigWithDeps(logFunc, sudoPassword, settings.EnableU2f, deps); err != nil { - return err - } - - if _, err := deps.stat(deps.greetdPath); err != nil { - if os.IsNotExist(err) { - logFunc("ℹ /etc/pam.d/greetd not found. Skipping greeter PAM sync.") - return nil - } - return fmt.Errorf("failed to inspect %s: %w", deps.greetdPath, err) - } - - if err := syncGreeterPamConfigWithDeps(logFunc, sudoPassword, settings, options.ForceGreeterAuth, deps); err != nil { - return err - } - - return nil -} - -func removeManagedGreeterPamBlockWithDeps(logFunc func(string), sudoPassword string, deps syncDeps) error { - if deps.isNixOS() { - return nil - } - - data, err := deps.readFile(deps.greetdPath) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return fmt.Errorf("failed to read %s: %w", deps.greetdPath, err) - } - - originalContent := string(data) - stripped, removed := stripManagedGreeterPamBlock(originalContent) - strippedAgain, removedLegacy := stripLegacyGreeterPamLines(stripped) - if !removed && !removedLegacy { - return nil - } - - if err := writeManagedPamFile(strippedAgain, deps.greetdPath, sudoPassword, deps); err != nil { - return fmt.Errorf("failed to write %s: %w", deps.greetdPath, err) - } - - logFunc("✓ Removed DMS managed PAM block from " + deps.greetdPath) - return nil -} - -func ParseManagedGreeterPamAuth(pamText string) (managed bool, fingerprint bool, u2f bool, legacy bool) { - if pamText == "" { - return false, false, false, false - } - - lines := strings.Split(pamText, "\n") - inManaged := false - for _, line := range lines { - trimmed := strings.TrimSpace(line) - switch trimmed { - case GreeterPamManagedBlockStart: - managed = true - inManaged = true - continue - case GreeterPamManagedBlockEnd: - inManaged = false - continue - } - - if strings.HasPrefix(trimmed, legacyGreeterPamFprintComment) || strings.HasPrefix(trimmed, legacyGreeterPamU2FComment) { - legacy = true - } - if !inManaged { - continue - } - if strings.Contains(trimmed, "pam_fprintd") { - fingerprint = true - } - if strings.Contains(trimmed, "pam_u2f") { - u2f = true - } - } - - return managed, fingerprint, u2f, legacy -} - -func StripManagedGreeterPamContent(pamText string) (string, bool) { - stripped, removed := stripManagedGreeterPamBlock(pamText) - stripped, removedLegacy := stripLegacyGreeterPamLines(stripped) - return stripped, removed || removedLegacy -} - -func PamTextIncludesFile(pamText, filename string) bool { - lines := strings.Split(pamText, "\n") - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if trimmed == "" || strings.HasPrefix(trimmed, "#") { - continue - } - if strings.Contains(trimmed, filename) && - (strings.Contains(trimmed, "include") || strings.Contains(trimmed, "substack") || strings.HasPrefix(trimmed, "@include")) { - return true - } - } - return false -} - -func PamFileHasModule(pamFilePath, module string) bool { - data, err := os.ReadFile(pamFilePath) - if err != nil { - return false - } - return pamContentHasModule(string(data), module) -} - -func DetectIncludedPamModule(pamText, module string) string { - return detectIncludedPamModule(pamText, module, defaultSyncDeps()) -} - -func detectIncludedPamModule(pamText, module string, deps syncDeps) string { - for _, includedFile := range includedPamAuthFiles { - if !PamTextIncludesFile(pamText, includedFile) { - continue - } - path := filepath.Join(deps.pamDir, includedFile) - data, err := deps.readFile(path) - if err != nil { - continue - } - if pamContentHasModule(string(data), module) { - return includedFile - } - } - return "" -} - -func pamContentHasModule(content, module string) bool { - lines := strings.Split(content, "\n") - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if trimmed == "" || strings.HasPrefix(trimmed, "#") { - continue - } - if strings.Contains(trimmed, module) { - return true - } - } - return false -} - -func hasManagedLockscreenPamFile(content string) bool { - return strings.Contains(content, LockscreenPamManagedBlockStart) && - strings.Contains(content, LockscreenPamManagedBlockEnd) -} - -func hasManagedLockscreenU2FPamFile(content string) bool { - return strings.Contains(content, LockscreenU2FPamManagedBlockStart) && - strings.Contains(content, LockscreenU2FPamManagedBlockEnd) -} - -func pamDirectiveType(line string) string { - fields := strings.Fields(line) - if len(fields) == 0 { - return "" - } - - directiveType := strings.TrimPrefix(fields[0], "-") - switch directiveType { - case "auth", "account", "password", "session": - return directiveType - default: - return "" - } -} - -func isExcludedLockscreenPamLine(line string) bool { - for _, field := range strings.Fields(line) { - if strings.HasPrefix(field, "#") { - break - } - if strings.Contains(field, "pam_u2f") || strings.Contains(field, "pam_fprintd") { - return true - } - } - return false -} - -func parseLockscreenPamIncludeDirective(trimmed string, inheritedFilter string) (lockscreenPamIncludeDirective, bool) { - fields := strings.Fields(trimmed) - if len(fields) >= 2 && fields[0] == "@include" { - return lockscreenPamIncludeDirective{ - target: fields[1], - filterType: inheritedFilter, - }, true - } - - if len(fields) >= 3 && (fields[1] == "include" || fields[1] == "substack") { - lineType := pamDirectiveType(trimmed) - if lineType == "" { - return lockscreenPamIncludeDirective{}, false - } - return lockscreenPamIncludeDirective{ - target: fields[2], - filterType: lineType, - }, true - } - - if len(fields) >= 3 && fields[1] == "@include" { - lineType := pamDirectiveType(trimmed) - if lineType == "" { - return lockscreenPamIncludeDirective{}, false - } - return lockscreenPamIncludeDirective{ - target: fields[2], - filterType: lineType, - }, true - } - - return lockscreenPamIncludeDirective{}, false -} - -func resolveLockscreenPamIncludePath(pamDir, target string) (string, error) { - if strings.TrimSpace(target) == "" { - return "", fmt.Errorf("empty PAM include target") - } - - cleanPamDir := filepath.Clean(pamDir) - if filepath.IsAbs(target) { - cleanTarget := filepath.Clean(target) - if filepath.Dir(cleanTarget) != cleanPamDir { - return "", fmt.Errorf("unsupported PAM include outside %s: %s", cleanPamDir, target) - } - return cleanTarget, nil - } - - cleanTarget := filepath.Clean(target) - if cleanTarget == "." || cleanTarget == ".." || strings.HasPrefix(cleanTarget, ".."+string(os.PathSeparator)) { - return "", fmt.Errorf("invalid PAM include target: %s", target) - } - - return filepath.Join(cleanPamDir, cleanTarget), nil -} - -func (r lockscreenPamResolver) resolveService(serviceName string, filterType string, stack []string) ([]string, error) { - path, err := resolveLockscreenPamIncludePath(r.pamDir, serviceName) - if err != nil { - return nil, err - } - - for _, seen := range stack { - if seen == path { - chain := append(append([]string{}, stack...), path) - display := make([]string, 0, len(chain)) - for _, item := range chain { - display = append(display, filepath.Base(item)) - } - return nil, fmt.Errorf("cyclic PAM include detected: %s", strings.Join(display, " -> ")) - } - } - - data, err := r.readFile(path) - if err != nil { - return nil, fmt.Errorf("failed to read PAM file %s: %w", path, err) - } - - var resolved []string - for _, rawLine := range strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n") { - rawLine = strings.TrimRight(rawLine, "\r") - trimmed := strings.TrimSpace(rawLine) - if trimmed == "" || strings.HasPrefix(trimmed, "#") || trimmed == "#%PAM-1.0" { - continue - } - - if include, ok := parseLockscreenPamIncludeDirective(trimmed, filterType); ok { - lineType := pamDirectiveType(trimmed) - if filterType != "" && lineType != "" && lineType != filterType { - continue - } - - nested, err := r.resolveService(include.target, include.filterType, append(stack, path)) - if err != nil { - return nil, err - } - resolved = append(resolved, nested...) - continue - } - - lineType := pamDirectiveType(trimmed) - if lineType == "" { - return nil, fmt.Errorf("unsupported PAM directive in %s: %s", filepath.Base(path), trimmed) - } - if filterType != "" && lineType != filterType { - continue - } - if isExcludedLockscreenPamLine(trimmed) { - continue - } - - resolved = append(resolved, rawLine) - } - - return resolved, nil -} - -func buildManagedLockscreenPamContent(pamDir string, readFile func(string) ([]byte, error)) (string, error) { - resolver := lockscreenPamResolver{ - pamDir: pamDir, - readFile: readFile, - } - - resolvedLines, err := resolver.resolveService("login", "", nil) - if err != nil { - return "", err - } - if len(resolvedLines) == 0 { - return "", fmt.Errorf("no auth directives remained after filtering %s", filepath.Join(pamDir, "login")) - } - - hasAuth := false - for _, line := range resolvedLines { - if pamDirectiveType(strings.TrimSpace(line)) == "auth" { - hasAuth = true - break - } - } - if !hasAuth { - return "", fmt.Errorf("no auth directives remained after filtering %s", filepath.Join(pamDir, "login")) - } - - var b strings.Builder - b.WriteString("#%PAM-1.0\n") - b.WriteString(LockscreenPamManagedBlockStart + "\n") - for _, line := range resolvedLines { - b.WriteString(line) - b.WriteByte('\n') - } - b.WriteString(LockscreenPamManagedBlockEnd + "\n") - return b.String(), nil -} - -func buildManagedLockscreenU2FPamContent() string { - var b strings.Builder - b.WriteString("#%PAM-1.0\n") - b.WriteString(LockscreenU2FPamManagedBlockStart + "\n") - b.WriteString("auth required pam_u2f.so cue nouserok timeout=10\n") - b.WriteString(LockscreenU2FPamManagedBlockEnd + "\n") - return b.String() -} - -func syncLockscreenPamConfigWithDeps(logFunc func(string), sudoPassword string, deps syncDeps) error { - if deps.isNixOS() { - logFunc("ℹ NixOS detected. DMS continues to use /etc/pam.d/login for lock screen password auth on NixOS unless you declare security.pam.services.dankshell yourself. U2F and fingerprint are handled separately and should not be included in dankshell.") - return nil - } - - existingData, err := deps.readFile(deps.dankshellPath) - if err == nil { - if !hasManagedLockscreenPamFile(string(existingData)) { - logFunc("ℹ Custom /etc/pam.d/dankshell found (no DMS block). Skipping.") - return nil - } - } else if !os.IsNotExist(err) { - return fmt.Errorf("failed to read %s: %w", deps.dankshellPath, err) - } - - content, err := buildManagedLockscreenPamContent(deps.pamDir, deps.readFile) - if err != nil { - return fmt.Errorf("failed to build %s from %s: %w", deps.dankshellPath, filepath.Join(deps.pamDir, "login"), err) - } - - if err := writeManagedPamFile(content, deps.dankshellPath, sudoPassword, deps); err != nil { - return fmt.Errorf("failed to write %s: %w", deps.dankshellPath, err) - } - - logFunc("✓ Created or updated /etc/pam.d/dankshell for lock screen authentication") - return nil -} - -func syncLockscreenU2FPamConfigWithDeps(logFunc func(string), sudoPassword string, enabled bool, deps syncDeps) error { - if deps.isNixOS() { - logFunc("ℹ NixOS detected. DMS does not manage /etc/pam.d/dankshell-u2f on NixOS. Keep using the bundled U2F helper or configure a custom PAM service yourself.") - return nil - } - - existingData, err := deps.readFile(deps.dankshellU2fPath) - if err != nil && !os.IsNotExist(err) { - return fmt.Errorf("failed to read %s: %w", deps.dankshellU2fPath, err) - } - - if enabled { - if err == nil && !hasManagedLockscreenU2FPamFile(string(existingData)) { - logFunc("ℹ Custom /etc/pam.d/dankshell-u2f found (no DMS block). Skipping.") - return nil - } - if err := writeManagedPamFile(buildManagedLockscreenU2FPamContent(), deps.dankshellU2fPath, sudoPassword, deps); err != nil { - return fmt.Errorf("failed to write %s: %w", deps.dankshellU2fPath, err) - } - logFunc("✓ Created or updated /etc/pam.d/dankshell-u2f for lock screen security-key authentication") - return nil - } - - if os.IsNotExist(err) { - return nil - } - if err == nil && !hasManagedLockscreenU2FPamFile(string(existingData)) { - logFunc("ℹ Custom /etc/pam.d/dankshell-u2f found (no DMS block). Leaving it untouched.") - return nil - } - - if err := deps.runSudoCmd(sudoPassword, "rm", "-f", deps.dankshellU2fPath); err != nil { - return fmt.Errorf("failed to remove %s: %w", deps.dankshellU2fPath, err) - } - logFunc("✓ Removed DMS-managed /etc/pam.d/dankshell-u2f") - return nil -} - -func stripManagedGreeterPamBlock(content string) (string, bool) { - lines := strings.Split(content, "\n") - filtered := make([]string, 0, len(lines)) - inManagedBlock := false - removed := false - - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if trimmed == GreeterPamManagedBlockStart { - inManagedBlock = true - removed = true - continue - } - if trimmed == GreeterPamManagedBlockEnd { - inManagedBlock = false - removed = true - continue - } - if inManagedBlock { - removed = true - continue - } - filtered = append(filtered, line) - } - - return strings.Join(filtered, "\n"), removed -} - -func stripLegacyGreeterPamLines(content string) (string, bool) { - lines := strings.Split(content, "\n") - filtered := make([]string, 0, len(lines)) - removed := false - - for i := 0; i < len(lines); i++ { - trimmed := strings.TrimSpace(lines[i]) - if strings.HasPrefix(trimmed, legacyGreeterPamFprintComment) || strings.HasPrefix(trimmed, legacyGreeterPamU2FComment) { - removed = true - if i+1 < len(lines) { - nextLine := strings.TrimSpace(lines[i+1]) - if strings.HasPrefix(nextLine, "auth") && - (strings.Contains(nextLine, "pam_fprintd") || strings.Contains(nextLine, "pam_u2f")) { - i++ - } - } - continue - } - filtered = append(filtered, lines[i]) - } - - return strings.Join(filtered, "\n"), removed -} - -func insertManagedGreeterPamBlock(content string, blockLines []string, greetdPamPath string) (string, error) { - lines := strings.Split(content, "\n") - for i, line := range lines { - trimmed := strings.TrimSpace(line) - if trimmed != "" && !strings.HasPrefix(trimmed, "#") && strings.HasPrefix(trimmed, "auth") { - block := strings.Join(blockLines, "\n") - prefix := strings.Join(lines[:i], "\n") - suffix := strings.Join(lines[i:], "\n") - switch { - case prefix == "": - return block + "\n" + suffix, nil - case suffix == "": - return prefix + "\n" + block, nil - default: - return prefix + "\n" + block + "\n" + suffix, nil - } - } - } - return "", fmt.Errorf("no auth directive found in %s", greetdPamPath) -} - -func syncGreeterPamConfigWithDeps(logFunc func(string), sudoPassword string, settings AuthSettings, forceAuth bool, deps syncDeps) error { - var wantFprint, wantU2f bool - fprintToggleEnabled := forceAuth - u2fToggleEnabled := forceAuth - if forceAuth { - wantFprint = deps.pamModuleExists("pam_fprintd.so") - wantU2f = deps.pamModuleExists("pam_u2f.so") - } else { - fprintToggleEnabled = settings.GreeterEnableFprint - u2fToggleEnabled = settings.GreeterEnableU2f - fprintModule := deps.pamModuleExists("pam_fprintd.so") - u2fModule := deps.pamModuleExists("pam_u2f.so") - wantFprint = settings.GreeterEnableFprint && fprintModule - wantU2f = settings.GreeterEnableU2f && u2fModule - if settings.GreeterEnableFprint && !fprintModule { - logFunc("⚠ Warning: greeter fingerprint toggle is enabled, but pam_fprintd.so was not found.") - } - if settings.GreeterEnableU2f && !u2fModule { - logFunc("⚠ Warning: greeter security key toggle is enabled, but pam_u2f.so was not found.") - } - } - - if deps.isNixOS() { - logFunc("ℹ NixOS detected: PAM config is managed by NixOS modules. Skipping DMS PAM block write.") - logFunc(" Configure fingerprint/U2F auth via your greetd NixOS module options (e.g. security.pam.services.greetd).") - return nil - } - - pamData, err := deps.readFile(deps.greetdPath) - if err != nil { - return fmt.Errorf("failed to read %s: %w", deps.greetdPath, err) - } - originalContent := string(pamData) - content, _ := stripManagedGreeterPamBlock(originalContent) - content, _ = stripLegacyGreeterPamLines(content) - - includedFprintFile := detectIncludedPamModule(content, "pam_fprintd.so", deps) - includedU2fFile := detectIncludedPamModule(content, "pam_u2f.so", deps) - fprintAvailableForCurrentUser := deps.fingerprintAvailableForCurrentUser() - if wantFprint && includedFprintFile != "" { - logFunc("⚠ pam_fprintd already present in included " + includedFprintFile + " (managed by authselect/pam-auth-update). Skipping DMS fprint block to avoid double-fingerprint auth.") - wantFprint = false - } - if wantU2f && includedU2fFile != "" { - logFunc("⚠ pam_u2f already present in included " + includedU2fFile + " (managed by authselect/pam-auth-update). Skipping DMS U2F block to avoid double security-key auth.") - wantU2f = false - } - if !wantFprint && includedFprintFile != "" { - if fprintToggleEnabled { - logFunc("ℹ Fingerprint auth is still enabled via included " + includedFprintFile + ".") - if fprintAvailableForCurrentUser { - logFunc(" DMS toggle is enabled, and effective auth is provided by the included PAM stack.") - } else { - logFunc(" No enrolled fingerprints detected for the current user; password auth remains the effective path.") - } - } else { - if fprintAvailableForCurrentUser { - logFunc("ℹ Fingerprint auth is active via included " + includedFprintFile + " while DMS fingerprint toggle is off.") - logFunc(" Password login will work but may be delayed while the fingerprint module runs first.") - logFunc(" To eliminate the delay, " + pamManagerHintForCurrentDistro()) - } else { - logFunc("ℹ pam_fprintd is present via included " + includedFprintFile + ", but no enrolled fingerprints were detected for the current user.") - logFunc(" Password auth remains the effective login path.") - } - } - } - if !wantU2f && includedU2fFile != "" { - if u2fToggleEnabled { - logFunc("ℹ Security-key auth is still enabled via included " + includedU2fFile + ".") - logFunc(" DMS toggle is enabled, but effective auth is provided by the included PAM stack.") - } else { - logFunc("⚠ Security-key auth is active via included " + includedU2fFile + " while DMS security-key toggle is off.") - logFunc(" " + pamManagerHintForCurrentDistro()) - } - } - - if wantFprint || wantU2f { - blockLines := []string{GreeterPamManagedBlockStart} - if wantFprint { - blockLines = append(blockLines, "auth sufficient pam_fprintd.so max-tries=1 timeout=5") - } - if wantU2f { - blockLines = append(blockLines, "auth sufficient pam_u2f.so cue nouserok timeout=10") - } - blockLines = append(blockLines, GreeterPamManagedBlockEnd) - - content, err = insertManagedGreeterPamBlock(content, blockLines, deps.greetdPath) - if err != nil { - return err - } - } - - if content == originalContent { - return nil - } - - if err := writeManagedPamFile(content, deps.greetdPath, sudoPassword, deps); err != nil { - return fmt.Errorf("failed to install updated PAM config at %s: %w", deps.greetdPath, err) - } - if wantFprint || wantU2f { - logFunc("✓ Configured greetd PAM for fingerprint/U2F") - } else { - logFunc("✓ Cleared DMS-managed greeter PAM auth block") - } - return nil -} - -func writeManagedPamFile(content string, destPath string, sudoPassword string, deps syncDeps) error { - tmpFile, err := deps.createTemp("", "dms-pam-*.conf") - if err != nil { - return err - } - tmpPath := tmpFile.Name() - defer func() { - _ = deps.removeFile(tmpPath) - }() - - if _, err := tmpFile.WriteString(content); err != nil { - tmpFile.Close() - return err - } - if err := tmpFile.Close(); err != nil { - return err - } - if err := deps.runSudoCmd(sudoPassword, "cp", tmpPath, destPath); err != nil { - return err - } - if err := deps.runSudoCmd(sudoPassword, "chmod", "644", destPath); err != nil { - return fmt.Errorf("failed to set permissions on %s: %w", destPath, err) - } - return nil -} - -func pamManagerHintForCurrentDistro() string { - osInfo, err := distros.GetOSInfo() - if err != nil { - return "Disable it in your PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login." - } - config, exists := distros.Registry[osInfo.Distribution.ID] - if !exists { - return "Disable it in your PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login." - } - - switch config.Family { - case distros.FamilyFedora: - return "Disable it in authselect to force password-only greeter login." - case distros.FamilyDebian, distros.FamilyUbuntu: - return "Disable it in pam-auth-update to force password-only greeter login." - default: - return "Disable it in your distro PAM manager (authselect/pam-auth-update) or in the included PAM stack to force password-only greeter login." - } -} - -func pamModuleExists(module string) bool { - for _, libDir := range []string{ - "/usr/lib64/security", - "/usr/lib/security", - "/lib64/security", - "/lib/security", - "/lib/x86_64-linux-gnu/security", - "/usr/lib/x86_64-linux-gnu/security", - "/lib/aarch64-linux-gnu/security", - "/usr/lib/aarch64-linux-gnu/security", - "/run/current-system/sw/lib64/security", - "/run/current-system/sw/lib/security", - } { - if _, err := os.Stat(filepath.Join(libDir, module)); err == nil { - return true - } - } - return false -} - -func hasEnrolledFingerprintOutput(output string) bool { - lower := strings.ToLower(output) - if strings.Contains(lower, "no fingers enrolled") || - strings.Contains(lower, "no fingerprints enrolled") || - strings.Contains(lower, "no prints enrolled") { - return false - } - if strings.Contains(lower, "has fingers enrolled") || - strings.Contains(lower, "has fingerprints enrolled") { - return true - } - for _, line := range strings.Split(lower, "\n") { - trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, "finger:") { - return true - } - if strings.HasPrefix(trimmed, "- ") && strings.Contains(trimmed, "finger") { - return true - } - } - return false -} - -func FingerprintAuthAvailableForCurrentUser() bool { - username := strings.TrimSpace(os.Getenv("SUDO_USER")) - if username == "" { - username = strings.TrimSpace(os.Getenv("USER")) - } - if username == "" { - out, err := exec.Command("id", "-un").Output() - if err == nil { - username = strings.TrimSpace(string(out)) - } - } - return fingerprintAuthAvailableForUser(username) -} - -func fingerprintAuthAvailableForUser(username string) bool { - username = strings.TrimSpace(username) - if username == "" { - return false - } - if !pamModuleExists("pam_fprintd.so") { - return false - } - if _, err := exec.LookPath("fprintd-list"); err != nil { - return false - } - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - out, err := exec.CommandContext(ctx, "fprintd-list", username).CombinedOutput() - if err != nil { - return false - } - return hasEnrolledFingerprintOutput(string(out)) -} - -func runSudoCmd(sudoPassword string, command string, args ...string) error { - var cmd *exec.Cmd - - if sudoPassword != "" { - fullArgs := append([]string{command}, args...) - quotedArgs := make([]string, len(fullArgs)) - for i, arg := range fullArgs { - quotedArgs[i] = "'" + strings.ReplaceAll(arg, "'", "'\\''") + "'" - } - cmdStr := strings.Join(quotedArgs, " ") - - cmd = distros.ExecSudoCommand(context.Background(), sudoPassword, cmdStr) - } else { - cmd = exec.Command("sudo", append([]string{command}, args...)...) - } - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/pam/pam_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/pam/pam_test.go deleted file mode 100644 index 113bf10..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/pam/pam_test.go +++ /dev/null @@ -1,671 +0,0 @@ -package pam - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "testing" -) - -func writeTestFile(t *testing.T, path string, content string) { - t.Helper() - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - t.Fatalf("failed to create parent dir for %s: %v", path, err) - } - if err := os.WriteFile(path, []byte(content), 0o644); err != nil { - t.Fatalf("failed to write %s: %v", path, err) - } -} - -type pamTestEnv struct { - pamDir string - greetdPath string - dankshellPath string - dankshellU2fPath string - tmpDir string - homeDir string - availableModules map[string]bool - fingerprintAvailable bool -} - -func newPamTestEnv(t *testing.T) *pamTestEnv { - t.Helper() - - root := t.TempDir() - pamDir := filepath.Join(root, "pam.d") - tmpDir := filepath.Join(root, "tmp") - homeDir := filepath.Join(root, "home") - - for _, dir := range []string{pamDir, tmpDir, homeDir} { - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatalf("failed to create %s: %v", dir, err) - } - } - - return &pamTestEnv{ - pamDir: pamDir, - greetdPath: filepath.Join(pamDir, "greetd"), - dankshellPath: filepath.Join(pamDir, "dankshell"), - dankshellU2fPath: filepath.Join(pamDir, "dankshell-u2f"), - tmpDir: tmpDir, - homeDir: homeDir, - availableModules: map[string]bool{}, - } -} - -func (e *pamTestEnv) writePamFile(t *testing.T, name string, content string) { - t.Helper() - writeTestFile(t, filepath.Join(e.pamDir, name), content) -} - -func (e *pamTestEnv) writeSettings(t *testing.T, content string) { - t.Helper() - writeTestFile(t, filepath.Join(e.homeDir, ".config", "DankMaterialShell", "settings.json"), content) -} - -func (e *pamTestEnv) deps(isNixOS bool) syncDeps { - return syncDeps{ - pamDir: e.pamDir, - greetdPath: e.greetdPath, - dankshellPath: e.dankshellPath, - dankshellU2fPath: e.dankshellU2fPath, - isNixOS: func() bool { return isNixOS }, - readFile: os.ReadFile, - stat: os.Stat, - createTemp: func(_ string, pattern string) (*os.File, error) { - return os.CreateTemp(e.tmpDir, pattern) - }, - removeFile: os.Remove, - runSudoCmd: func(_ string, command string, args ...string) error { - switch command { - case "cp": - if len(args) != 2 { - return fmt.Errorf("unexpected cp args: %v", args) - } - data, err := os.ReadFile(args[0]) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(args[1]), 0o755); err != nil { - return err - } - return os.WriteFile(args[1], data, 0o644) - case "chmod": - if len(args) != 2 { - return fmt.Errorf("unexpected chmod args: %v", args) - } - return nil - case "rm": - if len(args) != 2 || args[0] != "-f" { - return fmt.Errorf("unexpected rm args: %v", args) - } - if err := os.Remove(args[1]); err != nil && !os.IsNotExist(err) { - return err - } - return nil - default: - return fmt.Errorf("unexpected sudo command: %s %v", command, args) - } - }, - pamModuleExists: func(module string) bool { - return e.availableModules[module] - }, - fingerprintAvailableForCurrentUser: func() bool { - return e.fingerprintAvailable - }, - } -} - -func readFileString(t *testing.T, path string) string { - t.Helper() - data, err := os.ReadFile(path) - if err != nil { - t.Fatalf("failed to read %s: %v", path, err) - } - return string(data) -} - -func TestHasManagedLockscreenPamFile(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - content string - want bool - }{ - { - name: "both markers present", - content: "#%PAM-1.0\n" + - LockscreenPamManagedBlockStart + "\n" + - "auth sufficient pam_unix.so\n" + - LockscreenPamManagedBlockEnd + "\n", - want: true, - }, - { - name: "missing end marker is not managed", - content: "#%PAM-1.0\n" + - LockscreenPamManagedBlockStart + "\n" + - "auth sufficient pam_unix.so\n", - want: false, - }, - { - name: "custom file is not managed", - content: "#%PAM-1.0\nauth sufficient pam_unix.so\n", - want: false, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - if got := hasManagedLockscreenPamFile(tt.content); got != tt.want { - t.Fatalf("hasManagedLockscreenPamFile() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestBuildManagedLockscreenPamContent(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - files map[string]string - wantContains []string - wantNotContains []string - wantCounts map[string]int - wantErr string - }{ - { - name: "preserves custom modules and strips direct u2f and fprint directives", - files: map[string]string{ - "login": "#%PAM-1.0\n" + - "auth include system-auth\n" + - "account include system-auth\n" + - "session include system-auth\n", - "system-auth": "auth requisite pam_nologin.so\n" + - "auth sufficient pam_unix.so try_first_pass nullok\n" + - "auth sufficient pam_u2f.so cue\n" + - "auth sufficient pam_fprintd.so max-tries=1\n" + - "auth required pam_radius_auth.so conf=/etc/raddb/server\n" + - "account required pam_access.so\n" + - "session optional pam_lastlog.so silent\n", - }, - wantContains: []string{ - "#%PAM-1.0", - LockscreenPamManagedBlockStart, - LockscreenPamManagedBlockEnd, - "auth requisite pam_nologin.so", - "auth sufficient pam_unix.so try_first_pass nullok", - "auth required pam_radius_auth.so conf=/etc/raddb/server", - "account required pam_access.so", - "session optional pam_lastlog.so silent", - }, - wantNotContains: []string{ - "pam_u2f", - "pam_fprintd", - }, - wantCounts: map[string]int{ - "auth required pam_radius_auth.so conf=/etc/raddb/server": 1, - "account required pam_access.so": 1, - }, - }, - { - name: "resolves nested include substack and @include transitively", - files: map[string]string{ - "login": "#%PAM-1.0\n" + - "auth include system-auth\n" + - "account include system-auth\n" + - "password include system-auth\n" + - "session include system-auth\n", - "system-auth": "auth substack custom-auth\n" + - "account include custom-auth\n" + - "password include custom-auth\n" + - "session @include common-session\n", - "custom-auth": "auth required pam_custom.so one=two\n" + - "account required pam_custom_account.so\n" + - "password required pam_custom_password.so\n", - "common-session": "session optional pam_fprintd.so max-tries=1\n" + - "session optional pam_lastlog.so silent\n", - }, - wantContains: []string{ - "auth required pam_custom.so one=two", - "account required pam_custom_account.so", - "password required pam_custom_password.so", - "session optional pam_lastlog.so silent", - }, - wantNotContains: []string{ - "pam_fprintd", - }, - wantCounts: map[string]int{ - "auth required pam_custom.so one=two": 1, - "account required pam_custom_account.so": 1, - "password required pam_custom_password.so": 1, - "session optional pam_lastlog.so silent": 1, - }, - }, - { - name: "missing include fails", - files: map[string]string{ - "login": "#%PAM-1.0\nauth include missing-auth\n", - }, - wantErr: "failed to read PAM file", - }, - { - name: "cyclic include fails", - files: map[string]string{ - "login": "#%PAM-1.0\nauth include system-auth\n", - "system-auth": "auth include login\n", - }, - wantErr: "cyclic PAM include detected", - }, - { - name: "no auth directives remain after filtering fails", - files: map[string]string{ - "login": "#%PAM-1.0\nauth include system-auth\n", - "system-auth": "auth sufficient pam_u2f.so cue\n", - }, - wantErr: "no auth directives remained after filtering", - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - for name, content := range tt.files { - env.writePamFile(t, name, content) - } - - content, err := buildManagedLockscreenPamContent(env.pamDir, os.ReadFile) - if tt.wantErr != "" { - if err == nil { - t.Fatalf("expected error containing %q, got nil", tt.wantErr) - } - if !strings.Contains(err.Error(), tt.wantErr) { - t.Fatalf("error = %q, want substring %q", err.Error(), tt.wantErr) - } - return - } - if err != nil { - t.Fatalf("buildManagedLockscreenPamContent returned error: %v", err) - } - - for _, want := range tt.wantContains { - if !strings.Contains(content, want) { - t.Errorf("missing expected string %q in output:\n%s", want, content) - } - } - for _, notWant := range tt.wantNotContains { - if strings.Contains(content, notWant) { - t.Errorf("unexpected string %q found in output:\n%s", notWant, content) - } - } - for want, wantCount := range tt.wantCounts { - if gotCount := strings.Count(content, want); gotCount != wantCount { - t.Errorf("count for %q = %d, want %d\noutput:\n%s", want, gotCount, wantCount, content) - } - } - }) - } -} - -func TestSyncLockscreenPamConfigWithDeps(t *testing.T) { - t.Parallel() - - t.Run("custom dankshell file is skipped untouched", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - customContent := "#%PAM-1.0\nauth required pam_unix.so\n" - env.writePamFile(t, "dankshell", customContent) - - var logs []string - err := syncLockscreenPamConfigWithDeps(func(msg string) { - logs = append(logs, msg) - }, "", env.deps(false)) - if err != nil { - t.Fatalf("syncLockscreenPamConfigWithDeps returned error: %v", err) - } - - if got := readFileString(t, env.dankshellPath); got != customContent { - t.Fatalf("custom dankshell content changed\ngot:\n%s\nwant:\n%s", got, customContent) - } - if len(logs) == 0 || !strings.Contains(logs[0], "Custom /etc/pam.d/dankshell found") { - t.Fatalf("expected custom-file skip log, got %v", logs) - } - }) - - t.Run("managed dankshell file is rewritten from resolved login stack", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.writePamFile(t, "login", "#%PAM-1.0\nauth include system-auth\naccount include system-auth\n") - env.writePamFile(t, "system-auth", "auth sufficient pam_unix.so try_first_pass nullok\nauth sufficient pam_u2f.so cue\naccount required pam_access.so\n") - env.writePamFile(t, "dankshell", "#%PAM-1.0\n"+LockscreenPamManagedBlockStart+"\nauth required pam_env.so\n"+LockscreenPamManagedBlockEnd+"\n") - - var logs []string - err := syncLockscreenPamConfigWithDeps(func(msg string) { - logs = append(logs, msg) - }, "", env.deps(false)) - if err != nil { - t.Fatalf("syncLockscreenPamConfigWithDeps returned error: %v", err) - } - - output := readFileString(t, env.dankshellPath) - for _, want := range []string{ - LockscreenPamManagedBlockStart, - "auth sufficient pam_unix.so try_first_pass nullok", - "account required pam_access.so", - LockscreenPamManagedBlockEnd, - } { - if !strings.Contains(output, want) { - t.Errorf("missing expected string %q in rewritten dankshell:\n%s", want, output) - } - } - if strings.Contains(output, "pam_u2f") { - t.Errorf("rewritten dankshell still contains pam_u2f:\n%s", output) - } - if len(logs) == 0 || !strings.Contains(logs[len(logs)-1], "Created or updated /etc/pam.d/dankshell") { - t.Fatalf("expected success log, got %v", logs) - } - }) - - t.Run("mutable systems fail when login stack cannot be converted safely", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - err := syncLockscreenPamConfigWithDeps(func(string) {}, "", env.deps(false)) - if err == nil { - t.Fatal("expected error when login PAM file is missing, got nil") - } - if !strings.Contains(err.Error(), "failed to build") { - t.Fatalf("error = %q, want substring %q", err.Error(), "failed to build") - } - }) - - t.Run("NixOS remains informational and does not write dankshell", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - var logs []string - - err := syncLockscreenPamConfigWithDeps(func(msg string) { - logs = append(logs, msg) - }, "", env.deps(true)) - if err != nil { - t.Fatalf("syncLockscreenPamConfigWithDeps returned error on NixOS path: %v", err) - } - if len(logs) == 0 || !strings.Contains(logs[0], "NixOS detected") || !strings.Contains(logs[0], "/etc/pam.d/login") { - t.Fatalf("expected NixOS informational log mentioning /etc/pam.d/login, got %v", logs) - } - if _, err := os.Stat(env.dankshellPath); !os.IsNotExist(err) { - t.Fatalf("expected no dankshell file to be written on NixOS path, stat err = %v", err) - } - }) -} - -func TestSyncLockscreenU2FPamConfigWithDeps(t *testing.T) { - t.Parallel() - - t.Run("enabled creates managed file", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - var logs []string - - err := syncLockscreenU2FPamConfigWithDeps(func(msg string) { - logs = append(logs, msg) - }, "", true, env.deps(false)) - if err != nil { - t.Fatalf("syncLockscreenU2FPamConfigWithDeps returned error: %v", err) - } - - got := readFileString(t, env.dankshellU2fPath) - if got != buildManagedLockscreenU2FPamContent() { - t.Fatalf("unexpected managed dankshell-u2f content:\n%s", got) - } - if len(logs) == 0 || !strings.Contains(logs[len(logs)-1], "Created or updated /etc/pam.d/dankshell-u2f") { - t.Fatalf("expected create log, got %v", logs) - } - }) - - t.Run("enabled rewrites existing managed file", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.writePamFile(t, "dankshell-u2f", "#%PAM-1.0\n"+LockscreenU2FPamManagedBlockStart+"\nauth required pam_u2f.so old\n"+LockscreenU2FPamManagedBlockEnd+"\n") - - if err := syncLockscreenU2FPamConfigWithDeps(func(string) {}, "", true, env.deps(false)); err != nil { - t.Fatalf("syncLockscreenU2FPamConfigWithDeps returned error: %v", err) - } - if got := readFileString(t, env.dankshellU2fPath); got != buildManagedLockscreenU2FPamContent() { - t.Fatalf("managed dankshell-u2f was not rewritten:\n%s", got) - } - }) - - t.Run("disabled removes DMS-managed file", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.writePamFile(t, "dankshell-u2f", buildManagedLockscreenU2FPamContent()) - - var logs []string - err := syncLockscreenU2FPamConfigWithDeps(func(msg string) { - logs = append(logs, msg) - }, "", false, env.deps(false)) - if err != nil { - t.Fatalf("syncLockscreenU2FPamConfigWithDeps returned error: %v", err) - } - if _, err := os.Stat(env.dankshellU2fPath); !os.IsNotExist(err) { - t.Fatalf("expected managed dankshell-u2f to be removed, stat err = %v", err) - } - if len(logs) == 0 || !strings.Contains(logs[len(logs)-1], "Removed DMS-managed /etc/pam.d/dankshell-u2f") { - t.Fatalf("expected removal log, got %v", logs) - } - }) - - t.Run("disabled preserves custom file", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - customContent := "#%PAM-1.0\nauth required pam_u2f.so cue\n" - env.writePamFile(t, "dankshell-u2f", customContent) - - var logs []string - err := syncLockscreenU2FPamConfigWithDeps(func(msg string) { - logs = append(logs, msg) - }, "", false, env.deps(false)) - if err != nil { - t.Fatalf("syncLockscreenU2FPamConfigWithDeps returned error: %v", err) - } - if got := readFileString(t, env.dankshellU2fPath); got != customContent { - t.Fatalf("custom dankshell-u2f content changed\ngot:\n%s\nwant:\n%s", got, customContent) - } - if len(logs) == 0 || !strings.Contains(logs[0], "Custom /etc/pam.d/dankshell-u2f found") { - t.Fatalf("expected custom-file log, got %v", logs) - } - }) -} - -func TestSyncGreeterPamConfigWithDeps(t *testing.T) { - t.Parallel() - - t.Run("adds managed block for enabled auth modules", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.availableModules["pam_fprintd.so"] = true - env.availableModules["pam_u2f.so"] = true - env.writePamFile(t, "greetd", "#%PAM-1.0\nauth include system-auth\naccount include system-auth\n") - env.writePamFile(t, "system-auth", "auth sufficient pam_unix.so\naccount required pam_unix.so\n") - - settings := AuthSettings{GreeterEnableFprint: true, GreeterEnableU2f: true} - if err := syncGreeterPamConfigWithDeps(func(string) {}, "", settings, false, env.deps(false)); err != nil { - t.Fatalf("syncGreeterPamConfigWithDeps returned error: %v", err) - } - - got := readFileString(t, env.greetdPath) - for _, want := range []string{ - GreeterPamManagedBlockStart, - "auth sufficient pam_fprintd.so max-tries=1 timeout=5", - "auth sufficient pam_u2f.so cue nouserok timeout=10", - GreeterPamManagedBlockEnd, - } { - if !strings.Contains(got, want) { - t.Errorf("missing expected string %q in greetd PAM:\n%s", want, got) - } - } - if strings.Index(got, GreeterPamManagedBlockStart) > strings.Index(got, "auth include system-auth") { - t.Fatalf("managed block was not inserted before first auth line:\n%s", got) - } - }) - - t.Run("avoids duplicate fingerprint when included stack already provides it", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.availableModules["pam_fprintd.so"] = true - env.fingerprintAvailable = true - original := "#%PAM-1.0\nauth include system-auth\naccount include system-auth\n" - env.writePamFile(t, "greetd", original) - env.writePamFile(t, "system-auth", "auth sufficient pam_fprintd.so max-tries=1\nauth sufficient pam_unix.so\n") - - settings := AuthSettings{GreeterEnableFprint: true} - if err := syncGreeterPamConfigWithDeps(func(string) {}, "", settings, false, env.deps(false)); err != nil { - t.Fatalf("syncGreeterPamConfigWithDeps returned error: %v", err) - } - - got := readFileString(t, env.greetdPath) - if got != original { - t.Fatalf("greetd PAM changed despite included pam_fprintd stack\ngot:\n%s\nwant:\n%s", got, original) - } - if strings.Contains(got, GreeterPamManagedBlockStart) { - t.Fatalf("managed block should not be inserted when included stack already has pam_fprintd:\n%s", got) - } - }) -} - -func TestRemoveManagedGreeterPamBlockWithDeps(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.writePamFile(t, "greetd", "#%PAM-1.0\n"+ - legacyGreeterPamFprintComment+"\n"+ - "auth sufficient pam_fprintd.so max-tries=1\n"+ - GreeterPamManagedBlockStart+"\n"+ - "auth sufficient pam_u2f.so cue nouserok timeout=10\n"+ - GreeterPamManagedBlockEnd+"\n"+ - "auth include system-auth\n") - - if err := removeManagedGreeterPamBlockWithDeps(func(string) {}, "", env.deps(false)); err != nil { - t.Fatalf("removeManagedGreeterPamBlockWithDeps returned error: %v", err) - } - - got := readFileString(t, env.greetdPath) - if strings.Contains(got, GreeterPamManagedBlockStart) || strings.Contains(got, legacyGreeterPamFprintComment) { - t.Fatalf("managed or legacy DMS auth lines remained in greetd PAM:\n%s", got) - } - if !strings.Contains(got, "auth include system-auth") { - t.Fatalf("expected non-DMS greetd auth lines to remain:\n%s", got) - } -} - -func TestSyncAuthConfigWithDeps(t *testing.T) { - t.Parallel() - - t.Run("creates lockscreen targets and skips greetd when greeter is not installed", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.writeSettings(t, `{"enableU2f":true}`) - env.writePamFile(t, "login", "#%PAM-1.0\nauth include system-auth\naccount include system-auth\n") - env.writePamFile(t, "system-auth", "auth sufficient pam_unix.so try_first_pass nullok\naccount required pam_access.so\n") - - var logs []string - err := syncAuthConfigWithDeps(func(msg string) { - logs = append(logs, msg) - }, "", SyncAuthOptions{HomeDir: env.homeDir}, env.deps(false)) - if err != nil { - t.Fatalf("syncAuthConfigWithDeps returned error: %v", err) - } - - if _, err := os.Stat(env.dankshellPath); err != nil { - t.Fatalf("expected dankshell to be created: %v", err) - } - if got := readFileString(t, env.dankshellU2fPath); got != buildManagedLockscreenU2FPamContent() { - t.Fatalf("unexpected dankshell-u2f content:\n%s", got) - } - if len(logs) == 0 || !strings.Contains(logs[len(logs)-1], "greetd not found") { - t.Fatalf("expected greetd skip log, got %v", logs) - } - }) - - t.Run("separate greeter and lockscreen toggles are respected", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.availableModules["pam_fprintd.so"] = true - env.writeSettings(t, `{"enableU2f":false,"greeterEnableFprint":true,"greeterEnableU2f":false}`) - env.writePamFile(t, "login", "#%PAM-1.0\nauth include system-auth\naccount include system-auth\n") - env.writePamFile(t, "system-auth", "auth sufficient pam_unix.so try_first_pass nullok\naccount required pam_access.so\n") - env.writePamFile(t, "greetd", "#%PAM-1.0\nauth include system-auth\naccount include system-auth\n") - - err := syncAuthConfigWithDeps(func(string) {}, "", SyncAuthOptions{HomeDir: env.homeDir}, env.deps(false)) - if err != nil { - t.Fatalf("syncAuthConfigWithDeps returned error: %v", err) - } - - dankshell := readFileString(t, env.dankshellPath) - if strings.Contains(dankshell, "pam_fprintd") || strings.Contains(dankshell, "pam_u2f") { - t.Fatalf("lockscreen PAM should strip fingerprint and U2F modules:\n%s", dankshell) - } - if _, err := os.Stat(env.dankshellU2fPath); !os.IsNotExist(err) { - t.Fatalf("expected dankshell-u2f to remain absent when enableU2f is false, stat err = %v", err) - } - - greetd := readFileString(t, env.greetdPath) - if !strings.Contains(greetd, "auth sufficient pam_fprintd.so max-tries=1 timeout=5") { - t.Fatalf("expected greetd PAM to receive fingerprint auth block:\n%s", greetd) - } - if strings.Contains(greetd, "auth sufficient pam_u2f.so cue nouserok timeout=10") { - t.Fatalf("did not expect greetd PAM to receive U2F auth block:\n%s", greetd) - } - }) - - t.Run("NixOS remains informational and non-mutating", func(t *testing.T) { - t.Parallel() - - env := newPamTestEnv(t) - env.availableModules["pam_fprintd.so"] = true - env.availableModules["pam_u2f.so"] = true - env.writeSettings(t, `{"enableU2f":true,"greeterEnableFprint":true,"greeterEnableU2f":true}`) - originalGreetd := "#%PAM-1.0\nauth include system-auth\naccount include system-auth\n" - env.writePamFile(t, "greetd", originalGreetd) - - var logs []string - err := syncAuthConfigWithDeps(func(msg string) { - logs = append(logs, msg) - }, "", SyncAuthOptions{HomeDir: env.homeDir}, env.deps(true)) - if err != nil { - t.Fatalf("syncAuthConfigWithDeps returned error: %v", err) - } - - if _, err := os.Stat(env.dankshellPath); !os.IsNotExist(err) { - t.Fatalf("expected dankshell to remain absent on NixOS path, stat err = %v", err) - } - if _, err := os.Stat(env.dankshellU2fPath); !os.IsNotExist(err) { - t.Fatalf("expected dankshell-u2f to remain absent on NixOS path, stat err = %v", err) - } - if got := readFileString(t, env.greetdPath); got != originalGreetd { - t.Fatalf("expected greetd PAM to remain unchanged on NixOS path\ngot:\n%s\nwant:\n%s", got, originalGreetd) - } - if len(logs) < 2 || !strings.Contains(strings.Join(logs, "\n"), "NixOS detected") { - t.Fatalf("expected informational NixOS logs, got %v", logs) - } - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/manager.go deleted file mode 100644 index 1c0ddf5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/manager.go +++ /dev/null @@ -1,588 +0,0 @@ -package plugins - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/spf13/afero" -) - -type Manager struct { - fs afero.Fs - pluginsDir string - gitClient GitClient -} - -func NewManager() (*Manager, error) { - return NewManagerWithFs(afero.NewOsFs()) -} - -func NewManagerWithFs(fs afero.Fs) (*Manager, error) { - pluginsDir := getPluginsDir() - return &Manager{ - fs: fs, - pluginsDir: pluginsDir, - gitClient: &realGitClient{}, - }, nil -} - -func getPluginsDir() string { - configDir, err := os.UserConfigDir() - if err != nil { - log.Error("failed to get user config dir", "err", err) - return "" - } - return filepath.Join(configDir, "DankMaterialShell", "plugins") -} - -func (m *Manager) IsInstalled(plugin Plugin) (bool, error) { - path, err := m.findInstalledPath(plugin.ID) - if err != nil { - return false, err - } - return path != "", nil -} - -func (m *Manager) findInstalledPath(pluginID string) (string, error) { - // Check user plugins directory - path, err := m.findInDir(m.pluginsDir, pluginID) - if err != nil { - return "", err - } - if path != "" { - return path, nil - } - - // Check system plugins directory - systemDir := "/etc/xdg/quickshell/dms-plugins" - return m.findInDir(systemDir, pluginID) -} - -func (m *Manager) findInDir(dir, pluginID string) (string, error) { - // First, check if folder with exact ID name exists - exactPath := filepath.Join(dir, pluginID) - if exists, _ := afero.DirExists(m.fs, exactPath); exists { - return exactPath, nil - } - - // Scan all folders and check plugin.json for matching ID - exists, err := afero.DirExists(m.fs, dir) - if err != nil || !exists { - return "", nil - } - - entries, err := afero.ReadDir(m.fs, dir) - if err != nil { - return "", nil - } - - for _, entry := range entries { - name := entry.Name() - if name == ".repos" || strings.HasSuffix(name, ".meta") { - continue - } - - fullPath := filepath.Join(dir, name) - isPlugin := entry.IsDir() || entry.Mode()&os.ModeSymlink != 0 - if !isPlugin { - if info, err := m.fs.Stat(fullPath); err == nil && info.IsDir() { - isPlugin = true - } - } - - if isPlugin && m.getPluginID(fullPath) == pluginID { - return fullPath, nil - } - } - - return "", nil -} - -func (m *Manager) Install(plugin Plugin) error { - pluginPath := filepath.Join(m.pluginsDir, plugin.ID) - - exists, err := afero.DirExists(m.fs, pluginPath) - if err != nil { - return fmt.Errorf("failed to check if plugin exists: %w", err) - } - - if exists { - return fmt.Errorf("plugin already installed: %s", plugin.Name) - } - - if err := m.fs.MkdirAll(m.pluginsDir, 0o755); err != nil { - return fmt.Errorf("failed to create plugins directory: %w", err) - } - - reposDir := filepath.Join(m.pluginsDir, ".repos") - if err := m.fs.MkdirAll(reposDir, 0o755); err != nil { - return fmt.Errorf("failed to create repos directory: %w", err) - } - - if plugin.Path != "" { - repoName := m.getRepoName(plugin.Repo) - repoPath := filepath.Join(reposDir, repoName) - - repoExists, err := afero.DirExists(m.fs, repoPath) - if err != nil { - return fmt.Errorf("failed to check if repo exists: %w", err) - } - - if !repoExists { - if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil { - m.fs.RemoveAll(repoPath) //nolint:errcheck - return fmt.Errorf("failed to clone repository: %w", err) - } - } else { - // Pull latest changes if repo already exists - if err := m.gitClient.Pull(repoPath); err != nil { - // If pull fails (e.g., corrupted shallow clone), delete and re-clone - if err := m.fs.RemoveAll(repoPath); err != nil { - return fmt.Errorf("failed to remove corrupted repository: %w", err) - } - - if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil { - return fmt.Errorf("failed to re-clone repository: %w", err) - } - } - } - - sourcePath := filepath.Join(repoPath, plugin.Path) - sourceExists, err := afero.DirExists(m.fs, sourcePath) - if err != nil { - return fmt.Errorf("failed to check plugin path: %w", err) - } - if !sourceExists { - return fmt.Errorf("plugin path does not exist in repository: %s", plugin.Path) - } - - if err := m.createSymlink(sourcePath, pluginPath); err != nil { - return fmt.Errorf("failed to create symlink: %w", err) - } - - metaPath := pluginPath + ".meta" - metaContent := fmt.Sprintf("repo=%s\npath=%s\nrepodir=%s", plugin.Repo, plugin.Path, repoName) - if err := afero.WriteFile(m.fs, metaPath, []byte(metaContent), 0o644); err != nil { - return fmt.Errorf("failed to write metadata: %w", err) - } - } else { - if err := m.gitClient.PlainClone(pluginPath, plugin.Repo); err != nil { - m.fs.RemoveAll(pluginPath) //nolint:errcheck - return fmt.Errorf("failed to clone plugin: %w", err) - } - } - - return nil -} - -func (m *Manager) getRepoName(repoURL string) string { - hash := sha256.Sum256([]byte(repoURL)) - return hex.EncodeToString(hash[:])[:16] -} - -func (m *Manager) createSymlink(source, dest string) error { - if symlinkFs, ok := m.fs.(afero.Symlinker); ok { - return symlinkFs.SymlinkIfPossible(source, dest) - } - return os.Symlink(source, dest) -} - -func (m *Manager) Update(plugin Plugin) error { - pluginPath, err := m.findInstalledPath(plugin.ID) - if err != nil { - return fmt.Errorf("failed to find plugin: %w", err) - } - - if pluginPath == "" { - return fmt.Errorf("plugin not installed: %s", plugin.Name) - } - - if strings.HasPrefix(pluginPath, "/etc/xdg/quickshell/dms-plugins") { - return fmt.Errorf("cannot update system plugin: %s", plugin.Name) - } - - metaPath := pluginPath + ".meta" - metaExists, err := afero.Exists(m.fs, metaPath) - if err != nil { - return fmt.Errorf("failed to check metadata: %w", err) - } - - if metaExists { - reposDir := filepath.Join(m.pluginsDir, ".repos") - repoName := m.getRepoName(plugin.Repo) - repoPath := filepath.Join(reposDir, repoName) - - // Try to pull, if it fails (e.g., shallow clone corruption), delete and re-clone - if err := m.gitClient.Pull(repoPath); err != nil { - // Repository is likely corrupted or has issues, delete and re-clone - if err := m.fs.RemoveAll(repoPath); err != nil { - return fmt.Errorf("failed to remove corrupted repository: %w", err) - } - - if err := m.gitClient.PlainClone(repoPath, plugin.Repo); err != nil { - return fmt.Errorf("failed to re-clone repository: %w", err) - } - } - } else { - // Try to pull, if it fails, delete and re-clone - if err := m.gitClient.Pull(pluginPath); err != nil { - if err := m.fs.RemoveAll(pluginPath); err != nil { - return fmt.Errorf("failed to remove corrupted plugin: %w", err) - } - - if err := m.gitClient.PlainClone(pluginPath, plugin.Repo); err != nil { - return fmt.Errorf("failed to re-clone plugin: %w", err) - } - } - } - - return nil -} - -func (m *Manager) Uninstall(plugin Plugin) error { - pluginPath, err := m.findInstalledPath(plugin.ID) - if err != nil { - return fmt.Errorf("failed to find plugin: %w", err) - } - - if pluginPath == "" { - return fmt.Errorf("plugin not installed: %s", plugin.Name) - } - - if strings.HasPrefix(pluginPath, "/etc/xdg/quickshell/dms-plugins") { - return fmt.Errorf("cannot uninstall system plugin: %s", plugin.Name) - } - - metaPath := pluginPath + ".meta" - metaExists, err := afero.Exists(m.fs, metaPath) - if err != nil { - return fmt.Errorf("failed to check metadata: %w", err) - } - - if metaExists { - reposDir := filepath.Join(m.pluginsDir, ".repos") - repoName := m.getRepoName(plugin.Repo) - repoPath := filepath.Join(reposDir, repoName) - - shouldCleanup, err := m.shouldCleanupRepo(repoPath, plugin.Repo, plugin.ID) - if err != nil { - return fmt.Errorf("failed to check repo cleanup: %w", err) - } - - if err := m.fs.Remove(pluginPath); err != nil { - return fmt.Errorf("failed to remove symlink: %w", err) - } - - if err := m.fs.Remove(metaPath); err != nil { - return fmt.Errorf("failed to remove metadata: %w", err) - } - - if shouldCleanup { - if err := m.fs.RemoveAll(repoPath); err != nil { - return fmt.Errorf("failed to cleanup repository: %w", err) - } - } - } else { - if err := m.fs.RemoveAll(pluginPath); err != nil { - return fmt.Errorf("failed to remove plugin: %w", err) - } - } - - return nil -} - -func (m *Manager) shouldCleanupRepo(repoPath, repoURL, excludePlugin string) (bool, error) { - installed, err := m.ListInstalled() - if err != nil { - return false, err - } - - registry, err := NewRegistry() - if err != nil { - return false, err - } - - allPlugins, err := registry.List() - if err != nil { - return false, err - } - - for _, id := range installed { - if id == excludePlugin { - continue - } - - for _, p := range allPlugins { - if p.ID == id && p.Repo == repoURL && p.Path != "" { - return false, nil - } - } - } - - return true, nil -} - -func (m *Manager) ListInstalled() ([]string, error) { - installedMap := make(map[string]bool) - - exists, err := afero.DirExists(m.fs, m.pluginsDir) - if err != nil { - return nil, err - } - - if exists { - entries, err := afero.ReadDir(m.fs, m.pluginsDir) - if err != nil { - return nil, fmt.Errorf("failed to read plugins directory: %w", err) - } - - for _, entry := range entries { - name := entry.Name() - if name == ".repos" || strings.HasSuffix(name, ".meta") { - continue - } - - fullPath := filepath.Join(m.pluginsDir, name) - isPlugin := false - - if entry.IsDir() { - isPlugin = true - } else if entry.Mode()&os.ModeSymlink != 0 { - isPlugin = true - } else { - info, err := m.fs.Stat(fullPath) - if err == nil && info.IsDir() { - isPlugin = true - } - } - - if isPlugin { - // Read plugin.json to get the actual plugin ID - pluginID := m.getPluginID(fullPath) - if pluginID != "" { - installedMap[pluginID] = true - } - } - } - } - - systemPluginsDir := "/etc/xdg/quickshell/dms-plugins" - systemExists, err := afero.DirExists(m.fs, systemPluginsDir) - if err == nil && systemExists { - entries, err := afero.ReadDir(m.fs, systemPluginsDir) - if err == nil { - for _, entry := range entries { - if entry.IsDir() { - fullPath := filepath.Join(systemPluginsDir, entry.Name()) - // Read plugin.json to get the actual plugin ID - pluginID := m.getPluginID(fullPath) - if pluginID != "" { - installedMap[pluginID] = true - } - } - } - } - } - - var installed []string - for name := range installedMap { - installed = append(installed, name) - } - - return installed, nil -} - -// getPluginID reads the plugin.json file and returns the plugin ID -func (m *Manager) getPluginID(pluginPath string) string { - manifest := m.getPluginManifest(pluginPath) - if manifest == nil { - return "" - } - return manifest.ID -} - -func (m *Manager) getPluginManifest(pluginPath string) *pluginManifest { - manifestPath := filepath.Join(pluginPath, "plugin.json") - data, err := afero.ReadFile(m.fs, manifestPath) - if err != nil { - return nil - } - - var manifest pluginManifest - if err := json.Unmarshal(data, &manifest); err != nil { - return nil - } - - return &manifest -} - -type pluginManifest struct { - ID string `json:"id"` - Name string `json:"name"` -} - -func (m *Manager) GetPluginsDir() string { - return m.pluginsDir -} - -func (m *Manager) UninstallByIDOrName(idOrName string) error { - pluginPath, err := m.findInstalledPathByIDOrName(idOrName) - if err != nil { - return err - } - if pluginPath == "" { - return fmt.Errorf("plugin not found: %s", idOrName) - } - - if strings.HasPrefix(pluginPath, "/etc/xdg/quickshell/dms-plugins") { - return fmt.Errorf("cannot uninstall system plugin: %s", idOrName) - } - - metaPath := pluginPath + ".meta" - metaExists, _ := afero.Exists(m.fs, metaPath) - - if metaExists { - if err := m.fs.Remove(pluginPath); err != nil { - return fmt.Errorf("failed to remove symlink: %w", err) - } - if err := m.fs.Remove(metaPath); err != nil { - return fmt.Errorf("failed to remove metadata: %w", err) - } - } else { - if err := m.fs.RemoveAll(pluginPath); err != nil { - return fmt.Errorf("failed to remove plugin: %w", err) - } - } - - return nil -} - -func (m *Manager) UpdateByIDOrName(idOrName string) error { - pluginPath, err := m.findInstalledPathByIDOrName(idOrName) - if err != nil { - return err - } - if pluginPath == "" { - return fmt.Errorf("plugin not found: %s", idOrName) - } - - if strings.HasPrefix(pluginPath, "/etc/xdg/quickshell/dms-plugins") { - return fmt.Errorf("cannot update system plugin: %s", idOrName) - } - - metaPath := pluginPath + ".meta" - metaExists, _ := afero.Exists(m.fs, metaPath) - - if metaExists { - // Plugin is from monorepo, but we don't know the repo URL without registry - // Just try to pull from existing .git in the symlink target - return fmt.Errorf("cannot update monorepo plugin without registry info: %s", idOrName) - } - - // Standalone plugin - just pull - if err := m.gitClient.Pull(pluginPath); err != nil { - return fmt.Errorf("failed to update plugin: %w", err) - } - - return nil -} - -func (m *Manager) findInstalledPathByIDOrName(idOrName string) (string, error) { - path, err := m.findInDirByIDOrName(m.pluginsDir, idOrName) - if err != nil { - return "", err - } - if path != "" { - return path, nil - } - - systemDir := "/etc/xdg/quickshell/dms-plugins" - return m.findInDirByIDOrName(systemDir, idOrName) -} - -func (m *Manager) findInDirByIDOrName(dir, idOrName string) (string, error) { - // Check exact folder name match first - exactPath := filepath.Join(dir, idOrName) - if exists, _ := afero.DirExists(m.fs, exactPath); exists { - return exactPath, nil - } - - exists, err := afero.DirExists(m.fs, dir) - if err != nil || !exists { - return "", nil - } - - entries, err := afero.ReadDir(m.fs, dir) - if err != nil { - return "", nil - } - - for _, entry := range entries { - name := entry.Name() - if name == ".repos" || strings.HasSuffix(name, ".meta") { - continue - } - - fullPath := filepath.Join(dir, name) - isPlugin := entry.IsDir() || entry.Mode()&os.ModeSymlink != 0 - if !isPlugin { - if info, err := m.fs.Stat(fullPath); err == nil && info.IsDir() { - isPlugin = true - } - } - - if !isPlugin { - continue - } - - manifest := m.getPluginManifest(fullPath) - if manifest == nil { - continue - } - - if manifest.ID == idOrName || manifest.Name == idOrName { - return fullPath, nil - } - } - - return "", nil -} - -func (m *Manager) HasUpdates(pluginID string, plugin Plugin) (bool, error) { - pluginPath, err := m.findInstalledPath(pluginID) - if err != nil { - return false, fmt.Errorf("failed to find plugin: %w", err) - } - - if pluginPath == "" { - return false, fmt.Errorf("plugin not installed: %s", pluginID) - } - - if strings.HasPrefix(pluginPath, "/etc/xdg/quickshell/dms-plugins") { - return false, nil - } - - metaPath := pluginPath + ".meta" - metaExists, err := afero.Exists(m.fs, metaPath) - if err != nil { - return false, fmt.Errorf("failed to check metadata: %w", err) - } - - if metaExists { - // Plugin is from a monorepo, check the repo directory - reposDir := filepath.Join(m.pluginsDir, ".repos") - repoName := m.getRepoName(plugin.Repo) - repoPath := filepath.Join(reposDir, repoName) - - return m.gitClient.HasUpdates(repoPath) - } - - // Plugin is a standalone repo - return m.gitClient.HasUpdates(pluginPath) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/manager_test.go deleted file mode 100644 index 485479e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/manager_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package plugins - -import ( - "os" - "path/filepath" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func setupTestManager(t *testing.T) (*Manager, afero.Fs, string) { - fs := afero.NewMemMapFs() - pluginsDir := "/test-plugins" - manager := &Manager{ - fs: fs, - pluginsDir: pluginsDir, - gitClient: &mockGitClient{}, - } - return manager, fs, pluginsDir -} - -func TestNewManager(t *testing.T) { - manager, err := NewManager() - assert.NoError(t, err) - assert.NotNil(t, manager) - assert.NotEmpty(t, manager.pluginsDir) -} - -func TestGetPluginsDir(t *testing.T) { - t.Run("uses XDG_CONFIG_HOME when set", func(t *testing.T) { - oldConfig := os.Getenv("XDG_CONFIG_HOME") - defer func() { - if oldConfig != "" { - os.Setenv("XDG_CONFIG_HOME", oldConfig) - } else { - os.Unsetenv("XDG_CONFIG_HOME") - } - }() - - os.Setenv("XDG_CONFIG_HOME", "/tmp/test-config") - dir := getPluginsDir() - assert.Equal(t, "/tmp/test-config/DankMaterialShell/plugins", dir) - }) - - t.Run("falls back to home directory", func(t *testing.T) { - oldConfig := os.Getenv("XDG_CONFIG_HOME") - defer func() { - if oldConfig != "" { - os.Setenv("XDG_CONFIG_HOME", oldConfig) - } else { - os.Unsetenv("XDG_CONFIG_HOME") - } - }() - - os.Unsetenv("XDG_CONFIG_HOME") - dir := getPluginsDir() - assert.Contains(t, dir, ".config/DankMaterialShell/plugins") - }) -} - -func TestIsInstalled(t *testing.T) { - t.Run("returns true when plugin is installed", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ID: "test-plugin", Name: "TestPlugin"} - pluginPath := filepath.Join(pluginsDir, plugin.ID) - err := fs.MkdirAll(pluginPath, 0o755) - require.NoError(t, err) - - installed, err := manager.IsInstalled(plugin) - assert.NoError(t, err) - assert.True(t, installed) - }) - - t.Run("returns false when plugin is not installed", func(t *testing.T) { - manager, _, _ := setupTestManager(t) - - plugin := Plugin{ID: "non-existent", Name: "NonExistent"} - installed, err := manager.IsInstalled(plugin) - assert.NoError(t, err) - assert.False(t, installed) - }) -} - -func TestInstall(t *testing.T) { - t.Run("installs plugin successfully", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ - ID: "test-plugin", - Name: "TestPlugin", - Repo: "https://github.com/test/plugin", - } - - cloneCalled := false - mockGit := &mockGitClient{ - cloneFunc: func(path string, url string) error { - cloneCalled = true - assert.Equal(t, filepath.Join(pluginsDir, plugin.ID), path) - assert.Equal(t, plugin.Repo, url) - return fs.MkdirAll(path, 0o755) - }, - } - manager.gitClient = mockGit - - err := manager.Install(plugin) - assert.NoError(t, err) - assert.True(t, cloneCalled) - - exists, _ := afero.DirExists(fs, filepath.Join(pluginsDir, plugin.ID)) - assert.True(t, exists) - }) - - t.Run("returns error when plugin already installed", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ID: "test-plugin", Name: "TestPlugin"} - pluginPath := filepath.Join(pluginsDir, plugin.ID) - err := fs.MkdirAll(pluginPath, 0o755) - require.NoError(t, err) - - err = manager.Install(plugin) - assert.Error(t, err) - assert.Contains(t, err.Error(), "already installed") - }) - - t.Run("installs monorepo plugin with symlink", func(t *testing.T) { - t.Skip("Skipping symlink test as MemMapFs doesn't support symlinks") - }) -} - -func TestManagerUpdate(t *testing.T) { - t.Run("updates plugin successfully", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ID: "test-plugin", Name: "TestPlugin"} - pluginPath := filepath.Join(pluginsDir, plugin.ID) - err := fs.MkdirAll(pluginPath, 0o755) - require.NoError(t, err) - - pullCalled := false - mockGit := &mockGitClient{ - pullFunc: func(path string) error { - pullCalled = true - assert.Equal(t, pluginPath, path) - return nil - }, - } - manager.gitClient = mockGit - - err = manager.Update(plugin) - assert.NoError(t, err) - assert.True(t, pullCalled) - }) - - t.Run("returns error when plugin not installed", func(t *testing.T) { - manager, _, _ := setupTestManager(t) - - plugin := Plugin{ID: "non-existent", Name: "NonExistent"} - err := manager.Update(plugin) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not installed") - }) -} - -func TestUninstall(t *testing.T) { - t.Run("uninstalls plugin successfully", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - plugin := Plugin{ID: "test-plugin", Name: "TestPlugin"} - pluginPath := filepath.Join(pluginsDir, plugin.ID) - err := fs.MkdirAll(pluginPath, 0o755) - require.NoError(t, err) - - err = manager.Uninstall(plugin) - assert.NoError(t, err) - - exists, _ := afero.DirExists(fs, pluginPath) - assert.False(t, exists) - }) - - t.Run("returns error when plugin not installed", func(t *testing.T) { - manager, _, _ := setupTestManager(t) - - plugin := Plugin{ID: "non-existent", Name: "NonExistent"} - err := manager.Uninstall(plugin) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not installed") - }) -} - -func TestListInstalled(t *testing.T) { - t.Run("lists installed plugins", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - err := fs.MkdirAll(filepath.Join(pluginsDir, "Plugin1"), 0o755) - require.NoError(t, err) - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "Plugin1", "plugin.json"), []byte(`{"id":"Plugin1"}`), 0o644) - require.NoError(t, err) - - err = fs.MkdirAll(filepath.Join(pluginsDir, "Plugin2"), 0o755) - require.NoError(t, err) - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "Plugin2", "plugin.json"), []byte(`{"id":"Plugin2"}`), 0o644) - require.NoError(t, err) - - installed, err := manager.ListInstalled() - assert.NoError(t, err) - assert.Len(t, installed, 2) - assert.Contains(t, installed, "Plugin1") - assert.Contains(t, installed, "Plugin2") - }) - - t.Run("returns empty list when no plugins installed", func(t *testing.T) { - manager, _, _ := setupTestManager(t) - - installed, err := manager.ListInstalled() - assert.NoError(t, err) - assert.Empty(t, installed) - }) - - t.Run("ignores files and .repos directory", func(t *testing.T) { - manager, fs, pluginsDir := setupTestManager(t) - - err := fs.MkdirAll(pluginsDir, 0o755) - require.NoError(t, err) - err = fs.MkdirAll(filepath.Join(pluginsDir, "Plugin1"), 0o755) - require.NoError(t, err) - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "Plugin1", "plugin.json"), []byte(`{"id":"Plugin1"}`), 0o644) - require.NoError(t, err) - err = fs.MkdirAll(filepath.Join(pluginsDir, ".repos"), 0o755) - require.NoError(t, err) - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "README.md"), []byte("test"), 0o644) - require.NoError(t, err) - - installed, err := manager.ListInstalled() - assert.NoError(t, err) - assert.Len(t, installed, 1) - assert.Equal(t, "Plugin1", installed[0]) - }) -} - -func TestManagerGetPluginsDir(t *testing.T) { - manager, _, pluginsDir := setupTestManager(t) - assert.Equal(t, pluginsDir, manager.GetPluginsDir()) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/registry.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/registry.go deleted file mode 100644 index 1728e72..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/registry.go +++ /dev/null @@ -1,258 +0,0 @@ -package plugins - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/go-git/go-git/v6" - "github.com/spf13/afero" -) - -const registryRepo = "https://github.com/AvengeMedia/dms-plugin-registry.git" - -type Plugin struct { - ID string `json:"id"` - Name string `json:"name"` - Capabilities []string `json:"capabilities"` - Category string `json:"category"` - Repo string `json:"repo"` - Path string `json:"path,omitempty"` - Author string `json:"author"` - Description string `json:"description"` - Dependencies []string `json:"dependencies,omitempty"` - Compositors []string `json:"compositors"` - Distro []string `json:"distro"` - Screenshot string `json:"screenshot,omitempty"` - RequiresDMS string `json:"requires_dms,omitempty"` - Featured bool `json:"featured,omitempty"` -} - -type GitClient interface { - PlainClone(path string, url string) error - Pull(path string) error - HasUpdates(path string) (bool, error) -} - -type realGitClient struct{} - -func (g *realGitClient) PlainClone(path string, url string) error { - _, err := git.PlainClone(path, &git.CloneOptions{ - URL: url, - Progress: os.Stdout, - }) - return err -} - -func (g *realGitClient) Pull(path string) error { - repo, err := git.PlainOpen(path) - if err != nil { - return err - } - - worktree, err := repo.Worktree() - if err != nil { - return err - } - - err = worktree.Pull(&git.PullOptions{}) - if err != nil && err.Error() != "already up-to-date" { - return err - } - - return nil -} - -func (g *realGitClient) HasUpdates(path string) (bool, error) { - repo, err := git.PlainOpen(path) - if err != nil { - return false, err - } - - // Fetch remote changes - err = repo.Fetch(&git.FetchOptions{}) - if err != nil && err.Error() != "already up-to-date" { - // If fetch fails, we can't determine if there are updates - // Return false and the error - return false, err - } - - // Get the HEAD reference - head, err := repo.Head() - if err != nil { - return false, err - } - - // Get the remote HEAD reference (typically origin/HEAD or origin/main or origin/master) - remote, err := repo.Remote("origin") - if err != nil { - return false, err - } - - refs, err := remote.List(&git.ListOptions{}) - if err != nil { - return false, err - } - - // Find the default branch remote ref - var remoteHead string - for _, ref := range refs { - if ref.Name().IsBranch() { - // Try common branch names - if ref.Name().Short() == "main" || ref.Name().Short() == "master" { - remoteHead = ref.Hash().String() - break - } - } - } - - // If we couldn't find a remote HEAD, assume no updates - if remoteHead == "" { - return false, nil - } - - // Compare local HEAD with remote HEAD - return head.Hash().String() != remoteHead, nil -} - -type Registry struct { - fs afero.Fs - cacheDir string - plugins []Plugin - git GitClient -} - -func NewRegistry() (*Registry, error) { - return NewRegistryWithFs(afero.NewOsFs()) -} - -func NewRegistryWithFs(fs afero.Fs) (*Registry, error) { - cacheDir := getCacheDir() - return &Registry{ - fs: fs, - cacheDir: cacheDir, - git: &realGitClient{}, - }, nil -} - -func getCacheDir() string { - return filepath.Join(os.TempDir(), "dankdots-plugin-registry") -} - -func (r *Registry) Update() error { - exists, err := afero.DirExists(r.fs, r.cacheDir) - if err != nil { - return fmt.Errorf("failed to check cache directory: %w", err) - } - - if !exists { - if err := r.fs.MkdirAll(filepath.Dir(r.cacheDir), 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - if err := r.git.PlainClone(r.cacheDir, registryRepo); err != nil { - return fmt.Errorf("failed to clone registry: %w", err) - } - } else { - // Try to pull, if it fails (e.g., shallow clone corruption), delete and re-clone - if err := r.git.Pull(r.cacheDir); err != nil { - // Repository is likely corrupted or has issues, delete and re-clone - if err := r.fs.RemoveAll(r.cacheDir); err != nil { - return fmt.Errorf("failed to remove corrupted registry: %w", err) - } - - if err := r.fs.MkdirAll(filepath.Dir(r.cacheDir), 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - if err := r.git.PlainClone(r.cacheDir, registryRepo); err != nil { - return fmt.Errorf("failed to re-clone registry: %w", err) - } - } - } - - return r.loadPlugins() -} - -func (r *Registry) loadPlugins() error { - pluginsDir := filepath.Join(r.cacheDir, "plugins") - - entries, err := afero.ReadDir(r.fs, pluginsDir) - if err != nil { - return fmt.Errorf("failed to read plugins directory: %w", err) - } - - r.plugins = []Plugin{} - - for _, entry := range entries { - if entry.IsDir() || filepath.Ext(entry.Name()) != ".json" { - continue - } - - data, err := afero.ReadFile(r.fs, filepath.Join(pluginsDir, entry.Name())) - if err != nil { - continue - } - - var plugin Plugin - if err := json.Unmarshal(data, &plugin); err != nil { - continue - } - - if plugin.ID == "" { - plugin.ID = strings.TrimSuffix(entry.Name(), ".json") - } - - r.plugins = append(r.plugins, plugin) - } - - return nil -} - -func (r *Registry) List() ([]Plugin, error) { - if len(r.plugins) == 0 { - if err := r.Update(); err != nil { - return nil, err - } - } - - return SortByFirstParty(r.plugins), nil -} - -func (r *Registry) Search(query string) ([]Plugin, error) { - allPlugins, err := r.List() - if err != nil { - return nil, err - } - - if query == "" { - return allPlugins, nil - } - - return SortByFirstParty(FuzzySearch(query, allPlugins)), nil -} - -func (r *Registry) Get(idOrName string) (*Plugin, error) { - plugins, err := r.List() - if err != nil { - return nil, err - } - - // First, try to find by ID (preferred method) - for _, p := range plugins { - if p.ID == idOrName { - return &p, nil - } - } - - // Fallback to name for backward compatibility - for _, p := range plugins { - if p.Name == idOrName { - return &p, nil - } - } - - return nil, fmt.Errorf("plugin not found: %s", idOrName) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/registry_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/registry_test.go deleted file mode 100644 index e535e6f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/registry_test.go +++ /dev/null @@ -1,326 +0,0 @@ -package plugins - -import ( - "encoding/json" - "path/filepath" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type mockGitClient struct { - cloneFunc func(path string, url string) error - pullFunc func(path string) error - hasUpdatesFunc func(path string) (bool, error) -} - -func (m *mockGitClient) PlainClone(path string, url string) error { - if m.cloneFunc != nil { - return m.cloneFunc(path, url) - } - return nil -} - -func (m *mockGitClient) Pull(path string) error { - if m.pullFunc != nil { - return m.pullFunc(path) - } - return nil -} - -func (m *mockGitClient) HasUpdates(path string) (bool, error) { - if m.hasUpdatesFunc != nil { - return m.hasUpdatesFunc(path) - } - return false, nil -} - -func TestNewRegistry(t *testing.T) { - registry, err := NewRegistry() - assert.NoError(t, err) - assert.NotNil(t, registry) - assert.NotEmpty(t, registry.cacheDir) -} - -func TestGetCacheDir(t *testing.T) { - cacheDir := getCacheDir() - assert.Contains(t, cacheDir, "/tmp/dankdots-plugin-registry") -} - -func setupTestRegistry(t *testing.T) (*Registry, afero.Fs, string) { - fs := afero.NewMemMapFs() - tmpDir := "/test-cache" - registry := &Registry{ - fs: fs, - cacheDir: tmpDir, - plugins: []Plugin{}, - git: &mockGitClient{}, - } - return registry, fs, tmpDir -} - -func createTestPlugin(t *testing.T, fs afero.Fs, dir string, filename string, plugin Plugin) { - pluginsDir := filepath.Join(dir, "plugins") - err := fs.MkdirAll(pluginsDir, 0o755) - require.NoError(t, err) - - data, err := json.Marshal(plugin) - require.NoError(t, err) - - err = afero.WriteFile(fs, filepath.Join(pluginsDir, filename), data, 0o644) - require.NoError(t, err) -} - -func TestLoadPlugins(t *testing.T) { - t.Run("loads valid plugin files", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - plugin1 := Plugin{ - Name: "TestPlugin1", - Capabilities: []string{"dankbar-widget"}, - Category: "monitoring", - Repo: "https://github.com/test/plugin1", - Author: "Test Author", - Description: "Test plugin 1", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - plugin2 := Plugin{ - Name: "TestPlugin2", - Capabilities: []string{"system-tray"}, - Category: "utilities", - Repo: "https://github.com/test/plugin2", - Author: "Another Author", - Description: "Test plugin 2", - Dependencies: []string{"dep1", "dep2"}, - Compositors: []string{"hyprland", "niri"}, - Distro: []string{"arch"}, - Screenshot: "https://example.com/screenshot.png", - } - - createTestPlugin(t, fs, tmpDir, "plugin1.json", plugin1) - createTestPlugin(t, fs, tmpDir, "plugin2.json", plugin2) - - err := registry.loadPlugins() - assert.NoError(t, err) - assert.Len(t, registry.plugins, 2) - - assert.Equal(t, "TestPlugin1", registry.plugins[0].Name) - assert.Equal(t, "TestPlugin2", registry.plugins[1].Name) - assert.Equal(t, []string{"dankbar-widget"}, registry.plugins[0].Capabilities) - assert.Equal(t, []string{"dep1", "dep2"}, registry.plugins[1].Dependencies) - }) - - t.Run("skips non-json files", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - pluginsDir := filepath.Join(tmpDir, "plugins") - err := fs.MkdirAll(pluginsDir, 0o755) - require.NoError(t, err) - - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "README.md"), []byte("# Test"), 0o644) - require.NoError(t, err) - - plugin := Plugin{ - Name: "ValidPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - createTestPlugin(t, fs, tmpDir, "valid.json", plugin) - - err = registry.loadPlugins() - assert.NoError(t, err) - assert.Len(t, registry.plugins, 1) - assert.Equal(t, "ValidPlugin", registry.plugins[0].Name) - }) - - t.Run("skips directories", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - pluginsDir := filepath.Join(tmpDir, "plugins") - err := fs.MkdirAll(filepath.Join(pluginsDir, "subdir"), 0o755) - require.NoError(t, err) - - plugin := Plugin{ - Name: "ValidPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - createTestPlugin(t, fs, tmpDir, "valid.json", plugin) - - err = registry.loadPlugins() - assert.NoError(t, err) - assert.Len(t, registry.plugins, 1) - }) - - t.Run("skips invalid json files", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - pluginsDir := filepath.Join(tmpDir, "plugins") - err := fs.MkdirAll(pluginsDir, 0o755) - require.NoError(t, err) - - err = afero.WriteFile(fs, filepath.Join(pluginsDir, "invalid.json"), []byte("{invalid json}"), 0o644) - require.NoError(t, err) - - plugin := Plugin{ - Name: "ValidPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - createTestPlugin(t, fs, tmpDir, "valid.json", plugin) - - err = registry.loadPlugins() - assert.NoError(t, err) - assert.Len(t, registry.plugins, 1) - assert.Equal(t, "ValidPlugin", registry.plugins[0].Name) - }) - - t.Run("returns error when plugins directory missing", func(t *testing.T) { - registry, _, _ := setupTestRegistry(t) - - err := registry.loadPlugins() - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to read plugins directory") - }) -} - -func TestList(t *testing.T) { - t.Run("returns cached plugins if available", func(t *testing.T) { - registry, _, _ := setupTestRegistry(t) - - plugin := Plugin{ - Name: "CachedPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - registry.plugins = []Plugin{plugin} - - plugins, err := registry.List() - assert.NoError(t, err) - assert.Len(t, plugins, 1) - assert.Equal(t, "CachedPlugin", plugins[0].Name) - }) - - t.Run("updates and loads plugins when cache is empty", func(t *testing.T) { - registry, fs, _ := setupTestRegistry(t) - - plugin := Plugin{ - Name: "NewPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - mockGit := &mockGitClient{ - cloneFunc: func(path string, url string) error { - createTestPlugin(t, fs, path, "plugin.json", plugin) - return nil - }, - } - registry.git = mockGit - - plugins, err := registry.List() - assert.NoError(t, err) - assert.Len(t, plugins, 1) - assert.Equal(t, "NewPlugin", plugins[0].Name) - }) -} - -func TestUpdate(t *testing.T) { - t.Run("clones repository when cache doesn't exist", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - plugin := Plugin{ - Name: "RepoPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - cloneCalled := false - mockGit := &mockGitClient{ - cloneFunc: func(path string, url string) error { - cloneCalled = true - assert.Equal(t, registryRepo, url) - assert.Equal(t, tmpDir, path) - createTestPlugin(t, fs, path, "plugin.json", plugin) - return nil - }, - } - registry.git = mockGit - - err := registry.Update() - assert.NoError(t, err) - assert.True(t, cloneCalled) - assert.Len(t, registry.plugins, 1) - assert.Equal(t, "RepoPlugin", registry.plugins[0].Name) - }) - - t.Run("pulls updates when cache exists", func(t *testing.T) { - registry, fs, tmpDir := setupTestRegistry(t) - - plugin := Plugin{ - Name: "UpdatedPlugin", - Capabilities: []string{"test"}, - Category: "test", - Repo: "https://github.com/test/test", - Author: "Test", - Description: "Test", - Compositors: []string{"niri"}, - Distro: []string{"any"}, - } - - err := fs.MkdirAll(tmpDir, 0o755) - require.NoError(t, err) - - pullCalled := false - mockGit := &mockGitClient{ - pullFunc: func(path string) error { - pullCalled = true - assert.Equal(t, tmpDir, path) - createTestPlugin(t, fs, path, "plugin.json", plugin) - return nil - }, - } - registry.git = mockGit - - err = registry.Update() - assert.NoError(t, err) - assert.True(t, pullCalled) - assert.Len(t, registry.plugins, 1) - assert.Equal(t, "UpdatedPlugin", registry.plugins[0].Name) - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/search.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/search.go deleted file mode 100644 index 833158f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/plugins/search.go +++ /dev/null @@ -1,91 +0,0 @@ -package plugins - -import ( - "sort" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -func FuzzySearch(query string, plugins []Plugin) []Plugin { - if query == "" { - return plugins - } - - queryLower := strings.ToLower(query) - return utils.Filter(plugins, func(p Plugin) bool { - return fuzzyMatch(queryLower, strings.ToLower(p.Name)) || - fuzzyMatch(queryLower, strings.ToLower(p.Category)) || - fuzzyMatch(queryLower, strings.ToLower(p.Description)) || - fuzzyMatch(queryLower, strings.ToLower(p.Author)) - }) -} - -func fuzzyMatch(query, text string) bool { - queryIdx := 0 - for _, char := range text { - if queryIdx < len(query) && char == rune(query[queryIdx]) { - queryIdx++ - } - } - return queryIdx == len(query) -} - -func FilterByCategory(category string, plugins []Plugin) []Plugin { - if category == "" { - return plugins - } - categoryLower := strings.ToLower(category) - return utils.Filter(plugins, func(p Plugin) bool { - return strings.ToLower(p.Category) == categoryLower - }) -} - -func FilterByCompositor(compositor string, plugins []Plugin) []Plugin { - if compositor == "" { - return plugins - } - compositorLower := strings.ToLower(compositor) - return utils.Filter(plugins, func(p Plugin) bool { - return utils.Any(p.Compositors, func(c string) bool { - return strings.ToLower(c) == compositorLower - }) - }) -} - -func FilterByCapability(capability string, plugins []Plugin) []Plugin { - if capability == "" { - return plugins - } - capabilityLower := strings.ToLower(capability) - return utils.Filter(plugins, func(p Plugin) bool { - return utils.Any(p.Capabilities, func(c string) bool { - return strings.ToLower(c) == capabilityLower - }) - }) -} - -func SortByFirstParty(plugins []Plugin) []Plugin { - sort.SliceStable(plugins, func(i, j int) bool { - if plugins[i].Featured != plugins[j].Featured { - return plugins[i].Featured - } - isFirstPartyI := strings.HasPrefix(plugins[i].Repo, "https://github.com/AvengeMedia") - isFirstPartyJ := strings.HasPrefix(plugins[j].Repo, "https://github.com/AvengeMedia") - if isFirstPartyI != isFirstPartyJ { - return isFirstPartyI - } - return false - }) - return plugins -} - -func FindByIDOrName(idOrName string, plugins []Plugin) *Plugin { - if p, found := utils.Find(plugins, func(p Plugin) bool { return p.ID == idOrName }); found { - return &p - } - if p, found := utils.Find(plugins, func(p Plugin) bool { return p.Name == idOrName }); found { - return &p - } - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/dwl_ipc/dwl_ipc.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/dwl_ipc/dwl_ipc.go deleted file mode 100644 index fc0e50b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/dwl_ipc/dwl_ipc.go +++ /dev/null @@ -1,791 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/cmd/go-wayland-scanner -// XML file : internal/proto/xml/dwl-ipc-unstable-v2.xml -// -// dwl_ipc_unstable_v2 Protocol Copyright: - -package dwl_ipc - -import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - -// ZdwlIpcManagerV2InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZdwlIpcManagerV2InterfaceName = "zdwl_ipc_manager_v2" - -// ZdwlIpcManagerV2 : manage dwl state -// -// This interface is exposed as a global in wl_registry. -// -// Clients can use this interface to get a dwl_ipc_output. -// After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. -// The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. -type ZdwlIpcManagerV2 struct { - client.BaseProxy - tagsHandler ZdwlIpcManagerV2TagsHandlerFunc - layoutHandler ZdwlIpcManagerV2LayoutHandlerFunc -} - -// NewZdwlIpcManagerV2 : manage dwl state -// -// This interface is exposed as a global in wl_registry. -// -// Clients can use this interface to get a dwl_ipc_output. -// After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. -// The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. -func NewZdwlIpcManagerV2(ctx *client.Context) *ZdwlIpcManagerV2 { - zdwlIpcManagerV2 := &ZdwlIpcManagerV2{} - ctx.Register(zdwlIpcManagerV2) - return zdwlIpcManagerV2 -} - -// Release : release dwl_ipc_manager -// -// Indicates that the client will not the dwl_ipc_manager object anymore. -// Objects created through this instance are not affected. -func (i *ZdwlIpcManagerV2) Release() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// GetOutput : get a dwl_ipc_outout for a wl_output -// -// Get a dwl_ipc_outout for the specified wl_output. -func (i *ZdwlIpcManagerV2) GetOutput(output *client.Output) (*ZdwlIpcOutputV2, error) { - id := NewZdwlIpcOutputV2(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// ZdwlIpcManagerV2TagsEvent : Announces tag amount -// -// This event is sent after binding. -// A roundtrip after binding guarantees the client recieved all tags. -type ZdwlIpcManagerV2TagsEvent struct { - Amount uint32 -} -type ZdwlIpcManagerV2TagsHandlerFunc func(ZdwlIpcManagerV2TagsEvent) - -// SetTagsHandler : sets handler for ZdwlIpcManagerV2TagsEvent -func (i *ZdwlIpcManagerV2) SetTagsHandler(f ZdwlIpcManagerV2TagsHandlerFunc) { - i.tagsHandler = f -} - -// ZdwlIpcManagerV2LayoutEvent : Announces a layout -// -// This event is sent after binding. -// A roundtrip after binding guarantees the client recieved all layouts. -type ZdwlIpcManagerV2LayoutEvent struct { - Name string -} -type ZdwlIpcManagerV2LayoutHandlerFunc func(ZdwlIpcManagerV2LayoutEvent) - -// SetLayoutHandler : sets handler for ZdwlIpcManagerV2LayoutEvent -func (i *ZdwlIpcManagerV2) SetLayoutHandler(f ZdwlIpcManagerV2LayoutHandlerFunc) { - i.layoutHandler = f -} - -func (i *ZdwlIpcManagerV2) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.tagsHandler == nil { - return - } - var e ZdwlIpcManagerV2TagsEvent - l := 0 - e.Amount = client.Uint32(data[l : l+4]) - l += 4 - - i.tagsHandler(e) - case 1: - if i.layoutHandler == nil { - return - } - var e ZdwlIpcManagerV2LayoutEvent - l := 0 - nameLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Name = client.String(data[l : l+nameLen]) - l += nameLen - - i.layoutHandler(e) - } -} - -// ZdwlIpcOutputV2InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZdwlIpcOutputV2InterfaceName = "zdwl_ipc_output_v2" - -// ZdwlIpcOutputV2 : control dwl output -// -// Observe and control a dwl output. -// -// Events are double-buffered: -// Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. -// -// Request are not double-buffered: -// The compositor will update immediately upon request. -type ZdwlIpcOutputV2 struct { - client.BaseProxy - toggleVisibilityHandler ZdwlIpcOutputV2ToggleVisibilityHandlerFunc - activeHandler ZdwlIpcOutputV2ActiveHandlerFunc - tagHandler ZdwlIpcOutputV2TagHandlerFunc - layoutHandler ZdwlIpcOutputV2LayoutHandlerFunc - titleHandler ZdwlIpcOutputV2TitleHandlerFunc - appidHandler ZdwlIpcOutputV2AppidHandlerFunc - layoutSymbolHandler ZdwlIpcOutputV2LayoutSymbolHandlerFunc - frameHandler ZdwlIpcOutputV2FrameHandlerFunc - fullscreenHandler ZdwlIpcOutputV2FullscreenHandlerFunc - floatingHandler ZdwlIpcOutputV2FloatingHandlerFunc - xHandler ZdwlIpcOutputV2XHandlerFunc - yHandler ZdwlIpcOutputV2YHandlerFunc - widthHandler ZdwlIpcOutputV2WidthHandlerFunc - heightHandler ZdwlIpcOutputV2HeightHandlerFunc - lastLayerHandler ZdwlIpcOutputV2LastLayerHandlerFunc - kbLayoutHandler ZdwlIpcOutputV2KbLayoutHandlerFunc - keymodeHandler ZdwlIpcOutputV2KeymodeHandlerFunc - scalefactorHandler ZdwlIpcOutputV2ScalefactorHandlerFunc -} - -// NewZdwlIpcOutputV2 : control dwl output -// -// Observe and control a dwl output. -// -// Events are double-buffered: -// Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. -// -// Request are not double-buffered: -// The compositor will update immediately upon request. -func NewZdwlIpcOutputV2(ctx *client.Context) *ZdwlIpcOutputV2 { - zdwlIpcOutputV2 := &ZdwlIpcOutputV2{} - ctx.Register(zdwlIpcOutputV2) - return zdwlIpcOutputV2 -} - -// Release : release dwl_ipc_outout -// -// Indicates to that the client no longer needs this dwl_ipc_output. -func (i *ZdwlIpcOutputV2) Release() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetTags : Set the active tags of this output -// -// tagmask: bitmask of the tags that should be set. -// toggleTagset: toggle the selected tagset, zero for invalid, nonzero for valid. -func (i *ZdwlIpcOutputV2) SetTags(tagmask, toggleTagset uint32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(tagmask)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(toggleTagset)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetClientTags : Set the tags of the focused client. -// -// The tags are updated as follows: -// new_tags = (current_tags AND and_tags) XOR xor_tags -func (i *ZdwlIpcOutputV2) SetClientTags(andTags, xorTags uint32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(andTags)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(xorTags)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetLayout : Set the layout of this output -// -// index: index of a layout recieved by dwl_ipc_manager.layout -func (i *ZdwlIpcOutputV2) SetLayout(index uint32) error { - const opcode = 3 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(index)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Quit : Quit mango -// This request allows clients to instruct the compositor to quit mango. -func (i *ZdwlIpcOutputV2) Quit() error { - const opcode = 4 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SendDispatch : Set the active tags of this output -// -// dispatch: dispatch name. -// arg1: arg1. -// arg2: arg2. -// arg3: arg3. -// arg4: arg4. -// arg5: arg5. -func (i *ZdwlIpcOutputV2) SendDispatch(dispatch, arg1, arg2, arg3, arg4, arg5 string) error { - const opcode = 5 - dispatchLen := client.PaddedLen(len(dispatch) + 1) - arg1Len := client.PaddedLen(len(arg1) + 1) - arg2Len := client.PaddedLen(len(arg2) + 1) - arg3Len := client.PaddedLen(len(arg3) + 1) - arg4Len := client.PaddedLen(len(arg4) + 1) - arg5Len := client.PaddedLen(len(arg5) + 1) - _reqBufLen := 8 + (4 + dispatchLen) + (4 + arg1Len) + (4 + arg2Len) + (4 + arg3Len) + (4 + arg4Len) + (4 + arg5Len) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutString(_reqBuf[l:l+(4+dispatchLen)], dispatch) - l += (4 + dispatchLen) - client.PutString(_reqBuf[l:l+(4+arg1Len)], arg1) - l += (4 + arg1Len) - client.PutString(_reqBuf[l:l+(4+arg2Len)], arg2) - l += (4 + arg2Len) - client.PutString(_reqBuf[l:l+(4+arg3Len)], arg3) - l += (4 + arg3Len) - client.PutString(_reqBuf[l:l+(4+arg4Len)], arg4) - l += (4 + arg4Len) - client.PutString(_reqBuf[l:l+(4+arg5Len)], arg5) - l += (4 + arg5Len) - err := i.Context().WriteMsg(_reqBuf, nil) - return err -} - -type ZdwlIpcOutputV2TagState uint32 - -// ZdwlIpcOutputV2TagState : -const ( - // ZdwlIpcOutputV2TagStateNone : no state - ZdwlIpcOutputV2TagStateNone ZdwlIpcOutputV2TagState = 0 - // ZdwlIpcOutputV2TagStateActive : tag is active - ZdwlIpcOutputV2TagStateActive ZdwlIpcOutputV2TagState = 1 - // ZdwlIpcOutputV2TagStateUrgent : tag has at least one urgent client - ZdwlIpcOutputV2TagStateUrgent ZdwlIpcOutputV2TagState = 2 -) - -func (e ZdwlIpcOutputV2TagState) Name() string { - switch e { - case ZdwlIpcOutputV2TagStateNone: - return "none" - case ZdwlIpcOutputV2TagStateActive: - return "active" - case ZdwlIpcOutputV2TagStateUrgent: - return "urgent" - default: - return "" - } -} - -func (e ZdwlIpcOutputV2TagState) Value() string { - switch e { - case ZdwlIpcOutputV2TagStateNone: - return "0" - case ZdwlIpcOutputV2TagStateActive: - return "1" - case ZdwlIpcOutputV2TagStateUrgent: - return "2" - default: - return "" - } -} - -func (e ZdwlIpcOutputV2TagState) String() string { - return e.Name() + "=" + e.Value() -} - -// ZdwlIpcOutputV2ToggleVisibilityEvent : Toggle client visibilty -// -// Indicates the client should hide or show themselves. -// If the client is visible then hide, if hidden then show. -type ZdwlIpcOutputV2ToggleVisibilityEvent struct{} -type ZdwlIpcOutputV2ToggleVisibilityHandlerFunc func(ZdwlIpcOutputV2ToggleVisibilityEvent) - -// SetToggleVisibilityHandler : sets handler for ZdwlIpcOutputV2ToggleVisibilityEvent -func (i *ZdwlIpcOutputV2) SetToggleVisibilityHandler(f ZdwlIpcOutputV2ToggleVisibilityHandlerFunc) { - i.toggleVisibilityHandler = f -} - -// ZdwlIpcOutputV2ActiveEvent : Update the selected output. -// -// Indicates if the output is active. Zero is invalid, nonzero is valid. -type ZdwlIpcOutputV2ActiveEvent struct { - Active uint32 -} -type ZdwlIpcOutputV2ActiveHandlerFunc func(ZdwlIpcOutputV2ActiveEvent) - -// SetActiveHandler : sets handler for ZdwlIpcOutputV2ActiveEvent -func (i *ZdwlIpcOutputV2) SetActiveHandler(f ZdwlIpcOutputV2ActiveHandlerFunc) { - i.activeHandler = f -} - -// ZdwlIpcOutputV2TagEvent : Update the state of a tag. -// -// Indicates that a tag has been updated. -type ZdwlIpcOutputV2TagEvent struct { - Tag uint32 - State uint32 - Clients uint32 - Focused uint32 -} -type ZdwlIpcOutputV2TagHandlerFunc func(ZdwlIpcOutputV2TagEvent) - -// SetTagHandler : sets handler for ZdwlIpcOutputV2TagEvent -func (i *ZdwlIpcOutputV2) SetTagHandler(f ZdwlIpcOutputV2TagHandlerFunc) { - i.tagHandler = f -} - -// ZdwlIpcOutputV2LayoutEvent : Update the layout. -// -// Indicates a new layout is selected. -type ZdwlIpcOutputV2LayoutEvent struct { - Layout uint32 -} -type ZdwlIpcOutputV2LayoutHandlerFunc func(ZdwlIpcOutputV2LayoutEvent) - -// SetLayoutHandler : sets handler for ZdwlIpcOutputV2LayoutEvent -func (i *ZdwlIpcOutputV2) SetLayoutHandler(f ZdwlIpcOutputV2LayoutHandlerFunc) { - i.layoutHandler = f -} - -// ZdwlIpcOutputV2TitleEvent : Update the title. -// -// Indicates the title has changed. -type ZdwlIpcOutputV2TitleEvent struct { - Title string -} -type ZdwlIpcOutputV2TitleHandlerFunc func(ZdwlIpcOutputV2TitleEvent) - -// SetTitleHandler : sets handler for ZdwlIpcOutputV2TitleEvent -func (i *ZdwlIpcOutputV2) SetTitleHandler(f ZdwlIpcOutputV2TitleHandlerFunc) { - i.titleHandler = f -} - -// ZdwlIpcOutputV2AppidEvent : Update the appid. -// -// Indicates the appid has changed. -type ZdwlIpcOutputV2AppidEvent struct { - Appid string -} -type ZdwlIpcOutputV2AppidHandlerFunc func(ZdwlIpcOutputV2AppidEvent) - -// SetAppidHandler : sets handler for ZdwlIpcOutputV2AppidEvent -func (i *ZdwlIpcOutputV2) SetAppidHandler(f ZdwlIpcOutputV2AppidHandlerFunc) { - i.appidHandler = f -} - -// ZdwlIpcOutputV2LayoutSymbolEvent : Update the current layout symbol -// -// Indicates the layout has changed. Since layout symbols are dynamic. -// As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. -// You can ignore the zdwl_ipc_output.layout event. -type ZdwlIpcOutputV2LayoutSymbolEvent struct { - Layout string -} -type ZdwlIpcOutputV2LayoutSymbolHandlerFunc func(ZdwlIpcOutputV2LayoutSymbolEvent) - -// SetLayoutSymbolHandler : sets handler for ZdwlIpcOutputV2LayoutSymbolEvent -func (i *ZdwlIpcOutputV2) SetLayoutSymbolHandler(f ZdwlIpcOutputV2LayoutSymbolHandlerFunc) { - i.layoutSymbolHandler = f -} - -// ZdwlIpcOutputV2FrameEvent : The update sequence is done. -// -// Indicates that a sequence of status updates have finished and the client should redraw. -type ZdwlIpcOutputV2FrameEvent struct{} -type ZdwlIpcOutputV2FrameHandlerFunc func(ZdwlIpcOutputV2FrameEvent) - -// SetFrameHandler : sets handler for ZdwlIpcOutputV2FrameEvent -func (i *ZdwlIpcOutputV2) SetFrameHandler(f ZdwlIpcOutputV2FrameHandlerFunc) { - i.frameHandler = f -} - -// ZdwlIpcOutputV2FullscreenEvent : Update fullscreen status -// -// Indicates if the selected client on this output is fullscreen. -type ZdwlIpcOutputV2FullscreenEvent struct { - IsFullscreen uint32 -} -type ZdwlIpcOutputV2FullscreenHandlerFunc func(ZdwlIpcOutputV2FullscreenEvent) - -// SetFullscreenHandler : sets handler for ZdwlIpcOutputV2FullscreenEvent -func (i *ZdwlIpcOutputV2) SetFullscreenHandler(f ZdwlIpcOutputV2FullscreenHandlerFunc) { - i.fullscreenHandler = f -} - -// ZdwlIpcOutputV2FloatingEvent : Update the floating status -// -// Indicates if the selected client on this output is floating. -type ZdwlIpcOutputV2FloatingEvent struct { - IsFloating uint32 -} -type ZdwlIpcOutputV2FloatingHandlerFunc func(ZdwlIpcOutputV2FloatingEvent) - -// SetFloatingHandler : sets handler for ZdwlIpcOutputV2FloatingEvent -func (i *ZdwlIpcOutputV2) SetFloatingHandler(f ZdwlIpcOutputV2FloatingHandlerFunc) { - i.floatingHandler = f -} - -// ZdwlIpcOutputV2XEvent : Update the x coordinates -// -// Indicates if x coordinates of the selected client. -type ZdwlIpcOutputV2XEvent struct { - X int32 -} -type ZdwlIpcOutputV2XHandlerFunc func(ZdwlIpcOutputV2XEvent) - -// SetXHandler : sets handler for ZdwlIpcOutputV2XEvent -func (i *ZdwlIpcOutputV2) SetXHandler(f ZdwlIpcOutputV2XHandlerFunc) { - i.xHandler = f -} - -// ZdwlIpcOutputV2YEvent : Update the y coordinates -// -// Indicates if y coordinates of the selected client. -type ZdwlIpcOutputV2YEvent struct { - Y int32 -} -type ZdwlIpcOutputV2YHandlerFunc func(ZdwlIpcOutputV2YEvent) - -// SetYHandler : sets handler for ZdwlIpcOutputV2YEvent -func (i *ZdwlIpcOutputV2) SetYHandler(f ZdwlIpcOutputV2YHandlerFunc) { - i.yHandler = f -} - -// ZdwlIpcOutputV2WidthEvent : Update the width -// -// Indicates if width of the selected client. -type ZdwlIpcOutputV2WidthEvent struct { - Width int32 -} -type ZdwlIpcOutputV2WidthHandlerFunc func(ZdwlIpcOutputV2WidthEvent) - -// SetWidthHandler : sets handler for ZdwlIpcOutputV2WidthEvent -func (i *ZdwlIpcOutputV2) SetWidthHandler(f ZdwlIpcOutputV2WidthHandlerFunc) { - i.widthHandler = f -} - -// ZdwlIpcOutputV2HeightEvent : Update the height -// -// Indicates if height of the selected client. -type ZdwlIpcOutputV2HeightEvent struct { - Height int32 -} -type ZdwlIpcOutputV2HeightHandlerFunc func(ZdwlIpcOutputV2HeightEvent) - -// SetHeightHandler : sets handler for ZdwlIpcOutputV2HeightEvent -func (i *ZdwlIpcOutputV2) SetHeightHandler(f ZdwlIpcOutputV2HeightHandlerFunc) { - i.heightHandler = f -} - -// ZdwlIpcOutputV2LastLayerEvent : last map layer. -// -// last map layer. -type ZdwlIpcOutputV2LastLayerEvent struct { - LastLayer string -} -type ZdwlIpcOutputV2LastLayerHandlerFunc func(ZdwlIpcOutputV2LastLayerEvent) - -// SetLastLayerHandler : sets handler for ZdwlIpcOutputV2LastLayerEvent -func (i *ZdwlIpcOutputV2) SetLastLayerHandler(f ZdwlIpcOutputV2LastLayerHandlerFunc) { - i.lastLayerHandler = f -} - -// ZdwlIpcOutputV2KbLayoutEvent : current keyboard layout. -// -// current keyboard layout. -type ZdwlIpcOutputV2KbLayoutEvent struct { - KbLayout string -} -type ZdwlIpcOutputV2KbLayoutHandlerFunc func(ZdwlIpcOutputV2KbLayoutEvent) - -// SetKbLayoutHandler : sets handler for ZdwlIpcOutputV2KbLayoutEvent -func (i *ZdwlIpcOutputV2) SetKbLayoutHandler(f ZdwlIpcOutputV2KbLayoutHandlerFunc) { - i.kbLayoutHandler = f -} - -// ZdwlIpcOutputV2KeymodeEvent : current keybind mode. -// -// current keybind mode. -type ZdwlIpcOutputV2KeymodeEvent struct { - Keymode string -} -type ZdwlIpcOutputV2KeymodeHandlerFunc func(ZdwlIpcOutputV2KeymodeEvent) - -// SetKeymodeHandler : sets handler for ZdwlIpcOutputV2KeymodeEvent -func (i *ZdwlIpcOutputV2) SetKeymodeHandler(f ZdwlIpcOutputV2KeymodeHandlerFunc) { - i.keymodeHandler = f -} - -// ZdwlIpcOutputV2ScalefactorEvent : scale factor of monitor. -// -// scale factor of monitor. -type ZdwlIpcOutputV2ScalefactorEvent struct { - Scalefactor uint32 -} -type ZdwlIpcOutputV2ScalefactorHandlerFunc func(ZdwlIpcOutputV2ScalefactorEvent) - -// SetScalefactorHandler : sets handler for ZdwlIpcOutputV2ScalefactorEvent -func (i *ZdwlIpcOutputV2) SetScalefactorHandler(f ZdwlIpcOutputV2ScalefactorHandlerFunc) { - i.scalefactorHandler = f -} - -func (i *ZdwlIpcOutputV2) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.toggleVisibilityHandler == nil { - return - } - var e ZdwlIpcOutputV2ToggleVisibilityEvent - - i.toggleVisibilityHandler(e) - case 1: - if i.activeHandler == nil { - return - } - var e ZdwlIpcOutputV2ActiveEvent - l := 0 - e.Active = client.Uint32(data[l : l+4]) - l += 4 - - i.activeHandler(e) - case 2: - if i.tagHandler == nil { - return - } - var e ZdwlIpcOutputV2TagEvent - l := 0 - e.Tag = client.Uint32(data[l : l+4]) - l += 4 - e.State = client.Uint32(data[l : l+4]) - l += 4 - e.Clients = client.Uint32(data[l : l+4]) - l += 4 - e.Focused = client.Uint32(data[l : l+4]) - l += 4 - - i.tagHandler(e) - case 3: - if i.layoutHandler == nil { - return - } - var e ZdwlIpcOutputV2LayoutEvent - l := 0 - e.Layout = client.Uint32(data[l : l+4]) - l += 4 - - i.layoutHandler(e) - case 4: - if i.titleHandler == nil { - return - } - var e ZdwlIpcOutputV2TitleEvent - l := 0 - titleLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Title = client.String(data[l : l+titleLen]) - l += titleLen - - i.titleHandler(e) - case 5: - if i.appidHandler == nil { - return - } - var e ZdwlIpcOutputV2AppidEvent - l := 0 - appidLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Appid = client.String(data[l : l+appidLen]) - l += appidLen - - i.appidHandler(e) - case 6: - if i.layoutSymbolHandler == nil { - return - } - var e ZdwlIpcOutputV2LayoutSymbolEvent - l := 0 - layoutLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Layout = client.String(data[l : l+layoutLen]) - l += layoutLen - - i.layoutSymbolHandler(e) - case 7: - if i.frameHandler == nil { - return - } - var e ZdwlIpcOutputV2FrameEvent - - i.frameHandler(e) - case 8: - if i.fullscreenHandler == nil { - return - } - var e ZdwlIpcOutputV2FullscreenEvent - l := 0 - e.IsFullscreen = client.Uint32(data[l : l+4]) - l += 4 - - i.fullscreenHandler(e) - case 9: - if i.floatingHandler == nil { - return - } - var e ZdwlIpcOutputV2FloatingEvent - l := 0 - e.IsFloating = client.Uint32(data[l : l+4]) - l += 4 - - i.floatingHandler(e) - case 10: - if i.xHandler == nil { - return - } - var e ZdwlIpcOutputV2XEvent - l := 0 - e.X = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.xHandler(e) - case 11: - if i.yHandler == nil { - return - } - var e ZdwlIpcOutputV2YEvent - l := 0 - e.Y = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.yHandler(e) - case 12: - if i.widthHandler == nil { - return - } - var e ZdwlIpcOutputV2WidthEvent - l := 0 - e.Width = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.widthHandler(e) - case 13: - if i.heightHandler == nil { - return - } - var e ZdwlIpcOutputV2HeightEvent - l := 0 - e.Height = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.heightHandler(e) - case 14: - if i.lastLayerHandler == nil { - return - } - var e ZdwlIpcOutputV2LastLayerEvent - l := 0 - lastLayerLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.LastLayer = client.String(data[l : l+lastLayerLen]) - l += lastLayerLen - - i.lastLayerHandler(e) - case 15: - if i.kbLayoutHandler == nil { - return - } - var e ZdwlIpcOutputV2KbLayoutEvent - l := 0 - kbLayoutLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.KbLayout = client.String(data[l : l+kbLayoutLen]) - l += kbLayoutLen - - i.kbLayoutHandler(e) - case 16: - if i.keymodeHandler == nil { - return - } - var e ZdwlIpcOutputV2KeymodeEvent - l := 0 - keymodeLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Keymode = client.String(data[l : l+keymodeLen]) - l += keymodeLen - - i.keymodeHandler(e) - case 17: - if i.scalefactorHandler == nil { - return - } - var e ZdwlIpcOutputV2ScalefactorEvent - l := 0 - e.Scalefactor = client.Uint32(data[l : l+4]) - l += 4 - - i.scalefactorHandler(e) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/ext_data_control/data_control.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/ext_data_control/data_control.go deleted file mode 100644 index 3a4de9f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/ext_data_control/data_control.go +++ /dev/null @@ -1,695 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : internal/proto/xml/ext-data-control-v1.xml -// -// ext_data_control_v1 Protocol Copyright: -// -// Copyright © 2018 Simon Ser -// Copyright © 2019 Ivan Molodetskikh -// Copyright © 2024 Neal Gompa -// -// Permission to use, copy, modify, distribute, and sell this -// software and its documentation for any purpose is hereby granted -// without fee, provided that the above copyright notice appear in -// all copies and that both that copyright notice and this permission -// notice appear in supporting documentation, and that the name of -// the copyright holders not be used in advertising or publicity -// pertaining to distribution of the software without specific, -// written prior permission. The copyright holders make no -// representations about the suitability of this software for any -// purpose. It is provided "as is" without express or implied -// warranty. -// -// THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS -// SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY -// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN -// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -// THIS SOFTWARE. - -package ext_data_control - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "golang.org/x/sys/unix" -) - -// ExtDataControlManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ExtDataControlManagerV1InterfaceName = "ext_data_control_manager_v1" - -// ExtDataControlManagerV1 : manager to control data devices -// -// This interface is a manager that allows creating per-seat data device -// controls. -type ExtDataControlManagerV1 struct { - client.BaseProxy -} - -// NewExtDataControlManagerV1 : manager to control data devices -// -// This interface is a manager that allows creating per-seat data device -// controls. -func NewExtDataControlManagerV1(ctx *client.Context) *ExtDataControlManagerV1 { - extDataControlManagerV1 := &ExtDataControlManagerV1{} - ctx.Register(extDataControlManagerV1) - return extDataControlManagerV1 -} - -// CreateDataSource : create a new data source -// -// Create a new data source. -func (i *ExtDataControlManagerV1) CreateDataSource() (*ExtDataControlSourceV1, error) { - id := NewExtDataControlSourceV1(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// GetDataDevice : get a data device for a seat -// -// Create a data device that can be used to manage a seat's selection. -func (i *ExtDataControlManagerV1) GetDataDevice(seat *client.Seat) (*ExtDataControlDeviceV1, error) { - id := NewExtDataControlDeviceV1(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], seat.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// GetDataDeviceWithProxy : get a data device for a seat using a pre-created proxy -// -// Like GetDataDevice, but uses a pre-created ExtDataControlDeviceV1 proxy. -// This allows setting up event handlers before the request is sent. -func (i *ExtDataControlManagerV1) GetDataDeviceWithProxy(device *ExtDataControlDeviceV1, seat *client.Seat) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], device.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], seat.ID()) - l += 4 - return i.Context().WriteMsg(_reqBuf[:], nil) -} - -// Destroy : destroy the manager -// -// All objects created by the manager will still remain valid, until their -// appropriate destroy request has been called. -func (i *ExtDataControlManagerV1) Destroy() error { - defer i.MarkZombie() - const opcode = 2 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ExtDataControlDeviceV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ExtDataControlDeviceV1InterfaceName = "ext_data_control_device_v1" - -// ExtDataControlDeviceV1 : manage a data device for a seat -// -// This interface allows a client to manage a seat's selection. -// -// When the seat is destroyed, this object becomes inert. -type ExtDataControlDeviceV1 struct { - client.BaseProxy - dataOfferHandler ExtDataControlDeviceV1DataOfferHandlerFunc - selectionHandler ExtDataControlDeviceV1SelectionHandlerFunc - finishedHandler ExtDataControlDeviceV1FinishedHandlerFunc - primarySelectionHandler ExtDataControlDeviceV1PrimarySelectionHandlerFunc -} - -// NewExtDataControlDeviceV1 : manage a data device for a seat -// -// This interface allows a client to manage a seat's selection. -// -// When the seat is destroyed, this object becomes inert. -func NewExtDataControlDeviceV1(ctx *client.Context) *ExtDataControlDeviceV1 { - extDataControlDeviceV1 := &ExtDataControlDeviceV1{} - ctx.Register(extDataControlDeviceV1) - return extDataControlDeviceV1 -} - -// SetSelection : copy data to the selection -// -// This request asks the compositor to set the selection to the data from -// the source on behalf of the client. -// -// The given source may not be used in any further set_selection or -// set_primary_selection requests. Attempting to use a previously used -// source triggers the used_source protocol error. -// -// To unset the selection, set the source to NULL. -func (i *ExtDataControlDeviceV1) SetSelection(source *ExtDataControlSourceV1) error { - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - if source == nil { - client.PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - client.PutUint32(_reqBuf[l:l+4], source.ID()) - l += 4 - } - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Destroy : destroy this data device -// -// Destroys the data device object. -func (i *ExtDataControlDeviceV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetPrimarySelection : copy data to the primary selection -// -// This request asks the compositor to set the primary selection to the -// data from the source on behalf of the client. -// -// The given source may not be used in any further set_selection or -// set_primary_selection requests. Attempting to use a previously used -// source triggers the used_source protocol error. -// -// To unset the primary selection, set the source to NULL. -// -// The compositor will ignore this request if it does not support primary -// selection. -func (i *ExtDataControlDeviceV1) SetPrimarySelection(source *ExtDataControlSourceV1) error { - const opcode = 2 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - if source == nil { - client.PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - client.PutUint32(_reqBuf[l:l+4], source.ID()) - l += 4 - } - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ExtDataControlDeviceV1Error uint32 - -// ExtDataControlDeviceV1Error : -const ( - // ExtDataControlDeviceV1ErrorUsedSource : source given to set_selection or set_primary_selection was already used before - ExtDataControlDeviceV1ErrorUsedSource ExtDataControlDeviceV1Error = 1 -) - -func (e ExtDataControlDeviceV1Error) Name() string { - switch e { - case ExtDataControlDeviceV1ErrorUsedSource: - return "used_source" - default: - return "" - } -} - -func (e ExtDataControlDeviceV1Error) Value() string { - switch e { - case ExtDataControlDeviceV1ErrorUsedSource: - return "1" - default: - return "" - } -} - -func (e ExtDataControlDeviceV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -// ExtDataControlDeviceV1DataOfferEvent : introduce a new ext_data_control_offer -// -// The data_offer event introduces a new ext_data_control_offer object, -// which will subsequently be used in either the -// ext_data_control_device.selection event (for the regular clipboard -// selections) or the ext_data_control_device.primary_selection event (for -// the primary clipboard selections). Immediately following the -// ext_data_control_device.data_offer event, the new data_offer object -// will send out ext_data_control_offer.offer events to describe the MIME -// types it offers. -type ExtDataControlDeviceV1DataOfferEvent struct { - Id *ExtDataControlOfferV1 -} -type ExtDataControlDeviceV1DataOfferHandlerFunc func(ExtDataControlDeviceV1DataOfferEvent) - -// SetDataOfferHandler : sets handler for ExtDataControlDeviceV1DataOfferEvent -func (i *ExtDataControlDeviceV1) SetDataOfferHandler(f ExtDataControlDeviceV1DataOfferHandlerFunc) { - i.dataOfferHandler = f -} - -// ExtDataControlDeviceV1SelectionEvent : advertise new selection -// -// The selection event is sent out to notify the client of a new -// ext_data_control_offer for the selection for this device. The -// ext_data_control_device.data_offer and the ext_data_control_offer.offer -// events are sent out immediately before this event to introduce the data -// offer object. The selection event is sent to a client when a new -// selection is set. The ext_data_control_offer is valid until a new -// ext_data_control_offer or NULL is received. The client must destroy the -// previous selection ext_data_control_offer, if any, upon receiving this -// event. Regardless, the previous selection will be ignored once a new -// selection ext_data_control_offer is received. -// -// The first selection event is sent upon binding the -// ext_data_control_device object. -type ExtDataControlDeviceV1SelectionEvent struct { - Id *ExtDataControlOfferV1 - OfferId uint32 // Raw object ID for external registry lookups -} -type ExtDataControlDeviceV1SelectionHandlerFunc func(ExtDataControlDeviceV1SelectionEvent) - -// SetSelectionHandler : sets handler for ExtDataControlDeviceV1SelectionEvent -func (i *ExtDataControlDeviceV1) SetSelectionHandler(f ExtDataControlDeviceV1SelectionHandlerFunc) { - i.selectionHandler = f -} - -// ExtDataControlDeviceV1FinishedEvent : this data control is no longer valid -// -// This data control object is no longer valid and should be destroyed by -// the client. -type ExtDataControlDeviceV1FinishedEvent struct{} -type ExtDataControlDeviceV1FinishedHandlerFunc func(ExtDataControlDeviceV1FinishedEvent) - -// SetFinishedHandler : sets handler for ExtDataControlDeviceV1FinishedEvent -func (i *ExtDataControlDeviceV1) SetFinishedHandler(f ExtDataControlDeviceV1FinishedHandlerFunc) { - i.finishedHandler = f -} - -// ExtDataControlDeviceV1PrimarySelectionEvent : advertise new primary selection -// -// The primary_selection event is sent out to notify the client of a new -// ext_data_control_offer for the primary selection for this device. The -// ext_data_control_device.data_offer and the ext_data_control_offer.offer -// events are sent out immediately before this event to introduce the data -// offer object. The primary_selection event is sent to a client when a -// new primary selection is set. The ext_data_control_offer is valid until -// a new ext_data_control_offer or NULL is received. The client must -// destroy the previous primary selection ext_data_control_offer, if any, -// upon receiving this event. Regardless, the previous primary selection -// will be ignored once a new primary selection ext_data_control_offer is -// received. -// -// If the compositor supports primary selection, the first -// primary_selection event is sent upon binding the -// ext_data_control_device object. -type ExtDataControlDeviceV1PrimarySelectionEvent struct { - Id *ExtDataControlOfferV1 - OfferId uint32 // Raw object ID for external registry lookups -} -type ExtDataControlDeviceV1PrimarySelectionHandlerFunc func(ExtDataControlDeviceV1PrimarySelectionEvent) - -// SetPrimarySelectionHandler : sets handler for ExtDataControlDeviceV1PrimarySelectionEvent -func (i *ExtDataControlDeviceV1) SetPrimarySelectionHandler(f ExtDataControlDeviceV1PrimarySelectionHandlerFunc) { - i.primarySelectionHandler = f -} - -func (i *ExtDataControlDeviceV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - // data_offer event: server creates a new object (new_id) - if i.dataOfferHandler == nil { - return - } - var e ExtDataControlDeviceV1DataOfferEvent - l := 0 - newID := client.Uint32(data[l : l+4]) - l += 4 - - ctx := i.Context() - offer := &ExtDataControlOfferV1{} - offer.SetContext(ctx) - offer.SetID(newID) - ctx.RegisterWithID(offer, newID) - e.Id = offer - - i.dataOfferHandler(e) - case 1: - // selection event: nullable object reference - if i.selectionHandler == nil { - return - } - var e ExtDataControlDeviceV1SelectionEvent - l := 0 - objID := client.Uint32(data[l : l+4]) - l += 4 - - e.OfferId = objID - if objID != 0 { - if p := i.Context().GetProxy(objID); p != nil { - e.Id = p.(*ExtDataControlOfferV1) - } - } - - i.selectionHandler(e) - case 2: - if i.finishedHandler == nil { - return - } - var e ExtDataControlDeviceV1FinishedEvent - - i.finishedHandler(e) - case 3: - // primary_selection event: nullable object reference - if i.primarySelectionHandler == nil { - return - } - var e ExtDataControlDeviceV1PrimarySelectionEvent - l := 0 - objID := client.Uint32(data[l : l+4]) - l += 4 - - e.OfferId = objID - if objID != 0 { - if p := i.Context().GetProxy(objID); p != nil { - e.Id = p.(*ExtDataControlOfferV1) - } - } - - i.primarySelectionHandler(e) - } -} - -// ExtDataControlSourceV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ExtDataControlSourceV1InterfaceName = "ext_data_control_source_v1" - -// ExtDataControlSourceV1 : offer to transfer data -// -// The ext_data_control_source object is the source side of a -// ext_data_control_offer. It is created by the source client in a data -// transfer and provides a way to describe the offered data and a way to -// respond to requests to transfer the data. -type ExtDataControlSourceV1 struct { - client.BaseProxy - sendHandler ExtDataControlSourceV1SendHandlerFunc - cancelledHandler ExtDataControlSourceV1CancelledHandlerFunc -} - -// NewExtDataControlSourceV1 : offer to transfer data -// -// The ext_data_control_source object is the source side of a -// ext_data_control_offer. It is created by the source client in a data -// transfer and provides a way to describe the offered data and a way to -// respond to requests to transfer the data. -func NewExtDataControlSourceV1(ctx *client.Context) *ExtDataControlSourceV1 { - extDataControlSourceV1 := &ExtDataControlSourceV1{} - ctx.Register(extDataControlSourceV1) - return extDataControlSourceV1 -} - -// Offer : add an offered MIME type -// -// This request adds a MIME type to the set of MIME types advertised to -// targets. Can be called several times to offer multiple types. -// -// Calling this after ext_data_control_device.set_selection is a protocol -// error. -// -// mimeType: MIME type offered by the data source -func (i *ExtDataControlSourceV1) Offer(mimeType string) error { - const opcode = 0 - mimeTypeLen := client.PaddedLen(len(mimeType) + 1) - _reqBufLen := 8 + (4 + mimeTypeLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType) - l += (4 + mimeTypeLen) - err := i.Context().WriteMsg(_reqBuf, nil) - return err -} - -// Destroy : destroy this source -// -// Destroys the data source object. -func (i *ExtDataControlSourceV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ExtDataControlSourceV1Error uint32 - -// ExtDataControlSourceV1Error : -const ( - // ExtDataControlSourceV1ErrorInvalidOffer : offer sent after ext_data_control_device.set_selection - ExtDataControlSourceV1ErrorInvalidOffer ExtDataControlSourceV1Error = 1 -) - -func (e ExtDataControlSourceV1Error) Name() string { - switch e { - case ExtDataControlSourceV1ErrorInvalidOffer: - return "invalid_offer" - default: - return "" - } -} - -func (e ExtDataControlSourceV1Error) Value() string { - switch e { - case ExtDataControlSourceV1ErrorInvalidOffer: - return "1" - default: - return "" - } -} - -func (e ExtDataControlSourceV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -// ExtDataControlSourceV1SendEvent : send the data -// -// Request for data from the client. Send the data as the specified MIME -// type over the passed file descriptor, then close it. -type ExtDataControlSourceV1SendEvent struct { - MimeType string - Fd int -} -type ExtDataControlSourceV1SendHandlerFunc func(ExtDataControlSourceV1SendEvent) - -// SetSendHandler : sets handler for ExtDataControlSourceV1SendEvent -func (i *ExtDataControlSourceV1) SetSendHandler(f ExtDataControlSourceV1SendHandlerFunc) { - i.sendHandler = f -} - -// ExtDataControlSourceV1CancelledEvent : selection was cancelled -// -// This data source is no longer valid. The data source has been replaced -// by another data source. -// -// The client should clean up and destroy this data source. -type ExtDataControlSourceV1CancelledEvent struct{} -type ExtDataControlSourceV1CancelledHandlerFunc func(ExtDataControlSourceV1CancelledEvent) - -// SetCancelledHandler : sets handler for ExtDataControlSourceV1CancelledEvent -func (i *ExtDataControlSourceV1) SetCancelledHandler(f ExtDataControlSourceV1CancelledHandlerFunc) { - i.cancelledHandler = f -} - -func (i *ExtDataControlSourceV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.sendHandler == nil { - if fd != -1 { - unix.Close(fd) - } - return - } - var e ExtDataControlSourceV1SendEvent - l := 0 - mimeTypeLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.MimeType = client.String(data[l : l+mimeTypeLen]) - l += mimeTypeLen - e.Fd = fd - - i.sendHandler(e) - case 1: - if i.cancelledHandler == nil { - return - } - var e ExtDataControlSourceV1CancelledEvent - - i.cancelledHandler(e) - } -} - -// ExtDataControlOfferV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ExtDataControlOfferV1InterfaceName = "ext_data_control_offer_v1" - -// ExtDataControlOfferV1 : offer to transfer data -// -// A ext_data_control_offer represents a piece of data offered for transfer -// by another client (the source client). The offer describes the different -// MIME types that the data can be converted to and provides the mechanism -// for transferring the data directly from the source client. -type ExtDataControlOfferV1 struct { - client.BaseProxy - offerHandler ExtDataControlOfferV1OfferHandlerFunc -} - -// NewExtDataControlOfferV1 : offer to transfer data -// -// A ext_data_control_offer represents a piece of data offered for transfer -// by another client (the source client). The offer describes the different -// MIME types that the data can be converted to and provides the mechanism -// for transferring the data directly from the source client. -func NewExtDataControlOfferV1(ctx *client.Context) *ExtDataControlOfferV1 { - extDataControlOfferV1 := &ExtDataControlOfferV1{} - ctx.Register(extDataControlOfferV1) - return extDataControlOfferV1 -} - -// Receive : request that the data is transferred -// -// To transfer the offered data, the client issues this request and -// indicates the MIME type it wants to receive. The transfer happens -// through the passed file descriptor (typically created with the pipe -// system call). The source client writes the data in the MIME type -// representation requested and then closes the file descriptor. -// -// The receiving client reads from the read end of the pipe until EOF and -// then closes its end, at which point the transfer is complete. -// -// This request may happen multiple times for different MIME types. -// -// mimeType: MIME type desired by receiver -// fd: file descriptor for data transfer -func (i *ExtDataControlOfferV1) Receive(mimeType string, fd int) error { - const opcode = 0 - mimeTypeLen := client.PaddedLen(len(mimeType) + 1) - _reqBufLen := 8 + (4 + mimeTypeLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType) - l += (4 + mimeTypeLen) - oob := unix.UnixRights(int(fd)) - err := i.Context().WriteMsg(_reqBuf, oob) - return err -} - -// Destroy : destroy this offer -// -// Destroys the data offer object. -func (i *ExtDataControlOfferV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ExtDataControlOfferV1OfferEvent : advertise offered MIME type -// -// Sent immediately after creating the ext_data_control_offer object. -// One event per offered MIME type. -type ExtDataControlOfferV1OfferEvent struct { - MimeType string -} -type ExtDataControlOfferV1OfferHandlerFunc func(ExtDataControlOfferV1OfferEvent) - -// SetOfferHandler : sets handler for ExtDataControlOfferV1OfferEvent -func (i *ExtDataControlOfferV1) SetOfferHandler(f ExtDataControlOfferV1OfferHandlerFunc) { - i.offerHandler = f -} - -func (i *ExtDataControlOfferV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.offerHandler == nil { - return - } - var e ExtDataControlOfferV1OfferEvent - l := 0 - mimeTypeLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.MimeType = client.String(data[l : l+mimeTypeLen]) - l += mimeTypeLen - - i.offerHandler(e) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/ext_workspace/workspace.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/ext_workspace/workspace.go deleted file mode 100644 index c76ab98..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/ext_workspace/workspace.go +++ /dev/null @@ -1,1040 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/cmd/go-wayland-scanner -// XML file : ext-workspace-v1.xml -// -// ext_workspace_v1 Protocol Copyright: -// -// Copyright © 2019 Christopher Billington -// Copyright © 2020 Ilia Bozhinov -// Copyright © 2022 Victoria Brekenfeld -// -// Permission to use, copy, modify, distribute, and sell this -// software and its documentation for any purpose is hereby granted -// without fee, provided that the above copyright notice appear in -// all copies and that both that copyright notice and this permission -// notice appear in supporting documentation, and that the name of -// the copyright holders not be used in advertising or publicity -// pertaining to distribution of the software without specific, -// written prior permission. The copyright holders make no -// representations about the suitability of this software for any -// purpose. It is provided "as is" without express or implied -// warranty. -// -// THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS -// SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY -// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN -// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -// THIS SOFTWARE. - -package ext_workspace - -import ( - "reflect" - "unsafe" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -// registerServerProxy registers a proxy with a server-assigned ID. -// This is necessary because go-wayland-scanner doesn't properly handle new_id arguments in events. -// In requests (like DWL), the client creates the ID via NewXxx(ctx) which calls ctx.Register(). -// In events (like ext-workspace), the server creates the ID and sends it, requiring manual registration. -// The Context.objects map is private with no public API for server IDs, requiring reflection. -func registerServerProxy(ctx *client.Context, proxy client.Proxy, serverID uint32) { - defer func() { - if r := recover(); r != nil { - return - } - }() - - ctxVal := reflect.ValueOf(ctx) - if ctxVal.Kind() != reflect.Ptr || ctxVal.IsNil() { - return - } - - ctxElem := ctxVal.Elem() - objectsField := ctxElem.FieldByName("objects") - if !objectsField.IsValid() { - return - } - - objectsMapPtr := unsafe.Pointer(objectsField.UnsafeAddr()) - objectsMap := (*syncmap.Map[uint32, client.Proxy])(objectsMapPtr) - objectsMap.Store(serverID, proxy) -} - -// ExtWorkspaceManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ExtWorkspaceManagerV1InterfaceName = "ext_workspace_manager_v1" - -// ExtWorkspaceManagerV1 : list and control workspaces -// -// Workspaces, also called virtual desktops, are groups of surfaces. A -// compositor with a concept of workspaces may only show some such groups of -// surfaces (those of 'active' workspaces) at a time. 'Activating' a -// workspace is a request for the compositor to display that workspace's -// surfaces as normal, whereas the compositor may hide or otherwise -// de-emphasise surfaces that are associated only with 'inactive' workspaces. -// Workspaces are grouped by which sets of outputs they correspond to, and -// may contain surfaces only from those outputs. In this way, it is possible -// for each output to have its own set of workspaces, or for all outputs (or -// any other arbitrary grouping) to share workspaces. Compositors may -// optionally conceptually arrange each group of workspaces in an -// N-dimensional grid. -// -// The purpose of this protocol is to enable the creation of taskbars and -// docks by providing them with a list of workspaces and their properties, -// and allowing them to activate and deactivate workspaces. -// -// After a client binds the ext_workspace_manager_v1, each workspace will be -// sent via the workspace event. -type ExtWorkspaceManagerV1 struct { - client.BaseProxy - workspaceGroupHandler ExtWorkspaceManagerV1WorkspaceGroupHandlerFunc - workspaceHandler ExtWorkspaceManagerV1WorkspaceHandlerFunc - doneHandler ExtWorkspaceManagerV1DoneHandlerFunc - finishedHandler ExtWorkspaceManagerV1FinishedHandlerFunc -} - -// NewExtWorkspaceManagerV1 : list and control workspaces -// -// Workspaces, also called virtual desktops, are groups of surfaces. A -// compositor with a concept of workspaces may only show some such groups of -// surfaces (those of 'active' workspaces) at a time. 'Activating' a -// workspace is a request for the compositor to display that workspace's -// surfaces as normal, whereas the compositor may hide or otherwise -// de-emphasise surfaces that are associated only with 'inactive' workspaces. -// Workspaces are grouped by which sets of outputs they correspond to, and -// may contain surfaces only from those outputs. In this way, it is possible -// for each output to have its own set of workspaces, or for all outputs (or -// any other arbitrary grouping) to share workspaces. Compositors may -// optionally conceptually arrange each group of workspaces in an -// N-dimensional grid. -// -// The purpose of this protocol is to enable the creation of taskbars and -// docks by providing them with a list of workspaces and their properties, -// and allowing them to activate and deactivate workspaces. -// -// After a client binds the ext_workspace_manager_v1, each workspace will be -// sent via the workspace event. -func NewExtWorkspaceManagerV1(ctx *client.Context) *ExtWorkspaceManagerV1 { - extWorkspaceManagerV1 := &ExtWorkspaceManagerV1{} - ctx.Register(extWorkspaceManagerV1) - return extWorkspaceManagerV1 -} - -// Commit : all requests about the workspaces have been sent -// -// The client must send this request after it has finished sending other -// requests. The compositor must process a series of requests preceding a -// commit request atomically. -// -// This allows changes to the workspace properties to be seen as atomic, -// even if they happen via multiple events, and even if they involve -// multiple ext_workspace_handle_v1 objects, for example, deactivating one -// workspace and activating another. -func (i *ExtWorkspaceManagerV1) Commit() error { - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Stop : stop sending events -// -// Indicates the client no longer wishes to receive events for new -// workspace groups. However the compositor may emit further workspace -// events, until the finished event is emitted. The compositor is expected -// to send the finished event eventually once the stop request has been processed. -// -// The client must not send any requests after this one, doing so will raise a wl_display -// invalid_object error. -func (i *ExtWorkspaceManagerV1) Stop() error { - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -func (i *ExtWorkspaceManagerV1) Destroy() error { - i.MarkZombie() - return nil -} - -// ExtWorkspaceManagerV1WorkspaceGroupEvent : a workspace group has been created -// -// This event is emitted whenever a new workspace group has been created. -// -// All initial details of the workspace group (outputs) will be -// sent immediately after this event via the corresponding events in -// ext_workspace_group_handle_v1 and ext_workspace_handle_v1. -type ExtWorkspaceManagerV1WorkspaceGroupEvent struct { - WorkspaceGroup *ExtWorkspaceGroupHandleV1 -} -type ExtWorkspaceManagerV1WorkspaceGroupHandlerFunc func(ExtWorkspaceManagerV1WorkspaceGroupEvent) - -// SetWorkspaceGroupHandler : sets handler for ExtWorkspaceManagerV1WorkspaceGroupEvent -func (i *ExtWorkspaceManagerV1) SetWorkspaceGroupHandler(f ExtWorkspaceManagerV1WorkspaceGroupHandlerFunc) { - i.workspaceGroupHandler = f -} - -// ExtWorkspaceManagerV1WorkspaceEvent : workspace has been created -// -// This event is emitted whenever a new workspace has been created. -// -// All initial details of the workspace (name, coordinates, state) will -// be sent immediately after this event via the corresponding events in -// ext_workspace_handle_v1. -// -// Workspaces start off unassigned to any workspace group. -type ExtWorkspaceManagerV1WorkspaceEvent struct { - Workspace *ExtWorkspaceHandleV1 -} -type ExtWorkspaceManagerV1WorkspaceHandlerFunc func(ExtWorkspaceManagerV1WorkspaceEvent) - -// SetWorkspaceHandler : sets handler for ExtWorkspaceManagerV1WorkspaceEvent -func (i *ExtWorkspaceManagerV1) SetWorkspaceHandler(f ExtWorkspaceManagerV1WorkspaceHandlerFunc) { - i.workspaceHandler = f -} - -// ExtWorkspaceManagerV1DoneEvent : all information about the workspaces and workspace groups has been sent -// -// This event is sent after all changes in all workspaces and workspace groups have been -// sent. -// -// This allows changes to one or more ext_workspace_group_handle_v1 -// properties and ext_workspace_handle_v1 properties -// to be seen as atomic, even if they happen via multiple events. -// In particular, an output moving from one workspace group to -// another sends an output_enter event and an output_leave event to the two -// ext_workspace_group_handle_v1 objects in question. The compositor sends -// the done event only after updating the output information in both -// workspace groups. -type ExtWorkspaceManagerV1DoneEvent struct{} -type ExtWorkspaceManagerV1DoneHandlerFunc func(ExtWorkspaceManagerV1DoneEvent) - -// SetDoneHandler : sets handler for ExtWorkspaceManagerV1DoneEvent -func (i *ExtWorkspaceManagerV1) SetDoneHandler(f ExtWorkspaceManagerV1DoneHandlerFunc) { - i.doneHandler = f -} - -// ExtWorkspaceManagerV1FinishedEvent : the compositor has finished with the workspace_manager -// -// This event indicates that the compositor is done sending events to the -// ext_workspace_manager_v1. The server will destroy the object -// immediately after sending this request. -type ExtWorkspaceManagerV1FinishedEvent struct{} -type ExtWorkspaceManagerV1FinishedHandlerFunc func(ExtWorkspaceManagerV1FinishedEvent) - -// SetFinishedHandler : sets handler for ExtWorkspaceManagerV1FinishedEvent -func (i *ExtWorkspaceManagerV1) SetFinishedHandler(f ExtWorkspaceManagerV1FinishedHandlerFunc) { - i.finishedHandler = f -} - -func (i *ExtWorkspaceManagerV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.workspaceGroupHandler == nil { - return - } - var e ExtWorkspaceManagerV1WorkspaceGroupEvent - l := 0 - objectID := client.Uint32(data[l : l+4]) - proxy := i.Context().GetProxy(objectID) - if proxy != nil && !proxy.IsZombie() { - e.WorkspaceGroup = proxy.(*ExtWorkspaceGroupHandleV1) - } else { - groupHandle := &ExtWorkspaceGroupHandleV1{} - groupHandle.SetContext(i.Context()) - groupHandle.SetID(objectID) - registerServerProxy(i.Context(), groupHandle, objectID) - e.WorkspaceGroup = groupHandle - } - l += 4 - - i.workspaceGroupHandler(e) - case 1: - if i.workspaceHandler == nil { - return - } - var e ExtWorkspaceManagerV1WorkspaceEvent - l := 0 - objectID := client.Uint32(data[l : l+4]) - proxy := i.Context().GetProxy(objectID) - if proxy != nil && !proxy.IsZombie() { - e.Workspace = proxy.(*ExtWorkspaceHandleV1) - } else { - wsHandle := &ExtWorkspaceHandleV1{} - wsHandle.SetContext(i.Context()) - wsHandle.SetID(objectID) - registerServerProxy(i.Context(), wsHandle, objectID) - e.Workspace = wsHandle - } - l += 4 - - i.workspaceHandler(e) - case 2: - if i.doneHandler == nil { - return - } - var e ExtWorkspaceManagerV1DoneEvent - - i.doneHandler(e) - case 3: - if i.finishedHandler == nil { - return - } - var e ExtWorkspaceManagerV1FinishedEvent - - i.finishedHandler(e) - } -} - -// ExtWorkspaceGroupHandleV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ExtWorkspaceGroupHandleV1InterfaceName = "ext_workspace_group_handle_v1" - -// ExtWorkspaceGroupHandleV1 : a workspace group assigned to a set of outputs -// -// A ext_workspace_group_handle_v1 object represents a workspace group -// that is assigned a set of outputs and contains a number of workspaces. -// -// The set of outputs assigned to the workspace group is conveyed to the client via -// output_enter and output_leave events, and its workspaces are conveyed with -// workspace events. -// -// For example, a compositor which has a set of workspaces for each output may -// advertise a workspace group (and its workspaces) per output, whereas a compositor -// where a workspace spans all outputs may advertise a single workspace group for all -// outputs. -type ExtWorkspaceGroupHandleV1 struct { - client.BaseProxy - capabilitiesHandler ExtWorkspaceGroupHandleV1CapabilitiesHandlerFunc - outputEnterHandler ExtWorkspaceGroupHandleV1OutputEnterHandlerFunc - outputLeaveHandler ExtWorkspaceGroupHandleV1OutputLeaveHandlerFunc - workspaceEnterHandler ExtWorkspaceGroupHandleV1WorkspaceEnterHandlerFunc - workspaceLeaveHandler ExtWorkspaceGroupHandleV1WorkspaceLeaveHandlerFunc - removedHandler ExtWorkspaceGroupHandleV1RemovedHandlerFunc -} - -// NewExtWorkspaceGroupHandleV1 : a workspace group assigned to a set of outputs -// -// A ext_workspace_group_handle_v1 object represents a workspace group -// that is assigned a set of outputs and contains a number of workspaces. -// -// The set of outputs assigned to the workspace group is conveyed to the client via -// output_enter and output_leave events, and its workspaces are conveyed with -// workspace events. -// -// For example, a compositor which has a set of workspaces for each output may -// advertise a workspace group (and its workspaces) per output, whereas a compositor -// where a workspace spans all outputs may advertise a single workspace group for all -// outputs. -func NewExtWorkspaceGroupHandleV1(ctx *client.Context) *ExtWorkspaceGroupHandleV1 { - extWorkspaceGroupHandleV1 := &ExtWorkspaceGroupHandleV1{} - ctx.Register(extWorkspaceGroupHandleV1) - return extWorkspaceGroupHandleV1 -} - -// CreateWorkspace : create a new workspace -// -// Request that the compositor create a new workspace with the given name -// and assign it to this group. -// -// There is no guarantee that the compositor will create a new workspace, -// or that the created workspace will have the provided name. -func (i *ExtWorkspaceGroupHandleV1) CreateWorkspace(workspace string) error { - const opcode = 0 - workspaceLen := client.PaddedLen(len(workspace) + 1) - _reqBufLen := 8 + (4 + workspaceLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutString(_reqBuf[l:l+(4+workspaceLen)], workspace) - l += (4 + workspaceLen) - err := i.Context().WriteMsg(_reqBuf, nil) - return err -} - -// Destroy : destroy the ext_workspace_group_handle_v1 object -// -// Destroys the ext_workspace_group_handle_v1 object. -// -// This request should be send either when the client does not want to -// use the workspace group object any more or after the removed event to finalize -// the destruction of the object. -func (i *ExtWorkspaceGroupHandleV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ExtWorkspaceGroupHandleV1GroupCapabilities uint32 - -// ExtWorkspaceGroupHandleV1GroupCapabilities : -const ( - // ExtWorkspaceGroupHandleV1GroupCapabilitiesCreateWorkspace : create_workspace request is available - ExtWorkspaceGroupHandleV1GroupCapabilitiesCreateWorkspace ExtWorkspaceGroupHandleV1GroupCapabilities = 1 -) - -func (e ExtWorkspaceGroupHandleV1GroupCapabilities) Name() string { - switch e { - case ExtWorkspaceGroupHandleV1GroupCapabilitiesCreateWorkspace: - return "create_workspace" - default: - return "" - } -} - -func (e ExtWorkspaceGroupHandleV1GroupCapabilities) Value() string { - switch e { - case ExtWorkspaceGroupHandleV1GroupCapabilitiesCreateWorkspace: - return "1" - default: - return "" - } -} - -func (e ExtWorkspaceGroupHandleV1GroupCapabilities) String() string { - return e.Name() + "=" + e.Value() -} - -// ExtWorkspaceGroupHandleV1CapabilitiesEvent : compositor capabilities -// -// This event advertises the capabilities supported by the compositor. If -// a capability isn't supported, clients should hide or disable the UI -// elements that expose this functionality. For instance, if the -// compositor doesn't advertise support for creating workspaces, a button -// triggering the create_workspace request should not be displayed. -// -// The compositor will ignore requests it doesn't support. For instance, -// a compositor which doesn't advertise support for creating workspaces will ignore -// create_workspace requests. -// -// Compositors must send this event once after creation of an -// ext_workspace_group_handle_v1. When the capabilities change, compositors -// must send this event again. -type ExtWorkspaceGroupHandleV1CapabilitiesEvent struct { - Capabilities uint32 -} -type ExtWorkspaceGroupHandleV1CapabilitiesHandlerFunc func(ExtWorkspaceGroupHandleV1CapabilitiesEvent) - -// SetCapabilitiesHandler : sets handler for ExtWorkspaceGroupHandleV1CapabilitiesEvent -func (i *ExtWorkspaceGroupHandleV1) SetCapabilitiesHandler(f ExtWorkspaceGroupHandleV1CapabilitiesHandlerFunc) { - i.capabilitiesHandler = f -} - -// ExtWorkspaceGroupHandleV1OutputEnterEvent : output assigned to workspace group -// -// This event is emitted whenever an output is assigned to the workspace -// group or a new `wl_output` object is bound by the client, which was already -// assigned to this workspace_group. -type ExtWorkspaceGroupHandleV1OutputEnterEvent struct { - Output *client.Output -} -type ExtWorkspaceGroupHandleV1OutputEnterHandlerFunc func(ExtWorkspaceGroupHandleV1OutputEnterEvent) - -// SetOutputEnterHandler : sets handler for ExtWorkspaceGroupHandleV1OutputEnterEvent -func (i *ExtWorkspaceGroupHandleV1) SetOutputEnterHandler(f ExtWorkspaceGroupHandleV1OutputEnterHandlerFunc) { - i.outputEnterHandler = f -} - -// ExtWorkspaceGroupHandleV1OutputLeaveEvent : output removed from workspace group -// -// This event is emitted whenever an output is removed from the workspace -// group. -type ExtWorkspaceGroupHandleV1OutputLeaveEvent struct { - Output *client.Output -} -type ExtWorkspaceGroupHandleV1OutputLeaveHandlerFunc func(ExtWorkspaceGroupHandleV1OutputLeaveEvent) - -// SetOutputLeaveHandler : sets handler for ExtWorkspaceGroupHandleV1OutputLeaveEvent -func (i *ExtWorkspaceGroupHandleV1) SetOutputLeaveHandler(f ExtWorkspaceGroupHandleV1OutputLeaveHandlerFunc) { - i.outputLeaveHandler = f -} - -// ExtWorkspaceGroupHandleV1WorkspaceEnterEvent : workspace added to workspace group -// -// This event is emitted whenever a workspace is assigned to this group. -// A workspace may only ever be assigned to a single group at a single point -// in time, but can be re-assigned during it's lifetime. -type ExtWorkspaceGroupHandleV1WorkspaceEnterEvent struct { - Workspace *ExtWorkspaceHandleV1 -} -type ExtWorkspaceGroupHandleV1WorkspaceEnterHandlerFunc func(ExtWorkspaceGroupHandleV1WorkspaceEnterEvent) - -// SetWorkspaceEnterHandler : sets handler for ExtWorkspaceGroupHandleV1WorkspaceEnterEvent -func (i *ExtWorkspaceGroupHandleV1) SetWorkspaceEnterHandler(f ExtWorkspaceGroupHandleV1WorkspaceEnterHandlerFunc) { - i.workspaceEnterHandler = f -} - -// ExtWorkspaceGroupHandleV1WorkspaceLeaveEvent : workspace removed from workspace group -// -// This event is emitted whenever a workspace is removed from this group. -type ExtWorkspaceGroupHandleV1WorkspaceLeaveEvent struct { - Workspace *ExtWorkspaceHandleV1 -} -type ExtWorkspaceGroupHandleV1WorkspaceLeaveHandlerFunc func(ExtWorkspaceGroupHandleV1WorkspaceLeaveEvent) - -// SetWorkspaceLeaveHandler : sets handler for ExtWorkspaceGroupHandleV1WorkspaceLeaveEvent -func (i *ExtWorkspaceGroupHandleV1) SetWorkspaceLeaveHandler(f ExtWorkspaceGroupHandleV1WorkspaceLeaveHandlerFunc) { - i.workspaceLeaveHandler = f -} - -// ExtWorkspaceGroupHandleV1RemovedEvent : this workspace group has been removed -// -// This event is send when the group associated with the ext_workspace_group_handle_v1 -// has been removed. After sending this request the compositor will immediately consider -// the object inert. Any requests will be ignored except the destroy request. -// It is guaranteed there won't be any more events referencing this -// ext_workspace_group_handle_v1. -// -// The compositor must remove all workspaces belonging to a workspace group -// via a workspace_leave event before removing the workspace group. -type ExtWorkspaceGroupHandleV1RemovedEvent struct{} -type ExtWorkspaceGroupHandleV1RemovedHandlerFunc func(ExtWorkspaceGroupHandleV1RemovedEvent) - -// SetRemovedHandler : sets handler for ExtWorkspaceGroupHandleV1RemovedEvent -func (i *ExtWorkspaceGroupHandleV1) SetRemovedHandler(f ExtWorkspaceGroupHandleV1RemovedHandlerFunc) { - i.removedHandler = f -} - -func (i *ExtWorkspaceGroupHandleV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.capabilitiesHandler == nil { - return - } - var e ExtWorkspaceGroupHandleV1CapabilitiesEvent - l := 0 - e.Capabilities = client.Uint32(data[l : l+4]) - l += 4 - - i.capabilitiesHandler(e) - case 1: - if i.outputEnterHandler == nil { - return - } - var e ExtWorkspaceGroupHandleV1OutputEnterEvent - l := 0 - e.Output = i.Context().GetProxy(client.Uint32(data[l : l+4])).(*client.Output) - l += 4 - - i.outputEnterHandler(e) - case 2: - if i.outputLeaveHandler == nil { - return - } - var e ExtWorkspaceGroupHandleV1OutputLeaveEvent - l := 0 - e.Output = i.Context().GetProxy(client.Uint32(data[l : l+4])).(*client.Output) - l += 4 - - i.outputLeaveHandler(e) - case 3: - if i.workspaceEnterHandler == nil { - return - } - var e ExtWorkspaceGroupHandleV1WorkspaceEnterEvent - l := 0 - e.Workspace = i.Context().GetProxy(client.Uint32(data[l : l+4])).(*ExtWorkspaceHandleV1) - l += 4 - - i.workspaceEnterHandler(e) - case 4: - if i.workspaceLeaveHandler == nil { - return - } - var e ExtWorkspaceGroupHandleV1WorkspaceLeaveEvent - l := 0 - e.Workspace = i.Context().GetProxy(client.Uint32(data[l : l+4])).(*ExtWorkspaceHandleV1) - l += 4 - - i.workspaceLeaveHandler(e) - case 5: - if i.removedHandler == nil { - return - } - var e ExtWorkspaceGroupHandleV1RemovedEvent - - i.removedHandler(e) - } -} - -// ExtWorkspaceHandleV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ExtWorkspaceHandleV1InterfaceName = "ext_workspace_handle_v1" - -// ExtWorkspaceHandleV1 : a workspace handing a group of surfaces -// -// A ext_workspace_handle_v1 object represents a workspace that handles a -// group of surfaces. -// -// Each workspace has: -// - a name, conveyed to the client with the name event -// - potentially an id conveyed with the id event -// - a list of states, conveyed to the client with the state event -// - and optionally a set of coordinates, conveyed to the client with the -// coordinates event -// -// The client may request that the compositor activate or deactivate the workspace. -// -// Each workspace can belong to only a single workspace group. -// Depending on the compositor policy, there might be workspaces with -// the same name in different workspace groups, but these workspaces are still -// separate (e.g. one of them might be active while the other is not). -type ExtWorkspaceHandleV1 struct { - client.BaseProxy - idHandler ExtWorkspaceHandleV1IdHandlerFunc - nameHandler ExtWorkspaceHandleV1NameHandlerFunc - coordinatesHandler ExtWorkspaceHandleV1CoordinatesHandlerFunc - stateHandler ExtWorkspaceHandleV1StateHandlerFunc - capabilitiesHandler ExtWorkspaceHandleV1CapabilitiesHandlerFunc - removedHandler ExtWorkspaceHandleV1RemovedHandlerFunc -} - -// NewExtWorkspaceHandleV1 : a workspace handing a group of surfaces -// -// A ext_workspace_handle_v1 object represents a workspace that handles a -// group of surfaces. -// -// Each workspace has: -// - a name, conveyed to the client with the name event -// - potentially an id conveyed with the id event -// - a list of states, conveyed to the client with the state event -// - and optionally a set of coordinates, conveyed to the client with the -// coordinates event -// -// The client may request that the compositor activate or deactivate the workspace. -// -// Each workspace can belong to only a single workspace group. -// Depending on the compositor policy, there might be workspaces with -// the same name in different workspace groups, but these workspaces are still -// separate (e.g. one of them might be active while the other is not). -func NewExtWorkspaceHandleV1(ctx *client.Context) *ExtWorkspaceHandleV1 { - extWorkspaceHandleV1 := &ExtWorkspaceHandleV1{} - ctx.Register(extWorkspaceHandleV1) - return extWorkspaceHandleV1 -} - -// Destroy : destroy the ext_workspace_handle_v1 object -// -// Destroys the ext_workspace_handle_v1 object. -// -// This request should be made either when the client does not want to -// use the workspace object any more or after the remove event to finalize -// the destruction of the object. -func (i *ExtWorkspaceHandleV1) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Activate : activate the workspace -// -// Request that this workspace be activated. -// -// There is no guarantee the workspace will be actually activated, and -// behaviour may be compositor-dependent. For example, activating a -// workspace may or may not deactivate all other workspaces in the same -// group. -func (i *ExtWorkspaceHandleV1) Activate() error { - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Deactivate : deactivate the workspace -// -// Request that this workspace be deactivated. -// -// There is no guarantee the workspace will be actually deactivated. -func (i *ExtWorkspaceHandleV1) Deactivate() error { - const opcode = 2 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Assign : assign workspace to group -// -// Requests that this workspace is assigned to the given workspace group. -// -// There is no guarantee the workspace will be assigned. -func (i *ExtWorkspaceHandleV1) Assign(workspaceGroup *ExtWorkspaceGroupHandleV1) error { - const opcode = 3 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], workspaceGroup.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Remove : remove the workspace -// -// Request that this workspace be removed. -// -// There is no guarantee the workspace will be actually removed. -func (i *ExtWorkspaceHandleV1) Remove() error { - const opcode = 4 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ExtWorkspaceHandleV1State uint32 - -// ExtWorkspaceHandleV1State : types of states on the workspace -// -// The different states that a workspace can have. -const ( - // ExtWorkspaceHandleV1StateActive : the workspace is active - ExtWorkspaceHandleV1StateActive ExtWorkspaceHandleV1State = 1 - // ExtWorkspaceHandleV1StateUrgent : the workspace requests attention - ExtWorkspaceHandleV1StateUrgent ExtWorkspaceHandleV1State = 2 - ExtWorkspaceHandleV1StateHidden ExtWorkspaceHandleV1State = 4 -) - -func (e ExtWorkspaceHandleV1State) Name() string { - switch e { - case ExtWorkspaceHandleV1StateActive: - return "active" - case ExtWorkspaceHandleV1StateUrgent: - return "urgent" - case ExtWorkspaceHandleV1StateHidden: - return "hidden" - default: - return "" - } -} - -func (e ExtWorkspaceHandleV1State) Value() string { - switch e { - case ExtWorkspaceHandleV1StateActive: - return "1" - case ExtWorkspaceHandleV1StateUrgent: - return "2" - case ExtWorkspaceHandleV1StateHidden: - return "4" - default: - return "" - } -} - -func (e ExtWorkspaceHandleV1State) String() string { - return e.Name() + "=" + e.Value() -} - -type ExtWorkspaceHandleV1WorkspaceCapabilities uint32 - -// ExtWorkspaceHandleV1WorkspaceCapabilities : -const ( - // ExtWorkspaceHandleV1WorkspaceCapabilitiesActivate : activate request is available - ExtWorkspaceHandleV1WorkspaceCapabilitiesActivate ExtWorkspaceHandleV1WorkspaceCapabilities = 1 - // ExtWorkspaceHandleV1WorkspaceCapabilitiesDeactivate : deactivate request is available - ExtWorkspaceHandleV1WorkspaceCapabilitiesDeactivate ExtWorkspaceHandleV1WorkspaceCapabilities = 2 - // ExtWorkspaceHandleV1WorkspaceCapabilitiesRemove : remove request is available - ExtWorkspaceHandleV1WorkspaceCapabilitiesRemove ExtWorkspaceHandleV1WorkspaceCapabilities = 4 - // ExtWorkspaceHandleV1WorkspaceCapabilitiesAssign : assign request is available - ExtWorkspaceHandleV1WorkspaceCapabilitiesAssign ExtWorkspaceHandleV1WorkspaceCapabilities = 8 -) - -func (e ExtWorkspaceHandleV1WorkspaceCapabilities) Name() string { - switch e { - case ExtWorkspaceHandleV1WorkspaceCapabilitiesActivate: - return "activate" - case ExtWorkspaceHandleV1WorkspaceCapabilitiesDeactivate: - return "deactivate" - case ExtWorkspaceHandleV1WorkspaceCapabilitiesRemove: - return "remove" - case ExtWorkspaceHandleV1WorkspaceCapabilitiesAssign: - return "assign" - default: - return "" - } -} - -func (e ExtWorkspaceHandleV1WorkspaceCapabilities) Value() string { - switch e { - case ExtWorkspaceHandleV1WorkspaceCapabilitiesActivate: - return "1" - case ExtWorkspaceHandleV1WorkspaceCapabilitiesDeactivate: - return "2" - case ExtWorkspaceHandleV1WorkspaceCapabilitiesRemove: - return "4" - case ExtWorkspaceHandleV1WorkspaceCapabilitiesAssign: - return "8" - default: - return "" - } -} - -func (e ExtWorkspaceHandleV1WorkspaceCapabilities) String() string { - return e.Name() + "=" + e.Value() -} - -// ExtWorkspaceHandleV1IdEvent : workspace id -// -// If this event is emitted, it will be send immediately after the -// ext_workspace_handle_v1 is created or when an id is assigned to -// a workspace (at most once during it's lifetime). -// -// An id will never change during the lifetime of the `ext_workspace_handle_v1` -// and is guaranteed to be unique during it's lifetime. -// -// Ids are not human-readable and shouldn't be displayed, use `name` for that purpose. -// -// Compositors are expected to only send ids for workspaces likely stable across multiple -// sessions and can be used by clients to store preferences for workspaces. Workspaces without -// ids should be considered temporary and any data associated with them should be deleted once -// the respective object is lost. -type ExtWorkspaceHandleV1IdEvent struct { - Id string -} -type ExtWorkspaceHandleV1IdHandlerFunc func(ExtWorkspaceHandleV1IdEvent) - -// SetIdHandler : sets handler for ExtWorkspaceHandleV1IdEvent -func (i *ExtWorkspaceHandleV1) SetIdHandler(f ExtWorkspaceHandleV1IdHandlerFunc) { - i.idHandler = f -} - -// ExtWorkspaceHandleV1NameEvent : workspace name changed -// -// This event is emitted immediately after the ext_workspace_handle_v1 is -// created and whenever the name of the workspace changes. -// -// A name is meant to be human-readable and can be displayed to a user. -// Unlike the id it is neither stable nor unique. -type ExtWorkspaceHandleV1NameEvent struct { - Name string -} -type ExtWorkspaceHandleV1NameHandlerFunc func(ExtWorkspaceHandleV1NameEvent) - -// SetNameHandler : sets handler for ExtWorkspaceHandleV1NameEvent -func (i *ExtWorkspaceHandleV1) SetNameHandler(f ExtWorkspaceHandleV1NameHandlerFunc) { - i.nameHandler = f -} - -// ExtWorkspaceHandleV1CoordinatesEvent : workspace coordinates changed -// -// This event is used to organize workspaces into an N-dimensional grid -// within a workspace group, and if supported, is emitted immediately after -// the ext_workspace_handle_v1 is created and whenever the coordinates of -// the workspace change. Compositors may not send this event if they do not -// conceptually arrange workspaces in this way. If compositors simply -// number workspaces, without any geometric interpretation, they may send -// 1D coordinates, which clients should not interpret as implying any -// geometry. Sending an empty array means that the compositor no longer -// orders the workspace geometrically. -// -// Coordinates have an arbitrary number of dimensions N with an uint32 -// position along each dimension. By convention if N > 1, the first -// dimension is X, the second Y, the third Z, and so on. The compositor may -// chose to utilize these events for a more novel workspace layout -// convention, however. No guarantee is made about the grid being filled or -// bounded; there may be a workspace at coordinate 1 and another at -// coordinate 1000 and none in between. Within a workspace group, however, -// workspaces must have unique coordinates of equal dimensionality. -type ExtWorkspaceHandleV1CoordinatesEvent struct { - Coordinates []byte -} -type ExtWorkspaceHandleV1CoordinatesHandlerFunc func(ExtWorkspaceHandleV1CoordinatesEvent) - -// SetCoordinatesHandler : sets handler for ExtWorkspaceHandleV1CoordinatesEvent -func (i *ExtWorkspaceHandleV1) SetCoordinatesHandler(f ExtWorkspaceHandleV1CoordinatesHandlerFunc) { - i.coordinatesHandler = f -} - -// ExtWorkspaceHandleV1StateEvent : the state of the workspace changed -// -// This event is emitted immediately after the ext_workspace_handle_v1 is -// created and each time the workspace state changes, either because of a -// compositor action or because of a request in this protocol. -// -// Missing states convey the opposite meaning, e.g. an unset active bit -// means the workspace is currently inactive. -type ExtWorkspaceHandleV1StateEvent struct { - State uint32 -} -type ExtWorkspaceHandleV1StateHandlerFunc func(ExtWorkspaceHandleV1StateEvent) - -// SetStateHandler : sets handler for ExtWorkspaceHandleV1StateEvent -func (i *ExtWorkspaceHandleV1) SetStateHandler(f ExtWorkspaceHandleV1StateHandlerFunc) { - i.stateHandler = f -} - -// ExtWorkspaceHandleV1CapabilitiesEvent : compositor capabilities -// -// This event advertises the capabilities supported by the compositor. If -// a capability isn't supported, clients should hide or disable the UI -// elements that expose this functionality. For instance, if the -// compositor doesn't advertise support for removing workspaces, a button -// triggering the remove request should not be displayed. -// -// The compositor will ignore requests it doesn't support. For instance, -// a compositor which doesn't advertise support for remove will ignore -// remove requests. -// -// Compositors must send this event once after creation of an -// ext_workspace_handle_v1 . When the capabilities change, compositors -// must send this event again. -type ExtWorkspaceHandleV1CapabilitiesEvent struct { - Capabilities uint32 -} -type ExtWorkspaceHandleV1CapabilitiesHandlerFunc func(ExtWorkspaceHandleV1CapabilitiesEvent) - -// SetCapabilitiesHandler : sets handler for ExtWorkspaceHandleV1CapabilitiesEvent -func (i *ExtWorkspaceHandleV1) SetCapabilitiesHandler(f ExtWorkspaceHandleV1CapabilitiesHandlerFunc) { - i.capabilitiesHandler = f -} - -// ExtWorkspaceHandleV1RemovedEvent : this workspace has been removed -// -// This event is send when the workspace associated with the ext_workspace_handle_v1 -// has been removed. After sending this request, the compositor will immediately consider -// the object inert. Any requests will be ignored except the destroy request. -// -// It is guaranteed there won't be any more events referencing this -// ext_workspace_handle_v1. -// -// The compositor must only remove a workspaces not currently belonging to any -// workspace_group. -type ExtWorkspaceHandleV1RemovedEvent struct{} -type ExtWorkspaceHandleV1RemovedHandlerFunc func(ExtWorkspaceHandleV1RemovedEvent) - -// SetRemovedHandler : sets handler for ExtWorkspaceHandleV1RemovedEvent -func (i *ExtWorkspaceHandleV1) SetRemovedHandler(f ExtWorkspaceHandleV1RemovedHandlerFunc) { - i.removedHandler = f -} - -func (i *ExtWorkspaceHandleV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.idHandler == nil { - return - } - var e ExtWorkspaceHandleV1IdEvent - l := 0 - idLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Id = client.String(data[l : l+idLen]) - l += idLen - - i.idHandler(e) - case 1: - if i.nameHandler == nil { - return - } - var e ExtWorkspaceHandleV1NameEvent - l := 0 - nameLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Name = client.String(data[l : l+nameLen]) - l += nameLen - - i.nameHandler(e) - case 2: - if i.coordinatesHandler == nil { - return - } - var e ExtWorkspaceHandleV1CoordinatesEvent - l := 0 - coordinatesLen := int(client.Uint32(data[l : l+4])) - l += 4 - e.Coordinates = make([]byte, coordinatesLen) - copy(e.Coordinates, data[l:l+coordinatesLen]) - l += coordinatesLen - - i.coordinatesHandler(e) - case 3: - if i.stateHandler == nil { - return - } - var e ExtWorkspaceHandleV1StateEvent - l := 0 - e.State = client.Uint32(data[l : l+4]) - l += 4 - - i.stateHandler(e) - case 4: - if i.capabilitiesHandler == nil { - return - } - var e ExtWorkspaceHandleV1CapabilitiesEvent - l := 0 - e.Capabilities = client.Uint32(data[l : l+4]) - l += 4 - - i.capabilitiesHandler(e) - case 5: - if i.removedHandler == nil { - return - } - var e ExtWorkspaceHandleV1RemovedEvent - - i.removedHandler(e) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/keyboard_shortcuts_inhibit/keyboard_shortcuts_inhibit.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/keyboard_shortcuts_inhibit/keyboard_shortcuts_inhibit.go deleted file mode 100644 index 0a23bb2..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/keyboard_shortcuts_inhibit/keyboard_shortcuts_inhibit.go +++ /dev/null @@ -1,284 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml -// -// keyboard_shortcuts_inhibit_unstable_v1 Protocol Copyright: -// -// Copyright © 2017 Red Hat Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice (including the next -// paragraph) shall be included in all copies or substantial portions of the -// Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package keyboard_shortcuts_inhibit - -import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - -// ZwpKeyboardShortcutsInhibitManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwpKeyboardShortcutsInhibitManagerV1InterfaceName = "zwp_keyboard_shortcuts_inhibit_manager_v1" - -// ZwpKeyboardShortcutsInhibitManagerV1 : context object for keyboard grab_manager -// -// A global interface used for inhibiting the compositor keyboard shortcuts. -type ZwpKeyboardShortcutsInhibitManagerV1 struct { - client.BaseProxy -} - -// NewZwpKeyboardShortcutsInhibitManagerV1 : context object for keyboard grab_manager -// -// A global interface used for inhibiting the compositor keyboard shortcuts. -func NewZwpKeyboardShortcutsInhibitManagerV1(ctx *client.Context) *ZwpKeyboardShortcutsInhibitManagerV1 { - zwpKeyboardShortcutsInhibitManagerV1 := &ZwpKeyboardShortcutsInhibitManagerV1{} - ctx.Register(zwpKeyboardShortcutsInhibitManagerV1) - return zwpKeyboardShortcutsInhibitManagerV1 -} - -// Destroy : destroy the keyboard shortcuts inhibitor object -// -// Destroy the keyboard shortcuts inhibitor manager. -func (i *ZwpKeyboardShortcutsInhibitManagerV1) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// InhibitShortcuts : create a new keyboard shortcuts inhibitor object -// -// Create a new keyboard shortcuts inhibitor object associated with -// the given surface for the given seat. -// -// If shortcuts are already inhibited for the specified seat and surface, -// a protocol error "already_inhibited" is raised by the compositor. -// -// surface: the surface that inhibits the keyboard shortcuts behavior -// seat: the wl_seat for which keyboard shortcuts should be disabled -func (i *ZwpKeyboardShortcutsInhibitManagerV1) InhibitShortcuts(surface *client.Surface, seat *client.Seat) (*ZwpKeyboardShortcutsInhibitorV1, error) { - id := NewZwpKeyboardShortcutsInhibitorV1(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], surface.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], seat.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -type ZwpKeyboardShortcutsInhibitManagerV1Error uint32 - -// ZwpKeyboardShortcutsInhibitManagerV1Error : -const ( - // ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited : the shortcuts are already inhibited for this surface - ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited ZwpKeyboardShortcutsInhibitManagerV1Error = 0 -) - -func (e ZwpKeyboardShortcutsInhibitManagerV1Error) Name() string { - switch e { - case ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited: - return "already_inhibited" - default: - return "" - } -} - -func (e ZwpKeyboardShortcutsInhibitManagerV1Error) Value() string { - switch e { - case ZwpKeyboardShortcutsInhibitManagerV1ErrorAlreadyInhibited: - return "0" - default: - return "" - } -} - -func (e ZwpKeyboardShortcutsInhibitManagerV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwpKeyboardShortcutsInhibitorV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwpKeyboardShortcutsInhibitorV1InterfaceName = "zwp_keyboard_shortcuts_inhibitor_v1" - -// ZwpKeyboardShortcutsInhibitorV1 : context object for keyboard shortcuts inhibitor -// -// A keyboard shortcuts inhibitor instructs the compositor to ignore -// its own keyboard shortcuts when the associated surface has keyboard -// focus. As a result, when the surface has keyboard focus on the given -// seat, it will receive all key events originating from the specified -// seat, even those which would normally be caught by the compositor for -// its own shortcuts. -// -// The Wayland compositor is however under no obligation to disable -// all of its shortcuts, and may keep some special key combo for its own -// use, including but not limited to one allowing the user to forcibly -// restore normal keyboard events routing in the case of an unwilling -// client. The compositor may also use the same key combo to reactivate -// an existing shortcut inhibitor that was previously deactivated on -// user request. -// -// When the compositor restores its own keyboard shortcuts, an -// "inactive" event is emitted to notify the client that the keyboard -// shortcuts inhibitor is not effectively active for the surface and -// seat any more, and the client should not expect to receive all -// keyboard events. -// -// When the keyboard shortcuts inhibitor is inactive, the client has -// no way to forcibly reactivate the keyboard shortcuts inhibitor. -// -// The user can chose to re-enable a previously deactivated keyboard -// shortcuts inhibitor using any mechanism the compositor may offer, -// in which case the compositor will send an "active" event to notify -// the client. -// -// If the surface is destroyed, unmapped, or loses the seat's keyboard -// focus, the keyboard shortcuts inhibitor becomes irrelevant and the -// compositor will restore its own keyboard shortcuts but no "inactive" -// event is emitted in this case. -type ZwpKeyboardShortcutsInhibitorV1 struct { - client.BaseProxy - activeHandler ZwpKeyboardShortcutsInhibitorV1ActiveHandlerFunc - inactiveHandler ZwpKeyboardShortcutsInhibitorV1InactiveHandlerFunc -} - -// NewZwpKeyboardShortcutsInhibitorV1 : context object for keyboard shortcuts inhibitor -// -// A keyboard shortcuts inhibitor instructs the compositor to ignore -// its own keyboard shortcuts when the associated surface has keyboard -// focus. As a result, when the surface has keyboard focus on the given -// seat, it will receive all key events originating from the specified -// seat, even those which would normally be caught by the compositor for -// its own shortcuts. -// -// The Wayland compositor is however under no obligation to disable -// all of its shortcuts, and may keep some special key combo for its own -// use, including but not limited to one allowing the user to forcibly -// restore normal keyboard events routing in the case of an unwilling -// client. The compositor may also use the same key combo to reactivate -// an existing shortcut inhibitor that was previously deactivated on -// user request. -// -// When the compositor restores its own keyboard shortcuts, an -// "inactive" event is emitted to notify the client that the keyboard -// shortcuts inhibitor is not effectively active for the surface and -// seat any more, and the client should not expect to receive all -// keyboard events. -// -// When the keyboard shortcuts inhibitor is inactive, the client has -// no way to forcibly reactivate the keyboard shortcuts inhibitor. -// -// The user can chose to re-enable a previously deactivated keyboard -// shortcuts inhibitor using any mechanism the compositor may offer, -// in which case the compositor will send an "active" event to notify -// the client. -// -// If the surface is destroyed, unmapped, or loses the seat's keyboard -// focus, the keyboard shortcuts inhibitor becomes irrelevant and the -// compositor will restore its own keyboard shortcuts but no "inactive" -// event is emitted in this case. -func NewZwpKeyboardShortcutsInhibitorV1(ctx *client.Context) *ZwpKeyboardShortcutsInhibitorV1 { - zwpKeyboardShortcutsInhibitorV1 := &ZwpKeyboardShortcutsInhibitorV1{} - ctx.Register(zwpKeyboardShortcutsInhibitorV1) - return zwpKeyboardShortcutsInhibitorV1 -} - -// Destroy : destroy the keyboard shortcuts inhibitor object -// -// Remove the keyboard shortcuts inhibitor from the associated wl_surface. -func (i *ZwpKeyboardShortcutsInhibitorV1) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ZwpKeyboardShortcutsInhibitorV1ActiveEvent : shortcuts are inhibited -// -// This event indicates that the shortcut inhibitor is active. -// -// The compositor sends this event every time compositor shortcuts -// are inhibited on behalf of the surface. When active, the client -// may receive input events normally reserved by the compositor -// (see zwp_keyboard_shortcuts_inhibitor_v1). -// -// This occurs typically when the initial request "inhibit_shortcuts" -// first becomes active or when the user instructs the compositor to -// re-enable and existing shortcuts inhibitor using any mechanism -// offered by the compositor. -type ZwpKeyboardShortcutsInhibitorV1ActiveEvent struct{} -type ZwpKeyboardShortcutsInhibitorV1ActiveHandlerFunc func(ZwpKeyboardShortcutsInhibitorV1ActiveEvent) - -// SetActiveHandler : sets handler for ZwpKeyboardShortcutsInhibitorV1ActiveEvent -func (i *ZwpKeyboardShortcutsInhibitorV1) SetActiveHandler(f ZwpKeyboardShortcutsInhibitorV1ActiveHandlerFunc) { - i.activeHandler = f -} - -// ZwpKeyboardShortcutsInhibitorV1InactiveEvent : shortcuts are restored -// -// This event indicates that the shortcuts inhibitor is inactive, -// normal shortcuts processing is restored by the compositor. -type ZwpKeyboardShortcutsInhibitorV1InactiveEvent struct{} -type ZwpKeyboardShortcutsInhibitorV1InactiveHandlerFunc func(ZwpKeyboardShortcutsInhibitorV1InactiveEvent) - -// SetInactiveHandler : sets handler for ZwpKeyboardShortcutsInhibitorV1InactiveEvent -func (i *ZwpKeyboardShortcutsInhibitorV1) SetInactiveHandler(f ZwpKeyboardShortcutsInhibitorV1InactiveHandlerFunc) { - i.inactiveHandler = f -} - -func (i *ZwpKeyboardShortcutsInhibitorV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.activeHandler == nil { - return - } - var e ZwpKeyboardShortcutsInhibitorV1ActiveEvent - - i.activeHandler(e) - case 1: - if i.inactiveHandler == nil { - return - } - var e ZwpKeyboardShortcutsInhibitorV1InactiveEvent - - i.inactiveHandler(e) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_gamma_control/gamma_control.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_gamma_control/gamma_control.go deleted file mode 100644 index a1cb429..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_gamma_control/gamma_control.go +++ /dev/null @@ -1,268 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/cmd/go-wayland-scanner -// XML file : wayland-protocols/wlr-gamma-control-unstable-v1.xml -// -// wlr_gamma_control_unstable_v1 Protocol Copyright: -// -// Copyright © 2015 Giulio camuffo -// Copyright © 2018 Simon Ser -// -// Permission to use, copy, modify, distribute, and sell this -// software and its documentation for any purpose is hereby granted -// without fee, provided that the above copyright notice appear in -// all copies and that both that copyright notice and this permission -// notice appear in supporting documentation, and that the name of -// the copyright holders not be used in advertising or publicity -// pertaining to distribution of the software without specific, -// written prior permission. The copyright holders make no -// representations about the suitability of this software for any -// purpose. It is provided "as is" without express or implied -// warranty. -// -// THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS -// SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY -// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN -// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -// THIS SOFTWARE. - -package wlr_gamma_control - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "golang.org/x/sys/unix" -) - -// ZwlrGammaControlManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrGammaControlManagerV1InterfaceName = "zwlr_gamma_control_manager_v1" - -// ZwlrGammaControlManagerV1 : manager to create per-output gamma controls -// -// This interface is a manager that allows creating per-output gamma -// controls. -type ZwlrGammaControlManagerV1 struct { - client.BaseProxy -} - -// NewZwlrGammaControlManagerV1 : manager to create per-output gamma controls -// -// This interface is a manager that allows creating per-output gamma -// controls. -func NewZwlrGammaControlManagerV1(ctx *client.Context) *ZwlrGammaControlManagerV1 { - zwlrGammaControlManagerV1 := &ZwlrGammaControlManagerV1{} - ctx.Register(zwlrGammaControlManagerV1) - return zwlrGammaControlManagerV1 -} - -// GetGammaControl : get a gamma control for an output -// -// Create a gamma control that can be used to adjust gamma tables for the -// provided output. -func (i *ZwlrGammaControlManagerV1) GetGammaControl(output *client.Output) (*ZwlrGammaControlV1, error) { - id := NewZwlrGammaControlV1(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// Destroy : destroy the manager -// -// All objects created by the manager will still remain valid, until their -// appropriate destroy request has been called. -func (i *ZwlrGammaControlManagerV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ZwlrGammaControlV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrGammaControlV1InterfaceName = "zwlr_gamma_control_v1" - -// ZwlrGammaControlV1 : adjust gamma tables for an output -// -// This interface allows a client to adjust gamma tables for a particular -// output. -// -// The client will receive the gamma size, and will then be able to set gamma -// tables. At any time the compositor can send a failed event indicating that -// this object is no longer valid. -// -// There can only be at most one gamma control object per output, which -// has exclusive access to this particular output. When the gamma control -// object is destroyed, the gamma table is restored to its original value. -type ZwlrGammaControlV1 struct { - client.BaseProxy - gammaSizeHandler ZwlrGammaControlV1GammaSizeHandlerFunc - failedHandler ZwlrGammaControlV1FailedHandlerFunc -} - -// NewZwlrGammaControlV1 : adjust gamma tables for an output -// -// This interface allows a client to adjust gamma tables for a particular -// output. -// -// The client will receive the gamma size, and will then be able to set gamma -// tables. At any time the compositor can send a failed event indicating that -// this object is no longer valid. -// -// There can only be at most one gamma control object per output, which -// has exclusive access to this particular output. When the gamma control -// object is destroyed, the gamma table is restored to its original value. -func NewZwlrGammaControlV1(ctx *client.Context) *ZwlrGammaControlV1 { - zwlrGammaControlV1 := &ZwlrGammaControlV1{} - ctx.Register(zwlrGammaControlV1) - return zwlrGammaControlV1 -} - -// SetGamma : set the gamma table -// -// Set the gamma table. The file descriptor can be memory-mapped to provide -// the raw gamma table, which contains successive gamma ramps for the red, -// green and blue channels. Each gamma ramp is an array of 16-byte unsigned -// integers which has the same length as the gamma size. -// -// The file descriptor data must have the same length as three times the -// gamma size. -// -// fd: gamma table file descriptor -func (i *ZwlrGammaControlV1) SetGamma(fd int) error { - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - oob := unix.UnixRights(int(fd)) - err := i.Context().WriteMsg(_reqBuf[:], oob) - return err -} - -// Destroy : destroy this control -// -// Destroys the gamma control object. If the object is still valid, this -// restores the original gamma tables. -func (i *ZwlrGammaControlV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZwlrGammaControlV1Error uint32 - -// ZwlrGammaControlV1Error : -const ( - // ZwlrGammaControlV1ErrorInvalidGamma : invalid gamma tables - ZwlrGammaControlV1ErrorInvalidGamma ZwlrGammaControlV1Error = 1 -) - -func (e ZwlrGammaControlV1Error) Name() string { - switch e { - case ZwlrGammaControlV1ErrorInvalidGamma: - return "invalid_gamma" - default: - return "" - } -} - -func (e ZwlrGammaControlV1Error) Value() string { - switch e { - case ZwlrGammaControlV1ErrorInvalidGamma: - return "1" - default: - return "" - } -} - -func (e ZwlrGammaControlV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwlrGammaControlV1GammaSizeEvent : size of gamma ramps -// -// Advertise the size of each gamma ramp. -// -// This event is sent immediately when the gamma control object is created. -type ZwlrGammaControlV1GammaSizeEvent struct { - Size uint32 -} -type ZwlrGammaControlV1GammaSizeHandlerFunc func(ZwlrGammaControlV1GammaSizeEvent) - -// SetGammaSizeHandler : sets handler for ZwlrGammaControlV1GammaSizeEvent -func (i *ZwlrGammaControlV1) SetGammaSizeHandler(f ZwlrGammaControlV1GammaSizeHandlerFunc) { - i.gammaSizeHandler = f -} - -// ZwlrGammaControlV1FailedEvent : object no longer valid -// -// This event indicates that the gamma control is no longer valid. This -// can happen for a number of reasons, including: -// - The output doesn't support gamma tables -// - Setting the gamma tables failed -// - Another client already has exclusive gamma control for this output -// - The compositor has transferred gamma control to another client -// -// Upon receiving this event, the client should destroy this object. -type ZwlrGammaControlV1FailedEvent struct{} -type ZwlrGammaControlV1FailedHandlerFunc func(ZwlrGammaControlV1FailedEvent) - -// SetFailedHandler : sets handler for ZwlrGammaControlV1FailedEvent -func (i *ZwlrGammaControlV1) SetFailedHandler(f ZwlrGammaControlV1FailedHandlerFunc) { - i.failedHandler = f -} - -func (i *ZwlrGammaControlV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.gammaSizeHandler == nil { - return - } - var e ZwlrGammaControlV1GammaSizeEvent - l := 0 - e.Size = client.Uint32(data[l : l+4]) - l += 4 - - i.gammaSizeHandler(e) - case 1: - if i.failedHandler == nil { - return - } - var e ZwlrGammaControlV1FailedEvent - - i.failedHandler(e) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_layer_shell/layer_shell.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_layer_shell/layer_shell.go deleted file mode 100644 index 6fea165..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_layer_shell/layer_shell.go +++ /dev/null @@ -1,792 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : internal/proto/xml/wlr-layer-shell-unstable-v1.xml -// -// wlr_layer_shell_unstable_v1 Protocol Copyright: -// -// Copyright © 2017 Drew DeVault -// -// Permission to use, copy, modify, distribute, and sell this -// software and its documentation for any purpose is hereby granted -// without fee, provided that the above copyright notice appear in -// all copies and that both that copyright notice and this permission -// notice appear in supporting documentation, and that the name of -// the copyright holders not be used in advertising or publicity -// pertaining to distribution of the software without specific, -// written prior permission. The copyright holders make no -// representations about the suitability of this software for any -// purpose. It is provided "as is" without express or implied -// warranty. -// -// THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS -// SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY -// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN -// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -// THIS SOFTWARE. - -package wlr_layer_shell - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - xdg_shell "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell" -) - -// ZwlrLayerShellV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrLayerShellV1InterfaceName = "zwlr_layer_shell_v1" - -// ZwlrLayerShellV1 : create surfaces that are layers of the desktop -// -// Clients can use this interface to assign the surface_layer role to -// wl_surfaces. Such surfaces are assigned to a "layer" of the output and -// rendered with a defined z-depth respective to each other. They may also be -// anchored to the edges and corners of a screen and specify input handling -// semantics. This interface should be suitable for the implementation of -// many desktop shell components, and a broad number of other applications -// that interact with the desktop. -type ZwlrLayerShellV1 struct { - client.BaseProxy -} - -// NewZwlrLayerShellV1 : create surfaces that are layers of the desktop -// -// Clients can use this interface to assign the surface_layer role to -// wl_surfaces. Such surfaces are assigned to a "layer" of the output and -// rendered with a defined z-depth respective to each other. They may also be -// anchored to the edges and corners of a screen and specify input handling -// semantics. This interface should be suitable for the implementation of -// many desktop shell components, and a broad number of other applications -// that interact with the desktop. -func NewZwlrLayerShellV1(ctx *client.Context) *ZwlrLayerShellV1 { - zwlrLayerShellV1 := &ZwlrLayerShellV1{} - ctx.Register(zwlrLayerShellV1) - return zwlrLayerShellV1 -} - -// GetLayerSurface : create a layer_surface from a surface -// -// Create a layer surface for an existing surface. This assigns the role of -// layer_surface, or raises a protocol error if another role is already -// assigned. -// -// Creating a layer surface from a wl_surface which has a buffer attached -// or committed is a client error, and any attempts by a client to attach -// or manipulate a buffer prior to the first layer_surface.configure call -// must also be treated as errors. -// -// After creating a layer_surface object and setting it up, the client -// must perform an initial commit without any buffer attached. -// The compositor will reply with a layer_surface.configure event. -// The client must acknowledge it and is then allowed to attach a buffer -// to map the surface. -// -// You may pass NULL for output to allow the compositor to decide which -// output to use. Generally this will be the one that the user most -// recently interacted with. -// -// Clients can specify a namespace that defines the purpose of the layer -// surface. -// -// layer: layer to add this surface to -// namespace: namespace for the layer surface -func (i *ZwlrLayerShellV1) GetLayerSurface(surface *client.Surface, output *client.Output, layer uint32, namespace string) (*ZwlrLayerSurfaceV1, error) { - id := NewZwlrLayerSurfaceV1(i.Context()) - const opcode = 0 - namespaceLen := client.PaddedLen(len(namespace) + 1) - _reqBufLen := 8 + 4 + 4 + 4 + 4 + (4 + namespaceLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], surface.ID()) - l += 4 - if output == nil { - client.PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - client.PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - } - client.PutUint32(_reqBuf[l:l+4], uint32(layer)) - l += 4 - client.PutString(_reqBuf[l:l+(4+namespaceLen)], namespace) - l += (4 + namespaceLen) - err := i.Context().WriteMsg(_reqBuf, nil) - return id, err -} - -// Destroy : destroy the layer_shell object -// -// This request indicates that the client will not use the layer_shell -// object any more. Objects that have been created through this instance -// are not affected. -func (i *ZwlrLayerShellV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZwlrLayerShellV1Error uint32 - -// ZwlrLayerShellV1Error : -const ( - // ZwlrLayerShellV1ErrorRole : wl_surface has another role - ZwlrLayerShellV1ErrorRole ZwlrLayerShellV1Error = 0 - // ZwlrLayerShellV1ErrorInvalidLayer : layer value is invalid - ZwlrLayerShellV1ErrorInvalidLayer ZwlrLayerShellV1Error = 1 - // ZwlrLayerShellV1ErrorAlreadyConstructed : wl_surface has a buffer attached or committed - ZwlrLayerShellV1ErrorAlreadyConstructed ZwlrLayerShellV1Error = 2 -) - -func (e ZwlrLayerShellV1Error) Name() string { - switch e { - case ZwlrLayerShellV1ErrorRole: - return "role" - case ZwlrLayerShellV1ErrorInvalidLayer: - return "invalid_layer" - case ZwlrLayerShellV1ErrorAlreadyConstructed: - return "already_constructed" - default: - return "" - } -} - -func (e ZwlrLayerShellV1Error) Value() string { - switch e { - case ZwlrLayerShellV1ErrorRole: - return "0" - case ZwlrLayerShellV1ErrorInvalidLayer: - return "1" - case ZwlrLayerShellV1ErrorAlreadyConstructed: - return "2" - default: - return "" - } -} - -func (e ZwlrLayerShellV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -type ZwlrLayerShellV1Layer uint32 - -// ZwlrLayerShellV1Layer : available layers for surfaces -// -// These values indicate which layers a surface can be rendered in. They -// are ordered by z depth, bottom-most first. Traditional shell surfaces -// will typically be rendered between the bottom and top layers. -// Fullscreen shell surfaces are typically rendered at the top layer. -// Multiple surfaces can share a single layer, and ordering within a -// single layer is undefined. -const ( - ZwlrLayerShellV1LayerBackground ZwlrLayerShellV1Layer = 0 - ZwlrLayerShellV1LayerBottom ZwlrLayerShellV1Layer = 1 - ZwlrLayerShellV1LayerTop ZwlrLayerShellV1Layer = 2 - ZwlrLayerShellV1LayerOverlay ZwlrLayerShellV1Layer = 3 -) - -func (e ZwlrLayerShellV1Layer) Name() string { - switch e { - case ZwlrLayerShellV1LayerBackground: - return "background" - case ZwlrLayerShellV1LayerBottom: - return "bottom" - case ZwlrLayerShellV1LayerTop: - return "top" - case ZwlrLayerShellV1LayerOverlay: - return "overlay" - default: - return "" - } -} - -func (e ZwlrLayerShellV1Layer) Value() string { - switch e { - case ZwlrLayerShellV1LayerBackground: - return "0" - case ZwlrLayerShellV1LayerBottom: - return "1" - case ZwlrLayerShellV1LayerTop: - return "2" - case ZwlrLayerShellV1LayerOverlay: - return "3" - default: - return "" - } -} - -func (e ZwlrLayerShellV1Layer) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwlrLayerSurfaceV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrLayerSurfaceV1InterfaceName = "zwlr_layer_surface_v1" - -// ZwlrLayerSurfaceV1 : layer metadata interface -// -// An interface that may be implemented by a wl_surface, for surfaces that -// are designed to be rendered as a layer of a stacked desktop-like -// environment. -// -// Layer surface state (layer, size, anchor, exclusive zone, -// margin, interactivity) is double-buffered, and will be applied at the -// time wl_surface.commit of the corresponding wl_surface is called. -// -// Attaching a null buffer to a layer surface unmaps it. -// -// Unmapping a layer_surface means that the surface cannot be shown by the -// compositor until it is explicitly mapped again. The layer_surface -// returns to the state it had right after layer_shell.get_layer_surface. -// The client can re-map the surface by performing a commit without any -// buffer attached, waiting for a configure event and handling it as usual. -type ZwlrLayerSurfaceV1 struct { - client.BaseProxy - configureHandler ZwlrLayerSurfaceV1ConfigureHandlerFunc - closedHandler ZwlrLayerSurfaceV1ClosedHandlerFunc -} - -// NewZwlrLayerSurfaceV1 : layer metadata interface -// -// An interface that may be implemented by a wl_surface, for surfaces that -// are designed to be rendered as a layer of a stacked desktop-like -// environment. -// -// Layer surface state (layer, size, anchor, exclusive zone, -// margin, interactivity) is double-buffered, and will be applied at the -// time wl_surface.commit of the corresponding wl_surface is called. -// -// Attaching a null buffer to a layer surface unmaps it. -// -// Unmapping a layer_surface means that the surface cannot be shown by the -// compositor until it is explicitly mapped again. The layer_surface -// returns to the state it had right after layer_shell.get_layer_surface. -// The client can re-map the surface by performing a commit without any -// buffer attached, waiting for a configure event and handling it as usual. -func NewZwlrLayerSurfaceV1(ctx *client.Context) *ZwlrLayerSurfaceV1 { - zwlrLayerSurfaceV1 := &ZwlrLayerSurfaceV1{} - ctx.Register(zwlrLayerSurfaceV1) - return zwlrLayerSurfaceV1 -} - -// SetSize : sets the size of the surface -// -// Sets the size of the surface in surface-local coordinates. The -// compositor will display the surface centered with respect to its -// anchors. -// -// If you pass 0 for either value, the compositor will assign it and -// inform you of the assignment in the configure event. You must set your -// anchor to opposite edges in the dimensions you omit; not doing so is a -// protocol error. Both values are 0 by default. -// -// Size is double-buffered, see wl_surface.commit. -func (i *ZwlrLayerSurfaceV1) SetSize(width, height uint32) error { - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetAnchor : configures the anchor point of the surface -// -// Requests that the compositor anchor the surface to the specified edges -// and corners. If two orthogonal edges are specified (e.g. 'top' and -// 'left'), then the anchor point will be the intersection of the edges -// (e.g. the top left corner of the output); otherwise the anchor point -// will be centered on that edge, or in the center if none is specified. -// -// Anchor is double-buffered, see wl_surface.commit. -func (i *ZwlrLayerSurfaceV1) SetAnchor(anchor uint32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(anchor)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetExclusiveZone : configures the exclusive geometry of this surface -// -// Requests that the compositor avoids occluding an area with other -// surfaces. The compositor's use of this information is -// implementation-dependent - do not assume that this region will not -// actually be occluded. -// -// A positive value is only meaningful if the surface is anchored to one -// edge or an edge and both perpendicular edges. If the surface is not -// anchored, anchored to only two perpendicular edges (a corner), anchored -// to only two parallel edges or anchored to all edges, a positive value -// will be treated the same as zero. -// -// A positive zone is the distance from the edge in surface-local -// coordinates to consider exclusive. -// -// Surfaces that do not wish to have an exclusive zone may instead specify -// how they should interact with surfaces that do. If set to zero, the -// surface indicates that it would like to be moved to avoid occluding -// surfaces with a positive exclusive zone. If set to -1, the surface -// indicates that it would not like to be moved to accommodate for other -// surfaces, and the compositor should extend it all the way to the edges -// it is anchored to. -// -// For example, a panel might set its exclusive zone to 10, so that -// maximized shell surfaces are not shown on top of it. A notification -// might set its exclusive zone to 0, so that it is moved to avoid -// occluding the panel, but shell surfaces are shown underneath it. A -// wallpaper or lock screen might set their exclusive zone to -1, so that -// they stretch below or over the panel. -// -// The default value is 0. -// -// Exclusive zone is double-buffered, see wl_surface.commit. -func (i *ZwlrLayerSurfaceV1) SetExclusiveZone(zone int32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(zone)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetMargin : sets a margin from the anchor point -// -// Requests that the surface be placed some distance away from the anchor -// point on the output, in surface-local coordinates. Setting this value -// for edges you are not anchored to has no effect. -// -// The exclusive zone includes the margin. -// -// Margin is double-buffered, see wl_surface.commit. -func (i *ZwlrLayerSurfaceV1) SetMargin(top, right, bottom, left int32) error { - const opcode = 3 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(top)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(right)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(bottom)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(left)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetKeyboardInteractivity : requests keyboard events -// -// Set how keyboard events are delivered to this surface. By default, -// layer shell surfaces do not receive keyboard events; this request can -// be used to change this. -// -// This setting is inherited by child surfaces set by the get_popup -// request. -// -// Layer surfaces receive pointer, touch, and tablet events normally. If -// you do not want to receive them, set the input region on your surface -// to an empty region. -// -// Keyboard interactivity is double-buffered, see wl_surface.commit. -func (i *ZwlrLayerSurfaceV1) SetKeyboardInteractivity(keyboardInteractivity uint32) error { - const opcode = 4 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(keyboardInteractivity)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// GetPopup : assign this layer_surface as an xdg_popup parent -// -// This assigns an xdg_popup's parent to this layer_surface. This popup -// should have been created via xdg_surface::get_popup with the parent set -// to NULL, and this request must be invoked before committing the popup's -// initial state. -// -// See the documentation of xdg_popup for more details about what an -// xdg_popup is and how it is used. -func (i *ZwlrLayerSurfaceV1) GetPopup(popup *xdg_shell.Popup) error { - const opcode = 5 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], popup.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// AckConfigure : ack a configure event -// -// When a configure event is received, if a client commits the -// surface in response to the configure event, then the client -// must make an ack_configure request sometime before the commit -// request, passing along the serial of the configure event. -// -// If the client receives multiple configure events before it -// can respond to one, it only has to ack the last configure event. -// -// A client is not required to commit immediately after sending -// an ack_configure request - it may even ack_configure several times -// before its next surface commit. -// -// A client may send multiple ack_configure requests before committing, but -// only the last request sent before a commit indicates which configure -// event the client really is responding to. -// -// serial: the serial from the configure event -func (i *ZwlrLayerSurfaceV1) AckConfigure(serial uint32) error { - const opcode = 6 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Destroy : destroy the layer_surface -// -// This request destroys the layer surface. -func (i *ZwlrLayerSurfaceV1) Destroy() error { - defer i.MarkZombie() - const opcode = 7 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetLayer : change the layer of the surface -// -// Change the layer that the surface is rendered on. -// -// Layer is double-buffered, see wl_surface.commit. -// -// layer: layer to move this surface to -func (i *ZwlrLayerSurfaceV1) SetLayer(layer uint32) error { - const opcode = 8 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(layer)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetExclusiveEdge : set the edge the exclusive zone will be applied to -// -// Requests an edge for the exclusive zone to apply. The exclusive -// edge will be automatically deduced from anchor points when possible, -// but when the surface is anchored to a corner, it will be necessary -// to set it explicitly to disambiguate, as it is not possible to deduce -// which one of the two corner edges should be used. -// -// The edge must be one the surface is anchored to, otherwise the -// invalid_exclusive_edge protocol error will be raised. -func (i *ZwlrLayerSurfaceV1) SetExclusiveEdge(edge uint32) error { - const opcode = 9 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(edge)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZwlrLayerSurfaceV1KeyboardInteractivity uint32 - -// ZwlrLayerSurfaceV1KeyboardInteractivity : types of keyboard interaction possible for a layer shell surface -// -// Types of keyboard interaction possible for layer shell surfaces. The -// rationale for this is twofold: (1) some applications are not interested -// in keyboard events and not allowing them to be focused can improve the -// desktop experience; (2) some applications will want to take exclusive -// keyboard focus. -const ( - ZwlrLayerSurfaceV1KeyboardInteractivityNone ZwlrLayerSurfaceV1KeyboardInteractivity = 0 - ZwlrLayerSurfaceV1KeyboardInteractivityExclusive ZwlrLayerSurfaceV1KeyboardInteractivity = 1 - ZwlrLayerSurfaceV1KeyboardInteractivityOnDemand ZwlrLayerSurfaceV1KeyboardInteractivity = 2 -) - -func (e ZwlrLayerSurfaceV1KeyboardInteractivity) Name() string { - switch e { - case ZwlrLayerSurfaceV1KeyboardInteractivityNone: - return "none" - case ZwlrLayerSurfaceV1KeyboardInteractivityExclusive: - return "exclusive" - case ZwlrLayerSurfaceV1KeyboardInteractivityOnDemand: - return "on_demand" - default: - return "" - } -} - -func (e ZwlrLayerSurfaceV1KeyboardInteractivity) Value() string { - switch e { - case ZwlrLayerSurfaceV1KeyboardInteractivityNone: - return "0" - case ZwlrLayerSurfaceV1KeyboardInteractivityExclusive: - return "1" - case ZwlrLayerSurfaceV1KeyboardInteractivityOnDemand: - return "2" - default: - return "" - } -} - -func (e ZwlrLayerSurfaceV1KeyboardInteractivity) String() string { - return e.Name() + "=" + e.Value() -} - -type ZwlrLayerSurfaceV1Error uint32 - -// ZwlrLayerSurfaceV1Error : -const ( - // ZwlrLayerSurfaceV1ErrorInvalidSurfaceState : provided surface state is invalid - ZwlrLayerSurfaceV1ErrorInvalidSurfaceState ZwlrLayerSurfaceV1Error = 0 - // ZwlrLayerSurfaceV1ErrorInvalidSize : size is invalid - ZwlrLayerSurfaceV1ErrorInvalidSize ZwlrLayerSurfaceV1Error = 1 - // ZwlrLayerSurfaceV1ErrorInvalidAnchor : anchor bitfield is invalid - ZwlrLayerSurfaceV1ErrorInvalidAnchor ZwlrLayerSurfaceV1Error = 2 - // ZwlrLayerSurfaceV1ErrorInvalidKeyboardInteractivity : keyboard interactivity is invalid - ZwlrLayerSurfaceV1ErrorInvalidKeyboardInteractivity ZwlrLayerSurfaceV1Error = 3 - // ZwlrLayerSurfaceV1ErrorInvalidExclusiveEdge : exclusive edge is invalid given the surface anchors - ZwlrLayerSurfaceV1ErrorInvalidExclusiveEdge ZwlrLayerSurfaceV1Error = 4 -) - -func (e ZwlrLayerSurfaceV1Error) Name() string { - switch e { - case ZwlrLayerSurfaceV1ErrorInvalidSurfaceState: - return "invalid_surface_state" - case ZwlrLayerSurfaceV1ErrorInvalidSize: - return "invalid_size" - case ZwlrLayerSurfaceV1ErrorInvalidAnchor: - return "invalid_anchor" - case ZwlrLayerSurfaceV1ErrorInvalidKeyboardInteractivity: - return "invalid_keyboard_interactivity" - case ZwlrLayerSurfaceV1ErrorInvalidExclusiveEdge: - return "invalid_exclusive_edge" - default: - return "" - } -} - -func (e ZwlrLayerSurfaceV1Error) Value() string { - switch e { - case ZwlrLayerSurfaceV1ErrorInvalidSurfaceState: - return "0" - case ZwlrLayerSurfaceV1ErrorInvalidSize: - return "1" - case ZwlrLayerSurfaceV1ErrorInvalidAnchor: - return "2" - case ZwlrLayerSurfaceV1ErrorInvalidKeyboardInteractivity: - return "3" - case ZwlrLayerSurfaceV1ErrorInvalidExclusiveEdge: - return "4" - default: - return "" - } -} - -func (e ZwlrLayerSurfaceV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -type ZwlrLayerSurfaceV1Anchor uint32 - -// ZwlrLayerSurfaceV1Anchor : -const ( - // ZwlrLayerSurfaceV1AnchorTop : the top edge of the anchor rectangle - ZwlrLayerSurfaceV1AnchorTop ZwlrLayerSurfaceV1Anchor = 1 - // ZwlrLayerSurfaceV1AnchorBottom : the bottom edge of the anchor rectangle - ZwlrLayerSurfaceV1AnchorBottom ZwlrLayerSurfaceV1Anchor = 2 - // ZwlrLayerSurfaceV1AnchorLeft : the left edge of the anchor rectangle - ZwlrLayerSurfaceV1AnchorLeft ZwlrLayerSurfaceV1Anchor = 4 - // ZwlrLayerSurfaceV1AnchorRight : the right edge of the anchor rectangle - ZwlrLayerSurfaceV1AnchorRight ZwlrLayerSurfaceV1Anchor = 8 -) - -func (e ZwlrLayerSurfaceV1Anchor) Name() string { - switch e { - case ZwlrLayerSurfaceV1AnchorTop: - return "top" - case ZwlrLayerSurfaceV1AnchorBottom: - return "bottom" - case ZwlrLayerSurfaceV1AnchorLeft: - return "left" - case ZwlrLayerSurfaceV1AnchorRight: - return "right" - default: - return "" - } -} - -func (e ZwlrLayerSurfaceV1Anchor) Value() string { - switch e { - case ZwlrLayerSurfaceV1AnchorTop: - return "1" - case ZwlrLayerSurfaceV1AnchorBottom: - return "2" - case ZwlrLayerSurfaceV1AnchorLeft: - return "4" - case ZwlrLayerSurfaceV1AnchorRight: - return "8" - default: - return "" - } -} - -func (e ZwlrLayerSurfaceV1Anchor) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwlrLayerSurfaceV1ConfigureEvent : suggest a surface change -// -// The configure event asks the client to resize its surface. -// -// Clients should arrange their surface for the new states, and then send -// an ack_configure request with the serial sent in this configure event at -// some point before committing the new surface. -// -// The client is free to dismiss all but the last configure event it -// received. -// -// The width and height arguments specify the size of the window in -// surface-local coordinates. -// -// The size is a hint, in the sense that the client is free to ignore it if -// it doesn't resize, pick a smaller size (to satisfy aspect ratio or -// resize in steps of NxM pixels). If the client picks a smaller size and -// is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the -// surface will be centered on this axis. -// -// If the width or height arguments are zero, it means the client should -// decide its own window dimension. -type ZwlrLayerSurfaceV1ConfigureEvent struct { - Serial uint32 - Width uint32 - Height uint32 -} -type ZwlrLayerSurfaceV1ConfigureHandlerFunc func(ZwlrLayerSurfaceV1ConfigureEvent) - -// SetConfigureHandler : sets handler for ZwlrLayerSurfaceV1ConfigureEvent -func (i *ZwlrLayerSurfaceV1) SetConfigureHandler(f ZwlrLayerSurfaceV1ConfigureHandlerFunc) { - i.configureHandler = f -} - -// ZwlrLayerSurfaceV1ClosedEvent : surface should be closed -// -// The closed event is sent by the compositor when the surface will no -// longer be shown. The output may have been destroyed or the user may -// have asked for it to be removed. Further changes to the surface will be -// ignored. The client should destroy the resource after receiving this -// event, and create a new surface if they so choose. -type ZwlrLayerSurfaceV1ClosedEvent struct{} -type ZwlrLayerSurfaceV1ClosedHandlerFunc func(ZwlrLayerSurfaceV1ClosedEvent) - -// SetClosedHandler : sets handler for ZwlrLayerSurfaceV1ClosedEvent -func (i *ZwlrLayerSurfaceV1) SetClosedHandler(f ZwlrLayerSurfaceV1ClosedHandlerFunc) { - i.closedHandler = f -} - -func (i *ZwlrLayerSurfaceV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.configureHandler == nil { - return - } - var e ZwlrLayerSurfaceV1ConfigureEvent - l := 0 - e.Serial = client.Uint32(data[l : l+4]) - l += 4 - e.Width = client.Uint32(data[l : l+4]) - l += 4 - e.Height = client.Uint32(data[l : l+4]) - l += 4 - - i.configureHandler(e) - case 1: - if i.closedHandler == nil { - return - } - var e ZwlrLayerSurfaceV1ClosedEvent - - i.closedHandler(e) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_output_management/output_management.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_output_management/output_management.go deleted file mode 100644 index 23f934f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_output_management/output_management.go +++ /dev/null @@ -1,1515 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/cmd/go-wayland-scanner -// XML file : /home/brandon/repos/dankdots/wlr-output-management-unstable-v1.xml -// -// wlr_output_management_unstable_v1 Protocol Copyright: -// -// Copyright © 2019 Purism SPC -// -// Permission to use, copy, modify, distribute, and sell this -// software and its documentation for any purpose is hereby granted -// without fee, provided that the above copyright notice appear in -// all copies and that both that copyright notice and this permission -// notice appear in supporting documentation, and that the name of -// the copyright holders not be used in advertising or publicity -// pertaining to distribution of the software without specific, -// written prior permission. The copyright holders make no -// representations about the suitability of this software for any -// purpose. It is provided "as is" without express or implied -// warranty. -// -// THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS -// SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY -// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN -// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -// THIS SOFTWARE. - -package wlr_output_management - -import ( - "reflect" - "unsafe" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -func registerServerProxy(ctx *client.Context, proxy client.Proxy, serverID uint32) { - defer func() { - if r := recover(); r != nil { - return - } - }() - ctxVal := reflect.ValueOf(ctx).Elem() - objectsField := ctxVal.FieldByName("objects") - if !objectsField.IsValid() { - return - } - objectsMapPtr := unsafe.Pointer(objectsField.UnsafeAddr()) - objectsMap := (*syncmap.Map[uint32, client.Proxy])(objectsMapPtr) - objectsMap.Store(serverID, proxy) -} - -// ZwlrOutputManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrOutputManagerV1InterfaceName = "zwlr_output_manager_v1" - -// ZwlrOutputManagerV1 : output device configuration manager -// -// This interface is a manager that allows reading and writing the current -// output device configuration. -// -// Output devices that display pixels (e.g. a physical monitor or a virtual -// output in a window) are represented as heads. Heads cannot be created nor -// destroyed by the client, but they can be enabled or disabled and their -// properties can be changed. Each head may have one or more available modes. -// -// Whenever a head appears (e.g. a monitor is plugged in), it will be -// advertised via the head event. Immediately after the output manager is -// bound, all current heads are advertised. -// -// Whenever a head's properties change, the relevant wlr_output_head events -// will be sent. Not all head properties will be sent: only properties that -// have changed need to. -// -// Whenever a head disappears (e.g. a monitor is unplugged), a -// wlr_output_head.finished event will be sent. -// -// After one or more heads appear, change or disappear, the done event will -// be sent. It carries a serial which can be used in a create_configuration -// request to update heads properties. -// -// The information obtained from this protocol should only be used for output -// configuration purposes. This protocol is not designed to be a generic -// output property advertisement protocol for regular clients. Instead, -// protocols such as xdg-output should be used. -type ZwlrOutputManagerV1 struct { - client.BaseProxy - headHandler ZwlrOutputManagerV1HeadHandlerFunc - doneHandler ZwlrOutputManagerV1DoneHandlerFunc - finishedHandler ZwlrOutputManagerV1FinishedHandlerFunc -} - -// NewZwlrOutputManagerV1 : output device configuration manager -// -// This interface is a manager that allows reading and writing the current -// output device configuration. -// -// Output devices that display pixels (e.g. a physical monitor or a virtual -// output in a window) are represented as heads. Heads cannot be created nor -// destroyed by the client, but they can be enabled or disabled and their -// properties can be changed. Each head may have one or more available modes. -// -// Whenever a head appears (e.g. a monitor is plugged in), it will be -// advertised via the head event. Immediately after the output manager is -// bound, all current heads are advertised. -// -// Whenever a head's properties change, the relevant wlr_output_head events -// will be sent. Not all head properties will be sent: only properties that -// have changed need to. -// -// Whenever a head disappears (e.g. a monitor is unplugged), a -// wlr_output_head.finished event will be sent. -// -// After one or more heads appear, change or disappear, the done event will -// be sent. It carries a serial which can be used in a create_configuration -// request to update heads properties. -// -// The information obtained from this protocol should only be used for output -// configuration purposes. This protocol is not designed to be a generic -// output property advertisement protocol for regular clients. Instead, -// protocols such as xdg-output should be used. -func NewZwlrOutputManagerV1(ctx *client.Context) *ZwlrOutputManagerV1 { - zwlrOutputManagerV1 := &ZwlrOutputManagerV1{} - ctx.Register(zwlrOutputManagerV1) - return zwlrOutputManagerV1 -} - -// CreateConfiguration : create a new output configuration object -// -// Create a new output configuration object. This allows to update head -// properties. -func (i *ZwlrOutputManagerV1) CreateConfiguration(serial uint32) (*ZwlrOutputConfigurationV1, error) { - id := NewZwlrOutputConfigurationV1(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// Stop : stop sending events -// -// Indicates the client no longer wishes to receive events for output -// configuration changes. However the compositor may emit further events, -// until the finished event is emitted. -// -// The client must not send any more requests after this one. -func (i *ZwlrOutputManagerV1) Stop() error { - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -func (i *ZwlrOutputManagerV1) Destroy() error { - i.MarkZombie() - return nil -} - -// ZwlrOutputManagerV1HeadEvent : introduce a new head -// -// This event introduces a new head. This happens whenever a new head -// appears (e.g. a monitor is plugged in) or after the output manager is -// bound. -type ZwlrOutputManagerV1HeadEvent struct { - Head *ZwlrOutputHeadV1 -} -type ZwlrOutputManagerV1HeadHandlerFunc func(ZwlrOutputManagerV1HeadEvent) - -// SetHeadHandler : sets handler for ZwlrOutputManagerV1HeadEvent -func (i *ZwlrOutputManagerV1) SetHeadHandler(f ZwlrOutputManagerV1HeadHandlerFunc) { - i.headHandler = f -} - -// ZwlrOutputManagerV1DoneEvent : sent all information about current configuration -// -// This event is sent after all information has been sent after binding to -// the output manager object and after any subsequent changes. This applies -// to child head and mode objects as well. In other words, this event is -// sent whenever a head or mode is created or destroyed and whenever one of -// their properties has been changed. Not all state is re-sent each time -// the current configuration changes: only the actual changes are sent. -// -// This allows changes to the output configuration to be seen as atomic, -// even if they happen via multiple events. -// -// A serial is sent to be used in a future create_configuration request. -type ZwlrOutputManagerV1DoneEvent struct { - Serial uint32 -} -type ZwlrOutputManagerV1DoneHandlerFunc func(ZwlrOutputManagerV1DoneEvent) - -// SetDoneHandler : sets handler for ZwlrOutputManagerV1DoneEvent -func (i *ZwlrOutputManagerV1) SetDoneHandler(f ZwlrOutputManagerV1DoneHandlerFunc) { - i.doneHandler = f -} - -// ZwlrOutputManagerV1FinishedEvent : the compositor has finished with the manager -// -// This event indicates that the compositor is done sending manager events. -// The compositor will destroy the object immediately after sending this -// event, so it will become invalid and the client should release any -// resources associated with it. -type ZwlrOutputManagerV1FinishedEvent struct{} -type ZwlrOutputManagerV1FinishedHandlerFunc func(ZwlrOutputManagerV1FinishedEvent) - -// SetFinishedHandler : sets handler for ZwlrOutputManagerV1FinishedEvent -func (i *ZwlrOutputManagerV1) SetFinishedHandler(f ZwlrOutputManagerV1FinishedHandlerFunc) { - i.finishedHandler = f -} - -func (i *ZwlrOutputManagerV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.headHandler == nil { - return - } - var e ZwlrOutputManagerV1HeadEvent - l := 0 - objectID := client.Uint32(data[l : l+4]) - proxy := i.Context().GetProxy(objectID) - if proxy == nil || proxy.IsZombie() { - head := &ZwlrOutputHeadV1{} - head.SetContext(i.Context()) - head.SetID(objectID) - registerServerProxy(i.Context(), head, objectID) - e.Head = head - } else if head, ok := proxy.(*ZwlrOutputHeadV1); ok { - e.Head = head - } else { - // Stale proxy of wrong type (can happen after suspend/resume) - // Replace it with the correct type - head := &ZwlrOutputHeadV1{} - head.SetContext(i.Context()) - head.SetID(objectID) - registerServerProxy(i.Context(), head, objectID) - e.Head = head - } - l += 4 - - i.headHandler(e) - case 1: - if i.doneHandler == nil { - return - } - var e ZwlrOutputManagerV1DoneEvent - l := 0 - e.Serial = client.Uint32(data[l : l+4]) - l += 4 - - i.doneHandler(e) - case 2: - if i.finishedHandler == nil { - return - } - var e ZwlrOutputManagerV1FinishedEvent - - i.finishedHandler(e) - } -} - -// ZwlrOutputHeadV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrOutputHeadV1InterfaceName = "zwlr_output_head_v1" - -// ZwlrOutputHeadV1 : output device -// -// A head is an output device. The difference between a wl_output object and -// a head is that heads are advertised even if they are turned off. A head -// object only advertises properties and cannot be used directly to change -// them. -// -// A head has some read-only properties: modes, name, description and -// physical_size. These cannot be changed by clients. -// -// Other properties can be updated via a wlr_output_configuration object. -// -// Properties sent via this interface are applied atomically via the -// wlr_output_manager.done event. No guarantees are made regarding the order -// in which properties are sent. -type ZwlrOutputHeadV1 struct { - client.BaseProxy - nameHandler ZwlrOutputHeadV1NameHandlerFunc - descriptionHandler ZwlrOutputHeadV1DescriptionHandlerFunc - physicalSizeHandler ZwlrOutputHeadV1PhysicalSizeHandlerFunc - modeHandler ZwlrOutputHeadV1ModeHandlerFunc - enabledHandler ZwlrOutputHeadV1EnabledHandlerFunc - currentModeHandler ZwlrOutputHeadV1CurrentModeHandlerFunc - positionHandler ZwlrOutputHeadV1PositionHandlerFunc - transformHandler ZwlrOutputHeadV1TransformHandlerFunc - scaleHandler ZwlrOutputHeadV1ScaleHandlerFunc - finishedHandler ZwlrOutputHeadV1FinishedHandlerFunc - makeHandler ZwlrOutputHeadV1MakeHandlerFunc - modelHandler ZwlrOutputHeadV1ModelHandlerFunc - serialNumberHandler ZwlrOutputHeadV1SerialNumberHandlerFunc - adaptiveSyncHandler ZwlrOutputHeadV1AdaptiveSyncHandlerFunc -} - -// NewZwlrOutputHeadV1 : output device -// -// A head is an output device. The difference between a wl_output object and -// a head is that heads are advertised even if they are turned off. A head -// object only advertises properties and cannot be used directly to change -// them. -// -// A head has some read-only properties: modes, name, description and -// physical_size. These cannot be changed by clients. -// -// Other properties can be updated via a wlr_output_configuration object. -// -// Properties sent via this interface are applied atomically via the -// wlr_output_manager.done event. No guarantees are made regarding the order -// in which properties are sent. -func NewZwlrOutputHeadV1(ctx *client.Context) *ZwlrOutputHeadV1 { - zwlrOutputHeadV1 := &ZwlrOutputHeadV1{} - ctx.Register(zwlrOutputHeadV1) - return zwlrOutputHeadV1 -} - -// Release : destroy the head object -// -// This request indicates that the client will no longer use this head -// object. -func (i *ZwlrOutputHeadV1) Release() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZwlrOutputHeadV1AdaptiveSyncState uint32 - -// ZwlrOutputHeadV1AdaptiveSyncState : -const ( - // ZwlrOutputHeadV1AdaptiveSyncStateDisabled : adaptive sync is disabled - ZwlrOutputHeadV1AdaptiveSyncStateDisabled ZwlrOutputHeadV1AdaptiveSyncState = 0 - // ZwlrOutputHeadV1AdaptiveSyncStateEnabled : adaptive sync is enabled - ZwlrOutputHeadV1AdaptiveSyncStateEnabled ZwlrOutputHeadV1AdaptiveSyncState = 1 -) - -func (e ZwlrOutputHeadV1AdaptiveSyncState) Name() string { - switch e { - case ZwlrOutputHeadV1AdaptiveSyncStateDisabled: - return "disabled" - case ZwlrOutputHeadV1AdaptiveSyncStateEnabled: - return "enabled" - default: - return "" - } -} - -func (e ZwlrOutputHeadV1AdaptiveSyncState) Value() string { - switch e { - case ZwlrOutputHeadV1AdaptiveSyncStateDisabled: - return "0" - case ZwlrOutputHeadV1AdaptiveSyncStateEnabled: - return "1" - default: - return "" - } -} - -func (e ZwlrOutputHeadV1AdaptiveSyncState) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwlrOutputHeadV1NameEvent : head name -// -// This event describes the head name. -// -// The naming convention is compositor defined, but limited to alphanumeric -// characters and dashes (-). Each name is unique among all wlr_output_head -// objects, but if a wlr_output_head object is destroyed the same name may -// be reused later. The names will also remain consistent across sessions -// with the same hardware and software configuration. -// -// Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do -// not assume that the name is a reflection of an underlying DRM -// connector, X11 connection, etc. -// -// If this head matches a wl_output, the wl_output.name event must report -// the same name. -// -// The name event is sent after a wlr_output_head object is created. This -// event is only sent once per object, and the name does not change over -// the lifetime of the wlr_output_head object. -type ZwlrOutputHeadV1NameEvent struct { - Name string -} -type ZwlrOutputHeadV1NameHandlerFunc func(ZwlrOutputHeadV1NameEvent) - -// SetNameHandler : sets handler for ZwlrOutputHeadV1NameEvent -func (i *ZwlrOutputHeadV1) SetNameHandler(f ZwlrOutputHeadV1NameHandlerFunc) { - i.nameHandler = f -} - -// ZwlrOutputHeadV1DescriptionEvent : head description -// -// This event describes a human-readable description of the head. -// -// The description is a UTF-8 string with no convention defined for its -// contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11 -// output via :1'. However, do not assume that the name is a reflection of -// the make, model, serial of the underlying DRM connector or the display -// name of the underlying X11 connection, etc. -// -// If this head matches a wl_output, the wl_output.description event must -// report the same name. -// -// The description event is sent after a wlr_output_head object is created. -// This event is only sent once per object, and the description does not -// change over the lifetime of the wlr_output_head object. -type ZwlrOutputHeadV1DescriptionEvent struct { - Description string -} -type ZwlrOutputHeadV1DescriptionHandlerFunc func(ZwlrOutputHeadV1DescriptionEvent) - -// SetDescriptionHandler : sets handler for ZwlrOutputHeadV1DescriptionEvent -func (i *ZwlrOutputHeadV1) SetDescriptionHandler(f ZwlrOutputHeadV1DescriptionHandlerFunc) { - i.descriptionHandler = f -} - -// ZwlrOutputHeadV1PhysicalSizeEvent : head physical size -// -// This event describes the physical size of the head. This event is only -// sent if the head has a physical size (e.g. is not a projector or a -// virtual device). -// -// The physical size event is sent after a wlr_output_head object is created. This -// event is only sent once per object, and the physical size does not change over -// the lifetime of the wlr_output_head object. -type ZwlrOutputHeadV1PhysicalSizeEvent struct { - Width int32 - Height int32 -} -type ZwlrOutputHeadV1PhysicalSizeHandlerFunc func(ZwlrOutputHeadV1PhysicalSizeEvent) - -// SetPhysicalSizeHandler : sets handler for ZwlrOutputHeadV1PhysicalSizeEvent -func (i *ZwlrOutputHeadV1) SetPhysicalSizeHandler(f ZwlrOutputHeadV1PhysicalSizeHandlerFunc) { - i.physicalSizeHandler = f -} - -// ZwlrOutputHeadV1ModeEvent : introduce a mode -// -// This event introduces a mode for this head. It is sent once per -// supported mode. -type ZwlrOutputHeadV1ModeEvent struct { - Mode *ZwlrOutputModeV1 -} -type ZwlrOutputHeadV1ModeHandlerFunc func(ZwlrOutputHeadV1ModeEvent) - -// SetModeHandler : sets handler for ZwlrOutputHeadV1ModeEvent -func (i *ZwlrOutputHeadV1) SetModeHandler(f ZwlrOutputHeadV1ModeHandlerFunc) { - i.modeHandler = f -} - -// ZwlrOutputHeadV1EnabledEvent : head is enabled or disabled -// -// This event describes whether the head is enabled. A disabled head is not -// mapped to a region of the global compositor space. -// -// When a head is disabled, some properties (current_mode, position, -// transform and scale) are irrelevant. -type ZwlrOutputHeadV1EnabledEvent struct { - Enabled int32 -} -type ZwlrOutputHeadV1EnabledHandlerFunc func(ZwlrOutputHeadV1EnabledEvent) - -// SetEnabledHandler : sets handler for ZwlrOutputHeadV1EnabledEvent -func (i *ZwlrOutputHeadV1) SetEnabledHandler(f ZwlrOutputHeadV1EnabledHandlerFunc) { - i.enabledHandler = f -} - -// ZwlrOutputHeadV1CurrentModeEvent : current mode -// -// This event describes the mode currently in use for this head. It is only -// sent if the output is enabled. -type ZwlrOutputHeadV1CurrentModeEvent struct { - Mode *ZwlrOutputModeV1 -} -type ZwlrOutputHeadV1CurrentModeHandlerFunc func(ZwlrOutputHeadV1CurrentModeEvent) - -// SetCurrentModeHandler : sets handler for ZwlrOutputHeadV1CurrentModeEvent -func (i *ZwlrOutputHeadV1) SetCurrentModeHandler(f ZwlrOutputHeadV1CurrentModeHandlerFunc) { - i.currentModeHandler = f -} - -// ZwlrOutputHeadV1PositionEvent : current position -// -// This events describes the position of the head in the global compositor -// space. It is only sent if the output is enabled. -type ZwlrOutputHeadV1PositionEvent struct { - X int32 - Y int32 -} -type ZwlrOutputHeadV1PositionHandlerFunc func(ZwlrOutputHeadV1PositionEvent) - -// SetPositionHandler : sets handler for ZwlrOutputHeadV1PositionEvent -func (i *ZwlrOutputHeadV1) SetPositionHandler(f ZwlrOutputHeadV1PositionHandlerFunc) { - i.positionHandler = f -} - -// ZwlrOutputHeadV1TransformEvent : current transformation -// -// This event describes the transformation currently applied to the head. -// It is only sent if the output is enabled. -type ZwlrOutputHeadV1TransformEvent struct { - Transform int32 -} -type ZwlrOutputHeadV1TransformHandlerFunc func(ZwlrOutputHeadV1TransformEvent) - -// SetTransformHandler : sets handler for ZwlrOutputHeadV1TransformEvent -func (i *ZwlrOutputHeadV1) SetTransformHandler(f ZwlrOutputHeadV1TransformHandlerFunc) { - i.transformHandler = f -} - -// ZwlrOutputHeadV1ScaleEvent : current scale -// -// This events describes the scale of the head in the global compositor -// space. It is only sent if the output is enabled. -type ZwlrOutputHeadV1ScaleEvent struct { - Scale float64 -} -type ZwlrOutputHeadV1ScaleHandlerFunc func(ZwlrOutputHeadV1ScaleEvent) - -// SetScaleHandler : sets handler for ZwlrOutputHeadV1ScaleEvent -func (i *ZwlrOutputHeadV1) SetScaleHandler(f ZwlrOutputHeadV1ScaleHandlerFunc) { - i.scaleHandler = f -} - -// ZwlrOutputHeadV1FinishedEvent : the head has disappeared -// -// This event indicates that the head is no longer available. The head -// object becomes inert. Clients should send a destroy request and release -// any resources associated with it. -type ZwlrOutputHeadV1FinishedEvent struct{} -type ZwlrOutputHeadV1FinishedHandlerFunc func(ZwlrOutputHeadV1FinishedEvent) - -// SetFinishedHandler : sets handler for ZwlrOutputHeadV1FinishedEvent -func (i *ZwlrOutputHeadV1) SetFinishedHandler(f ZwlrOutputHeadV1FinishedHandlerFunc) { - i.finishedHandler = f -} - -// ZwlrOutputHeadV1MakeEvent : head manufacturer -// -// This event describes the manufacturer of the head. -// -// Together with the model and serial_number events the purpose is to -// allow clients to recognize heads from previous sessions and for example -// load head-specific configurations back. -// -// It is not guaranteed this event will be ever sent. A reason for that -// can be that the compositor does not have information about the make of -// the head or the definition of a make is not sensible in the current -// setup, for example in a virtual session. Clients can still try to -// identify the head by available information from other events but should -// be aware that there is an increased risk of false positives. -// -// If sent, the make event is sent after a wlr_output_head object is -// created and only sent once per object. The make does not change over -// the lifetime of the wlr_output_head object. -// -// It is not recommended to display the make string in UI to users. For -// that the string provided by the description event should be preferred. -type ZwlrOutputHeadV1MakeEvent struct { - Make string -} -type ZwlrOutputHeadV1MakeHandlerFunc func(ZwlrOutputHeadV1MakeEvent) - -// SetMakeHandler : sets handler for ZwlrOutputHeadV1MakeEvent -func (i *ZwlrOutputHeadV1) SetMakeHandler(f ZwlrOutputHeadV1MakeHandlerFunc) { - i.makeHandler = f -} - -// ZwlrOutputHeadV1ModelEvent : head model -// -// This event describes the model of the head. -// -// Together with the make and serial_number events the purpose is to -// allow clients to recognize heads from previous sessions and for example -// load head-specific configurations back. -// -// It is not guaranteed this event will be ever sent. A reason for that -// can be that the compositor does not have information about the model of -// the head or the definition of a model is not sensible in the current -// setup, for example in a virtual session. Clients can still try to -// identify the head by available information from other events but should -// be aware that there is an increased risk of false positives. -// -// If sent, the model event is sent after a wlr_output_head object is -// created and only sent once per object. The model does not change over -// the lifetime of the wlr_output_head object. -// -// It is not recommended to display the model string in UI to users. For -// that the string provided by the description event should be preferred. -type ZwlrOutputHeadV1ModelEvent struct { - Model string -} -type ZwlrOutputHeadV1ModelHandlerFunc func(ZwlrOutputHeadV1ModelEvent) - -// SetModelHandler : sets handler for ZwlrOutputHeadV1ModelEvent -func (i *ZwlrOutputHeadV1) SetModelHandler(f ZwlrOutputHeadV1ModelHandlerFunc) { - i.modelHandler = f -} - -// ZwlrOutputHeadV1SerialNumberEvent : head serial number -// -// This event describes the serial number of the head. -// -// Together with the make and model events the purpose is to allow clients -// to recognize heads from previous sessions and for example load head- -// specific configurations back. -// -// It is not guaranteed this event will be ever sent. A reason for that -// can be that the compositor does not have information about the serial -// number of the head or the definition of a serial number is not sensible -// in the current setup. Clients can still try to identify the head by -// available information from other events but should be aware that there -// is an increased risk of false positives. -// -// If sent, the serial number event is sent after a wlr_output_head object -// is created and only sent once per object. The serial number does not -// change over the lifetime of the wlr_output_head object. -// -// It is not recommended to display the serial_number string in UI to -// users. For that the string provided by the description event should be -// preferred. -type ZwlrOutputHeadV1SerialNumberEvent struct { - SerialNumber string -} -type ZwlrOutputHeadV1SerialNumberHandlerFunc func(ZwlrOutputHeadV1SerialNumberEvent) - -// SetSerialNumberHandler : sets handler for ZwlrOutputHeadV1SerialNumberEvent -func (i *ZwlrOutputHeadV1) SetSerialNumberHandler(f ZwlrOutputHeadV1SerialNumberHandlerFunc) { - i.serialNumberHandler = f -} - -// ZwlrOutputHeadV1AdaptiveSyncEvent : current adaptive sync state -// -// This event describes whether adaptive sync is currently enabled for -// the head or not. Adaptive sync is also known as Variable Refresh -// Rate or VRR. -type ZwlrOutputHeadV1AdaptiveSyncEvent struct { - State uint32 -} -type ZwlrOutputHeadV1AdaptiveSyncHandlerFunc func(ZwlrOutputHeadV1AdaptiveSyncEvent) - -// SetAdaptiveSyncHandler : sets handler for ZwlrOutputHeadV1AdaptiveSyncEvent -func (i *ZwlrOutputHeadV1) SetAdaptiveSyncHandler(f ZwlrOutputHeadV1AdaptiveSyncHandlerFunc) { - i.adaptiveSyncHandler = f -} - -func (i *ZwlrOutputHeadV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.nameHandler == nil { - return - } - var e ZwlrOutputHeadV1NameEvent - l := 0 - nameLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Name = client.String(data[l : l+nameLen]) - l += nameLen - - i.nameHandler(e) - case 1: - if i.descriptionHandler == nil { - return - } - var e ZwlrOutputHeadV1DescriptionEvent - l := 0 - descriptionLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Description = client.String(data[l : l+descriptionLen]) - l += descriptionLen - - i.descriptionHandler(e) - case 2: - if i.physicalSizeHandler == nil { - return - } - var e ZwlrOutputHeadV1PhysicalSizeEvent - l := 0 - e.Width = int32(client.Uint32(data[l : l+4])) - l += 4 - e.Height = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.physicalSizeHandler(e) - case 3: - if i.modeHandler == nil { - return - } - var e ZwlrOutputHeadV1ModeEvent - l := 0 - objectID := client.Uint32(data[l : l+4]) - proxy := i.Context().GetProxy(objectID) - if proxy == nil || proxy.IsZombie() { - mode := &ZwlrOutputModeV1{} - mode.SetContext(i.Context()) - mode.SetID(objectID) - registerServerProxy(i.Context(), mode, objectID) - e.Mode = mode - } else if mode, ok := proxy.(*ZwlrOutputModeV1); ok { - e.Mode = mode - } else { - // Stale proxy of wrong type (can happen after suspend/resume) - // Replace it with the correct type - mode := &ZwlrOutputModeV1{} - mode.SetContext(i.Context()) - mode.SetID(objectID) - registerServerProxy(i.Context(), mode, objectID) - e.Mode = mode - } - l += 4 - - i.modeHandler(e) - case 4: - if i.enabledHandler == nil { - return - } - var e ZwlrOutputHeadV1EnabledEvent - l := 0 - e.Enabled = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.enabledHandler(e) - case 5: - if i.currentModeHandler == nil { - return - } - var e ZwlrOutputHeadV1CurrentModeEvent - l := 0 - objectID := client.Uint32(data[l : l+4]) - proxy := i.Context().GetProxy(objectID) - if proxy == nil || proxy.IsZombie() { - // Mode not yet registered or zombie, create fresh - mode := &ZwlrOutputModeV1{} - mode.SetContext(i.Context()) - mode.SetID(objectID) - registerServerProxy(i.Context(), mode, objectID) - e.Mode = mode - } else if mode, ok := proxy.(*ZwlrOutputModeV1); ok { - e.Mode = mode - } else { - // Stale proxy of wrong type (can happen after suspend/resume) - // Replace it with the correct type - mode := &ZwlrOutputModeV1{} - mode.SetContext(i.Context()) - mode.SetID(objectID) - registerServerProxy(i.Context(), mode, objectID) - e.Mode = mode - } - l += 4 - - i.currentModeHandler(e) - case 6: - if i.positionHandler == nil { - return - } - var e ZwlrOutputHeadV1PositionEvent - l := 0 - e.X = int32(client.Uint32(data[l : l+4])) - l += 4 - e.Y = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.positionHandler(e) - case 7: - if i.transformHandler == nil { - return - } - var e ZwlrOutputHeadV1TransformEvent - l := 0 - e.Transform = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.transformHandler(e) - case 8: - if i.scaleHandler == nil { - return - } - var e ZwlrOutputHeadV1ScaleEvent - l := 0 - e.Scale = client.Fixed(data[l : l+4]) - l += 4 - - i.scaleHandler(e) - case 9: - if i.finishedHandler == nil { - return - } - var e ZwlrOutputHeadV1FinishedEvent - - i.finishedHandler(e) - case 10: - if i.makeHandler == nil { - return - } - var e ZwlrOutputHeadV1MakeEvent - l := 0 - makeLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Make = client.String(data[l : l+makeLen]) - l += makeLen - - i.makeHandler(e) - case 11: - if i.modelHandler == nil { - return - } - var e ZwlrOutputHeadV1ModelEvent - l := 0 - modelLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.Model = client.String(data[l : l+modelLen]) - l += modelLen - - i.modelHandler(e) - case 12: - if i.serialNumberHandler == nil { - return - } - var e ZwlrOutputHeadV1SerialNumberEvent - l := 0 - serialNumberLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) - l += 4 - e.SerialNumber = client.String(data[l : l+serialNumberLen]) - l += serialNumberLen - - i.serialNumberHandler(e) - case 13: - if i.adaptiveSyncHandler == nil { - return - } - var e ZwlrOutputHeadV1AdaptiveSyncEvent - l := 0 - e.State = client.Uint32(data[l : l+4]) - l += 4 - - i.adaptiveSyncHandler(e) - } -} - -// ZwlrOutputModeV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrOutputModeV1InterfaceName = "zwlr_output_mode_v1" - -// ZwlrOutputModeV1 : output mode -// -// This object describes an output mode. -// -// Some heads don't support output modes, in which case modes won't be -// advertised. -// -// Properties sent via this interface are applied atomically via the -// wlr_output_manager.done event. No guarantees are made regarding the order -// in which properties are sent. -type ZwlrOutputModeV1 struct { - client.BaseProxy - sizeHandler ZwlrOutputModeV1SizeHandlerFunc - refreshHandler ZwlrOutputModeV1RefreshHandlerFunc - preferredHandler ZwlrOutputModeV1PreferredHandlerFunc - finishedHandler ZwlrOutputModeV1FinishedHandlerFunc -} - -// NewZwlrOutputModeV1 : output mode -// -// This object describes an output mode. -// -// Some heads don't support output modes, in which case modes won't be -// advertised. -// -// Properties sent via this interface are applied atomically via the -// wlr_output_manager.done event. No guarantees are made regarding the order -// in which properties are sent. -func NewZwlrOutputModeV1(ctx *client.Context) *ZwlrOutputModeV1 { - zwlrOutputModeV1 := &ZwlrOutputModeV1{} - ctx.Register(zwlrOutputModeV1) - return zwlrOutputModeV1 -} - -// Release : destroy the mode object -// -// This request indicates that the client will no longer use this mode -// object. -func (i *ZwlrOutputModeV1) Release() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ZwlrOutputModeV1SizeEvent : mode size -// -// This event describes the mode size. The size is given in physical -// hardware units of the output device. This is not necessarily the same as -// the output size in the global compositor space. For instance, the output -// may be scaled or transformed. -type ZwlrOutputModeV1SizeEvent struct { - Width int32 - Height int32 -} -type ZwlrOutputModeV1SizeHandlerFunc func(ZwlrOutputModeV1SizeEvent) - -// SetSizeHandler : sets handler for ZwlrOutputModeV1SizeEvent -func (i *ZwlrOutputModeV1) SetSizeHandler(f ZwlrOutputModeV1SizeHandlerFunc) { - i.sizeHandler = f -} - -// ZwlrOutputModeV1RefreshEvent : mode refresh rate -// -// This event describes the mode's fixed vertical refresh rate. It is only -// sent if the mode has a fixed refresh rate. -type ZwlrOutputModeV1RefreshEvent struct { - Refresh int32 -} -type ZwlrOutputModeV1RefreshHandlerFunc func(ZwlrOutputModeV1RefreshEvent) - -// SetRefreshHandler : sets handler for ZwlrOutputModeV1RefreshEvent -func (i *ZwlrOutputModeV1) SetRefreshHandler(f ZwlrOutputModeV1RefreshHandlerFunc) { - i.refreshHandler = f -} - -// ZwlrOutputModeV1PreferredEvent : mode is preferred -// -// This event advertises this mode as preferred. -type ZwlrOutputModeV1PreferredEvent struct{} -type ZwlrOutputModeV1PreferredHandlerFunc func(ZwlrOutputModeV1PreferredEvent) - -// SetPreferredHandler : sets handler for ZwlrOutputModeV1PreferredEvent -func (i *ZwlrOutputModeV1) SetPreferredHandler(f ZwlrOutputModeV1PreferredHandlerFunc) { - i.preferredHandler = f -} - -// ZwlrOutputModeV1FinishedEvent : the mode has disappeared -// -// This event indicates that the mode is no longer available. The mode -// object becomes inert. Clients should send a destroy request and release -// any resources associated with it. -type ZwlrOutputModeV1FinishedEvent struct{} -type ZwlrOutputModeV1FinishedHandlerFunc func(ZwlrOutputModeV1FinishedEvent) - -// SetFinishedHandler : sets handler for ZwlrOutputModeV1FinishedEvent -func (i *ZwlrOutputModeV1) SetFinishedHandler(f ZwlrOutputModeV1FinishedHandlerFunc) { - i.finishedHandler = f -} - -func (i *ZwlrOutputModeV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.sizeHandler == nil { - return - } - var e ZwlrOutputModeV1SizeEvent - l := 0 - e.Width = int32(client.Uint32(data[l : l+4])) - l += 4 - e.Height = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.sizeHandler(e) - case 1: - if i.refreshHandler == nil { - return - } - var e ZwlrOutputModeV1RefreshEvent - l := 0 - e.Refresh = int32(client.Uint32(data[l : l+4])) - l += 4 - - i.refreshHandler(e) - case 2: - if i.preferredHandler == nil { - return - } - var e ZwlrOutputModeV1PreferredEvent - - i.preferredHandler(e) - case 3: - if i.finishedHandler == nil { - return - } - var e ZwlrOutputModeV1FinishedEvent - - i.finishedHandler(e) - } -} - -// ZwlrOutputConfigurationV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrOutputConfigurationV1InterfaceName = "zwlr_output_configuration_v1" - -// ZwlrOutputConfigurationV1 : output configuration -// -// This object is used by the client to describe a full output configuration. -// -// First, the client needs to setup the output configuration. Each head can -// be either enabled (and configured) or disabled. It is a protocol error to -// send two enable_head or disable_head requests with the same head. It is a -// protocol error to omit a head in a configuration. -// -// Then, the client can apply or test the configuration. The compositor will -// then reply with a succeeded, failed or cancelled event. Finally the client -// should destroy the configuration object. -type ZwlrOutputConfigurationV1 struct { - client.BaseProxy - succeededHandler ZwlrOutputConfigurationV1SucceededHandlerFunc - failedHandler ZwlrOutputConfigurationV1FailedHandlerFunc - cancelledHandler ZwlrOutputConfigurationV1CancelledHandlerFunc -} - -// NewZwlrOutputConfigurationV1 : output configuration -// -// This object is used by the client to describe a full output configuration. -// -// First, the client needs to setup the output configuration. Each head can -// be either enabled (and configured) or disabled. It is a protocol error to -// send two enable_head or disable_head requests with the same head. It is a -// protocol error to omit a head in a configuration. -// -// Then, the client can apply or test the configuration. The compositor will -// then reply with a succeeded, failed or cancelled event. Finally the client -// should destroy the configuration object. -func NewZwlrOutputConfigurationV1(ctx *client.Context) *ZwlrOutputConfigurationV1 { - zwlrOutputConfigurationV1 := &ZwlrOutputConfigurationV1{} - ctx.Register(zwlrOutputConfigurationV1) - return zwlrOutputConfigurationV1 -} - -// EnableHead : enable and configure a head -// -// Enable a head. This request creates a head configuration object that can -// be used to change the head's properties. -// -// head: the head to be enabled -func (i *ZwlrOutputConfigurationV1) EnableHead(head *ZwlrOutputHeadV1) (*ZwlrOutputConfigurationHeadV1, error) { - id := NewZwlrOutputConfigurationHeadV1(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], head.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// DisableHead : disable a head -// -// Disable a head. -// -// head: the head to be disabled -func (i *ZwlrOutputConfigurationV1) DisableHead(head *ZwlrOutputHeadV1) error { - const opcode = 1 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], head.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Apply : apply the configuration -// -// Apply the new output configuration. -// -// In case the configuration is successfully applied, there is no guarantee -// that the new output state matches completely the requested -// configuration. For instance, a compositor might round the scale if it -// doesn't support fractional scaling. -// -// After this request has been sent, the compositor must respond with an -// succeeded, failed or cancelled event. Sending a request that isn't the -// destructor is a protocol error. -func (i *ZwlrOutputConfigurationV1) Apply() error { - const opcode = 2 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Test : test the configuration -// -// Test the new output configuration. The configuration won't be applied, -// but will only be validated. -// -// Even if the compositor succeeds to test a configuration, applying it may -// fail. -// -// After this request has been sent, the compositor must respond with an -// succeeded, failed or cancelled event. Sending a request that isn't the -// destructor is a protocol error. -func (i *ZwlrOutputConfigurationV1) Test() error { - const opcode = 3 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Destroy : destroy the output configuration -// -// Using this request a client can tell the compositor that it is not going -// to use the configuration object anymore. Any changes to the outputs -// that have not been applied will be discarded. -// -// This request also destroys wlr_output_configuration_head objects created -// via this object. -func (i *ZwlrOutputConfigurationV1) Destroy() error { - defer i.MarkZombie() - const opcode = 4 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZwlrOutputConfigurationV1Error uint32 - -// ZwlrOutputConfigurationV1Error : -const ( - // ZwlrOutputConfigurationV1ErrorAlreadyConfiguredHead : head has been configured twice - ZwlrOutputConfigurationV1ErrorAlreadyConfiguredHead ZwlrOutputConfigurationV1Error = 1 - // ZwlrOutputConfigurationV1ErrorUnconfiguredHead : head has not been configured - ZwlrOutputConfigurationV1ErrorUnconfiguredHead ZwlrOutputConfigurationV1Error = 2 - // ZwlrOutputConfigurationV1ErrorAlreadyUsed : request sent after configuration has been applied or tested - ZwlrOutputConfigurationV1ErrorAlreadyUsed ZwlrOutputConfigurationV1Error = 3 -) - -func (e ZwlrOutputConfigurationV1Error) Name() string { - switch e { - case ZwlrOutputConfigurationV1ErrorAlreadyConfiguredHead: - return "already_configured_head" - case ZwlrOutputConfigurationV1ErrorUnconfiguredHead: - return "unconfigured_head" - case ZwlrOutputConfigurationV1ErrorAlreadyUsed: - return "already_used" - default: - return "" - } -} - -func (e ZwlrOutputConfigurationV1Error) Value() string { - switch e { - case ZwlrOutputConfigurationV1ErrorAlreadyConfiguredHead: - return "1" - case ZwlrOutputConfigurationV1ErrorUnconfiguredHead: - return "2" - case ZwlrOutputConfigurationV1ErrorAlreadyUsed: - return "3" - default: - return "" - } -} - -func (e ZwlrOutputConfigurationV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwlrOutputConfigurationV1SucceededEvent : configuration changes succeeded -// -// Sent after the compositor has successfully applied the changes or -// tested them. -// -// Upon receiving this event, the client should destroy this object. -// -// If the current configuration has changed, events to describe the changes -// will be sent followed by a wlr_output_manager.done event. -type ZwlrOutputConfigurationV1SucceededEvent struct{} -type ZwlrOutputConfigurationV1SucceededHandlerFunc func(ZwlrOutputConfigurationV1SucceededEvent) - -// SetSucceededHandler : sets handler for ZwlrOutputConfigurationV1SucceededEvent -func (i *ZwlrOutputConfigurationV1) SetSucceededHandler(f ZwlrOutputConfigurationV1SucceededHandlerFunc) { - i.succeededHandler = f -} - -// ZwlrOutputConfigurationV1FailedEvent : configuration changes failed -// -// Sent if the compositor rejects the changes or failed to apply them. The -// compositor should revert any changes made by the apply request that -// triggered this event. -// -// Upon receiving this event, the client should destroy this object. -type ZwlrOutputConfigurationV1FailedEvent struct{} -type ZwlrOutputConfigurationV1FailedHandlerFunc func(ZwlrOutputConfigurationV1FailedEvent) - -// SetFailedHandler : sets handler for ZwlrOutputConfigurationV1FailedEvent -func (i *ZwlrOutputConfigurationV1) SetFailedHandler(f ZwlrOutputConfigurationV1FailedHandlerFunc) { - i.failedHandler = f -} - -// ZwlrOutputConfigurationV1CancelledEvent : configuration has been cancelled -// -// Sent if the compositor cancels the configuration because the state of an -// output changed and the client has outdated information (e.g. after an -// output has been hotplugged). -// -// The client can create a new configuration with a newer serial and try -// again. -// -// Upon receiving this event, the client should destroy this object. -type ZwlrOutputConfigurationV1CancelledEvent struct{} -type ZwlrOutputConfigurationV1CancelledHandlerFunc func(ZwlrOutputConfigurationV1CancelledEvent) - -// SetCancelledHandler : sets handler for ZwlrOutputConfigurationV1CancelledEvent -func (i *ZwlrOutputConfigurationV1) SetCancelledHandler(f ZwlrOutputConfigurationV1CancelledHandlerFunc) { - i.cancelledHandler = f -} - -func (i *ZwlrOutputConfigurationV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.succeededHandler == nil { - return - } - var e ZwlrOutputConfigurationV1SucceededEvent - - i.succeededHandler(e) - case 1: - if i.failedHandler == nil { - return - } - var e ZwlrOutputConfigurationV1FailedEvent - - i.failedHandler(e) - case 2: - if i.cancelledHandler == nil { - return - } - var e ZwlrOutputConfigurationV1CancelledEvent - - i.cancelledHandler(e) - } -} - -// ZwlrOutputConfigurationHeadV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrOutputConfigurationHeadV1InterfaceName = "zwlr_output_configuration_head_v1" - -// ZwlrOutputConfigurationHeadV1 : head configuration -// -// This object is used by the client to update a single head's configuration. -// -// It is a protocol error to set the same property twice. -type ZwlrOutputConfigurationHeadV1 struct { - client.BaseProxy -} - -// NewZwlrOutputConfigurationHeadV1 : head configuration -// -// This object is used by the client to update a single head's configuration. -// -// It is a protocol error to set the same property twice. -func NewZwlrOutputConfigurationHeadV1(ctx *client.Context) *ZwlrOutputConfigurationHeadV1 { - zwlrOutputConfigurationHeadV1 := &ZwlrOutputConfigurationHeadV1{} - ctx.Register(zwlrOutputConfigurationHeadV1) - return zwlrOutputConfigurationHeadV1 -} - -// SetMode : set the mode -// -// This request sets the head's mode. -func (i *ZwlrOutputConfigurationHeadV1) SetMode(mode *ZwlrOutputModeV1) error { - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], mode.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetCustomMode : set a custom mode -// -// This request assigns a custom mode to the head. The size is given in -// physical hardware units of the output device. If set to zero, the -// refresh rate is unspecified. -// -// It is a protocol error to set both a mode and a custom mode. -// -// width: width of the mode in hardware units -// height: height of the mode in hardware units -// refresh: vertical refresh rate in mHz or zero -func (i *ZwlrOutputConfigurationHeadV1) SetCustomMode(width, height, refresh int32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(refresh)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetPosition : set the position -// -// This request sets the head's position in the global compositor space. -// -// x: x position in the global compositor space -// y: y position in the global compositor space -func (i *ZwlrOutputConfigurationHeadV1) SetPosition(x, y int32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetTransform : set the transform -// -// This request sets the head's transform. -func (i *ZwlrOutputConfigurationHeadV1) SetTransform(transform int32) error { - const opcode = 3 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(transform)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetScale : set the scale -// -// This request sets the head's scale. -func (i *ZwlrOutputConfigurationHeadV1) SetScale(scale float64) error { - const opcode = 4 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutFixed(_reqBuf[l:l+4], scale) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetAdaptiveSync : enable/disable adaptive sync -// -// This request enables/disables adaptive sync. Adaptive sync is also -// known as Variable Refresh Rate or VRR. -func (i *ZwlrOutputConfigurationHeadV1) SetAdaptiveSync(state uint32) error { - const opcode = 5 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(state)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -func (i *ZwlrOutputConfigurationHeadV1) Destroy() error { - i.MarkZombie() - return nil -} - -type ZwlrOutputConfigurationHeadV1Error uint32 - -// ZwlrOutputConfigurationHeadV1Error : -const ( - // ZwlrOutputConfigurationHeadV1ErrorAlreadySet : property has already been set - ZwlrOutputConfigurationHeadV1ErrorAlreadySet ZwlrOutputConfigurationHeadV1Error = 1 - // ZwlrOutputConfigurationHeadV1ErrorInvalidMode : mode doesn't belong to head - ZwlrOutputConfigurationHeadV1ErrorInvalidMode ZwlrOutputConfigurationHeadV1Error = 2 - // ZwlrOutputConfigurationHeadV1ErrorInvalidCustomMode : mode is invalid - ZwlrOutputConfigurationHeadV1ErrorInvalidCustomMode ZwlrOutputConfigurationHeadV1Error = 3 - // ZwlrOutputConfigurationHeadV1ErrorInvalidTransform : transform value outside enum - ZwlrOutputConfigurationHeadV1ErrorInvalidTransform ZwlrOutputConfigurationHeadV1Error = 4 - // ZwlrOutputConfigurationHeadV1ErrorInvalidScale : scale negative or zero - ZwlrOutputConfigurationHeadV1ErrorInvalidScale ZwlrOutputConfigurationHeadV1Error = 5 - // ZwlrOutputConfigurationHeadV1ErrorInvalidAdaptiveSyncState : invalid enum value used in the set_adaptive_sync request - ZwlrOutputConfigurationHeadV1ErrorInvalidAdaptiveSyncState ZwlrOutputConfigurationHeadV1Error = 6 -) - -func (e ZwlrOutputConfigurationHeadV1Error) Name() string { - switch e { - case ZwlrOutputConfigurationHeadV1ErrorAlreadySet: - return "already_set" - case ZwlrOutputConfigurationHeadV1ErrorInvalidMode: - return "invalid_mode" - case ZwlrOutputConfigurationHeadV1ErrorInvalidCustomMode: - return "invalid_custom_mode" - case ZwlrOutputConfigurationHeadV1ErrorInvalidTransform: - return "invalid_transform" - case ZwlrOutputConfigurationHeadV1ErrorInvalidScale: - return "invalid_scale" - case ZwlrOutputConfigurationHeadV1ErrorInvalidAdaptiveSyncState: - return "invalid_adaptive_sync_state" - default: - return "" - } -} - -func (e ZwlrOutputConfigurationHeadV1Error) Value() string { - switch e { - case ZwlrOutputConfigurationHeadV1ErrorAlreadySet: - return "1" - case ZwlrOutputConfigurationHeadV1ErrorInvalidMode: - return "2" - case ZwlrOutputConfigurationHeadV1ErrorInvalidCustomMode: - return "3" - case ZwlrOutputConfigurationHeadV1ErrorInvalidTransform: - return "4" - case ZwlrOutputConfigurationHeadV1ErrorInvalidScale: - return "5" - case ZwlrOutputConfigurationHeadV1ErrorInvalidAdaptiveSyncState: - return "6" - default: - return "" - } -} - -func (e ZwlrOutputConfigurationHeadV1Error) String() string { - return e.Name() + "=" + e.Value() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_output_power/output_power.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_output_power/output_power.go deleted file mode 100644 index 8a10088..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_output_power/output_power.go +++ /dev/null @@ -1,283 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : internal/proto/xml/wlr-output-power-management-unstable-v1.xml -// -// wlr_output_power_management_unstable_v1 Protocol Copyright: -// -// Copyright © 2019 Purism SPC -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice (including the next -// paragraph) shall be included in all copies or substantial portions of the -// Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package wlr_output_power - -import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - -// ZwlrOutputPowerManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrOutputPowerManagerV1InterfaceName = "zwlr_output_power_manager_v1" - -// ZwlrOutputPowerManagerV1 : manager to create per-output power management -// -// This interface is a manager that allows creating per-output power -// management mode controls. -type ZwlrOutputPowerManagerV1 struct { - client.BaseProxy -} - -// NewZwlrOutputPowerManagerV1 : manager to create per-output power management -// -// This interface is a manager that allows creating per-output power -// management mode controls. -func NewZwlrOutputPowerManagerV1(ctx *client.Context) *ZwlrOutputPowerManagerV1 { - zwlrOutputPowerManagerV1 := &ZwlrOutputPowerManagerV1{} - ctx.Register(zwlrOutputPowerManagerV1) - return zwlrOutputPowerManagerV1 -} - -// GetOutputPower : get a power management for an output -// -// Create an output power management mode control that can be used to -// adjust the power management mode for a given output. -func (i *ZwlrOutputPowerManagerV1) GetOutputPower(output *client.Output) (*ZwlrOutputPowerV1, error) { - id := NewZwlrOutputPowerV1(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// Destroy : destroy the manager -// -// All objects created by the manager will still remain valid, until their -// appropriate destroy request has been called. -func (i *ZwlrOutputPowerManagerV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ZwlrOutputPowerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrOutputPowerV1InterfaceName = "zwlr_output_power_v1" - -// ZwlrOutputPowerV1 : adjust power management mode for an output -// -// This object offers requests to set the power management mode of -// an output. -type ZwlrOutputPowerV1 struct { - client.BaseProxy - modeHandler ZwlrOutputPowerV1ModeHandlerFunc - failedHandler ZwlrOutputPowerV1FailedHandlerFunc -} - -// NewZwlrOutputPowerV1 : adjust power management mode for an output -// -// This object offers requests to set the power management mode of -// an output. -func NewZwlrOutputPowerV1(ctx *client.Context) *ZwlrOutputPowerV1 { - zwlrOutputPowerV1 := &ZwlrOutputPowerV1{} - ctx.Register(zwlrOutputPowerV1) - return zwlrOutputPowerV1 -} - -// SetMode : Set an outputs power save mode -// -// Set an output's power save mode to the given mode. The mode change -// is effective immediately. If the output does not support the given -// mode a failed event is sent. -// -// mode: the power save mode to set -func (i *ZwlrOutputPowerV1) SetMode(mode uint32) error { - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(mode)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Destroy : destroy this power management -// -// Destroys the output power management mode control object. -func (i *ZwlrOutputPowerV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZwlrOutputPowerV1Mode uint32 - -// ZwlrOutputPowerV1Mode : -const ( - // ZwlrOutputPowerV1ModeOff : Output is turned off. - ZwlrOutputPowerV1ModeOff ZwlrOutputPowerV1Mode = 0 - // ZwlrOutputPowerV1ModeOn : Output is turned on, no power saving - ZwlrOutputPowerV1ModeOn ZwlrOutputPowerV1Mode = 1 -) - -func (e ZwlrOutputPowerV1Mode) Name() string { - switch e { - case ZwlrOutputPowerV1ModeOff: - return "off" - case ZwlrOutputPowerV1ModeOn: - return "on" - default: - return "" - } -} - -func (e ZwlrOutputPowerV1Mode) Value() string { - switch e { - case ZwlrOutputPowerV1ModeOff: - return "0" - case ZwlrOutputPowerV1ModeOn: - return "1" - default: - return "" - } -} - -func (e ZwlrOutputPowerV1Mode) String() string { - return e.Name() + "=" + e.Value() -} - -type ZwlrOutputPowerV1Error uint32 - -// ZwlrOutputPowerV1Error : -const ( - // ZwlrOutputPowerV1ErrorInvalidMode : nonexistent power save mode - ZwlrOutputPowerV1ErrorInvalidMode ZwlrOutputPowerV1Error = 1 -) - -func (e ZwlrOutputPowerV1Error) Name() string { - switch e { - case ZwlrOutputPowerV1ErrorInvalidMode: - return "invalid_mode" - default: - return "" - } -} - -func (e ZwlrOutputPowerV1Error) Value() string { - switch e { - case ZwlrOutputPowerV1ErrorInvalidMode: - return "1" - default: - return "" - } -} - -func (e ZwlrOutputPowerV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwlrOutputPowerV1ModeEvent : Report a power management mode change -// -// Report the power management mode change of an output. -// -// The mode event is sent after an output changed its power -// management mode. The reason can be a client using set_mode or the -// compositor deciding to change an output's mode. -// This event is also sent immediately when the object is created -// so the client is informed about the current power management mode. -type ZwlrOutputPowerV1ModeEvent struct { - Mode uint32 -} -type ZwlrOutputPowerV1ModeHandlerFunc func(ZwlrOutputPowerV1ModeEvent) - -// SetModeHandler : sets handler for ZwlrOutputPowerV1ModeEvent -func (i *ZwlrOutputPowerV1) SetModeHandler(f ZwlrOutputPowerV1ModeHandlerFunc) { - i.modeHandler = f -} - -// ZwlrOutputPowerV1FailedEvent : object no longer valid -// -// This event indicates that the output power management mode control -// is no longer valid. This can happen for a number of reasons, -// including: -// - The output doesn't support power management -// - Another client already has exclusive power management mode control -// for this output -// - The output disappeared -// -// Upon receiving this event, the client should destroy this object. -type ZwlrOutputPowerV1FailedEvent struct{} -type ZwlrOutputPowerV1FailedHandlerFunc func(ZwlrOutputPowerV1FailedEvent) - -// SetFailedHandler : sets handler for ZwlrOutputPowerV1FailedEvent -func (i *ZwlrOutputPowerV1) SetFailedHandler(f ZwlrOutputPowerV1FailedHandlerFunc) { - i.failedHandler = f -} - -func (i *ZwlrOutputPowerV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.modeHandler == nil { - return - } - var e ZwlrOutputPowerV1ModeEvent - l := 0 - e.Mode = client.Uint32(data[l : l+4]) - l += 4 - - i.modeHandler(e) - case 1: - if i.failedHandler == nil { - return - } - var e ZwlrOutputPowerV1FailedEvent - - i.failedHandler(e) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_screencopy/screencopy.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_screencopy/screencopy.go deleted file mode 100644 index edb9c5b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wlr_screencopy/screencopy.go +++ /dev/null @@ -1,532 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : internal/proto/xml/wlr-screencopy-unstable-v1.xml -// -// wlr_screencopy_unstable_v1 Protocol Copyright: -// -// Copyright © 2018 Simon Ser -// Copyright © 2019 Andri Yngvason -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice (including the next -// paragraph) shall be included in all copies or substantial portions of the -// Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package wlr_screencopy - -import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - -// ZwlrScreencopyManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrScreencopyManagerV1InterfaceName = "zwlr_screencopy_manager_v1" - -// ZwlrScreencopyManagerV1 : manager to inform clients and begin capturing -// -// This object is a manager which offers requests to start capturing from a -// source. -type ZwlrScreencopyManagerV1 struct { - client.BaseProxy -} - -// NewZwlrScreencopyManagerV1 : manager to inform clients and begin capturing -// -// This object is a manager which offers requests to start capturing from a -// source. -func NewZwlrScreencopyManagerV1(ctx *client.Context) *ZwlrScreencopyManagerV1 { - zwlrScreencopyManagerV1 := &ZwlrScreencopyManagerV1{} - ctx.Register(zwlrScreencopyManagerV1) - return zwlrScreencopyManagerV1 -} - -// CaptureOutput : capture an output -// -// Capture the next frame of an entire output. -// -// overlayCursor: composite cursor onto the frame -func (i *ZwlrScreencopyManagerV1) CaptureOutput(overlayCursor int32, output *client.Output) (*ZwlrScreencopyFrameV1, error) { - frame := NewZwlrScreencopyFrameV1(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], frame.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(overlayCursor)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return frame, err -} - -// CaptureOutputRegion : capture an output's region -// -// Capture the next frame of an output's region. -// -// The region is given in output logical coordinates, see -// xdg_output.logical_size. The region will be clipped to the output's -// extents. -// -// overlayCursor: composite cursor onto the frame -func (i *ZwlrScreencopyManagerV1) CaptureOutputRegion(overlayCursor int32, output *client.Output, x, y, width, height int32) (*ZwlrScreencopyFrameV1, error) { - frame := NewZwlrScreencopyFrameV1(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], frame.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(overlayCursor)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return frame, err -} - -// Destroy : destroy the manager -// -// All objects created by the manager will still remain valid, until their -// appropriate destroy request has been called. -func (i *ZwlrScreencopyManagerV1) Destroy() error { - defer i.MarkZombie() - const opcode = 2 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ZwlrScreencopyFrameV1InterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ZwlrScreencopyFrameV1InterfaceName = "zwlr_screencopy_frame_v1" - -// ZwlrScreencopyFrameV1 : a frame ready for copy -// -// This object represents a single frame. -// -// When created, a series of buffer events will be sent, each representing a -// supported buffer type. The "buffer_done" event is sent afterwards to -// indicate that all supported buffer types have been enumerated. The client -// will then be able to send a "copy" request. If the capture is successful, -// the compositor will send a "flags" event followed by a "ready" event. -// -// For objects version 2 or lower, wl_shm buffers are always supported, ie. -// the "buffer" event is guaranteed to be sent. -// -// If the capture failed, the "failed" event is sent. This can happen anytime -// before the "ready" event. -// -// Once either a "ready" or a "failed" event is received, the client should -// destroy the frame. -type ZwlrScreencopyFrameV1 struct { - client.BaseProxy - bufferHandler ZwlrScreencopyFrameV1BufferHandlerFunc - flagsHandler ZwlrScreencopyFrameV1FlagsHandlerFunc - readyHandler ZwlrScreencopyFrameV1ReadyHandlerFunc - failedHandler ZwlrScreencopyFrameV1FailedHandlerFunc - damageHandler ZwlrScreencopyFrameV1DamageHandlerFunc - linuxDmabufHandler ZwlrScreencopyFrameV1LinuxDmabufHandlerFunc - bufferDoneHandler ZwlrScreencopyFrameV1BufferDoneHandlerFunc -} - -// NewZwlrScreencopyFrameV1 : a frame ready for copy -// -// This object represents a single frame. -// -// When created, a series of buffer events will be sent, each representing a -// supported buffer type. The "buffer_done" event is sent afterwards to -// indicate that all supported buffer types have been enumerated. The client -// will then be able to send a "copy" request. If the capture is successful, -// the compositor will send a "flags" event followed by a "ready" event. -// -// For objects version 2 or lower, wl_shm buffers are always supported, ie. -// the "buffer" event is guaranteed to be sent. -// -// If the capture failed, the "failed" event is sent. This can happen anytime -// before the "ready" event. -// -// Once either a "ready" or a "failed" event is received, the client should -// destroy the frame. -func NewZwlrScreencopyFrameV1(ctx *client.Context) *ZwlrScreencopyFrameV1 { - zwlrScreencopyFrameV1 := &ZwlrScreencopyFrameV1{} - ctx.Register(zwlrScreencopyFrameV1) - return zwlrScreencopyFrameV1 -} - -// Copy : copy the frame -// -// Copy the frame to the supplied buffer. The buffer must have the -// correct size, see zwlr_screencopy_frame_v1.buffer and -// zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a -// supported format. -// -// If the frame is successfully copied, "flags" and "ready" events are -// sent. Otherwise, a "failed" event is sent. -func (i *ZwlrScreencopyFrameV1) Copy(buffer *client.Buffer) error { - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], buffer.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Destroy : delete this object, used or not -// -// Destroys the frame. This request can be sent at any time by the client. -func (i *ZwlrScreencopyFrameV1) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// CopyWithDamage : copy the frame when it's damaged -// -// Same as copy, except it waits until there is damage to copy. -func (i *ZwlrScreencopyFrameV1) CopyWithDamage(buffer *client.Buffer) error { - const opcode = 2 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], buffer.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ZwlrScreencopyFrameV1Error uint32 - -// ZwlrScreencopyFrameV1Error : -const ( - // ZwlrScreencopyFrameV1ErrorAlreadyUsed : the object has already been used to copy a wl_buffer - ZwlrScreencopyFrameV1ErrorAlreadyUsed ZwlrScreencopyFrameV1Error = 0 - // ZwlrScreencopyFrameV1ErrorInvalidBuffer : buffer attributes are invalid - ZwlrScreencopyFrameV1ErrorInvalidBuffer ZwlrScreencopyFrameV1Error = 1 -) - -func (e ZwlrScreencopyFrameV1Error) Name() string { - switch e { - case ZwlrScreencopyFrameV1ErrorAlreadyUsed: - return "already_used" - case ZwlrScreencopyFrameV1ErrorInvalidBuffer: - return "invalid_buffer" - default: - return "" - } -} - -func (e ZwlrScreencopyFrameV1Error) Value() string { - switch e { - case ZwlrScreencopyFrameV1ErrorAlreadyUsed: - return "0" - case ZwlrScreencopyFrameV1ErrorInvalidBuffer: - return "1" - default: - return "" - } -} - -func (e ZwlrScreencopyFrameV1Error) String() string { - return e.Name() + "=" + e.Value() -} - -type ZwlrScreencopyFrameV1Flags uint32 - -// ZwlrScreencopyFrameV1Flags : -const ( - // ZwlrScreencopyFrameV1FlagsYInvert : contents are y-inverted - ZwlrScreencopyFrameV1FlagsYInvert ZwlrScreencopyFrameV1Flags = 1 -) - -func (e ZwlrScreencopyFrameV1Flags) Name() string { - switch e { - case ZwlrScreencopyFrameV1FlagsYInvert: - return "y_invert" - default: - return "" - } -} - -func (e ZwlrScreencopyFrameV1Flags) Value() string { - switch e { - case ZwlrScreencopyFrameV1FlagsYInvert: - return "1" - default: - return "" - } -} - -func (e ZwlrScreencopyFrameV1Flags) String() string { - return e.Name() + "=" + e.Value() -} - -// ZwlrScreencopyFrameV1BufferEvent : wl_shm buffer information -// -// Provides information about wl_shm buffer parameters that need to be -// used for this frame. This event is sent once after the frame is created -// if wl_shm buffers are supported. -type ZwlrScreencopyFrameV1BufferEvent struct { - Format uint32 - Width uint32 - Height uint32 - Stride uint32 -} -type ZwlrScreencopyFrameV1BufferHandlerFunc func(ZwlrScreencopyFrameV1BufferEvent) - -// SetBufferHandler : sets handler for ZwlrScreencopyFrameV1BufferEvent -func (i *ZwlrScreencopyFrameV1) SetBufferHandler(f ZwlrScreencopyFrameV1BufferHandlerFunc) { - i.bufferHandler = f -} - -// ZwlrScreencopyFrameV1FlagsEvent : frame flags -// -// Provides flags about the frame. This event is sent once before the -// "ready" event. -type ZwlrScreencopyFrameV1FlagsEvent struct { - Flags uint32 -} -type ZwlrScreencopyFrameV1FlagsHandlerFunc func(ZwlrScreencopyFrameV1FlagsEvent) - -// SetFlagsHandler : sets handler for ZwlrScreencopyFrameV1FlagsEvent -func (i *ZwlrScreencopyFrameV1) SetFlagsHandler(f ZwlrScreencopyFrameV1FlagsHandlerFunc) { - i.flagsHandler = f -} - -// ZwlrScreencopyFrameV1ReadyEvent : indicates frame is available for reading -// -// Called as soon as the frame is copied, indicating it is available -// for reading. This event includes the time at which the presentation took place. -// -// The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, -// each component being an unsigned 32-bit value. Whole seconds are in -// tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, -// and the additional fractional part in tv_nsec as nanoseconds. Hence, -// for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part -// may have an arbitrary offset at start. -// -// After receiving this event, the client should destroy the object. -type ZwlrScreencopyFrameV1ReadyEvent struct { - TvSecHi uint32 - TvSecLo uint32 - TvNsec uint32 -} -type ZwlrScreencopyFrameV1ReadyHandlerFunc func(ZwlrScreencopyFrameV1ReadyEvent) - -// SetReadyHandler : sets handler for ZwlrScreencopyFrameV1ReadyEvent -func (i *ZwlrScreencopyFrameV1) SetReadyHandler(f ZwlrScreencopyFrameV1ReadyHandlerFunc) { - i.readyHandler = f -} - -// ZwlrScreencopyFrameV1FailedEvent : frame copy failed -// -// This event indicates that the attempted frame copy has failed. -// -// After receiving this event, the client should destroy the object. -type ZwlrScreencopyFrameV1FailedEvent struct{} -type ZwlrScreencopyFrameV1FailedHandlerFunc func(ZwlrScreencopyFrameV1FailedEvent) - -// SetFailedHandler : sets handler for ZwlrScreencopyFrameV1FailedEvent -func (i *ZwlrScreencopyFrameV1) SetFailedHandler(f ZwlrScreencopyFrameV1FailedHandlerFunc) { - i.failedHandler = f -} - -// ZwlrScreencopyFrameV1DamageEvent : carries the coordinates of the damaged region -// -// This event is sent right before the ready event when copy_with_damage is -// requested. It may be generated multiple times for each copy_with_damage -// request. -// -// The arguments describe a box around an area that has changed since the -// last copy request that was derived from the current screencopy manager -// instance. -// -// The union of all regions received between the call to copy_with_damage -// and a ready event is the total damage since the prior ready event. -type ZwlrScreencopyFrameV1DamageEvent struct { - X uint32 - Y uint32 - Width uint32 - Height uint32 -} -type ZwlrScreencopyFrameV1DamageHandlerFunc func(ZwlrScreencopyFrameV1DamageEvent) - -// SetDamageHandler : sets handler for ZwlrScreencopyFrameV1DamageEvent -func (i *ZwlrScreencopyFrameV1) SetDamageHandler(f ZwlrScreencopyFrameV1DamageHandlerFunc) { - i.damageHandler = f -} - -// ZwlrScreencopyFrameV1LinuxDmabufEvent : linux-dmabuf buffer information -// -// Provides information about linux-dmabuf buffer parameters that need to -// be used for this frame. This event is sent once after the frame is -// created if linux-dmabuf buffers are supported. -type ZwlrScreencopyFrameV1LinuxDmabufEvent struct { - Format uint32 - Width uint32 - Height uint32 -} -type ZwlrScreencopyFrameV1LinuxDmabufHandlerFunc func(ZwlrScreencopyFrameV1LinuxDmabufEvent) - -// SetLinuxDmabufHandler : sets handler for ZwlrScreencopyFrameV1LinuxDmabufEvent -func (i *ZwlrScreencopyFrameV1) SetLinuxDmabufHandler(f ZwlrScreencopyFrameV1LinuxDmabufHandlerFunc) { - i.linuxDmabufHandler = f -} - -// ZwlrScreencopyFrameV1BufferDoneEvent : all buffer types reported -// -// This event is sent once after all buffer events have been sent. -// -// The client should proceed to create a buffer of one of the supported -// types, and send a "copy" request. -type ZwlrScreencopyFrameV1BufferDoneEvent struct{} -type ZwlrScreencopyFrameV1BufferDoneHandlerFunc func(ZwlrScreencopyFrameV1BufferDoneEvent) - -// SetBufferDoneHandler : sets handler for ZwlrScreencopyFrameV1BufferDoneEvent -func (i *ZwlrScreencopyFrameV1) SetBufferDoneHandler(f ZwlrScreencopyFrameV1BufferDoneHandlerFunc) { - i.bufferDoneHandler = f -} - -func (i *ZwlrScreencopyFrameV1) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.bufferHandler == nil { - return - } - var e ZwlrScreencopyFrameV1BufferEvent - l := 0 - e.Format = client.Uint32(data[l : l+4]) - l += 4 - e.Width = client.Uint32(data[l : l+4]) - l += 4 - e.Height = client.Uint32(data[l : l+4]) - l += 4 - e.Stride = client.Uint32(data[l : l+4]) - l += 4 - - i.bufferHandler(e) - case 1: - if i.flagsHandler == nil { - return - } - var e ZwlrScreencopyFrameV1FlagsEvent - l := 0 - e.Flags = client.Uint32(data[l : l+4]) - l += 4 - - i.flagsHandler(e) - case 2: - if i.readyHandler == nil { - return - } - var e ZwlrScreencopyFrameV1ReadyEvent - l := 0 - e.TvSecHi = client.Uint32(data[l : l+4]) - l += 4 - e.TvSecLo = client.Uint32(data[l : l+4]) - l += 4 - e.TvNsec = client.Uint32(data[l : l+4]) - l += 4 - - i.readyHandler(e) - case 3: - if i.failedHandler == nil { - return - } - var e ZwlrScreencopyFrameV1FailedEvent - - i.failedHandler(e) - case 4: - if i.damageHandler == nil { - return - } - var e ZwlrScreencopyFrameV1DamageEvent - l := 0 - e.X = client.Uint32(data[l : l+4]) - l += 4 - e.Y = client.Uint32(data[l : l+4]) - l += 4 - e.Width = client.Uint32(data[l : l+4]) - l += 4 - e.Height = client.Uint32(data[l : l+4]) - l += 4 - - i.damageHandler(e) - case 5: - if i.linuxDmabufHandler == nil { - return - } - var e ZwlrScreencopyFrameV1LinuxDmabufEvent - l := 0 - e.Format = client.Uint32(data[l : l+4]) - l += 4 - e.Width = client.Uint32(data[l : l+4]) - l += 4 - e.Height = client.Uint32(data[l : l+4]) - l += 4 - - i.linuxDmabufHandler(e) - case 6: - if i.bufferDoneHandler == nil { - return - } - var e ZwlrScreencopyFrameV1BufferDoneEvent - - i.bufferDoneHandler(e) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wp_viewporter/viewporter.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wp_viewporter/viewporter.go deleted file mode 100644 index 4b390bf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/wp_viewporter/viewporter.go +++ /dev/null @@ -1,399 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : /tmp/viewporter.xml -// -// viewporter Protocol Copyright: -// -// Copyright © 2013-2016 Collabora, Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice (including the next -// paragraph) shall be included in all copies or substantial portions of the -// Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -package wp_viewporter - -import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - -// WpViewporterInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const WpViewporterInterfaceName = "wp_viewporter" - -// WpViewporter : surface cropping and scaling -// -// The global interface exposing surface cropping and scaling -// capabilities is used to instantiate an interface extension for a -// wl_surface object. This extended interface will then allow -// cropping and scaling the surface contents, effectively -// disconnecting the direct relationship between the buffer and the -// surface size. -type WpViewporter struct { - client.BaseProxy -} - -// NewWpViewporter : surface cropping and scaling -// -// The global interface exposing surface cropping and scaling -// capabilities is used to instantiate an interface extension for a -// wl_surface object. This extended interface will then allow -// cropping and scaling the surface contents, effectively -// disconnecting the direct relationship between the buffer and the -// surface size. -func NewWpViewporter(ctx *client.Context) *WpViewporter { - wpViewporter := &WpViewporter{} - ctx.Register(wpViewporter) - return wpViewporter -} - -// Destroy : unbind from the cropping and scaling interface -// -// Informs the server that the client will not be using this -// protocol object anymore. This does not affect any other objects, -// wp_viewport objects included. -func (i *WpViewporter) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// GetViewport : extend surface interface for crop and scale -// -// Instantiate an interface extension for the given wl_surface to -// crop and scale its content. If the given wl_surface already has -// a wp_viewport object associated, the viewport_exists -// protocol error is raised. -// -// surface: the surface -func (i *WpViewporter) GetViewport(surface *client.Surface) (*WpViewport, error) { - id := NewWpViewport(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], surface.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -type WpViewporterError uint32 - -// WpViewporterError : -const ( - // WpViewporterErrorViewportExists : the surface already has a viewport object associated - WpViewporterErrorViewportExists WpViewporterError = 0 -) - -func (e WpViewporterError) Name() string { - switch e { - case WpViewporterErrorViewportExists: - return "viewport_exists" - default: - return "" - } -} - -func (e WpViewporterError) Value() string { - switch e { - case WpViewporterErrorViewportExists: - return "0" - default: - return "" - } -} - -func (e WpViewporterError) String() string { - return e.Name() + "=" + e.Value() -} - -// WpViewportInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const WpViewportInterfaceName = "wp_viewport" - -// WpViewport : crop and scale interface to a wl_surface -// -// An additional interface to a wl_surface object, which allows the -// client to specify the cropping and scaling of the surface -// contents. -// -// This interface works with two concepts: the source rectangle (src_x, -// src_y, src_width, src_height), and the destination size (dst_width, -// dst_height). The contents of the source rectangle are scaled to the -// destination size, and content outside the source rectangle is ignored. -// This state is double-buffered, see wl_surface.commit. -// -// The two parts of crop and scale state are independent: the source -// rectangle, and the destination size. Initially both are unset, that -// is, no scaling is applied. The whole of the current wl_buffer is -// used as the source, and the surface size is as defined in -// wl_surface.attach. -// -// If the destination size is set, it causes the surface size to become -// dst_width, dst_height. The source (rectangle) is scaled to exactly -// this size. This overrides whatever the attached wl_buffer size is, -// unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface -// has no content and therefore no size. Otherwise, the size is always -// at least 1x1 in surface local coordinates. -// -// If the source rectangle is set, it defines what area of the wl_buffer is -// taken as the source. If the source rectangle is set and the destination -// size is not set, then src_width and src_height must be integers, and the -// surface size becomes the source rectangle size. This results in cropping -// without scaling. If src_width or src_height are not integers and -// destination size is not set, the bad_size protocol error is raised when -// the surface state is applied. -// -// The coordinate transformations from buffer pixel coordinates up to -// the surface-local coordinates happen in the following order: -// 1. buffer_transform (wl_surface.set_buffer_transform) -// 2. buffer_scale (wl_surface.set_buffer_scale) -// 3. crop and scale (wp_viewport.set*) -// This means, that the source rectangle coordinates of crop and scale -// are given in the coordinates after the buffer transform and scale, -// i.e. in the coordinates that would be the surface-local coordinates -// if the crop and scale was not applied. -// -// If src_x or src_y are negative, the bad_value protocol error is raised. -// Otherwise, if the source rectangle is partially or completely outside of -// the non-NULL wl_buffer, then the out_of_buffer protocol error is raised -// when the surface state is applied. A NULL wl_buffer does not raise the -// out_of_buffer error. -// -// If the wl_surface associated with the wp_viewport is destroyed, -// all wp_viewport requests except 'destroy' raise the protocol error -// no_surface. -// -// If the wp_viewport object is destroyed, the crop and scale -// state is removed from the wl_surface. The change will be applied -// on the next wl_surface.commit. -type WpViewport struct { - client.BaseProxy -} - -// NewWpViewport : crop and scale interface to a wl_surface -// -// An additional interface to a wl_surface object, which allows the -// client to specify the cropping and scaling of the surface -// contents. -// -// This interface works with two concepts: the source rectangle (src_x, -// src_y, src_width, src_height), and the destination size (dst_width, -// dst_height). The contents of the source rectangle are scaled to the -// destination size, and content outside the source rectangle is ignored. -// This state is double-buffered, see wl_surface.commit. -// -// The two parts of crop and scale state are independent: the source -// rectangle, and the destination size. Initially both are unset, that -// is, no scaling is applied. The whole of the current wl_buffer is -// used as the source, and the surface size is as defined in -// wl_surface.attach. -// -// If the destination size is set, it causes the surface size to become -// dst_width, dst_height. The source (rectangle) is scaled to exactly -// this size. This overrides whatever the attached wl_buffer size is, -// unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface -// has no content and therefore no size. Otherwise, the size is always -// at least 1x1 in surface local coordinates. -// -// If the source rectangle is set, it defines what area of the wl_buffer is -// taken as the source. If the source rectangle is set and the destination -// size is not set, then src_width and src_height must be integers, and the -// surface size becomes the source rectangle size. This results in cropping -// without scaling. If src_width or src_height are not integers and -// destination size is not set, the bad_size protocol error is raised when -// the surface state is applied. -// -// The coordinate transformations from buffer pixel coordinates up to -// the surface-local coordinates happen in the following order: -// 1. buffer_transform (wl_surface.set_buffer_transform) -// 2. buffer_scale (wl_surface.set_buffer_scale) -// 3. crop and scale (wp_viewport.set*) -// This means, that the source rectangle coordinates of crop and scale -// are given in the coordinates after the buffer transform and scale, -// i.e. in the coordinates that would be the surface-local coordinates -// if the crop and scale was not applied. -// -// If src_x or src_y are negative, the bad_value protocol error is raised. -// Otherwise, if the source rectangle is partially or completely outside of -// the non-NULL wl_buffer, then the out_of_buffer protocol error is raised -// when the surface state is applied. A NULL wl_buffer does not raise the -// out_of_buffer error. -// -// If the wl_surface associated with the wp_viewport is destroyed, -// all wp_viewport requests except 'destroy' raise the protocol error -// no_surface. -// -// If the wp_viewport object is destroyed, the crop and scale -// state is removed from the wl_surface. The change will be applied -// on the next wl_surface.commit. -func NewWpViewport(ctx *client.Context) *WpViewport { - wpViewport := &WpViewport{} - ctx.Register(wpViewport) - return wpViewport -} - -// Destroy : remove scaling and cropping from the surface -// -// The associated wl_surface's crop and scale state is removed. -// The change is applied on the next wl_surface.commit. -func (i *WpViewport) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetSource : set the source rectangle for cropping -// -// Set the source rectangle of the associated wl_surface. See -// wp_viewport for the description, and relation to the wl_buffer -// size. -// -// If all of x, y, width and height are -1.0, the source rectangle is -// unset instead. Any other set of values where width or height are zero -// or negative, or x or y are negative, raise the bad_value protocol -// error. -// -// The crop and scale state is double-buffered, see wl_surface.commit. -// -// x: source rectangle x -// y: source rectangle y -// width: source rectangle width -// height: source rectangle height -func (i *WpViewport) SetSource(x, y, width, height float64) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutFixed(_reqBuf[l:l+4], x) - l += 4 - client.PutFixed(_reqBuf[l:l+4], y) - l += 4 - client.PutFixed(_reqBuf[l:l+4], width) - l += 4 - client.PutFixed(_reqBuf[l:l+4], height) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetDestination : set the surface size for scaling -// -// Set the destination size of the associated wl_surface. See -// wp_viewport for the description, and relation to the wl_buffer -// size. -// -// If width is -1 and height is -1, the destination size is unset -// instead. Any other pair of values for width and height that -// contains zero or negative values raises the bad_value protocol -// error. -// -// The crop and scale state is double-buffered, see wl_surface.commit. -// -// width: surface width -// height: surface height -func (i *WpViewport) SetDestination(width, height int32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - client.PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - client.PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type WpViewportError uint32 - -// WpViewportError : -const ( - // WpViewportErrorBadValue : negative or zero values in width or height - WpViewportErrorBadValue WpViewportError = 0 - // WpViewportErrorBadSize : destination size is not integer - WpViewportErrorBadSize WpViewportError = 1 - // WpViewportErrorOutOfBuffer : source rectangle extends outside of the content area - WpViewportErrorOutOfBuffer WpViewportError = 2 - // WpViewportErrorNoSurface : the wl_surface was destroyed - WpViewportErrorNoSurface WpViewportError = 3 -) - -func (e WpViewportError) Name() string { - switch e { - case WpViewportErrorBadValue: - return "bad_value" - case WpViewportErrorBadSize: - return "bad_size" - case WpViewportErrorOutOfBuffer: - return "out_of_buffer" - case WpViewportErrorNoSurface: - return "no_surface" - default: - return "" - } -} - -func (e WpViewportError) Value() string { - switch e { - case WpViewportErrorBadValue: - return "0" - case WpViewportErrorBadSize: - return "1" - case WpViewportErrorOutOfBuffer: - return "2" - case WpViewportErrorNoSurface: - return "3" - default: - return "" - } -} - -func (e WpViewportError) String() string { - return e.Name() + "=" + e.Value() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/dwl-ipc-unstable-v2.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/dwl-ipc-unstable-v2.xml deleted file mode 100644 index a4e0b49..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/dwl-ipc-unstable-v2.xml +++ /dev/null @@ -1,252 +0,0 @@ - - - - - This protocol allows clients to update and get updates from dwl. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible - changes may be added together with the corresponding interface - version bump. - Backward incompatible changes are done by bumping the version - number in the protocol and interface names and resetting the - interface version. Once the protocol is to be declared stable, - the 'z' prefix and the version number in the protocol and - interface names are removed and the interface version number is - reset. - - - - - This interface is exposed as a global in wl_registry. - - Clients can use this interface to get a dwl_ipc_output. - After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. - The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. - - - - - Indicates that the client will not the dwl_ipc_manager object anymore. - Objects created through this instance are not affected. - - - - - - Get a dwl_ipc_outout for the specified wl_output. - - - - - - - - This event is sent after binding. - A roundtrip after binding guarantees the client recieved all tags. - - - - - - - This event is sent after binding. - A roundtrip after binding guarantees the client recieved all layouts. - - - - - - - - Observe and control a dwl output. - - Events are double-buffered: - Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. - - Request are not double-buffered: - The compositor will update immediately upon request. - - - - - - - - - - - Indicates to that the client no longer needs this dwl_ipc_output. - - - - - - Indicates the client should hide or show themselves. - If the client is visible then hide, if hidden then show. - - - - - - Indicates if the output is active. Zero is invalid, nonzero is valid. - - - - - - - Indicates that a tag has been updated. - - - - - - - - - - Indicates a new layout is selected. - - - - - - - Indicates the title has changed. - - - - - - - Indicates the appid has changed. - - - - - - - Indicates the layout has changed. Since layout symbols are dynamic. - As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. - You can ignore the zdwl_ipc_output.layout event. - - - - - - - Indicates that a sequence of status updates have finished and the client should redraw. - - - - - - - - - - - - The tags are updated as follows: - new_tags = (current_tags AND and_tags) XOR xor_tags - - - - - - - - - - - - This request allows clients to instruct the compositor to quit mango. - - - - - - - - - - - - - - - - Indicates if the selected client on this output is fullscreen. - - - - - - - Indicates if the selected client on this output is floating. - - - - - - - Indicates if x coordinates of the selected client. - - - - - - - Indicates if y coordinates of the selected client. - - - - - - - Indicates if width of the selected client. - - - - - - - Indicates if height of the selected client. - - - - - - - last map layer. - - - - - - - current keyboard layout. - - - - - - - current keybind mode. - - - - - - - scale factor of monitor. - - - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/ext-data-control-v1.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/ext-data-control-v1.xml deleted file mode 100644 index 37ee577..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/ext-data-control-v1.xml +++ /dev/null @@ -1,276 +0,0 @@ - - - - Copyright © 2018 Simon Ser - Copyright © 2019 Ivan Molodetskikh - Copyright © 2024 Neal Gompa - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - This protocol allows a privileged client to control data devices. In - particular, the client will be able to manage the current selection and take - the role of a clipboard manager. - - Warning! The protocol described in this file is currently in the testing - phase. Backward compatible changes may be added together with the - corresponding interface version bump. Backward incompatible changes can - only be done by creating a new major version of the extension. - - - - - This interface is a manager that allows creating per-seat data device - controls. - - - - - Create a new data source. - - - - - - - Create a data device that can be used to manage a seat's selection. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This interface allows a client to manage a seat's selection. - - When the seat is destroyed, this object becomes inert. - - - - - This request asks the compositor to set the selection to the data from - the source on behalf of the client. - - The given source may not be used in any further set_selection or - set_primary_selection requests. Attempting to use a previously used - source triggers the used_source protocol error. - - To unset the selection, set the source to NULL. - - - - - - - Destroys the data device object. - - - - - - The data_offer event introduces a new ext_data_control_offer object, - which will subsequently be used in either the - ext_data_control_device.selection event (for the regular clipboard - selections) or the ext_data_control_device.primary_selection event (for - the primary clipboard selections). Immediately following the - ext_data_control_device.data_offer event, the new data_offer object - will send out ext_data_control_offer.offer events to describe the MIME - types it offers. - - - - - - - The selection event is sent out to notify the client of a new - ext_data_control_offer for the selection for this device. The - ext_data_control_device.data_offer and the ext_data_control_offer.offer - events are sent out immediately before this event to introduce the data - offer object. The selection event is sent to a client when a new - selection is set. The ext_data_control_offer is valid until a new - ext_data_control_offer or NULL is received. The client must destroy the - previous selection ext_data_control_offer, if any, upon receiving this - event. Regardless, the previous selection will be ignored once a new - selection ext_data_control_offer is received. - - The first selection event is sent upon binding the - ext_data_control_device object. - - - - - - - This data control object is no longer valid and should be destroyed by - the client. - - - - - - The primary_selection event is sent out to notify the client of a new - ext_data_control_offer for the primary selection for this device. The - ext_data_control_device.data_offer and the ext_data_control_offer.offer - events are sent out immediately before this event to introduce the data - offer object. The primary_selection event is sent to a client when a - new primary selection is set. The ext_data_control_offer is valid until - a new ext_data_control_offer or NULL is received. The client must - destroy the previous primary selection ext_data_control_offer, if any, - upon receiving this event. Regardless, the previous primary selection - will be ignored once a new primary selection ext_data_control_offer is - received. - - If the compositor supports primary selection, the first - primary_selection event is sent upon binding the - ext_data_control_device object. - - - - - - - This request asks the compositor to set the primary selection to the - data from the source on behalf of the client. - - The given source may not be used in any further set_selection or - set_primary_selection requests. Attempting to use a previously used - source triggers the used_source protocol error. - - To unset the primary selection, set the source to NULL. - - The compositor will ignore this request if it does not support primary - selection. - - - - - - - - - - - - The ext_data_control_source object is the source side of a - ext_data_control_offer. It is created by the source client in a data - transfer and provides a way to describe the offered data and a way to - respond to requests to transfer the data. - - - - - - - - - This request adds a MIME type to the set of MIME types advertised to - targets. Can be called several times to offer multiple types. - - Calling this after ext_data_control_device.set_selection is a protocol - error. - - - - - - - Destroys the data source object. - - - - - - Request for data from the client. Send the data as the specified MIME - type over the passed file descriptor, then close it. - - - - - - - - This data source is no longer valid. The data source has been replaced - by another data source. - - The client should clean up and destroy this data source. - - - - - - - A ext_data_control_offer represents a piece of data offered for transfer - by another client (the source client). The offer describes the different - MIME types that the data can be converted to and provides the mechanism - for transferring the data directly from the source client. - - - - - To transfer the offered data, the client issues this request and - indicates the MIME type it wants to receive. The transfer happens - through the passed file descriptor (typically created with the pipe - system call). The source client writes the data in the MIME type - representation requested and then closes the file descriptor. - - The receiving client reads from the read end of the pipe until EOF and - then closes its end, at which point the transfer is complete. - - This request may happen multiple times for different MIME types. - - - - - - - - Destroys the data offer object. - - - - - - Sent immediately after creating the ext_data_control_offer object. - One event per offered MIME type. - - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/ext-workspace-v1.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/ext-workspace-v1.xml deleted file mode 100644 index 134b729..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/ext-workspace-v1.xml +++ /dev/null @@ -1,422 +0,0 @@ - - - - Copyright © 2019 Christopher Billington - Copyright © 2020 Ilia Bozhinov - Copyright © 2022 Victoria Brekenfeld - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - Workspaces, also called virtual desktops, are groups of surfaces. A - compositor with a concept of workspaces may only show some such groups of - surfaces (those of 'active' workspaces) at a time. 'Activating' a - workspace is a request for the compositor to display that workspace's - surfaces as normal, whereas the compositor may hide or otherwise - de-emphasise surfaces that are associated only with 'inactive' workspaces. - Workspaces are grouped by which sets of outputs they correspond to, and - may contain surfaces only from those outputs. In this way, it is possible - for each output to have its own set of workspaces, or for all outputs (or - any other arbitrary grouping) to share workspaces. Compositors may - optionally conceptually arrange each group of workspaces in an - N-dimensional grid. - - The purpose of this protocol is to enable the creation of taskbars and - docks by providing them with a list of workspaces and their properties, - and allowing them to activate and deactivate workspaces. - - After a client binds the ext_workspace_manager_v1, each workspace will be - sent via the workspace event. - - - - - This event is emitted whenever a new workspace group has been created. - - All initial details of the workspace group (outputs) will be - sent immediately after this event via the corresponding events in - ext_workspace_group_handle_v1 and ext_workspace_handle_v1. - - - - - - - This event is emitted whenever a new workspace has been created. - - All initial details of the workspace (name, coordinates, state) will - be sent immediately after this event via the corresponding events in - ext_workspace_handle_v1. - - Workspaces start off unassigned to any workspace group. - - - - - - - The client must send this request after it has finished sending other - requests. The compositor must process a series of requests preceding a - commit request atomically. - - This allows changes to the workspace properties to be seen as atomic, - even if they happen via multiple events, and even if they involve - multiple ext_workspace_handle_v1 objects, for example, deactivating one - workspace and activating another. - - - - - - This event is sent after all changes in all workspaces and workspace groups have been - sent. - - This allows changes to one or more ext_workspace_group_handle_v1 - properties and ext_workspace_handle_v1 properties - to be seen as atomic, even if they happen via multiple events. - In particular, an output moving from one workspace group to - another sends an output_enter event and an output_leave event to the two - ext_workspace_group_handle_v1 objects in question. The compositor sends - the done event only after updating the output information in both - workspace groups. - - - - - - This event indicates that the compositor is done sending events to the - ext_workspace_manager_v1. The server will destroy the object - immediately after sending this request. - - - - - - Indicates the client no longer wishes to receive events for new - workspace groups. However the compositor may emit further workspace - events, until the finished event is emitted. The compositor is expected - to send the finished event eventually once the stop request has been processed. - - The client must not send any requests after this one, doing so will raise a wl_display - invalid_object error. - - - - - - - - A ext_workspace_group_handle_v1 object represents a workspace group - that is assigned a set of outputs and contains a number of workspaces. - - The set of outputs assigned to the workspace group is conveyed to the client via - output_enter and output_leave events, and its workspaces are conveyed with - workspace events. - - For example, a compositor which has a set of workspaces for each output may - advertise a workspace group (and its workspaces) per output, whereas a compositor - where a workspace spans all outputs may advertise a single workspace group for all - outputs. - - - - - - - - - This event advertises the capabilities supported by the compositor. If - a capability isn't supported, clients should hide or disable the UI - elements that expose this functionality. For instance, if the - compositor doesn't advertise support for creating workspaces, a button - triggering the create_workspace request should not be displayed. - - The compositor will ignore requests it doesn't support. For instance, - a compositor which doesn't advertise support for creating workspaces will ignore - create_workspace requests. - - Compositors must send this event once after creation of an - ext_workspace_group_handle_v1. When the capabilities change, compositors - must send this event again. - - - - - - - This event is emitted whenever an output is assigned to the workspace - group or a new `wl_output` object is bound by the client, which was already - assigned to this workspace_group. - - - - - - - This event is emitted whenever an output is removed from the workspace - group. - - - - - - - This event is emitted whenever a workspace is assigned to this group. - A workspace may only ever be assigned to a single group at a single point - in time, but can be re-assigned during it's lifetime. - - - - - - - This event is emitted whenever a workspace is removed from this group. - - - - - - - This event is send when the group associated with the ext_workspace_group_handle_v1 - has been removed. After sending this request the compositor will immediately consider - the object inert. Any requests will be ignored except the destroy request. - It is guaranteed there won't be any more events referencing this - ext_workspace_group_handle_v1. - - The compositor must remove all workspaces belonging to a workspace group - via a workspace_leave event before removing the workspace group. - - - - - - Request that the compositor create a new workspace with the given name - and assign it to this group. - - There is no guarantee that the compositor will create a new workspace, - or that the created workspace will have the provided name. - - - - - - - Destroys the ext_workspace_group_handle_v1 object. - - This request should be send either when the client does not want to - use the workspace group object any more or after the removed event to finalize - the destruction of the object. - - - - - - - A ext_workspace_handle_v1 object represents a workspace that handles a - group of surfaces. - - Each workspace has: - - a name, conveyed to the client with the name event - - potentially an id conveyed with the id event - - a list of states, conveyed to the client with the state event - - and optionally a set of coordinates, conveyed to the client with the - coordinates event - - The client may request that the compositor activate or deactivate the workspace. - - Each workspace can belong to only a single workspace group. - Depending on the compositor policy, there might be workspaces with - the same name in different workspace groups, but these workspaces are still - separate (e.g. one of them might be active while the other is not). - - - - - If this event is emitted, it will be send immediately after the - ext_workspace_handle_v1 is created or when an id is assigned to - a workspace (at most once during it's lifetime). - - An id will never change during the lifetime of the `ext_workspace_handle_v1` - and is guaranteed to be unique during it's lifetime. - - Ids are not human-readable and shouldn't be displayed, use `name` for that purpose. - - Compositors are expected to only send ids for workspaces likely stable across multiple - sessions and can be used by clients to store preferences for workspaces. Workspaces without - ids should be considered temporary and any data associated with them should be deleted once - the respective object is lost. - - - - - - - This event is emitted immediately after the ext_workspace_handle_v1 is - created and whenever the name of the workspace changes. - - A name is meant to be human-readable and can be displayed to a user. - Unlike the id it is neither stable nor unique. - - - - - - - This event is used to organize workspaces into an N-dimensional grid - within a workspace group, and if supported, is emitted immediately after - the ext_workspace_handle_v1 is created and whenever the coordinates of - the workspace change. Compositors may not send this event if they do not - conceptually arrange workspaces in this way. If compositors simply - number workspaces, without any geometric interpretation, they may send - 1D coordinates, which clients should not interpret as implying any - geometry. Sending an empty array means that the compositor no longer - orders the workspace geometrically. - - Coordinates have an arbitrary number of dimensions N with an uint32 - position along each dimension. By convention if N > 1, the first - dimension is X, the second Y, the third Z, and so on. The compositor may - chose to utilize these events for a more novel workspace layout - convention, however. No guarantee is made about the grid being filled or - bounded; there may be a workspace at coordinate 1 and another at - coordinate 1000 and none in between. Within a workspace group, however, - workspaces must have unique coordinates of equal dimensionality. - - - - - - - The different states that a workspace can have. - - - - - - - The workspace is not visible in its workspace group, and clients - attempting to visualize the compositor workspace state should not - display such workspaces. - - - - - - - This event is emitted immediately after the ext_workspace_handle_v1 is - created and each time the workspace state changes, either because of a - compositor action or because of a request in this protocol. - - Missing states convey the opposite meaning, e.g. an unset active bit - means the workspace is currently inactive. - - - - - - - - - - - - - - This event advertises the capabilities supported by the compositor. If - a capability isn't supported, clients should hide or disable the UI - elements that expose this functionality. For instance, if the - compositor doesn't advertise support for removing workspaces, a button - triggering the remove request should not be displayed. - - The compositor will ignore requests it doesn't support. For instance, - a compositor which doesn't advertise support for remove will ignore - remove requests. - - Compositors must send this event once after creation of an - ext_workspace_handle_v1 . When the capabilities change, compositors - must send this event again. - - - - - - - This event is send when the workspace associated with the ext_workspace_handle_v1 - has been removed. After sending this request, the compositor will immediately consider - the object inert. Any requests will be ignored except the destroy request. - - It is guaranteed there won't be any more events referencing this - ext_workspace_handle_v1. - - The compositor must only remove a workspaces not currently belonging to any - workspace_group. - - - - - - Destroys the ext_workspace_handle_v1 object. - - This request should be made either when the client does not want to - use the workspace object any more or after the remove event to finalize - the destruction of the object. - - - - - - Request that this workspace be activated. - - There is no guarantee the workspace will be actually activated, and - behaviour may be compositor-dependent. For example, activating a - workspace may or may not deactivate all other workspaces in the same - group. - - - - - - Request that this workspace be deactivated. - - There is no guarantee the workspace will be actually deactivated. - - - - - - Requests that this workspace is assigned to the given workspace group. - - There is no guarantee the workspace will be assigned. - - - - - - - Request that this workspace be removed. - - There is no guarantee the workspace will be actually removed. - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml deleted file mode 100644 index 2774876..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/keyboard-shortcuts-inhibit-unstable-v1.xml +++ /dev/null @@ -1,143 +0,0 @@ - - - - - Copyright © 2017 Red Hat Inc. - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - - - This protocol specifies a way for a client to request the compositor - to ignore its own keyboard shortcuts for a given seat, so that all - key events from that seat get forwarded to a surface. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible - changes may be added together with the corresponding interface - version bump. - Backward incompatible changes are done by bumping the version - number in the protocol and interface names and resetting the - interface version. Once the protocol is to be declared stable, - the 'z' prefix and the version number in the protocol and - interface names are removed and the interface version number is - reset. - - - - - A global interface used for inhibiting the compositor keyboard shortcuts. - - - - - Destroy the keyboard shortcuts inhibitor manager. - - - - - - Create a new keyboard shortcuts inhibitor object associated with - the given surface for the given seat. - - If shortcuts are already inhibited for the specified seat and surface, - a protocol error "already_inhibited" is raised by the compositor. - - - - - - - - - - - - - - A keyboard shortcuts inhibitor instructs the compositor to ignore - its own keyboard shortcuts when the associated surface has keyboard - focus. As a result, when the surface has keyboard focus on the given - seat, it will receive all key events originating from the specified - seat, even those which would normally be caught by the compositor for - its own shortcuts. - - The Wayland compositor is however under no obligation to disable - all of its shortcuts, and may keep some special key combo for its own - use, including but not limited to one allowing the user to forcibly - restore normal keyboard events routing in the case of an unwilling - client. The compositor may also use the same key combo to reactivate - an existing shortcut inhibitor that was previously deactivated on - user request. - - When the compositor restores its own keyboard shortcuts, an - "inactive" event is emitted to notify the client that the keyboard - shortcuts inhibitor is not effectively active for the surface and - seat any more, and the client should not expect to receive all - keyboard events. - - When the keyboard shortcuts inhibitor is inactive, the client has - no way to forcibly reactivate the keyboard shortcuts inhibitor. - - The user can chose to re-enable a previously deactivated keyboard - shortcuts inhibitor using any mechanism the compositor may offer, - in which case the compositor will send an "active" event to notify - the client. - - If the surface is destroyed, unmapped, or loses the seat's keyboard - focus, the keyboard shortcuts inhibitor becomes irrelevant and the - compositor will restore its own keyboard shortcuts but no "inactive" - event is emitted in this case. - - - - - Remove the keyboard shortcuts inhibitor from the associated wl_surface. - - - - - - This event indicates that the shortcut inhibitor is active. - - The compositor sends this event every time compositor shortcuts - are inhibited on behalf of the surface. When active, the client - may receive input events normally reserved by the compositor - (see zwp_keyboard_shortcuts_inhibitor_v1). - - This occurs typically when the initial request "inhibit_shortcuts" - first becomes active or when the user instructs the compositor to - re-enable and existing shortcuts inhibitor using any mechanism - offered by the compositor. - - - - - - This event indicates that the shortcuts inhibitor is inactive, - normal shortcuts processing is restored by the compositor. - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-gamma-control-unstable-v1.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-gamma-control-unstable-v1.xml deleted file mode 100644 index 16e0be8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-gamma-control-unstable-v1.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - Copyright © 2015 Giulio camuffo - Copyright © 2018 Simon Ser - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - This protocol allows a privileged client to set the gamma tables for - outputs. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - - - - - This interface is a manager that allows creating per-output gamma - controls. - - - - - Create a gamma control that can be used to adjust gamma tables for the - provided output. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This interface allows a client to adjust gamma tables for a particular - output. - - The client will receive the gamma size, and will then be able to set gamma - tables. At any time the compositor can send a failed event indicating that - this object is no longer valid. - - There can only be at most one gamma control object per output, which - has exclusive access to this particular output. When the gamma control - object is destroyed, the gamma table is restored to its original value. - - - - - Advertise the size of each gamma ramp. - - This event is sent immediately when the gamma control object is created. - - - - - - - - - - - Set the gamma table. The file descriptor can be memory-mapped to provide - the raw gamma table, which contains successive gamma ramps for the red, - green and blue channels. Each gamma ramp is an array of 16-byte unsigned - integers which has the same length as the gamma size. - - The file descriptor data must have the same length as three times the - gamma size. - - - - - - - This event indicates that the gamma control is no longer valid. This - can happen for a number of reasons, including: - - The output doesn't support gamma tables - - Setting the gamma tables failed - - Another client already has exclusive gamma control for this output - - The compositor has transferred gamma control to another client - - Upon receiving this event, the client should destroy this object. - - - - - - Destroys the gamma control object. If the object is still valid, this - restores the original gamma tables. - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-layer-shell-unstable-v1.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-layer-shell-unstable-v1.xml deleted file mode 100644 index e9f27e4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-layer-shell-unstable-v1.xml +++ /dev/null @@ -1,407 +0,0 @@ - - - - Copyright © 2017 Drew DeVault - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - Clients can use this interface to assign the surface_layer role to - wl_surfaces. Such surfaces are assigned to a "layer" of the output and - rendered with a defined z-depth respective to each other. They may also be - anchored to the edges and corners of a screen and specify input handling - semantics. This interface should be suitable for the implementation of - many desktop shell components, and a broad number of other applications - that interact with the desktop. - - - - - Create a layer surface for an existing surface. This assigns the role of - layer_surface, or raises a protocol error if another role is already - assigned. - - Creating a layer surface from a wl_surface which has a buffer attached - or committed is a client error, and any attempts by a client to attach - or manipulate a buffer prior to the first layer_surface.configure call - must also be treated as errors. - - After creating a layer_surface object and setting it up, the client - must perform an initial commit without any buffer attached. - The compositor will reply with a layer_surface.configure event. - The client must acknowledge it and is then allowed to attach a buffer - to map the surface. - - You may pass NULL for output to allow the compositor to decide which - output to use. Generally this will be the one that the user most - recently interacted with. - - Clients can specify a namespace that defines the purpose of the layer - surface. - - - - - - - - - - - - - - - - - These values indicate which layers a surface can be rendered in. They - are ordered by z depth, bottom-most first. Traditional shell surfaces - will typically be rendered between the bottom and top layers. - Fullscreen shell surfaces are typically rendered at the top layer. - Multiple surfaces can share a single layer, and ordering within a - single layer is undefined. - - - - - - - - - - - - - This request indicates that the client will not use the layer_shell - object any more. Objects that have been created through this instance - are not affected. - - - - - - - An interface that may be implemented by a wl_surface, for surfaces that - are designed to be rendered as a layer of a stacked desktop-like - environment. - - Layer surface state (layer, size, anchor, exclusive zone, - margin, interactivity) is double-buffered, and will be applied at the - time wl_surface.commit of the corresponding wl_surface is called. - - Attaching a null buffer to a layer surface unmaps it. - - Unmapping a layer_surface means that the surface cannot be shown by the - compositor until it is explicitly mapped again. The layer_surface - returns to the state it had right after layer_shell.get_layer_surface. - The client can re-map the surface by performing a commit without any - buffer attached, waiting for a configure event and handling it as usual. - - - - - Sets the size of the surface in surface-local coordinates. The - compositor will display the surface centered with respect to its - anchors. - - If you pass 0 for either value, the compositor will assign it and - inform you of the assignment in the configure event. You must set your - anchor to opposite edges in the dimensions you omit; not doing so is a - protocol error. Both values are 0 by default. - - Size is double-buffered, see wl_surface.commit. - - - - - - - - Requests that the compositor anchor the surface to the specified edges - and corners. If two orthogonal edges are specified (e.g. 'top' and - 'left'), then the anchor point will be the intersection of the edges - (e.g. the top left corner of the output); otherwise the anchor point - will be centered on that edge, or in the center if none is specified. - - Anchor is double-buffered, see wl_surface.commit. - - - - - - - Requests that the compositor avoids occluding an area with other - surfaces. The compositor's use of this information is - implementation-dependent - do not assume that this region will not - actually be occluded. - - A positive value is only meaningful if the surface is anchored to one - edge or an edge and both perpendicular edges. If the surface is not - anchored, anchored to only two perpendicular edges (a corner), anchored - to only two parallel edges or anchored to all edges, a positive value - will be treated the same as zero. - - A positive zone is the distance from the edge in surface-local - coordinates to consider exclusive. - - Surfaces that do not wish to have an exclusive zone may instead specify - how they should interact with surfaces that do. If set to zero, the - surface indicates that it would like to be moved to avoid occluding - surfaces with a positive exclusive zone. If set to -1, the surface - indicates that it would not like to be moved to accommodate for other - surfaces, and the compositor should extend it all the way to the edges - it is anchored to. - - For example, a panel might set its exclusive zone to 10, so that - maximized shell surfaces are not shown on top of it. A notification - might set its exclusive zone to 0, so that it is moved to avoid - occluding the panel, but shell surfaces are shown underneath it. A - wallpaper or lock screen might set their exclusive zone to -1, so that - they stretch below or over the panel. - - The default value is 0. - - Exclusive zone is double-buffered, see wl_surface.commit. - - - - - - - Requests that the surface be placed some distance away from the anchor - point on the output, in surface-local coordinates. Setting this value - for edges you are not anchored to has no effect. - - The exclusive zone includes the margin. - - Margin is double-buffered, see wl_surface.commit. - - - - - - - - - - Types of keyboard interaction possible for layer shell surfaces. The - rationale for this is twofold: (1) some applications are not interested - in keyboard events and not allowing them to be focused can improve the - desktop experience; (2) some applications will want to take exclusive - keyboard focus. - - - - - This value indicates that this surface is not interested in keyboard - events and the compositor should never assign it the keyboard focus. - - This is the default value, set for newly created layer shell surfaces. - - This is useful for e.g. desktop widgets that display information or - only have interaction with non-keyboard input devices. - - - - - Request exclusive keyboard focus if this surface is above the shell surface layer. - - For the top and overlay layers, the seat will always give - exclusive keyboard focus to the top-most layer which has keyboard - interactivity set to exclusive. If this layer contains multiple - surfaces with keyboard interactivity set to exclusive, the compositor - determines the one receiving keyboard events in an implementation- - defined manner. In this case, no guarantee is made when this surface - will receive keyboard focus (if ever). - - For the bottom and background layers, the compositor is allowed to use - normal focus semantics. - - This setting is mainly intended for applications that need to ensure - they receive all keyboard events, such as a lock screen or a password - prompt. - - - - - This requests the compositor to allow this surface to be focused and - unfocused by the user in an implementation-defined manner. The user - should be able to unfocus this surface even regardless of the layer - it is on. - - Typically, the compositor will want to use its normal mechanism to - manage keyboard focus between layer shell surfaces with this setting - and regular toplevels on the desktop layer (e.g. click to focus). - Nevertheless, it is possible for a compositor to require a special - interaction to focus or unfocus layer shell surfaces (e.g. requiring - a click even if focus follows the mouse normally, or providing a - keybinding to switch focus between layers). - - This setting is mainly intended for desktop shell components (e.g. - panels) that allow keyboard interaction. Using this option can allow - implementing a desktop shell that can be fully usable without the - mouse. - - - - - - - Set how keyboard events are delivered to this surface. By default, - layer shell surfaces do not receive keyboard events; this request can - be used to change this. - - This setting is inherited by child surfaces set by the get_popup - request. - - Layer surfaces receive pointer, touch, and tablet events normally. If - you do not want to receive them, set the input region on your surface - to an empty region. - - Keyboard interactivity is double-buffered, see wl_surface.commit. - - - - - - - This assigns an xdg_popup's parent to this layer_surface. This popup - should have been created via xdg_surface::get_popup with the parent set - to NULL, and this request must be invoked before committing the popup's - initial state. - - See the documentation of xdg_popup for more details about what an - xdg_popup is and how it is used. - - - - - - - When a configure event is received, if a client commits the - surface in response to the configure event, then the client - must make an ack_configure request sometime before the commit - request, passing along the serial of the configure event. - - If the client receives multiple configure events before it - can respond to one, it only has to ack the last configure event. - - A client is not required to commit immediately after sending - an ack_configure request - it may even ack_configure several times - before its next surface commit. - - A client may send multiple ack_configure requests before committing, but - only the last request sent before a commit indicates which configure - event the client really is responding to. - - - - - - - This request destroys the layer surface. - - - - - - The configure event asks the client to resize its surface. - - Clients should arrange their surface for the new states, and then send - an ack_configure request with the serial sent in this configure event at - some point before committing the new surface. - - The client is free to dismiss all but the last configure event it - received. - - The width and height arguments specify the size of the window in - surface-local coordinates. - - The size is a hint, in the sense that the client is free to ignore it if - it doesn't resize, pick a smaller size (to satisfy aspect ratio or - resize in steps of NxM pixels). If the client picks a smaller size and - is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the - surface will be centered on this axis. - - If the width or height arguments are zero, it means the client should - decide its own window dimension. - - - - - - - - - The closed event is sent by the compositor when the surface will no - longer be shown. The output may have been destroyed or the user may - have asked for it to be removed. Further changes to the surface will be - ignored. The client should destroy the resource after receiving this - event, and create a new surface if they so choose. - - - - - - - - - - - - - - - - - - - - - - - Change the layer that the surface is rendered on. - - Layer is double-buffered, see wl_surface.commit. - - - - - - - - - Requests an edge for the exclusive zone to apply. The exclusive - edge will be automatically deduced from anchor points when possible, - but when the surface is anchored to a corner, it will be necessary - to set it explicitly to disambiguate, as it is not possible to deduce - which one of the two corner edges should be used. - - The edge must be one the surface is anchored to, otherwise the - invalid_exclusive_edge protocol error will be raised. - - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-output-management-unstable-v1.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-output-management-unstable-v1.xml deleted file mode 100644 index 541284a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-output-management-unstable-v1.xml +++ /dev/null @@ -1,611 +0,0 @@ - - - - Copyright © 2019 Purism SPC - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - This protocol exposes interfaces to obtain and modify output device - configuration. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - - - - - This interface is a manager that allows reading and writing the current - output device configuration. - - Output devices that display pixels (e.g. a physical monitor or a virtual - output in a window) are represented as heads. Heads cannot be created nor - destroyed by the client, but they can be enabled or disabled and their - properties can be changed. Each head may have one or more available modes. - - Whenever a head appears (e.g. a monitor is plugged in), it will be - advertised via the head event. Immediately after the output manager is - bound, all current heads are advertised. - - Whenever a head's properties change, the relevant wlr_output_head events - will be sent. Not all head properties will be sent: only properties that - have changed need to. - - Whenever a head disappears (e.g. a monitor is unplugged), a - wlr_output_head.finished event will be sent. - - After one or more heads appear, change or disappear, the done event will - be sent. It carries a serial which can be used in a create_configuration - request to update heads properties. - - The information obtained from this protocol should only be used for output - configuration purposes. This protocol is not designed to be a generic - output property advertisement protocol for regular clients. Instead, - protocols such as xdg-output should be used. - - - - - This event introduces a new head. This happens whenever a new head - appears (e.g. a monitor is plugged in) or after the output manager is - bound. - - - - - - - This event is sent after all information has been sent after binding to - the output manager object and after any subsequent changes. This applies - to child head and mode objects as well. In other words, this event is - sent whenever a head or mode is created or destroyed and whenever one of - their properties has been changed. Not all state is re-sent each time - the current configuration changes: only the actual changes are sent. - - This allows changes to the output configuration to be seen as atomic, - even if they happen via multiple events. - - A serial is sent to be used in a future create_configuration request. - - - - - - - Create a new output configuration object. This allows to update head - properties. - - - - - - - - Indicates the client no longer wishes to receive events for output - configuration changes. However the compositor may emit further events, - until the finished event is emitted. - - The client must not send any more requests after this one. - - - - - - This event indicates that the compositor is done sending manager events. - The compositor will destroy the object immediately after sending this - event, so it will become invalid and the client should release any - resources associated with it. - - - - - - - A head is an output device. The difference between a wl_output object and - a head is that heads are advertised even if they are turned off. A head - object only advertises properties and cannot be used directly to change - them. - - A head has some read-only properties: modes, name, description and - physical_size. These cannot be changed by clients. - - Other properties can be updated via a wlr_output_configuration object. - - Properties sent via this interface are applied atomically via the - wlr_output_manager.done event. No guarantees are made regarding the order - in which properties are sent. - - - - - This event describes the head name. - - The naming convention is compositor defined, but limited to alphanumeric - characters and dashes (-). Each name is unique among all wlr_output_head - objects, but if a wlr_output_head object is destroyed the same name may - be reused later. The names will also remain consistent across sessions - with the same hardware and software configuration. - - Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do - not assume that the name is a reflection of an underlying DRM - connector, X11 connection, etc. - - If this head matches a wl_output, the wl_output.name event must report - the same name. - - The name event is sent after a wlr_output_head object is created. This - event is only sent once per object, and the name does not change over - the lifetime of the wlr_output_head object. - - - - - - - This event describes a human-readable description of the head. - - The description is a UTF-8 string with no convention defined for its - contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11 - output via :1'. However, do not assume that the name is a reflection of - the make, model, serial of the underlying DRM connector or the display - name of the underlying X11 connection, etc. - - If this head matches a wl_output, the wl_output.description event must - report the same name. - - The description event is sent after a wlr_output_head object is created. - This event is only sent once per object, and the description does not - change over the lifetime of the wlr_output_head object. - - - - - - - This event describes the physical size of the head. This event is only - sent if the head has a physical size (e.g. is not a projector or a - virtual device). - - The physical size event is sent after a wlr_output_head object is created. This - event is only sent once per object, and the physical size does not change over - the lifetime of the wlr_output_head object. - - - - - - - - This event introduces a mode for this head. It is sent once per - supported mode. - - - - - - - This event describes whether the head is enabled. A disabled head is not - mapped to a region of the global compositor space. - - When a head is disabled, some properties (current_mode, position, - transform and scale) are irrelevant. - - - - - - - This event describes the mode currently in use for this head. It is only - sent if the output is enabled. - - - - - - - This events describes the position of the head in the global compositor - space. It is only sent if the output is enabled. - - - - - - - - This event describes the transformation currently applied to the head. - It is only sent if the output is enabled. - - - - - - - This events describes the scale of the head in the global compositor - space. It is only sent if the output is enabled. - - - - - - - This event indicates that the head is no longer available. The head - object becomes inert. Clients should send a destroy request and release - any resources associated with it. - - - - - - - - This event describes the manufacturer of the head. - - Together with the model and serial_number events the purpose is to - allow clients to recognize heads from previous sessions and for example - load head-specific configurations back. - - It is not guaranteed this event will be ever sent. A reason for that - can be that the compositor does not have information about the make of - the head or the definition of a make is not sensible in the current - setup, for example in a virtual session. Clients can still try to - identify the head by available information from other events but should - be aware that there is an increased risk of false positives. - - If sent, the make event is sent after a wlr_output_head object is - created and only sent once per object. The make does not change over - the lifetime of the wlr_output_head object. - - It is not recommended to display the make string in UI to users. For - that the string provided by the description event should be preferred. - - - - - - - This event describes the model of the head. - - Together with the make and serial_number events the purpose is to - allow clients to recognize heads from previous sessions and for example - load head-specific configurations back. - - It is not guaranteed this event will be ever sent. A reason for that - can be that the compositor does not have information about the model of - the head or the definition of a model is not sensible in the current - setup, for example in a virtual session. Clients can still try to - identify the head by available information from other events but should - be aware that there is an increased risk of false positives. - - If sent, the model event is sent after a wlr_output_head object is - created and only sent once per object. The model does not change over - the lifetime of the wlr_output_head object. - - It is not recommended to display the model string in UI to users. For - that the string provided by the description event should be preferred. - - - - - - - This event describes the serial number of the head. - - Together with the make and model events the purpose is to allow clients - to recognize heads from previous sessions and for example load head- - specific configurations back. - - It is not guaranteed this event will be ever sent. A reason for that - can be that the compositor does not have information about the serial - number of the head or the definition of a serial number is not sensible - in the current setup. Clients can still try to identify the head by - available information from other events but should be aware that there - is an increased risk of false positives. - - If sent, the serial number event is sent after a wlr_output_head object - is created and only sent once per object. The serial number does not - change over the lifetime of the wlr_output_head object. - - It is not recommended to display the serial_number string in UI to - users. For that the string provided by the description event should be - preferred. - - - - - - - - - This request indicates that the client will no longer use this head - object. - - - - - - - - - - - - - This event describes whether adaptive sync is currently enabled for - the head or not. Adaptive sync is also known as Variable Refresh - Rate or VRR. - - - - - - - - This object describes an output mode. - - Some heads don't support output modes, in which case modes won't be - advertised. - - Properties sent via this interface are applied atomically via the - wlr_output_manager.done event. No guarantees are made regarding the order - in which properties are sent. - - - - - This event describes the mode size. The size is given in physical - hardware units of the output device. This is not necessarily the same as - the output size in the global compositor space. For instance, the output - may be scaled or transformed. - - - - - - - - This event describes the mode's fixed vertical refresh rate. It is only - sent if the mode has a fixed refresh rate. - - - - - - - This event advertises this mode as preferred. - - - - - - This event indicates that the mode is no longer available. The mode - object becomes inert. Clients should send a destroy request and release - any resources associated with it. - - - - - - - - This request indicates that the client will no longer use this mode - object. - - - - - - - This object is used by the client to describe a full output configuration. - - First, the client needs to setup the output configuration. Each head can - be either enabled (and configured) or disabled. It is a protocol error to - send two enable_head or disable_head requests with the same head. It is a - protocol error to omit a head in a configuration. - - Then, the client can apply or test the configuration. The compositor will - then reply with a succeeded, failed or cancelled event. Finally the client - should destroy the configuration object. - - - - - - - - - - - Enable a head. This request creates a head configuration object that can - be used to change the head's properties. - - - - - - - - Disable a head. - - - - - - - Apply the new output configuration. - - In case the configuration is successfully applied, there is no guarantee - that the new output state matches completely the requested - configuration. For instance, a compositor might round the scale if it - doesn't support fractional scaling. - - After this request has been sent, the compositor must respond with an - succeeded, failed or cancelled event. Sending a request that isn't the - destructor is a protocol error. - - - - - - Test the new output configuration. The configuration won't be applied, - but will only be validated. - - Even if the compositor succeeds to test a configuration, applying it may - fail. - - After this request has been sent, the compositor must respond with an - succeeded, failed or cancelled event. Sending a request that isn't the - destructor is a protocol error. - - - - - - Sent after the compositor has successfully applied the changes or - tested them. - - Upon receiving this event, the client should destroy this object. - - If the current configuration has changed, events to describe the changes - will be sent followed by a wlr_output_manager.done event. - - - - - - Sent if the compositor rejects the changes or failed to apply them. The - compositor should revert any changes made by the apply request that - triggered this event. - - Upon receiving this event, the client should destroy this object. - - - - - - Sent if the compositor cancels the configuration because the state of an - output changed and the client has outdated information (e.g. after an - output has been hotplugged). - - The client can create a new configuration with a newer serial and try - again. - - Upon receiving this event, the client should destroy this object. - - - - - - Using this request a client can tell the compositor that it is not going - to use the configuration object anymore. Any changes to the outputs - that have not been applied will be discarded. - - This request also destroys wlr_output_configuration_head objects created - via this object. - - - - - - - This object is used by the client to update a single head's configuration. - - It is a protocol error to set the same property twice. - - - - - - - - - - - - - - This request sets the head's mode. - - - - - - - This request assigns a custom mode to the head. The size is given in - physical hardware units of the output device. If set to zero, the - refresh rate is unspecified. - - It is a protocol error to set both a mode and a custom mode. - - - - - - - - - This request sets the head's position in the global compositor space. - - - - - - - - This request sets the head's transform. - - - - - - - This request sets the head's scale. - - - - - - - - - This request enables/disables adaptive sync. Adaptive sync is also - known as Variable Refresh Rate or VRR. - - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-output-power-management-unstable-v1.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-output-power-management-unstable-v1.xml deleted file mode 100644 index 20dbb77..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-output-power-management-unstable-v1.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - Copyright © 2019 Purism SPC - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - - - This protocol allows clients to control power management modes - of outputs that are currently part of the compositor space. The - intent is to allow special clients like desktop shells to power - down outputs when the system is idle. - - To modify outputs not currently part of the compositor space see - wlr-output-management. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - - - - - This interface is a manager that allows creating per-output power - management mode controls. - - - - - Create an output power management mode control that can be used to - adjust the power management mode for a given output. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This object offers requests to set the power management mode of - an output. - - - - - - - - - - - - - - Set an output's power save mode to the given mode. The mode change - is effective immediately. If the output does not support the given - mode a failed event is sent. - - - - - - - Report the power management mode change of an output. - - The mode event is sent after an output changed its power - management mode. The reason can be a client using set_mode or the - compositor deciding to change an output's mode. - This event is also sent immediately when the object is created - so the client is informed about the current power management mode. - - - - - - - This event indicates that the output power management mode control - is no longer valid. This can happen for a number of reasons, - including: - - The output doesn't support power management - - Another client already has exclusive power management mode control - for this output - - The output disappeared - - Upon receiving this event, the client should destroy this object. - - - - - - Destroys the output power management mode control object. - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-screencopy-unstable-v1.xml b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-screencopy-unstable-v1.xml deleted file mode 100644 index 8b20661..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/proto/xml/wlr-screencopy-unstable-v1.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - - Copyright © 2018 Simon Ser - Copyright © 2019 Andri Yngvason - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - - - This protocol allows clients to ask the compositor to copy part of the - screen content to a client buffer. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - - Note! This protocol is deprecated and not intended for production use. - The ext-image-copy-capture-v1 protocol should be used instead. - - - - - This object is a manager which offers requests to start capturing from a - source. - - - - - Capture the next frame of an entire output. - - - - - - - - - Capture the next frame of an output's region. - - The region is given in output logical coordinates, see - xdg_output.logical_size. The region will be clipped to the output's - extents. - - - - - - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This object represents a single frame. - - When created, a series of buffer events will be sent, each representing a - supported buffer type. The "buffer_done" event is sent afterwards to - indicate that all supported buffer types have been enumerated. The client - will then be able to send a "copy" request. If the capture is successful, - the compositor will send a "flags" event followed by a "ready" event. - - For objects version 2 or lower, wl_shm buffers are always supported, ie. - the "buffer" event is guaranteed to be sent. - - If the capture failed, the "failed" event is sent. This can happen anytime - before the "ready" event. - - Once either a "ready" or a "failed" event is received, the client should - destroy the frame. - - - - - Provides information about wl_shm buffer parameters that need to be - used for this frame. This event is sent once after the frame is created - if wl_shm buffers are supported. - - - - - - - - - - Copy the frame to the supplied buffer. The buffer must have the - correct size, see zwlr_screencopy_frame_v1.buffer and - zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a - supported format. - - If the frame is successfully copied, "flags" and "ready" events are - sent. Otherwise, a "failed" event is sent. - - - - - - - - - - - - - - - - Provides flags about the frame. This event is sent once before the - "ready" event. - - - - - - - Called as soon as the frame is copied, indicating it is available - for reading. This event includes the time at which the presentation took place. - - The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, - each component being an unsigned 32-bit value. Whole seconds are in - tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, - and the additional fractional part in tv_nsec as nanoseconds. Hence, - for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part - may have an arbitrary offset at start. - - After receiving this event, the client should destroy the object. - - - - - - - - - This event indicates that the attempted frame copy has failed. - - After receiving this event, the client should destroy the object. - - - - - - Destroys the frame. This request can be sent at any time by the client. - - - - - - - Same as copy, except it waits until there is damage to copy. - - - - - - - This event is sent right before the ready event when copy_with_damage is - requested. It may be generated multiple times for each copy_with_damage - request. - - The arguments describe a box around an area that has changed since the - last copy request that was derived from the current screencopy manager - instance. - - The union of all regions received between the call to copy_with_damage - and a ready event is the total damage since the prior ready event. - - - - - - - - - - - Provides information about linux-dmabuf buffer parameters that need to - be used for this frame. This event is sent once after the frame is - created if linux-dmabuf buffers are supported. - - - - - - - - - This event is sent once after all buffer events have been sent. - - The client should proceed to create a buffer of one of the supported - types, and send a "copy" request. - - - - diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/compositor.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/compositor.go deleted file mode 100644 index 9bd5f0e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/compositor.go +++ /dev/null @@ -1,694 +0,0 @@ -package screenshot - -import ( - "encoding/json" - "fmt" - "os" - "os/exec" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/dwl_ipc" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management" - wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type Compositor int - -const ( - CompositorUnknown Compositor = iota - CompositorHyprland - CompositorSway - CompositorNiri - CompositorDWL - CompositorScroll - CompositorMiracle -) - -var detectedCompositor Compositor = -1 - -func DetectCompositor() Compositor { - if detectedCompositor >= 0 { - return detectedCompositor - } - - hyprlandSig := os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") - niriSocket := os.Getenv("NIRI_SOCKET") - swaySocket := os.Getenv("SWAYSOCK") - scrollSocket := os.Getenv("SCROLLSOCK") - miracleSocket := os.Getenv("MIRACLESOCK") - - switch { - case niriSocket != "": - if _, err := os.Stat(niriSocket); err == nil { - detectedCompositor = CompositorNiri - return detectedCompositor - } - case scrollSocket != "": - if _, err := os.Stat(scrollSocket); err == nil { - detectedCompositor = CompositorScroll - return detectedCompositor - } - case miracleSocket != "": - if _, err := os.Stat(miracleSocket); err == nil { - detectedCompositor = CompositorMiracle - return detectedCompositor - } - case swaySocket != "": - if _, err := os.Stat(swaySocket); err == nil { - detectedCompositor = CompositorSway - return detectedCompositor - } - case hyprlandSig != "": - detectedCompositor = CompositorHyprland - return detectedCompositor - } - - if detectDWLProtocol() { - detectedCompositor = CompositorDWL - return detectedCompositor - } - - detectedCompositor = CompositorUnknown - return detectedCompositor -} - -func detectDWLProtocol() bool { - display, err := client.Connect("") - if err != nil { - return false - } - ctx := display.Context() - defer ctx.Close() - - registry, err := display.GetRegistry() - if err != nil { - return false - } - - found := false - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - if e.Interface == dwl_ipc.ZdwlIpcManagerV2InterfaceName { - found = true - } - }) - - if err := wlhelpers.Roundtrip(display, ctx); err != nil { - return false - } - - return found -} - -func SetCompositorDWL() { - detectedCompositor = CompositorDWL -} - -type WindowGeometry struct { - X int32 - Y int32 - Width int32 - Height int32 - Output string - Scale float64 - OutputX int32 - OutputY int32 - OutputTransform int32 -} - -func GetActiveWindow() (*WindowGeometry, error) { - switch DetectCompositor() { - case CompositorHyprland: - return getHyprlandActiveWindow() - case CompositorDWL: - return getDWLActiveWindow() - default: - return nil, fmt.Errorf("window capture requires Hyprland or DWL") - } -} - -type hyprlandWindow struct { - At [2]int32 `json:"at"` - Size [2]int32 `json:"size"` -} - -func getHyprlandActiveWindow() (*WindowGeometry, error) { - output, err := exec.Command("hyprctl", "-j", "activewindow").Output() - if err != nil { - return nil, fmt.Errorf("hyprctl activewindow: %w", err) - } - - var win hyprlandWindow - if err := json.Unmarshal(output, &win); err != nil { - return nil, fmt.Errorf("parse activewindow: %w", err) - } - - if win.Size[0] <= 0 || win.Size[1] <= 0 { - return nil, fmt.Errorf("no active window") - } - - return &WindowGeometry{ - X: win.At[0], - Y: win.At[1], - Width: win.Size[0], - Height: win.Size[1], - }, nil -} - -type hyprlandMonitor struct { - Name string `json:"name"` - X int32 `json:"x"` - Y int32 `json:"y"` - Width int32 `json:"width"` - Height int32 `json:"height"` - Scale float64 `json:"scale"` - Focused bool `json:"focused"` -} - -func GetHyprlandMonitorScale(name string) float64 { - output, err := exec.Command("hyprctl", "-j", "monitors").Output() - if err != nil { - return 0 - } - - var monitors []hyprlandMonitor - if err := json.Unmarshal(output, &monitors); err != nil { - return 0 - } - - for _, m := range monitors { - if m.Name == name { - return m.Scale - } - } - return 0 -} - -func getHyprlandFocusedMonitor() string { - output, err := exec.Command("hyprctl", "-j", "monitors").Output() - if err != nil { - return "" - } - - var monitors []hyprlandMonitor - if err := json.Unmarshal(output, &monitors); err != nil { - return "" - } - - for _, m := range monitors { - if m.Focused { - return m.Name - } - } - return "" -} - -func GetHyprlandMonitorGeometry(name string) (x, y, w, h int32, ok bool) { - output, err := exec.Command("hyprctl", "-j", "monitors").Output() - if err != nil { - return 0, 0, 0, 0, false - } - - var monitors []hyprlandMonitor - if err := json.Unmarshal(output, &monitors); err != nil { - return 0, 0, 0, 0, false - } - - for _, m := range monitors { - if m.Name == name { - logicalW := int32(float64(m.Width) / m.Scale) - logicalH := int32(float64(m.Height) / m.Scale) - return m.X, m.Y, logicalW, logicalH, true - } - } - return 0, 0, 0, 0, false -} - -type swayWorkspace struct { - Output string `json:"output"` - Focused bool `json:"focused"` -} - -func getSwayFocusedMonitor() string { - output, err := exec.Command("swaymsg", "-t", "get_workspaces").Output() - if err != nil { - return "" - } - - var workspaces []swayWorkspace - if err := json.Unmarshal(output, &workspaces); err != nil { - return "" - } - - for _, ws := range workspaces { - if ws.Focused { - return ws.Output - } - } - return "" -} - -func getScrollFocusedMonitor() string { - output, err := exec.Command("scrollmsg", "-t", "get_workspaces").Output() - if err != nil { - return "" - } - - var workspaces []swayWorkspace - if err := json.Unmarshal(output, &workspaces); err != nil { - return "" - } - - for _, ws := range workspaces { - if ws.Focused { - return ws.Output - } - } - return "" -} - -func getMiracleFocusedMonitor() string { - output, err := exec.Command("miraclemsg", "-t", "get_workspaces").Output() - if err != nil { - return "" - } - - var workspaces []swayWorkspace - if err := json.Unmarshal(output, &workspaces); err != nil { - return "" - } - - for _, ws := range workspaces { - if ws.Focused { - return ws.Output - } - } - return "" -} - -type niriWorkspace struct { - Output string `json:"output"` - IsFocused bool `json:"is_focused"` -} - -func getNiriFocusedMonitor() string { - output, err := exec.Command("niri", "msg", "-j", "workspaces").Output() - if err != nil { - return "" - } - - var workspaces []niriWorkspace - if err := json.Unmarshal(output, &workspaces); err != nil { - return "" - } - - for _, ws := range workspaces { - if ws.IsFocused { - return ws.Output - } - } - return "" -} - -var dwlActiveOutput string - -func SetDWLActiveOutput(name string) { - dwlActiveOutput = name -} - -func getDWLFocusedMonitor() string { - if dwlActiveOutput != "" { - return dwlActiveOutput - } - return queryDWLActiveOutput() -} - -func queryDWLActiveOutput() string { - display, err := client.Connect("") - if err != nil { - return "" - } - ctx := display.Context() - defer ctx.Close() - - registry, err := display.GetRegistry() - if err != nil { - return "" - } - - var dwlManager *dwl_ipc.ZdwlIpcManagerV2 - outputs := make(map[uint32]*client.Output) - - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - switch e.Interface { - case dwl_ipc.ZdwlIpcManagerV2InterfaceName: - mgr := dwl_ipc.NewZdwlIpcManagerV2(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil { - dwlManager = mgr - } - case client.OutputInterfaceName: - out := client.NewOutput(ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := registry.Bind(e.Name, e.Interface, version, out); err == nil { - outputs[e.Name] = out - } - } - }) - - if err := wlhelpers.Roundtrip(display, ctx); err != nil { - return "" - } - - if dwlManager == nil || len(outputs) == 0 { - return "" - } - - outputNames := make(map[uint32]string) - for name, out := range outputs { - n := name - out.SetNameHandler(func(e client.OutputNameEvent) { - outputNames[n] = e.Name - }) - } - - if err := wlhelpers.Roundtrip(display, ctx); err != nil { - return "" - } - - type outputState struct { - name string - active bool - gotFrame bool - } - states := make(map[uint32]*outputState) - - for name, out := range outputs { - dwlOut, err := dwlManager.GetOutput(out) - if err != nil { - continue - } - state := &outputState{name: outputNames[name]} - states[name] = state - - dwlOut.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) { - state.active = e.Active != 0 - }) - dwlOut.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) { - state.gotFrame = true - }) - } - - allFramesReceived := func() bool { - for _, s := range states { - if !s.gotFrame { - return false - } - } - return true - } - - for !allFramesReceived() { - if err := ctx.Dispatch(); err != nil { - return "" - } - } - - for _, state := range states { - if state.active { - return state.name - } - } - - return "" -} - -func GetFocusedMonitor() string { - switch DetectCompositor() { - case CompositorHyprland: - return getHyprlandFocusedMonitor() - case CompositorSway: - return getSwayFocusedMonitor() - case CompositorScroll: - return getScrollFocusedMonitor() - case CompositorMiracle: - return getMiracleFocusedMonitor() - case CompositorNiri: - return getNiriFocusedMonitor() - case CompositorDWL: - return getDWLFocusedMonitor() - } - return "" -} - -type outputInfo struct { - x, y int32 - scale float64 - transform int32 -} - -func getAllOutputInfos() map[string]*outputInfo { - display, err := client.Connect("") - if err != nil { - return nil - } - ctx := display.Context() - defer ctx.Close() - - registry, err := display.GetRegistry() - if err != nil { - return nil - } - - var outputManager *wlr_output_management.ZwlrOutputManagerV1 - - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - if e.Interface == wlr_output_management.ZwlrOutputManagerV1InterfaceName { - mgr := wlr_output_management.NewZwlrOutputManagerV1(ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := registry.Bind(e.Name, e.Interface, version, mgr); err == nil { - outputManager = mgr - } - } - }) - - if err := wlhelpers.Roundtrip(display, ctx); err != nil { - return nil - } - - if outputManager == nil { - return nil - } - - type headState struct { - name string - x, y int32 - scale float64 - transform int32 - } - heads := make(map[*wlr_output_management.ZwlrOutputHeadV1]*headState) - done := false - - outputManager.SetHeadHandler(func(e wlr_output_management.ZwlrOutputManagerV1HeadEvent) { - state := &headState{} - heads[e.Head] = state - e.Head.SetNameHandler(func(ne wlr_output_management.ZwlrOutputHeadV1NameEvent) { - state.name = ne.Name - }) - e.Head.SetPositionHandler(func(pe wlr_output_management.ZwlrOutputHeadV1PositionEvent) { - state.x = pe.X - state.y = pe.Y - }) - e.Head.SetScaleHandler(func(se wlr_output_management.ZwlrOutputHeadV1ScaleEvent) { - state.scale = se.Scale - }) - e.Head.SetTransformHandler(func(te wlr_output_management.ZwlrOutputHeadV1TransformEvent) { - state.transform = te.Transform - }) - }) - outputManager.SetDoneHandler(func(e wlr_output_management.ZwlrOutputManagerV1DoneEvent) { - done = true - }) - - for !done { - if err := ctx.Dispatch(); err != nil { - return nil - } - } - - result := make(map[string]*outputInfo, len(heads)) - for _, state := range heads { - if state.name == "" { - continue - } - result[state.name] = &outputInfo{ - x: state.x, - y: state.y, - scale: state.scale, - transform: state.transform, - } - } - return result -} - -func getOutputInfo(outputName string) (*outputInfo, bool) { - infos := getAllOutputInfos() - if infos == nil { - return nil, false - } - info, ok := infos[outputName] - return info, ok -} - -func getDWLActiveWindow() (*WindowGeometry, error) { - display, err := client.Connect("") - if err != nil { - return nil, fmt.Errorf("connect: %w", err) - } - ctx := display.Context() - defer ctx.Close() - - registry, err := display.GetRegistry() - if err != nil { - return nil, fmt.Errorf("get registry: %w", err) - } - - var dwlManager *dwl_ipc.ZdwlIpcManagerV2 - outputs := make(map[uint32]*client.Output) - - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - switch e.Interface { - case dwl_ipc.ZdwlIpcManagerV2InterfaceName: - mgr := dwl_ipc.NewZdwlIpcManagerV2(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil { - dwlManager = mgr - } - case client.OutputInterfaceName: - out := client.NewOutput(ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := registry.Bind(e.Name, e.Interface, version, out); err == nil { - outputs[e.Name] = out - } - } - }) - - if err := wlhelpers.Roundtrip(display, ctx); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - if dwlManager == nil { - return nil, fmt.Errorf("dwl_ipc_manager not available") - } - - if len(outputs) == 0 { - return nil, fmt.Errorf("no outputs found") - } - - outputNames := make(map[uint32]string) - for name, out := range outputs { - n := name - out.SetNameHandler(func(e client.OutputNameEvent) { - outputNames[n] = e.Name - }) - } - - if err := wlhelpers.Roundtrip(display, ctx); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - type dwlOutputState struct { - output *dwl_ipc.ZdwlIpcOutputV2 - name string - active bool - x, y int32 - w, h int32 - scalefactor uint32 - gotFrame bool - } - - dwlOutputs := make(map[uint32]*dwlOutputState) - for name, out := range outputs { - dwlOut, err := dwlManager.GetOutput(out) - if err != nil { - continue - } - state := &dwlOutputState{output: dwlOut, name: outputNames[name]} - dwlOutputs[name] = state - - dwlOut.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) { - state.active = e.Active != 0 - }) - dwlOut.SetXHandler(func(e dwl_ipc.ZdwlIpcOutputV2XEvent) { - state.x = e.X - }) - dwlOut.SetYHandler(func(e dwl_ipc.ZdwlIpcOutputV2YEvent) { - state.y = e.Y - }) - dwlOut.SetWidthHandler(func(e dwl_ipc.ZdwlIpcOutputV2WidthEvent) { - state.w = e.Width - }) - dwlOut.SetHeightHandler(func(e dwl_ipc.ZdwlIpcOutputV2HeightEvent) { - state.h = e.Height - }) - dwlOut.SetScalefactorHandler(func(e dwl_ipc.ZdwlIpcOutputV2ScalefactorEvent) { - state.scalefactor = e.Scalefactor - }) - dwlOut.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) { - state.gotFrame = true - }) - } - - allFramesReceived := func() bool { - for _, s := range dwlOutputs { - if !s.gotFrame { - return false - } - } - return true - } - - for !allFramesReceived() { - if err := ctx.Dispatch(); err != nil { - return nil, fmt.Errorf("dispatch: %w", err) - } - } - - for _, state := range dwlOutputs { - if !state.active { - continue - } - if state.w <= 0 || state.h <= 0 { - return nil, fmt.Errorf("no active window") - } - scale := float64(state.scalefactor) / 100.0 - if scale <= 0 { - scale = 1.0 - } - - geom := &WindowGeometry{ - X: state.x, - Y: state.y, - Width: state.w, - Height: state.h, - Output: state.name, - Scale: scale, - } - - if info, ok := getOutputInfo(state.name); ok { - geom.OutputX = info.x - geom.OutputY = info.y - geom.OutputTransform = info.transform - } - - return geom, nil - } - - return nil, fmt.Errorf("no active output found") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/encode.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/encode.go deleted file mode 100644 index 8b40dca..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/encode.go +++ /dev/null @@ -1,172 +0,0 @@ -package screenshot - -import ( - "bufio" - "fmt" - "image" - "image/jpeg" - "image/png" - "io" - "os" - "path/filepath" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -func BufferToImage(buf *ShmBuffer) *image.RGBA { - return BufferToImageWithFormat(buf, uint32(FormatARGB8888)) -} - -func BufferToImageWithFormat(buf *ShmBuffer, format uint32) *image.RGBA { - img := image.NewRGBA(image.Rect(0, 0, buf.Width, buf.Height)) - data := buf.Data() - - var swapRB bool - switch format { - case uint32(FormatABGR8888), uint32(FormatXBGR8888): - swapRB = false - default: - swapRB = true - } - - for y := 0; y < buf.Height; y++ { - srcOff := y * buf.Stride - dstOff := y * img.Stride - for x := 0; x < buf.Width; x++ { - si := srcOff + x*4 - di := dstOff + x*4 - if si+3 >= len(data) || di+3 >= len(img.Pix) { - continue - } - if swapRB { - img.Pix[di+0] = data[si+2] - img.Pix[di+1] = data[si+1] - img.Pix[di+2] = data[si+0] - } else { - img.Pix[di+0] = data[si+0] - img.Pix[di+1] = data[si+1] - img.Pix[di+2] = data[si+2] - } - img.Pix[di+3] = 255 - } - } - return img -} - -func EncodePNG(w io.Writer, img image.Image) error { - enc := png.Encoder{CompressionLevel: png.BestSpeed} - return enc.Encode(w, img) -} - -func EncodeJPEG(w io.Writer, img image.Image, quality int) error { - return jpeg.Encode(w, img, &jpeg.Options{Quality: quality}) -} - -func EncodePPM(w io.Writer, img *image.RGBA) error { - bw := bufio.NewWriter(w) - bounds := img.Bounds() - if _, err := fmt.Fprintf(bw, "P6\n%d %d\n255\n", bounds.Dx(), bounds.Dy()); err != nil { - return err - } - for y := bounds.Min.Y; y < bounds.Max.Y; y++ { - for x := bounds.Min.X; x < bounds.Max.X; x++ { - off := (y-bounds.Min.Y)*img.Stride + (x-bounds.Min.X)*4 - if err := bw.WriteByte(img.Pix[off+0]); err != nil { - return err - } - if err := bw.WriteByte(img.Pix[off+1]); err != nil { - return err - } - if err := bw.WriteByte(img.Pix[off+2]); err != nil { - return err - } - } - } - return bw.Flush() -} - -func GenerateFilename(format Format) string { - t := time.Now() - ext := "png" - switch format { - case FormatJPEG: - ext = "jpg" - case FormatPPM: - ext = "ppm" - } - return fmt.Sprintf("screenshot_%s.%s", t.Format("2006-01-02_15-04-05"), ext) -} - -func GetOutputDir() string { - if dir := os.Getenv("DMS_SCREENSHOT_DIR"); dir != "" { - return dir - } - - if xdgPics := getXDGPicturesDir(); xdgPics != "" { - screenshotDir := filepath.Join(xdgPics, "Screenshots") - if err := os.MkdirAll(screenshotDir, 0o755); err == nil { - return screenshotDir - } - return xdgPics - } - - if home := os.Getenv("HOME"); home != "" { - return home - } - return "." -} - -func getXDGPicturesDir() string { - userConfigDir, err := os.UserConfigDir() - if err != nil { - log.Error("failed to get user config dir", "err", err) - return "" - } - userDirsFile := filepath.Join(userConfigDir, "user-dirs.dirs") - data, err := os.ReadFile(userDirsFile) - if err != nil { - return "" - } - - for _, line := range strings.Split(string(data), "\n") { - if len(line) == 0 || line[0] == '#' { - continue - } - const prefix = "XDG_PICTURES_DIR=" - if !strings.HasPrefix(line, prefix) { - continue - } - path := strings.Trim(line[len(prefix):], "\"") - expanded, err := utils.ExpandPath(path) - if err != nil { - return "" - } - return expanded - } - return "" -} - -func WriteToFile(buf *ShmBuffer, path string, format Format, quality int) error { - return WriteToFileWithFormat(buf, path, format, quality, uint32(FormatARGB8888)) -} - -func WriteToFileWithFormat(buf *ShmBuffer, path string, format Format, quality int, pixelFormat uint32) error { - f, err := os.Create(path) - if err != nil { - return err - } - defer f.Close() - - img := BufferToImageWithFormat(buf, pixelFormat) - switch format { - case FormatJPEG: - return EncodeJPEG(f, img, quality) - case FormatPPM: - return EncodePPM(f, img) - default: - return EncodePNG(f, img) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/notify.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/notify.go deleted file mode 100644 index 9633b42..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/notify.go +++ /dev/null @@ -1,180 +0,0 @@ -package screenshot - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strconv" - "syscall" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - notifyDest = "org.freedesktop.Notifications" - notifyPath = "/org/freedesktop/Notifications" - notifyInterface = "org.freedesktop.Notifications" -) - -type NotifyResult struct { - FilePath string - Clipboard bool - ImageData []byte - Width int - Height int -} - -func SendNotification(result NotifyResult) { - conn, err := dbus.SessionBus() - if err != nil { - log.Debug("dbus session failed", "err", err) - return - } - - var actions []string - if result.FilePath != "" { - actions = []string{"default", "Open"} - } - - hints := map[string]dbus.Variant{} - if len(result.ImageData) > 0 && result.Width > 0 && result.Height > 0 { - rowstride := result.Width * 3 - hints["image_data"] = dbus.MakeVariant(struct { - Width int32 - Height int32 - Rowstride int32 - HasAlpha bool - BitsPerSample int32 - Channels int32 - Data []byte - }{ - Width: int32(result.Width), - Height: int32(result.Height), - Rowstride: int32(rowstride), - HasAlpha: false, - BitsPerSample: 8, - Channels: 3, - Data: result.ImageData, - }) - } else if result.FilePath != "" { - hints["image_path"] = dbus.MakeVariant(result.FilePath) - } - - summary := "Screenshot captured" - body := "" - if result.Clipboard && result.FilePath != "" { - body = fmt.Sprintf("Copied to clipboard\n%s", filepath.Base(result.FilePath)) - } else if result.Clipboard { - body = "Copied to clipboard" - } else if result.FilePath != "" { - body = filepath.Base(result.FilePath) - } - - obj := conn.Object(notifyDest, notifyPath) - call := obj.Call( - notifyInterface+".Notify", - 0, - "DMS", - uint32(0), - "", - summary, - body, - actions, - hints, - int32(5000), - ) - - if call.Err != nil { - log.Debug("notify call failed", "err", call.Err) - return - } - - var notificationID uint32 - if err := call.Store(¬ificationID); err != nil { - log.Debug("failed to get notification id", "err", err) - return - } - - if len(actions) == 0 || result.FilePath == "" { - return - } - - spawnActionListener(notificationID, result.FilePath) -} - -func spawnActionListener(notificationID uint32, filePath string) { - exe, err := os.Executable() - if err != nil { - log.Debug("failed to get executable", "err", err) - return - } - - cmd := exec.Command(exe, "notify-action", fmt.Sprintf("%d", notificationID), filePath) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - cmd.Start() -} - -func RunNotifyActionListener(args []string) { - if len(args) < 2 { - return - } - - notificationID, err := strconv.ParseUint(args[0], 10, 32) - if err != nil { - return - } - - filePath := args[1] - - conn, err := dbus.SessionBus() - if err != nil { - return - } - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(notifyPath), - dbus.WithMatchInterface(notifyInterface), - ); err != nil { - return - } - - signals := make(chan *dbus.Signal, 10) - conn.Signal(signals) - - for sig := range signals { - switch sig.Name { - case notifyInterface + ".ActionInvoked": - if len(sig.Body) < 2 { - continue - } - id, ok := sig.Body[0].(uint32) - if !ok || id != uint32(notificationID) { - continue - } - openFile(filePath) - return - - case notifyInterface + ".NotificationClosed": - if len(sig.Body) < 1 { - continue - } - id, ok := sig.Body[0].(uint32) - if !ok || id != uint32(notificationID) { - continue - } - return - } - } -} - -func openFile(filePath string) { - cmd := exec.Command("xdg-open", filePath) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setsid: true, - } - cmd.Start() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region.go deleted file mode 100644 index f296434..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region.go +++ /dev/null @@ -1,859 +0,0 @@ -package screenshot - -import ( - "fmt" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/keyboard_shortcuts_inhibit" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_layer_shell" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_screencopy" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wp_viewporter" - wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type SelectionState struct { - hasSelection bool // There's a selection to display (pre-loaded or user-drawn) - dragging bool // User is actively drawing a new selection - surface *OutputSurface // Surface where selection was made - // Surface-local logical coordinates (from pointer events) - anchorX float64 - anchorY float64 - currentX float64 - currentY float64 -} - -type RenderSlot struct { - shm *ShmBuffer - pool *client.ShmPool - wlBuf *client.Buffer - busy bool -} - -type OutputSurface struct { - output *WaylandOutput - wlSurface *client.Surface - layerSurf *wlr_layer_shell.ZwlrLayerSurfaceV1 - viewport *wp_viewporter.WpViewport - screenBuf *ShmBuffer - screenBufNoCursor *ShmBuffer - screenFormat uint32 - logicalW int - logicalH int - configured bool - yInverted bool - - // Triple-buffered render slots - slots [3]*RenderSlot - slotsReady bool -} - -type PreCapture struct { - screenBuf *ShmBuffer - screenBufNoCursor *ShmBuffer - format uint32 - yInverted bool -} - -type RegionSelector struct { - screenshoter *Screenshoter - - display *client.Display - registry *client.Registry - ctx *client.Context - - compositor *client.Compositor - shm *client.Shm - seat *client.Seat - pointer *client.Pointer - keyboard *client.Keyboard - layerShell *wlr_layer_shell.ZwlrLayerShellV1 - screencopy *wlr_screencopy.ZwlrScreencopyManagerV1 - viewporter *wp_viewporter.WpViewporter - - shortcutsInhibitMgr *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1 - shortcutsInhibitor *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1 - - outputs map[uint32]*WaylandOutput - outputsMu sync.Mutex - preCapture map[*WaylandOutput]*PreCapture - - surfaces []*OutputSurface - activeSurface *OutputSurface - - // Cursor surface for crosshair - cursorSurface *client.Surface - cursorBuffer *ShmBuffer - cursorWlBuf *client.Buffer - cursorPool *client.ShmPool - - selection SelectionState - pointerX float64 - pointerY float64 - preSelect Region - showCapturedCursor bool - shiftHeld bool - - running bool - cancelled bool - result Region - - capturedBuffer *ShmBuffer - capturedRegion Region -} - -func NewRegionSelector(s *Screenshoter) *RegionSelector { - return &RegionSelector{ - screenshoter: s, - outputs: make(map[uint32]*WaylandOutput), - preCapture: make(map[*WaylandOutput]*PreCapture), - showCapturedCursor: s.config.Cursor == CursorOn, - } -} - -func (r *RegionSelector) Run() (*CaptureResult, bool, error) { - if r.screenshoter != nil && r.screenshoter.config.Reset { - r.preSelect = Region{} - } else { - r.preSelect = GetLastRegion() - } - - if err := r.connect(); err != nil { - return nil, false, fmt.Errorf("wayland connect: %w", err) - } - defer r.cleanup() - - if err := r.setupRegistry(); err != nil { - return nil, false, fmt.Errorf("registry setup: %w", err) - } - - if err := r.roundtrip(); err != nil { - return nil, false, fmt.Errorf("roundtrip after registry: %w", err) - } - - switch { - case r.screencopy == nil: - return nil, false, fmt.Errorf("compositor does not support wlr-screencopy-unstable-v1") - case r.layerShell == nil: - return nil, false, fmt.Errorf("compositor does not support wlr-layer-shell-unstable-v1") - case r.seat == nil: - return nil, false, fmt.Errorf("no seat available") - case r.compositor == nil: - return nil, false, fmt.Errorf("compositor not available") - case r.shm == nil: - return nil, false, fmt.Errorf("wl_shm not available") - case len(r.outputs) == 0: - return nil, false, fmt.Errorf("no outputs available") - } - - if err := r.roundtrip(); err != nil { - return nil, false, fmt.Errorf("roundtrip after protocol check: %w", err) - } - - if err := r.preCaptureAllOutputs(); err != nil { - return nil, false, fmt.Errorf("pre-capture: %w", err) - } - - if err := r.createSurfaces(); err != nil { - return nil, false, fmt.Errorf("create surfaces: %w", err) - } - - _ = r.createCursor() - - if err := r.roundtrip(); err != nil { - return nil, false, fmt.Errorf("roundtrip after surfaces: %w", err) - } - - r.running = true - for r.running { - if err := r.ctx.Dispatch(); err != nil { - return nil, false, fmt.Errorf("dispatch: %w", err) - } - } - - if r.cancelled || r.capturedBuffer == nil { - return nil, r.cancelled, nil - } - - yInverted := false - var format uint32 - if r.selection.surface != nil { - yInverted = r.selection.surface.yInverted - format = r.selection.surface.screenFormat - } - - return &CaptureResult{ - Buffer: r.capturedBuffer, - Region: r.result, - YInverted: yInverted, - Format: format, - }, false, nil -} - -func (r *RegionSelector) connect() error { - display, err := client.Connect("") - if err != nil { - return err - } - r.display = display - r.ctx = display.Context() - return nil -} - -func (r *RegionSelector) roundtrip() error { - return wlhelpers.Roundtrip(r.display, r.ctx) -} - -func (r *RegionSelector) setupRegistry() error { - registry, err := r.display.GetRegistry() - if err != nil { - return err - } - r.registry = registry - - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - r.handleGlobal(e) - }) - - registry.SetGlobalRemoveHandler(func(e client.RegistryGlobalRemoveEvent) { - r.outputsMu.Lock() - delete(r.outputs, e.Name) - r.outputsMu.Unlock() - }) - - return nil -} - -func (r *RegionSelector) handleGlobal(e client.RegistryGlobalEvent) { - switch e.Interface { - case client.CompositorInterfaceName: - comp := client.NewCompositor(r.ctx) - if err := r.registry.Bind(e.Name, e.Interface, e.Version, comp); err == nil { - r.compositor = comp - } - - case client.ShmInterfaceName: - shm := client.NewShm(r.ctx) - if err := r.registry.Bind(e.Name, e.Interface, e.Version, shm); err == nil { - r.shm = shm - } - - case client.SeatInterfaceName: - seat := client.NewSeat(r.ctx) - if err := r.registry.Bind(e.Name, e.Interface, e.Version, seat); err == nil { - r.seat = seat - r.setupInput() - } - - case client.OutputInterfaceName: - output := client.NewOutput(r.ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := r.registry.Bind(e.Name, e.Interface, version, output); err == nil { - r.outputsMu.Lock() - r.outputs[e.Name] = &WaylandOutput{ - wlOutput: output, - globalName: e.Name, - scale: 1, - fractionalScale: 1.0, - } - r.outputsMu.Unlock() - r.setupOutputHandlers(e.Name, output) - } - - case wlr_layer_shell.ZwlrLayerShellV1InterfaceName: - ls := wlr_layer_shell.NewZwlrLayerShellV1(r.ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := r.registry.Bind(e.Name, e.Interface, version, ls); err == nil { - r.layerShell = ls - } - - case wlr_screencopy.ZwlrScreencopyManagerV1InterfaceName: - sc := wlr_screencopy.NewZwlrScreencopyManagerV1(r.ctx) - version := e.Version - if version > 3 { - version = 3 - } - if err := r.registry.Bind(e.Name, e.Interface, version, sc); err == nil { - r.screencopy = sc - } - - case wp_viewporter.WpViewporterInterfaceName: - vp := wp_viewporter.NewWpViewporter(r.ctx) - if err := r.registry.Bind(e.Name, e.Interface, e.Version, vp); err == nil { - r.viewporter = vp - } - - case keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1InterfaceName: - mgr := keyboard_shortcuts_inhibit.NewZwpKeyboardShortcutsInhibitManagerV1(r.ctx) - if err := r.registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil { - r.shortcutsInhibitMgr = mgr - } - } -} - -func (r *RegionSelector) setupOutputHandlers(name uint32, output *client.Output) { - output.SetGeometryHandler(func(e client.OutputGeometryEvent) { - r.outputsMu.Lock() - if o, ok := r.outputs[name]; ok { - o.x = e.X - o.y = e.Y - o.transform = int32(e.Transform) - } - r.outputsMu.Unlock() - }) - - output.SetModeHandler(func(e client.OutputModeEvent) { - if e.Flags&uint32(client.OutputModeCurrent) == 0 { - return - } - r.outputsMu.Lock() - if o, ok := r.outputs[name]; ok { - o.width = e.Width - o.height = e.Height - } - r.outputsMu.Unlock() - }) - - output.SetScaleHandler(func(e client.OutputScaleEvent) { - r.outputsMu.Lock() - if o, ok := r.outputs[name]; ok { - o.scale = e.Factor - o.fractionalScale = float64(e.Factor) - } - r.outputsMu.Unlock() - }) - - output.SetNameHandler(func(e client.OutputNameEvent) { - r.outputsMu.Lock() - if o, ok := r.outputs[name]; ok { - o.name = e.Name - } - r.outputsMu.Unlock() - }) -} - -func (r *RegionSelector) preCaptureAllOutputs() error { - r.outputsMu.Lock() - outputs := make([]*WaylandOutput, 0, len(r.outputs)) - for _, o := range r.outputs { - outputs = append(outputs, o) - } - r.outputsMu.Unlock() - - pending := len(outputs) * 2 - done := make(chan struct{}, pending) - - for _, output := range outputs { - pc := &PreCapture{} - r.preCapture[output] = pc - - r.preCaptureOutput(output, pc, true, func() { done <- struct{}{} }) - r.preCaptureOutput(output, pc, false, func() { done <- struct{}{} }) - } - - for i := 0; i < pending; i++ { - if err := r.ctx.Dispatch(); err != nil { - return err - } - select { - case <-done: - default: - i-- - } - } - return nil -} - -func (r *RegionSelector) preCaptureOutput(output *WaylandOutput, pc *PreCapture, withCursor bool, onReady func()) { - cursor := int32(0) - if withCursor { - cursor = 1 - } - - frame, err := r.screencopy.CaptureOutput(cursor, output.wlOutput) - if err != nil { - log.Error("screencopy capture failed", "err", err) - onReady() - return - } - - var capturedBuf *ShmBuffer - - var capturedFormat PixelFormat - frame.SetBufferHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferEvent) { - capturedFormat = PixelFormat(e.Format) - bpp := capturedFormat.BytesPerPixel() - if int(e.Stride) < int(e.Width)*bpp { - log.Error("invalid stride from compositor", "stride", e.Stride, "width", e.Width, "bpp", bpp) - return - } - buf, err := CreateShmBuffer(int(e.Width), int(e.Height), int(e.Stride)) - if err != nil { - log.Error("create screen buffer failed", "err", err) - return - } - - capturedBuf = buf - buf.Format = capturedFormat - - pool, err := r.shm.CreatePool(buf.Fd(), int32(buf.Size())) - if err != nil { - log.Error("create shm pool failed", "err", err) - return - } - - wlBuf, err := pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), e.Format) - if err != nil { - log.Error("create wl_buffer failed", "err", err) - pool.Destroy() - return - } - - if err := frame.Copy(wlBuf); err != nil { - log.Error("frame copy failed", "err", err) - } - pool.Destroy() - }) - - frame.SetFlagsHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FlagsEvent) { - if withCursor { - pc.yInverted = (e.Flags & 1) != 0 - } - }) - - frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) { - frame.Destroy() - - if capturedBuf == nil { - onReady() - return - } - - if capturedFormat.Is24Bit() { - converted, newFormat, err := capturedBuf.ConvertTo32Bit(capturedFormat) - if err != nil { - log.Error("convert 24-bit to 32-bit failed", "err", err) - } else if converted != capturedBuf { - capturedBuf.Close() - capturedBuf = converted - capturedFormat = newFormat - } - } - - pc.format = uint32(capturedFormat) - - if pc.yInverted { - capturedBuf.FlipVertical() - pc.yInverted = false - } - - if output.transform != TransformNormal { - invTransform := InverseTransform(output.transform) - transformed, err := capturedBuf.ApplyTransform(invTransform) - if err != nil { - log.Error("apply transform failed", "err", err) - } else if transformed != capturedBuf { - capturedBuf.Close() - capturedBuf = transformed - } - } - - if withCursor { - pc.screenBuf = capturedBuf - } else { - pc.screenBufNoCursor = capturedBuf - } - - onReady() - }) - - frame.SetFailedHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FailedEvent) { - log.Error("screencopy failed") - frame.Destroy() - onReady() - }) -} - -func (r *RegionSelector) createSurfaces() error { - r.outputsMu.Lock() - outputs := make([]*WaylandOutput, 0, len(r.outputs)) - for _, o := range r.outputs { - outputs = append(outputs, o) - } - r.outputsMu.Unlock() - - for _, output := range outputs { - os, err := r.createOutputSurface(output) - if err != nil { - return fmt.Errorf("output %s: %w", output.name, err) - } - r.surfaces = append(r.surfaces, os) - } - - return nil -} - -func (r *RegionSelector) createCursor() error { - const size = 24 - const hotspot = size / 2 - - surface, err := r.compositor.CreateSurface() - if err != nil { - return fmt.Errorf("create cursor surface: %w", err) - } - r.cursorSurface = surface - - buf, err := CreateShmBuffer(size, size, size*4) - if err != nil { - return fmt.Errorf("create cursor buffer: %w", err) - } - r.cursorBuffer = buf - - // Draw crosshair - data := buf.Data() - for y := 0; y < size; y++ { - for x := 0; x < size; x++ { - off := (y*size + x) * 4 - // Vertical line - if x >= hotspot-1 && x <= hotspot && y >= 2 && y < size-2 { - data[off+0] = 255 // B - data[off+1] = 255 // G - data[off+2] = 255 // R - data[off+3] = 255 // A - continue - } - // Horizontal line - if y >= hotspot-1 && y <= hotspot && x >= 2 && x < size-2 { - data[off+0] = 255 - data[off+1] = 255 - data[off+2] = 255 - data[off+3] = 255 - continue - } - // Transparent - data[off+0] = 0 - data[off+1] = 0 - data[off+2] = 0 - data[off+3] = 0 - } - } - - pool, err := r.shm.CreatePool(buf.Fd(), int32(buf.Size())) - if err != nil { - return fmt.Errorf("create cursor pool: %w", err) - } - r.cursorPool = pool - - wlBuf, err := pool.CreateBuffer(0, size, size, size*4, uint32(FormatARGB8888)) - if err != nil { - return fmt.Errorf("create cursor wl_buffer: %w", err) - } - r.cursorWlBuf = wlBuf - - if err := surface.Attach(wlBuf, 0, 0); err != nil { - return fmt.Errorf("attach cursor: %w", err) - } - if err := surface.Damage(0, 0, size, size); err != nil { - return fmt.Errorf("damage cursor: %w", err) - } - if err := surface.Commit(); err != nil { - return fmt.Errorf("commit cursor: %w", err) - } - - return nil -} - -func (r *RegionSelector) createOutputSurface(output *WaylandOutput) (*OutputSurface, error) { - surface, err := r.compositor.CreateSurface() - if err != nil { - return nil, fmt.Errorf("create surface: %w", err) - } - - layerSurf, err := r.layerShell.GetLayerSurface( - surface, - output.wlOutput, - uint32(wlr_layer_shell.ZwlrLayerShellV1LayerOverlay), - "dms-screenshot", - ) - if err != nil { - return nil, fmt.Errorf("get layer surface: %w", err) - } - - os := &OutputSurface{ - output: output, - wlSurface: surface, - layerSurf: layerSurf, - } - - if r.viewporter != nil { - vp, err := r.viewporter.GetViewport(surface) - if err == nil { - os.viewport = vp - } - } - - if err := layerSurf.SetAnchor( - uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorTop) | - uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorBottom) | - uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorLeft) | - uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorRight), - ); err != nil { - return nil, fmt.Errorf("set anchor: %w", err) - } - if err := layerSurf.SetExclusiveZone(-1); err != nil { - return nil, fmt.Errorf("set exclusive zone: %w", err) - } - if err := layerSurf.SetKeyboardInteractivity(uint32(wlr_layer_shell.ZwlrLayerSurfaceV1KeyboardInteractivityExclusive)); err != nil { - return nil, fmt.Errorf("set keyboard interactivity: %w", err) - } - - layerSurf.SetConfigureHandler(func(e wlr_layer_shell.ZwlrLayerSurfaceV1ConfigureEvent) { - if err := layerSurf.AckConfigure(e.Serial); err != nil { - log.Error("ack configure failed", "err", err) - return - } - os.logicalW = int(e.Width) - os.logicalH = int(e.Height) - os.configured = true - r.captureForSurface(os) - r.ensureShortcutsInhibitor(os) - }) - - layerSurf.SetClosedHandler(func(e wlr_layer_shell.ZwlrLayerSurfaceV1ClosedEvent) { - r.running = false - r.cancelled = true - }) - - if err := surface.Commit(); err != nil { - return nil, fmt.Errorf("surface commit: %w", err) - } - - return os, nil -} - -func (r *RegionSelector) ensureShortcutsInhibitor(os *OutputSurface) { - if r.shortcutsInhibitMgr == nil || r.seat == nil || r.shortcutsInhibitor != nil { - return - } - inhibitor, err := r.shortcutsInhibitMgr.InhibitShortcuts(os.wlSurface, r.seat) - if err == nil { - r.shortcutsInhibitor = inhibitor - } -} - -func (r *RegionSelector) captureForSurface(os *OutputSurface) { - pc := r.preCapture[os.output] - if pc == nil { - return - } - - os.screenBuf = pc.screenBuf - os.screenBufNoCursor = pc.screenBufNoCursor - os.screenFormat = pc.format - os.yInverted = pc.yInverted - - if os.logicalW > 0 && os.screenBuf != nil { - os.output.fractionalScale = float64(os.screenBuf.Width) / float64(os.logicalW) - } - - r.initRenderBuffer(os) - r.applyPreSelection(os) - r.redrawSurface(os) -} - -func (r *RegionSelector) initRenderBuffer(os *OutputSurface) { - if os.screenBuf == nil { - return - } - - for i := 0; i < 3; i++ { - slot := &RenderSlot{} - - buf, err := CreateShmBuffer(os.screenBuf.Width, os.screenBuf.Height, os.screenBuf.Stride) - if err != nil { - log.Error("create render slot buffer failed", "err", err) - return - } - slot.shm = buf - - pool, err := r.shm.CreatePool(buf.Fd(), int32(buf.Size())) - if err != nil { - log.Error("create render slot pool failed", "err", err) - buf.Close() - return - } - slot.pool = pool - - wlBuf, err := pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), os.screenFormat) - if err != nil { - log.Error("create render slot wl_buffer failed", "err", err) - pool.Destroy() - buf.Close() - return - } - slot.wlBuf = wlBuf - - slotRef := slot - wlBuf.SetReleaseHandler(func(e client.BufferReleaseEvent) { - slotRef.busy = false - }) - - os.slots[i] = slot - } - os.slotsReady = true -} - -func (os *OutputSurface) acquireFreeSlot() *RenderSlot { - for _, slot := range os.slots { - if slot != nil && !slot.busy { - return slot - } - } - return nil -} - -func (r *RegionSelector) applyPreSelection(os *OutputSurface) { - if r.preSelect.IsEmpty() || os.screenBuf == nil || r.selection.hasSelection { - return - } - - if r.preSelect.Output != "" && r.preSelect.Output != os.output.name { - return - } - - scaleX := float64(os.logicalW) / float64(os.screenBuf.Width) - scaleY := float64(os.logicalH) / float64(os.screenBuf.Height) - - x1 := float64(r.preSelect.X-os.output.x) * scaleX - y1 := float64(r.preSelect.Y-os.output.y) * scaleY - x2 := float64(r.preSelect.X-os.output.x+r.preSelect.Width) * scaleX - y2 := float64(r.preSelect.Y-os.output.y+r.preSelect.Height) * scaleY - - r.selection.hasSelection = true - r.selection.dragging = false - r.selection.surface = os - r.selection.anchorX = x1 - r.selection.anchorY = y1 - r.selection.currentX = x2 - r.selection.currentY = y2 - r.activeSurface = os -} - -func (r *RegionSelector) getSourceBuffer(os *OutputSurface) *ShmBuffer { - if !r.showCapturedCursor && os.screenBufNoCursor != nil { - return os.screenBufNoCursor - } - return os.screenBuf -} - -func (r *RegionSelector) redrawSurface(os *OutputSurface) { - srcBuf := r.getSourceBuffer(os) - if srcBuf == nil || !os.slotsReady { - return - } - - slot := os.acquireFreeSlot() - if slot == nil { - return - } - - slot.shm.CopyFrom(srcBuf) - - // Draw overlay (dimming + selection) into this slot - r.drawOverlay(os, slot.shm) - - if os.viewport != nil { - _ = os.wlSurface.SetBufferScale(1) - _ = os.viewport.SetSource(0, 0, float64(slot.shm.Width), float64(slot.shm.Height)) - _ = os.viewport.SetDestination(int32(os.logicalW), int32(os.logicalH)) - } else { - bufferScale := os.output.scale - if bufferScale <= 0 { - bufferScale = 1 - } - _ = os.wlSurface.SetBufferScale(bufferScale) - } - - _ = os.wlSurface.Attach(slot.wlBuf, 0, 0) - _ = os.wlSurface.Damage(0, 0, int32(os.logicalW), int32(os.logicalH)) - _ = os.wlSurface.Commit() - - // Mark this slot as busy until compositor releases it - slot.busy = true -} - -func (r *RegionSelector) cleanup() { - if r.cursorWlBuf != nil { - r.cursorWlBuf.Destroy() - } - if r.cursorPool != nil { - r.cursorPool.Destroy() - } - if r.cursorSurface != nil { - r.cursorSurface.Destroy() - } - if r.cursorBuffer != nil { - r.cursorBuffer.Close() - } - - for _, os := range r.surfaces { - for _, slot := range os.slots { - if slot == nil { - continue - } - if slot.wlBuf != nil { - slot.wlBuf.Destroy() - } - if slot.pool != nil { - slot.pool.Destroy() - } - if slot.shm != nil { - slot.shm.Close() - } - } - if os.viewport != nil { - os.viewport.Destroy() - } - if os.layerSurf != nil { - os.layerSurf.Destroy() - } - if os.wlSurface != nil { - os.wlSurface.Destroy() - } - if os.screenBuf != nil { - os.screenBuf.Close() - } - if os.screenBufNoCursor != nil { - os.screenBufNoCursor.Close() - } - } - - if r.shortcutsInhibitor != nil { - _ = r.shortcutsInhibitor.Destroy() - } - if r.shortcutsInhibitMgr != nil { - _ = r.shortcutsInhibitMgr.Destroy() - } - if r.viewporter != nil { - r.viewporter.Destroy() - } - if r.screencopy != nil { - r.screencopy.Destroy() - } - if r.pointer != nil { - r.pointer.Release() - } - if r.keyboard != nil { - r.keyboard.Release() - } - if r.display != nil { - r.ctx.Close() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_input.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_input.go deleted file mode 100644 index e243443..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_input.go +++ /dev/null @@ -1,274 +0,0 @@ -package screenshot - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -func (r *RegionSelector) setupInput() { - if r.seat == nil { - return - } - - r.seat.SetCapabilitiesHandler(func(e client.SeatCapabilitiesEvent) { - if e.Capabilities&uint32(client.SeatCapabilityPointer) != 0 && r.pointer == nil { - if pointer, err := r.seat.GetPointer(); err == nil { - r.pointer = pointer - r.setupPointerHandlers() - } - } - if e.Capabilities&uint32(client.SeatCapabilityKeyboard) != 0 && r.keyboard == nil { - if keyboard, err := r.seat.GetKeyboard(); err == nil { - r.keyboard = keyboard - r.setupKeyboardHandlers() - } - } - }) -} - -func (r *RegionSelector) setupPointerHandlers() { - r.pointer.SetEnterHandler(func(e client.PointerEnterEvent) { - if r.cursorSurface != nil { - _ = r.pointer.SetCursor(e.Serial, r.cursorSurface, 12, 12) - } - - r.activeSurface = nil - for _, os := range r.surfaces { - if os.wlSurface.ID() == e.Surface.ID() { - r.activeSurface = os - break - } - } - - r.pointerX = e.SurfaceX - r.pointerY = e.SurfaceY - }) - - r.pointer.SetMotionHandler(func(e client.PointerMotionEvent) { - if r.activeSurface == nil { - return - } - - r.pointerX = e.SurfaceX - r.pointerY = e.SurfaceY - - if !r.selection.dragging { - return - } - - curX, curY := e.SurfaceX, e.SurfaceY - if r.shiftHeld { - dx := curX - r.selection.anchorX - dy := curY - r.selection.anchorY - adx, ady := dx, dy - if adx < 0 { - adx = -adx - } - if ady < 0 { - ady = -ady - } - size := adx - if ady > adx { - size = ady - } - if dx < 0 { - curX = r.selection.anchorX - size - } else { - curX = r.selection.anchorX + size - } - if dy < 0 { - curY = r.selection.anchorY - size - } else { - curY = r.selection.anchorY + size - } - } - - r.selection.currentX = curX - r.selection.currentY = curY - for _, os := range r.surfaces { - r.redrawSurface(os) - } - }) - - r.pointer.SetButtonHandler(func(e client.PointerButtonEvent) { - if r.activeSurface == nil { - return - } - - switch e.Button { - case 0x110: // BTN_LEFT - switch e.State { - case 1: // pressed - r.preSelect = Region{} - r.selection.hasSelection = true - r.selection.dragging = true - r.selection.surface = r.activeSurface - r.selection.anchorX = r.pointerX - r.selection.anchorY = r.pointerY - r.selection.currentX = r.pointerX - r.selection.currentY = r.pointerY - for _, os := range r.surfaces { - r.redrawSurface(os) - } - case 0: // released - r.selection.dragging = false - for _, os := range r.surfaces { - r.redrawSurface(os) - } - if r.screenshoter != nil && r.screenshoter.config.NoConfirm && r.selection.hasSelection { - r.finishSelection() - } - } - default: - r.cancelled = true - r.running = false - } - }) -} - -func (r *RegionSelector) setupKeyboardHandlers() { - r.keyboard.SetModifiersHandler(func(e client.KeyboardModifiersEvent) { - r.shiftHeld = e.ModsDepressed&1 != 0 - }) - - r.keyboard.SetKeyHandler(func(e client.KeyboardKeyEvent) { - if e.State != 1 { - return - } - - switch e.Key { - case 1: - r.cancelled = true - r.running = false - case 25: - r.showCapturedCursor = !r.showCapturedCursor - for _, os := range r.surfaces { - r.redrawSurface(os) - } - case 28, 57, 96: - if r.selection.hasSelection { - r.finishSelection() - } - } - }) -} - -func (r *RegionSelector) finishSelection() { - if r.selection.surface == nil { - r.running = false - return - } - - os := r.selection.surface - srcBuf := r.getSourceBuffer(os) - if srcBuf == nil { - r.running = false - return - } - - x1, y1 := r.selection.anchorX, r.selection.anchorY - x2, y2 := r.selection.currentX, r.selection.currentY - - if x1 > x2 { - x1, x2 = x2, x1 - } - if y1 > y2 { - y1, y2 = y2, y1 - } - - scaleX, scaleY := 1.0, 1.0 - if os.logicalW > 0 { - scaleX = float64(srcBuf.Width) / float64(os.logicalW) - scaleY = float64(srcBuf.Height) / float64(os.logicalH) - } - - bx1 := int(x1 * scaleX) - by1 := int(y1 * scaleY) - bx2 := int(x2 * scaleX) - by2 := int(y2 * scaleY) - - // Clamp to buffer bounds - if bx1 < 0 { - bx1 = 0 - } - if by1 < 0 { - by1 = 0 - } - if bx2 > srcBuf.Width { - bx2 = srcBuf.Width - } - if by2 > srcBuf.Height { - by2 = srcBuf.Height - } - - w, h := bx2-bx1+1, by2-by1+1 - if r.shiftHeld && w != h { - if w < h { - h = w - } else { - w = h - } - } - if w < 1 { - w = 1 - } - if h < 1 { - h = 1 - } - - // Create cropped buffer and copy pixels directly - cropped, err := CreateShmBuffer(w, h, w*4) - if err != nil { - r.running = false - return - } - - srcData := srcBuf.Data() - dstData := cropped.Data() - for y := 0; y < h; y++ { - srcY := by1 + y - if os.yInverted { - srcY = srcBuf.Height - 1 - (by1 + y) - } - if srcY < 0 || srcY >= srcBuf.Height { - continue - } - dstY := y - if os.yInverted { - dstY = h - 1 - y - } - for x := 0; x < w; x++ { - srcX := bx1 + x - if srcX < 0 || srcX >= srcBuf.Width { - continue - } - si := srcY*srcBuf.Stride + srcX*4 - di := dstY*cropped.Stride + x*4 - if si+3 < len(srcData) && di+3 < len(dstData) { - dstData[di+0] = srcData[si+0] - dstData[di+1] = srcData[si+1] - dstData[di+2] = srcData[si+2] - dstData[di+3] = srcData[si+3] - } - } - } - - r.capturedBuffer = cropped - r.capturedRegion = Region{ - X: int32(bx1), - Y: int32(by1), - Width: int32(w), - Height: int32(h), - Output: os.output.name, - } - - // Also store for "last region" feature with global coords - r.result = Region{ - X: int32(bx1) + os.output.x, - Y: int32(by1) + os.output.y, - Width: int32(w), - Height: int32(h), - Output: os.output.name, - } - - r.running = false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_render.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_render.go deleted file mode 100644 index 4071f8a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_render.go +++ /dev/null @@ -1,326 +0,0 @@ -package screenshot - -import "fmt" - -var fontGlyphs = map[rune][12]uint8{ - '0': {0x3C, 0x66, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00}, - '1': {0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, 0x00}, - '2': {0x3C, 0x66, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x66, 0x7E, 0x00, 0x00}, - '3': {0x3C, 0x66, 0x06, 0x06, 0x1C, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00}, - '4': {0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xCC, 0xFE, 0x0C, 0x0C, 0x1E, 0x00, 0x00}, - '5': {0x7E, 0x60, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00}, - '6': {0x1C, 0x30, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00}, - '7': {0x7E, 0x66, 0x06, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00}, - '8': {0x3C, 0x66, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00}, - '9': {0x3C, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x0C, 0x38, 0x00, 0x00}, - 'x': {0x00, 0x00, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0x00}, - 'E': {0x7E, 0x60, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, 0x00}, - 'P': {0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00}, - 'S': {0x3C, 0x66, 0x60, 0x60, 0x3C, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00}, - 'a': {0x00, 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00}, - 'c': {0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, 0x00}, - 'd': {0x00, 0x00, 0x06, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00}, - 'e': {0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x7E, 0x60, 0x60, 0x3C, 0x00, 0x00}, - 'h': {0x00, 0x60, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00}, - 'i': {0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00}, - 'n': {0x00, 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00}, - 'o': {0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00}, - 'p': {0x00, 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, 0x00}, - 'r': {0x00, 0x00, 0x00, 0x6E, 0x76, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00}, - 's': {0x00, 0x00, 0x00, 0x3E, 0x60, 0x60, 0x3C, 0x06, 0x06, 0x7C, 0x00, 0x00}, - 't': {0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00}, - 'u': {0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00}, - 'w': {0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, 0x00}, - 'l': {0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00}, - ' ': {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - ':': {0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, - '/': {0x00, 0x02, 0x06, 0x0C, 0x18, 0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x00}, - '[': {0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00}, - ']': {0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00}, -} - -type OverlayStyle struct { - BackgroundR, BackgroundG, BackgroundB, BackgroundA uint8 - TextR, TextG, TextB uint8 - AccentR, AccentG, AccentB uint8 -} - -var DefaultOverlayStyle = OverlayStyle{ - BackgroundR: 30, BackgroundG: 30, BackgroundB: 30, BackgroundA: 220, - TextR: 255, TextG: 255, TextB: 255, - AccentR: 100, AccentG: 180, AccentB: 255, -} - -func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) { - data := renderBuf.Data() - stride := renderBuf.Stride - w, h := renderBuf.Width, renderBuf.Height - format := os.screenFormat - - // Dim the entire buffer - for y := 0; y < h; y++ { - off := y * stride - for x := 0; x < w; x++ { - i := off + x*4 - if i+3 >= len(data) { - continue - } - data[i+0] = uint8(int(data[i+0]) * 3 / 5) - data[i+1] = uint8(int(data[i+1]) * 3 / 5) - data[i+2] = uint8(int(data[i+2]) * 3 / 5) - } - } - - r.drawHUD(data, stride, w, h, format) - - if !r.selection.hasSelection || r.selection.surface != os { - return - } - - scaleX := float64(w) / float64(os.logicalW) - scaleY := float64(h) / float64(os.logicalH) - - bx1 := int(r.selection.anchorX * scaleX) - by1 := int(r.selection.anchorY * scaleY) - bx2 := int(r.selection.currentX * scaleX) - by2 := int(r.selection.currentY * scaleY) - - if bx1 > bx2 { - bx1, bx2 = bx2, bx1 - } - if by1 > by2 { - by1, by2 = by2, by1 - } - - bx1 = clamp(bx1, 0, w-1) - by1 = clamp(by1, 0, h-1) - bx2 = clamp(bx2, 0, w-1) - by2 = clamp(by2, 0, h-1) - - srcBuf := r.getSourceBuffer(os) - srcData := srcBuf.Data() - for y := by1; y <= by2; y++ { - rowOff := y * stride - for x := bx1; x <= bx2; x++ { - si := y*srcBuf.Stride + x*4 - di := rowOff + x*4 - if si+3 >= len(srcData) || di+3 >= len(data) { - continue - } - data[di+0] = srcData[si+0] - data[di+1] = srcData[si+1] - data[di+2] = srcData[si+2] - data[di+3] = srcData[si+3] - } - } - - selW, selH := bx2-bx1+1, by2-by1+1 - if r.shiftHeld && selW != selH { - if selW < selH { - selH = selW - } else { - selW = selH - } - } - r.drawBorder(data, stride, w, h, bx1, by1, selW, selH, format) - r.drawDimensions(data, stride, w, h, bx1, by1, selW, selH, format) -} - -func (r *RegionSelector) drawHUD(data []byte, stride, bufW, bufH int, format uint32) { - if r.selection.dragging { - return - } - - style := LoadOverlayStyle() - const charW, charH, padding, itemSpacing = 8, 12, 12, 24 - - cursorLabel := "hide" - if !r.showCapturedCursor { - cursorLabel = "show" - } - captureKey := "Space/Enter" - if r.screenshoter != nil && r.screenshoter.config.NoConfirm { - captureKey = "Drag+Release" - } - - items := []struct{ key, desc string }{ - {captureKey, "capture"}, - {"P", cursorLabel + " cursor"}, - {"Esc", "cancel"}, - } - - totalW := 0 - for i, item := range items { - totalW += len(item.key)*(charW+1) + 4 + len(item.desc)*(charW+1) - if i < len(items)-1 { - totalW += itemSpacing - } - } - - hudW := totalW + padding*2 - hudH := charH + padding*2 - hudX := (bufW - hudW) / 2 - hudY := bufH - hudH - 20 - - r.fillRect(data, stride, bufW, bufH, hudX, hudY, hudW, hudH, - style.BackgroundR, style.BackgroundG, style.BackgroundB, style.BackgroundA, format) - - tx, ty := hudX+padding, hudY+padding - for i, item := range items { - r.drawText(data, stride, bufW, bufH, tx, ty, item.key, - style.AccentR, style.AccentG, style.AccentB, format) - tx += len(item.key) * (charW + 1) - - r.drawText(data, stride, bufW, bufH, tx, ty, " "+item.desc, - style.TextR, style.TextG, style.TextB, format) - tx += (1 + len(item.desc)) * (charW + 1) - - if i < len(items)-1 { - tx += itemSpacing - } - } -} - -func (r *RegionSelector) drawBorder(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) { - const thickness = 2 - for i := 0; i < thickness; i++ { - r.drawHLine(data, stride, bufW, bufH, x-i, y-i, w+2*i, format) - r.drawHLine(data, stride, bufW, bufH, x-i, y+h+i-1, w+2*i, format) - r.drawVLine(data, stride, bufW, bufH, x-i, y-i, h+2*i, format) - r.drawVLine(data, stride, bufW, bufH, x+w+i-1, y-i, h+2*i, format) - } -} - -func (r *RegionSelector) drawHLine(data []byte, stride, bufW, bufH, x, y, length int, _ uint32) { - if y < 0 || y >= bufH { - return - } - rowOff := y * stride - for i := 0; i < length; i++ { - px := x + i - if px < 0 || px >= bufW { - continue - } - off := rowOff + px*4 - if off+3 >= len(data) { - continue - } - data[off], data[off+1], data[off+2], data[off+3] = 255, 255, 255, 255 - } -} - -func (r *RegionSelector) drawVLine(data []byte, stride, bufW, bufH, x, y, length int, _ uint32) { - if x < 0 || x >= bufW { - return - } - for i := 0; i < length; i++ { - py := y + i - if py < 0 || py >= bufH { - continue - } - off := py*stride + x*4 - if off+3 >= len(data) { - continue - } - data[off], data[off+1], data[off+2], data[off+3] = 255, 255, 255, 255 - } -} - -func (r *RegionSelector) drawDimensions(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) { - text := fmt.Sprintf("%dx%d", w, h) - - const charW, charH = 8, 12 - textW := len(text) * (charW + 1) - textH := charH - - tx := x + (w-textW)/2 - ty := y + h + 8 - - if ty+textH > bufH { - ty = y - textH - 8 - } - tx = clamp(tx, 0, bufW-textW) - - r.fillRect(data, stride, bufW, bufH, tx-4, ty-2, textW+8, textH+4, 0, 0, 0, 200, format) - r.drawText(data, stride, bufW, bufH, tx, ty, text, 255, 255, 255, format) -} - -func (r *RegionSelector) fillRect(data []byte, stride, bufW, bufH, x, y, w, h int, cr, cg, cb, ca uint8, format uint32) { - alpha := float64(ca) / 255.0 - invAlpha := 1.0 - alpha - - c0, c2 := cb, cr - if format == uint32(FormatABGR8888) || format == uint32(FormatXBGR8888) { - c0, c2 = cr, cb - } - - for py := y; py < y+h && py < bufH; py++ { - if py < 0 { - continue - } - for px := x; px < x+w && px < bufW; px++ { - if px < 0 { - continue - } - off := py*stride + px*4 - if off+3 >= len(data) { - continue - } - data[off+0] = uint8(float64(data[off+0])*invAlpha + float64(c0)*alpha) - data[off+1] = uint8(float64(data[off+1])*invAlpha + float64(cg)*alpha) - data[off+2] = uint8(float64(data[off+2])*invAlpha + float64(c2)*alpha) - data[off+3] = 255 - } - } -} - -func (r *RegionSelector) drawText(data []byte, stride, bufW, bufH, x, y int, text string, cr, cg, cb uint8, format uint32) { - for i, ch := range text { - r.drawChar(data, stride, bufW, bufH, x+i*9, y, ch, cr, cg, cb, format) - } -} - -func (r *RegionSelector) drawChar(data []byte, stride, bufW, bufH, x, y int, ch rune, cr, cg, cb uint8, format uint32) { - glyph, ok := fontGlyphs[ch] - if !ok { - return - } - - c0, c2 := cb, cr - if format == uint32(FormatABGR8888) || format == uint32(FormatXBGR8888) { - c0, c2 = cr, cb - } - - for row := 0; row < 12; row++ { - py := y + row - if py < 0 || py >= bufH { - continue - } - bits := glyph[row] - for col := 0; col < 8; col++ { - if (bits & (1 << (7 - col))) == 0 { - continue - } - px := x + col - if px < 0 || px >= bufW { - continue - } - off := py*stride + px*4 - if off+3 >= len(data) { - continue - } - data[off], data[off+1], data[off+2], data[off+3] = c0, cg, c2, 255 - } - } -} - -func clamp(v, lo, hi int) int { - switch { - case v < lo: - return lo - case v > hi: - return hi - default: - return v - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go deleted file mode 100644 index 42d3404..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go +++ /dev/null @@ -1,1089 +0,0 @@ -package screenshot - -import ( - "fmt" - "math" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_screencopy" - wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type WaylandOutput struct { - wlOutput *client.Output - globalName uint32 - name string - x, y int32 - width int32 - height int32 - scale int32 - fractionalScale float64 - transform int32 -} - -type CaptureResult struct { - Buffer *ShmBuffer - Region Region - YInverted bool - Format uint32 -} - -type Screenshoter struct { - config Config - - display *client.Display - registry *client.Registry - ctx *client.Context - - compositor *client.Compositor - shm *client.Shm - screencopy *wlr_screencopy.ZwlrScreencopyManagerV1 - - outputs map[uint32]*WaylandOutput - outputsMu sync.Mutex -} - -func New(config Config) *Screenshoter { - return &Screenshoter{ - config: config, - outputs: make(map[uint32]*WaylandOutput), - } -} - -func (s *Screenshoter) Run() (*CaptureResult, error) { - if err := s.connect(); err != nil { - return nil, fmt.Errorf("wayland connect: %w", err) - } - defer s.cleanup() - - if err := s.setupRegistry(); err != nil { - return nil, fmt.Errorf("registry setup: %w", err) - } - - if err := s.roundtrip(); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - if s.screencopy == nil { - return nil, fmt.Errorf("compositor does not support wlr-screencopy-unstable-v1") - } - - if err := s.roundtrip(); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - switch s.config.Mode { - case ModeLastRegion: - return s.captureLastRegion() - case ModeRegion: - return s.captureRegion() - case ModeWindow: - return s.captureWindow() - case ModeOutput: - return s.captureOutput(s.config.OutputName) - case ModeFullScreen: - return s.captureFullScreen() - case ModeAllScreens: - return s.captureAllScreens() - default: - return s.captureRegion() - } -} - -func (s *Screenshoter) captureLastRegion() (*CaptureResult, error) { - lastRegion := GetLastRegion() - if lastRegion.IsEmpty() { - return s.captureRegion() - } - - output := s.findOutputForRegion(lastRegion) - if output == nil { - return s.captureRegion() - } - - return s.captureRegionOnOutput(output, lastRegion) -} - -func (s *Screenshoter) captureRegion() (*CaptureResult, error) { - if s.config.Reset { - if err := SaveLastRegion(Region{}); err != nil { - log.Debug("failed to reset last region", "err", err) - } - } - - selector := NewRegionSelector(s) - result, cancelled, err := selector.Run() - if err != nil { - return nil, fmt.Errorf("region selection: %w", err) - } - if cancelled || result == nil { - return nil, nil - } - - if err := SaveLastRegion(result.Region); err != nil { - log.Debug("failed to save last region", "err", err) - } - - return result, nil -} - -func (s *Screenshoter) captureWindow() (*CaptureResult, error) { - geom, err := GetActiveWindow() - if err != nil { - return nil, err - } - - region := Region{ - X: geom.X, - Y: geom.Y, - Width: geom.Width, - Height: geom.Height, - } - - var output *WaylandOutput - if geom.Output != "" { - output = s.findOutputByName(geom.Output) - } - if output == nil { - output = s.findOutputForRegion(region) - } - if output == nil { - return nil, fmt.Errorf("could not find output for window") - } - - switch DetectCompositor() { - case CompositorHyprland: - return s.captureAndCrop(output, region) - case CompositorDWL: - return s.captureDWLWindow(output, region, geom) - default: - return s.captureRegionOnOutput(output, region) - } -} - -func (s *Screenshoter) captureDWLWindow(output *WaylandOutput, region Region, geom *WindowGeometry) (*CaptureResult, error) { - result, err := s.captureWholeOutput(output) - if err != nil { - return nil, err - } - - scale := geom.Scale - if scale <= 0 || scale == 1.0 { - if output.fractionalScale > 1.0 { - scale = output.fractionalScale - } - } - if scale <= 0 { - scale = 1.0 - } - - localX := int(float64(region.X-geom.OutputX) * scale) - localY := int(float64(region.Y-geom.OutputY) * scale) - w := int(float64(region.Width) * scale) - h := int(float64(region.Height) * scale) - - if localX < 0 { - w += localX - localX = 0 - } - if localY < 0 { - h += localY - localY = 0 - } - if localX+w > result.Buffer.Width { - w = result.Buffer.Width - localX - } - if localY+h > result.Buffer.Height { - h = result.Buffer.Height - localY - } - - if w <= 0 || h <= 0 { - result.Buffer.Close() - return nil, fmt.Errorf("window not visible on output") - } - - cropped, err := CreateShmBuffer(w, h, w*4) - if err != nil { - result.Buffer.Close() - return nil, fmt.Errorf("create crop buffer: %w", err) - } - - srcData := result.Buffer.Data() - dstData := cropped.Data() - - for y := 0; y < h; y++ { - srcY := localY + y - if result.YInverted { - srcY = result.Buffer.Height - 1 - (localY + y) - } - if srcY < 0 || srcY >= result.Buffer.Height { - continue - } - - dstY := y - if result.YInverted { - dstY = h - 1 - y - } - - for x := 0; x < w; x++ { - srcX := localX + x - if srcX < 0 || srcX >= result.Buffer.Width { - continue - } - - si := srcY*result.Buffer.Stride + srcX*4 - di := dstY*cropped.Stride + x*4 - - if si+3 >= len(srcData) || di+3 >= len(dstData) { - continue - } - - dstData[di+0] = srcData[si+0] - dstData[di+1] = srcData[si+1] - dstData[di+2] = srcData[si+2] - dstData[di+3] = srcData[si+3] - } - } - - result.Buffer.Close() - cropped.Format = PixelFormat(result.Format) - - return &CaptureResult{ - Buffer: cropped, - Region: region, - YInverted: false, - Format: result.Format, - }, nil -} - -func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) { - output := s.findFocusedOutput() - if output == nil { - s.outputsMu.Lock() - for _, o := range s.outputs { - output = o - break - } - s.outputsMu.Unlock() - } - - if output == nil { - return nil, fmt.Errorf("no output available") - } - - return s.captureWholeOutput(output) -} - -func (s *Screenshoter) captureOutput(name string) (*CaptureResult, error) { - s.outputsMu.Lock() - var output *WaylandOutput - for _, o := range s.outputs { - if o.name == name { - output = o - break - } - } - s.outputsMu.Unlock() - - if output == nil { - return nil, fmt.Errorf("output %q not found", name) - } - - return s.captureWholeOutput(output) -} - -func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) { - s.outputsMu.Lock() - outputs := make([]*WaylandOutput, 0, len(s.outputs)) - for _, o := range s.outputs { - outputs = append(outputs, o) - } - s.outputsMu.Unlock() - - if len(outputs) == 0 { - return nil, fmt.Errorf("no outputs available") - } - if len(outputs) == 1 { - return s.captureWholeOutput(outputs[0]) - } - - wlrInfos := getAllOutputInfos() - - type pendingOutput struct { - result *CaptureResult - logX float64 - logY float64 - scale float64 - } - var pending []pendingOutput - maxScale := 1.0 - - for _, output := range outputs { - result, err := s.captureWholeOutput(output) - if err != nil { - log.Warn("failed to capture output", "name", output.name, "err", err) - continue - } - - logX, logY := float64(output.x), float64(output.y) - scale := float64(output.scale) - - switch DetectCompositor() { - case CompositorHyprland: - if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok { - logX, logY = float64(hx), float64(hy) - } - if hs := GetHyprlandMonitorScale(output.name); hs > 0 { - scale = hs - } - default: - if wlrInfos != nil { - if info, ok := wlrInfos[output.name]; ok { - logX, logY = float64(info.x), float64(info.y) - if info.scale > 0 { - scale = info.scale - } - } - } - } - - if scale <= 0 { - scale = 1.0 - } - - pending = append(pending, pendingOutput{result: result, logX: logX, logY: logY, scale: scale}) - if scale > maxScale { - maxScale = scale - } - } - - if len(pending) == 0 { - return nil, fmt.Errorf("failed to capture any outputs") - } - if len(pending) == 1 { - return pending[0].result, nil - } - - type layoutEntry struct { - result *CaptureResult - canvasX int - canvasY int - canvasW int - canvasH int - } - entries := make([]layoutEntry, len(pending)) - var minX, minY, maxX, maxY int - - for i, p := range pending { - cx := int(math.Round(p.logX * maxScale)) - cy := int(math.Round(p.logY * maxScale)) - cw := int(math.Round(float64(p.result.Buffer.Width) * maxScale / p.scale)) - ch := int(math.Round(float64(p.result.Buffer.Height) * maxScale / p.scale)) - - entries[i] = layoutEntry{result: p.result, canvasX: cx, canvasY: cy, canvasW: cw, canvasH: ch} - - right := cx + cw - bottom := cy + ch - if i == 0 { - minX, minY, maxX, maxY = cx, cy, right, bottom - continue - } - if cx < minX { - minX = cx - } - if cy < minY { - minY = cy - } - if right > maxX { - maxX = right - } - if bottom > maxY { - maxY = bottom - } - } - - totalW := maxX - minX - totalH := maxY - minY - composite, err := CreateShmBuffer(totalW, totalH, totalW*4) - if err != nil { - for _, e := range entries { - e.result.Buffer.Close() - } - return nil, fmt.Errorf("create composite buffer: %w", err) - } - composite.Clear() - - var format uint32 - for _, e := range entries { - if format == 0 { - format = e.result.Format - } - s.blitBufferScaled(composite, e.result.Buffer, - e.canvasX-minX, e.canvasY-minY, e.canvasW, e.canvasH, - e.result.YInverted) - e.result.Buffer.Close() - } - - return &CaptureResult{ - Buffer: composite, - Region: Region{X: int32(minX), Y: int32(minY), Width: int32(totalW), Height: int32(totalH)}, - Format: format, - }, nil -} - -func (s *Screenshoter) blitBufferScaled(dst, src *ShmBuffer, dstX, dstY, dstW, dstH int, yInverted bool) { - if dstW <= 0 || dstH <= 0 { - return - } - - srcData := src.Data() - dstData := dst.Data() - - for dy := 0; dy < dstH; dy++ { - canvasY := dstY + dy - if canvasY < 0 || canvasY >= dst.Height { - continue - } - - srcY := dy * src.Height / dstH - if yInverted { - srcY = src.Height - 1 - srcY - } - if srcY < 0 || srcY >= src.Height { - continue - } - - srcRowOff := srcY * src.Stride - dstRowOff := canvasY * dst.Stride - - for dx := 0; dx < dstW; dx++ { - canvasX := dstX + dx - if canvasX < 0 || canvasX >= dst.Width { - continue - } - - srcX := dx * src.Width / dstW - if srcX >= src.Width { - continue - } - - si := srcRowOff + srcX*4 - di := dstRowOff + canvasX*4 - - if si+3 >= len(srcData) || di+3 >= len(dstData) { - continue - } - - dstData[di+0] = srcData[si+0] - dstData[di+1] = srcData[si+1] - dstData[di+2] = srcData[si+2] - dstData[di+3] = srcData[si+3] - } - } -} - -func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult, error) { - cursor := int32(s.config.Cursor) - - frame, err := s.screencopy.CaptureOutput(cursor, output.wlOutput) - if err != nil { - return nil, fmt.Errorf("capture output: %w", err) - } - - result, err := s.processFrame(frame, Region{ - X: output.x, - Y: output.y, - Width: output.width, - Height: output.height, - Output: output.name, - }) - if err != nil { - return nil, err - } - - if result.YInverted { - result.Buffer.FlipVertical() - result.YInverted = false - } - - if output.transform == TransformNormal { - return result, nil - } - - invTransform := InverseTransform(output.transform) - transformed, err := result.Buffer.ApplyTransform(invTransform) - if err != nil { - result.Buffer.Close() - return nil, fmt.Errorf("apply transform: %w", err) - } - - if transformed != result.Buffer { - result.Buffer.Close() - result.Buffer = transformed - } - - result.Region.Width = int32(transformed.Width) - result.Region.Height = int32(transformed.Height) - - return result, nil -} - -func (s *Screenshoter) captureAndCrop(output *WaylandOutput, region Region) (*CaptureResult, error) { - result, err := s.captureWholeOutput(output) - if err != nil { - return nil, err - } - - outX, outY := output.x, output.y - scale := float64(output.scale) - if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok { - outX, outY = hx, hy - } - if s := GetHyprlandMonitorScale(output.name); s > 0 { - scale = s - } - if scale <= 0 { - scale = 1.0 - } - - localX := int(float64(region.X-outX) * scale) - localY := int(float64(region.Y-outY) * scale) - w := int(float64(region.Width) * scale) - h := int(float64(region.Height) * scale) - - cropped, err := CreateShmBuffer(w, h, w*4) - if err != nil { - result.Buffer.Close() - return nil, fmt.Errorf("create crop buffer: %w", err) - } - - srcData := result.Buffer.Data() - dstData := cropped.Data() - - for y := 0; y < h; y++ { - srcY := localY + y - if result.YInverted { - srcY = result.Buffer.Height - 1 - (localY + y) - } - if srcY < 0 || srcY >= result.Buffer.Height { - continue - } - - dstY := y - if result.YInverted { - dstY = h - 1 - y - } - - for x := 0; x < w; x++ { - srcX := localX + x - if srcX < 0 || srcX >= result.Buffer.Width { - continue - } - - si := srcY*result.Buffer.Stride + srcX*4 - di := dstY*cropped.Stride + x*4 - - if si+3 >= len(srcData) || di+3 >= len(dstData) { - continue - } - - dstData[di+0] = srcData[si+0] - dstData[di+1] = srcData[si+1] - dstData[di+2] = srcData[si+2] - dstData[di+3] = srcData[si+3] - } - } - - result.Buffer.Close() - cropped.Format = PixelFormat(result.Format) - - return &CaptureResult{ - Buffer: cropped, - Region: region, - YInverted: false, - Format: result.Format, - }, nil -} - -func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Region) (*CaptureResult, error) { - if output.transform != TransformNormal { - return s.captureRegionOnTransformedOutput(output, region) - } - - scale := output.fractionalScale - if scale <= 0 && DetectCompositor() == CompositorHyprland { - scale = GetHyprlandMonitorScale(output.name) - } - if scale <= 0 { - scale = float64(output.scale) - } - if scale <= 0 { - scale = 1.0 - } - - localX := int32(float64(region.X-output.x) * scale) - localY := int32(float64(region.Y-output.y) * scale) - w := int32(float64(region.Width) * scale) - h := int32(float64(region.Height) * scale) - - if DetectCompositor() == CompositorDWL { - scaledOutW := int32(float64(output.width) * scale) - scaledOutH := int32(float64(output.height) * scale) - if localX >= scaledOutW { - localX = localX % scaledOutW - } - if localY >= scaledOutH { - localY = localY % scaledOutH - } - if localX+w > scaledOutW { - w = scaledOutW - localX - } - if localY+h > scaledOutH { - h = scaledOutH - localY - } - if localX < 0 { - w += localX - localX = 0 - } - if localY < 0 { - h += localY - localY = 0 - } - } - - cursor := int32(s.config.Cursor) - - frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h) - if err != nil { - return nil, fmt.Errorf("capture region: %w", err) - } - - return s.processFrame(frame, region) -} - -func (s *Screenshoter) captureRegionOnTransformedOutput(output *WaylandOutput, region Region) (*CaptureResult, error) { - result, err := s.captureWholeOutput(output) - if err != nil { - return nil, err - } - - scale := output.fractionalScale - if scale <= 0 && DetectCompositor() == CompositorHyprland { - scale = GetHyprlandMonitorScale(output.name) - } - if scale <= 0 { - scale = float64(output.scale) - } - if scale <= 0 { - scale = 1.0 - } - - localX := int(float64(region.X-output.x) * scale) - localY := int(float64(region.Y-output.y) * scale) - w := int(float64(region.Width) * scale) - h := int(float64(region.Height) * scale) - - if localX < 0 { - w += localX - localX = 0 - } - if localY < 0 { - h += localY - localY = 0 - } - if localX+w > result.Buffer.Width { - w = result.Buffer.Width - localX - } - if localY+h > result.Buffer.Height { - h = result.Buffer.Height - localY - } - - if w <= 0 || h <= 0 { - result.Buffer.Close() - return nil, fmt.Errorf("region not visible on output") - } - - cropped, err := CreateShmBuffer(w, h, w*4) - if err != nil { - result.Buffer.Close() - return nil, fmt.Errorf("create crop buffer: %w", err) - } - - srcData := result.Buffer.Data() - dstData := cropped.Data() - - for y := 0; y < h; y++ { - srcOff := (localY+y)*result.Buffer.Stride + localX*4 - dstOff := y * cropped.Stride - if srcOff+w*4 <= len(srcData) && dstOff+w*4 <= len(dstData) { - copy(dstData[dstOff:dstOff+w*4], srcData[srcOff:srcOff+w*4]) - } - } - - result.Buffer.Close() - cropped.Format = PixelFormat(result.Format) - - return &CaptureResult{ - Buffer: cropped, - Region: region, - YInverted: false, - Format: result.Format, - }, nil -} - -func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1, region Region) (*CaptureResult, error) { - var buf *ShmBuffer - var pool *client.ShmPool - var wlBuf *client.Buffer - var format PixelFormat - var yInverted bool - ready := false - failed := false - - frame.SetBufferHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferEvent) { - format = PixelFormat(e.Format) - bpp := format.BytesPerPixel() - if int(e.Stride) < int(e.Width)*bpp { - log.Error("invalid stride from compositor", "stride", e.Stride, "width", e.Width, "bpp", bpp) - return - } - var err error - buf, err = CreateShmBuffer(int(e.Width), int(e.Height), int(e.Stride)) - if err != nil { - log.Error("failed to create buffer", "err", err) - return - } - buf.Format = format - }) - - frame.SetFlagsHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FlagsEvent) { - yInverted = (e.Flags & 1) != 0 - }) - - frame.SetBufferDoneHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferDoneEvent) { - if buf == nil { - return - } - - var err error - pool, err = s.shm.CreatePool(buf.Fd(), int32(buf.Size())) - if err != nil { - log.Error("failed to create pool", "err", err) - return - } - - wlBuf, err = pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), uint32(format)) - if err != nil { - pool.Destroy() - pool = nil - log.Error("failed to create wl_buffer", "err", err) - return - } - - if err := frame.Copy(wlBuf); err != nil { - log.Error("failed to copy frame", "err", err) - } - }) - - frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) { - ready = true - }) - - frame.SetFailedHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FailedEvent) { - failed = true - }) - - for !ready && !failed { - if err := s.ctx.Dispatch(); err != nil { - frame.Destroy() - return nil, fmt.Errorf("dispatch: %w", err) - } - } - - frame.Destroy() - if wlBuf != nil { - wlBuf.Destroy() - } - if pool != nil { - pool.Destroy() - } - - if failed { - if buf != nil { - buf.Close() - } - return nil, fmt.Errorf("frame capture failed") - } - - if format.Is24Bit() { - converted, newFormat, err := buf.ConvertTo32Bit(format) - if err != nil { - buf.Close() - return nil, fmt.Errorf("convert 24-bit to 32-bit: %w", err) - } - if converted != buf { - buf.Close() - buf = converted - } - format = newFormat - } - - return &CaptureResult{ - Buffer: buf, - Region: region, - YInverted: yInverted, - Format: uint32(format), - }, nil -} - -func (s *Screenshoter) findOutputByName(name string) *WaylandOutput { - s.outputsMu.Lock() - defer s.outputsMu.Unlock() - for _, o := range s.outputs { - if o.name == name { - return o - } - } - return nil -} - -func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput { - s.outputsMu.Lock() - defer s.outputsMu.Unlock() - - cx := region.X + region.Width/2 - cy := region.Y + region.Height/2 - - for _, o := range s.outputs { - x, y, w, h := o.x, o.y, o.width, o.height - if DetectCompositor() == CompositorHyprland { - if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok { - x, y, w, h = hx, hy, hw, hh - } - } - if cx >= x && cx < x+w && cy >= y && cy < y+h { - return o - } - } - - for _, o := range s.outputs { - x, y, w, h := o.x, o.y, o.width, o.height - if DetectCompositor() == CompositorHyprland { - if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok { - x, y, w, h = hx, hy, hw, hh - } - } - if region.X >= x && region.X < x+w && - region.Y >= y && region.Y < y+h { - return o - } - } - - return nil -} - -func (s *Screenshoter) findFocusedOutput() *WaylandOutput { - if mon := GetFocusedMonitor(); mon != "" { - s.outputsMu.Lock() - defer s.outputsMu.Unlock() - for _, o := range s.outputs { - if o.name == mon { - return o - } - } - } - s.outputsMu.Lock() - defer s.outputsMu.Unlock() - for _, o := range s.outputs { - return o - } - return nil -} - -func (s *Screenshoter) connect() error { - display, err := client.Connect("") - if err != nil { - return err - } - s.display = display - s.ctx = display.Context() - return nil -} - -func (s *Screenshoter) roundtrip() error { - return wlhelpers.Roundtrip(s.display, s.ctx) -} - -func (s *Screenshoter) setupRegistry() error { - registry, err := s.display.GetRegistry() - if err != nil { - return err - } - s.registry = registry - - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - s.handleGlobal(e) - }) - - registry.SetGlobalRemoveHandler(func(e client.RegistryGlobalRemoveEvent) { - s.outputsMu.Lock() - delete(s.outputs, e.Name) - s.outputsMu.Unlock() - }) - - return nil -} - -func (s *Screenshoter) handleGlobal(e client.RegistryGlobalEvent) { - switch e.Interface { - case client.CompositorInterfaceName: - comp := client.NewCompositor(s.ctx) - if err := s.registry.Bind(e.Name, e.Interface, e.Version, comp); err == nil { - s.compositor = comp - } - - case client.ShmInterfaceName: - shm := client.NewShm(s.ctx) - if err := s.registry.Bind(e.Name, e.Interface, e.Version, shm); err == nil { - s.shm = shm - } - - case client.OutputInterfaceName: - output := client.NewOutput(s.ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := s.registry.Bind(e.Name, e.Interface, version, output); err == nil { - s.outputsMu.Lock() - s.outputs[e.Name] = &WaylandOutput{ - wlOutput: output, - globalName: e.Name, - scale: 1, - fractionalScale: 1.0, - } - s.outputsMu.Unlock() - s.setupOutputHandlers(e.Name, output) - } - - case wlr_screencopy.ZwlrScreencopyManagerV1InterfaceName: - sc := wlr_screencopy.NewZwlrScreencopyManagerV1(s.ctx) - version := e.Version - if version > 3 { - version = 3 - } - if err := s.registry.Bind(e.Name, e.Interface, version, sc); err == nil { - s.screencopy = sc - } - } -} - -func (s *Screenshoter) setupOutputHandlers(name uint32, output *client.Output) { - output.SetGeometryHandler(func(e client.OutputGeometryEvent) { - s.outputsMu.Lock() - if o, ok := s.outputs[name]; ok { - o.x, o.y = e.X, e.Y - o.transform = int32(e.Transform) - } - s.outputsMu.Unlock() - }) - - output.SetModeHandler(func(e client.OutputModeEvent) { - if e.Flags&uint32(client.OutputModeCurrent) == 0 { - return - } - s.outputsMu.Lock() - if o, ok := s.outputs[name]; ok { - o.width, o.height = e.Width, e.Height - } - s.outputsMu.Unlock() - }) - - output.SetScaleHandler(func(e client.OutputScaleEvent) { - s.outputsMu.Lock() - if o, ok := s.outputs[name]; ok { - o.scale = e.Factor - o.fractionalScale = float64(e.Factor) - } - s.outputsMu.Unlock() - }) - - output.SetNameHandler(func(e client.OutputNameEvent) { - s.outputsMu.Lock() - if o, ok := s.outputs[name]; ok { - o.name = e.Name - } - s.outputsMu.Unlock() - }) -} - -func (s *Screenshoter) cleanup() { - if s.screencopy != nil { - s.screencopy.Destroy() - } - if s.display != nil { - s.ctx.Close() - } -} - -func (s *Screenshoter) GetOutputs() []*WaylandOutput { - s.outputsMu.Lock() - defer s.outputsMu.Unlock() - out := make([]*WaylandOutput, 0, len(s.outputs)) - for _, o := range s.outputs { - out = append(out, o) - } - return out -} - -func ListOutputs() ([]Output, error) { - sc := New(DefaultConfig()) - if err := sc.connect(); err != nil { - return nil, err - } - defer sc.cleanup() - - if err := sc.setupRegistry(); err != nil { - return nil, err - } - if err := sc.roundtrip(); err != nil { - return nil, err - } - if err := sc.roundtrip(); err != nil { - return nil, err - } - - sc.outputsMu.Lock() - defer sc.outputsMu.Unlock() - - compositor := DetectCompositor() - result := make([]Output, 0, len(sc.outputs)) - for _, o := range sc.outputs { - out := Output{ - Name: o.name, - X: o.x, - Y: o.y, - Width: o.width, - Height: o.height, - Scale: o.scale, - FractionalScale: o.fractionalScale, - Transform: o.transform, - } - - switch compositor { - case CompositorHyprland: - if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok { - out.X, out.Y = hx, hy - out.Width, out.Height = hw, hh - } - if s := GetHyprlandMonitorScale(o.name); s > 0 { - out.FractionalScale = s - } - } - - result = append(result, out) - } - return result, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/shm.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/shm.go deleted file mode 100644 index a178cf0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/shm.go +++ /dev/null @@ -1,35 +0,0 @@ -package screenshot - -import "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/shm" - -type PixelFormat = shm.PixelFormat - -const ( - FormatARGB8888 = shm.FormatARGB8888 - FormatXRGB8888 = shm.FormatXRGB8888 - FormatABGR8888 = shm.FormatABGR8888 - FormatXBGR8888 = shm.FormatXBGR8888 - FormatRGB888 = shm.FormatRGB888 - FormatBGR888 = shm.FormatBGR888 -) - -const ( - TransformNormal = shm.TransformNormal - Transform90 = shm.Transform90 - Transform180 = shm.Transform180 - Transform270 = shm.Transform270 - TransformFlipped = shm.TransformFlipped - TransformFlipped90 = shm.TransformFlipped90 - TransformFlipped180 = shm.TransformFlipped180 - TransformFlipped270 = shm.TransformFlipped270 -) - -type ShmBuffer = shm.Buffer - -func CreateShmBuffer(width, height, stride int) (*ShmBuffer, error) { - return shm.CreateBuffer(width, height, stride) -} - -func InverseTransform(transform int32) int32 { - return shm.InverseTransform(transform) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/state.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/state.go deleted file mode 100644 index 0ba6c63..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/state.go +++ /dev/null @@ -1,65 +0,0 @@ -package screenshot - -import ( - "encoding/json" - "os" - "path" - "path/filepath" -) - -type PersistentState struct { - LastRegion Region `json:"last_region"` -} - -func getStateFilePath() string { - cacheDir, err := os.UserCacheDir() - if err != nil { - cacheDir = path.Join(os.Getenv("HOME"), ".cache") - } - return filepath.Join(cacheDir, "dms", "screenshot-state.json") -} - -func LoadState() (*PersistentState, error) { - path := getStateFilePath() - data, err := os.ReadFile(path) - if err != nil { - if os.IsNotExist(err) { - return &PersistentState{}, nil - } - return nil, err - } - - var state PersistentState - if err := json.Unmarshal(data, &state); err != nil { - return &PersistentState{}, nil - } - return &state, nil -} - -func SaveState(state *PersistentState) error { - path := getStateFilePath() - dir := filepath.Dir(path) - if err := os.MkdirAll(dir, 0o755); err != nil { - return err - } - - data, err := json.MarshalIndent(state, "", " ") - if err != nil { - return err - } - return os.WriteFile(path, data, 0o644) -} - -func GetLastRegion() Region { - state, err := LoadState() - if err != nil { - return Region{} - } - return state.LastRegion -} - -func SaveLastRegion(r Region) error { - state, _ := LoadState() - state.LastRegion = r - return SaveState(state) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/theme.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/theme.go deleted file mode 100644 index a2bd5f6..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/theme.go +++ /dev/null @@ -1,125 +0,0 @@ -package screenshot - -import ( - "encoding/json" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -type ThemeColors struct { - Background string `json:"surface"` - OnSurface string `json:"on_surface"` - Primary string `json:"primary"` -} - -type ColorScheme struct { - Dark ThemeColors `json:"dark"` - Light ThemeColors `json:"light"` -} - -type ColorsFile struct { - Colors ColorScheme `json:"colors"` -} - -var cachedStyle *OverlayStyle - -func LoadOverlayStyle() OverlayStyle { - if cachedStyle != nil { - return *cachedStyle - } - - style := DefaultOverlayStyle - colors := loadColorsFile() - if colors == nil { - cachedStyle = &style - return style - } - - theme := &colors.Dark - if isLightMode() { - theme = &colors.Light - } - - if bg, ok := parseHexColor(theme.Background); ok { - style.BackgroundR, style.BackgroundG, style.BackgroundB = bg[0], bg[1], bg[2] - } - if text, ok := parseHexColor(theme.OnSurface); ok { - style.TextR, style.TextG, style.TextB = text[0], text[1], text[2] - } - if accent, ok := parseHexColor(theme.Primary); ok { - style.AccentR, style.AccentG, style.AccentB = accent[0], accent[1], accent[2] - } - - cachedStyle = &style - return style -} - -func loadColorsFile() *ColorScheme { - path := getColorsFilePath() - data, err := os.ReadFile(path) - if err != nil { - return nil - } - - var file ColorsFile - if err := json.Unmarshal(data, &file); err != nil { - return nil - } - - return &file.Colors -} - -func getColorsFilePath() string { - cacheDir, err := os.UserCacheDir() - if err != nil { - log.Error("Failed to get user cache dir", "err", err) - return "" - } - return filepath.Join(cacheDir, "DankMaterialShell", "dms-colors.json") -} - -func isLightMode() bool { - scheme, err := utils.GsettingsGet("org.gnome.desktop.interface", "color-scheme") - if err != nil { - return false - } - - switch scheme { - case "'prefer-light'", "'default'": - return true - } - return false -} - -func parseHexColor(hex string) ([3]uint8, bool) { - hex = strings.TrimPrefix(hex, "#") - if len(hex) != 6 { - return [3]uint8{}, false - } - - var r, g, b uint8 - for i, ptr := range []*uint8{&r, &g, &b} { - val := 0 - for j := 0; j < 2; j++ { - c := hex[i*2+j] - val *= 16 - switch { - case c >= '0' && c <= '9': - val += int(c - '0') - case c >= 'a' && c <= 'f': - val += int(c - 'a' + 10) - case c >= 'A' && c <= 'F': - val += int(c - 'A' + 10) - default: - return [3]uint8{}, false - } - } - *ptr = uint8(val) - } - - return [3]uint8{r, g, b}, true -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/types.go deleted file mode 100644 index 00cbe1c..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/types.go +++ /dev/null @@ -1,81 +0,0 @@ -package screenshot - -type Mode int - -const ( - ModeRegion Mode = iota - ModeWindow - ModeFullScreen - ModeAllScreens - ModeOutput - ModeLastRegion -) - -type Format int - -const ( - FormatPNG Format = iota - FormatJPEG - FormatPPM -) - -type CursorMode int - -const ( - CursorOff CursorMode = iota - CursorOn -) - -type Region struct { - X int32 `json:"x"` - Y int32 `json:"y"` - Width int32 `json:"width"` - Height int32 `json:"height"` - Output string `json:"output,omitempty"` -} - -func (r Region) IsEmpty() bool { - return r.Width <= 0 || r.Height <= 0 -} - -type Output struct { - Name string - X, Y int32 - Width int32 - Height int32 - Scale int32 - FractionalScale float64 - Transform int32 -} - -type Config struct { - Mode Mode - OutputName string - Cursor CursorMode - NoConfirm bool - Reset bool - Format Format - Quality int - OutputDir string - Filename string - Clipboard bool - SaveFile bool - Notify bool - Stdout bool -} - -func DefaultConfig() Config { - return Config{ - Mode: ModeRegion, - Cursor: CursorOff, - NoConfirm: false, - Reset: false, - Format: FormatPNG, - Quality: 90, - OutputDir: "", - Filename: "", - Clipboard: true, - SaveFile: true, - Notify: true, - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/handlers.go deleted file mode 100644 index 007ab3e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/handlers.go +++ /dev/null @@ -1,51 +0,0 @@ -package apppicker - -import ( - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - switch req.Method { - case "apppicker.open", "browser.open": - handleOpen(conn, req, manager) - default: - models.RespondError(conn, req.ID, "unknown method") - } -} - -func handleOpen(conn net.Conn, req models.Request, manager *Manager) { - log.Infof("AppPicker: Received %s request with params: %+v", req.Method, req.Params) - - target, ok := models.Get[string](req, "target") - if !ok { - target, ok = models.Get[string](req, "url") - if !ok { - log.Warnf("AppPicker: Invalid target parameter in request") - models.RespondError(conn, req.ID, "invalid target parameter") - return - } - } - - event := OpenEvent{ - Target: target, - RequestType: models.GetOr(req, "requestType", "url"), - MimeType: models.GetOr(req, "mimeType", ""), - } - - if categories, ok := models.Get[[]any](req, "categories"); ok { - event.Categories = make([]string, 0, len(categories)) - for _, cat := range categories { - if catStr, ok := cat.(string); ok { - event.Categories = append(event.Categories, catStr) - } - } - } - - log.Infof("AppPicker: Broadcasting event: %+v", event) - manager.RequestOpen(event) - models.Respond(conn, req.ID, "ok") - log.Infof("AppPicker: Request handled successfully") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/manager.go deleted file mode 100644 index bde0846..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/manager.go +++ /dev/null @@ -1,48 +0,0 @@ -package apppicker - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type Manager struct { - subscribers syncmap.Map[string, chan OpenEvent] - closeOnce sync.Once -} - -func NewManager() *Manager { - return &Manager{} -} - -func (m *Manager) Subscribe(id string) chan OpenEvent { - ch := make(chan OpenEvent, 16) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) RequestOpen(event OpenEvent) { - m.subscribers.Range(func(key string, ch chan OpenEvent) bool { - select { - case ch <- event: - default: - } - return true - }) -} - -func (m *Manager) Close() { - m.closeOnce.Do(func() { - m.subscribers.Range(func(key string, ch chan OpenEvent) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/models.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/models.go deleted file mode 100644 index 8861461..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/apppicker/models.go +++ /dev/null @@ -1,8 +0,0 @@ -package apppicker - -type OpenEvent struct { - Target string `json:"target"` - MimeType string `json:"mimeType,omitempty"` - Categories []string `json:"categories,omitempty"` - RequestType string `json:"requestType"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/agent.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/agent.go deleted file mode 100644 index b630a19..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/agent.go +++ /dev/null @@ -1,341 +0,0 @@ -package bluez - -import ( - "context" - "errors" - "fmt" - "strconv" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - bluezService = "org.bluez" - agentManagerPath = "/org/bluez" - agentManagerIface = "org.bluez.AgentManager1" - agent1Iface = "org.bluez.Agent1" - device1Iface = "org.bluez.Device1" - agentPath = "/com/danklinux/bluez/agent" - agentCapability = "KeyboardDisplay" -) - -const introspectXML = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -` - -type BluezAgent struct { - conn *dbus.Conn - broker PromptBroker -} - -func NewBluezAgent(broker PromptBroker) (*BluezAgent, error) { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("system bus connection failed: %w", err) - } - - agent := &BluezAgent{ - conn: conn, - broker: broker, - } - - if err := conn.Export(agent, dbus.ObjectPath(agentPath), agent1Iface); err != nil { - conn.Close() - return nil, fmt.Errorf("agent export failed: %w", err) - } - - if err := conn.Export(agent, dbus.ObjectPath(agentPath), "org.freedesktop.DBus.Introspectable"); err != nil { - conn.Close() - return nil, fmt.Errorf("introspection export failed: %w", err) - } - - mgr := conn.Object(bluezService, dbus.ObjectPath(agentManagerPath)) - if err := mgr.Call(agentManagerIface+".RegisterAgent", 0, dbus.ObjectPath(agentPath), agentCapability).Err; err != nil { - conn.Close() - return nil, fmt.Errorf("agent registration failed: %w", err) - } - - if err := mgr.Call(agentManagerIface+".RequestDefaultAgent", 0, dbus.ObjectPath(agentPath)).Err; err != nil { - log.Debugf("[BluezAgent] not default agent: %v", err) - } - - log.Infof("[BluezAgent] registered at %s with capability %s", agentPath, agentCapability) - return agent, nil -} - -func (a *BluezAgent) Close() { - if a.conn == nil { - return - } - mgr := a.conn.Object(bluezService, dbus.ObjectPath(agentManagerPath)) - mgr.Call(agentManagerIface+".UnregisterAgent", 0, dbus.ObjectPath(agentPath)) - a.conn.Close() -} - -func (a *BluezAgent) Release() *dbus.Error { - log.Infof("[BluezAgent] Release called") - return nil -} - -func (a *BluezAgent) RequestPinCode(device dbus.ObjectPath) (string, *dbus.Error) { - log.Infof("[BluezAgent] RequestPinCode: device=%s", device) - - secrets, err := a.promptFor(device, "pin", []string{"pin"}, nil) - if err != nil { - log.Warnf("[BluezAgent] RequestPinCode failed: %v", err) - return "", a.errorFrom(err) - } - - pin := secrets["pin"] - log.Infof("[BluezAgent] RequestPinCode returning PIN (len=%d)", len(pin)) - return pin, nil -} - -func (a *BluezAgent) RequestPasskey(device dbus.ObjectPath) (uint32, *dbus.Error) { - log.Infof("[BluezAgent] RequestPasskey: device=%s", device) - - secrets, err := a.promptFor(device, "passkey", []string{"passkey"}, nil) - if err != nil { - log.Warnf("[BluezAgent] RequestPasskey failed: %v", err) - return 0, a.errorFrom(err) - } - - passkey, err := strconv.ParseUint(secrets["passkey"], 10, 32) - if err != nil { - log.Warnf("[BluezAgent] invalid passkey format: %v", err) - return 0, dbus.MakeFailedError(fmt.Errorf("invalid passkey: %w", err)) - } - - log.Infof("[BluezAgent] RequestPasskey returning: %d", passkey) - return uint32(passkey), nil -} - -func (a *BluezAgent) DisplayPinCode(device dbus.ObjectPath, pincode string) *dbus.Error { - log.Infof("[BluezAgent] DisplayPinCode: device=%s, pin=%s", device, pincode) - - _, err := a.promptFor(device, "display-pin", []string{}, &pincode) - if err != nil { - log.Warnf("[BluezAgent] DisplayPinCode acknowledgment failed: %v", err) - } - - return nil -} - -func (a *BluezAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, entered uint16) *dbus.Error { - log.Infof("[BluezAgent] DisplayPasskey: device=%s, passkey=%06d, entered=%d", device, passkey, entered) - - if entered == 0 { - passkeyStr := strconv.FormatUint(uint64(passkey), 10) - _, err := a.promptFor(device, "display-passkey", []string{}, &passkeyStr) - if err != nil { - log.Warnf("[BluezAgent] DisplayPasskey acknowledgment failed: %v", err) - } - } - - return nil -} - -func (a *BluezAgent) RequestConfirmation(device dbus.ObjectPath, passkey uint32) *dbus.Error { - log.Infof("[BluezAgent] RequestConfirmation: device=%s, passkey=%06d", device, passkey) - - passkeyStr := strconv.FormatUint(uint64(passkey), 10) - secrets, err := a.promptFor(device, "confirm", []string{"decision"}, &passkeyStr) - if err != nil { - log.Warnf("[BluezAgent] RequestConfirmation failed: %v", err) - return a.errorFrom(err) - } - - if secrets["decision"] != "yes" && secrets["decision"] != "accept" { - log.Debugf("[BluezAgent] RequestConfirmation rejected by user") - return dbus.NewError("org.bluez.Error.Rejected", nil) - } - - log.Infof("[BluezAgent] RequestConfirmation accepted") - return nil -} - -func (a *BluezAgent) RequestAuthorization(device dbus.ObjectPath) *dbus.Error { - log.Infof("[BluezAgent] RequestAuthorization: device=%s", device) - - secrets, err := a.promptFor(device, "authorize", []string{"decision"}, nil) - if err != nil { - log.Warnf("[BluezAgent] RequestAuthorization failed: %v", err) - return a.errorFrom(err) - } - - if secrets["decision"] != "yes" && secrets["decision"] != "accept" { - log.Debugf("[BluezAgent] RequestAuthorization rejected by user") - return dbus.NewError("org.bluez.Error.Rejected", nil) - } - - log.Infof("[BluezAgent] RequestAuthorization accepted") - return nil -} - -func (a *BluezAgent) AuthorizeService(device dbus.ObjectPath, uuid string) *dbus.Error { - log.Infof("[BluezAgent] AuthorizeService: device=%s, uuid=%s", device, uuid) - - secrets, err := a.promptFor(device, "authorize-service:"+uuid, []string{"decision"}, nil) - if err != nil { - log.Warnf("[BluezAgent] AuthorizeService failed: %v", err) - return a.errorFrom(err) - } - - if secrets["decision"] != "yes" && secrets["decision"] != "accept" { - log.Debugf("[BluezAgent] AuthorizeService rejected by user") - return dbus.NewError("org.bluez.Error.Rejected", nil) - } - - log.Infof("[BluezAgent] AuthorizeService accepted") - return nil -} - -func (a *BluezAgent) Cancel() *dbus.Error { - log.Infof("[BluezAgent] Cancel called") - return nil -} - -func (a *BluezAgent) Introspect() (string, *dbus.Error) { - return introspectXML, nil -} - -func (a *BluezAgent) promptFor(device dbus.ObjectPath, requestType string, fields []string, displayValue *string) (map[string]string, error) { - if a.broker == nil { - return nil, fmt.Errorf("broker not initialized") - } - - deviceName, deviceAddr := a.getDeviceInfo(device) - hints := []string{} - if displayValue != nil { - hints = append(hints, *displayValue) - } - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - var passkey *uint32 - if requestType == "confirm" || requestType == "display-passkey" { - if displayValue != nil { - if pk, err := strconv.ParseUint(*displayValue, 10, 32); err == nil { - pk32 := uint32(pk) - passkey = &pk32 - } - } - } - - token, err := a.broker.Ask(ctx, PromptRequest{ - DevicePath: string(device), - DeviceName: deviceName, - DeviceAddr: deviceAddr, - RequestType: requestType, - Fields: fields, - Hints: hints, - Passkey: passkey, - }) - if err != nil { - return nil, fmt.Errorf("prompt creation failed: %w", err) - } - - log.Infof("[BluezAgent] waiting for user response (token=%s)", token) - reply, err := a.broker.Wait(ctx, token) - if err != nil { - if errors.Is(err, errdefs.ErrSecretPromptTimeout) { - return nil, err - } - if reply.Cancel || errors.Is(err, errdefs.ErrSecretPromptCancelled) { - return nil, errdefs.ErrSecretPromptCancelled - } - return nil, err - } - - if !reply.Accept && len(fields) > 0 { - return nil, errdefs.ErrSecretPromptCancelled - } - - return reply.Secrets, nil -} - -func (a *BluezAgent) getDeviceInfo(device dbus.ObjectPath) (string, string) { - obj := a.conn.Object(bluezService, device) - - var name, alias, addr string - - nameVar, err := obj.GetProperty(device1Iface + ".Name") - if err == nil { - if n, ok := nameVar.Value().(string); ok { - name = n - } - } - - aliasVar, err := obj.GetProperty(device1Iface + ".Alias") - if err == nil { - if a, ok := aliasVar.Value().(string); ok { - alias = a - } - } - - addrVar, err := obj.GetProperty(device1Iface + ".Address") - if err == nil { - if a, ok := addrVar.Value().(string); ok { - addr = a - } - } - - if alias != "" { - return alias, addr - } - if name != "" { - return name, addr - } - return addr, addr -} - -func (a *BluezAgent) errorFrom(err error) *dbus.Error { - if errors.Is(err, errdefs.ErrSecretPromptTimeout) { - return dbus.NewError("org.bluez.Error.Canceled", nil) - } - if errors.Is(err, errdefs.ErrSecretPromptCancelled) { - return dbus.NewError("org.bluez.Error.Canceled", nil) - } - return dbus.MakeFailedError(err) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker.go deleted file mode 100644 index f817707..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker.go +++ /dev/null @@ -1,21 +0,0 @@ -package bluez - -import ( - "context" - "crypto/rand" - "encoding/hex" -) - -type PromptBroker interface { - Ask(ctx context.Context, req PromptRequest) (token string, err error) - Wait(ctx context.Context, token string) (PromptReply, error) - Resolve(token string, reply PromptReply) error -} - -func generateToken() (string, error) { - bytes := make([]byte, 16) - if _, err := rand.Read(bytes); err != nil { - return "", err - } - return hex.EncodeToString(bytes), nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker_test.go deleted file mode 100644 index 9eebe61..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/broker_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package bluez - -import ( - "context" - "testing" - "time" -) - -func TestSubscriptionBrokerAskWait(t *testing.T) { - promptReceived := false - broker := NewSubscriptionBroker(func(p PairingPrompt) { - promptReceived = true - if p.Token == "" { - t.Error("expected token to be non-empty") - } - if p.DeviceName != "TestDevice" { - t.Errorf("expected DeviceName=TestDevice, got %s", p.DeviceName) - } - }) - - ctx := context.Background() - req := PromptRequest{ - DevicePath: "/org/bluez/test", - DeviceName: "TestDevice", - DeviceAddr: "AA:BB:CC:DD:EE:FF", - RequestType: "pin", - Fields: []string{"pin"}, - } - - token, err := broker.Ask(ctx, req) - if err != nil { - t.Fatalf("Ask failed: %v", err) - } - - if token == "" { - t.Fatal("expected non-empty token") - } - - if !promptReceived { - t.Fatal("expected prompt broadcast to be called") - } - - go func() { - time.Sleep(50 * time.Millisecond) - broker.Resolve(token, PromptReply{ - Secrets: map[string]string{"pin": "1234"}, - Accept: true, - }) - }() - - reply, err := broker.Wait(ctx, token) - if err != nil { - t.Fatalf("Wait failed: %v", err) - } - - if reply.Secrets["pin"] != "1234" { - t.Errorf("expected pin=1234, got %s", reply.Secrets["pin"]) - } - - if !reply.Accept { - t.Error("expected Accept=true") - } -} - -func TestSubscriptionBrokerTimeout(t *testing.T) { - broker := NewSubscriptionBroker(nil) - - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) - defer cancel() - - req := PromptRequest{ - DevicePath: "/org/bluez/test", - DeviceName: "TestDevice", - RequestType: "passkey", - Fields: []string{"passkey"}, - } - - token, err := broker.Ask(ctx, req) - if err != nil { - t.Fatalf("Ask failed: %v", err) - } - - _, err = broker.Wait(ctx, token) - if err == nil { - t.Fatal("expected timeout error") - } -} - -func TestSubscriptionBrokerCancel(t *testing.T) { - broker := NewSubscriptionBroker(nil) - - ctx := context.Background() - req := PromptRequest{ - DevicePath: "/org/bluez/test", - DeviceName: "TestDevice", - RequestType: "confirm", - Fields: []string{"decision"}, - } - - token, err := broker.Ask(ctx, req) - if err != nil { - t.Fatalf("Ask failed: %v", err) - } - - go func() { - time.Sleep(50 * time.Millisecond) - broker.Resolve(token, PromptReply{ - Cancel: true, - }) - }() - - _, err = broker.Wait(ctx, token) - if err == nil { - t.Fatal("expected cancelled error") - } -} - -func TestSubscriptionBrokerUnknownToken(t *testing.T) { - broker := NewSubscriptionBroker(nil) - - ctx := context.Background() - _, err := broker.Wait(ctx, "invalid-token") - if err == nil { - t.Fatal("expected error for unknown token") - } -} - -func TestGenerateToken(t *testing.T) { - token1, err := generateToken() - if err != nil { - t.Fatalf("generateToken failed: %v", err) - } - - token2, err := generateToken() - if err != nil { - t.Fatalf("generateToken failed: %v", err) - } - - if token1 == token2 { - t.Error("expected unique tokens") - } - - if len(token1) != 32 { - t.Errorf("expected token length 32, got %d", len(token1)) - } -} - -func TestSubscriptionBrokerResolveUnknownToken(t *testing.T) { - broker := NewSubscriptionBroker(nil) - - err := broker.Resolve("unknown-token", PromptReply{ - Secrets: map[string]string{"test": "value"}, - }) - if err == nil { - t.Fatal("expected error for unknown token") - } -} - -func TestSubscriptionBrokerMultipleRequests(t *testing.T) { - broker := NewSubscriptionBroker(nil) - ctx := context.Background() - - req1 := PromptRequest{ - DevicePath: "/org/bluez/test1", - DeviceName: "Device1", - RequestType: "pin", - Fields: []string{"pin"}, - } - - req2 := PromptRequest{ - DevicePath: "/org/bluez/test2", - DeviceName: "Device2", - RequestType: "passkey", - Fields: []string{"passkey"}, - } - - token1, err := broker.Ask(ctx, req1) - if err != nil { - t.Fatalf("Ask1 failed: %v", err) - } - - token2, err := broker.Ask(ctx, req2) - if err != nil { - t.Fatalf("Ask2 failed: %v", err) - } - - if token1 == token2 { - t.Error("expected different tokens") - } - - go func() { - time.Sleep(50 * time.Millisecond) - broker.Resolve(token1, PromptReply{ - Secrets: map[string]string{"pin": "1234"}, - Accept: true, - }) - broker.Resolve(token2, PromptReply{ - Secrets: map[string]string{"passkey": "567890"}, - Accept: true, - }) - }() - - reply1, err := broker.Wait(ctx, token1) - if err != nil { - t.Fatalf("Wait1 failed: %v", err) - } - - reply2, err := broker.Wait(ctx, token2) - if err != nil { - t.Fatalf("Wait2 failed: %v", err) - } - - if reply1.Secrets["pin"] != "1234" { - t.Errorf("expected pin=1234, got %s", reply1.Secrets["pin"]) - } - - if reply2.Secrets["passkey"] != "567890" { - t.Errorf("expected passkey=567890, got %s", reply2.Secrets["passkey"]) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers.go deleted file mode 100644 index be5a1c4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers.go +++ /dev/null @@ -1,237 +0,0 @@ -package bluez - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -type BluetoothEvent struct { - Type string `json:"type"` - Data BluetoothState `json:"data"` -} - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - switch req.Method { - case "bluetooth.getState": - handleGetState(conn, req, manager) - case "bluetooth.startDiscovery": - handleStartDiscovery(conn, req, manager) - case "bluetooth.stopDiscovery": - handleStopDiscovery(conn, req, manager) - case "bluetooth.setPowered": - handleSetPowered(conn, req, manager) - case "bluetooth.pair": - handlePairDevice(conn, req, manager) - case "bluetooth.connect": - handleConnectDevice(conn, req, manager) - case "bluetooth.disconnect": - handleDisconnectDevice(conn, req, manager) - case "bluetooth.remove": - handleRemoveDevice(conn, req, manager) - case "bluetooth.trust": - handleTrustDevice(conn, req, manager) - case "bluetooth.untrust": - handleUntrustDevice(conn, req, manager) - case "bluetooth.subscribe": - handleSubscribe(conn, req, manager) - case "bluetooth.pairing.submit": - handlePairingSubmit(conn, req, manager) - case "bluetooth.pairing.cancel": - handlePairingCancel(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleStartDiscovery(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.StartDiscovery(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "discovery started"}) -} - -func handleStopDiscovery(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.StopDiscovery(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "discovery stopped"}) -} - -func handleSetPowered(conn net.Conn, req models.Request, manager *Manager) { - powered, err := params.Bool(req.Params, "powered") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetPowered(powered); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "powered state updated"}) -} - -func handlePairDevice(conn net.Conn, req models.Request, manager *Manager) { - devicePath, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.PairDevice(devicePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing initiated"}) -} - -func handleConnectDevice(conn net.Conn, req models.Request, manager *Manager) { - devicePath, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.ConnectDevice(devicePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"}) -} - -func handleDisconnectDevice(conn net.Conn, req models.Request, manager *Manager) { - devicePath, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.DisconnectDevice(devicePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "disconnected"}) -} - -func handleRemoveDevice(conn net.Conn, req models.Request, manager *Manager) { - devicePath, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.RemoveDevice(devicePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device removed"}) -} - -func handleTrustDevice(conn net.Conn, req models.Request, manager *Manager) { - devicePath, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.TrustDevice(devicePath, true); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device trusted"}) -} - -func handleUntrustDevice(conn net.Conn, req models.Request, manager *Manager) { - devicePath, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.TrustDevice(devicePath, false); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "device untrusted"}) -} - -func handlePairingSubmit(conn net.Conn, req models.Request, manager *Manager) { - token, err := params.String(req.Params, "token") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - secrets := params.StringMapOpt(req.Params, "secrets") - accept := params.BoolOpt(req.Params, "accept", false) - - if err := manager.SubmitPairing(token, secrets, accept); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing response submitted"}) -} - -func handlePairingCancel(conn net.Conn, req models.Request, manager *Manager) { - token, err := params.String(req.Params, "token") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.CancelPairing(token); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "pairing cancelled"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := BluetoothEvent{ - Type: "state_changed", - Data: initialState, - } - - if err := json.NewEncoder(conn).Encode(models.Response[BluetoothEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := BluetoothEvent{ - Type: "state_changed", - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[BluetoothEvent]{ - Result: &event, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers_test.go deleted file mode 100644 index 1846eb0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/handlers_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package bluez - -import ( - "context" - "testing" - "time" -) - -func TestBrokerIntegration(t *testing.T) { - broker := NewSubscriptionBroker(nil) - ctx := context.Background() - - req := PromptRequest{ - DevicePath: "/org/bluez/test", - DeviceName: "TestDevice", - RequestType: "pin", - Fields: []string{"pin"}, - } - - token, err := broker.Ask(ctx, req) - if err != nil { - t.Fatalf("Ask failed: %v", err) - } - - go func() { - time.Sleep(50 * time.Millisecond) - broker.Resolve(token, PromptReply{ - Secrets: map[string]string{"pin": "1234"}, - Accept: true, - }) - }() - - reply, err := broker.Wait(ctx, token) - if err != nil { - t.Fatalf("Wait failed: %v", err) - } - - if reply.Secrets["pin"] != "1234" { - t.Errorf("expected pin=1234, got %s", reply.Secrets["pin"]) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/manager.go deleted file mode 100644 index 4408d25..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/manager.go +++ /dev/null @@ -1,588 +0,0 @@ -package bluez - -import ( - "fmt" - "strings" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil" - "github.com/godbus/dbus/v5" -) - -const ( - adapter1Iface = "org.bluez.Adapter1" - objectMgrIface = "org.freedesktop.DBus.ObjectManager" - propertiesIface = "org.freedesktop.DBus.Properties" -) - -func NewManager() (*Manager, error) { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("system bus connection failed: %w", err) - } - - m := &Manager{ - state: &BluetoothState{ - Powered: false, - Discovering: false, - Devices: []Device{}, - PairedDevices: []Device{}, - ConnectedDevices: []Device{}, - }, - stateMutex: sync.RWMutex{}, - - stopChan: make(chan struct{}), - dbusConn: conn, - signals: make(chan *dbus.Signal, 256), - dirty: make(chan struct{}, 1), - eventQueue: make(chan func(), 32), - } - - broker := NewSubscriptionBroker(m.broadcastPairingPrompt) - m.promptBroker = broker - - adapter, err := m.findAdapter() - if err != nil { - conn.Close() - return nil, fmt.Errorf("no bluetooth adapter found: %w", err) - } - m.adapterPath = adapter - - if err := m.initialize(); err != nil { - conn.Close() - return nil, err - } - - if err := m.startAgent(); err != nil { - conn.Close() - return nil, fmt.Errorf("agent start failed: %w", err) - } - - if err := m.startSignalPump(); err != nil { - m.Close() - return nil, err - } - - m.notifierWg.Add(1) - go m.notifier() - - m.eventWg.Add(1) - go m.eventWorker() - - return m, nil -} - -func (m *Manager) findAdapter() (dbus.ObjectPath, error) { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath("/")) - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - - if err := obj.Call(objectMgrIface+".GetManagedObjects", 0).Store(&objects); err != nil { - return "", err - } - - for path, interfaces := range objects { - if _, ok := interfaces[adapter1Iface]; ok { - log.Infof("[BluezManager] found adapter: %s", path) - return path, nil - } - } - - return "", fmt.Errorf("no adapter found") -} - -func (m *Manager) initialize() error { - if err := m.updateAdapterState(); err != nil { - return err - } - - if err := m.updateDevices(); err != nil { - return err - } - - return nil -} - -func (m *Manager) updateAdapterState() error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - - poweredVar, err := obj.GetProperty(adapter1Iface + ".Powered") - if err != nil { - return err - } - - discoveringVar, err := obj.GetProperty(adapter1Iface + ".Discovering") - if err != nil { - return err - } - - m.stateMutex.Lock() - m.state.Powered = dbusutil.AsOr(poweredVar, false) - m.state.Discovering = dbusutil.AsOr(discoveringVar, false) - m.stateMutex.Unlock() - - return nil -} - -func (m *Manager) updateDevices() error { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath("/")) - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - - if err := obj.Call(objectMgrIface+".GetManagedObjects", 0).Store(&objects); err != nil { - return err - } - - devices := []Device{} - paired := []Device{} - connected := []Device{} - - for path, interfaces := range objects { - devProps, ok := interfaces[device1Iface] - if !ok { - continue - } - - if !strings.HasPrefix(string(path), string(m.adapterPath)+"/") { - continue - } - - dev := m.deviceFromProps(string(path), devProps) - devices = append(devices, dev) - - if dev.Paired { - paired = append(paired, dev) - } - if dev.Connected { - connected = append(connected, dev) - } - } - - m.stateMutex.Lock() - m.state.Devices = devices - m.state.PairedDevices = paired - m.state.ConnectedDevices = connected - m.stateMutex.Unlock() - - return nil -} - -func (m *Manager) deviceFromProps(path string, props map[string]dbus.Variant) Device { - return Device{ - Path: path, - Address: dbusutil.GetOr(props, "Address", ""), - Name: dbusutil.GetOr(props, "Name", ""), - Alias: dbusutil.GetOr(props, "Alias", ""), - Paired: dbusutil.GetOr(props, "Paired", false), - Trusted: dbusutil.GetOr(props, "Trusted", false), - Blocked: dbusutil.GetOr(props, "Blocked", false), - Connected: dbusutil.GetOr(props, "Connected", false), - Class: dbusutil.GetOr(props, "Class", uint32(0)), - Icon: dbusutil.GetOr(props, "Icon", ""), - RSSI: dbusutil.GetOr(props, "RSSI", int16(0)), - LegacyPairing: dbusutil.GetOr(props, "LegacyPairing", false), - } -} - -func (m *Manager) startAgent() error { - if m.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - agent, err := NewBluezAgent(m.promptBroker) - if err != nil { - return err - } - - m.agent = agent - return nil -} - -func (m *Manager) startSignalPump() error { - m.dbusConn.Signal(m.signals) - - if err := m.dbusConn.AddMatchSignal( - dbus.WithMatchInterface(propertiesIface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - return err - } - - if err := m.dbusConn.AddMatchSignal( - dbus.WithMatchInterface(objectMgrIface), - dbus.WithMatchMember("InterfacesAdded"), - ); err != nil { - return err - } - - if err := m.dbusConn.AddMatchSignal( - dbus.WithMatchInterface(objectMgrIface), - dbus.WithMatchMember("InterfacesRemoved"), - ); err != nil { - return err - } - - m.sigWG.Add(1) - go func() { - defer m.sigWG.Done() - for { - select { - case <-m.stopChan: - return - case sig, ok := <-m.signals: - if !ok { - return - } - if sig == nil { - continue - } - m.handleSignal(sig) - } - } - }() - - return nil -} - -func (m *Manager) handleSignal(sig *dbus.Signal) { - switch sig.Name { - case propertiesIface + ".PropertiesChanged": - if len(sig.Body) < 2 { - return - } - - iface, ok := sig.Body[0].(string) - if !ok { - return - } - - changed, ok := sig.Body[1].(map[string]dbus.Variant) - if !ok { - return - } - - switch iface { - case adapter1Iface: - if strings.HasPrefix(string(sig.Path), string(m.adapterPath)) { - m.handleAdapterPropertiesChanged(changed) - } - case device1Iface: - m.handleDevicePropertiesChanged(sig.Path, changed) - } - - case objectMgrIface + ".InterfacesAdded": - m.notifySubscribers() - - case objectMgrIface + ".InterfacesRemoved": - m.notifySubscribers() - } -} - -func (m *Manager) handleAdapterPropertiesChanged(changed map[string]dbus.Variant) { - m.stateMutex.Lock() - dirty := false - - if powered, ok := dbusutil.Get[bool](changed, "Powered"); ok { - m.state.Powered = powered - dirty = true - } - if discovering, ok := dbusutil.Get[bool](changed, "Discovering"); ok { - m.state.Discovering = discovering - dirty = true - } - - m.stateMutex.Unlock() - - if dirty { - m.notifySubscribers() - } -} - -func (m *Manager) handleDevicePropertiesChanged(path dbus.ObjectPath, changed map[string]dbus.Variant) { - paired, hasPaired := dbusutil.Get[bool](changed, "Paired") - _, hasConnected := changed["Connected"] - _, hasTrusted := changed["Trusted"] - - if hasPaired { - devicePath := string(path) - if paired { - _, wasPending := m.pendingPairings.LoadAndDelete(devicePath) - if wasPending { - select { - case m.eventQueue <- func() { - time.Sleep(300 * time.Millisecond) - log.Infof("[Bluetooth] Auto-trusting newly paired device: %s", devicePath) - if err := m.TrustDevice(devicePath, true); err != nil { - log.Warnf("[Bluetooth] Auto-trust failed: %v", err) - } - log.Infof("[Bluetooth] Auto-connecting newly paired device: %s", devicePath) - if err := m.ConnectDevice(devicePath); err != nil { - log.Warnf("[Bluetooth] Auto-connect failed: %v", err) - } - }: - default: - } - } - } else { - m.pendingPairings.Delete(devicePath) - } - } - - if hasPaired || hasConnected || hasTrusted { - select { - case m.eventQueue <- func() { - time.Sleep(100 * time.Millisecond) - m.updateDevices() - m.notifySubscribers() - }: - default: - } - } -} - -func (m *Manager) eventWorker() { - defer m.eventWg.Done() - for { - select { - case <-m.stopChan: - return - case event := <-m.eventQueue: - event() - } - } -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 200 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - m.updateDevices() - - currentState := m.snapshotState() - - if m.lastNotifiedState != nil && !stateChanged(m.lastNotifiedState, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan BluetoothState) bool { - select { - case ch <- currentState: - default: - } - return true - }) - - stateCopy := currentState - m.lastNotifiedState = &stateCopy - pending = false - } - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) GetState() BluetoothState { - return m.snapshotState() -} - -func (m *Manager) snapshotState() BluetoothState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - - s := *m.state - s.Devices = append([]Device(nil), m.state.Devices...) - s.PairedDevices = append([]Device(nil), m.state.PairedDevices...) - s.ConnectedDevices = append([]Device(nil), m.state.ConnectedDevices...) - return s -} - -func (m *Manager) Subscribe(id string) chan BluetoothState { - ch := make(chan BluetoothState, 64) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if ch, ok := m.subscribers.LoadAndDelete(id); ok { - close(ch) - } -} - -func (m *Manager) SubscribePairing(id string) chan PairingPrompt { - ch := make(chan PairingPrompt, 16) - m.pairingSubscribers.Store(id, ch) - return ch -} - -func (m *Manager) UnsubscribePairing(id string) { - if ch, ok := m.pairingSubscribers.LoadAndDelete(id); ok { - close(ch) - } -} - -func (m *Manager) broadcastPairingPrompt(prompt PairingPrompt) { - m.pairingSubscribers.Range(func(key string, ch chan PairingPrompt) bool { - select { - case ch <- prompt: - default: - } - return true - }) -} - -func (m *Manager) SubmitPairing(token string, secrets map[string]string, accept bool) error { - if m.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return m.promptBroker.Resolve(token, PromptReply{ - Secrets: secrets, - Accept: accept, - Cancel: false, - }) -} - -func (m *Manager) CancelPairing(token string) error { - if m.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return m.promptBroker.Resolve(token, PromptReply{ - Cancel: true, - }) -} - -func (m *Manager) StartDiscovery() error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - return obj.Call(adapter1Iface+".StartDiscovery", 0).Err -} - -func (m *Manager) StopDiscovery() error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - return obj.Call(adapter1Iface+".StopDiscovery", 0).Err -} - -func (m *Manager) SetPowered(powered bool) error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - return obj.Call(propertiesIface+".Set", 0, adapter1Iface, "Powered", dbus.MakeVariant(powered)).Err -} - -func (m *Manager) PairDevice(devicePath string) error { - m.pendingPairings.Store(devicePath, true) - - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath)) - err := obj.Call(device1Iface+".Pair", 0).Err - - if err != nil { - m.pendingPairings.Delete(devicePath) - } - - return err -} - -func (m *Manager) ConnectDevice(devicePath string) error { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath)) - return obj.Call(device1Iface+".Connect", 0).Err -} - -func (m *Manager) DisconnectDevice(devicePath string) error { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath)) - return obj.Call(device1Iface+".Disconnect", 0).Err -} - -func (m *Manager) RemoveDevice(devicePath string) error { - obj := m.dbusConn.Object(bluezService, m.adapterPath) - return obj.Call(adapter1Iface+".RemoveDevice", 0, dbus.ObjectPath(devicePath)).Err -} - -func (m *Manager) TrustDevice(devicePath string, trusted bool) error { - obj := m.dbusConn.Object(bluezService, dbus.ObjectPath(devicePath)) - return obj.Call(propertiesIface+".Set", 0, device1Iface, "Trusted", dbus.MakeVariant(trusted)).Err -} - -func (m *Manager) Close() { - close(m.stopChan) - m.notifierWg.Wait() - m.eventWg.Wait() - - m.sigWG.Wait() - - if m.signals != nil { - m.dbusConn.RemoveSignal(m.signals) - close(m.signals) - } - - if m.agent != nil { - m.agent.Close() - } - - m.subscribers.Range(func(key string, ch chan BluetoothState) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - m.pairingSubscribers.Range(func(key string, ch chan PairingPrompt) bool { - close(ch) - m.pairingSubscribers.Delete(key) - return true - }) - - if m.dbusConn != nil { - m.dbusConn.Close() - } -} - -func stateChanged(old, new *BluetoothState) bool { - if old.Powered != new.Powered { - return true - } - if old.Discovering != new.Discovering { - return true - } - if len(old.Devices) != len(new.Devices) { - return true - } - if len(old.PairedDevices) != len(new.PairedDevices) { - return true - } - if len(old.ConnectedDevices) != len(new.ConnectedDevices) { - return true - } - for i := range old.Devices { - if old.Devices[i].Path != new.Devices[i].Path { - return true - } - if old.Devices[i].Paired != new.Devices[i].Paired { - return true - } - if old.Devices[i].Connected != new.Devices[i].Connected { - return true - } - } - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/subscription_broker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/subscription_broker.go deleted file mode 100644 index ea04a19..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/subscription_broker.go +++ /dev/null @@ -1,86 +0,0 @@ -package bluez - -import ( - "context" - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type SubscriptionBroker struct { - pending syncmap.Map[string, chan PromptReply] - requests syncmap.Map[string, PromptRequest] - broadcastPrompt func(PairingPrompt) -} - -func NewSubscriptionBroker(broadcastPrompt func(PairingPrompt)) PromptBroker { - return &SubscriptionBroker{ - broadcastPrompt: broadcastPrompt, - } -} - -func (b *SubscriptionBroker) Ask(ctx context.Context, req PromptRequest) (string, error) { - token, err := generateToken() - if err != nil { - return "", err - } - - replyChan := make(chan PromptReply, 1) - b.pending.Store(token, replyChan) - b.requests.Store(token, req) - - if b.broadcastPrompt != nil { - prompt := PairingPrompt{ - Token: token, - DevicePath: req.DevicePath, - DeviceName: req.DeviceName, - DeviceAddr: req.DeviceAddr, - RequestType: req.RequestType, - Fields: req.Fields, - Hints: req.Hints, - Passkey: req.Passkey, - } - b.broadcastPrompt(prompt) - } - - return token, nil -} - -func (b *SubscriptionBroker) Wait(ctx context.Context, token string) (PromptReply, error) { - replyChan, exists := b.pending.Load(token) - if !exists { - return PromptReply{}, fmt.Errorf("unknown token: %s", token) - } - - select { - case <-ctx.Done(): - b.cleanup(token) - return PromptReply{}, errdefs.ErrSecretPromptTimeout - case reply := <-replyChan: - b.cleanup(token) - if reply.Cancel { - return reply, errdefs.ErrSecretPromptCancelled - } - return reply, nil - } -} - -func (b *SubscriptionBroker) Resolve(token string, reply PromptReply) error { - replyChan, exists := b.pending.Load(token) - if !exists { - return fmt.Errorf("unknown or expired token: %s", token) - } - - select { - case replyChan <- reply: - return nil - default: - return fmt.Errorf("failed to deliver reply for token: %s", token) - } -} - -func (b *SubscriptionBroker) cleanup(token string) { - b.pending.Delete(token) - b.requests.Delete(token) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types.go deleted file mode 100644 index 663acca..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types.go +++ /dev/null @@ -1,78 +0,0 @@ -package bluez - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/godbus/dbus/v5" -) - -type BluetoothState struct { - Powered bool `json:"powered"` - Discovering bool `json:"discovering"` - Devices []Device `json:"devices"` - PairedDevices []Device `json:"pairedDevices"` - ConnectedDevices []Device `json:"connectedDevices"` -} - -type Device struct { - Path string `json:"path"` - Address string `json:"address"` - Name string `json:"name"` - Alias string `json:"alias"` - Paired bool `json:"paired"` - Trusted bool `json:"trusted"` - Blocked bool `json:"blocked"` - Connected bool `json:"connected"` - Class uint32 `json:"class"` - Icon string `json:"icon"` - RSSI int16 `json:"rssi"` - LegacyPairing bool `json:"legacyPairing"` -} - -type PromptRequest struct { - DevicePath string `json:"devicePath"` - DeviceName string `json:"deviceName"` - DeviceAddr string `json:"deviceAddr"` - RequestType string `json:"requestType"` - Fields []string `json:"fields"` - Hints []string `json:"hints"` - Passkey *uint32 `json:"passkey,omitempty"` -} - -type PromptReply struct { - Secrets map[string]string `json:"secrets"` - Accept bool `json:"accept"` - Cancel bool `json:"cancel"` -} - -type PairingPrompt struct { - Token string `json:"token"` - DevicePath string `json:"devicePath"` - DeviceName string `json:"deviceName"` - DeviceAddr string `json:"deviceAddr"` - RequestType string `json:"requestType"` - Fields []string `json:"fields"` - Hints []string `json:"hints"` - Passkey *uint32 `json:"passkey,omitempty"` -} - -type Manager struct { - state *BluetoothState - stateMutex sync.RWMutex - subscribers syncmap.Map[string, chan BluetoothState] - stopChan chan struct{} - dbusConn *dbus.Conn - signals chan *dbus.Signal - sigWG sync.WaitGroup - agent *BluezAgent - promptBroker PromptBroker - pairingSubscribers syncmap.Map[string, chan PairingPrompt] - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotifiedState *BluetoothState - adapterPath dbus.ObjectPath - pendingPairings syncmap.Map[string, bool] - eventQueue chan func() - eventWg sync.WaitGroup -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types_test.go deleted file mode 100644 index ab5b857..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/bluez/types_test.go +++ /dev/null @@ -1,210 +0,0 @@ -package bluez - -import ( - "encoding/json" - "testing" -) - -func TestBluetoothStateJSON(t *testing.T) { - state := BluetoothState{ - Powered: true, - Discovering: false, - Devices: []Device{ - { - Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - Address: "AA:BB:CC:DD:EE:FF", - Name: "TestDevice", - Alias: "My Device", - Paired: true, - Trusted: false, - Connected: true, - Class: 0x240418, - Icon: "audio-headset", - RSSI: -50, - }, - }, - PairedDevices: []Device{ - { - Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - Address: "AA:BB:CC:DD:EE:FF", - Paired: true, - }, - }, - ConnectedDevices: []Device{ - { - Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - Address: "AA:BB:CC:DD:EE:FF", - Connected: true, - }, - }, - } - - data, err := json.Marshal(state) - if err != nil { - t.Fatalf("failed to marshal state: %v", err) - } - - var decoded BluetoothState - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal state: %v", err) - } - - if decoded.Powered != state.Powered { - t.Errorf("expected Powered=%v, got %v", state.Powered, decoded.Powered) - } - - if len(decoded.Devices) != 1 { - t.Fatalf("expected 1 device, got %d", len(decoded.Devices)) - } - - if decoded.Devices[0].Address != "AA:BB:CC:DD:EE:FF" { - t.Errorf("expected address AA:BB:CC:DD:EE:FF, got %s", decoded.Devices[0].Address) - } -} - -func TestDeviceJSON(t *testing.T) { - device := Device{ - Path: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - Address: "AA:BB:CC:DD:EE:FF", - Name: "TestDevice", - Alias: "My Device", - Paired: true, - Trusted: true, - Blocked: false, - Connected: true, - Class: 0x240418, - Icon: "audio-headset", - RSSI: -50, - LegacyPairing: false, - } - - data, err := json.Marshal(device) - if err != nil { - t.Fatalf("failed to marshal device: %v", err) - } - - var decoded Device - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal device: %v", err) - } - - if decoded.Address != device.Address { - t.Errorf("expected Address=%s, got %s", device.Address, decoded.Address) - } - - if decoded.Name != device.Name { - t.Errorf("expected Name=%s, got %s", device.Name, decoded.Name) - } - - if decoded.Paired != device.Paired { - t.Errorf("expected Paired=%v, got %v", device.Paired, decoded.Paired) - } - - if decoded.RSSI != device.RSSI { - t.Errorf("expected RSSI=%d, got %d", device.RSSI, decoded.RSSI) - } -} - -func TestPairingPromptJSON(t *testing.T) { - passkey := uint32(123456) - prompt := PairingPrompt{ - Token: "test-token", - DevicePath: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - DeviceName: "TestDevice", - DeviceAddr: "AA:BB:CC:DD:EE:FF", - RequestType: "confirm", - Fields: []string{"decision"}, - Hints: []string{}, - Passkey: &passkey, - } - - data, err := json.Marshal(prompt) - if err != nil { - t.Fatalf("failed to marshal prompt: %v", err) - } - - var decoded PairingPrompt - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal prompt: %v", err) - } - - if decoded.Token != prompt.Token { - t.Errorf("expected Token=%s, got %s", prompt.Token, decoded.Token) - } - - if decoded.DeviceName != prompt.DeviceName { - t.Errorf("expected DeviceName=%s, got %s", prompt.DeviceName, decoded.DeviceName) - } - - if decoded.Passkey == nil { - t.Fatal("expected non-nil Passkey") - } - - if *decoded.Passkey != *prompt.Passkey { - t.Errorf("expected Passkey=%d, got %d", *prompt.Passkey, *decoded.Passkey) - } -} - -func TestPromptReplyJSON(t *testing.T) { - reply := PromptReply{ - Secrets: map[string]string{ - "pin": "1234", - "passkey": "567890", - }, - Accept: true, - Cancel: false, - } - - data, err := json.Marshal(reply) - if err != nil { - t.Fatalf("failed to marshal reply: %v", err) - } - - var decoded PromptReply - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal reply: %v", err) - } - - if decoded.Secrets["pin"] != reply.Secrets["pin"] { - t.Errorf("expected pin=%s, got %s", reply.Secrets["pin"], decoded.Secrets["pin"]) - } - - if decoded.Accept != reply.Accept { - t.Errorf("expected Accept=%v, got %v", reply.Accept, decoded.Accept) - } -} - -func TestPromptRequestJSON(t *testing.T) { - passkey := uint32(123456) - req := PromptRequest{ - DevicePath: "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF", - DeviceName: "TestDevice", - DeviceAddr: "AA:BB:CC:DD:EE:FF", - RequestType: "confirm", - Fields: []string{"decision"}, - Hints: []string{"hint1", "hint2"}, - Passkey: &passkey, - } - - data, err := json.Marshal(req) - if err != nil { - t.Fatalf("failed to marshal request: %v", err) - } - - var decoded PromptRequest - if err := json.Unmarshal(data, &decoded); err != nil { - t.Fatalf("failed to unmarshal request: %v", err) - } - - if decoded.DevicePath != req.DevicePath { - t.Errorf("expected DevicePath=%s, got %s", req.DevicePath, decoded.DevicePath) - } - - if decoded.RequestType != req.RequestType { - t.Errorf("expected RequestType=%s, got %s", req.RequestType, decoded.RequestType) - } - - if len(decoded.Fields) != len(req.Fields) { - t.Errorf("expected %d fields, got %d", len(req.Fields), len(decoded.Fields)) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc.go deleted file mode 100644 index 00cf2d9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc.go +++ /dev/null @@ -1,510 +0,0 @@ -package brightness - -import ( - "encoding/binary" - "fmt" - "math" - "os" - "strings" - "syscall" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "golang.org/x/sys/unix" -) - -const ( - I2C_SLAVE = 0x0703 - DDCCI_ADDR = 0x37 - DDCCI_VCP_GET = 0x01 - DDCCI_VCP_SET = 0x03 - VCP_BRIGHTNESS = 0x10 - DDC_SOURCE_ADDR = 0x51 -) - -func NewDDCBackend() (*DDCBackend, error) { - b := &DDCBackend{ - scanInterval: 30 * time.Second, - debounceTimers: make(map[string]*time.Timer), - debouncePending: make(map[string]ddcPendingSet), - } - - if err := b.scanI2CDevices(); err != nil { - return nil, err - } - - return b, nil -} - -func (b *DDCBackend) scanI2CDevices() error { - return b.scanI2CDevicesInternal(false) -} - -func (b *DDCBackend) ForceRescan() error { - return b.scanI2CDevicesInternal(true) -} - -func (b *DDCBackend) scanI2CDevicesInternal(force bool) error { - b.scanMutex.Lock() - defer b.scanMutex.Unlock() - - if !force && time.Since(b.lastScan) < b.scanInterval { - return nil - } - - activeBuses := make(map[int]bool) - - for i := 0; i < 32; i++ { - busPath := fmt.Sprintf("/dev/i2c-%d", i) - if _, err := os.Stat(busPath); os.IsNotExist(err) { - continue - } - - if isIgnorableI2CBus(i) { - log.Debugf("Skipping ignorable i2c-%d", i) - continue - } - - activeBuses[i] = true - id := fmt.Sprintf("ddc:i2c-%d", i) - - dev, err := b.probeDDCDevice(i) - if err != nil || dev == nil { - continue - } - - dev.id = id - b.devices.Store(id, dev) - log.Debugf("found DDC device on i2c-%d", i) - } - - b.devices.Range(func(id string, dev *ddcDevice) bool { - if !activeBuses[dev.bus] { - b.devices.Delete(id) - log.Debugf("removed DDC device %s (bus no longer exists)", id) - } - return true - }) - - b.lastScan = time.Now() - - return nil -} - -func (b *DDCBackend) probeDDCDevice(bus int) (*ddcDevice, error) { - busPath := fmt.Sprintf("/dev/i2c-%d", bus) - - fd, err := syscall.Open(busPath, syscall.O_RDWR, 0) - if err != nil { - return nil, err - } - defer syscall.Close(fd) - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(DDCCI_ADDR)); errno != 0 { - return nil, errno - } - - dummy := make([]byte, 32) - syscall.Read(fd, dummy) //nolint:errcheck - - writebuf := []byte{0x00} - n, err := syscall.Write(fd, writebuf) - if err == nil && n == len(writebuf) { - name := b.getDDCName(bus) - dev := &ddcDevice{ - bus: bus, - addr: DDCCI_ADDR, - name: name, - } - b.readInitialBrightness(fd, dev) - return dev, nil - } - - readbuf := make([]byte, 4) - n, err = syscall.Read(fd, readbuf) - if err != nil || n == 0 { - return nil, fmt.Errorf("x37 unresponsive") - } - - name := b.getDDCName(bus) - - dev := &ddcDevice{ - bus: bus, - addr: DDCCI_ADDR, - name: name, - } - b.readInitialBrightness(fd, dev) - return dev, nil -} - -func (b *DDCBackend) getDDCName(bus int) string { - sysfsPath := fmt.Sprintf("/sys/class/i2c-adapter/i2c-%d/name", bus) - data, err := os.ReadFile(sysfsPath) - if err != nil { - return fmt.Sprintf("I2C-%d", bus) - } - - name := strings.TrimSpace(string(data)) - if name == "" { - name = fmt.Sprintf("I2C-%d", bus) - } - - return name -} - -func (b *DDCBackend) readInitialBrightness(fd int, dev *ddcDevice) { - cap, err := b.getVCPFeature(fd, VCP_BRIGHTNESS) - if err != nil { - log.Debugf("failed to read initial brightness for %s: %v", dev.name, err) - return - } - - dev.max = cap.max - dev.lastBrightness = cap.current - log.Debugf("initialized %s with brightness %d/%d", dev.name, cap.current, cap.max) -} - -func (b *DDCBackend) GetDevices() ([]Device, error) { - if err := b.scanI2CDevices(); err != nil { - log.Debugf("DDC scan error: %v", err) - } - - devices := make([]Device, 0) - - b.devices.Range(func(id string, dev *ddcDevice) bool { - devices = append(devices, Device{ - Class: ClassDDC, - ID: id, - Name: dev.name, - Current: dev.lastBrightness, - Max: dev.max, - CurrentPercent: dev.lastBrightness, - Backend: "ddc", - }) - return true - }) - - return devices, nil -} - -func (b *DDCBackend) SetBrightness(id string, value int, exponential bool, callback func()) error { - return b.SetBrightnessWithExponent(id, value, exponential, 1.2, callback) -} - -func (b *DDCBackend) SetBrightnessWithExponent(id string, value int, exponential bool, exponent float64, callback func()) error { - _, ok := b.devices.Load(id) - - if !ok { - if err := b.scanI2CDevicesInternal(true); err != nil { - log.Debugf("rescan failed for %s: %v", id, err) - } - _, ok = b.devices.Load(id) - } - - if !ok { - return fmt.Errorf("device not found: %s", id) - } - - if value < 0 { - return fmt.Errorf("value out of range: %d", value) - } - - b.debounceMutex.Lock() - b.debouncePending[id] = ddcPendingSet{ - percent: value, - callback: callback, - } - - if timer, exists := b.debounceTimers[id]; exists { - timer.Reset(200 * time.Millisecond) - } else { - b.debounceWg.Add(1) - b.debounceTimers[id] = time.AfterFunc(200*time.Millisecond, func() { - defer b.debounceWg.Done() - b.debounceMutex.Lock() - pending, exists := b.debouncePending[id] - if exists { - delete(b.debouncePending, id) - } - b.debounceMutex.Unlock() - - if !exists { - return - } - - err := b.setBrightnessImmediateWithExponent(id, pending.percent) - if err != nil { - log.Debugf("Failed to set brightness for %s: %v", id, err) - } - - if pending.callback != nil { - pending.callback() - } - }) - } - b.debounceMutex.Unlock() - - return nil -} - -func (b *DDCBackend) setBrightnessImmediateWithExponent(id string, value int) error { - dev, ok := b.devices.Load(id) - - if !ok { - if err := b.scanI2CDevicesInternal(true); err != nil { - log.Debugf("rescan failed for %s: %v", id, err) - } - dev, ok = b.devices.Load(id) - } - - if !ok { - return fmt.Errorf("device not found: %s", id) - } - - busPath := fmt.Sprintf("/dev/i2c-%d", dev.bus) - - if _, err := os.Stat(busPath); os.IsNotExist(err) { - b.devices.Delete(id) - log.Debugf("removed stale DDC device %s (bus no longer exists)", id) - return fmt.Errorf("device disconnected: %s", id) - } - - fd, err := syscall.Open(busPath, syscall.O_RDWR, 0) - if err != nil { - b.devices.Delete(id) - log.Debugf("removed DDC device %s (open failed: %v)", id, err) - return fmt.Errorf("open i2c device: %w", err) - } - defer syscall.Close(fd) - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(dev.addr)); errno != 0 { - return fmt.Errorf("set i2c slave addr: %w", errno) - } - - max := dev.max - if max == 0 { - cap, err := b.getVCPFeature(fd, VCP_BRIGHTNESS) - if err != nil { - return fmt.Errorf("get current capability: %w", err) - } - max = cap.max - dev.max = max - b.devices.Store(id, dev) - } - - if err := b.setVCPFeature(fd, VCP_BRIGHTNESS, value); err != nil { - return fmt.Errorf("set vcp feature: %w", err) - } - - log.Debugf("set %s to %d/%d", id, value, max) - - dev.max = max - dev.lastBrightness = value - b.devices.Store(id, dev) - - return nil -} - -func (b *DDCBackend) getVCPFeature(fd int, vcp byte) (*ddcCapability, error) { - for flushTry := 0; flushTry < 3; flushTry++ { - dummy := make([]byte, 32) - n, _ := syscall.Read(fd, dummy) - if n == 0 { - break - } - time.Sleep(20 * time.Millisecond) - } - - data := []byte{ - DDCCI_VCP_GET, - vcp, - } - - payload := []byte{ - DDC_SOURCE_ADDR, - byte(len(data)) | 0x80, - } - payload = append(payload, data...) - payload = append(payload, ddcciChecksum(payload)) - - n, err := syscall.Write(fd, payload) - if err != nil || n != len(payload) { - return nil, fmt.Errorf("write i2c: %w", err) - } - - time.Sleep(50 * time.Millisecond) - - pollFds := []unix.PollFd{ - { - Fd: int32(fd), - Events: unix.POLLIN, - }, - } - - pollTimeout := 200 - pollResult, err := unix.Poll(pollFds, pollTimeout) - if err != nil { - return nil, fmt.Errorf("poll i2c: %w", err) - } - if pollResult == 0 { - return nil, fmt.Errorf("poll timeout after %dms", pollTimeout) - } - if pollFds[0].Revents&unix.POLLIN == 0 { - return nil, fmt.Errorf("poll returned but POLLIN not set") - } - - response := make([]byte, 12) - n, err = syscall.Read(fd, response) - if err != nil || n < 8 { - return nil, fmt.Errorf("read i2c: %w", err) - } - - if response[0] != 0x6E || response[2] != 0x02 { - return nil, fmt.Errorf("invalid ddc response") - } - - resultCode := response[3] - if resultCode != 0x00 { - return nil, fmt.Errorf("vcp feature not supported") - } - - responseVCP := response[4] - if responseVCP != vcp { - return nil, fmt.Errorf("vcp mismatch: wanted 0x%02x, got 0x%02x", vcp, responseVCP) - } - - maxHigh := response[6] - maxLow := response[7] - currentHigh := response[8] - currentLow := response[9] - - max := int(binary.BigEndian.Uint16([]byte{maxHigh, maxLow})) - current := int(binary.BigEndian.Uint16([]byte{currentHigh, currentLow})) - - return &ddcCapability{ - vcp: vcp, - max: max, - current: current, - }, nil -} - -func ddcciChecksum(payload []byte) byte { - sum := byte(0x6E) - for _, b := range payload { - sum ^= b - } - return sum -} - -func (b *DDCBackend) setVCPFeature(fd int, vcp byte, value int) error { - data := []byte{ - DDCCI_VCP_SET, - vcp, - byte(value >> 8), - byte(value & 0xFF), - } - - payload := []byte{ - DDC_SOURCE_ADDR, - byte(len(data)) | 0x80, - } - payload = append(payload, data...) - payload = append(payload, ddcciChecksum(payload)) - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), I2C_SLAVE, uintptr(DDCCI_ADDR)); errno != 0 { - return fmt.Errorf("set i2c slave for write: %w", errno) - } - - n, err := syscall.Write(fd, payload) - if err != nil || n != len(payload) { - return fmt.Errorf("write i2c: wrote %d/%d: %w", n, len(payload), err) - } - - time.Sleep(50 * time.Millisecond) - - return nil -} - -func (b *DDCBackend) percentToValue(percent int, max int, exponential bool) int { - const minValue = 1 - - if percent == 0 { - return minValue - } - - usableRange := max - minValue - var value int - - if exponential { - const exponent = 2.0 - normalizedPercent := float64(percent) / 100.0 - hardwarePercent := math.Pow(normalizedPercent, 1.0/exponent) - value = minValue + int(math.Round(hardwarePercent*float64(usableRange))) - } else { - value = minValue + ((percent - 1) * usableRange / 99) - } - - if value < minValue { - value = minValue - } - if value > max { - value = max - } - - return value -} - -func (b *DDCBackend) valueToPercent(value int, max int, exponential bool) int { - const minValue = 1 - - if max == 0 { - return 0 - } - - if value <= minValue { - return 1 - } - - usableRange := max - minValue - if usableRange == 0 { - return 100 - } - - var percent int - - if exponential { - const exponent = 2.0 - linearPercent := 1 + ((value - minValue) * 99 / usableRange) - normalizedLinear := float64(linearPercent) / 100.0 - expPercent := math.Pow(normalizedLinear, exponent) - percent = int(math.Round(expPercent * 100.0)) - } else { - percent = 1 + ((value - minValue) * 99 / usableRange) - } - - if percent > 100 { - percent = 100 - } - if percent < 1 { - percent = 1 - } - - return percent -} - -func (b *DDCBackend) WaitPending() { - done := make(chan struct{}) - go func() { - b.debounceWg.Wait() - close(done) - }() - - select { - case <-done: - case <-time.After(5 * time.Second): - log.Debug("WaitPending timed out waiting for DDC writes") - } -} - -func (b *DDCBackend) Close() { -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_filter.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_filter.go deleted file mode 100644 index 12fc535..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_filter.go +++ /dev/null @@ -1,135 +0,0 @@ -package brightness - -import ( - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -// isIgnorableI2CBus checks if an I2C bus should be skipped during DDC probing. -// Based on ddcutil's sysfs_is_ignorable_i2c_device() (sysfs_base.c:1441) -func isIgnorableI2CBus(busno int) bool { - name := getI2CDeviceSysfsName(busno) - driver := getI2CSysfsDriver(busno) - - if name != "" && isIgnorableI2CDeviceName(name, driver) { - log.Debugf("i2c-%d: ignoring '%s' (driver: %s)", busno, name, driver) - return true - } - - // Only probe display adapters (0x03xxxx) and docking stations (0x0axxxx) - class := getI2CDeviceSysfsClass(busno) - if class != 0 { - classHigh := class & 0xFFFF0000 - ignorable := (classHigh != 0x030000 && classHigh != 0x0A0000) - if ignorable { - log.Debugf("i2c-%d: ignoring class 0x%08x", busno, class) - } - return ignorable - } - - return false -} - -// Based on ddcutil's ignorable_i2c_device_sysfs_name() (sysfs_base.c:1408) -func isIgnorableI2CDeviceName(name, driver string) bool { - ignorablePrefixes := []string{ - "SMBus", - "Synopsys DesignWare", - "soc:i2cdsi", - "smu", - "mac-io", - "u4", - "AMDGPU SMU", // AMD Navi2+ - probing hangs GPU - } - - for _, prefix := range ignorablePrefixes { - if strings.HasPrefix(name, prefix) { - return true - } - } - - // nouveau driver: only nvkm-* buses are valid - if driver == "nouveau" && !strings.HasPrefix(name, "nvkm-") { - return true - } - - return false -} - -// Based on ddcutil's get_i2c_device_sysfs_name() (sysfs_base.c:1175) -func getI2CDeviceSysfsName(busno int) string { - path := fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/name", busno) - data, err := os.ReadFile(path) - if err != nil { - return "" - } - return strings.TrimSpace(string(data)) -} - -// Based on ddcutil's get_i2c_device_sysfs_class() (sysfs_base.c:1380) -func getI2CDeviceSysfsClass(busno int) uint32 { - classPath := fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/device/class", busno) - data, err := os.ReadFile(classPath) - if err != nil { - classPath = fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d/device/device/device/class", busno) - data, err = os.ReadFile(classPath) - if err != nil { - return 0 - } - } - - classStr := strings.TrimSpace(string(data)) - classStr = strings.TrimPrefix(classStr, "0x") - - class, err := strconv.ParseUint(classStr, 16, 32) - if err != nil { - return 0 - } - - return uint32(class) -} - -// Based on ddcutil's get_i2c_sysfs_driver_by_busno() (sysfs_base.c:1284) -func getI2CSysfsDriver(busno int) string { - devicePath := fmt.Sprintf("/sys/bus/i2c/devices/i2c-%d", busno) - adapterPath, err := findI2CAdapter(devicePath) - if err != nil { - return "" - } - - driverLink := filepath.Join(adapterPath, "driver") - target, err := os.Readlink(driverLink) - if err != nil { - return "" - } - - return filepath.Base(target) -} - -func findI2CAdapter(devicePath string) (string, error) { - currentPath := devicePath - - for depth := 0; depth < 10; depth++ { - if _, err := os.Stat(filepath.Join(currentPath, "name")); err == nil { - return currentPath, nil - } - - deviceLink := filepath.Join(currentPath, "device") - target, err := os.Readlink(deviceLink) - if err != nil { - break - } - - if !filepath.IsAbs(target) { - target = filepath.Join(filepath.Dir(currentPath), target) - } - currentPath = filepath.Clean(target) - } - - return "", fmt.Errorf("could not find adapter for %s", devicePath) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_filter_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_filter_test.go deleted file mode 100644 index dc3751f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_filter_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package brightness - -import ( - "testing" -) - -func TestIsIgnorableI2CDeviceName(t *testing.T) { - tests := []struct { - name string - deviceName string - driver string - want bool - }{ - { - name: "AMDGPU SMU should be ignored", - deviceName: "AMDGPU SMU", - driver: "amdgpu", - want: true, - }, - { - name: "SMBus should be ignored", - deviceName: "SMBus I801 adapter", - driver: "", - want: true, - }, - { - name: "Synopsys DesignWare should be ignored", - deviceName: "Synopsys DesignWare I2C adapter", - driver: "", - want: true, - }, - { - name: "smu prefix should be ignored (Mac G5)", - deviceName: "smu-i2c-controller", - driver: "", - want: true, - }, - { - name: "Regular NVIDIA DDC should not be ignored", - deviceName: "NVIDIA i2c adapter 1", - driver: "nvidia", - want: false, - }, - { - name: "nouveau nvkm bus should not be ignored", - deviceName: "nvkm-0000:01:00.0-bus-0000", - driver: "nouveau", - want: false, - }, - { - name: "nouveau non-nvkm bus should be ignored", - deviceName: "nouveau-other-bus", - driver: "nouveau", - want: true, - }, - { - name: "Regular AMD display adapter should not be ignored", - deviceName: "AMDGPU DM i2c hw bus 0", - driver: "amdgpu", - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := isIgnorableI2CDeviceName(tt.deviceName, tt.driver) - if got != tt.want { - t.Errorf("isIgnorableI2CDeviceName(%q, %q) = %v, want %v", - tt.deviceName, tt.driver, got, tt.want) - } - }) - } -} - -func TestClassFiltering(t *testing.T) { - tests := []struct { - name string - class uint32 - want bool - }{ - { - name: "Display adapter class should not be ignored", - class: 0x030000, - want: false, - }, - { - name: "Docking station class should not be ignored", - class: 0x0a0000, - want: false, - }, - { - name: "Display adapter with subclass should not be ignored", - class: 0x030001, - want: false, - }, - { - name: "SMBus class should be ignored", - class: 0x0c0500, - want: true, - }, - { - name: "Bridge class should be ignored", - class: 0x060400, - want: true, - }, - { - name: "Generic system peripheral should be ignored", - class: 0x088000, - want: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - classHigh := tt.class & 0xFFFF0000 - ignorable := (classHigh != 0x030000 && classHigh != 0x0A0000) - if ignorable != tt.want { - t.Errorf("class 0x%08x: ignorable = %v, want %v", tt.class, ignorable, tt.want) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_test.go deleted file mode 100644 index 7f85510..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/ddc_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package brightness - -import ( - "testing" -) - -func TestDDCBackend_PercentConversions(t *testing.T) { - tests := []struct { - name string - max int - percent int - wantValue int - }{ - { - name: "0% should map to minValue=1", - max: 100, - percent: 0, - wantValue: 1, - }, - { - name: "1% should be 1", - max: 100, - percent: 1, - wantValue: 1, - }, - { - name: "50% should be ~50", - max: 100, - percent: 50, - wantValue: 50, - }, - { - name: "100% should be max", - max: 100, - percent: 100, - wantValue: 100, - }, - } - - b := &DDCBackend{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := b.percentToValue(tt.percent, tt.max, false) - diff := got - tt.wantValue - if diff < 0 { - diff = -diff - } - if diff > 1 { - t.Errorf("percentToValue() = %v, want %v (±1)", got, tt.wantValue) - } - }) - } -} - -func TestDDCBackend_ValueToPercent(t *testing.T) { - tests := []struct { - name string - max int - value int - wantPercent int - tolerance int - }{ - { - name: "zero value should be 1%", - max: 100, - value: 0, - wantPercent: 1, - tolerance: 0, - }, - { - name: "min value should be 1%", - max: 100, - value: 1, - wantPercent: 1, - tolerance: 0, - }, - { - name: "mid value should be ~50%", - max: 100, - value: 50, - wantPercent: 50, - tolerance: 2, - }, - { - name: "max value should be 100%", - max: 100, - value: 100, - wantPercent: 100, - tolerance: 0, - }, - } - - b := &DDCBackend{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := b.valueToPercent(tt.value, tt.max, false) - diff := got - tt.wantPercent - if diff < 0 { - diff = -diff - } - if diff > tt.tolerance { - t.Errorf("valueToPercent() = %v, want %v (±%d)", got, tt.wantPercent, tt.tolerance) - } - }) - } -} - -func TestDDCBackend_RoundTrip(t *testing.T) { - b := &DDCBackend{} - - tests := []struct { - name string - max int - percent int - }{ - {"1%", 100, 1}, - {"25%", 100, 25}, - {"50%", 100, 50}, - {"75%", 100, 75}, - {"100%", 100, 100}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - value := b.percentToValue(tt.percent, tt.max, false) - gotPercent := b.valueToPercent(value, tt.max, false) - - if diff := tt.percent - gotPercent; diff < -1 || diff > 1 { - t.Errorf("round trip failed: wanted %d%%, got %d%% (value=%d)", tt.percent, gotPercent, value) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/handlers.go deleted file mode 100644 index c71163e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/handlers.go +++ /dev/null @@ -1,124 +0,0 @@ -package brightness - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -func HandleRequest(conn net.Conn, req models.Request, m *Manager) { - switch req.Method { - case "brightness.getState": - handleGetState(conn, req, m) - case "brightness.setBrightness": - handleSetBrightness(conn, req, m) - case "brightness.increment": - handleIncrement(conn, req, m) - case "brightness.decrement": - handleDecrement(conn, req, m) - case "brightness.rescan": - handleRescan(conn, req, m) - case "brightness.subscribe": - handleSubscribe(conn, req, m) - default: - models.RespondError(conn, req.ID, "unknown method: "+req.Method) - } -} - -func handleGetState(conn net.Conn, req models.Request, m *Manager) { - models.Respond(conn, req.ID, m.GetState()) -} - -func handleSetBrightness(conn net.Conn, req models.Request, m *Manager) { - device, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - percent, err := params.Int(req.Params, "percent") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - exponential := params.BoolOpt(req.Params, "exponential", false) - exponent := params.FloatOpt(req.Params, "exponent", 1.2) - - if err := m.SetBrightnessWithExponent(device, percent, exponential, exponent); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, m.GetState()) -} - -func handleIncrement(conn net.Conn, req models.Request, m *Manager) { - device, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - step := params.IntOpt(req.Params, "step", 10) - exponential := params.BoolOpt(req.Params, "exponential", false) - exponent := params.FloatOpt(req.Params, "exponent", 1.2) - - if err := m.IncrementBrightnessWithExponent(device, step, exponential, exponent); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, m.GetState()) -} - -func handleDecrement(conn net.Conn, req models.Request, m *Manager) { - device, err := params.String(req.Params, "device") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - step := params.IntOpt(req.Params, "step", 10) - exponential := params.BoolOpt(req.Params, "exponential", false) - exponent := params.FloatOpt(req.Params, "exponent", 1.2) - - if err := m.IncrementBrightnessWithExponent(device, -step, exponential, exponent); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, m.GetState()) -} - -func handleRescan(conn net.Conn, req models.Request, m *Manager) { - m.Rescan() - models.Respond(conn, req.ID, m.GetState()) -} - -func handleSubscribe(conn net.Conn, req models.Request, m *Manager) { - clientID := fmt.Sprintf("brightness-%d", req.ID) - - ch := m.Subscribe(clientID) - defer m.Unsubscribe(clientID) - - initialState := m.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range ch { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &state, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/logind.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/logind.go deleted file mode 100644 index 8449ab5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/logind.go +++ /dev/null @@ -1,66 +0,0 @@ -package brightness - -import ( - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" -) - -type DBusConn interface { - Object(dest string, path dbus.ObjectPath) dbus.BusObject - Close() error -} - -type LogindBackend struct { - conn DBusConn -} - -func NewLogindBackend() (*LogindBackend, error) { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("connect to system bus: %w", err) - } - - obj := conn.Object("org.freedesktop.login1", "/org/freedesktop/login1/session/auto") - call := obj.Call("org.freedesktop.DBus.Peer.Ping", 0) - if call.Err != nil { - conn.Close() - return nil, fmt.Errorf("logind not available: %w", call.Err) - } - - conn.Close() - - return &LogindBackend{}, nil -} - -func NewLogindBackendWithConn(conn DBusConn) *LogindBackend { - return &LogindBackend{ - conn: conn, - } -} - -func (b *LogindBackend) SetBrightness(subsystem, name string, brightness uint32) error { - if b.conn == nil { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("connect to system bus: %w", err) - } - b.conn = conn - } - - obj := b.conn.Object("org.freedesktop.login1", "/org/freedesktop/login1/session/auto") - call := obj.Call("org.freedesktop.login1.Session.SetBrightness", 0, subsystem, name, brightness) - if call.Err != nil { - return fmt.Errorf("dbus call failed: %w", call.Err) - } - - log.Debugf("logind: set %s/%s to %d", subsystem, name, brightness) - return nil -} - -func (b *LogindBackend) Close() { - if b.conn != nil { - b.conn.Close() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/logind_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/logind_test.go deleted file mode 100644 index 3d4c2ee..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/logind_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package brightness - -import ( - "errors" - "testing" - - mocks_brightness "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/brightness" - mock_dbus "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/mock" -) - -func TestLogindBackend_SetBrightness_Success(t *testing.T) { - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - backend := NewLogindBackendWithConn(mockConn) - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", dbus.Flags(0), "backlight", "nvidia_0", uint32(75)). - Return(&dbus.Call{Err: nil}). - Once() - - err := backend.SetBrightness("backlight", "nvidia_0", 75) - if err != nil { - t.Errorf("SetBrightness() error = %v, want nil", err) - } -} - -func TestLogindBackend_SetBrightness_DBusError(t *testing.T) { - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - backend := NewLogindBackendWithConn(mockConn) - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - dbusErr := errors.New("permission denied") - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(&dbus.Call{Err: dbusErr}). - Once() - - err := backend.SetBrightness("backlight", "test_device", 50) - if err == nil { - t.Error("SetBrightness() error = nil, want error") - } -} - -func TestLogindBackend_SetBrightness_LEDDevice(t *testing.T) { - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - backend := NewLogindBackendWithConn(mockConn) - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", dbus.Flags(0), "leds", "test_led", uint32(128)). - Return(&dbus.Call{Err: nil}). - Once() - - err := backend.SetBrightness("leds", "test_led", 128) - if err != nil { - t.Errorf("SetBrightness() error = %v, want nil", err) - } -} - -func TestLogindBackend_Close(t *testing.T) { - mockConn := mocks_brightness.NewMockDBusConn(t) - backend := NewLogindBackendWithConn(mockConn) - - mockConn.EXPECT(). - Close(). - Return(nil). - Once() - - backend.Close() -} - -func TestLogindBackend_Close_NilConn(t *testing.T) { - backend := &LogindBackend{conn: nil} - backend.Close() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/manager.go deleted file mode 100644 index 4cf7dd4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/manager.go +++ /dev/null @@ -1,389 +0,0 @@ -package brightness - -import ( - "fmt" - "sort" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -func NewManager() (*Manager, error) { - return NewManagerWithOptions(false) -} - -func NewManagerWithOptions(exponential bool) (*Manager, error) { - m := &Manager{ - stopChan: make(chan struct{}), - exponential: exponential, - } - - go m.initLogind() - go m.initSysfs() - go m.initDDC() - - return m, nil -} - -func (m *Manager) initLogind() { - log.Debug("Initializing logind backend...") - logind, err := NewLogindBackend() - if err != nil { - log.Infof("Logind backend not available: %v", err) - log.Info("Will use direct sysfs access for brightness control") - return - } - - m.logindBackend = logind - m.logindReady = true - log.Info("Logind backend initialized - will use for brightness control") -} - -func (m *Manager) initSysfs() { - log.Debug("Initializing sysfs backend...") - sysfs, err := NewSysfsBackend() - if err != nil { - log.Warnf("Failed to initialize sysfs backend: %v", err) - return - } - - devices, err := sysfs.GetDevices() - if err != nil { - log.Warnf("Failed to get initial sysfs devices: %v", err) - m.sysfsBackend = sysfs - m.sysfsReady = true - m.updateState() - m.initUdev() - return - } - - log.Infof("Sysfs backend initialized with %d devices", len(devices)) - for _, d := range devices { - log.Debugf(" - %s: %s (%d%%)", d.ID, d.Name, d.CurrentPercent) - } - - m.sysfsBackend = sysfs - m.sysfsReady = true - m.updateState() - m.initUdev() -} - -func (m *Manager) initUdev() { - m.udevMonitor = NewUdevMonitor(m) -} - -func (m *Manager) initDDC() { - ddc, err := NewDDCBackend() - if err != nil { - log.Debugf("Failed to initialize DDC backend: %v", err) - return - } - - m.ddcBackend = ddc - m.ddcReady = true - log.Info("DDC backend initialized") - - m.updateState() -} - -func (m *Manager) Rescan() { - log.Debug("Rescanning brightness devices...") - - if m.ddcReady && m.ddcBackend != nil { - if err := m.ddcBackend.ForceRescan(); err != nil { - log.Debugf("DDC force rescan failed: %v", err) - } - } - - if m.sysfsReady && m.sysfsBackend != nil { - if err := m.sysfsBackend.Rescan(); err != nil { - log.Debugf("Sysfs rescan failed: %v", err) - } - } - - m.updateState() -} - -func sortDevices(devices []Device) { - sort.Slice(devices, func(i, j int) bool { - classOrder := map[DeviceClass]int{ - ClassBacklight: 0, - ClassDDC: 1, - ClassLED: 2, - } - - orderI := classOrder[devices[i].Class] - orderJ := classOrder[devices[j].Class] - - if orderI != orderJ { - return orderI < orderJ - } - - return devices[i].Name < devices[j].Name - }) -} - -func stateChanged(old, new State) bool { - if len(old.Devices) != len(new.Devices) { - return true - } - - oldMap := make(map[string]Device) - for _, d := range old.Devices { - oldMap[d.ID] = d - } - - for _, newDev := range new.Devices { - oldDev, exists := oldMap[newDev.ID] - if !exists { - return true - } - if oldDev.Current != newDev.Current || oldDev.Max != newDev.Max { - return true - } - } - - return false -} - -func (m *Manager) updateState() { - allDevices := make([]Device, 0) - - if m.sysfsReady && m.sysfsBackend != nil { - devices, err := m.sysfsBackend.GetDevices() - if err != nil { - log.Debugf("Failed to get sysfs devices: %v", err) - } - if err == nil { - allDevices = append(allDevices, devices...) - } - } - - if m.ddcReady && m.ddcBackend != nil { - devices, err := m.ddcBackend.GetDevices() - if err != nil { - log.Debugf("Failed to get DDC devices: %v", err) - } - if err == nil { - allDevices = append(allDevices, devices...) - } - } - - sortDevices(allDevices) - - m.stateMutex.Lock() - oldState := m.state - newState := State{Devices: allDevices} - - if !stateChanged(oldState, newState) { - m.stateMutex.Unlock() - return - } - - m.state = newState - m.stateMutex.Unlock() - log.Debugf("State changed, notifying subscribers") - m.NotifySubscribers() -} - -func (m *Manager) SetBrightness(deviceID string, percent int) error { - return m.SetBrightnessWithMode(deviceID, percent, m.exponential) -} - -func (m *Manager) SetBrightnessWithMode(deviceID string, percent int, exponential bool) error { - return m.SetBrightnessWithExponent(deviceID, percent, exponential, 1.2) -} - -func (m *Manager) SetBrightnessWithExponent(deviceID string, percent int, exponential bool, exponent float64) error { - if percent < 0 { - return fmt.Errorf("percent out of range: %d", percent) - } - - log.Debugf("SetBrightness: %s to %d%%", deviceID, percent) - - m.stateMutex.Lock() - currentState := m.state - var found bool - var deviceClass DeviceClass - var deviceIndex int - - log.Debugf("Current state has %d devices", len(currentState.Devices)) - - for i, dev := range currentState.Devices { - if dev.ID == deviceID { - found = true - deviceClass = dev.Class - deviceIndex = i - break - } - } - - if !found { - m.stateMutex.Unlock() - log.Debugf("Device not found in state: %s", deviceID) - return fmt.Errorf("device not found: %s", deviceID) - } - - newDevices := make([]Device, len(currentState.Devices)) - copy(newDevices, currentState.Devices) - newDevices[deviceIndex].CurrentPercent = percent - m.state = State{Devices: newDevices} - m.stateMutex.Unlock() - - var err error - if deviceClass == ClassDDC { - log.Debugf("Calling DDC backend for %s", deviceID) - err = m.ddcBackend.SetBrightnessWithExponent(deviceID, percent, exponential, exponent, func() { - m.updateState() - m.debouncedBroadcast(deviceID) - }) - } else if m.logindReady && m.logindBackend != nil { - log.Debugf("Calling logind backend for %s", deviceID) - err = m.setViaSysfsWithLogindWithExponent(deviceID, percent, exponential, exponent) - } else { - log.Debugf("Calling sysfs backend for %s", deviceID) - err = m.sysfsBackend.SetBrightnessWithExponent(deviceID, percent, exponential, exponent) - } - - if err != nil { - m.updateState() - return fmt.Errorf("failed to set brightness: %w", err) - } - - if deviceClass != ClassDDC { - log.Debugf("Queueing broadcast for %s", deviceID) - m.debouncedBroadcast(deviceID) - } - return nil -} - -func (m *Manager) IncrementBrightness(deviceID string, step int) error { - return m.IncrementBrightnessWithMode(deviceID, step, m.exponential) -} - -func (m *Manager) IncrementBrightnessWithMode(deviceID string, step int, exponential bool) error { - return m.IncrementBrightnessWithExponent(deviceID, step, exponential, 1.2) -} - -func (m *Manager) IncrementBrightnessWithExponent(deviceID string, step int, exponential bool, exponent float64) error { - m.stateMutex.RLock() - currentState := m.state - m.stateMutex.RUnlock() - - var currentPercent int - var found bool - - for _, dev := range currentState.Devices { - if dev.ID == deviceID { - currentPercent = dev.CurrentPercent - found = true - break - } - } - - if !found { - return fmt.Errorf("device not found: %s", deviceID) - } - - newPercent := currentPercent + step - if newPercent > 100 { - newPercent = 100 - } - if newPercent < 0 { - newPercent = 0 - } - - return m.SetBrightnessWithExponent(deviceID, newPercent, exponential, exponent) -} - -func (m *Manager) DecrementBrightness(deviceID string, step int) error { - return m.IncrementBrightness(deviceID, -step) -} - -func (m *Manager) setViaSysfsWithLogindWithExponent(deviceID string, percent int, exponential bool, exponent float64) error { - parts := strings.SplitN(deviceID, ":", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid device id: %s", deviceID) - } - - subsystem := parts[0] - name := parts[1] - - dev, err := m.sysfsBackend.GetDevice(deviceID) - if err != nil { - return err - } - - value := m.sysfsBackend.PercentToValueWithExponent(percent, dev, exponential, exponent) - - if m.logindBackend == nil { - return m.sysfsBackend.SetBrightnessWithExponent(deviceID, percent, exponential, exponent) - } - - err = m.logindBackend.SetBrightness(subsystem, name, uint32(value)) - if err != nil { - log.Debugf("logind SetBrightness failed, falling back to direct sysfs: %v", err) - return m.sysfsBackend.SetBrightnessWithExponent(deviceID, percent, exponential, exponent) - } - - log.Debugf("set %s to %d%% (%d/%d) via logind", deviceID, percent, value, dev.maxBrightness) - return nil -} - -func (m *Manager) debouncedBroadcast(deviceID string) { - m.broadcastMutex.Lock() - defer m.broadcastMutex.Unlock() - - m.broadcastPending = true - m.pendingDeviceID = deviceID - - if m.broadcastTimer == nil { - m.broadcastTimer = time.AfterFunc(150*time.Millisecond, func() { - m.broadcastMutex.Lock() - pending := m.broadcastPending - deviceID := m.pendingDeviceID - m.broadcastPending = false - m.pendingDeviceID = "" - m.broadcastMutex.Unlock() - - if !pending || deviceID == "" { - return - } - - m.broadcastDeviceUpdate(deviceID) - }) - } else { - m.broadcastTimer.Reset(150 * time.Millisecond) - } -} - -func (m *Manager) broadcastDeviceUpdate(deviceID string) { - m.stateMutex.RLock() - var targetDevice *Device - for _, dev := range m.state.Devices { - if dev.ID == deviceID { - devCopy := dev - targetDevice = &devCopy - break - } - } - m.stateMutex.RUnlock() - - if targetDevice == nil { - log.Debugf("Device not found for broadcast: %s", deviceID) - return - } - - update := DeviceUpdate{Device: *targetDevice} - - log.Debugf("Broadcasting device update: %s at %d%%", deviceID, targetDevice.CurrentPercent) - - m.updateSubscribers.Range(func(key string, ch chan DeviceUpdate) bool { - select { - case ch <- update: - default: - } - return true - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/manager_test.go deleted file mode 100644 index a8ae0f8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/manager_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package brightness - -import ( - "testing" -) - -// Manager tests can be added here as needed -func TestManager_Placeholder(t *testing.T) { - // Placeholder test to keep the test file valid - t.Skip("No tests implemented yet") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs.go deleted file mode 100644 index 1ff38f9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs.go +++ /dev/null @@ -1,267 +0,0 @@ -package brightness - -import ( - "fmt" - "math" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -func NewSysfsBackend() (*SysfsBackend, error) { - b := &SysfsBackend{ - basePath: "/sys/class", - classes: []string{"backlight", "leds"}, - } - - if err := b.scanDevices(); err != nil { - return nil, err - } - - return b, nil -} - -func (b *SysfsBackend) scanDevices() error { - for _, class := range b.classes { - classPath := filepath.Join(b.basePath, class) - entries, err := os.ReadDir(classPath) - if err != nil { - if os.IsNotExist(err) { - continue - } - return fmt.Errorf("read %s: %w", classPath, err) - } - - for _, entry := range entries { - devicePath := filepath.Join(classPath, entry.Name()) - - stat, err := os.Stat(devicePath) - if err != nil || !stat.IsDir() { - continue - } - maxPath := filepath.Join(devicePath, "max_brightness") - - maxData, err := os.ReadFile(maxPath) - if err != nil { - log.Debugf("skip %s/%s: no max_brightness", class, entry.Name()) - continue - } - - maxBrightness, err := strconv.Atoi(strings.TrimSpace(string(maxData))) - if err != nil || maxBrightness <= 0 { - log.Debugf("skip %s/%s: invalid max_brightness", class, entry.Name()) - continue - } - - deviceClass := ClassBacklight - minValue := 1 - if class == "leds" { - deviceClass = ClassLED - minValue = 0 - } - - deviceID := fmt.Sprintf("%s:%s", class, entry.Name()) - b.deviceCache.Store(deviceID, &sysfsDevice{ - class: deviceClass, - id: deviceID, - name: entry.Name(), - maxBrightness: maxBrightness, - minValue: minValue, - }) - - log.Debugf("found %s device: %s (max=%d)", class, entry.Name(), maxBrightness) - } - } - - return nil -} - -func shouldSuppressDevice(name string) bool { - if strings.HasSuffix(name, "::lan") { - return true - } - - keyboardLEDs := []string{ - "::scrolllock", - "::capslock", - "::numlock", - "::kana", - "::compose", - } - - for _, suffix := range keyboardLEDs { - if strings.HasSuffix(name, suffix) { - return true - } - } - - return false -} - -func (b *SysfsBackend) Rescan() error { - return b.scanDevices() -} - -func (b *SysfsBackend) GetDevices() ([]Device, error) { - devices := make([]Device, 0) - - b.deviceCache.Range(func(key string, dev *sysfsDevice) bool { - if shouldSuppressDevice(dev.name) { - return true - } - - parts := strings.SplitN(dev.id, ":", 2) - if len(parts) != 2 { - return true - } - - class := parts[0] - name := parts[1] - - devicePath := filepath.Join(b.basePath, class, name) - brightnessPath := filepath.Join(devicePath, "brightness") - - brightnessData, err := os.ReadFile(brightnessPath) - if err != nil { - log.Debugf("failed to read brightness for %s: %v", dev.id, err) - return true - } - - current, err := strconv.Atoi(strings.TrimSpace(string(brightnessData))) - if err != nil { - log.Debugf("failed to parse brightness for %s: %v", dev.id, err) - return true - } - - percent := b.ValueToPercent(current, dev, false) - - devices = append(devices, Device{ - Class: dev.class, - ID: dev.id, - Name: dev.name, - Current: current, - Max: dev.maxBrightness, - CurrentPercent: percent, - Backend: "sysfs", - }) - return true - }) - - return devices, nil -} - -func (b *SysfsBackend) GetDevice(id string) (*sysfsDevice, error) { - dev, ok := b.deviceCache.Load(id) - if !ok { - return nil, fmt.Errorf("device not found: %s", id) - } - - return dev, nil -} - -func (b *SysfsBackend) SetBrightness(id string, percent int, exponential bool) error { - return b.SetBrightnessWithExponent(id, percent, exponential, 1.2) -} - -func (b *SysfsBackend) SetBrightnessWithExponent(id string, percent int, exponential bool, exponent float64) error { - dev, err := b.GetDevice(id) - if err != nil { - return err - } - - if percent < 0 { - return fmt.Errorf("percent out of range: %d", percent) - } - - value := b.PercentToValueWithExponent(percent, dev, exponential, exponent) - - parts := strings.SplitN(id, ":", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid device id: %s", id) - } - - class := parts[0] - name := parts[1] - - devicePath := filepath.Join(b.basePath, class, name) - brightnessPath := filepath.Join(devicePath, "brightness") - - data := []byte(fmt.Sprintf("%d", value)) - if err := os.WriteFile(brightnessPath, data, 0o644); err != nil { - return fmt.Errorf("write brightness: %w", err) - } - - log.Debugf("set %s to %d%% (%d/%d) via direct sysfs", id, percent, value, dev.maxBrightness) - - return nil -} - -func (b *SysfsBackend) PercentToValue(percent int, dev *sysfsDevice, exponential bool) int { - return b.PercentToValueWithExponent(percent, dev, exponential, 1.2) -} - -func (b *SysfsBackend) PercentToValueWithExponent(percent int, dev *sysfsDevice, exponential bool, exponent float64) int { - if percent == 0 { - return dev.minValue - } - - usableRange := dev.maxBrightness - dev.minValue - var value int - - if exponential { - normalizedPercent := float64(percent-1) / 99.0 - hardwarePercent := math.Pow(normalizedPercent, exponent) - value = dev.minValue + int(math.Round(hardwarePercent*float64(usableRange))) - } else { - value = dev.minValue + ((percent - 1) * usableRange / 99) - } - - if value < dev.minValue { - value = dev.minValue - } - if value > dev.maxBrightness { - value = dev.maxBrightness - } - - return value -} - -func (b *SysfsBackend) ValueToPercent(value int, dev *sysfsDevice, exponential bool) int { - return b.ValueToPercentWithExponent(value, dev, exponential, 1.2) -} - -func (b *SysfsBackend) ValueToPercentWithExponent(value int, dev *sysfsDevice, exponential bool, exponent float64) int { - if value <= dev.minValue { - if dev.minValue == 0 && value == 0 { - return 0 - } - return 1 - } - - usableRange := dev.maxBrightness - dev.minValue - if usableRange == 0 { - return 100 - } - - var percent int - - if exponential { - hardwarePercent := float64(value-dev.minValue) / float64(usableRange) - normalizedPercent := math.Pow(hardwarePercent, 1.0/exponent) - percent = 1 + int(math.Round(normalizedPercent*99.0)) - } else { - percent = 1 + int(math.Round(float64(value-dev.minValue)*99.0/float64(usableRange))) - } - - if percent > 100 { - percent = 100 - } - if percent < 1 { - percent = 1 - } - - return percent -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs_logind_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs_logind_test.go deleted file mode 100644 index 6ba84b8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs_logind_test.go +++ /dev/null @@ -1,278 +0,0 @@ -package brightness - -import ( - "os" - "path/filepath" - "testing" - - mocks_brightness "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/brightness" - mock_dbus "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/mock" -) - -func TestManager_SetBrightness_LogindSuccess(t *testing.T) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "test_backlight") - if err := os.MkdirAll(backlightDir, 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("100\n"), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("50\n"), 0o644); err != nil { - t.Fatal(err) - } - - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - mockLogind := NewLogindBackendWithConn(mockConn) - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight"}, - } - - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - logindBackend: mockLogind, - sysfsBackend: sysfs, - logindReady: true, - sysfsReady: true, - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassBacklight, - ID: "backlight:test_backlight", - Name: "test_backlight", - Current: 50, - Max: 100, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", mock.Anything, "backlight", "test_backlight", uint32(75)). - Return(&dbus.Call{Err: nil}). - Once() - - err := m.SetBrightness("backlight:test_backlight", 75) - if err != nil { - t.Errorf("SetBrightness() with logind error = %v, want nil", err) - } - - data, _ := os.ReadFile(filepath.Join(backlightDir, "brightness")) - if string(data) == "75\n" { - t.Error("Direct sysfs write occurred when logind should have been used") - } -} - -func TestManager_SetBrightness_LogindFailsFallbackToSysfs(t *testing.T) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "test_backlight") - if err := os.MkdirAll(backlightDir, 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("100\n"), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("50\n"), 0o644); err != nil { - t.Fatal(err) - } - - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - mockLogind := NewLogindBackendWithConn(mockConn) - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight"}, - } - - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - logindBackend: mockLogind, - sysfsBackend: sysfs, - logindReady: true, - sysfsReady: true, - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassBacklight, - ID: "backlight:test_backlight", - Name: "test_backlight", - Current: 50, - Max: 100, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", mock.Anything, "backlight", "test_backlight", mock.Anything). - Return(&dbus.Call{Err: dbus.ErrMsgNoObject}). - Once() - - err := m.SetBrightness("backlight:test_backlight", 75) - if err != nil { - t.Errorf("SetBrightness() with fallback error = %v, want nil", err) - } - - data, _ := os.ReadFile(filepath.Join(backlightDir, "brightness")) - brightness := string(data) - if brightness != "75" { - t.Errorf("Fallback sysfs write did not occur, got brightness = %q, want %q", brightness, "75") - } -} - -func TestManager_SetBrightness_NoLogind(t *testing.T) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "test_backlight") - if err := os.MkdirAll(backlightDir, 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("100\n"), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("50\n"), 0o644); err != nil { - t.Fatal(err) - } - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight"}, - } - - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - logindBackend: nil, - sysfsBackend: sysfs, - logindReady: false, - sysfsReady: true, - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassBacklight, - ID: "backlight:test_backlight", - Name: "test_backlight", - Current: 50, - Max: 100, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - err := m.SetBrightness("backlight:test_backlight", 75) - if err != nil { - t.Errorf("SetBrightness() without logind error = %v, want nil", err) - } - - data, _ := os.ReadFile(filepath.Join(backlightDir, "brightness")) - brightness := string(data) - if brightness != "75" { - t.Errorf("Direct sysfs write = %q, want %q", brightness, "75") - } -} - -func TestManager_SetBrightness_LEDWithLogind(t *testing.T) { - tmpDir := t.TempDir() - - ledsDir := filepath.Join(tmpDir, "leds", "test_led") - if err := os.MkdirAll(ledsDir, 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(ledsDir, "max_brightness"), []byte("255\n"), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(ledsDir, "brightness"), []byte("128\n"), 0o644); err != nil { - t.Fatal(err) - } - - mockConn := mocks_brightness.NewMockDBusConn(t) - mockObj := mock_dbus.NewMockBusObject(t) - - mockLogind := NewLogindBackendWithConn(mockConn) - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"leds"}, - } - - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - logindBackend: mockLogind, - sysfsBackend: sysfs, - logindReady: true, - sysfsReady: true, - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassLED, - ID: "leds:test_led", - Name: "test_led", - Current: 128, - Max: 255, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - mockConn.EXPECT(). - Object("org.freedesktop.login1", dbus.ObjectPath("/org/freedesktop/login1/session/auto")). - Return(mockObj). - Once() - - mockObj.EXPECT(). - Call("org.freedesktop.login1.Session.SetBrightness", mock.Anything, "leds", "test_led", uint32(0)). - Return(&dbus.Call{Err: nil}). - Once() - - err := m.SetBrightness("leds:test_led", 0) - if err != nil { - t.Errorf("SetBrightness() LED with logind error = %v, want nil", err) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs_test.go deleted file mode 100644 index 8061513..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/sysfs_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package brightness - -import ( - "os" - "path/filepath" - "testing" -) - -func TestSysfsBackend_PercentConversions(t *testing.T) { - tests := []struct { - name string - device *sysfsDevice - percent int - wantValue int - tolerance int - }{ - { - name: "backlight 0% should be minValue=1", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - percent: 0, - wantValue: 1, - tolerance: 0, - }, - { - name: "backlight 1% should be minValue=1", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - percent: 1, - wantValue: 1, - tolerance: 0, - }, - { - name: "backlight 50% should be ~50", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - percent: 50, - wantValue: 50, - tolerance: 1, - }, - { - name: "backlight 100% should be max", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - percent: 100, - wantValue: 100, - tolerance: 0, - }, - { - name: "led 0% should be 0", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - percent: 0, - wantValue: 0, - tolerance: 0, - }, - { - name: "led 1% should be ~2-3", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - percent: 1, - wantValue: 2, - tolerance: 3, - }, - { - name: "led 50% should be ~127", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - percent: 50, - wantValue: 127, - tolerance: 2, - }, - { - name: "led 100% should be max", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - percent: 100, - wantValue: 255, - tolerance: 0, - }, - } - - b := &SysfsBackend{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := b.PercentToValue(tt.percent, tt.device, false) - diff := got - tt.wantValue - if diff < 0 { - diff = -diff - } - if diff > tt.tolerance { - t.Errorf("percentToValue() = %v, want %v (±%d)", got, tt.wantValue, tt.tolerance) - } - - gotPercent := b.ValueToPercent(got, tt.device, false) - if tt.percent > 1 && gotPercent == 0 { - t.Errorf("valueToPercent() returned 0 for non-zero input (percent=%d, got value=%d)", tt.percent, got) - } - }) - } -} - -func TestSysfsBackend_ValueToPercent(t *testing.T) { - tests := []struct { - name string - device *sysfsDevice - value int - wantPercent int - }{ - { - name: "backlight min value", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - value: 1, - wantPercent: 1, - }, - { - name: "backlight max value", - device: &sysfsDevice{maxBrightness: 100, minValue: 1, class: ClassBacklight}, - value: 100, - wantPercent: 100, - }, - { - name: "led zero", - device: &sysfsDevice{maxBrightness: 255, minValue: 0, class: ClassLED}, - value: 0, - wantPercent: 0, - }, - } - - b := &SysfsBackend{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := b.ValueToPercent(tt.value, tt.device, false) - if got != tt.wantPercent { - t.Errorf("valueToPercent() = %v, want %v", got, tt.wantPercent) - } - }) - } -} - -func TestSysfsBackend_ScanDevices(t *testing.T) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "test_backlight") - if err := os.MkdirAll(backlightDir, 0o755); err != nil { - t.Fatal(err) - } - - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("100\n"), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("50\n"), 0o644); err != nil { - t.Fatal(err) - } - - ledsDir := filepath.Join(tmpDir, "leds", "test_led") - if err := os.MkdirAll(ledsDir, 0o755); err != nil { - t.Fatal(err) - } - - if err := os.WriteFile(filepath.Join(ledsDir, "max_brightness"), []byte("255\n"), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(ledsDir, "brightness"), []byte("128\n"), 0o644); err != nil { - t.Fatal(err) - } - - b := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight", "leds"}, - } - - if err := b.scanDevices(); err != nil { - t.Fatalf("scanDevices() error = %v", err) - } - - backlightID := "backlight:test_backlight" - if _, ok := b.deviceCache.Load(backlightID); !ok { - t.Errorf("backlight device not found") - } - - ledID := "leds:test_led" - if _, ok := b.deviceCache.Load(ledID); !ok { - t.Errorf("LED device not found") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/types.go deleted file mode 100644 index 81e00cf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/types.go +++ /dev/null @@ -1,184 +0,0 @@ -package brightness - -import ( - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type DeviceClass string - -const ( - ClassBacklight DeviceClass = "backlight" - ClassLED DeviceClass = "leds" - ClassDDC DeviceClass = "ddc" -) - -type Device struct { - Class DeviceClass `json:"class"` - ID string `json:"id"` - Name string `json:"name"` - Current int `json:"current"` - Max int `json:"max"` - CurrentPercent int `json:"currentPercent"` - Backend string `json:"backend"` -} - -type State struct { - Devices []Device `json:"devices"` -} - -type DeviceUpdate struct { - Device Device `json:"device"` -} - -type Manager struct { - logindBackend *LogindBackend - sysfsBackend *SysfsBackend - ddcBackend *DDCBackend - udevMonitor *UdevMonitor - - logindReady bool - sysfsReady bool - ddcReady bool - - exponential bool - - stateMutex sync.RWMutex - state State - - subscribers syncmap.Map[string, chan State] - updateSubscribers syncmap.Map[string, chan DeviceUpdate] - - broadcastMutex sync.Mutex - broadcastTimer *time.Timer - broadcastPending bool - pendingDeviceID string - - stopChan chan struct{} -} - -type SysfsBackend struct { - basePath string - classes []string - - deviceCache syncmap.Map[string, *sysfsDevice] -} - -type sysfsDevice struct { - class DeviceClass - id string - name string - maxBrightness int - minValue int -} - -type DDCBackend struct { - devices syncmap.Map[string, *ddcDevice] - - scanMutex sync.Mutex - lastScan time.Time - scanInterval time.Duration - - debounceMutex sync.Mutex - debounceTimers map[string]*time.Timer - debouncePending map[string]ddcPendingSet - debounceWg sync.WaitGroup -} - -type ddcPendingSet struct { - percent int - callback func() -} - -type ddcDevice struct { - bus int - addr int - id string - name string - max int - lastBrightness int -} - -type ddcCapability struct { - vcp byte - max int - current int -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 16) - - m.subscribers.Store(id, ch) - - return ch -} - -func (m *Manager) Unsubscribe(id string) { - - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - - } - -} - -func (m *Manager) SubscribeUpdates(id string) chan DeviceUpdate { - ch := make(chan DeviceUpdate, 16) - m.updateSubscribers.Store(id, ch) - return ch -} - -func (m *Manager) UnsubscribeUpdates(id string) { - if val, ok := m.updateSubscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) NotifySubscribers() { - m.stateMutex.RLock() - state := m.state - m.stateMutex.RUnlock() - - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- state: - default: - } - return true - }) -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return m.state -} - -func (m *Manager) Close() { - close(m.stopChan) - - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - m.updateSubscribers.Range(func(key string, ch chan DeviceUpdate) bool { - close(ch) - m.updateSubscribers.Delete(key) - return true - }) - - if m.udevMonitor != nil { - m.udevMonitor.Close() - } - - if m.logindBackend != nil { - m.logindBackend.Close() - } - - if m.ddcBackend != nil { - m.ddcBackend.Close() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/udev.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/udev.go deleted file mode 100644 index b7f02a8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/udev.go +++ /dev/null @@ -1,237 +0,0 @@ -package brightness - -import ( - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "syscall" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/pilebones/go-udev/netlink" -) - -const ( - udevRecvBufSize = 8 * 1024 * 1024 - udevMaxRetries = 5 - udevBaseDelay = 2 * time.Second - udevMaxDelay = 60 * time.Second -) - -type UdevMonitor struct { - stop chan struct{} - rescanMutex sync.Mutex - rescanTimer *time.Timer - rescanPending bool -} - -func NewUdevMonitor(manager *Manager) *UdevMonitor { - m := &UdevMonitor{ - stop: make(chan struct{}), - } - - go m.run(manager) - return m -} - -func (m *UdevMonitor) run(manager *Manager) { - matcher := &netlink.RuleDefinitions{ - Rules: []netlink.RuleDefinition{ - {Env: map[string]string{"SUBSYSTEM": "backlight"}}, - {Env: map[string]string{"SUBSYSTEM": "drm"}}, - {Env: map[string]string{"SUBSYSTEM": "i2c"}}, - }, - } - if err := matcher.Compile(); err != nil { - log.Errorf("Failed to compile udev matcher: %v", err) - return - } - - failures := 0 - for { - if err := m.monitorLoop(manager, matcher); err != nil { - log.Errorf("Udev monitor error: %v", err) - } - - select { - case <-m.stop: - return - default: - } - - failures++ - if failures > udevMaxRetries { - log.Errorf("Udev monitor exceeded %d retries, giving up", udevMaxRetries) - return - } - - delay := min(udevBaseDelay*time.Duration(1<<(failures-1)), udevMaxDelay) - log.Infof("Udev monitor reconnecting in %v (attempt %d/%d)", delay, failures, udevMaxRetries) - - select { - case <-m.stop: - return - case <-time.After(delay): - } - } -} - -func (m *UdevMonitor) monitorLoop(manager *Manager, matcher *netlink.RuleDefinitions) error { - conn := &netlink.UEventConn{} - if err := conn.Connect(netlink.UdevEvent); err != nil { - return err - } - defer conn.Close() - - if err := syscall.SetsockoptInt(conn.Fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, udevRecvBufSize); err != nil { - log.Warnf("Failed to set udev socket receive buffer: %v", err) - } - - events := make(chan netlink.UEvent) - errs := make(chan error) - conn.Monitor(events, errs, matcher) - - log.Info("Udev monitor started for backlight/drm/i2c events") - - for { - select { - case <-m.stop: - return nil - case err := <-errs: - return err - case event := <-events: - m.handleEvent(manager, event) - } - } -} - -func (m *UdevMonitor) handleEvent(manager *Manager, event netlink.UEvent) { - subsystem := event.Env["SUBSYSTEM"] - devpath := event.Env["DEVPATH"] - - if subsystem == "" || devpath == "" { - return - } - - sysname := filepath.Base(devpath) - action := string(event.Action) - - switch subsystem { - case "drm", "i2c": - m.handleDisplayEvent(manager, action, subsystem, sysname) - case "backlight": - m.handleBacklightEvent(manager, action, sysname) - } -} - -func (m *UdevMonitor) handleDisplayEvent(manager *Manager, action, subsystem, sysname string) { - switch action { - case "add", "remove", "change": - log.Debugf("Udev %s event: %s:%s - queueing DDC rescan", action, subsystem, sysname) - m.debouncedRescan(manager) - } -} - -func (m *UdevMonitor) debouncedRescan(manager *Manager) { - m.rescanMutex.Lock() - defer m.rescanMutex.Unlock() - - m.rescanPending = true - - if m.rescanTimer != nil { - m.rescanTimer.Reset(2 * time.Second) - return - } - - m.rescanTimer = time.AfterFunc(2*time.Second, func() { - m.rescanMutex.Lock() - pending := m.rescanPending - m.rescanPending = false - m.rescanMutex.Unlock() - - if !pending { - return - } - - log.Debug("Executing debounced DDC rescan") - manager.Rescan() - }) -} - -func (m *UdevMonitor) handleBacklightEvent(manager *Manager, action, sysname string) { - switch action { - case "change": - m.handleChange(manager, "backlight", sysname) - case "add", "remove": - log.Debugf("Udev %s event: backlight:%s - triggering rescan", action, sysname) - manager.Rescan() - } -} - -func (m *UdevMonitor) handleChange(manager *Manager, subsystem, sysname string) { - deviceID := subsystem + ":" + sysname - - if manager.sysfsBackend == nil { - return - } - - brightnessPath := filepath.Join(manager.sysfsBackend.basePath, subsystem, sysname, "brightness") - data, err := os.ReadFile(brightnessPath) - if err != nil { - log.Debugf("Udev change event for %s but failed to read brightness: %v", deviceID, err) - return - } - - brightness, err := strconv.Atoi(strings.TrimSpace(string(data))) - if err != nil { - log.Debugf("Failed to parse brightness for %s: %v", deviceID, err) - return - } - - manager.handleUdevBrightnessChange(deviceID, brightness) -} - -func (m *UdevMonitor) Close() { - close(m.stop) -} - -func (m *Manager) handleUdevBrightnessChange(deviceID string, rawBrightness int) { - if m.sysfsBackend == nil { - return - } - - dev, err := m.sysfsBackend.GetDevice(deviceID) - if err != nil { - log.Debugf("Udev event for unknown device %s: %v", deviceID, err) - return - } - - percent := m.sysfsBackend.ValueToPercent(rawBrightness, dev, false) - - m.stateMutex.Lock() - var found bool - for i, d := range m.state.Devices { - if d.ID != deviceID { - continue - } - found = true - if d.Current == rawBrightness { - m.stateMutex.Unlock() - return - } - m.state.Devices[i].Current = rawBrightness - m.state.Devices[i].CurrentPercent = percent - break - } - m.stateMutex.Unlock() - - if !found { - log.Debugf("Udev event for device not in state: %s", deviceID) - return - } - - log.Debugf("Udev brightness change: %s -> %d (%d%%)", deviceID, rawBrightness, percent) - m.broadcastDeviceUpdate(deviceID) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/udev_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/udev_test.go deleted file mode 100644 index a5c406b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/brightness/udev_test.go +++ /dev/null @@ -1,260 +0,0 @@ -package brightness - -import ( - "os" - "path/filepath" - "testing" - "time" - - "github.com/pilebones/go-udev/netlink" -) - -func setupTestManager(t *testing.T) (*Manager, string) { - tmpDir := t.TempDir() - - backlightDir := filepath.Join(tmpDir, "backlight", "intel_backlight") - if err := os.MkdirAll(backlightDir, 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "max_brightness"), []byte("1000\n"), 0o644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(backlightDir, "brightness"), []byte("500\n"), 0o644); err != nil { - t.Fatal(err) - } - - sysfs := &SysfsBackend{ - basePath: tmpDir, - classes: []string{"backlight"}, - } - if err := sysfs.scanDevices(); err != nil { - t.Fatal(err) - } - - m := &Manager{ - sysfsBackend: sysfs, - sysfsReady: true, - stopChan: make(chan struct{}), - } - - m.state = State{ - Devices: []Device{ - { - Class: ClassBacklight, - ID: "backlight:intel_backlight", - Name: "intel_backlight", - Current: 500, - Max: 1000, - CurrentPercent: 50, - Backend: "sysfs", - }, - }, - } - - return m, tmpDir -} - -func TestHandleUdevBrightnessChange_UpdatesState(t *testing.T) { - m, _ := setupTestManager(t) - - m.handleUdevBrightnessChange("backlight:intel_backlight", 750) - - state := m.GetState() - if len(state.Devices) != 1 { - t.Fatalf("expected 1 device, got %d", len(state.Devices)) - } - - dev := state.Devices[0] - if dev.Current != 750 { - t.Errorf("expected Current=750, got %d", dev.Current) - } - if dev.CurrentPercent != 75 { - t.Errorf("expected CurrentPercent=75, got %d", dev.CurrentPercent) - } -} - -func TestHandleUdevBrightnessChange_NoChangeWhenSameValue(t *testing.T) { - m, _ := setupTestManager(t) - - updateCh := m.SubscribeUpdates("test") - defer m.UnsubscribeUpdates("test") - - m.handleUdevBrightnessChange("backlight:intel_backlight", 500) - - select { - case <-updateCh: - t.Error("should not broadcast when brightness unchanged") - case <-time.After(50 * time.Millisecond): - } -} - -func TestHandleUdevBrightnessChange_BroadcastsOnChange(t *testing.T) { - m, _ := setupTestManager(t) - - updateCh := m.SubscribeUpdates("test") - defer m.UnsubscribeUpdates("test") - - m.handleUdevBrightnessChange("backlight:intel_backlight", 750) - - select { - case update := <-updateCh: - if update.Device.Current != 750 { - t.Errorf("broadcast had wrong Current: got %d, want 750", update.Device.Current) - } - case <-time.After(100 * time.Millisecond): - t.Error("expected broadcast on brightness change") - } -} - -func TestHandleUdevBrightnessChange_UnknownDevice(t *testing.T) { - m, _ := setupTestManager(t) - - m.handleUdevBrightnessChange("backlight:unknown_device", 500) - - state := m.GetState() - if len(state.Devices) != 1 { - t.Errorf("state should be unchanged, got %d devices", len(state.Devices)) - } -} - -func TestHandleUdevBrightnessChange_NilSysfsBackend(t *testing.T) { - m := &Manager{ - sysfsBackend: nil, - stopChan: make(chan struct{}), - } - - m.handleUdevBrightnessChange("backlight:test", 500) -} - -func TestHandleUdevBrightnessChange_DeviceNotInState(t *testing.T) { - m, _ := setupTestManager(t) - - m.sysfsBackend.deviceCache.Store("backlight:other_device", &sysfsDevice{ - class: ClassBacklight, - id: "backlight:other_device", - name: "other_device", - maxBrightness: 100, - minValue: 1, - }) - - m.handleUdevBrightnessChange("backlight:other_device", 50) - - state := m.GetState() - for _, d := range state.Devices { - if d.ID == "backlight:other_device" { - t.Error("device should not be added to state via udev change event") - } - } -} - -func TestHandleEvent_ChangeAction(t *testing.T) { - m, tmpDir := setupTestManager(t) - um := &UdevMonitor{stop: make(chan struct{})} - - brightnessPath := filepath.Join(tmpDir, "backlight", "intel_backlight", "brightness") - if err := os.WriteFile(brightnessPath, []byte("800\n"), 0o644); err != nil { - t.Fatal(err) - } - - event := netlink.UEvent{ - Action: netlink.CHANGE, - Env: map[string]string{ - "SUBSYSTEM": "backlight", - "DEVPATH": "/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/intel_backlight", - }, - } - - um.handleEvent(m, event) - - state := m.GetState() - if state.Devices[0].Current != 800 { - t.Errorf("expected Current=800 after change event, got %d", state.Devices[0].Current) - } -} - -func TestHandleEvent_MissingEnvVars(t *testing.T) { - m, _ := setupTestManager(t) - um := &UdevMonitor{stop: make(chan struct{})} - - event := netlink.UEvent{ - Action: netlink.CHANGE, - Env: map[string]string{}, - } - - um.handleEvent(m, event) - - state := m.GetState() - if state.Devices[0].Current != 500 { - t.Error("state should be unchanged with missing env vars") - } -} - -func TestHandleEvent_MissingSubsystem(t *testing.T) { - m, _ := setupTestManager(t) - um := &UdevMonitor{stop: make(chan struct{})} - - event := netlink.UEvent{ - Action: netlink.CHANGE, - Env: map[string]string{ - "DEVPATH": "/devices/foo/bar", - }, - } - - um.handleEvent(m, event) - - state := m.GetState() - if state.Devices[0].Current != 500 { - t.Error("state should be unchanged with missing SUBSYSTEM") - } -} - -func TestHandleChange_BrightnessFileNotFound(t *testing.T) { - m, _ := setupTestManager(t) - um := &UdevMonitor{stop: make(chan struct{})} - - um.handleChange(m, "backlight", "nonexistent_device") - - state := m.GetState() - if state.Devices[0].Current != 500 { - t.Error("state should be unchanged when brightness file not found") - } -} - -func TestHandleChange_InvalidBrightnessValue(t *testing.T) { - m, tmpDir := setupTestManager(t) - um := &UdevMonitor{stop: make(chan struct{})} - - brightnessPath := filepath.Join(tmpDir, "backlight", "intel_backlight", "brightness") - if err := os.WriteFile(brightnessPath, []byte("not_a_number\n"), 0o644); err != nil { - t.Fatal(err) - } - - um.handleChange(m, "backlight", "intel_backlight") - - state := m.GetState() - if state.Devices[0].Current != 500 { - t.Error("state should be unchanged with invalid brightness value") - } -} - -func TestUdevMonitor_Close(t *testing.T) { - um := &UdevMonitor{stop: make(chan struct{})} - - um.Close() - - select { - case <-um.stop: - default: - t.Error("stop channel should be closed") - } -} - -func TestHandleChange_NilSysfsBackend(t *testing.T) { - m := &Manager{ - sysfsBackend: nil, - stopChan: make(chan struct{}), - } - um := &UdevMonitor{stop: make(chan struct{})} - - um.handleChange(m, "backlight", "test_device") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/handlers.go deleted file mode 100644 index 0b4159a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/handlers.go +++ /dev/null @@ -1,22 +0,0 @@ -package browser - -import ( - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - switch req.Method { - case "browser.open": - url, ok := models.Get[string](req, "url") - if !ok { - models.RespondError(conn, req.ID, "invalid url parameter") - return - } - manager.RequestOpen(url) - models.Respond(conn, req.ID, "ok") - default: - models.RespondError(conn, req.ID, "unknown method") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/manager.go deleted file mode 100644 index 5306759..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/manager.go +++ /dev/null @@ -1,49 +0,0 @@ -package browser - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type Manager struct { - subscribers syncmap.Map[string, chan OpenEvent] - closeOnce sync.Once -} - -func NewManager() *Manager { - return &Manager{} -} - -func (m *Manager) Subscribe(id string) chan OpenEvent { - ch := make(chan OpenEvent, 16) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) RequestOpen(url string) { - event := OpenEvent{URL: url} - m.subscribers.Range(func(key string, ch chan OpenEvent) bool { - select { - case ch <- event: - default: - } - return true - }) -} - -func (m *Manager) Close() { - m.closeOnce.Do(func() { - m.subscribers.Range(func(key string, ch chan OpenEvent) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/models.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/models.go deleted file mode 100644 index fc63519..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/browser/models.go +++ /dev/null @@ -1,5 +0,0 @@ -package browser - -type OpenEvent struct { - URL string `json:"url"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/handlers.go deleted file mode 100644 index 1ccc710..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/handlers.go +++ /dev/null @@ -1,325 +0,0 @@ -package clipboard - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -func HandleRequest(conn net.Conn, req models.Request, m *Manager) { - switch req.Method { - case "clipboard.getState": - handleGetState(conn, req, m) - case "clipboard.getHistory": - handleGetHistory(conn, req, m) - case "clipboard.getEntry": - handleGetEntry(conn, req, m) - case "clipboard.deleteEntry": - handleDeleteEntry(conn, req, m) - case "clipboard.clearHistory": - handleClearHistory(conn, req, m) - case "clipboard.copy": - handleCopy(conn, req, m) - case "clipboard.copyEntry": - handleCopyEntry(conn, req, m) - case "clipboard.paste": - handlePaste(conn, req, m) - case "clipboard.subscribe": - handleSubscribe(conn, req, m) - case "clipboard.search": - handleSearch(conn, req, m) - case "clipboard.getConfig": - handleGetConfig(conn, req, m) - case "clipboard.setConfig": - handleSetConfig(conn, req, m) - case "clipboard.store": - handleStore(conn, req, m) - case "clipboard.pinEntry": - handlePinEntry(conn, req, m) - case "clipboard.unpinEntry": - handleUnpinEntry(conn, req, m) - case "clipboard.getPinnedEntries": - handleGetPinnedEntries(conn, req, m) - case "clipboard.getPinnedCount": - handleGetPinnedCount(conn, req, m) - case "clipboard.copyFile": - handleCopyFile(conn, req, m) - default: - models.RespondError(conn, req.ID, "unknown method: "+req.Method) - } -} - -func handleGetState(conn net.Conn, req models.Request, m *Manager) { - models.Respond(conn, req.ID, m.GetState()) -} - -func handleGetHistory(conn net.Conn, req models.Request, m *Manager) { - history := m.GetHistory() - for i := range history { - history[i].Data = nil - } - models.Respond(conn, req.ID, history) -} - -func handleGetEntry(conn net.Conn, req models.Request, m *Manager) { - id, err := params.Int(req.Params, "id") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - entry, err := m.GetEntry(uint64(id)) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, entry) -} - -func handleDeleteEntry(conn net.Conn, req models.Request, m *Manager) { - id, err := params.Int(req.Params, "id") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := m.DeleteEntry(uint64(id)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "entry deleted"}) -} - -func handleClearHistory(conn net.Conn, req models.Request, m *Manager) { - m.ClearHistory() - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "history cleared"}) -} - -func handleCopy(conn net.Conn, req models.Request, m *Manager) { - text, err := params.String(req.Params, "text") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := m.CopyText(text); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "copied to clipboard"}) -} - -func handleCopyEntry(conn net.Conn, req models.Request, m *Manager) { - id, err := params.Int(req.Params, "id") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - entry, err := m.GetEntry(uint64(id)) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - filePath := m.EntryToFile(entry) - if filePath != "" { - if err := m.CopyFile(filePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, map[string]any{ - "success": true, - "filePath": filePath, - }) - return - } - - if err := m.SetClipboard(entry.Data, entry.MimeType); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if entry.Pinned { - if err := m.CreateHistoryEntryFromPinned(entry); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - } else { - if err := m.TouchEntry(uint64(id)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "copied to clipboard"}) -} - -func handlePaste(conn net.Conn, req models.Request, m *Manager) { - text, err := m.PasteText() - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, map[string]string{"text": text}) -} - -func handleSubscribe(conn net.Conn, req models.Request, m *Manager) { - clientID := fmt.Sprintf("clipboard-%d", req.ID) - - ch := m.Subscribe(clientID) - defer m.Unsubscribe(clientID) - - initialState := m.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range ch { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &state, - }); err != nil { - return - } - } -} - -func handleSearch(conn net.Conn, req models.Request, m *Manager) { - p := SearchParams{ - Query: params.StringOpt(req.Params, "query", ""), - MimeType: params.StringOpt(req.Params, "mimeType", ""), - Limit: params.IntOpt(req.Params, "limit", 50), - Offset: params.IntOpt(req.Params, "offset", 0), - } - - if img, ok := models.Get[bool](req, "isImage"); ok { - p.IsImage = &img - } - if b, ok := models.Get[float64](req, "before"); ok { - v := int64(b) - p.Before = &v - } - if a, ok := models.Get[float64](req, "after"); ok { - v := int64(a) - p.After = &v - } - - models.Respond(conn, req.ID, m.Search(p)) -} - -func handleGetConfig(conn net.Conn, req models.Request, m *Manager) { - models.Respond(conn, req.ID, m.GetConfig()) -} - -func handleSetConfig(conn net.Conn, req models.Request, m *Manager) { - cfg := m.GetConfig() - - if v, ok := models.Get[float64](req, "maxHistory"); ok { - cfg.MaxHistory = int(v) - } - if v, ok := models.Get[float64](req, "maxEntrySize"); ok { - cfg.MaxEntrySize = int64(v) - } - if v, ok := models.Get[float64](req, "autoClearDays"); ok { - cfg.AutoClearDays = int(v) - } - if v, ok := models.Get[bool](req, "clearAtStartup"); ok { - cfg.ClearAtStartup = v - } - if v, ok := models.Get[bool](req, "disabled"); ok { - cfg.Disabled = v - } - if v, ok := models.Get[float64](req, "maxPinned"); ok { - cfg.MaxPinned = int(v) - } - - if err := m.SetConfig(cfg); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "config updated"}) -} - -func handleStore(conn net.Conn, req models.Request, m *Manager) { - data, err := params.String(req.Params, "data") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - mimeType := params.StringOpt(req.Params, "mimeType", "text/plain;charset=utf-8") - - if err := m.StoreData([]byte(data), mimeType); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "stored"}) -} - -func handlePinEntry(conn net.Conn, req models.Request, m *Manager) { - id, err := params.Int(req.Params, "id") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := m.PinEntry(uint64(id)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "entry pinned"}) -} - -func handleUnpinEntry(conn net.Conn, req models.Request, m *Manager) { - id, err := params.Int(req.Params, "id") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := m.UnpinEntry(uint64(id)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "entry unpinned"}) -} - -func handleGetPinnedEntries(conn net.Conn, req models.Request, m *Manager) { - pinned := m.GetPinnedEntries() - models.Respond(conn, req.ID, pinned) -} - -func handleGetPinnedCount(conn net.Conn, req models.Request, m *Manager) { - count := m.GetPinnedCount() - models.Respond(conn, req.ID, map[string]int{"count": count}) -} - -func handleCopyFile(conn net.Conn, req models.Request, m *Manager) { - filePath, err := params.String(req.Params, "filePath") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := m.CopyFile(filePath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "copied"}) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/manager.go deleted file mode 100644 index f5f528b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/manager.go +++ /dev/null @@ -1,1911 +0,0 @@ -package clipboard - -import ( - "bytes" - "encoding/binary" - "fmt" - "image" - _ "image/gif" - _ "image/jpeg" - _ "image/png" - "io" - "os" - "os/exec" - "path/filepath" - "slices" - "strings" - "syscall" - "time" - - "hash/fnv" - - "github.com/fsnotify/fsnotify" - "github.com/godbus/dbus/v5" - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/tiff" - _ "golang.org/x/image/webp" - - bolt "go.etcd.io/bbolt" - - clipboardstore "github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_data_control" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlcontext" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -// These mime types won't be stored in history -var sensitiveMimeTypes = []string{ - "x-kde-passwordManagerHint", -} - -func NewManager(wlCtx wlcontext.WaylandContext, config Config) (*Manager, error) { - display := wlCtx.Display() - dbPath, err := clipboardstore.GetDBPath() - if err != nil { - return nil, fmt.Errorf("failed to get db path: %w", err) - } - - configPath, _ := getConfigPath() - - m := &Manager{ - config: config, - configPath: configPath, - display: display, - wlCtx: wlCtx, - stopChan: make(chan struct{}), - subscribers: make(map[string]chan State), - dirty: make(chan struct{}, 1), - offerMimeTypes: make(map[any][]string), - offerRegistry: make(map[uint32]any), - dbPath: dbPath, - } - - if !config.Disabled { - if err := m.setupRegistry(); err != nil { - return nil, err - } - } - - m.notifierWg.Add(1) - go m.notifier() - - go m.watchConfig() - - db, err := openDB(dbPath) - if err != nil { - return nil, fmt.Errorf("failed to open db: %w", err) - } - m.db = db - - if err := m.migrateHashes(); err != nil { - log.Errorf("Failed to migrate hashes: %v", err) - } - - if !config.Disabled { - if config.ClearAtStartup { - if err := m.clearHistoryInternal(); err != nil { - log.Errorf("Failed to clear history at startup: %v", err) - } - } - - if config.AutoClearDays > 0 { - if err := m.clearOldEntries(config.AutoClearDays); err != nil { - log.Errorf("Failed to clear old entries: %v", err) - } - } - } - - m.alive = true - m.updateState() - - if !config.Disabled && m.dataControlMgr != nil && m.seat != nil { - m.setupDataDeviceSync() - } - - return m, nil -} - -func openDB(path string) (*bolt.DB, error) { - db, err := bolt.Open(path, 0o644, &bolt.Options{ - Timeout: 1 * time.Second, - }) - if err != nil { - return nil, err - } - - err = db.Update(func(tx *bolt.Tx) error { - _, err := tx.CreateBucketIfNotExists([]byte("clipboard")) - return err - }) - if err != nil { - db.Close() - return nil, err - } - - return db, nil -} - -func (m *Manager) post(fn func()) { - m.wlCtx.Post(fn) -} - -func (m *Manager) setupRegistry() error { - ctx := m.display.Context() - - registry, err := m.display.GetRegistry() - if err != nil { - return fmt.Errorf("failed to get registry: %w", err) - } - m.registry = registry - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case "ext_data_control_manager_v1": - if e.Version < 1 { - return - } - dataControlMgr := ext_data_control.NewExtDataControlManagerV1(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil { - log.Errorf("Failed to bind ext_data_control_manager_v1: %v", err) - return - } - m.dataControlMgr = dataControlMgr - log.Info("Bound ext_data_control_manager_v1") - case "wl_seat": - seat := wlclient.NewSeat(ctx) - if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil { - log.Errorf("Failed to bind wl_seat: %v", err) - return - } - m.seat = seat - m.seatName = e.Name - log.Info("Bound wl_seat") - } - }) - - m.display.Roundtrip() - m.display.Roundtrip() - - if m.dataControlMgr == nil { - return fmt.Errorf("compositor does not support ext_data_control_manager_v1") - } - - if m.seat == nil { - return fmt.Errorf("no seat available") - } - - return nil -} - -func (m *Manager) setupDataDeviceSync() { - if m.dataControlMgr == nil || m.seat == nil { - return - } - - ctx := m.display.Context() - dataMgr := m.dataControlMgr.(*ext_data_control.ExtDataControlManagerV1) - - dataDevice := ext_data_control.NewExtDataControlDeviceV1(ctx) - - dataDevice.SetDataOfferHandler(func(e ext_data_control.ExtDataControlDeviceV1DataOfferEvent) { - if e.Id == nil { - return - } - - m.offerMutex.Lock() - m.offerRegistry[e.Id.ID()] = e.Id - m.offerMimeTypes[e.Id] = make([]string, 0) - m.offerMutex.Unlock() - - e.Id.SetOfferHandler(func(me ext_data_control.ExtDataControlOfferV1OfferEvent) { - m.offerMutex.Lock() - m.offerMimeTypes[e.Id] = append(m.offerMimeTypes[e.Id], me.MimeType) - m.offerMutex.Unlock() - }) - }) - - dataDevice.SetSelectionHandler(func(e ext_data_control.ExtDataControlDeviceV1SelectionEvent) { - if !m.initialized { - m.initialized = true - return - } - - var offer any - if e.Id != nil { - offer = e.Id - } else if e.OfferId != 0 { - m.offerMutex.RLock() - offer = m.offerRegistry[e.OfferId] - m.offerMutex.RUnlock() - } - - m.ownerLock.Lock() - wasOwner := m.isOwner - m.ownerLock.Unlock() - - if offer == nil { - return - } - - if wasOwner { - return - } - - prevOffer := m.currentOffer - m.currentOffer = offer - - if prevOffer != nil && prevOffer != offer { - m.offerMutex.Lock() - delete(m.offerMimeTypes, prevOffer) - m.offerMutex.Unlock() - } - - m.offerMutex.RLock() - mimes := m.offerMimeTypes[offer] - m.offerMutex.RUnlock() - - m.mimeTypes = mimes - - if len(mimes) == 0 { - return - } - - if m.hasSensitiveMimeType(mimes) { - return - } - - preferredMime := m.selectMimeType(mimes) - if preferredMime == "" { - return - } - - typedOffer := offer.(*ext_data_control.ExtDataControlOfferV1) - - r, w, err := os.Pipe() - if err != nil { - return - } - - if err := typedOffer.Receive(preferredMime, int(w.Fd())); err != nil { - r.Close() - w.Close() - return - } - w.Close() - - go m.readAndStore(r, preferredMime) - }) - - if err := dataMgr.GetDataDeviceWithProxy(dataDevice, m.seat); err != nil { - log.Errorf("Failed to send get_data_device request: %v", err) - return - } - - m.dataDevice = dataDevice - - if err := ctx.Dispatch(); err != nil { - log.Errorf("Failed to dispatch initial events: %v", err) - return - } - - log.Info("Data device setup complete") -} - -func (m *Manager) readAndStore(r *os.File, mimeType string) { - defer r.Close() - - cfg := m.getConfig() - - done := make(chan []byte, 1) - go func() { - data, _ := io.ReadAll(r) - done <- data - }() - - var data []byte - select { - case data = <-done: - case <-time.After(500 * time.Millisecond): - return - } - - if len(data) == 0 || int64(len(data)) > cfg.MaxEntrySize { - return - } - if len(bytes.TrimSpace(data)) == 0 { - return - } - - if !cfg.Disabled && m.db != nil { - m.storeClipboardEntry(data, mimeType) - } - - m.updateState() - m.notifySubscribers() -} - -func (m *Manager) storeClipboardEntry(data []byte, mimeType string) { - if mimeType == "text/uri-list" { - if imgData, imgMime, ok := m.tryReadImageFromURI(data); ok { - data = imgData - mimeType = imgMime - } - } - - entry := Entry{ - Data: data, - MimeType: mimeType, - Size: len(data), - Timestamp: time.Now(), - IsImage: m.isImageMimeType(mimeType), - } - - switch { - case entry.IsImage: - entry.Preview = m.imagePreview(data, mimeType) - case mimeType == "text/uri-list": - entry.Preview, entry.IsImage = m.uriListPreview(data) - default: - entry.Preview = m.textPreview(data) - } - - if err := m.storeEntry(entry); err != nil { - log.Errorf("Failed to store clipboard entry: %v", err) - } -} - -func (m *Manager) storeEntry(entry Entry) error { - if m.db == nil { - return fmt.Errorf("database not available") - } - - entry.Hash = computeHash(entry.Data) - - return m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - - if err := m.deduplicateInTx(b, entry.Hash); err != nil { - return err - } - - id, err := b.NextSequence() - if err != nil { - return err - } - - entry.ID = id - - encoded, err := encodeEntry(entry) - if err != nil { - return err - } - - if err := b.Put(itob(id), encoded); err != nil { - return err - } - - return m.trimLengthInTx(b) - }) -} - -func (m *Manager) deduplicateInTx(b *bolt.Bucket, hash uint64) error { - c := b.Cursor() - for k, v := c.Last(); k != nil; k, v = c.Prev() { - if extractHash(v) != hash { - continue - } - entry, err := decodeEntry(v) - if err == nil && entry.Pinned { - continue - } - if err := b.Delete(k); err != nil { - return err - } - } - return nil -} - -func (m *Manager) trimLengthInTx(b *bolt.Bucket) error { - if m.config.MaxHistory < 0 { - return nil - } - c := b.Cursor() - var count int - for k, v := c.Last(); k != nil; k, v = c.Prev() { - entry, err := decodeEntry(v) - if err == nil && entry.Pinned { - continue - } - if count < m.config.MaxHistory { - count++ - continue - } - if err := b.Delete(k); err != nil { - return err - } - } - return nil -} - -func encodeEntry(e Entry) ([]byte, error) { - buf := new(bytes.Buffer) - - binary.Write(buf, binary.BigEndian, e.ID) - binary.Write(buf, binary.BigEndian, uint32(len(e.Data))) - buf.Write(e.Data) - binary.Write(buf, binary.BigEndian, uint32(len(e.MimeType))) - buf.WriteString(e.MimeType) - binary.Write(buf, binary.BigEndian, uint32(len(e.Preview))) - buf.WriteString(e.Preview) - binary.Write(buf, binary.BigEndian, int32(e.Size)) - binary.Write(buf, binary.BigEndian, e.Timestamp.Unix()) - if e.IsImage { - buf.WriteByte(1) - } else { - buf.WriteByte(0) - } - binary.Write(buf, binary.BigEndian, e.Hash) - if e.Pinned { - buf.WriteByte(1) - } else { - buf.WriteByte(0) - } - - return buf.Bytes(), nil -} - -func decodeEntry(data []byte) (Entry, error) { - buf := bytes.NewReader(data) - var e Entry - - binary.Read(buf, binary.BigEndian, &e.ID) - - var dataLen uint32 - binary.Read(buf, binary.BigEndian, &dataLen) - e.Data = make([]byte, dataLen) - buf.Read(e.Data) - - var mimeLen uint32 - binary.Read(buf, binary.BigEndian, &mimeLen) - mimeBytes := make([]byte, mimeLen) - buf.Read(mimeBytes) - e.MimeType = string(mimeBytes) - - var prevLen uint32 - binary.Read(buf, binary.BigEndian, &prevLen) - prevBytes := make([]byte, prevLen) - buf.Read(prevBytes) - e.Preview = string(prevBytes) - - var size int32 - binary.Read(buf, binary.BigEndian, &size) - e.Size = int(size) - - var timestamp int64 - binary.Read(buf, binary.BigEndian, ×tamp) - e.Timestamp = time.Unix(timestamp, 0) - - var isImage byte - binary.Read(buf, binary.BigEndian, &isImage) - e.IsImage = isImage == 1 - - if buf.Len() >= 8 { - binary.Read(buf, binary.BigEndian, &e.Hash) - } - - if buf.Len() >= 1 { - var pinnedByte byte - binary.Read(buf, binary.BigEndian, &pinnedByte) - e.Pinned = pinnedByte == 1 - } - - return e, nil -} - -func itob(v uint64) []byte { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, v) - return b -} - -func computeHash(data []byte) uint64 { - h := fnv.New64a() - h.Write(data) - return h.Sum64() -} - -func extractHash(data []byte) uint64 { - if len(data) < 9 { - return 0 - } - return binary.BigEndian.Uint64(data[len(data)-9 : len(data)-1]) -} - -func (m *Manager) hasSensitiveMimeType(mimes []string) bool { - return slices.ContainsFunc(mimes, func(mime string) bool { - return slices.Contains(sensitiveMimeTypes, mime) - }) -} - -func (m *Manager) selectMimeType(mimes []string) string { - preferredTypes := []string{ - "text/uri-list", - "text/plain;charset=utf-8", - "text/plain", - "UTF8_STRING", - "STRING", - "TEXT", - "image/png", - "image/jpeg", - "image/gif", - "image/bmp", - "image/tiff", - } - - for _, pref := range preferredTypes { - for _, mime := range mimes { - if mime == pref { - return mime - } - } - } - - // Skip useless MIME types when falling back - for _, mime := range mimes { - switch mime { - case "application/vnd.portal.filetransfer": - continue - default: - return mime - } - } - - return "" -} - -func (m *Manager) isImageMimeType(mime string) bool { - return strings.HasPrefix(mime, "image/") -} - -func (m *Manager) textPreview(data []byte) string { - text := string(data) - text = strings.TrimSpace(text) - text = strings.Join(strings.Fields(text), " ") - - if len(text) > 100 { - return text[:100] + "…" - } - return text -} - -func (m *Manager) imagePreview(data []byte, format string) string { - config, imgFmt, err := image.DecodeConfig(bytes.NewReader(data)) - if err != nil { - return fmt.Sprintf("[[ image %s %s ]]", sizeStr(len(data)), format) - } - return fmt.Sprintf("[[ image %s %s %dx%d ]]", sizeStr(len(data)), imgFmt, config.Width, config.Height) -} - -func (m *Manager) uriListPreview(data []byte) (string, bool) { - text := strings.TrimSpace(string(data)) - uris := strings.Split(text, "\r\n") - if len(uris) == 0 { - uris = strings.Split(text, "\n") - } - - if len(uris) > 1 { - return fmt.Sprintf("[[ %d files ]]", len(uris)), false - } - - if len(uris) == 1 && strings.HasPrefix(uris[0], "file://") { - filePath := strings.TrimPrefix(uris[0], "file://") - info, err := os.Stat(filePath) - if err != nil || info.IsDir() { - return m.textPreview(data), false - } - - cfg := m.getConfig() - if info.Size() <= cfg.MaxEntrySize { - if imgData, err := os.ReadFile(filePath); err == nil { - if config, imgFmt, err := image.DecodeConfig(bytes.NewReader(imgData)); err == nil { - return fmt.Sprintf("[[ file %s %s %dx%d ]]", filepath.Base(filePath), imgFmt, config.Width, config.Height), true - } - } - } - return fmt.Sprintf("[[ file %s ]]", filepath.Base(filePath)), false - } - - return m.textPreview(data), false -} - -func (m *Manager) tryReadImageFromURI(data []byte) ([]byte, string, bool) { - text := strings.TrimSpace(string(data)) - uris := strings.Split(text, "\r\n") - if len(uris) == 0 { - uris = strings.Split(text, "\n") - } - - if len(uris) != 1 || !strings.HasPrefix(uris[0], "file://") { - return nil, "", false - } - - filePath := strings.TrimPrefix(uris[0], "file://") - info, err := os.Stat(filePath) - if err != nil || info.IsDir() { - return nil, "", false - } - - cfg := m.getConfig() - if info.Size() > cfg.MaxEntrySize { - return nil, "", false - } - - imgData, err := os.ReadFile(filePath) - if err != nil { - return nil, "", false - } - - _, imgFmt, err := image.DecodeConfig(bytes.NewReader(imgData)) - if err != nil { - return nil, "", false - } - - return imgData, "image/" + imgFmt, true -} - -func sizeStr(size int) string { - units := []string{"B", "KiB", "MiB"} - var i int - fsize := float64(size) - for fsize >= 1024 && i < len(units)-1 { - fsize /= 1024 - i++ - } - return fmt.Sprintf("%.0f %s", fsize, units[i]) -} - -func (m *Manager) updateState() { - history := m.GetHistory() - - for i := range history { - history[i].Data = nil - } - - var current *Entry - if len(history) > 0 { - c := history[0] - c.Data = nil - current = &c - } - - newState := &State{ - Enabled: m.alive, - History: history, - Current: current, - } - - m.stateMutex.Lock() - m.state = newState - m.stateMutex.Unlock() -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - - for range m.dirty { - state := m.GetState() - - if m.lastState != nil && stateEqual(m.lastState, &state) { - continue - } - - m.lastState = &state - - m.subMutex.RLock() - subs := make([]chan State, 0, len(m.subscribers)) - for _, ch := range m.subscribers { - subs = append(subs, ch) - } - m.subMutex.RUnlock() - - for _, ch := range subs { - select { - case ch <- state: - default: - } - } - } -} - -func stateEqual(a, b *State) bool { - if a == nil || b == nil { - return false - } - if a.Enabled != b.Enabled { - return false - } - if len(a.History) != len(b.History) { - return false - } - return true -} - -func (m *Manager) GetHistory() []Entry { - if m.db == nil { - return nil - } - - cfg := m.getConfig() - var cutoff time.Time - if cfg.AutoClearDays > 0 { - cutoff = time.Now().AddDate(0, 0, -cfg.AutoClearDays) - } - - var history []Entry - var stale []uint64 - - if err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - c := b.Cursor() - - for k, v := c.Last(); k != nil; k, v = c.Prev() { - entry, err := decodeEntry(v) - if err != nil { - continue - } - if !cutoff.IsZero() && entry.Timestamp.Before(cutoff) { - stale = append(stale, entry.ID) - continue - } - history = append(history, entry) - } - return nil - }); err != nil { - log.Errorf("Failed to read clipboard history: %v", err) - } - - if len(stale) > 0 { - go m.deleteStaleEntries(stale) - } - - return history -} - -func (m *Manager) deleteStaleEntries(ids []uint64) { - if m.db == nil { - return - } - - if err := m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - for _, id := range ids { - if err := b.Delete(itob(id)); err != nil { - log.Errorf("Failed to delete stale entry %d: %v", id, err) - } - } - return nil - }); err != nil { - log.Errorf("Failed to delete stale entries: %v", err) - } -} - -func (m *Manager) GetEntry(id uint64) (*Entry, error) { - if m.db == nil { - return nil, fmt.Errorf("database not available") - } - - var entry Entry - var found bool - - err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - v := b.Get(itob(id)) - if v == nil { - return nil - } - - var err error - entry, err = decodeEntry(v) - if err != nil { - return err - } - found = true - return nil - }) - - if err != nil { - return nil, err - } - if !found { - return nil, fmt.Errorf("entry not found") - } - - return &entry, nil -} - -func (m *Manager) DeleteEntry(id uint64) error { - if m.db == nil { - return fmt.Errorf("database not available") - } - - err := m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - return b.Delete(itob(id)) - }) - - if err == nil { - m.updateState() - m.notifySubscribers() - } - - return err -} - -func (m *Manager) TouchEntry(id uint64) error { - if m.db == nil { - return fmt.Errorf("database not available") - } - - entry, err := m.GetEntry(id) - if err != nil { - return err - } - - entry.Timestamp = time.Now() - - if err := m.storeEntry(*entry); err != nil { - return err - } - - m.updateState() - m.notifySubscribers() - - return nil -} - -func (m *Manager) CreateHistoryEntryFromPinned(pinnedEntry *Entry) error { - if m.db == nil { - return fmt.Errorf("database not available") - } - - // Create a new unpinned entry with the same data - newEntry := Entry{ - Data: pinnedEntry.Data, - MimeType: pinnedEntry.MimeType, - Size: pinnedEntry.Size, - Timestamp: time.Now(), - IsImage: pinnedEntry.IsImage, - Preview: pinnedEntry.Preview, - Pinned: false, - } - - if err := m.storeEntryWithoutDedup(newEntry); err != nil { - return err - } - - m.updateState() - m.notifySubscribers() - - return nil -} - -func (m *Manager) storeEntryWithoutDedup(entry Entry) error { - if m.db == nil { - return fmt.Errorf("database not available") - } - - entry.Hash = computeHash(entry.Data) - - return m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - - id, err := b.NextSequence() - if err != nil { - return err - } - - entry.ID = id - - encoded, err := encodeEntry(entry) - if err != nil { - return err - } - - if err := b.Put(itob(id), encoded); err != nil { - return err - } - - return m.trimLengthInTx(b) - }) -} - -func (m *Manager) ClearHistory() { - if m.db == nil { - return - } - - // Delete only non-pinned entries - if err := m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - - var toDelete [][]byte - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - entry, err := decodeEntry(v) - if err != nil || !entry.Pinned { - toDelete = append(toDelete, k) - } - } - - for _, k := range toDelete { - if err := b.Delete(k); err != nil { - return err - } - } - return nil - }); err != nil { - log.Errorf("Failed to clear clipboard history: %v", err) - return - } - - pinnedCount := 0 - if err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b != nil { - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - entry, _ := decodeEntry(v) - if entry.Pinned { - pinnedCount++ - } - } - } - return nil - }); err != nil { - log.Errorf("Failed to count pinned entries: %v", err) - } - - if pinnedCount == 0 { - if err := m.compactDB(); err != nil { - log.Errorf("Failed to compact database: %v", err) - } - } - - m.updateState() - m.notifySubscribers() -} - -func (m *Manager) compactDB() error { - m.db.Close() - - tmpPath := m.dbPath + ".compact" - defer os.Remove(tmpPath) - - srcDB, err := bolt.Open(m.dbPath, 0o644, &bolt.Options{ReadOnly: true, Timeout: time.Second}) - if err != nil { - m.db, _ = bolt.Open(m.dbPath, 0o644, &bolt.Options{Timeout: time.Second}) - return fmt.Errorf("open source: %w", err) - } - - dstDB, err := bolt.Open(tmpPath, 0o644, &bolt.Options{Timeout: time.Second}) - if err != nil { - srcDB.Close() - m.db, _ = bolt.Open(m.dbPath, 0o644, &bolt.Options{Timeout: time.Second}) - return fmt.Errorf("open destination: %w", err) - } - - if err := bolt.Compact(dstDB, srcDB, 0); err != nil { - srcDB.Close() - dstDB.Close() - m.db, _ = bolt.Open(m.dbPath, 0o644, &bolt.Options{Timeout: time.Second}) - return fmt.Errorf("compact: %w", err) - } - - srcDB.Close() - dstDB.Close() - - if err := os.Rename(tmpPath, m.dbPath); err != nil { - m.db, _ = bolt.Open(m.dbPath, 0o644, &bolt.Options{Timeout: time.Second}) - return fmt.Errorf("rename: %w", err) - } - - m.db, err = bolt.Open(m.dbPath, 0o644, &bolt.Options{Timeout: time.Second}) - if err != nil { - return fmt.Errorf("reopen: %w", err) - } - - return nil -} - -func (m *Manager) SetClipboard(data []byte, mimeType string) error { - if int64(len(data)) > m.config.MaxEntrySize { - return fmt.Errorf("data too large") - } - - dataCopy := make([]byte, len(data)) - copy(dataCopy, data) - - m.post(func() { - if m.dataControlMgr == nil || m.dataDevice == nil { - log.Error("Data control manager or device not initialized") - return - } - - dataMgr := m.dataControlMgr.(*ext_data_control.ExtDataControlManagerV1) - - source, err := dataMgr.CreateDataSource() - if err != nil { - log.Errorf("Failed to create data source: %v", err) - return - } - - if err := source.Offer(mimeType); err != nil { - log.Errorf("Failed to offer mime type: %v", err) - return - } - - source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) { - fd := e.Fd - defer syscall.Close(fd) - - file := os.NewFile(uintptr(fd), "clipboard-pipe") - defer file.Close() - - if _, err := file.Write(dataCopy); err != nil { - log.Errorf("Failed to write clipboard data: %v", err) - } - }) - - source.SetCancelledHandler(func(e ext_data_control.ExtDataControlSourceV1CancelledEvent) { - m.ownerLock.Lock() - m.isOwner = false - m.ownerLock.Unlock() - }) - - m.currentSource = source - m.sourceMutex.Lock() - m.sourceMimeTypes = []string{mimeType} - m.sourceMutex.Unlock() - - m.ownerLock.Lock() - m.isOwner = true - m.ownerLock.Unlock() - - device := m.dataDevice.(*ext_data_control.ExtDataControlDeviceV1) - if err := device.SetSelection(source); err != nil { - log.Errorf("Failed to set selection: %v", err) - } - }) - - return nil -} - -func (m *Manager) CopyText(text string) error { - if err := m.SetClipboard([]byte(text), "text/plain;charset=utf-8"); err != nil { - return err - } - - entry := Entry{ - Data: []byte(text), - MimeType: "text/plain;charset=utf-8", - Size: len(text), - Timestamp: time.Now(), - IsImage: false, - Preview: m.textPreview([]byte(text)), - } - - if err := m.storeEntry(entry); err != nil { - log.Errorf("Failed to store clipboard entry: %v", err) - } - - m.updateState() - m.notifySubscribers() - - return nil -} - -func (m *Manager) PasteText() (string, error) { - history := m.GetHistory() - if len(history) == 0 { - return "", fmt.Errorf("no clipboard data available") - } - - entry := history[0] - if entry.IsImage { - return "", fmt.Errorf("clipboard contains image, not text") - } - - fullEntry, err := m.GetEntry(entry.ID) - if err != nil { - return "", err - } - - return string(fullEntry.Data), nil -} - -func (m *Manager) Close() { - if !m.alive { - return - } - - m.alive = false - close(m.stopChan) - - close(m.dirty) - m.notifierWg.Wait() - - m.subMutex.Lock() - for _, ch := range m.subscribers { - close(ch) - } - m.subscribers = make(map[string]chan State) - m.subMutex.Unlock() - - if m.currentSource != nil { - source := m.currentSource.(*ext_data_control.ExtDataControlSourceV1) - source.Destroy() - } - - if m.dataDevice != nil { - device := m.dataDevice.(*ext_data_control.ExtDataControlDeviceV1) - device.Destroy() - } - - if m.dataControlMgr != nil { - mgr := m.dataControlMgr.(*ext_data_control.ExtDataControlManagerV1) - mgr.Destroy() - } - - if m.registry != nil { - m.registry.Destroy() - } - - if m.db != nil { - m.db.Close() - } -} - -func (m *Manager) clearHistoryInternal() error { - return m.db.Update(func(tx *bolt.Tx) error { - if err := tx.DeleteBucket([]byte("clipboard")); err != nil { - return err - } - _, err := tx.CreateBucket([]byte("clipboard")) - return err - }) -} - -func (m *Manager) clearOldEntries(days int) error { - cutoff := time.Now().AddDate(0, 0, -days) - - return m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - - var toDelete [][]byte - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - entry, err := decodeEntry(v) - if err != nil { - continue - } - // Skip pinned entries - if entry.Pinned { - continue - } - if entry.Timestamp.Before(cutoff) { - toDelete = append(toDelete, k) - } - } - - for _, k := range toDelete { - if err := b.Delete(k); err != nil { - return err - } - } - return nil - }) -} - -func (m *Manager) migrateHashes() error { - if m.db == nil { - return nil - } - - var needsMigration bool - if err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - if extractHash(v) == 0 { - needsMigration = true - return nil - } - } - return nil - }); err != nil { - return err - } - - if !needsMigration { - return nil - } - - log.Info("Migrating clipboard entries to add hashes...") - - return m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - - var updates []struct { - key []byte - entry Entry - } - - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - entry, err := decodeEntry(v) - if err != nil { - continue - } - if entry.Hash != 0 { - continue - } - entry.Hash = computeHash(entry.Data) - keyCopy := make([]byte, len(k)) - copy(keyCopy, k) - updates = append(updates, struct { - key []byte - entry Entry - }{keyCopy, entry}) - } - - for _, u := range updates { - encoded, err := encodeEntry(u.entry) - if err != nil { - continue - } - if err := b.Put(u.key, encoded); err != nil { - return err - } - } - - log.Infof("Migrated %d clipboard entries", len(updates)) - return nil - }) -} - -func (m *Manager) Search(params SearchParams) SearchResult { - if m.db == nil { - return SearchResult{} - } - - if params.Limit <= 0 { - params.Limit = 50 - } - if params.Limit > 500 { - params.Limit = 500 - } - - query := strings.ToLower(params.Query) - mimeFilter := strings.ToLower(params.MimeType) - - var all []Entry - if err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - - c := b.Cursor() - for k, v := c.Last(); k != nil; k, v = c.Prev() { - entry, err := decodeEntry(v) - if err != nil { - continue - } - - if params.IsImage != nil && entry.IsImage != *params.IsImage { - continue - } - - if mimeFilter != "" && !strings.Contains(strings.ToLower(entry.MimeType), mimeFilter) { - continue - } - - if params.Before != nil && entry.Timestamp.Unix() >= *params.Before { - continue - } - - if params.After != nil && entry.Timestamp.Unix() <= *params.After { - continue - } - - if query != "" && !strings.Contains(strings.ToLower(entry.Preview), query) { - continue - } - - entry.Data = nil - all = append(all, entry) - } - return nil - }); err != nil { - log.Errorf("Search failed: %v", err) - } - - total := len(all) - - start := params.Offset - if start > total { - start = total - } - end := start + params.Limit - if end > total { - end = total - } - - return SearchResult{ - Entries: all[start:end], - Total: total, - HasMore: end < total, - } -} - -func (m *Manager) GetConfig() Config { - return m.config -} - -func (m *Manager) SetConfig(cfg Config) error { - m.configMutex.Lock() - m.config = cfg - m.configMutex.Unlock() - - m.updateState() - m.notifySubscribers() - - return SaveConfig(cfg) -} - -func (m *Manager) getConfig() Config { - m.configMutex.RLock() - defer m.configMutex.RUnlock() - return m.config -} - -func (m *Manager) watchConfig() { - if m.configPath == "" { - return - } - - watcher, err := fsnotify.NewWatcher() - if err != nil { - log.Warnf("Failed to create config watcher: %v", err) - return - } - defer watcher.Close() - - configDir := filepath.Dir(m.configPath) - if err := watcher.Add(configDir); err != nil { - log.Warnf("Failed to watch config directory: %v", err) - return - } - - for { - select { - case <-m.stopChan: - return - case event, ok := <-watcher.Events: - if !ok { - return - } - if event.Name != m.configPath { - continue - } - if event.Op&(fsnotify.Write|fsnotify.Create) == 0 { - continue - } - newCfg := LoadConfig() - m.applyConfigChange(newCfg) - case err, ok := <-watcher.Errors: - if !ok { - return - } - log.Warnf("Config watcher error: %v", err) - } - } -} - -func (m *Manager) applyConfigChange(newCfg Config) { - m.configMutex.Lock() - oldCfg := m.config - m.config = newCfg - m.configMutex.Unlock() - - switch { - case newCfg.Disabled && !oldCfg.Disabled: - log.Info("Clipboard tracking disabled") - case !newCfg.Disabled && oldCfg.Disabled: - log.Info("Clipboard tracking enabled") - } - - m.updateState() - m.notifySubscribers() -} - -func (m *Manager) StoreData(data []byte, mimeType string) error { - cfg := m.getConfig() - - if cfg.Disabled { - return fmt.Errorf("clipboard tracking disabled") - } - - if m.db == nil { - return fmt.Errorf("database not available") - } - - if len(data) == 0 { - return nil - } - - if int64(len(data)) > cfg.MaxEntrySize { - return fmt.Errorf("data too large") - } - - if len(bytes.TrimSpace(data)) == 0 { - return nil - } - - entry := Entry{ - Data: data, - MimeType: mimeType, - Size: len(data), - Timestamp: time.Now(), - IsImage: m.isImageMimeType(mimeType), - } - - switch { - case entry.IsImage: - entry.Preview = m.imagePreview(data, mimeType) - case mimeType == "text/uri-list": - entry.Preview, entry.IsImage = m.uriListPreview(data) - default: - entry.Preview = m.textPreview(data) - } - - if err := m.storeEntry(entry); err != nil { - return err - } - - m.updateState() - m.notifySubscribers() - - return nil -} - -func (m *Manager) PinEntry(id uint64) error { - if m.db == nil { - return fmt.Errorf("database not available") - } - - entryToPin, err := m.GetEntry(id) - if err != nil { - return err - } - - var hashExists bool - if err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - entry, err := decodeEntry(v) - if err != nil || !entry.Pinned { - continue - } - if entry.Hash == entryToPin.Hash { - hashExists = true - return nil - } - } - return nil - }); err != nil { - return err - } - - if hashExists { - return nil - } - - // Check pinned count - cfg := m.getConfig() - pinnedCount := 0 - if err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - entry, err := decodeEntry(v) - if err == nil && entry.Pinned { - pinnedCount++ - } - } - return nil - }); err != nil { - log.Errorf("Failed to count pinned entries: %v", err) - } - - if pinnedCount >= cfg.MaxPinned { - return fmt.Errorf("maximum pinned entries reached (%d)", cfg.MaxPinned) - } - - err = m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - v := b.Get(itob(id)) - if v == nil { - return fmt.Errorf("entry not found") - } - - entry, err := decodeEntry(v) - if err != nil { - return err - } - - entry.Pinned = true - encoded, err := encodeEntry(entry) - if err != nil { - return err - } - - return b.Put(itob(id), encoded) - }) - - if err == nil { - m.updateState() - m.notifySubscribers() - } - - return err -} - -func (m *Manager) UnpinEntry(id uint64) error { - if m.db == nil { - return fmt.Errorf("database not available") - } - - err := m.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - v := b.Get(itob(id)) - if v == nil { - return fmt.Errorf("entry not found") - } - - entry, err := decodeEntry(v) - if err != nil { - return err - } - - entry.Pinned = false - encoded, err := encodeEntry(entry) - if err != nil { - return err - } - - return b.Put(itob(id), encoded) - }) - - if err == nil { - m.updateState() - m.notifySubscribers() - } - - return err -} - -func (m *Manager) GetPinnedEntries() []Entry { - if m.db == nil { - return nil - } - - var pinned []Entry - if err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - - c := b.Cursor() - for k, v := c.Last(); k != nil; k, v = c.Prev() { - entry, err := decodeEntry(v) - if err != nil { - continue - } - if entry.Pinned { - entry.Data = nil - pinned = append(pinned, entry) - } - } - return nil - }); err != nil { - log.Errorf("Failed to get pinned entries: %v", err) - } - - return pinned -} - -func (m *Manager) GetPinnedCount() int { - if m.db == nil { - return 0 - } - - count := 0 - if err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("clipboard")) - if b == nil { - return nil - } - - c := b.Cursor() - for k, v := c.First(); k != nil; k, v = c.Next() { - entry, err := decodeEntry(v) - if err == nil && entry.Pinned { - count++ - } - } - return nil - }); err != nil { - log.Errorf("Failed to count pinned entries: %v", err) - } - - return count -} - -func (m *Manager) CopyFile(filePath string) error { - fileInfo, err := os.Stat(filePath) - if err != nil { - return fmt.Errorf("file not found: %w", err) - } - - cfg := m.getConfig() - if fileInfo.Size() > cfg.MaxEntrySize { - return fmt.Errorf("file too large: %d > %d", fileInfo.Size(), cfg.MaxEntrySize) - } - - fileData, err := os.ReadFile(filePath) - if err != nil { - return fmt.Errorf("read file: %w", err) - } - - exportedPath, err := m.ExportFileForFlatpak(filePath) - if err != nil { - exportedPath = filePath - } - fileURI := "file://" + exportedPath - - if imgData, imgMime, ok := m.tryReadImageFromURI([]byte("file://" + filePath)); ok { - entry := Entry{ - Data: imgData, - MimeType: imgMime, - Size: len(imgData), - Timestamp: time.Now(), - IsImage: true, - Preview: m.imagePreview(imgData, imgMime), - } - if err := m.storeEntry(entry); err != nil { - log.Errorf("Failed to store file entry: %v", err) - } - } else { - entry := Entry{ - Data: fileData, - MimeType: "text/uri-list", - Size: len(fileData), - Timestamp: time.Now(), - IsImage: false, - Preview: fmt.Sprintf("[[ file %s ]]", filepath.Base(filePath)), - } - if err := m.storeEntry(entry); err != nil { - log.Errorf("Failed to store file entry: %v", err) - } - } - - m.updateState() - m.notifySubscribers() - - _, imgMime, imgErr := image.DecodeConfig(bytes.NewReader(fileData)) - - m.post(func() { - if m.dataControlMgr == nil || m.dataDevice == nil { - log.Error("Data control manager or device not initialized") - return - } - - dataMgr := m.dataControlMgr.(*ext_data_control.ExtDataControlManagerV1) - source, err := dataMgr.CreateDataSource() - if err != nil { - log.Errorf("Failed to create data source: %v", err) - return - } - - type offer struct { - mime string - data []byte - } - offers := []offer{ - {"x-special/gnome-copied-files", []byte("copy\n" + fileURI)}, - {"text/uri-list", []byte(fileURI + "\r\n")}, - {"text/plain", []byte(filePath)}, - } - - if imgErr == nil { - imgMimeType := "image/" + imgMime - offers = append(offers, offer{imgMimeType, fileData}) - } - - offerData := make(map[string][]byte) - for _, o := range offers { - if err := source.Offer(o.mime); err != nil { - log.Errorf("Failed to offer %s: %v", o.mime, err) - return - } - offerData[o.mime] = o.data - } - - source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) { - fd := e.Fd - defer syscall.Close(fd) - file := os.NewFile(uintptr(fd), "clipboard-pipe") - defer file.Close() - if data, ok := offerData[e.MimeType]; ok { - file.Write(data) - } - }) - - source.SetCancelledHandler(func(e ext_data_control.ExtDataControlSourceV1CancelledEvent) { - m.ownerLock.Lock() - m.isOwner = false - m.ownerLock.Unlock() - }) - - m.currentSource = source - - m.ownerLock.Lock() - m.isOwner = true - m.ownerLock.Unlock() - - device := m.dataDevice.(*ext_data_control.ExtDataControlDeviceV1) - if err := device.SetSelection(source); err != nil { - log.Errorf("Failed to set selection: %v", err) - } - }) - - return nil -} - -func (m *Manager) EntryToFile(entry *Entry) string { - switch { - case entry.MimeType == "text/uri-list": - data := strings.TrimSpace(string(entry.Data)) - lines := strings.Split(data, "\n") - if len(lines) == 0 { - return "" - } - uri := strings.TrimSuffix(strings.TrimSpace(lines[0]), "\r") - if path, ok := strings.CutPrefix(uri, "file://"); ok { - return path - } - case entry.IsImage: - ext := ".png" - if suffix, ok := strings.CutPrefix(entry.MimeType, "image/"); ok { - ext = "." + suffix - } - cacheDir, err := os.UserCacheDir() - if err != nil { - return "" - } - clipDir := filepath.Join(cacheDir, "dms", "clipboard") - if err := os.MkdirAll(clipDir, 0o755); err != nil { - return "" - } - filePath := filepath.Join(clipDir, fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)) - if os.WriteFile(filePath, entry.Data, 0o644) != nil { - return "" - } - return filePath - } - return "" -} - -func (m *Manager) ExportFileForFlatpak(filePath string) (string, error) { - if _, err := os.Stat(filePath); err != nil { - return "", fmt.Errorf("file not found: %w", err) - } - - if m.dbusConn == nil { - conn, err := dbus.ConnectSessionBus() - if err != nil { - return "", fmt.Errorf("connect session bus: %w", err) - } - if !conn.SupportsUnixFDs() { - conn.Close() - return "", fmt.Errorf("D-Bus connection does not support Unix FD passing") - } - m.dbusConn = conn - } - - file, err := os.Open(filePath) - if err != nil { - return "", fmt.Errorf("open file: %w", err) - } - fd := int(file.Fd()) - - portal := m.dbusConn.Object("org.freedesktop.portal.Documents", "/org/freedesktop/portal/documents") - - var docIds []string - var extra map[string]dbus.Variant - flags := uint32(0) - - err = portal.Call( - "org.freedesktop.portal.Documents.AddFull", - 0, - []dbus.UnixFD{dbus.UnixFD(fd)}, - flags, - "", - []string{}, - ).Store(&docIds, &extra) - - file.Close() - - if err != nil { - return "", fmt.Errorf("AddFull: %w", err) - } - - if len(docIds) == 0 { - return "", fmt.Errorf("no doc IDs returned") - } - - docId := docIds[0] - - for _, app := range getInstalledFlatpaks() { - _ = portal.Call( - "org.freedesktop.portal.Documents.GrantPermissions", - 0, - docId, - app, - []string{"read"}, - ).Err - } - - uid := os.Getuid() - basename := filepath.Base(filePath) - exportedPath := fmt.Sprintf("/run/user/%d/doc/%s/%s", uid, docId, basename) - - return exportedPath, nil -} - -func getInstalledFlatpaks() []string { - out, err := exec.Command("flatpak", "list", "--app", "--columns=application").Output() - if err != nil { - return nil - } - - var apps []string - for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { - if app := strings.TrimSpace(line); app != "" { - apps = append(apps, app) - } - } - return apps -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/manager_test.go deleted file mode 100644 index db45bca..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/manager_test.go +++ /dev/null @@ -1,531 +0,0 @@ -package clipboard - -import ( - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - mocks_wlcontext "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/wlcontext" -) - -func TestEncodeDecodeEntry_Roundtrip(t *testing.T) { - original := Entry{ - ID: 12345, - Data: []byte("hello world"), - MimeType: "text/plain;charset=utf-8", - Preview: "hello world", - Size: 11, - Timestamp: time.Now().Truncate(time.Second), - IsImage: false, - } - - encoded, err := encodeEntry(original) - assert.NoError(t, err) - - decoded, err := decodeEntry(encoded) - assert.NoError(t, err) - - assert.Equal(t, original.ID, decoded.ID) - assert.Equal(t, original.Data, decoded.Data) - assert.Equal(t, original.MimeType, decoded.MimeType) - assert.Equal(t, original.Preview, decoded.Preview) - assert.Equal(t, original.Size, decoded.Size) - assert.Equal(t, original.Timestamp.Unix(), decoded.Timestamp.Unix()) - assert.Equal(t, original.IsImage, decoded.IsImage) -} - -func TestEncodeDecodeEntry_Image(t *testing.T) { - original := Entry{ - ID: 99999, - Data: []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, - MimeType: "image/png", - Preview: "[[ image 8 B png 100x100 ]]", - Size: 8, - Timestamp: time.Now().Truncate(time.Second), - IsImage: true, - } - - encoded, err := encodeEntry(original) - assert.NoError(t, err) - - decoded, err := decodeEntry(encoded) - assert.NoError(t, err) - - assert.Equal(t, original.ID, decoded.ID) - assert.Equal(t, original.Data, decoded.Data) - assert.True(t, decoded.IsImage) - assert.Equal(t, original.Preview, decoded.Preview) -} - -func TestEncodeDecodeEntry_EmptyData(t *testing.T) { - original := Entry{ - ID: 1, - Data: []byte{}, - MimeType: "text/plain", - Preview: "", - Size: 0, - Timestamp: time.Now().Truncate(time.Second), - IsImage: false, - } - - encoded, err := encodeEntry(original) - assert.NoError(t, err) - - decoded, err := decodeEntry(encoded) - assert.NoError(t, err) - - assert.Equal(t, original.ID, decoded.ID) - assert.Empty(t, decoded.Data) -} - -func TestEncodeDecodeEntry_LargeData(t *testing.T) { - largeData := make([]byte, 100000) - for i := range largeData { - largeData[i] = byte(i % 256) - } - - original := Entry{ - ID: 777, - Data: largeData, - MimeType: "application/octet-stream", - Preview: "binary data...", - Size: len(largeData), - Timestamp: time.Now().Truncate(time.Second), - IsImage: false, - } - - encoded, err := encodeEntry(original) - assert.NoError(t, err) - - decoded, err := decodeEntry(encoded) - assert.NoError(t, err) - - assert.Equal(t, original.Data, decoded.Data) - assert.Equal(t, original.Size, decoded.Size) -} - -func TestStateEqual_BothNil(t *testing.T) { - assert.False(t, stateEqual(nil, nil)) -} - -func TestStateEqual_OneNil(t *testing.T) { - s := &State{Enabled: true} - assert.False(t, stateEqual(s, nil)) - assert.False(t, stateEqual(nil, s)) -} - -func TestStateEqual_EnabledDiffers(t *testing.T) { - a := &State{Enabled: true, History: []Entry{}} - b := &State{Enabled: false, History: []Entry{}} - assert.False(t, stateEqual(a, b)) -} - -func TestStateEqual_HistoryLengthDiffers(t *testing.T) { - a := &State{Enabled: true, History: []Entry{{ID: 1}}} - b := &State{Enabled: true, History: []Entry{}} - assert.False(t, stateEqual(a, b)) -} - -func TestStateEqual_BothEqual(t *testing.T) { - a := &State{Enabled: true, History: []Entry{{ID: 1}, {ID: 2}}} - b := &State{Enabled: true, History: []Entry{{ID: 3}, {ID: 4}}} - assert.True(t, stateEqual(a, b)) -} - -func TestManager_ConcurrentSubscriberAccess(t *testing.T) { - m := &Manager{ - subscribers: make(map[string]chan State), - dirty: make(chan struct{}, 1), - } - - var wg sync.WaitGroup - const goroutines = 20 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - subID := string(rune('a' + id)) - ch := m.Subscribe(subID) - assert.NotNil(t, ch) - time.Sleep(time.Millisecond) - m.Unsubscribe(subID) - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentGetState(t *testing.T) { - m := &Manager{ - state: &State{ - Enabled: true, - History: []Entry{{ID: 1}, {ID: 2}}, - }, - } - - var wg sync.WaitGroup - const goroutines = 50 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - s := m.GetState() - _ = s.Enabled - _ = len(s.History) - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.stateMutex.Lock() - m.state = &State{ - Enabled: j%2 == 0, - History: []Entry{{ID: uint64(j)}}, - } - m.stateMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentConfigAccess(t *testing.T) { - m := &Manager{ - config: DefaultConfig(), - } - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - cfg := m.getConfig() - _ = cfg.MaxHistory - _ = cfg.MaxEntrySize - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.configMutex.Lock() - m.config.MaxHistory = 50 + j - m.config.MaxEntrySize = int64(1024 * j) - m.configMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_NotifySubscribersNonBlocking(t *testing.T) { - m := &Manager{ - dirty: make(chan struct{}, 1), - } - - for i := 0; i < 10; i++ { - m.notifySubscribers() - } - - assert.Len(t, m.dirty, 1) -} - -func TestManager_ConcurrentOfferAccess(t *testing.T) { - m := &Manager{ - offerMimeTypes: make(map[any][]string), - offerRegistry: make(map[uint32]any), - } - - var wg sync.WaitGroup - const goroutines = 20 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - m.offerMutex.Lock() - m.offerRegistry[key] = struct{}{} - m.offerMimeTypes[key] = []string{"text/plain"} - m.offerMutex.Unlock() - - m.offerMutex.RLock() - _ = m.offerRegistry[key] - _ = m.offerMimeTypes[key] - m.offerMutex.RUnlock() - - m.offerMutex.Lock() - delete(m.offerRegistry, key) - delete(m.offerMimeTypes, key) - m.offerMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentPersistAccess(t *testing.T) { - m := &Manager{ - persistData: make(map[string][]byte), - persistMimeTypes: []string{}, - } - - var wg sync.WaitGroup - const goroutines = 20 - const iterations = 50 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.persistMutex.RLock() - _ = m.persistData - _ = m.persistMimeTypes - m.persistMutex.RUnlock() - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.persistMutex.Lock() - m.persistMimeTypes = []string{"text/plain", "text/html"} - m.persistData = map[string][]byte{ - "text/plain": []byte("test"), - } - m.persistMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentOwnerAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.ownerLock.Lock() - _ = m.isOwner - m.ownerLock.Unlock() - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.ownerLock.Lock() - m.isOwner = j%2 == 0 - m.ownerLock.Unlock() - } - }() - } - - wg.Wait() -} - -func TestItob(t *testing.T) { - tests := []struct { - input uint64 - expected []byte - }{ - {0, []byte{0, 0, 0, 0, 0, 0, 0, 0}}, - {1, []byte{0, 0, 0, 0, 0, 0, 0, 1}}, - {256, []byte{0, 0, 0, 0, 0, 0, 1, 0}}, - {0xFFFFFFFFFFFFFFFF, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - } - - for _, tt := range tests { - result := itob(tt.input) - assert.Equal(t, tt.expected, result) - } -} - -func TestSizeStr(t *testing.T) { - tests := []struct { - input int - expected string - }{ - {0, "0 B"}, - {100, "100 B"}, - {1024, "1 KiB"}, - {2048, "2 KiB"}, - {1048576, "1 MiB"}, - {5242880, "5 MiB"}, - } - - for _, tt := range tests { - result := sizeStr(tt.input) - assert.Equal(t, tt.expected, result) - } -} - -func TestSelectMimeType(t *testing.T) { - m := &Manager{} - - tests := []struct { - mimes []string - expected string - }{ - {[]string{"text/plain;charset=utf-8", "text/html"}, "text/plain;charset=utf-8"}, - {[]string{"text/html", "text/plain"}, "text/plain"}, - {[]string{"text/html", "image/png"}, "image/png"}, - {[]string{"image/png", "image/jpeg"}, "image/png"}, - {[]string{"image/png"}, "image/png"}, - {[]string{"application/octet-stream"}, "application/octet-stream"}, - {[]string{}, ""}, - } - - for _, tt := range tests { - result := m.selectMimeType(tt.mimes) - assert.Equal(t, tt.expected, result) - } -} - -func TestIsImageMimeType(t *testing.T) { - m := &Manager{} - - assert.True(t, m.isImageMimeType("image/png")) - assert.True(t, m.isImageMimeType("image/jpeg")) - assert.True(t, m.isImageMimeType("image/gif")) - assert.False(t, m.isImageMimeType("text/plain")) - assert.False(t, m.isImageMimeType("application/json")) -} - -func TestTextPreview(t *testing.T) { - m := &Manager{} - - short := m.textPreview([]byte("hello world")) - assert.Equal(t, "hello world", short) - - withWhitespace := m.textPreview([]byte(" hello world ")) - assert.Equal(t, "hello world", withWhitespace) - - longText := make([]byte, 200) - for i := range longText { - longText[i] = 'a' - } - preview := m.textPreview(longText) - assert.True(t, len(preview) > 100) - assert.Contains(t, preview, "…") -} - -func TestDefaultConfig(t *testing.T) { - cfg := DefaultConfig() - assert.Equal(t, 100, cfg.MaxHistory) - assert.Equal(t, int64(5*1024*1024), cfg.MaxEntrySize) - assert.Equal(t, 0, cfg.AutoClearDays) - assert.False(t, cfg.ClearAtStartup) - assert.False(t, cfg.Disabled) -} - -func TestManager_PostDelegatesToWlContext(t *testing.T) { - mockCtx := mocks_wlcontext.NewMockWaylandContext(t) - - var called atomic.Bool - mockCtx.EXPECT().Post(mock.AnythingOfType("func()")).Run(func(fn func()) { - called.Store(true) - fn() - }).Once() - - m := &Manager{ - wlCtx: mockCtx, - } - - executed := false - m.post(func() { - executed = true - }) - - assert.True(t, called.Load()) - assert.True(t, executed) -} - -func TestManager_PostExecutesFunctionViaContext(t *testing.T) { - mockCtx := mocks_wlcontext.NewMockWaylandContext(t) - - var capturedFn func() - mockCtx.EXPECT().Post(mock.AnythingOfType("func()")).Run(func(fn func()) { - capturedFn = fn - }).Times(3) - - m := &Manager{ - wlCtx: mockCtx, - } - - counter := 0 - m.post(func() { counter++ }) - m.post(func() { counter += 10 }) - m.post(func() { counter += 100 }) - - assert.NotNil(t, capturedFn) - capturedFn() - assert.Equal(t, 100, counter) -} - -func TestManager_ConcurrentPostWithMock(t *testing.T) { - mockCtx := mocks_wlcontext.NewMockWaylandContext(t) - - var postCount atomic.Int32 - mockCtx.EXPECT().Post(mock.AnythingOfType("func()")).Run(func(fn func()) { - postCount.Add(1) - }).Times(100) - - m := &Manager{ - wlCtx: mockCtx, - } - - var wg sync.WaitGroup - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < 10; j++ { - m.post(func() {}) - } - }() - } - - wg.Wait() - assert.Equal(t, int32(100), postCount.Load()) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/types.go deleted file mode 100644 index c754561..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/clipboard/types.go +++ /dev/null @@ -1,196 +0,0 @@ -package clipboard - -import ( - "encoding/json" - "os" - "path/filepath" - "sync" - "time" - - "github.com/godbus/dbus/v5" - bolt "go.etcd.io/bbolt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlcontext" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type Config struct { - MaxHistory int `json:"maxHistory"` - MaxEntrySize int64 `json:"maxEntrySize"` - AutoClearDays int `json:"autoClearDays"` - ClearAtStartup bool `json:"clearAtStartup"` - Disabled bool `json:"disabled"` - MaxPinned int `json:"maxPinned"` -} - -func DefaultConfig() Config { - return Config{ - MaxHistory: 100, - MaxEntrySize: 5 * 1024 * 1024, - AutoClearDays: 0, - ClearAtStartup: false, - MaxPinned: 25, - } -} - -func getConfigPath() (string, error) { - configDir, err := os.UserConfigDir() - if err != nil { - return "", err - } - return filepath.Join(configDir, "DankMaterialShell", "clsettings.json"), nil -} - -func LoadConfig() Config { - cfg := DefaultConfig() - - path, err := getConfigPath() - if err != nil { - return cfg - } - - data, err := os.ReadFile(path) - if err != nil { - return cfg - } - - if err := json.Unmarshal(data, &cfg); err != nil { - return DefaultConfig() - } - return cfg -} - -func SaveConfig(cfg Config) error { - path, err := getConfigPath() - if err != nil { - return err - } - - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - return err - } - - data, err := json.MarshalIndent(cfg, "", " ") - if err != nil { - return err - } - - return os.WriteFile(path, data, 0o644) -} - -type SearchParams struct { - Query string `json:"query"` - MimeType string `json:"mimeType"` - IsImage *bool `json:"isImage"` - Limit int `json:"limit"` - Offset int `json:"offset"` - Before *int64 `json:"before"` - After *int64 `json:"after"` -} - -type SearchResult struct { - Entries []Entry `json:"entries"` - Total int `json:"total"` - HasMore bool `json:"hasMore"` -} - -type Entry struct { - ID uint64 `json:"id"` - Data []byte `json:"data,omitempty"` - MimeType string `json:"mimeType"` - Preview string `json:"preview"` - Size int `json:"size"` - Timestamp time.Time `json:"timestamp"` - IsImage bool `json:"isImage"` - Hash uint64 `json:"hash,omitempty"` - Pinned bool `json:"pinned"` -} - -type State struct { - Enabled bool `json:"enabled"` - History []Entry `json:"history"` - Current *Entry `json:"current,omitempty"` -} - -type Manager struct { - config Config - configMutex sync.RWMutex - configPath string - - display wlclient.WaylandDisplay - wlCtx wlcontext.WaylandContext - - registry *wlclient.Registry - dataControlMgr any - seat *wlclient.Seat - dataDevice any - currentOffer any - currentSource any - seatName uint32 - mimeTypes []string - offerMimeTypes map[any][]string - offerMutex sync.RWMutex - offerRegistry map[uint32]any - - sourceMimeTypes []string - sourceMutex sync.RWMutex - - persistData map[string][]byte - persistMimeTypes []string - persistMutex sync.RWMutex - - isOwner bool - ownerLock sync.Mutex - - initialized bool - - alive bool - stopChan chan struct{} - - db *bolt.DB - dbPath string - - state *State - stateMutex sync.RWMutex - - subscribers map[string]chan State - subMutex sync.RWMutex - dirty chan struct{} - notifierWg sync.WaitGroup - lastState *State - - dbusConn *dbus.Conn -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{} - } - return *m.state -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - m.subMutex.Lock() - m.subscribers[id] = ch - m.subMutex.Unlock() - return ch -} - -func (m *Manager) Unsubscribe(id string) { - m.subMutex.Lock() - if ch, ok := m.subscribers[id]; ok { - close(ch) - delete(m.subscribers, id) - } - m.subMutex.Unlock() -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions.go deleted file mode 100644 index 2f9b98b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions.go +++ /dev/null @@ -1,479 +0,0 @@ -package cups - -import ( - "errors" - "fmt" - "net" - "net/url" - "os/exec" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/config" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" -) - -func isAuthError(err error) bool { - if err == nil { - return false - } - - var httpErr ipp.HTTPError - if errors.As(err, &httpErr) { - return httpErr.Code == 401 || httpErr.Code == 403 - } - - var ippErr ipp.IPPError - if errors.As(err, &ippErr) { - return ippErr.Status == ipp.StatusErrorForbidden || - ippErr.Status == ipp.StatusErrorNotAuthenticated || - ippErr.Status == ipp.StatusErrorNotAuthorized - } - - return false -} - -func (m *Manager) GetPrinters() ([]Printer, error) { - attributes := []string{ - ipp.AttributePrinterName, - ipp.AttributePrinterUriSupported, - ipp.AttributePrinterState, - ipp.AttributePrinterStateReasons, - ipp.AttributePrinterLocation, - ipp.AttributePrinterInfo, - ipp.AttributePrinterMakeAndModel, - ipp.AttributePrinterIsAcceptingJobs, - } - - printerAttrs, err := m.client.GetPrinters(attributes) - if err != nil { - if isNoPrintersError(err) { - return []Printer{}, nil - } - return nil, err - } - - printers := make([]Printer, 0, len(printerAttrs)) - for _, attrs := range printerAttrs { - printer := Printer{ - Name: getStringAttr(attrs, ipp.AttributePrinterName), - URI: getStringAttr(attrs, ipp.AttributePrinterUriSupported), - State: parsePrinterState(attrs), - StateReason: getStringAttr(attrs, ipp.AttributePrinterStateReasons), - Location: getStringAttr(attrs, ipp.AttributePrinterLocation), - Info: getStringAttr(attrs, ipp.AttributePrinterInfo), - MakeModel: getStringAttr(attrs, ipp.AttributePrinterMakeAndModel), - Accepting: getBoolAttr(attrs, ipp.AttributePrinterIsAcceptingJobs), - } - - if printer.Name != "" { - printers = append(printers, printer) - } - } - - return printers, nil -} - -func (m *Manager) GetJobs(printerName string, whichJobs string) ([]Job, error) { - attributes := []string{ - ipp.AttributeJobID, - ipp.AttributeJobName, - ipp.AttributeJobState, - ipp.AttributeJobPrinterURI, - ipp.AttributeJobOriginatingUserName, - ipp.AttributeJobKilobyteOctets, - "time-at-creation", - } - - jobAttrs, err := m.client.GetJobs(printerName, "", whichJobs, false, 0, 0, attributes) - if err != nil { - return nil, err - } - - jobs := make([]Job, 0, len(jobAttrs)) - for _, attrs := range jobAttrs { - job := Job{ - ID: getIntAttr(attrs, ipp.AttributeJobID), - Name: getStringAttr(attrs, ipp.AttributeJobName), - State: parseJobState(attrs), - User: getStringAttr(attrs, ipp.AttributeJobOriginatingUserName), - Size: getIntAttr(attrs, ipp.AttributeJobKilobyteOctets) * 1024, - } - - if uri := getStringAttr(attrs, ipp.AttributeJobPrinterURI); uri != "" { - parts := strings.Split(uri, "/") - if len(parts) > 0 { - job.Printer = parts[len(parts)-1] - } - } - - if ts := getIntAttr(attrs, "time-at-creation"); ts > 0 { - job.TimeCreated = time.Unix(int64(ts), 0) - } - - if job.ID != 0 { - jobs = append(jobs, job) - } - } - - return jobs, nil -} - -func (m *Manager) CancelJob(jobID int) error { - err := m.client.CancelJob(jobID, false) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.JobCancelPurge(jobID, false) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) PausePrinter(printerName string) error { - err := m.client.PausePrinter(printerName) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.PrinterSetEnabled(printerName, false) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) ResumePrinter(printerName string) error { - err := m.client.ResumePrinter(printerName) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.PrinterSetEnabled(printerName, true) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) PurgeJobs(printerName string) error { - err := m.client.CancelAllJob(printerName, true) - if err == nil { - m.RefreshState() - } - return err -} - -func resolveIPFromURI(uri string) string { - parsed, err := url.Parse(uri) - if err != nil { - return "" - } - host := parsed.Hostname() - if host == "" { - return "" - } - if ip := net.ParseIP(host); ip != nil { - return ip.String() - } - addrs, err := net.LookupIP(host) - if err != nil || len(addrs) == 0 { - return "" - } - for _, addr := range addrs { - if v4 := addr.To4(); v4 != nil { - return v4.String() - } - } - return addrs[0].String() -} - -func (m *Manager) GetDevices() ([]Device, error) { - if m.pkHelper != nil { - devices, err := m.pkHelper.DevicesGet(10, 0, nil, nil) - if err != nil { - return nil, err - } - for i := range devices { - if devices[i].Class == "network" { - devices[i].IP = resolveIPFromURI(devices[i].URI) - } - } - return devices, nil - } - - deviceAttrs, err := m.client.GetDevices() - if err != nil { - return nil, err - } - - devices := make([]Device, 0, len(deviceAttrs)) - for uri, attrs := range deviceAttrs { - device := Device{ - URI: uri, - Class: getStringAttr(attrs, "device-class"), - Info: getStringAttr(attrs, "device-info"), - MakeModel: getStringAttr(attrs, "device-make-and-model"), - ID: getStringAttr(attrs, "device-id"), - Location: getStringAttr(attrs, "device-location"), - } - if device.Class == "network" { - device.IP = resolveIPFromURI(uri) - } - devices = append(devices, device) - } - - return devices, nil -} - -func (m *Manager) GetPPDs() ([]PPD, error) { - ppdAttrs, err := m.client.GetPPDs() - if err != nil { - return nil, err - } - - ppds := make([]PPD, 0, len(ppdAttrs)) - for name, attrs := range ppdAttrs { - ppd := PPD{ - Name: name, - NaturalLanguage: getStringAttr(attrs, "ppd-natural-language"), - MakeModel: getStringAttr(attrs, ipp.AttributePPDMakeAndModel), - DeviceID: getStringAttr(attrs, "ppd-device-id"), - Product: getStringAttr(attrs, "ppd-product"), - PSVersion: getStringAttr(attrs, "ppd-psversion"), - Type: getStringAttr(attrs, "ppd-type"), - } - ppds = append(ppds, ppd) - } - - return ppds, nil -} - -func (m *Manager) GetClasses() ([]PrinterClass, error) { - attributes := []string{ - ipp.AttributePrinterName, - ipp.AttributePrinterUriSupported, - ipp.AttributePrinterState, - ipp.AttributeMemberURIs, - ipp.AttributeMemberNames, - ipp.AttributePrinterLocation, - ipp.AttributePrinterInfo, - } - - classAttrs, err := m.client.GetClasses(attributes) - if err != nil { - return nil, err - } - - classes := make([]PrinterClass, 0, len(classAttrs)) - for _, attrs := range classAttrs { - class := PrinterClass{ - Name: getStringAttr(attrs, ipp.AttributePrinterName), - URI: getStringAttr(attrs, ipp.AttributePrinterUriSupported), - State: parsePrinterState(attrs), - Location: getStringAttr(attrs, ipp.AttributePrinterLocation), - Info: getStringAttr(attrs, ipp.AttributePrinterInfo), - Members: getStringSliceAttr(attrs, ipp.AttributeMemberNames), - } - classes = append(classes, class) - } - - return classes, nil -} - -func createPrinterViaLpadmin(name, deviceURI, ppd, information, location string) error { - args := []string{"-p", name, "-E", "-v", deviceURI, "-m", ppd} - if information != "" { - args = append(args, "-D", information) - } - if location != "" { - args = append(args, "-L", location) - } - out, err := exec.Command("lpadmin", args...).CombinedOutput() - if err != nil { - return fmt.Errorf("lpadmin failed: %s: %w", strings.TrimSpace(string(out)), err) - } - return nil -} - -func deletePrinterViaLpadmin(name string) error { - out, err := exec.Command("lpadmin", "-x", name).CombinedOutput() - if err != nil { - return fmt.Errorf("lpadmin failed: %s: %w", strings.TrimSpace(string(out)), err) - } - return nil -} - -func (m *Manager) CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy, information, location string) error { - usedPkHelper := false - - err := m.client.CreatePrinter(name, deviceURI, ppd, shared, errorPolicy, information, location) - if isAuthError(err) && m.pkHelper != nil { - if err = m.pkHelper.PrinterAdd(name, deviceURI, ppd, information, location); err != nil { - // pkHelper failed (e.g., no polkit agent), try lpadmin as last resort. - // lpadmin -E enables the printer, so no further setup needed. - if lpadminErr := createPrinterViaLpadmin(name, deviceURI, ppd, information, location); lpadminErr != nil { - return err - } - m.RefreshState() - return nil - } - usedPkHelper = true - } else if err != nil { - return err - } - - if usedPkHelper { - m.pkHelper.PrinterSetEnabled(name, true) //nolint:errcheck - m.pkHelper.PrinterSetAcceptJobs(name, true, "") //nolint:errcheck - } else { - if err := m.client.ResumePrinter(name); isAuthError(err) && m.pkHelper != nil { - m.pkHelper.PrinterSetEnabled(name, true) //nolint:errcheck - } - if err := m.client.AcceptJobs(name); isAuthError(err) && m.pkHelper != nil { - m.pkHelper.PrinterSetAcceptJobs(name, true, "") //nolint:errcheck - } - } - - m.RefreshState() - return nil -} - -func (m *Manager) DeletePrinter(printerName string) error { - err := m.client.DeletePrinter(printerName) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.PrinterDelete(printerName) - if err != nil { - // pkHelper failed, try lpadmin as last resort - if lpadminErr := deletePrinterViaLpadmin(printerName); lpadminErr == nil { - err = nil - } - } - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) AcceptJobs(printerName string) error { - err := m.client.AcceptJobs(printerName) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.PrinterSetAcceptJobs(printerName, true, "") - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) RejectJobs(printerName string) error { - err := m.client.RejectJobs(printerName) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.PrinterSetAcceptJobs(printerName, false, "") - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) SetPrinterShared(printerName string, shared bool) error { - err := m.client.SetPrinterIsShared(printerName, shared) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.PrinterSetShared(printerName, shared) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) SetPrinterLocation(printerName, location string) error { - err := m.client.SetPrinterLocation(printerName, location) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.PrinterSetLocation(printerName, location) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) SetPrinterInfo(printerName, info string) error { - err := m.client.SetPrinterInformation(printerName, info) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.PrinterSetInfo(printerName, info) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) MoveJob(jobID int, destPrinter string) error { - err := m.client.MoveJob(jobID, destPrinter) - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) PrintTestPage(printerName string) (int, error) { - jobID, err := m.client.PrintTestPage(printerName, strings.NewReader(config.TestPage), len(config.TestPage)) - if err == nil { - m.RefreshState() - } - return jobID, err -} - -func (m *Manager) AddPrinterToClass(className, printerName string) error { - err := m.client.AddPrinterToClass(className, printerName) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.ClassAddPrinter(className, printerName) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) RemovePrinterFromClass(className, printerName string) error { - err := m.client.DeletePrinterFromClass(className, printerName) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.ClassDeletePrinter(className, printerName) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) DeleteClass(className string) error { - err := m.client.DeleteClass(className) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.ClassDelete(className) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) RestartJob(jobID int) error { - err := m.client.RestartJob(jobID) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.JobRestart(jobID) - } - if err == nil { - m.RefreshState() - } - return err -} - -func (m *Manager) HoldJob(jobID int, holdUntil string) error { - err := m.client.HoldJobUntil(jobID, holdUntil) - if isAuthError(err) && m.pkHelper != nil { - err = m.pkHelper.JobSetHoldUntil(jobID, holdUntil) - } - if err == nil { - m.RefreshState() - } - return err -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions_pkhelper_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions_pkhelper_test.go deleted file mode 100644 index ad16a8a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions_pkhelper_test.go +++ /dev/null @@ -1,235 +0,0 @@ -package cups_test - -import ( - "testing" - - mocks_cups "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups" - mocks_pkhelper "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups_pkhelper" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func authErr() error { - return ipp.IPPError{Status: ipp.StatusErrorForbidden} -} - -func TestManager_CancelJob_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelJob(1, false).Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().JobCancelPurge(1, false).Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.CancelJob(1)) -} - -func TestManager_CancelJob_PkHelperError(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelJob(1, false).Return(authErr()) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().JobCancelPurge(1, false).Return(assert.AnError) - - m := cups.NewTestManager(mockClient, mockPk) - assert.Error(t, m.CancelJob(1)) -} - -func TestManager_PausePrinter_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().PausePrinter("printer1").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterSetEnabled("printer1", false).Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.PausePrinter("printer1")) -} - -func TestManager_ResumePrinter_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().ResumePrinter("printer1").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterSetEnabled("printer1", true).Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.ResumePrinter("printer1")) -} - -func TestManager_GetDevices_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().DevicesGet(10, 0, []string(nil), []string(nil)).Return([]cups.Device{ - {URI: "usb://HP/LaserJet", Class: "direct"}, - }, nil) - - m := cups.NewTestManager(mockClient, mockPk) - got, err := m.GetDevices() - assert.NoError(t, err) - assert.Len(t, got, 1) - assert.Equal(t, "usb://HP/LaserJet", got[0].URI) -} - -func TestManager_GetDevices_PkHelperError(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().DevicesGet(10, 0, []string(nil), []string(nil)).Return(nil, assert.AnError) - - m := cups.NewTestManager(mockClient, mockPk) - _, err := m.GetDevices() - assert.Error(t, err) -} - -func TestManager_CreatePrinter_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterAdd("newprinter", "usb://HP", "generic.ppd", "info", "location").Return(nil) - mockPk.EXPECT().PrinterSetEnabled("newprinter", true).Return(nil) - mockPk.EXPECT().PrinterSetAcceptJobs("newprinter", true, "").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location")) -} - -func TestManager_DeletePrinter_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeletePrinter("printer1").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterDelete("printer1").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.DeletePrinter("printer1")) -} - -func TestManager_AcceptJobs_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().AcceptJobs("printer1").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterSetAcceptJobs("printer1", true, "").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.AcceptJobs("printer1")) -} - -func TestManager_RejectJobs_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().RejectJobs("printer1").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterSetAcceptJobs("printer1", false, "").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.RejectJobs("printer1")) -} - -func TestManager_SetPrinterShared_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterSetShared("printer1", true).Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.SetPrinterShared("printer1", true)) -} - -func TestManager_SetPrinterLocation_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterSetLocation("printer1", "Office").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.SetPrinterLocation("printer1", "Office")) -} - -func TestManager_SetPrinterInfo_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().PrinterSetInfo("printer1", "Main Printer").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.SetPrinterInfo("printer1", "Main Printer")) -} - -func TestManager_AddPrinterToClass_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().ClassAddPrinter("office", "printer1").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.AddPrinterToClass("office", "printer1")) -} - -func TestManager_RemovePrinterFromClass_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().ClassDeletePrinter("office", "printer1").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.RemovePrinterFromClass("office", "printer1")) -} - -func TestManager_DeleteClass_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeleteClass("office").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().ClassDelete("office").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.DeleteClass("office")) -} - -func TestManager_RestartJob_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().RestartJob(1).Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().JobRestart(1).Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.RestartJob(1)) -} - -func TestManager_HoldJob_WithPkHelper(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(authErr()) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - mockPk := mocks_pkhelper.NewMockPkHelper(t) - mockPk.EXPECT().JobSetHoldUntil(1, "indefinite").Return(nil) - - m := cups.NewTestManager(mockClient, mockPk) - assert.NoError(t, m.HoldJob(1, "indefinite")) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions_test.go deleted file mode 100644 index 70f4f21..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/actions_test.go +++ /dev/null @@ -1,450 +0,0 @@ -package cups - -import ( - "errors" - "testing" - "time" - - mocks_cups "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestManager_GetPrinters(t *testing.T) { - tests := []struct { - name string - mockRet map[string]ipp.Attributes - mockErr error - want int - wantErr bool - }{ - { - name: "success", - mockRet: map[string]ipp.Attributes{ - "printer1": { - ipp.AttributePrinterName: []ipp.Attribute{{Value: "printer1"}}, - ipp.AttributePrinterUriSupported: []ipp.Attribute{{Value: "ipp://localhost/printers/printer1"}}, - ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}}, - ipp.AttributePrinterStateReasons: []ipp.Attribute{{Value: "none"}}, - ipp.AttributePrinterLocation: []ipp.Attribute{{Value: "Office"}}, - ipp.AttributePrinterInfo: []ipp.Attribute{{Value: "Test Printer"}}, - ipp.AttributePrinterMakeAndModel: []ipp.Attribute{{Value: "Generic"}}, - ipp.AttributePrinterIsAcceptingJobs: []ipp.Attribute{{Value: true}}, - }, - }, - mockErr: nil, - want: 1, - wantErr: false, - }, - { - name: "error", - mockRet: nil, - mockErr: errors.New("test error"), - want: 0, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(tt.mockRet, tt.mockErr) - - m := &Manager{ - client: mockClient, - } - - got, err := m.GetPrinters() - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, len(got)) - if len(got) > 0 { - assert.Equal(t, "printer1", got[0].Name) - assert.Equal(t, "idle", got[0].State) - assert.Equal(t, "Office", got[0].Location) - assert.True(t, got[0].Accepting) - } - } - }) - } -} - -func TestManager_GetJobs(t *testing.T) { - tests := []struct { - name string - mockRet map[int]ipp.Attributes - mockErr error - want int - wantErr bool - }{ - { - name: "success", - mockRet: map[int]ipp.Attributes{ - 1: { - ipp.AttributeJobID: []ipp.Attribute{{Value: 1}}, - ipp.AttributeJobName: []ipp.Attribute{{Value: "test-job"}}, - ipp.AttributeJobState: []ipp.Attribute{{Value: 5}}, - ipp.AttributeJobPrinterURI: []ipp.Attribute{{Value: "ipp://localhost/printers/printer1"}}, - ipp.AttributeJobOriginatingUserName: []ipp.Attribute{{Value: "testuser"}}, - ipp.AttributeJobKilobyteOctets: []ipp.Attribute{{Value: 10}}, - "time-at-creation": []ipp.Attribute{{Value: 1609459200}}, - }, - }, - mockErr: nil, - want: 1, - wantErr: false, - }, - { - name: "error", - mockRet: nil, - mockErr: errors.New("test error"), - want: 0, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetJobs("printer1", "", "not-completed", false, 0, 0, mock.Anything). - Return(tt.mockRet, tt.mockErr) - - m := &Manager{ - client: mockClient, - } - - got, err := m.GetJobs("printer1", "not-completed") - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, len(got)) - if len(got) > 0 { - assert.Equal(t, 1, got[0].ID) - assert.Equal(t, "test-job", got[0].Name) - assert.Equal(t, "processing", got[0].State) - assert.Equal(t, "testuser", got[0].User) - assert.Equal(t, "printer1", got[0].Printer) - assert.Equal(t, 10240, got[0].Size) - assert.Equal(t, time.Unix(1609459200, 0), got[0].TimeCreated) - } - } - }) - } -} - -func TestManager_CancelJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelJob(1, false).Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.CancelJob(1)) -} - -func TestManager_PausePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().PausePrinter("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.PausePrinter("printer1")) -} - -func TestManager_ResumePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().ResumePrinter("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.ResumePrinter("printer1")) -} - -func TestManager_PurgeJobs(t *testing.T) { - tests := []struct { - name string - mockErr error - wantErr bool - }{ - { - name: "success", - mockErr: nil, - wantErr: false, - }, - { - name: "error", - mockErr: errors.New("test error"), - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelAllJob("printer1", true).Return(tt.mockErr) - if !tt.wantErr { - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - } - - m := NewTestManager(mockClient, nil) - - err := m.PurgeJobs("printer1") - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestManager_GetDevices(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetDevices().Return(map[string]ipp.Attributes{ - "usb://HP/LaserJet": { - "device-class": []ipp.Attribute{{Value: "direct"}}, - "device-info": []ipp.Attribute{{Value: "HP LaserJet"}}, - "device-make-and-model": []ipp.Attribute{{Value: "HP LaserJet 1020"}}, - }, - }, nil) - - m := &Manager{client: mockClient} - got, err := m.GetDevices() - assert.NoError(t, err) - assert.Len(t, got, 1) - assert.Equal(t, "usb://HP/LaserJet", got[0].URI) - assert.Equal(t, "direct", got[0].Class) -} - -func TestManager_GetPPDs(t *testing.T) { - tests := []struct { - name string - mockRet map[string]ipp.Attributes - mockErr error - want int - wantErr bool - }{ - { - name: "success", - mockRet: map[string]ipp.Attributes{ - "drv:///sample.drv/generic.ppd": { - "ppd-make-and-model": []ipp.Attribute{{Value: "Generic PostScript"}}, - "ppd-type": []ipp.Attribute{{Value: "ppd"}}, - }, - }, - mockErr: nil, - want: 1, - wantErr: false, - }, - { - name: "error", - mockRet: nil, - mockErr: errors.New("test error"), - want: 0, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetPPDs().Return(tt.mockRet, tt.mockErr) - - m := &Manager{client: mockClient} - - got, err := m.GetPPDs() - if tt.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.want, len(got)) - }) - } -} - -func TestManager_GetClasses(t *testing.T) { - tests := []struct { - name string - mockRet map[string]ipp.Attributes - mockErr error - want int - wantErr bool - }{ - { - name: "success", - mockRet: map[string]ipp.Attributes{ - "office": { - ipp.AttributePrinterName: []ipp.Attribute{{Value: "office"}}, - ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}}, - ipp.AttributeMemberNames: []ipp.Attribute{{Value: "printer1"}, {Value: "printer2"}}, - }, - }, - mockErr: nil, - want: 1, - wantErr: false, - }, - { - name: "error", - mockRet: nil, - mockErr: errors.New("test error"), - want: 0, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetClasses(mock.Anything).Return(tt.mockRet, tt.mockErr) - - m := &Manager{client: mockClient} - - got, err := m.GetClasses() - if tt.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.want, len(got)) - if len(got) > 0 { - assert.Equal(t, "office", got[0].Name) - assert.Equal(t, 2, len(got[0].Members)) - } - }) - } -} - -func TestManager_CreatePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location").Return(nil) - mockClient.EXPECT().ResumePrinter("newprinter").Return(nil) - mockClient.EXPECT().AcceptJobs("newprinter").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.CreatePrinter("newprinter", "usb://HP", "generic.ppd", true, "stop-printer", "info", "location")) -} - -func TestManager_DeletePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeletePrinter("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.DeletePrinter("printer1")) -} - -func TestManager_AcceptJobs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().AcceptJobs("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.AcceptJobs("printer1")) -} - -func TestManager_RejectJobs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().RejectJobs("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.RejectJobs("printer1")) -} - -func TestManager_SetPrinterShared(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.SetPrinterShared("printer1", true)) -} - -func TestManager_SetPrinterLocation(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.SetPrinterLocation("printer1", "Office")) -} - -func TestManager_SetPrinterInfo(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.SetPrinterInfo("printer1", "Main Printer")) -} - -func TestManager_MoveJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().MoveJob(1, "printer2").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - err := m.MoveJob(1, "printer2") - assert.NoError(t, err) -} - -func TestManager_PrintTestPage(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().PrintTestPage("printer1", mock.Anything, mock.Anything).Return(42, nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - jobID, err := m.PrintTestPage("printer1") - assert.NoError(t, err) - assert.Equal(t, 42, jobID) -} - -func TestManager_AddPrinterToClass(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.AddPrinterToClass("office", "printer1")) -} - -func TestManager_RemovePrinterFromClass(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.RemovePrinterFromClass("office", "printer1")) -} - -func TestManager_DeleteClass(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeleteClass("office").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.DeleteClass("office")) -} - -func TestManager_RestartJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().RestartJob(1).Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.RestartJob(1)) -} - -func TestManager_HoldJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - assert.NoError(t, m.HoldJob(1, "indefinite")) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/handlers.go deleted file mode 100644 index 57b45d5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/handlers.go +++ /dev/null @@ -1,487 +0,0 @@ -package cups - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -type CUPSEvent struct { - Type string `json:"type"` - Data CUPSState `json:"data"` -} - -type TestPageResult struct { - Success bool `json:"success"` - JobID int `json:"jobId"` - Message string `json:"message"` -} - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - switch req.Method { - case "cups.subscribe": - handleSubscribe(conn, req, manager) - case "cups.getPrinters": - handleGetPrinters(conn, req, manager) - case "cups.getJobs": - handleGetJobs(conn, req, manager) - case "cups.pausePrinter": - handlePausePrinter(conn, req, manager) - case "cups.resumePrinter": - handleResumePrinter(conn, req, manager) - case "cups.cancelJob": - handleCancelJob(conn, req, manager) - case "cups.purgeJobs": - handlePurgeJobs(conn, req, manager) - case "cups.getDevices": - handleGetDevices(conn, req, manager) - case "cups.getPPDs": - handleGetPPDs(conn, req, manager) - case "cups.getClasses": - handleGetClasses(conn, req, manager) - case "cups.createPrinter": - handleCreatePrinter(conn, req, manager) - case "cups.deletePrinter": - handleDeletePrinter(conn, req, manager) - case "cups.acceptJobs": - handleAcceptJobs(conn, req, manager) - case "cups.rejectJobs": - handleRejectJobs(conn, req, manager) - case "cups.setPrinterShared": - handleSetPrinterShared(conn, req, manager) - case "cups.setPrinterLocation": - handleSetPrinterLocation(conn, req, manager) - case "cups.setPrinterInfo": - handleSetPrinterInfo(conn, req, manager) - case "cups.moveJob": - handleMoveJob(conn, req, manager) - case "cups.printTestPage": - handlePrintTestPage(conn, req, manager) - case "cups.addPrinterToClass": - handleAddPrinterToClass(conn, req, manager) - case "cups.removePrinterFromClass": - handleRemovePrinterFromClass(conn, req, manager) - case "cups.deleteClass": - handleDeleteClass(conn, req, manager) - case "cups.restartJob": - handleRestartJob(conn, req, manager) - case "cups.holdJob": - handleHoldJob(conn, req, manager) - case "cups.testConnection": - handleTestConnection(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetPrinters(conn net.Conn, req models.Request, manager *Manager) { - printers, err := manager.GetPrinters() - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, printers) -} - -func handleGetJobs(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.String(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - jobs, err := manager.GetJobs(printerName, "not-completed") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, jobs) -} - -func handlePausePrinter(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.String(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.PausePrinter(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "paused"}) -} - -func handleResumePrinter(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.String(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.ResumePrinter(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "resumed"}) -} - -func handleCancelJob(conn net.Conn, req models.Request, manager *Manager) { - jobID, err := params.Int(req.Params, "jobID") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.CancelJob(jobID); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "job canceled"}) -} - -func handlePurgeJobs(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.String(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.PurgeJobs(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "jobs canceled"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := CUPSEvent{ - Type: "state_changed", - Data: initialState, - } - - if err := json.NewEncoder(conn).Encode(models.Response[CUPSEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := CUPSEvent{ - Type: "state_changed", - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[CUPSEvent]{ - Result: &event, - }); err != nil { - return - } - } -} - -func handleGetDevices(conn net.Conn, req models.Request, manager *Manager) { - devices, err := manager.GetDevices() - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, devices) -} - -func handleGetPPDs(conn net.Conn, req models.Request, manager *Manager) { - ppds, err := manager.GetPPDs() - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, ppds) -} - -func handleGetClasses(conn net.Conn, req models.Request, manager *Manager) { - classes, err := manager.GetClasses() - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, classes) -} - -func handleCreatePrinter(conn net.Conn, req models.Request, manager *Manager) { - name, err := params.StringNonEmpty(req.Params, "name") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - deviceURI, err := params.StringNonEmpty(req.Params, "deviceURI") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - ppd, err := params.StringNonEmpty(req.Params, "ppd") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - shared := params.BoolOpt(req.Params, "shared", false) - errorPolicy := params.StringOpt(req.Params, "errorPolicy", "") - information := params.StringOpt(req.Params, "information", "") - location := params.StringOpt(req.Params, "location", "") - - if err := manager.CreatePrinter(name, deviceURI, ppd, shared, errorPolicy, information, location); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "printer created"}) -} - -func handleDeletePrinter(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.DeletePrinter(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "printer deleted"}) -} - -func handleAcceptJobs(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.AcceptJobs(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "accepting jobs"}) -} - -func handleRejectJobs(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.RejectJobs(printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "rejecting jobs"}) -} - -func handleSetPrinterShared(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - shared, err := params.Bool(req.Params, "shared") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetPrinterShared(printerName, shared); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "sharing updated"}) -} - -func handleSetPrinterLocation(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - location, err := params.String(req.Params, "location") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetPrinterLocation(printerName, location); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "location updated"}) -} - -func handleSetPrinterInfo(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - info, err := params.String(req.Params, "info") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetPrinterInfo(printerName, info); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "info updated"}) -} - -func handleMoveJob(conn net.Conn, req models.Request, manager *Manager) { - jobID, err := params.Int(req.Params, "jobID") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - destPrinter, err := params.StringNonEmpty(req.Params, "destPrinter") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.MoveJob(jobID, destPrinter); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "job moved"}) -} - -func handlePrintTestPage(conn net.Conn, req models.Request, manager *Manager) { - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - jobID, err := manager.PrintTestPage(printerName) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, TestPageResult{Success: true, JobID: jobID, Message: "test page queued"}) -} - -func handleAddPrinterToClass(conn net.Conn, req models.Request, manager *Manager) { - className, err := params.StringNonEmpty(req.Params, "className") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.AddPrinterToClass(className, printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "printer added to class"}) -} - -func handleRemovePrinterFromClass(conn net.Conn, req models.Request, manager *Manager) { - className, err := params.StringNonEmpty(req.Params, "className") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - printerName, err := params.StringNonEmpty(req.Params, "printerName") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.RemovePrinterFromClass(className, printerName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "printer removed from class"}) -} - -func handleDeleteClass(conn net.Conn, req models.Request, manager *Manager) { - className, err := params.StringNonEmpty(req.Params, "className") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.DeleteClass(className); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "class deleted"}) -} - -func handleRestartJob(conn net.Conn, req models.Request, manager *Manager) { - jobID, err := params.Int(req.Params, "jobID") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.RestartJob(jobID); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "job restarted"}) -} - -func handleHoldJob(conn net.Conn, req models.Request, manager *Manager) { - jobID, err := params.Int(req.Params, "jobID") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - holdUntil := params.StringOpt(req.Params, "holdUntil", "indefinite") - - if err := manager.HoldJob(jobID, holdUntil); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "job held"}) -} - -func handleTestConnection(conn net.Conn, req models.Request, manager *Manager) { - host, err := params.StringNonEmpty(req.Params, "host") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - port := params.IntOpt(req.Params, "port", 631) - protocol := params.StringOpt(req.Params, "protocol", "ipp") - - result, err := manager.TestRemotePrinter(host, port, protocol) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, result) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/handlers_test.go deleted file mode 100644 index b73931e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/handlers_test.go +++ /dev/null @@ -1,711 +0,0 @@ -package cups - -import ( - "bytes" - "encoding/json" - "errors" - "net" - "testing" - "time" - - mocks_cups "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type mockConn struct { - *bytes.Buffer -} - -func (m *mockConn) Close() error { return nil } -func (m *mockConn) LocalAddr() net.Addr { return nil } -func (m *mockConn) RemoteAddr() net.Addr { return nil } -func (m *mockConn) SetDeadline(t time.Time) error { return nil } -func (m *mockConn) SetReadDeadline(t time.Time) error { return nil } -func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil } - -func TestHandleGetPrinters(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{ - "printer1": { - ipp.AttributePrinterName: []ipp.Attribute{{Value: "printer1"}}, - ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}}, - ipp.AttributePrinterUriSupported: []ipp.Attribute{{Value: "ipp://localhost/printers/printer1"}}, - }, - }, nil) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.getPrinters", - } - - handleGetPrinters(conn, req, m) - - var resp models.Response[[]Printer] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.Equal(t, 1, len(*resp.Result)) -} - -func TestHandleGetPrinters_Error(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(nil, errors.New("test error")) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.getPrinters", - } - - handleGetPrinters(conn, req, m) - - var resp models.Response[any] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.Nil(t, resp.Result) - assert.NotNil(t, resp.Error) -} - -func TestHandleGetJobs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetJobs("printer1", "", "not-completed", false, 0, 0, mock.Anything). - Return(map[int]ipp.Attributes{ - 1: { - ipp.AttributeJobID: []ipp.Attribute{{Value: 1}}, - ipp.AttributeJobName: []ipp.Attribute{{Value: "job1"}}, - ipp.AttributeJobState: []ipp.Attribute{{Value: 5}}, - }, - }, nil) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.getJobs", - Params: map[string]any{ - "printerName": "printer1", - }, - } - - handleGetJobs(conn, req, m) - - var resp models.Response[[]Job] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.Equal(t, 1, len(*resp.Result)) -} - -func TestHandleGetJobs_MissingParam(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.getJobs", - Params: map[string]any{}, - } - - handleGetJobs(conn, req, m) - - var resp models.Response[any] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.Nil(t, resp.Result) - assert.NotNil(t, resp.Error) -} - -func TestHandlePausePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().PausePrinter("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.pausePrinter", - Params: map[string]any{ - "printerName": "printer1", - }, - } - - handlePausePrinter(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleResumePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().ResumePrinter("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.resumePrinter", - Params: map[string]any{ - "printerName": "printer1", - }, - } - - handleResumePrinter(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleCancelJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelJob(1, false).Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.cancelJob", - Params: map[string]any{ - "jobID": float64(1), - }, - } - - handleCancelJob(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandlePurgeJobs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CancelAllJob("printer1", true).Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.purgeJobs", - Params: map[string]any{ - "printerName": "printer1", - }, - } - - handlePurgeJobs(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleRequest_UnknownMethod(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - client: mockClient, - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.unknownMethod", - } - - HandleRequest(conn, req, m) - - var resp models.Response[any] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.Nil(t, resp.Result) - assert.NotNil(t, resp.Error) -} - -func TestHandleGetDevices(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetDevices().Return(map[string]ipp.Attributes{ - "usb://HP/LaserJet": { - "device-class": []ipp.Attribute{{Value: "direct"}}, - "device-info": []ipp.Attribute{{Value: "HP LaserJet"}}, - }, - }, nil) - - m := &Manager{client: mockClient} - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ID: 1, Method: "cups.getDevices"} - handleGetDevices(conn, req, m) - - var resp models.Response[[]Device] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.Equal(t, 1, len(*resp.Result)) -} - -func TestHandleGetPPDs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetPPDs().Return(map[string]ipp.Attributes{ - "generic.ppd": { - "ppd-make-and-model": []ipp.Attribute{{Value: "Generic"}}, - }, - }, nil) - - m := &Manager{client: mockClient} - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ID: 1, Method: "cups.getPPDs"} - handleGetPPDs(conn, req, m) - - var resp models.Response[[]PPD] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.Equal(t, 1, len(*resp.Result)) -} - -func TestHandleGetClasses(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().GetClasses(mock.Anything).Return(map[string]ipp.Attributes{ - "office": { - ipp.AttributePrinterName: []ipp.Attribute{{Value: "office"}}, - ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}}, - }, - }, nil) - - m := &Manager{client: mockClient} - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ID: 1, Method: "cups.getClasses"} - handleGetClasses(conn, req, m) - - var resp models.Response[[]PrinterClass] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.Equal(t, 1, len(*resp.Result)) -} - -func TestHandleCreatePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().CreatePrinter("newprinter", "usb://HP", "generic.ppd", false, "", "", "").Return(nil) - mockClient.EXPECT().ResumePrinter("newprinter").Return(nil) - mockClient.EXPECT().AcceptJobs("newprinter").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.createPrinter", - Params: map[string]any{ - "name": "newprinter", - "deviceURI": "usb://HP", - "ppd": "generic.ppd", - }, - } - handleCreatePrinter(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleCreatePrinter_MissingParams(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - m := &Manager{client: mockClient} - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ID: 1, Method: "cups.createPrinter", Params: map[string]any{}} - handleCreatePrinter(conn, req, m) - - var resp models.Response[any] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.Nil(t, resp.Result) - assert.NotNil(t, resp.Error) -} - -func TestHandleDeletePrinter(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeletePrinter("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.deletePrinter", - Params: map[string]any{"printerName": "printer1"}, - } - handleDeletePrinter(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleAcceptJobs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().AcceptJobs("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.acceptJobs", - Params: map[string]any{"printerName": "printer1"}, - } - handleAcceptJobs(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleRejectJobs(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().RejectJobs("printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.rejectJobs", - Params: map[string]any{"printerName": "printer1"}, - } - handleRejectJobs(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleSetPrinterShared(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterIsShared("printer1", true).Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.setPrinterShared", - Params: map[string]any{"printerName": "printer1", "shared": true}, - } - handleSetPrinterShared(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleSetPrinterLocation(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterLocation("printer1", "Office").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.setPrinterLocation", - Params: map[string]any{"printerName": "printer1", "location": "Office"}, - } - handleSetPrinterLocation(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleSetPrinterInfo(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().SetPrinterInformation("printer1", "Main Printer").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.setPrinterInfo", - Params: map[string]any{"printerName": "printer1", "info": "Main Printer"}, - } - handleSetPrinterInfo(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleMoveJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().MoveJob(1, "printer2").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.moveJob", - Params: map[string]any{"jobID": float64(1), "destPrinter": "printer2"}, - } - handleMoveJob(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandlePrintTestPage(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().PrintTestPage("printer1", mock.Anything, mock.Anything).Return(42, nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.printTestPage", - Params: map[string]any{"printerName": "printer1"}, - } - handlePrintTestPage(conn, req, m) - - var resp models.Response[TestPageResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, 42, resp.Result.JobID) -} - -func TestHandleAddPrinterToClass(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().AddPrinterToClass("office", "printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.addPrinterToClass", - Params: map[string]any{"className": "office", "printerName": "printer1"}, - } - handleAddPrinterToClass(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleRemovePrinterFromClass(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeletePrinterFromClass("office", "printer1").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.removePrinterFromClass", - Params: map[string]any{"className": "office", "printerName": "printer1"}, - } - handleRemovePrinterFromClass(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleDeleteClass(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().DeleteClass("office").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.deleteClass", - Params: map[string]any{"className": "office"}, - } - handleDeleteClass(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleRestartJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().RestartJob(1).Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.restartJob", - Params: map[string]any{"jobID": float64(1)}, - } - handleRestartJob(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleHoldJob(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().HoldJobUntil(1, "indefinite").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.holdJob", - Params: map[string]any{"jobID": float64(1)}, - } - handleHoldJob(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} - -func TestHandleHoldJob_WithHoldUntil(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - mockClient.EXPECT().HoldJobUntil(1, "no-hold").Return(nil) - mockClient.EXPECT().GetPrinters(mock.Anything).Return(map[string]ipp.Attributes{}, nil) - - m := NewTestManager(mockClient, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.holdJob", - Params: map[string]any{"jobID": float64(1), "holdUntil": "no-hold"}, - } - handleHoldJob(conn, req, m) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/manager.go deleted file mode 100644 index e5b7e8c..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/manager.go +++ /dev/null @@ -1,390 +0,0 @@ -package cups - -import ( - "errors" - "fmt" - "os" - "strconv" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" -) - -func NewManager() (*Manager, error) { - host := os.Getenv("DMS_IPP_HOST") - if host == "" { - host = "localhost" - } - - portStr := os.Getenv("DMS_IPP_PORT") - port := 631 - if portStr != "" { - if p, err := strconv.Atoi(portStr); err == nil { - port = p - } - } - - username := os.Getenv("DMS_IPP_USERNAME") - password := os.Getenv("DMS_IPP_PASSWORD") - - client := ipp.NewCUPSClient(host, port, username, password, false) - baseURL := fmt.Sprintf("http://%s:%d", host, port) - - var pkHelper PkHelper - if isLocalCUPS(host) { - var err error - pkHelper, err = NewPkHelper() - if err != nil { - log.Warnf("[CUPS] Failed to initialize pkhelper: %v", err) - } - } - - m := &Manager{ - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - client: client, - pkHelper: pkHelper, - baseURL: baseURL, - stateMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - if err := m.updateState(); err != nil { - return nil, err - } - - if isLocalCUPS(host) { - m.subscription = NewDBusSubscriptionManager(client, baseURL) - log.Infof("[CUPS] Using D-Bus notifications for local CUPS") - } else { - m.subscription = NewSubscriptionManager(client, baseURL) - log.Infof("[CUPS] Using IPPGET notifications for remote CUPS") - } - - m.notifierWg.Add(1) - go m.notifier() - - return m, nil -} - -func isLocalCUPS(host string) bool { - switch host { - case "localhost", "127.0.0.1", "::1", "": - return true - } - return false -} - -func (m *Manager) eventHandler() { - defer m.eventWG.Done() - - if m.subscription == nil { - return - } - - for { - select { - case <-m.stopChan: - return - case event, ok := <-m.subscription.Events(): - if !ok { - return - } - log.Debugf("[CUPS] Received event: %s (printer: %s, job: %d)", - event.EventName, event.PrinterName, event.JobID) - - if err := m.updateState(); err != nil { - log.Warnf("[CUPS] Failed to update state after event: %v", err) - } else { - m.notifySubscribers() - } - } - } -} - -func (m *Manager) updateState() error { - printers, err := m.GetPrinters() - if err != nil { - if isNoPrintersError(err) { - m.stateMutex.Lock() - m.state.Printers = make(map[string]*Printer) - m.stateMutex.Unlock() - return nil - } - return err - } - - printerMap := make(map[string]*Printer, len(printers)) - for _, printer := range printers { - jobs, err := m.GetJobs(printer.Name, "not-completed") - if err != nil { - return err - } - - printer.Jobs = jobs - printerMap[printer.Name] = &printer - } - - m.stateMutex.Lock() - m.state.Printers = printerMap - m.stateMutex.Unlock() - - return nil -} - -func isNoPrintersError(err error) bool { - if err == nil { - return false - } - - var ippErr ipp.IPPError - if errors.As(err, &ippErr) { - return ippErr.Status == 1030 - } - - return false -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - - currentState := m.snapshotState() - - if m.lastNotifiedState != nil && !stateChanged(m.lastNotifiedState, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan CUPSState) bool { - select { - case ch <- currentState: - default: - } - return true - }) - - stateCopy := currentState - m.lastNotifiedState = &stateCopy - pending = false - } - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) RefreshState() { - if err := m.updateState(); err != nil { - log.Warnf("[CUPS] Failed to refresh state: %v", err) - return - } - m.notifySubscribers() -} - -func (m *Manager) GetState() CUPSState { - return m.snapshotState() -} - -func (m *Manager) snapshotState() CUPSState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - - s := CUPSState{ - Printers: make(map[string]*Printer, len(m.state.Printers)), - } - for name, printer := range m.state.Printers { - printerCopy := *printer - s.Printers[name] = &printerCopy - } - return s -} - -func (m *Manager) Subscribe(id string) chan CUPSState { - ch := make(chan CUPSState, 64) - - wasEmpty := true - m.subscribers.Range(func(key string, ch chan CUPSState) bool { - wasEmpty = false - return false - }) - - m.subscribers.Store(id, ch) - - if wasEmpty && m.subscription != nil { - if err := m.subscription.Start(); err != nil { - log.Warnf("[CUPS] Failed to start subscription manager: %v", err) - } else { - m.eventWG.Add(1) - go m.eventHandler() - } - } - - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } - - isEmpty := true - m.subscribers.Range(func(key string, ch chan CUPSState) bool { - isEmpty = false - return false - }) - - if isEmpty && m.subscription != nil { - m.subscription.Stop() - m.eventWG.Wait() - } -} - -func (m *Manager) Close() { - close(m.stopChan) - - if m.subscription != nil { - m.subscription.Stop() - } - - m.eventWG.Wait() - m.notifierWg.Wait() - - m.subscribers.Range(func(key string, ch chan CUPSState) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) -} - -func stateChanged(old, new *CUPSState) bool { - if len(old.Printers) != len(new.Printers) { - return true - } - for name, oldPrinter := range old.Printers { - newPrinter, exists := new.Printers[name] - if !exists { - return true - } - if oldPrinter.State != newPrinter.State || - oldPrinter.StateReason != newPrinter.StateReason || - oldPrinter.Accepting != newPrinter.Accepting || - len(oldPrinter.Jobs) != len(newPrinter.Jobs) { - return true - } - } - return false -} - -func parsePrinterState(attrs ipp.Attributes) string { - if stateAttr, ok := attrs[ipp.AttributePrinterState]; ok && len(stateAttr) > 0 { - if state, ok := stateAttr[0].Value.(int); ok { - switch state { - case 3: - return "idle" - case 4: - return "processing" - case 5: - return "stopped" - default: - return fmt.Sprintf("%d", state) - } - } - } - return "unknown" -} - -func parseJobState(attrs ipp.Attributes) string { - if stateAttr, ok := attrs[ipp.AttributeJobState]; ok && len(stateAttr) > 0 { - if state, ok := stateAttr[0].Value.(int); ok { - switch state { - case 3: - return "pending" - case 4: - return "pending-held" - case 5: - return "processing" - case 6: - return "processing-stopped" - case 7: - return "canceled" - case 8: - return "aborted" - case 9: - return "completed" - default: - return fmt.Sprintf("%d", state) - } - } - } - return "unknown" -} - -func getStringAttr(attrs ipp.Attributes, key string) string { - if attr, ok := attrs[key]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(string); ok { - return val - } - return fmt.Sprintf("%v", attr[0].Value) - } - return "" -} - -func getIntAttr(attrs ipp.Attributes, key string) int { - if attr, ok := attrs[key]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(int); ok { - return val - } - } - return 0 -} - -func getBoolAttr(attrs ipp.Attributes, key string) bool { - if attr, ok := attrs[key]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(bool); ok { - return val - } - } - return false -} - -func getStringSliceAttr(attrs ipp.Attributes, key string) []string { - attr, ok := attrs[key] - if !ok { - return nil - } - - result := make([]string, 0, len(attr)) - for _, a := range attr { - if val, ok := a.Value.(string); ok { - result = append(result, val) - } - } - return result -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/manager_test.go deleted file mode 100644 index 7f09e67..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/manager_test.go +++ /dev/null @@ -1,363 +0,0 @@ -package cups - -import ( - "testing" - - mocks_cups "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/cups" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" - "github.com/stretchr/testify/assert" -) - -func TestNewManager(t *testing.T) { - m := &Manager{ - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - client: nil, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - assert.NotNil(t, m) - assert.NotNil(t, m.state) -} - -func TestManager_GetState(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - state: &CUPSState{ - Printers: map[string]*Printer{ - "test-printer": { - Name: "test-printer", - State: "idle", - }, - }, - }, - client: mockClient, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - state := m.GetState() - assert.Equal(t, 1, len(state.Printers)) - assert.Equal(t, "test-printer", state.Printers["test-printer"].Name) -} - -func TestManager_Subscribe(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - client: mockClient, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - ch := m.Subscribe("test-client") - assert.NotNil(t, ch) - - count := 0 - m.subscribers.Range(func(key string, ch chan CUPSState) bool { - count++ - return true - }) - assert.Equal(t, 1, count) - - m.Unsubscribe("test-client") - count = 0 - m.subscribers.Range(func(key string, ch chan CUPSState) bool { - count++ - return true - }) - assert.Equal(t, 0, count) -} - -func TestManager_Close(t *testing.T) { - mockClient := mocks_cups.NewMockCUPSClientInterface(t) - - m := &Manager{ - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - client: mockClient, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - m.eventWG.Add(1) - go func() { - defer m.eventWG.Done() - <-m.stopChan - }() - - m.notifierWg.Add(1) - go func() { - defer m.notifierWg.Done() - <-m.stopChan - }() - - m.Close() - count := 0 - m.subscribers.Range(func(key string, ch chan CUPSState) bool { - count++ - return true - }) - assert.Equal(t, 0, count) -} - -func TestStateChanged(t *testing.T) { - tests := []struct { - name string - oldState *CUPSState - newState *CUPSState - want bool - }{ - { - name: "no change", - oldState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - newState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - want: false, - }, - { - name: "state changed", - oldState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - newState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "processing"}, - }, - }, - want: true, - }, - { - name: "printer added", - oldState: &CUPSState{ - Printers: map[string]*Printer{}, - }, - newState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - want: true, - }, - { - name: "printer removed", - oldState: &CUPSState{ - Printers: map[string]*Printer{ - "p1": {Name: "p1", State: "idle"}, - }, - }, - newState: &CUPSState{ - Printers: map[string]*Printer{}, - }, - want: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := stateChanged(tt.oldState, tt.newState) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestParsePrinterState(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - want string - }{ - { - name: "idle", - attrs: ipp.Attributes{ - ipp.AttributePrinterState: []ipp.Attribute{{Value: 3}}, - }, - want: "idle", - }, - { - name: "processing", - attrs: ipp.Attributes{ - ipp.AttributePrinterState: []ipp.Attribute{{Value: 4}}, - }, - want: "processing", - }, - { - name: "stopped", - attrs: ipp.Attributes{ - ipp.AttributePrinterState: []ipp.Attribute{{Value: 5}}, - }, - want: "stopped", - }, - { - name: "unknown", - attrs: ipp.Attributes{}, - want: "unknown", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := parsePrinterState(tt.attrs) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestParseJobState(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - want string - }{ - { - name: "pending", - attrs: ipp.Attributes{ - ipp.AttributeJobState: []ipp.Attribute{{Value: 3}}, - }, - want: "pending", - }, - { - name: "processing", - attrs: ipp.Attributes{ - ipp.AttributeJobState: []ipp.Attribute{{Value: 5}}, - }, - want: "processing", - }, - { - name: "completed", - attrs: ipp.Attributes{ - ipp.AttributeJobState: []ipp.Attribute{{Value: 9}}, - }, - want: "completed", - }, - { - name: "unknown", - attrs: ipp.Attributes{}, - want: "unknown", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := parseJobState(tt.attrs) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestGetStringAttr(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - key string - want string - }{ - { - name: "string value", - attrs: ipp.Attributes{ - "test-key": []ipp.Attribute{{Value: "test-value"}}, - }, - key: "test-key", - want: "test-value", - }, - { - name: "missing key", - attrs: ipp.Attributes{}, - key: "missing", - want: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := getStringAttr(tt.attrs, tt.key) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestGetIntAttr(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - key string - want int - }{ - { - name: "int value", - attrs: ipp.Attributes{ - "test-key": []ipp.Attribute{{Value: 42}}, - }, - key: "test-key", - want: 42, - }, - { - name: "missing key", - attrs: ipp.Attributes{}, - key: "missing", - want: 0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := getIntAttr(tt.attrs, tt.key) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestGetBoolAttr(t *testing.T) { - tests := []struct { - name string - attrs ipp.Attributes - key string - want bool - }{ - { - name: "true value", - attrs: ipp.Attributes{ - "test-key": []ipp.Attribute{{Value: true}}, - }, - key: "test-key", - want: true, - }, - { - name: "false value", - attrs: ipp.Attributes{ - "test-key": []ipp.Attribute{{Value: false}}, - }, - key: "test-key", - want: false, - }, - { - name: "missing key", - attrs: ipp.Attributes{}, - key: "missing", - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := getBoolAttr(tt.attrs, tt.key) - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/pkhelper.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/pkhelper.go deleted file mode 100644 index 81dd458..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/pkhelper.go +++ /dev/null @@ -1,184 +0,0 @@ -package cups - -import ( - "fmt" - "strings" - - "github.com/godbus/dbus/v5" -) - -const ( - pkHelperDest = "org.opensuse.CupsPkHelper.Mechanism" - pkHelperPath = "/" - pkHelperInterface = "org.opensuse.CupsPkHelper.Mechanism" -) - -type PkHelper interface { - DevicesGet(timeout, limit int, includeSchemes, excludeSchemes []string) ([]Device, error) - PrinterAdd(name, uri, ppd, info, location string) error - PrinterDelete(name string) error - PrinterSetEnabled(name string, enabled bool) error - PrinterSetAcceptJobs(name string, enabled bool, reason string) error - PrinterSetInfo(name, info string) error - PrinterSetLocation(name, location string) error - PrinterSetShared(name string, shared bool) error - ClassAddPrinter(className, printerName string) error - ClassDeletePrinter(className, printerName string) error - ClassDelete(className string) error - JobCancelPurge(jobID int, purge bool) error - JobRestart(jobID int) error - JobSetHoldUntil(jobID int, holdUntil string) error -} - -type DBusPkHelper struct { - conn *dbus.Conn - obj dbus.BusObject -} - -func NewPkHelper() (*DBusPkHelper, error) { - conn, err := dbus.SystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - return &DBusPkHelper{ - conn: conn, - obj: conn.Object(pkHelperDest, pkHelperPath), - }, nil -} - -func (p *DBusPkHelper) DevicesGet(timeout, limit int, includeSchemes, excludeSchemes []string) ([]Device, error) { - if includeSchemes == nil { - includeSchemes = []string{} - } - if excludeSchemes == nil { - excludeSchemes = []string{} - } - - var errStr string - var devicesMap map[string]string - - call := p.obj.Call(pkHelperInterface+".DevicesGet", 0, int32(timeout), int32(limit), includeSchemes, excludeSchemes) - if call.Err != nil { - return nil, call.Err - } - if err := call.Store(&errStr, &devicesMap); err != nil { - return nil, err - } - if errStr != "" { - return nil, fmt.Errorf("%s", errStr) - } - - return parseDevicesMap(devicesMap), nil -} - -func parseDevicesMap(devicesMap map[string]string) []Device { - devicesByIndex := make(map[string]*Device) - - for key, value := range devicesMap { - idx := strings.LastIndex(key, ":") - if idx == -1 { - continue - } - - attr := key[:idx] - index := key[idx+1:] - - dev, ok := devicesByIndex[index] - if !ok { - dev = &Device{} - devicesByIndex[index] = dev - } - - switch attr { - case "device-uri": - dev.URI = value - case "device-class": - dev.Class = value - case "device-info": - dev.Info = value - case "device-make-and-model": - dev.MakeModel = value - case "device-id": - dev.ID = value - case "device-location": - dev.Location = value - } - } - - devices := make([]Device, 0, len(devicesByIndex)) - for _, dev := range devicesByIndex { - if dev.URI != "" { - devices = append(devices, *dev) - } - } - return devices -} - -func (p *DBusPkHelper) PrinterAdd(name, uri, ppd, info, location string) error { - return p.callSimple("PrinterAdd", name, uri, ppd, info, location) -} - -func (p *DBusPkHelper) PrinterDelete(name string) error { - return p.callSimple("PrinterDelete", name) -} - -func (p *DBusPkHelper) PrinterSetEnabled(name string, enabled bool) error { - return p.callSimple("PrinterSetEnabled", name, enabled) -} - -func (p *DBusPkHelper) PrinterSetAcceptJobs(name string, enabled bool, reason string) error { - return p.callSimple("PrinterSetAcceptJobs", name, enabled, reason) -} - -func (p *DBusPkHelper) PrinterSetInfo(name, info string) error { - return p.callSimple("PrinterSetInfo", name, info) -} - -func (p *DBusPkHelper) PrinterSetLocation(name, location string) error { - return p.callSimple("PrinterSetLocation", name, location) -} - -func (p *DBusPkHelper) PrinterSetShared(name string, shared bool) error { - return p.callSimple("PrinterSetShared", name, shared) -} - -func (p *DBusPkHelper) ClassAddPrinter(className, printerName string) error { - return p.callSimple("ClassAddPrinter", className, printerName) -} - -func (p *DBusPkHelper) ClassDeletePrinter(className, printerName string) error { - return p.callSimple("ClassDeletePrinter", className, printerName) -} - -func (p *DBusPkHelper) ClassDelete(className string) error { - return p.callSimple("ClassDelete", className) -} - -func (p *DBusPkHelper) JobCancelPurge(jobID int, purge bool) error { - return p.callSimple("JobCancelPurge", int32(jobID), purge) -} - -func (p *DBusPkHelper) JobRestart(jobID int) error { - return p.callSimple("JobRestart", int32(jobID)) -} - -func (p *DBusPkHelper) JobSetHoldUntil(jobID int, holdUntil string) error { - return p.callSimple("JobSetHoldUntil", int32(jobID), holdUntil) -} - -func (p *DBusPkHelper) callSimple(method string, args ...any) error { - var errStr string - - call := p.obj.Call(pkHelperInterface+"."+method, 0, args...) - if call.Err != nil { - return call.Err - } - if err := call.Store(&errStr); err != nil { - return err - } - if errStr != "" { - return fmt.Errorf("%s", errStr) - } - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/pkhelper_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/pkhelper_test.go deleted file mode 100644 index 596f1d0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/pkhelper_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package cups - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestParseDevicesMap(t *testing.T) { - tests := []struct { - name string - input map[string]string - wantLen int - wantURIs []string - }{ - { - name: "empty", - input: map[string]string{}, - wantLen: 0, - wantURIs: nil, - }, - { - name: "single_device", - input: map[string]string{ - "device-uri:0": "usb://HP/LaserJet", - "device-class:0": "direct", - "device-info:0": "HP LaserJet", - "device-make-and-model:0": "HP LaserJet 1020", - "device-id:0": "MFG:HP;MDL:LaserJet", - }, - wantLen: 1, - wantURIs: []string{"usb://HP/LaserJet"}, - }, - { - name: "multiple_devices", - input: map[string]string{ - "device-uri:0": "usb://HP/LaserJet", - "device-class:0": "direct", - "device-info:0": "HP LaserJet", - "device-uri:1": "socket://192.168.1.100", - "device-class:1": "network", - "device-info:1": "Network Printer", - }, - wantLen: 2, - wantURIs: []string{"usb://HP/LaserJet", "socket://192.168.1.100"}, - }, - { - name: "malformed_key", - input: map[string]string{ - "no-colon-here": "value", - }, - wantLen: 0, - wantURIs: nil, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := parseDevicesMap(tt.input) - assert.Len(t, got, tt.wantLen) - - if tt.wantURIs != nil { - gotURIs := make(map[string]bool) - for _, d := range got { - gotURIs[d.URI] = true - } - for _, uri := range tt.wantURIs { - assert.True(t, gotURIs[uri], "expected URI %s not found", uri) - } - } - }) - } -} - -func TestParseDevicesMap_Attributes(t *testing.T) { - input := map[string]string{ - "device-uri:0": "usb://HP/LaserJet", - "device-class:0": "direct", - "device-info:0": "HP LaserJet", - "device-make-and-model:0": "HP LaserJet 1020", - "device-id:0": "MFG:HP;MDL:LaserJet", - "device-location:0": "USB Port", - } - - got := parseDevicesMap(input) - assert.Len(t, got, 1) - - dev := got[0] - assert.Equal(t, "usb://HP/LaserJet", dev.URI) - assert.Equal(t, "direct", dev.Class) - assert.Equal(t, "HP LaserJet", dev.Info) - assert.Equal(t, "HP LaserJet 1020", dev.MakeModel) - assert.Equal(t, "MFG:HP;MDL:LaserJet", dev.ID) - assert.Equal(t, "USB Port", dev.Location) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/subscription.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/subscription.go deleted file mode 100644 index 6b369b4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/subscription.go +++ /dev/null @@ -1,245 +0,0 @@ -package cups - -import ( - "fmt" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" -) - -type SubscriptionManager struct { - client CUPSClientInterface - subscriptionID int - sequenceNumber int - eventChan chan SubscriptionEvent - stopChan chan struct{} - wg sync.WaitGroup - baseURL string - running bool - mu sync.Mutex -} - -func NewSubscriptionManager(client CUPSClientInterface, baseURL string) *SubscriptionManager { - return &SubscriptionManager{ - client: client, - eventChan: make(chan SubscriptionEvent, 100), - stopChan: make(chan struct{}), - baseURL: baseURL, - } -} - -func (sm *SubscriptionManager) Start() error { - sm.mu.Lock() - if sm.running { - sm.mu.Unlock() - return fmt.Errorf("subscription manager already running") - } - sm.running = true - sm.mu.Unlock() - - subID, err := sm.createSubscription() - if err != nil { - sm.mu.Lock() - sm.running = false - sm.mu.Unlock() - return fmt.Errorf("failed to create subscription: %w", err) - } - - sm.subscriptionID = subID - log.Infof("[CUPS] Created IPP subscription with ID %d", subID) - - sm.wg.Add(1) - go sm.notificationLoop() - - return nil -} - -func (sm *SubscriptionManager) createSubscription() (int, error) { - req := ipp.NewRequest(ipp.OperationCreatePrinterSubscriptions, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - - // Subscription attributes go in SubscriptionAttributes (subscription-attributes-tag in IPP) - req.SubscriptionAttributes = map[string]any{ - "notify-events": []string{ - "printer-state-changed", - "printer-added", - "printer-deleted", - "job-created", - "job-completed", - "job-state-changed", - }, - "notify-pull-method": "ippget", - "notify-lease-duration": 0, - } - - // Send to root IPP endpoint - resp, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - return 0, fmt.Errorf("SendRequest failed: %w", err) - } - - // Check for IPP errors - if err := resp.CheckForErrors(); err != nil { - return 0, fmt.Errorf("IPP error: %w", err) - } - - // Subscription ID comes back in SubscriptionAttributes - if len(resp.SubscriptionAttributes) > 0 { - if idAttr, ok := resp.SubscriptionAttributes[0]["notify-subscription-id"]; ok && len(idAttr) > 0 { - if val, ok := idAttr[0].Value.(int); ok { - return val, nil - } - } - } - - return 0, fmt.Errorf("no subscription ID returned") -} - -func (sm *SubscriptionManager) notificationLoop() { - defer sm.wg.Done() - - backoff := 1 * time.Second - - for { - select { - case <-sm.stopChan: - return - default: - } - - gotAny, err := sm.fetchNotificationsWithWait() - if err != nil { - log.Warnf("[CUPS] Error fetching notifications: %v", err) - jitter := time.Duration(50+(time.Now().UnixNano()%200)) * time.Millisecond - sleepTime := backoff + jitter - if sleepTime > 30*time.Second { - sleepTime = 30 * time.Second - } - select { - case <-sm.stopChan: - return - case <-time.After(sleepTime): - } - if backoff < 30*time.Second { - backoff *= 2 - } - continue - } - - backoff = 1 * time.Second - - if gotAny { - continue - } - - select { - case <-sm.stopChan: - return - case <-time.After(2 * time.Second): - } - } -} - -func (sm *SubscriptionManager) fetchNotificationsWithWait() (bool, error) { - req := ipp.NewRequest(ipp.OperationGetNotifications, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - req.OperationAttributes["notify-subscription-ids"] = sm.subscriptionID - if sm.sequenceNumber > 0 { - req.OperationAttributes["notify-sequence-numbers"] = sm.sequenceNumber - } - - resp, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - return false, err - } - - gotAny := false - for _, eventGroup := range resp.SubscriptionAttributes { - if seqAttr, ok := eventGroup["notify-sequence-number"]; ok && len(seqAttr) > 0 { - if seqNum, ok := seqAttr[0].Value.(int); ok { - sm.sequenceNumber = seqNum + 1 - } - } - - event := sm.parseEvent(eventGroup) - gotAny = true - select { - case sm.eventChan <- event: - case <-sm.stopChan: - return gotAny, nil - default: - log.Warn("[CUPS] Event channel full, dropping event") - } - } - - return gotAny, nil -} - -func (sm *SubscriptionManager) parseEvent(attrs ipp.Attributes) SubscriptionEvent { - event := SubscriptionEvent{ - SubscribedAt: time.Now(), - } - - if attr, ok := attrs["notify-subscribed-event"]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(string); ok { - event.EventName = val - } - } - - if attr, ok := attrs["printer-name"]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(string); ok { - event.PrinterName = val - } - } - - if attr, ok := attrs["notify-job-id"]; ok && len(attr) > 0 { - if val, ok := attr[0].Value.(int); ok { - event.JobID = val - } - } - - return event -} - -func (sm *SubscriptionManager) Events() <-chan SubscriptionEvent { - return sm.eventChan -} - -func (sm *SubscriptionManager) Stop() { - sm.mu.Lock() - if !sm.running { - sm.mu.Unlock() - return - } - sm.running = false - sm.mu.Unlock() - - close(sm.stopChan) - sm.wg.Wait() - - if sm.subscriptionID != 0 { - sm.cancelSubscription() - sm.subscriptionID = 0 - sm.sequenceNumber = 0 - } - - sm.stopChan = make(chan struct{}) -} - -func (sm *SubscriptionManager) cancelSubscription() { - req := ipp.NewRequest(ipp.OperationCancelSubscription, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - req.OperationAttributes["notify-subscription-id"] = sm.subscriptionID - - _, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - log.Warnf("[CUPS] Failed to cancel subscription %d: %v", sm.subscriptionID, err) - } else { - log.Infof("[CUPS] Cancelled subscription %d", sm.subscriptionID) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/subscription_dbus.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/subscription_dbus.go deleted file mode 100644 index b205a6b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/subscription_dbus.go +++ /dev/null @@ -1,295 +0,0 @@ -package cups - -import ( - "fmt" - "strings" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" - "github.com/godbus/dbus/v5" -) - -type DBusSubscriptionManager struct { - client CUPSClientInterface - subscriptionID int - eventChan chan SubscriptionEvent - stopChan chan struct{} - wg sync.WaitGroup - baseURL string - running bool - mu sync.Mutex - conn *dbus.Conn -} - -func NewDBusSubscriptionManager(client CUPSClientInterface, baseURL string) *DBusSubscriptionManager { - return &DBusSubscriptionManager{ - client: client, - eventChan: make(chan SubscriptionEvent, 100), - stopChan: make(chan struct{}), - baseURL: baseURL, - } -} - -func (sm *DBusSubscriptionManager) Start() error { - sm.mu.Lock() - if sm.running { - sm.mu.Unlock() - return fmt.Errorf("subscription manager already running") - } - sm.running = true - sm.mu.Unlock() - - conn, err := dbus.ConnectSystemBus() - if err != nil { - sm.mu.Lock() - sm.running = false - sm.mu.Unlock() - return fmt.Errorf("connect to system bus: %w", err) - } - sm.conn = conn - - subID, err := sm.createDBusSubscription() - if err != nil { - sm.conn.Close() - sm.mu.Lock() - sm.running = false - sm.mu.Unlock() - return fmt.Errorf("failed to create D-Bus subscription: %w", err) - } - - sm.subscriptionID = subID - log.Infof("[CUPS] Created D-Bus subscription with ID %d", subID) - - if err := sm.conn.AddMatchSignal( - dbus.WithMatchInterface("org.cups.cupsd.Notifier"), - ); err != nil { - sm.cancelSubscription() - sm.conn.Close() - sm.mu.Lock() - sm.running = false - sm.mu.Unlock() - return fmt.Errorf("failed to add D-Bus match: %w", err) - } - - sm.wg.Add(1) - go sm.dbusListenerLoop() - - return nil -} - -func (sm *DBusSubscriptionManager) createDBusSubscription() (int, error) { - req := ipp.NewRequest(ipp.OperationCreatePrinterSubscriptions, 2) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - - req.SubscriptionAttributes = map[string]any{ - "notify-events": []string{ - "printer-state-changed", - "printer-added", - "printer-deleted", - "job-created", - "job-completed", - "job-state-changed", - }, - "notify-recipient-uri": "dbus:/", - "notify-lease-duration": 86400, - } - - resp, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - return 0, fmt.Errorf("SendRequest failed: %w", err) - } - - if err := resp.CheckForErrors(); err != nil { - return 0, fmt.Errorf("IPP error: %w", err) - } - - if len(resp.SubscriptionAttributes) > 0 { - if idAttr, ok := resp.SubscriptionAttributes[0]["notify-subscription-id"]; ok && len(idAttr) > 0 { - if val, ok := idAttr[0].Value.(int); ok { - return val, nil - } - } - } - - return 0, fmt.Errorf("no subscription ID returned") -} - -func (sm *DBusSubscriptionManager) dbusListenerLoop() { - defer sm.wg.Done() - - signalChan := make(chan *dbus.Signal, 10) - sm.conn.Signal(signalChan) - defer sm.conn.RemoveSignal(signalChan) - - for { - select { - case <-sm.stopChan: - return - case sig := <-signalChan: - if sig == nil { - continue - } - - event := sm.parseDBusSignal(sig) - if event.EventName == "" { - continue - } - - select { - case sm.eventChan <- event: - case <-sm.stopChan: - return - default: - log.Warn("[CUPS] Event channel full, dropping event") - } - } - } -} - -func (sm *DBusSubscriptionManager) parseDBusSignal(sig *dbus.Signal) SubscriptionEvent { - event := SubscriptionEvent{} - - switch sig.Name { - case "org.cups.cupsd.Notifier.JobStateChanged": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "job-state-changed" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - if printerURI, ok := sig.Body[1].(string); ok && event.PrinterName == "" { - if idx := strings.LastIndex(printerURI, "/"); idx != -1 { - event.PrinterName = printerURI[idx+1:] - } - } - if jobID, ok := sig.Body[3].(uint32); ok { - event.JobID = int(jobID) - } - } - - case "org.cups.cupsd.Notifier.JobCreated": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "job-created" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - if printerURI, ok := sig.Body[1].(string); ok && event.PrinterName == "" { - if idx := strings.LastIndex(printerURI, "/"); idx != -1 { - event.PrinterName = printerURI[idx+1:] - } - } - if jobID, ok := sig.Body[3].(uint32); ok { - event.JobID = int(jobID) - } - } - - case "org.cups.cupsd.Notifier.JobCompleted": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "job-completed" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - if printerURI, ok := sig.Body[1].(string); ok && event.PrinterName == "" { - if idx := strings.LastIndex(printerURI, "/"); idx != -1 { - event.PrinterName = printerURI[idx+1:] - } - } - if jobID, ok := sig.Body[3].(uint32); ok { - event.JobID = int(jobID) - } - } - - case "org.cups.cupsd.Notifier.PrinterStateChanged": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "printer-state-changed" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - if printerURI, ok := sig.Body[1].(string); ok && event.PrinterName == "" { - if idx := strings.LastIndex(printerURI, "/"); idx != -1 { - event.PrinterName = printerURI[idx+1:] - } - } - } - - case "org.cups.cupsd.Notifier.PrinterAdded": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "printer-added" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - } - - case "org.cups.cupsd.Notifier.PrinterDeleted": - if len(sig.Body) >= 6 { - if text, ok := sig.Body[0].(string); ok { - event.EventName = "printer-deleted" - parts := strings.Split(text, " ") - if len(parts) >= 2 { - event.PrinterName = parts[0] - } - } - } - } - - return event -} - -func (sm *DBusSubscriptionManager) Events() <-chan SubscriptionEvent { - return sm.eventChan -} - -func (sm *DBusSubscriptionManager) Stop() { - sm.mu.Lock() - if !sm.running { - sm.mu.Unlock() - return - } - sm.running = false - sm.mu.Unlock() - - close(sm.stopChan) - sm.wg.Wait() - - if sm.subscriptionID != 0 { - sm.cancelSubscription() - sm.subscriptionID = 0 - } - - if sm.conn != nil { - sm.conn.Close() - sm.conn = nil - } - - sm.stopChan = make(chan struct{}) -} - -func (sm *DBusSubscriptionManager) cancelSubscription() { - req := ipp.NewRequest(ipp.OperationCancelSubscription, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = fmt.Sprintf("%s/", sm.baseURL) - req.OperationAttributes[ipp.AttributeRequestingUserName] = "dms" - req.OperationAttributes["notify-subscription-id"] = sm.subscriptionID - - _, err := sm.client.SendRequest(fmt.Sprintf("%s/", sm.baseURL), req, nil) - if err != nil { - log.Warnf("[CUPS] Failed to cancel subscription %d: %v", sm.subscriptionID, err) - } else { - log.Infof("[CUPS] Cancelled subscription %d", sm.subscriptionID) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/test_connection.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/test_connection.go deleted file mode 100644 index d0203e9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/test_connection.go +++ /dev/null @@ -1,176 +0,0 @@ -package cups - -import ( - "errors" - "fmt" - "net" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" -) - -var validProtocols = map[string]bool{ - "ipp": true, - "ipps": true, - "lpd": true, - "socket": true, -} - -func validateTestConnectionParams(host string, port int, protocol string) error { - if host == "" { - return errors.New("host is required") - } - if strings.ContainsAny(host, " \t\n\r/\\") { - return errors.New("host contains invalid characters") - } - if port < 1 || port > 65535 { - return errors.New("port must be between 1 and 65535") - } - if protocol != "" && !validProtocols[protocol] { - return errors.New("protocol must be one of: ipp, ipps, lpd, socket") - } - return nil -} - -const probeTimeout = 10 * time.Second - -func probeRemotePrinter(host string, port int, useTLS bool) (*RemotePrinterInfo, error) { - addr := net.JoinHostPort(host, fmt.Sprintf("%d", port)) - - // Fast fail: TCP reachability check - conn, err := net.DialTimeout("tcp", addr, probeTimeout) - if err != nil { - return &RemotePrinterInfo{ - Reachable: false, - Error: fmt.Sprintf("cannot reach %s: %s", addr, err.Error()), - }, nil - } - conn.Close() - - // Create a temporary IPP client pointing at the remote host. - // The TCP dial above provides fast-fail for unreachable hosts. - // The IPP adapter's ResponseHeaderTimeout (90s) bounds stalling servers. - client := ipp.NewIPPClient(host, port, "", "", useTLS) - - // Try /ipp/print first (modern driverless printers), then / (legacy) - info, err := probeIPPEndpoint(client, host, port, useTLS, "/ipp/print") - if err != nil { - // If we got an auth error, the printer exists but requires credentials. - // Report it as reachable with the URI that triggered the auth challenge. - if isAuthError(err) { - proto := "ipp" - if useTLS { - proto = "ipps" - } - return &RemotePrinterInfo{ - Reachable: true, - URI: fmt.Sprintf("%s://%s:%d/ipp/print", proto, host, port), - Info: "authentication required", - }, nil - } - info, err = probeIPPEndpoint(client, host, port, useTLS, "/") - } - if err != nil { - if isAuthError(err) { - proto := "ipp" - if useTLS { - proto = "ipps" - } - return &RemotePrinterInfo{ - Reachable: true, - URI: fmt.Sprintf("%s://%s:%d/", proto, host, port), - Info: "authentication required", - }, nil - } - // TCP reachable but not an IPP printer - return &RemotePrinterInfo{ - Reachable: true, - Error: fmt.Sprintf("host is reachable but does not appear to be an IPP printer: %s", err.Error()), - }, nil - } - - return info, nil -} - -func probeIPPEndpoint(client *ipp.IPPClient, host string, port int, useTLS bool, resourcePath string) (*RemotePrinterInfo, error) { - proto := "ipp" - if useTLS { - proto = "ipps" - } - printerURI := fmt.Sprintf("%s://%s:%d%s", proto, host, port, resourcePath) - - httpProto := "http" - if useTLS { - httpProto = "https" - } - httpURL := fmt.Sprintf("%s://%s:%d%s", httpProto, host, port, resourcePath) - - req := ipp.NewRequest(ipp.OperationGetPrinterAttributes, 1) - req.OperationAttributes[ipp.AttributePrinterURI] = printerURI - req.OperationAttributes[ipp.AttributeRequestedAttributes] = []string{ - ipp.AttributePrinterName, - ipp.AttributePrinterMakeAndModel, - ipp.AttributePrinterState, - ipp.AttributePrinterInfo, - ipp.AttributePrinterUriSupported, - } - - resp, err := client.SendRequest(httpURL, req, nil) - if err != nil { - return nil, err - } - - if len(resp.PrinterAttributes) == 0 { - return nil, errors.New("no printer attributes returned") - } - - attrs := resp.PrinterAttributes[0] - - return &RemotePrinterInfo{ - Reachable: true, - MakeModel: getStringAttr(attrs, ipp.AttributePrinterMakeAndModel), - Name: getStringAttr(attrs, ipp.AttributePrinterName), - Info: getStringAttr(attrs, ipp.AttributePrinterInfo), - State: parsePrinterState(attrs), - URI: printerURI, - }, nil -} - -// TestRemotePrinter validates inputs and probes a remote printer via IPP. -// For lpd/socket protocols, only TCP reachability is tested. -func (m *Manager) TestRemotePrinter(host string, port int, protocol string) (*RemotePrinterInfo, error) { - if protocol == "" { - protocol = "ipp" - } - - if err := validateTestConnectionParams(host, port, protocol); err != nil { - return nil, err - } - - // For non-IPP protocols, only check TCP reachability - if protocol == "lpd" || protocol == "socket" { - addr := net.JoinHostPort(host, fmt.Sprintf("%d", port)) - conn, err := net.DialTimeout("tcp", addr, probeTimeout) - if err != nil { - return &RemotePrinterInfo{ - Reachable: false, - Error: fmt.Sprintf("cannot reach %s: %s", addr, err.Error()), - }, nil - } - conn.Close() - return &RemotePrinterInfo{ - Reachable: true, - URI: fmt.Sprintf("%s://%s:%d", protocol, host, port), - }, nil - } - - useTLS := protocol == "ipps" - - probeFn := m.probeRemoteFn - if probeFn == nil { - probeFn = probeRemotePrinter - } - - return probeFn(host, port, useTLS) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/test_connection_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/test_connection_test.go deleted file mode 100644 index b835028..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/test_connection_test.go +++ /dev/null @@ -1,397 +0,0 @@ -package cups - -import ( - "bytes" - "encoding/json" - "fmt" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" - "github.com/stretchr/testify/assert" -) - -func TestValidateTestConnectionParams(t *testing.T) { - tests := []struct { - name string - host string - port int - protocol string - wantErr string - }{ - { - name: "valid ipp", - host: "192.168.0.5", - port: 631, - protocol: "ipp", - wantErr: "", - }, - { - name: "valid ipps", - host: "printer.local", - port: 443, - protocol: "ipps", - wantErr: "", - }, - { - name: "valid lpd", - host: "10.0.0.1", - port: 515, - protocol: "lpd", - wantErr: "", - }, - { - name: "valid socket", - host: "10.0.0.1", - port: 9100, - protocol: "socket", - wantErr: "", - }, - { - name: "empty host", - host: "", - port: 631, - protocol: "ipp", - wantErr: "host is required", - }, - { - name: "port too low", - host: "192.168.0.5", - port: 0, - protocol: "ipp", - wantErr: "port must be between 1 and 65535", - }, - { - name: "port too high", - host: "192.168.0.5", - port: 70000, - protocol: "ipp", - wantErr: "port must be between 1 and 65535", - }, - { - name: "invalid protocol", - host: "192.168.0.5", - port: 631, - protocol: "ftp", - wantErr: "protocol must be one of: ipp, ipps, lpd, socket", - }, - { - name: "empty protocol treated as ipp", - host: "192.168.0.5", - port: 631, - protocol: "", - wantErr: "", - }, - { - name: "host with slash", - host: "192.168.0.5/admin", - port: 631, - protocol: "ipp", - wantErr: "host contains invalid characters", - }, - { - name: "host with space", - host: "192.168.0.5 ", - port: 631, - protocol: "ipp", - wantErr: "host contains invalid characters", - }, - { - name: "host with newline", - host: "192.168.0.5\n", - port: 631, - protocol: "ipp", - wantErr: "host contains invalid characters", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := validateTestConnectionParams(tt.host, tt.port, tt.protocol) - if tt.wantErr == "" { - assert.NoError(t, err) - } else { - assert.EqualError(t, err, tt.wantErr) - } - }) - } -} - -func TestManager_TestRemotePrinter_Validation(t *testing.T) { - m := NewTestManager(nil, nil) - - tests := []struct { - name string - host string - port int - protocol string - wantErr string - }{ - { - name: "empty host returns error", - host: "", - port: 631, - protocol: "ipp", - wantErr: "host is required", - }, - { - name: "invalid port returns error", - host: "192.168.0.5", - port: 0, - protocol: "ipp", - wantErr: "port must be between 1 and 65535", - }, - { - name: "invalid protocol returns error", - host: "192.168.0.5", - port: 631, - protocol: "ftp", - wantErr: "protocol must be one of: ipp, ipps, lpd, socket", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := m.TestRemotePrinter(tt.host, tt.port, tt.protocol) - assert.EqualError(t, err, tt.wantErr) - }) - } -} - -func TestManager_TestRemotePrinter_IPP(t *testing.T) { - tests := []struct { - name string - protocol string - probeRet *RemotePrinterInfo - probeErr error - wantTLS bool - wantReach bool - wantModel string - }{ - { - name: "successful ipp probe", - protocol: "ipp", - probeRet: &RemotePrinterInfo{ - Reachable: true, - MakeModel: "HP OfficeJet 8010", - Name: "OfficeJet", - State: "idle", - URI: "ipp://192.168.0.5:631/ipp/print", - }, - wantTLS: false, - wantReach: true, - wantModel: "HP OfficeJet 8010", - }, - { - name: "successful ipps probe", - protocol: "ipps", - probeRet: &RemotePrinterInfo{ - Reachable: true, - MakeModel: "HP OfficeJet 8010", - URI: "ipps://192.168.0.5:631/ipp/print", - }, - wantTLS: true, - wantReach: true, - wantModel: "HP OfficeJet 8010", - }, - { - name: "unreachable host", - protocol: "ipp", - probeRet: &RemotePrinterInfo{ - Reachable: false, - Error: "cannot reach 192.168.0.5:631: connection refused", - }, - wantReach: false, - }, - { - name: "empty protocol defaults to ipp", - protocol: "", - probeRet: &RemotePrinterInfo{ - Reachable: true, - MakeModel: "Test Printer", - }, - wantTLS: false, - wantReach: true, - wantModel: "Test Printer", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var capturedTLS bool - m := NewTestManager(nil, nil) - m.probeRemoteFn = func(host string, port int, useTLS bool) (*RemotePrinterInfo, error) { - capturedTLS = useTLS - return tt.probeRet, tt.probeErr - } - - result, err := m.TestRemotePrinter("192.168.0.5", 631, tt.protocol) - if tt.probeErr != nil { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.wantReach, result.Reachable) - assert.Equal(t, tt.wantModel, result.MakeModel) - assert.Equal(t, tt.wantTLS, capturedTLS) - }) - } -} - -func TestManager_TestRemotePrinter_AuthRequired(t *testing.T) { - m := NewTestManager(nil, nil) - m.probeRemoteFn = func(host string, port int, useTLS bool) (*RemotePrinterInfo, error) { - // Simulate what happens when the printer returns HTTP 401 - return probeRemotePrinterWithAuthError(host, port, useTLS) - } - - result, err := m.TestRemotePrinter("192.168.0.107", 631, "ipp") - assert.NoError(t, err) - assert.True(t, result.Reachable) - assert.Equal(t, "authentication required", result.Info) - assert.Contains(t, result.URI, "ipp://192.168.0.107:631") -} - -// probeRemotePrinterWithAuthError simulates a probe where the printer -// returns HTTP 401 on both endpoints. -func probeRemotePrinterWithAuthError(host string, port int, useTLS bool) (*RemotePrinterInfo, error) { - // This simulates what probeRemotePrinter does when both endpoints - // return auth errors. We test the auth detection logic directly. - err := ipp.HTTPError{Code: 401} - if isAuthError(err) { - proto := "ipp" - if useTLS { - proto = "ipps" - } - return &RemotePrinterInfo{ - Reachable: true, - URI: fmt.Sprintf("%s://%s:%d/ipp/print", proto, host, port), - Info: "authentication required", - }, nil - } - return nil, err -} - -func TestManager_TestRemotePrinter_NonIPPProtocol(t *testing.T) { - m := NewTestManager(nil, nil) - probeCalled := false - m.probeRemoteFn = func(host string, port int, useTLS bool) (*RemotePrinterInfo, error) { - probeCalled = true - return nil, nil - } - - // These will fail at TCP dial (no real server), but the important - // thing is that probeRemoteFn is NOT called for lpd/socket. - m.TestRemotePrinter("192.168.0.5", 9100, "socket") - assert.False(t, probeCalled, "probe function should not be called for socket protocol") - - m.TestRemotePrinter("192.168.0.5", 515, "lpd") - assert.False(t, probeCalled, "probe function should not be called for lpd protocol") -} - -func TestHandleTestConnection_Success(t *testing.T) { - m := NewTestManager(nil, nil) - m.probeRemoteFn = func(host string, port int, useTLS bool) (*RemotePrinterInfo, error) { - return &RemotePrinterInfo{ - Reachable: true, - MakeModel: "HP OfficeJet 8010", - Name: "OfficeJet", - State: "idle", - URI: "ipp://192.168.0.5:631/ipp/print", - }, nil - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.testConnection", - Params: map[string]any{ - "host": "192.168.0.5", - }, - } - - handleTestConnection(conn, req, m) - - var resp models.Response[RemotePrinterInfo] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Reachable) - assert.Equal(t, "HP OfficeJet 8010", resp.Result.MakeModel) -} - -func TestHandleTestConnection_MissingHost(t *testing.T) { - m := NewTestManager(nil, nil) - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.testConnection", - Params: map[string]any{}, - } - - handleTestConnection(conn, req, m) - - var resp models.Response[any] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.Nil(t, resp.Result) - assert.NotNil(t, resp.Error) -} - -func TestHandleTestConnection_CustomPortAndProtocol(t *testing.T) { - m := NewTestManager(nil, nil) - m.probeRemoteFn = func(host string, port int, useTLS bool) (*RemotePrinterInfo, error) { - assert.Equal(t, 9631, port) - assert.True(t, useTLS) - return &RemotePrinterInfo{Reachable: true, URI: "ipps://192.168.0.5:9631/ipp/print"}, nil - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.testConnection", - Params: map[string]any{ - "host": "192.168.0.5", - "port": float64(9631), - "protocol": "ipps", - }, - } - - handleTestConnection(conn, req, m) - - var resp models.Response[RemotePrinterInfo] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Reachable) -} - -func TestHandleRequest_TestConnection(t *testing.T) { - m := NewTestManager(nil, nil) - m.probeRemoteFn = func(host string, port int, useTLS bool) (*RemotePrinterInfo, error) { - return &RemotePrinterInfo{Reachable: true}, nil - } - - buf := &bytes.Buffer{} - conn := &mockConn{Buffer: buf} - - req := models.Request{ - ID: 1, - Method: "cups.testConnection", - Params: map[string]any{"host": "192.168.0.5"}, - } - - HandleRequest(conn, req, m) - - var resp models.Response[RemotePrinterInfo] - err := json.NewDecoder(buf).Decode(&resp) - assert.NoError(t, err) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Reachable) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/testing.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/testing.go deleted file mode 100644 index 4820e34..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/testing.go +++ /dev/null @@ -1,13 +0,0 @@ -package cups - -func NewTestManager(client CUPSClientInterface, pkHelper PkHelper) *Manager { - return &Manager{ - client: client, - pkHelper: pkHelper, - state: &CUPSState{ - Printers: make(map[string]*Printer), - }, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/types.go deleted file mode 100644 index 52e9d2c..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/cups/types.go +++ /dev/null @@ -1,132 +0,0 @@ -package cups - -import ( - "io" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/ipp" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type CUPSState struct { - Printers map[string]*Printer `json:"printers"` -} - -type Printer struct { - Name string `json:"name"` - URI string `json:"uri"` - State string `json:"state"` - StateReason string `json:"stateReason"` - Location string `json:"location"` - Info string `json:"info"` - MakeModel string `json:"makeModel"` - Accepting bool `json:"accepting"` - Jobs []Job `json:"jobs"` -} - -type Job struct { - ID int `json:"id"` - Name string `json:"name"` - State string `json:"state"` - Printer string `json:"printer"` - User string `json:"user"` - Size int `json:"size"` - TimeCreated time.Time `json:"timeCreated"` -} - -type Device struct { - URI string `json:"uri"` - Class string `json:"class"` - Info string `json:"info"` - MakeModel string `json:"makeModel"` - ID string `json:"id"` - Location string `json:"location"` - IP string `json:"ip,omitempty"` -} - -type PPD struct { - Name string `json:"name"` - NaturalLanguage string `json:"naturalLanguage"` - MakeModel string `json:"makeModel"` - DeviceID string `json:"deviceId"` - Product string `json:"product"` - PSVersion string `json:"psVersion"` - Type string `json:"type"` -} - -type RemotePrinterInfo struct { - Reachable bool `json:"reachable"` - MakeModel string `json:"makeModel"` - Name string `json:"name"` - Info string `json:"info"` - State string `json:"state"` - URI string `json:"uri"` - Error string `json:"error,omitempty"` -} - -type PrinterClass struct { - Name string `json:"name"` - URI string `json:"uri"` - State string `json:"state"` - Members []string `json:"members"` - Location string `json:"location"` - Info string `json:"info"` -} - -type Manager struct { - state *CUPSState - client CUPSClientInterface - pkHelper PkHelper - subscription SubscriptionManagerInterface - stateMutex sync.RWMutex - subscribers syncmap.Map[string, chan CUPSState] - stopChan chan struct{} - eventWG sync.WaitGroup - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotifiedState *CUPSState - baseURL string - probeRemoteFn func(host string, port int, useTLS bool) (*RemotePrinterInfo, error) -} - -type SubscriptionManagerInterface interface { - Start() error - Stop() - Events() <-chan SubscriptionEvent -} - -type CUPSClientInterface interface { - GetPrinters(attributes []string) (map[string]ipp.Attributes, error) - GetJobs(printer, class string, whichJobs string, myJobs bool, firstJobId, limit int, attributes []string) (map[int]ipp.Attributes, error) - CancelJob(jobID int, purge bool) error - PausePrinter(printer string) error - ResumePrinter(printer string) error - CancelAllJob(printer string, purge bool) error - SendRequest(url string, req *ipp.Request, additionalResponseData io.Writer) (*ipp.Response, error) - - GetDevices() (map[string]ipp.Attributes, error) - GetPPDs() (map[string]ipp.Attributes, error) - GetClasses(attributes []string) (map[string]ipp.Attributes, error) - CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy, information, location string) error - DeletePrinter(printer string) error - AcceptJobs(printer string) error - RejectJobs(printer string) error - SetPrinterIsShared(printer string, shared bool) error - SetPrinterLocation(printer, location string) error - SetPrinterInformation(printer, information string) error - MoveJob(jobID int, destPrinter string) error - PrintTestPage(printer string, testPageData io.Reader, size int) (int, error) - AddPrinterToClass(class, printer string) error - DeletePrinterFromClass(class, printer string) error - DeleteClass(class string) error - RestartJob(jobID int) error - HoldJobUntil(jobID int, holdUntil string) error -} - -type SubscriptionEvent struct { - EventName string - PrinterName string - JobID int - SubscribedAt time.Time -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/handlers.go deleted file mode 100644 index e625d79..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/handlers.go +++ /dev/null @@ -1,237 +0,0 @@ -package dbus - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -type objectParams struct { - bus string - dest string - path string - iface string -} - -func extractObjectParams(p map[string]any, requirePath bool) (objectParams, error) { - bus, err := params.String(p, "bus") - if err != nil { - return objectParams{}, err - } - dest, err := params.String(p, "dest") - if err != nil { - return objectParams{}, err - } - - var path string - if requirePath { - path, err = params.String(p, "path") - if err != nil { - return objectParams{}, err - } - } else { - path = params.StringOpt(p, "path", "/") - } - - iface, err := params.String(p, "interface") - if err != nil { - return objectParams{}, err - } - - return objectParams{bus: bus, dest: dest, path: path, iface: iface}, nil -} - -func HandleRequest(conn net.Conn, req models.Request, m *Manager, clientID string) { - switch req.Method { - case "dbus.call": - handleCall(conn, req, m) - case "dbus.getProperty": - handleGetProperty(conn, req, m) - case "dbus.setProperty": - handleSetProperty(conn, req, m) - case "dbus.getAllProperties": - handleGetAllProperties(conn, req, m) - case "dbus.introspect": - handleIntrospect(conn, req, m) - case "dbus.listNames": - handleListNames(conn, req, m) - case "dbus.subscribe": - handleSubscribe(conn, req, m, clientID) - case "dbus.unsubscribe": - handleUnsubscribe(conn, req, m) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleCall(conn net.Conn, req models.Request, m *Manager) { - op, err := extractObjectParams(req.Params, true) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - method, err := params.String(req.Params, "method") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - var args []any - if argsRaw, ok := params.Any(req.Params, "args"); ok { - if argsSlice, ok := argsRaw.([]any); ok { - args = argsSlice - } - } - - result, err := m.Call(op.bus, op.dest, op.path, op.iface, method, args) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, result) -} - -func handleGetProperty(conn net.Conn, req models.Request, m *Manager) { - op, err := extractObjectParams(req.Params, true) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - property, err := params.String(req.Params, "property") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - result, err := m.GetProperty(op.bus, op.dest, op.path, op.iface, property) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, result) -} - -func handleSetProperty(conn net.Conn, req models.Request, m *Manager) { - op, err := extractObjectParams(req.Params, true) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - property, err := params.String(req.Params, "property") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - value, ok := params.Any(req.Params, "value") - if !ok { - models.RespondError(conn, req.ID, "missing 'value' parameter") - return - } - - if err := m.SetProperty(op.bus, op.dest, op.path, op.iface, property, value); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true}) -} - -func handleGetAllProperties(conn net.Conn, req models.Request, m *Manager) { - op, err := extractObjectParams(req.Params, true) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - result, err := m.GetAllProperties(op.bus, op.dest, op.path, op.iface) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, result) -} - -func handleIntrospect(conn net.Conn, req models.Request, m *Manager) { - bus, err := params.String(req.Params, "bus") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - dest, err := params.String(req.Params, "dest") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - path := params.StringOpt(req.Params, "path", "/") - - result, err := m.Introspect(bus, dest, path) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, result) -} - -func handleListNames(conn net.Conn, req models.Request, m *Manager) { - bus, err := params.String(req.Params, "bus") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - result, err := m.ListNames(bus) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, result) -} - -func handleSubscribe(conn net.Conn, req models.Request, m *Manager, clientID string) { - bus, err := params.String(req.Params, "bus") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - sender := params.StringOpt(req.Params, "sender", "") - path := params.StringOpt(req.Params, "path", "") - iface := params.StringOpt(req.Params, "interface", "") - member := params.StringOpt(req.Params, "member", "") - - result, err := m.Subscribe(clientID, bus, sender, path, iface, member) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, result) -} - -func handleUnsubscribe(conn net.Conn, req models.Request, m *Manager) { - subID, err := params.String(req.Params, "subscriptionId") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := m.Unsubscribe(subID); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true}) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/manager.go deleted file mode 100644 index 86da6bd..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/manager.go +++ /dev/null @@ -1,392 +0,0 @@ -package dbus - -import ( - "crypto/rand" - "encoding/hex" - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil" - "github.com/godbus/dbus/v5" -) - -func NewManager() (*Manager, error) { - systemConn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - sessionConn, err := dbus.ConnectSessionBus() - if err != nil { - systemConn.Close() - return nil, fmt.Errorf("failed to connect to session bus: %w", err) - } - - m := &Manager{ - systemConn: systemConn, - sessionConn: sessionConn, - } - - go m.processSystemSignals() - go m.processSessionSignals() - - return m, nil -} - -func (m *Manager) getConn(bus string) (*dbus.Conn, error) { - switch bus { - case "system": - if m.systemConn == nil { - return nil, fmt.Errorf("system bus not connected") - } - return m.systemConn, nil - case "session": - if m.sessionConn == nil { - return nil, fmt.Errorf("session bus not connected") - } - return m.sessionConn, nil - default: - return nil, fmt.Errorf("invalid bus: %s (must be 'system' or 'session')", bus) - } -} - -func (m *Manager) Call(bus, dest, path, iface, method string, args []any) (*CallResult, error) { - conn, err := m.getConn(bus) - if err != nil { - return nil, err - } - - obj := conn.Object(dest, dbus.ObjectPath(path)) - fullMethod := iface + "." + method - - convertedArgs := convertArgs(args) - call := obj.Call(fullMethod, 0, convertedArgs...) - if call.Err != nil { - return nil, fmt.Errorf("dbus call failed: %w", call.Err) - } - - return &CallResult{Values: dbusutil.NormalizeSlice(call.Body)}, nil -} - -func convertArgs(args []any) []any { - result := make([]any, len(args)) - for i, arg := range args { - result[i] = convertArg(arg) - } - return result -} - -func convertArg(arg any) any { - switch v := arg.(type) { - case float64: - if v == float64(uint32(v)) && v >= 0 && v <= float64(^uint32(0)) { - return uint32(v) - } - if v == float64(int32(v)) { - return int32(v) - } - return v - case []any: - return convertArgs(v) - case map[string]any: - result := make(map[string]any) - for k, val := range v { - result[k] = convertArg(val) - } - return result - default: - return arg - } -} - -func (m *Manager) GetProperty(bus, dest, path, iface, property string) (*PropertyResult, error) { - conn, err := m.getConn(bus) - if err != nil { - return nil, err - } - - obj := conn.Object(dest, dbus.ObjectPath(path)) - - var variant dbus.Variant - err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, iface, property).Store(&variant) - if err != nil { - return nil, fmt.Errorf("failed to get property: %w", err) - } - - return &PropertyResult{Value: dbusutil.Normalize(variant.Value())}, nil -} - -func (m *Manager) SetProperty(bus, dest, path, iface, property string, value any) error { - conn, err := m.getConn(bus) - if err != nil { - return err - } - - obj := conn.Object(dest, dbus.ObjectPath(path)) - - call := obj.Call("org.freedesktop.DBus.Properties.Set", 0, iface, property, dbus.MakeVariant(value)) - if call.Err != nil { - return fmt.Errorf("failed to set property: %w", call.Err) - } - - return nil -} - -func (m *Manager) GetAllProperties(bus, dest, path, iface string) (map[string]any, error) { - conn, err := m.getConn(bus) - if err != nil { - return nil, err - } - - obj := conn.Object(dest, dbus.ObjectPath(path)) - - var props map[string]dbus.Variant - err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, iface).Store(&props) - if err != nil { - return nil, fmt.Errorf("failed to get properties: %w", err) - } - - result := make(map[string]any) - for k, v := range props { - result[k] = dbusutil.Normalize(v.Value()) - } - - return result, nil -} - -func (m *Manager) Introspect(bus, dest, path string) (*IntrospectResult, error) { - conn, err := m.getConn(bus) - if err != nil { - return nil, err - } - - obj := conn.Object(dest, dbus.ObjectPath(path)) - - var xml string - err = obj.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&xml) - if err != nil { - return nil, fmt.Errorf("failed to introspect: %w", err) - } - - return &IntrospectResult{XML: xml}, nil -} - -func (m *Manager) ListNames(bus string) (*ListNamesResult, error) { - conn, err := m.getConn(bus) - if err != nil { - return nil, err - } - - var names []string - err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&names) - if err != nil { - return nil, fmt.Errorf("failed to list names: %w", err) - } - - return &ListNamesResult{Names: names}, nil -} - -func (m *Manager) Subscribe(clientID, bus, sender, path, iface, member string) (*SubscribeResult, error) { - conn, err := m.getConn(bus) - if err != nil { - return nil, err - } - - subID := generateSubscriptionID() - - parts := []string{"type='signal'"} - if sender != "" { - parts = append(parts, fmt.Sprintf("sender='%s'", sender)) - } - if path != "" { - parts = append(parts, fmt.Sprintf("path='%s'", path)) - } - if iface != "" { - parts = append(parts, fmt.Sprintf("interface='%s'", iface)) - } - if member != "" { - parts = append(parts, fmt.Sprintf("member='%s'", member)) - } - matchRule := strings.Join(parts, ",") - - call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, matchRule) - if call.Err != nil { - return nil, fmt.Errorf("failed to add match rule: %w", call.Err) - } - - sub := &signalSubscription{ - Bus: bus, - Sender: sender, - Path: path, - Interface: iface, - Member: member, - ClientID: clientID, - } - m.subscriptions.Store(subID, sub) - - log.Debugf("dbus: subscribed %s to %s", subID, matchRule) - - return &SubscribeResult{SubscriptionID: subID}, nil -} - -func (m *Manager) Unsubscribe(subID string) error { - sub, ok := m.subscriptions.LoadAndDelete(subID) - if !ok { - return fmt.Errorf("subscription not found: %s", subID) - } - - conn, err := m.getConn(sub.Bus) - if err != nil { - return err - } - - parts := []string{"type='signal'"} - if sub.Sender != "" { - parts = append(parts, fmt.Sprintf("sender='%s'", sub.Sender)) - } - if sub.Path != "" { - parts = append(parts, fmt.Sprintf("path='%s'", sub.Path)) - } - if sub.Interface != "" { - parts = append(parts, fmt.Sprintf("interface='%s'", sub.Interface)) - } - if sub.Member != "" { - parts = append(parts, fmt.Sprintf("member='%s'", sub.Member)) - } - matchRule := strings.Join(parts, ",") - - call := conn.BusObject().Call("org.freedesktop.DBus.RemoveMatch", 0, matchRule) - if call.Err != nil { - log.Warnf("dbus: failed to remove match rule: %v", call.Err) - } - - log.Debugf("dbus: unsubscribed %s", subID) - - return nil -} - -func (m *Manager) UnsubscribeClient(clientID string) { - var toDelete []string - m.subscriptions.Range(func(subID string, sub *signalSubscription) bool { - if sub.ClientID == clientID { - toDelete = append(toDelete, subID) - } - return true - }) - - for _, subID := range toDelete { - _ = m.Unsubscribe(subID) - } -} - -func (m *Manager) SubscribeSignals(clientID string) chan SignalEvent { - ch := make(chan SignalEvent, 64) - existing, loaded := m.signalSubscribers.LoadOrStore(clientID, ch) - if loaded { - return existing - } - return ch -} - -func (m *Manager) UnsubscribeSignals(clientID string) { - if ch, ok := m.signalSubscribers.LoadAndDelete(clientID); ok { - close(ch) - } - m.UnsubscribeClient(clientID) -} - -func (m *Manager) processSystemSignals() { - if m.systemConn == nil { - return - } - ch := make(chan *dbus.Signal, 256) - m.systemConn.Signal(ch) - - for sig := range ch { - m.dispatchSignal("system", sig) - } -} - -func (m *Manager) processSessionSignals() { - if m.sessionConn == nil { - return - } - ch := make(chan *dbus.Signal, 256) - m.sessionConn.Signal(ch) - - for sig := range ch { - m.dispatchSignal("session", sig) - } -} - -func (m *Manager) dispatchSignal(bus string, sig *dbus.Signal) { - path := string(sig.Path) - iface := "" - member := sig.Name - - if idx := strings.LastIndex(sig.Name, "."); idx != -1 { - iface = sig.Name[:idx] - member = sig.Name[idx+1:] - } - - m.subscriptions.Range(func(subID string, sub *signalSubscription) bool { - if sub.Bus != bus { - return true - } - if sub.Path != "" && sub.Path != path && !strings.HasPrefix(path, sub.Path) { - return true - } - if sub.Interface != "" && sub.Interface != iface { - return true - } - if sub.Member != "" && sub.Member != member { - return true - } - - event := SignalEvent{ - SubscriptionID: subID, - Sender: sig.Sender, - Path: path, - Interface: iface, - Member: member, - Body: dbusutil.NormalizeSlice(sig.Body), - } - - ch, ok := m.signalSubscribers.Load(sub.ClientID) - if !ok { - return true - } - - select { - case ch <- event: - default: - log.Warnf("dbus: channel full for %s, dropping signal", subID) - } - - return true - }) -} - -func (m *Manager) Close() { - m.signalSubscribers.Range(func(clientID string, ch chan SignalEvent) bool { - close(ch) - m.signalSubscribers.Delete(clientID) - return true - }) - - if m.systemConn != nil { - m.systemConn.Close() - } - if m.sessionConn != nil { - m.sessionConn.Close() - } -} - -func generateSubscriptionID() string { - b := make([]byte, 8) - if _, err := rand.Read(b); err != nil { - log.Warnf("dbus: failed to generate random subscription ID: %v", err) - } - return hex.EncodeToString(b) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/types.go deleted file mode 100644 index 55c45bf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dbus/types.go +++ /dev/null @@ -1,52 +0,0 @@ -package dbus - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/godbus/dbus/v5" -) - -type Manager struct { - systemConn *dbus.Conn - sessionConn *dbus.Conn - - subscriptions syncmap.Map[string, *signalSubscription] - signalSubscribers syncmap.Map[string, chan SignalEvent] -} - -type signalSubscription struct { - Bus string - Sender string - Path string - Interface string - Member string - ClientID string -} - -type SignalEvent struct { - SubscriptionID string `json:"subscriptionId"` - Sender string `json:"sender"` - Path string `json:"path"` - Interface string `json:"interface"` - Member string `json:"member"` - Body []any `json:"body"` -} - -type CallResult struct { - Values []any `json:"values"` -} - -type PropertyResult struct { - Value any `json:"value"` -} - -type IntrospectResult struct { - XML string `json:"xml"` -} - -type ListNamesResult struct { - Names []string `json:"names"` -} - -type SubscribeResult struct { - SubscriptionID string `json:"subscriptionId"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/handlers.go deleted file mode 100644 index e83cc84..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/handlers.go +++ /dev/null @@ -1,138 +0,0 @@ -package dwl - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "dwl manager not initialized") - return - } - - switch req.Method { - case "dwl.getState": - handleGetState(conn, req, manager) - case "dwl.setTags": - handleSetTags(conn, req, manager) - case "dwl.setClientTags": - handleSetClientTags(conn, req, manager) - case "dwl.setLayout": - handleSetLayout(conn, req, manager) - case "dwl.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleSetTags(conn net.Conn, req models.Request, manager *Manager) { - output, ok := models.Get[string](req, "output") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - tagmask, ok := models.Get[float64](req, "tagmask") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'tagmask' parameter") - return - } - - toggleTagset, ok := models.Get[float64](req, "toggleTagset") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'toggleTagset' parameter") - return - } - - if err := manager.SetTags(output, uint32(tagmask), uint32(toggleTagset)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "tags set"}) -} - -func handleSetClientTags(conn net.Conn, req models.Request, manager *Manager) { - output, ok := models.Get[string](req, "output") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - andTags, ok := models.Get[float64](req, "andTags") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'andTags' parameter") - return - } - - xorTags, ok := models.Get[float64](req, "xorTags") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'xorTags' parameter") - return - } - - if err := manager.SetClientTags(output, uint32(andTags), uint32(xorTags)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "client tags set"}) -} - -func handleSetLayout(conn net.Conn, req models.Request, manager *Manager) { - output, ok := models.Get[string](req, "output") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'output' parameter") - return - } - - index, ok := models.Get[float64](req, "index") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'index' parameter") - return - } - - if err := manager.SetLayout(output, uint32(index)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "layout set"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager.go deleted file mode 100644 index 0d21afa..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager.go +++ /dev/null @@ -1,522 +0,0 @@ -package dwl - -import ( - "fmt" - "time" - - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/dwl_ipc" -) - -func NewManager(display wlclient.WaylandDisplay) (*Manager, error) { - m := &Manager{ - display: display, - ctx: display.Context(), - cmdq: make(chan cmd, 128), - outputSetupReq: make(chan uint32, 16), - stopChan: make(chan struct{}), - - dirty: make(chan struct{}, 1), - layouts: make([]string, 0), - } - - if err := m.setupRegistry(); err != nil { - return nil, err - } - - m.updateState() - - m.notifierWg.Add(1) - go m.notifier() - - m.wg.Add(1) - go m.waylandActor() - - return m, nil -} - -func (m *Manager) post(fn func()) { - select { - case m.cmdq <- cmd{fn: fn}: - default: - log.Warn("DWL actor command queue full, dropping command") - } -} - -func (m *Manager) waylandActor() { - defer m.wg.Done() - - for { - select { - case <-m.stopChan: - return - case c := <-m.cmdq: - c.fn() - case outputID := <-m.outputSetupReq: - out, exists := m.outputs.Load(outputID) - if !exists { - log.Warnf("DWL: Output %d no longer exists, skipping setup", outputID) - continue - } - - if out.ipcOutput != nil { - continue - } - - mgr, ok := m.manager.(*dwl_ipc.ZdwlIpcManagerV2) - if !ok || mgr == nil { - log.Errorf("DWL: Manager not available for output %d setup", outputID) - continue - } - - log.Infof("DWL: Setting up ipcOutput for dynamically added output %d", outputID) - if err := m.setupOutput(mgr, out.output); err != nil { - log.Errorf("DWL: Failed to setup output %d: %v", outputID, err) - } else { - m.updateState() - } - } - } -} - -func (m *Manager) setupRegistry() error { - log.Info("DWL: starting registry setup") - - registry, err := m.display.GetRegistry() - if err != nil { - return fmt.Errorf("failed to get registry: %w", err) - } - m.registry = registry - - outputs := make([]*wlclient.Output, 0) - outputRegNames := make(map[uint32]uint32) - var dwlMgr *dwl_ipc.ZdwlIpcManagerV2 - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case dwl_ipc.ZdwlIpcManagerV2InterfaceName: - log.Infof("DWL: found %s", dwl_ipc.ZdwlIpcManagerV2InterfaceName) - manager := dwl_ipc.NewZdwlIpcManagerV2(m.ctx) - version := e.Version - if version > 2 { - version = 2 - } - if err := registry.Bind(e.Name, e.Interface, version, manager); err == nil { - dwlMgr = manager - log.Info("DWL: manager bound successfully") - - // Set handlers immediately after binding, before roundtrips - manager.SetTagsHandler(func(e dwl_ipc.ZdwlIpcManagerV2TagsEvent) { - log.Infof("DWL: Tags count: %d", e.Amount) - m.tagCount = e.Amount - m.updateState() - }) - - manager.SetLayoutHandler(func(e dwl_ipc.ZdwlIpcManagerV2LayoutEvent) { - log.Infof("DWL: Layout: %s", e.Name) - m.layouts = append(m.layouts, e.Name) - m.updateState() - }) - } else { - log.Errorf("DWL: failed to bind manager: %v", err) - } - case "wl_output": - log.Debugf("DWL: found wl_output (name=%d)", e.Name) - output := wlclient.NewOutput(m.ctx) - - outState := &outputState{ - registryName: e.Name, - output: output, - tags: make([]TagState, 0), - } - - output.SetNameHandler(func(ev wlclient.OutputNameEvent) { - log.Debugf("DWL: Output name: %s (registry=%d)", ev.Name, e.Name) - outState.name = ev.Name - }) - - output.SetDescriptionHandler(func(ev wlclient.OutputDescriptionEvent) { - log.Debugf("DWL: Output description: %s", ev.Description) - }) - - version := e.Version - if version > 4 { - version = 4 - } - if err := registry.Bind(e.Name, e.Interface, version, output); err == nil { - outputID := output.ID() - outState.id = outputID - log.Infof("DWL: Bound wl_output id=%d registry_name=%d", outputID, e.Name) - outputs = append(outputs, output) - outputRegNames[outputID] = e.Name - - m.outputs.Store(outputID, outState) - - if m.manager != nil { - select { - case m.outputSetupReq <- outputID: - log.Debugf("DWL: Queued setup for output %d", outputID) - default: - log.Warnf("DWL: Setup queue full, output %d will not be initialized", outputID) - } - } - } else { - log.Errorf("DWL: Failed to bind wl_output: %v", err) - } - } - }) - - registry.SetGlobalRemoveHandler(func(e wlclient.RegistryGlobalRemoveEvent) { - m.post(func() { - var outToRelease *outputState - m.outputs.Range(func(id uint32, out *outputState) bool { - if out.registryName == e.Name { - log.Infof("DWL: Output %d removed", id) - outToRelease = out - m.outputs.Delete(id) - return false - } - return true - }) - - if outToRelease != nil { - if ipcOut, ok := outToRelease.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2); ok && ipcOut != nil { - m.wlMutex.Lock() - ipcOut.Release() - m.wlMutex.Unlock() - log.Debugf("DWL: Released ipcOutput for removed output %d", outToRelease.id) - } - m.updateState() - } - }) - }) - - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("first roundtrip failed: %w", err) - } - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("second roundtrip failed: %w", err) - } - - if dwlMgr == nil { - log.Info("DWL: manager not found in registry") - return fmt.Errorf("dwl_ipc_manager_v2 not available") - } - - m.manager = dwlMgr - - for _, output := range outputs { - if err := m.setupOutput(dwlMgr, output); err != nil { - log.Warnf("DWL: Failed to setup output %d: %v", output.ID(), err) - } - } - - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("final roundtrip failed: %w", err) - } - - log.Info("DWL: registry setup complete") - return nil -} - -func (m *Manager) setupOutput(manager *dwl_ipc.ZdwlIpcManagerV2, output *wlclient.Output) error { - m.wlMutex.Lock() - ipcOutput, err := manager.GetOutput(output) - m.wlMutex.Unlock() - if err != nil { - return fmt.Errorf("failed to get dwl output: %w", err) - } - - outState, exists := m.outputs.Load(output.ID()) - if !exists { - return fmt.Errorf("output state not found for id %d", output.ID()) - } - outState.ipcOutput = ipcOutput - - ipcOutput.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) { - outState.active = e.Active - }) - - ipcOutput.SetTagHandler(func(e dwl_ipc.ZdwlIpcOutputV2TagEvent) { - updated := false - for i, tag := range outState.tags { - if tag.Tag == e.Tag { - outState.tags[i] = TagState{ - Tag: e.Tag, - State: e.State, - Clients: e.Clients, - Focused: e.Focused, - } - updated = true - break - } - } - - if !updated { - outState.tags = append(outState.tags, TagState{ - Tag: e.Tag, - State: e.State, - Clients: e.Clients, - Focused: e.Focused, - }) - } - - m.updateState() - }) - - ipcOutput.SetLayoutHandler(func(e dwl_ipc.ZdwlIpcOutputV2LayoutEvent) { - outState.layout = e.Layout - }) - - ipcOutput.SetTitleHandler(func(e dwl_ipc.ZdwlIpcOutputV2TitleEvent) { - outState.title = e.Title - }) - - ipcOutput.SetAppidHandler(func(e dwl_ipc.ZdwlIpcOutputV2AppidEvent) { - outState.appID = e.Appid - }) - - ipcOutput.SetLayoutSymbolHandler(func(e dwl_ipc.ZdwlIpcOutputV2LayoutSymbolEvent) { - outState.layoutSymbol = e.Layout - }) - - ipcOutput.SetKbLayoutHandler(func(e dwl_ipc.ZdwlIpcOutputV2KbLayoutEvent) { - outState.kbLayout = e.KbLayout - }) - - ipcOutput.SetKeymodeHandler(func(e dwl_ipc.ZdwlIpcOutputV2KeymodeEvent) { - outState.keymode = e.Keymode - }) - - ipcOutput.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) { - m.updateState() - }) - - return nil -} - -func (m *Manager) updateState() { - outputs := make(map[string]*OutputState) - activeOutput := "" - - m.outputs.Range(func(key uint32, out *outputState) bool { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - - tagsCopy := make([]TagState, len(out.tags)) - copy(tagsCopy, out.tags) - - outputs[name] = &OutputState{ - Name: name, - Active: out.active, - Tags: tagsCopy, - Layout: out.layout, - LayoutSymbol: out.layoutSymbol, - Title: out.title, - AppID: out.appID, - KbLayout: out.kbLayout, - Keymode: out.keymode, - } - - if out.active != 0 { - activeOutput = name - } - return true - }) - - newState := State{ - Outputs: outputs, - TagCount: m.tagCount, - Layouts: m.layouts, - ActiveOutput: activeOutput, - } - - m.stateMutex.Lock() - m.state = &newState - m.stateMutex.Unlock() - - m.notifySubscribers() -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - - currentState := m.GetState() - - if m.lastNotified != nil && !stateChanged(m.lastNotified, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- currentState: - default: - log.Warn("DWL: subscriber channel full, dropping update") - } - return true - }) - - stateCopy := currentState - m.lastNotified = &stateCopy - pending = false - } - } -} - -func (m *Manager) ensureOutputSetup(out *outputState) error { - if out.ipcOutput != nil { - return nil - } - - return fmt.Errorf("output not yet initialized - setup in progress, retry in a moment") -} - -func (m *Manager) SetTags(outputName string, tagmask uint32, toggleTagset uint32) error { - availableOutputs := make([]string, 0) - var targetOut *outputState - m.outputs.Range(func(key uint32, out *outputState) bool { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - availableOutputs = append(availableOutputs, name) - if name == outputName { - targetOut = out - return false - } - return true - }) - - if targetOut == nil { - return fmt.Errorf("output not found: %s (available: %v)", outputName, availableOutputs) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetTags(tagmask, toggleTagset) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) SetClientTags(outputName string, andTags uint32, xorTags uint32) error { - var targetOut *outputState - m.outputs.Range(func(key uint32, out *outputState) bool { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - if name == outputName { - targetOut = out - return false - } - return true - }) - - if targetOut == nil { - return fmt.Errorf("output not found: %s", outputName) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetClientTags(andTags, xorTags) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) SetLayout(outputName string, index uint32) error { - var targetOut *outputState - m.outputs.Range(func(key uint32, out *outputState) bool { - name := out.name - if name == "" { - name = fmt.Sprintf("output-%d", out.id) - } - if name == outputName { - targetOut = out - return false - } - return true - }) - - if targetOut == nil { - return fmt.Errorf("output not found: %s", outputName) - } - - if err := m.ensureOutputSetup(targetOut); err != nil { - return fmt.Errorf("failed to setup output %s: %w", outputName, err) - } - - ipcOut, ok := targetOut.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2) - if !ok { - return fmt.Errorf("output %s has invalid ipcOutput type", outputName) - } - - m.wlMutex.Lock() - err := ipcOut.SetLayout(index) - m.wlMutex.Unlock() - return err -} - -func (m *Manager) Close() { - close(m.stopChan) - m.wg.Wait() - m.notifierWg.Wait() - - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - m.outputs.Range(func(key uint32, out *outputState) bool { - if ipcOut, ok := out.ipcOutput.(*dwl_ipc.ZdwlIpcOutputV2); ok { - ipcOut.Release() - } - m.outputs.Delete(key) - return true - }) - - if mgr, ok := m.manager.(*dwl_ipc.ZdwlIpcManagerV2); ok { - mgr.Release() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager_test.go deleted file mode 100644 index 13af137..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/manager_test.go +++ /dev/null @@ -1,366 +0,0 @@ -package dwl - -import ( - "errors" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - mocks_wlclient "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/wlclient" -) - -func TestStateChanged_BothNil(t *testing.T) { - assert.True(t, stateChanged(nil, nil)) -} - -func TestStateChanged_OneNil(t *testing.T) { - s := &State{TagCount: 9} - assert.True(t, stateChanged(s, nil)) - assert.True(t, stateChanged(nil, s)) -} - -func TestStateChanged_TagCountDiffers(t *testing.T) { - a := &State{TagCount: 9, Outputs: make(map[string]*OutputState), Layouts: []string{}} - b := &State{TagCount: 10, Outputs: make(map[string]*OutputState), Layouts: []string{}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_LayoutLengthDiffers(t *testing.T) { - a := &State{TagCount: 9, Layouts: []string{"tile"}, Outputs: make(map[string]*OutputState)} - b := &State{TagCount: 9, Layouts: []string{"tile", "monocle"}, Outputs: make(map[string]*OutputState)} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_ActiveOutputDiffers(t *testing.T) { - a := &State{TagCount: 9, ActiveOutput: "eDP-1", Outputs: make(map[string]*OutputState), Layouts: []string{}} - b := &State{TagCount: 9, ActiveOutput: "HDMI-A-1", Outputs: make(map[string]*OutputState), Layouts: []string{}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputCountDiffers(t *testing.T) { - a := &State{ - TagCount: 9, - Outputs: map[string]*OutputState{"eDP-1": {}}, - Layouts: []string{}, - } - b := &State{ - TagCount: 9, - Outputs: map[string]*OutputState{}, - Layouts: []string{}, - } - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputFieldsDiffer(t *testing.T) { - a := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Active: 1, Layout: 0, Title: "Firefox"}, - }, - } - b := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Active: 0, Layout: 0, Title: "Firefox"}, - }, - } - assert.True(t, stateChanged(a, b)) - - b.Outputs["eDP-1"].Active = 1 - b.Outputs["eDP-1"].Layout = 1 - assert.True(t, stateChanged(a, b)) - - b.Outputs["eDP-1"].Layout = 0 - b.Outputs["eDP-1"].Title = "Code" - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_TagsDiffer(t *testing.T) { - a := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Tags: []TagState{{Tag: 1, State: 1, Clients: 2, Focused: 1}}}, - }, - } - b := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Tags: []TagState{{Tag: 1, State: 2, Clients: 2, Focused: 1}}}, - }, - } - assert.True(t, stateChanged(a, b)) - - b.Outputs["eDP-1"].Tags[0].State = 1 - b.Outputs["eDP-1"].Tags[0].Clients = 3 - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_Equal(t *testing.T) { - a := &State{ - TagCount: 9, - ActiveOutput: "eDP-1", - Layouts: []string{"tile", "monocle"}, - Outputs: map[string]*OutputState{ - "eDP-1": { - Name: "eDP-1", - Active: 1, - Layout: 0, - LayoutSymbol: "[]=", - Title: "Firefox", - AppID: "firefox", - KbLayout: "us", - Keymode: "", - Tags: []TagState{{Tag: 1, State: 1, Clients: 2, Focused: 1}}, - }, - }, - } - b := &State{ - TagCount: 9, - ActiveOutput: "eDP-1", - Layouts: []string{"tile", "monocle"}, - Outputs: map[string]*OutputState{ - "eDP-1": { - Name: "eDP-1", - Active: 1, - Layout: 0, - LayoutSymbol: "[]=", - Title: "Firefox", - AppID: "firefox", - KbLayout: "us", - Keymode: "", - Tags: []TagState{{Tag: 1, State: 1, Clients: 2, Focused: 1}}, - }, - }, - } - assert.False(t, stateChanged(a, b)) -} - -func TestManager_ConcurrentGetState(t *testing.T) { - m := &Manager{ - state: &State{ - TagCount: 9, - Layouts: []string{"tile"}, - Outputs: map[string]*OutputState{"eDP-1": {Name: "eDP-1"}}, - }, - } - - var wg sync.WaitGroup - const goroutines = 50 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - s := m.GetState() - _ = s.TagCount - _ = s.Outputs - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.stateMutex.Lock() - m.state = &State{ - TagCount: uint32(j % 10), - Layouts: []string{"tile", "monocle"}, - Outputs: map[string]*OutputState{"eDP-1": {Active: uint32(j % 2)}}, - } - m.stateMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentSubscriberAccess(t *testing.T) { - m := &Manager{ - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - var wg sync.WaitGroup - const goroutines = 20 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - subID := string(rune('a' + id)) - ch := m.Subscribe(subID) - assert.NotNil(t, ch) - time.Sleep(time.Millisecond) - m.Unsubscribe(subID) - }(i) - } - - wg.Wait() -} - -func TestManager_SyncmapOutputsConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - state := &outputState{ - id: key, - name: "test-output", - active: uint32(j % 2), - tags: []TagState{{Tag: uint32(j), State: 1}}, - } - m.outputs.Store(key, state) - - if loaded, ok := m.outputs.Load(key); ok { - assert.Equal(t, key, loaded.id) - } - - m.outputs.Range(func(k uint32, v *outputState) bool { - _ = v.name - _ = v.active - return true - }) - } - - m.outputs.Delete(key) - }(i) - } - - wg.Wait() -} - -func TestManager_NotifySubscribersNonBlocking(t *testing.T) { - m := &Manager{ - dirty: make(chan struct{}, 1), - } - - for i := 0; i < 10; i++ { - m.notifySubscribers() - } - - assert.Len(t, m.dirty, 1) -} - -func TestManager_PostQueueFull(t *testing.T) { - m := &Manager{ - cmdq: make(chan cmd, 2), - stopChan: make(chan struct{}), - } - - m.post(func() {}) - m.post(func() {}) - m.post(func() {}) - m.post(func() {}) - - assert.Len(t, m.cmdq, 2) -} - -func TestManager_GetStateNilState(t *testing.T) { - m := &Manager{} - - s := m.GetState() - assert.NotNil(t, s.Outputs) - assert.NotNil(t, s.Layouts) - assert.Equal(t, uint32(0), s.TagCount) -} - -func TestTagState_Fields(t *testing.T) { - tag := TagState{ - Tag: 1, - State: 2, - Clients: 3, - Focused: 1, - } - - assert.Equal(t, uint32(1), tag.Tag) - assert.Equal(t, uint32(2), tag.State) - assert.Equal(t, uint32(3), tag.Clients) - assert.Equal(t, uint32(1), tag.Focused) -} - -func TestOutputState_Fields(t *testing.T) { - out := OutputState{ - Name: "eDP-1", - Active: 1, - Tags: []TagState{{Tag: 1}}, - Layout: 0, - LayoutSymbol: "[]=", - Title: "Firefox", - AppID: "firefox", - KbLayout: "us", - Keymode: "", - } - - assert.Equal(t, "eDP-1", out.Name) - assert.Equal(t, uint32(1), out.Active) - assert.Len(t, out.Tags, 1) - assert.Equal(t, "[]=", out.LayoutSymbol) -} - -func TestStateChanged_NewOutputAppears(t *testing.T) { - a := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Name: "eDP-1"}, - }, - } - b := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Name: "eDP-1"}, - "HDMI-A-1": {Name: "HDMI-A-1"}, - }, - } - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_TagsLengthDiffers(t *testing.T) { - a := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Tags: []TagState{{Tag: 1}}}, - }, - } - b := &State{ - TagCount: 9, - Layouts: []string{}, - Outputs: map[string]*OutputState{ - "eDP-1": {Tags: []TagState{{Tag: 1}, {Tag: 2}}}, - }, - } - assert.True(t, stateChanged(a, b)) -} - -func TestNewManager_GetRegistryError(t *testing.T) { - mockDisplay := mocks_wlclient.NewMockWaylandDisplay(t) - - mockDisplay.EXPECT().Context().Return(nil) - mockDisplay.EXPECT().GetRegistry().Return(nil, errors.New("failed to get registry")) - - _, err := NewManager(mockDisplay) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to get registry") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/types.go deleted file mode 100644 index 2b17537..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/dwl/types.go +++ /dev/null @@ -1,176 +0,0 @@ -package dwl - -import ( - "sync" - - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type TagState struct { - Tag uint32 `json:"tag"` - State uint32 `json:"state"` - Clients uint32 `json:"clients"` - Focused uint32 `json:"focused"` -} - -type OutputState struct { - Name string `json:"name"` - Active uint32 `json:"active"` - Tags []TagState `json:"tags"` - Layout uint32 `json:"layout"` - LayoutSymbol string `json:"layoutSymbol"` - Title string `json:"title"` - AppID string `json:"appId"` - KbLayout string `json:"kbLayout"` - Keymode string `json:"keymode"` -} - -type State struct { - Outputs map[string]*OutputState `json:"outputs"` - TagCount uint32 `json:"tagCount"` - Layouts []string `json:"layouts"` - ActiveOutput string `json:"activeOutput"` -} - -type cmd struct { - fn func() -} - -type Manager struct { - display wlclient.WaylandDisplay - ctx *wlclient.Context - registry *wlclient.Registry - manager any - - outputs syncmap.Map[uint32, *outputState] - - tagCount uint32 - layouts []string - - wlMutex sync.Mutex - cmdq chan cmd - outputSetupReq chan uint32 - stopChan chan struct{} - wg sync.WaitGroup - - subscribers syncmap.Map[string, chan State] - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotified *State - - stateMutex sync.RWMutex - state *State -} - -type outputState struct { - id uint32 - registryName uint32 - output *wlclient.Output - ipcOutput any - name string - active uint32 - tags []TagState - layout uint32 - layoutSymbol string - title string - appID string - kbLayout string - keymode string -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{ - Outputs: make(map[string]*OutputState), - Layouts: []string{}, - TagCount: 0, - } - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - - m.subscribers.Store(id, ch) - - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func stateChanged(old, new *State) bool { - if old == nil || new == nil { - return true - } - if old.TagCount != new.TagCount { - return true - } - if len(old.Layouts) != len(new.Layouts) { - return true - } - if old.ActiveOutput != new.ActiveOutput { - return true - } - if len(old.Outputs) != len(new.Outputs) { - return true - } - - for name, newOut := range new.Outputs { - oldOut, exists := old.Outputs[name] - if !exists { - return true - } - if oldOut.Active != newOut.Active { - return true - } - if oldOut.Layout != newOut.Layout { - return true - } - if oldOut.LayoutSymbol != newOut.LayoutSymbol { - return true - } - if oldOut.Title != newOut.Title { - return true - } - if oldOut.AppID != newOut.AppID { - return true - } - if oldOut.KbLayout != newOut.KbLayout { - return true - } - if oldOut.Keymode != newOut.Keymode { - return true - } - if len(oldOut.Tags) != len(newOut.Tags) { - return true - } - for i, newTag := range newOut.Tags { - if i >= len(oldOut.Tags) { - return true - } - oldTag := oldOut.Tags[i] - if oldTag.Tag != newTag.Tag || oldTag.State != newTag.State || - oldTag.Clients != newTag.Clients || oldTag.Focused != newTag.Focused { - return true - } - } - } - - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers.go deleted file mode 100644 index 54a7bca..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers.go +++ /dev/null @@ -1,20 +0,0 @@ -package evdev - -import ( - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleRequest(conn net.Conn, req models.Request, m *Manager) { - switch req.Method { - case "evdev.getState": - handleGetState(conn, req, m) - default: - models.RespondError(conn, req.ID, "unknown method: "+req.Method) - } -} - -func handleGetState(conn net.Conn, req models.Request, m *Manager) { - models.Respond(conn, req.ID, m.GetState()) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers_test.go deleted file mode 100644 index f9bd21d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/handlers_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package evdev - -import ( - "bytes" - "encoding/json" - "errors" - "net" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - mocks "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/evdev" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -type mockNetConn struct { - net.Conn - readBuf *bytes.Buffer - writeBuf *bytes.Buffer - closed bool -} - -func newMockNetConn() *mockNetConn { - return &mockNetConn{ - readBuf: &bytes.Buffer{}, - writeBuf: &bytes.Buffer{}, - } -} - -func (m *mockNetConn) Read(b []byte) (n int, err error) { - return m.readBuf.Read(b) -} - -func (m *mockNetConn) Write(b []byte) (n int, err error) { - return m.writeBuf.Write(b) -} - -func (m *mockNetConn) Close() error { - m.closed = true - return nil -} - -func TestHandleRequest(t *testing.T) { - t.Run("getState request", func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - state: State{Available: true, CapsLock: true}, - closeChan: make(chan struct{}), - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "evdev.getState", - Params: map[string]any{}, - } - - HandleRequest(conn, req, m) - - var resp models.Response[State] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Available) - assert.True(t, resp.Result.CapsLock) - }) - - t.Run("unknown method", func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - conn := newMockNetConn() - req := models.Request{ - ID: 456, - Method: "evdev.unknownMethod", - Params: map[string]any{}, - } - - HandleRequest(conn, req, m) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 456, resp.ID) - assert.NotEmpty(t, resp.Error) - assert.Contains(t, resp.Error, "unknown method") - }) -} - -func TestHandleGetState(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - conn := newMockNetConn() - req := models.Request{ - ID: 789, - Method: "evdev.getState", - Params: map[string]any{}, - } - - handleGetState(conn, req, m) - - var resp models.Response[State] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 789, resp.ID) - assert.NotNil(t, resp.Result) - assert.True(t, resp.Result.Available) - assert.False(t, resp.Result.CapsLock) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager.go deleted file mode 100644 index b578481..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager.go +++ /dev/null @@ -1,413 +0,0 @@ -package evdev - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/fsnotify/fsnotify" - evdev "github.com/holoplot/go-evdev" -) - -const ( - evKeyType = 0x01 - evLedType = 0x11 - keyCapslockKey = 58 - ledCapslockKey = 1 - keyStateOn = 1 -) - -type EvdevDevice interface { - Name() (string, error) - Path() string - Close() error - ReadOne() (*evdev.InputEvent, error) - State(t evdev.EvType) (evdev.StateMap, error) -} - -type Manager struct { - devices []EvdevDevice - devicesMutex sync.RWMutex - monitoredPaths map[string]bool - state State - stateMutex sync.RWMutex - subscribers syncmap.Map[string, chan State] - closeChan chan struct{} - closeOnce sync.Once - watcher *fsnotify.Watcher -} - -func NewManager() (*Manager, error) { - devices, err := findKeyboards() - if err != nil { - return nil, fmt.Errorf("failed to find keyboards: %w", err) - } - - initialCapsLock := readInitialCapsLockState(devices[0]) - - watcher, err := fsnotify.NewWatcher() - if err != nil { - log.Warnf("Failed to create fsnotify watcher, hotplug detection disabled: %v", err) - watcher = nil - } else if err := watcher.Add("/dev/input"); err != nil { - log.Warnf("Failed to watch /dev/input, hotplug detection disabled: %v", err) - watcher.Close() - watcher = nil - } - - monitoredPaths := make(map[string]bool) - for _, device := range devices { - monitoredPaths[device.Path()] = true - } - - m := &Manager{ - devices: devices, - monitoredPaths: monitoredPaths, - state: State{Available: true, CapsLock: initialCapsLock}, - - closeChan: make(chan struct{}), - watcher: watcher, - } - - for i, device := range devices { - go m.monitorDevice(device, i) - } - - if watcher != nil { - go m.watchForNewKeyboards() - } - - return m, nil -} - -func readInitialCapsLockState(device EvdevDevice) bool { - ledStates, err := device.State(evLedType) - if err != nil { - log.Debugf("Could not read LED state: %v", err) - return false - } - - return ledStates[ledCapslockKey] -} - -func findKeyboards() ([]EvdevDevice, error) { - pattern := "/dev/input/event*" - matches, err := filepath.Glob(pattern) - if err != nil { - return nil, fmt.Errorf("failed to glob input devices: %w", err) - } - - if len(matches) == 0 { - return nil, fmt.Errorf("no input devices found") - } - - var keyboards []EvdevDevice - for _, path := range matches { - device, err := evdev.Open(path) - if err != nil { - continue - } - - if !isKeyboard(device) { - device.Close() - continue - } - - deviceName, _ := device.Name() - log.Debugf("Found keyboard: %s at %s", deviceName, path) - keyboards = append(keyboards, device) - } - - if len(keyboards) == 0 { - return nil, fmt.Errorf("no keyboard device found") - } - - return keyboards, nil -} - -func isKeyboard(device EvdevDevice) bool { - deviceName, err := device.Name() - if err != nil { - return false - } - - name := strings.ToLower(deviceName) - - switch { - case strings.Contains(name, "keyboard"): - return true - case strings.Contains(name, "kbd"): - return true - case strings.Contains(name, "input") && strings.Contains(name, "key"): - return true - } - - keyStates, err := device.State(evKeyType) - if err != nil { - return false - } - - hasKeyA := len(keyStates) > 30 - hasKeyZ := len(keyStates) > 44 - hasEnter := len(keyStates) > 28 - - return hasKeyA && hasKeyZ && hasEnter && len(keyStates) > 100 -} - -func (m *Manager) watchForNewKeyboards() { - defer func() { - if r := recover(); r != nil { - log.Errorf("Panic in keyboard hotplug monitor: %v", r) - } - }() - - for { - select { - case <-m.closeChan: - return - case event, ok := <-m.watcher.Events: - if !ok { - return - } - - if !strings.HasPrefix(filepath.Base(event.Name), "event") { - continue - } - - if event.Op&fsnotify.Create == fsnotify.Create { - time.Sleep(100 * time.Millisecond) - - m.devicesMutex.Lock() - if m.monitoredPaths[event.Name] { - m.devicesMutex.Unlock() - continue - } - - device, err := evdev.Open(event.Name) - if err != nil { - m.devicesMutex.Unlock() - continue - } - - if !isKeyboard(device) { - device.Close() - m.devicesMutex.Unlock() - continue - } - - deviceName, _ := device.Name() - log.Debugf("Hotplugged keyboard: %s at %s", deviceName, event.Name) - - m.devices = append(m.devices, device) - m.monitoredPaths[event.Name] = true - deviceIndex := len(m.devices) - 1 - m.devicesMutex.Unlock() - - go m.monitorDevice(device, deviceIndex) - } else if event.Op&fsnotify.Remove == fsnotify.Remove { - m.devicesMutex.Lock() - if !m.monitoredPaths[event.Name] { - m.devicesMutex.Unlock() - continue - } - - delete(m.monitoredPaths, event.Name) - for i, device := range m.devices { - if device != nil && device.Path() == event.Name { - log.Debugf("Keyboard removed: %s", event.Name) - device.Close() - m.devices[i] = nil - break - } - } - m.devicesMutex.Unlock() - } - - case err, ok := <-m.watcher.Errors: - if !ok { - return - } - log.Warnf("Keyboard hotplug watcher error: %v", err) - } - } -} - -func (m *Manager) monitorDevice(device EvdevDevice, deviceIndex int) { - defer func() { - if r := recover(); r != nil { - log.Errorf("Panic in evdev monitor: %v", r) - } - }() - - for { - select { - case <-m.closeChan: - return - default: - } - - event, err := device.ReadOne() - if err != nil { - if isClosedError(err) { - return - } - log.Warnf("Failed to read evdev event: %v", err) - time.Sleep(100 * time.Millisecond) - continue - } - - if event == nil { - continue - } - - if event.Type == evKeyType && event.Code == keyCapslockKey && event.Value == keyStateOn { - time.Sleep(50 * time.Millisecond) - m.readAndUpdateCapsLockState(deviceIndex) - } else if event.Type == evLedType && event.Code == ledCapslockKey { - capsLockState := event.Value == keyStateOn - m.updateCapsLockStateDirect(capsLockState) - } - } -} - -func isClosedError(err error) bool { - if err == nil { - return false - } - - errStr := err.Error() - switch { - case strings.Contains(errStr, "closed"): - return true - case strings.Contains(errStr, "bad file descriptor"): - return true - default: - return false - } -} - -func (m *Manager) readAndUpdateCapsLockState(deviceIndex int) { - m.devicesMutex.RLock() - if deviceIndex >= len(m.devices) { - m.devicesMutex.RUnlock() - return - } - device := m.devices[deviceIndex] - m.devicesMutex.RUnlock() - - ledStates, err := device.State(evLedType) - if err != nil { - log.Warnf("Failed to read LED state: %v", err) - return - } - - if len(ledStates) == 0 { - log.Debug("No LED state available (empty map)") - - // This means the device either: - // - doesn't support LED reporting at all, or - // - the kernel returned an empty state - return - } - - capsLockState := ledStates[ledCapslockKey] - m.updateCapsLockStateDirect(capsLockState) -} - -func (m *Manager) updateCapsLockStateDirect(capsLockState bool) { - m.stateMutex.Lock() - if m.state.CapsLock == capsLockState { - m.stateMutex.Unlock() - return - } - - m.state.CapsLock = capsLockState - newState := m.state - m.stateMutex.Unlock() - - log.Debugf("Caps lock state: %v", newState.CapsLock) - m.notifySubscribers(newState) -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return m.state -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 16) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) notifySubscribers(state State) { - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- state: - default: - } - return true - }) -} - -func (m *Manager) Close() { - m.closeOnce.Do(func() { - close(m.closeChan) - - if m.watcher != nil { - m.watcher.Close() - } - - m.devicesMutex.Lock() - for _, device := range m.devices { - if device == nil { - continue - } - if err := device.Close(); err != nil && !isClosedError(err) { - log.Warnf("Error closing evdev device: %v", err) - } - } - m.devicesMutex.Unlock() - - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - }) -} - -func InitializeManager() (*Manager, error) { - if os.Getuid() != 0 && !hasInputGroupAccess() { - return nil, fmt.Errorf("insufficient permissions to access input devices") - } - - return NewManager() -} - -func hasInputGroupAccess() bool { - pattern := "/dev/input/event*" - matches, err := filepath.Glob(pattern) - if err != nil || len(matches) == 0 { - return false - } - - testFile, err := os.Open(matches[0]) - if err != nil { - return false - } - testFile.Close() - return true -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager_test.go deleted file mode 100644 index 667c078..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/manager_test.go +++ /dev/null @@ -1,344 +0,0 @@ -package evdev - -import ( - "errors" - "testing" - - evdev "github.com/holoplot/go-evdev" - "github.com/stretchr/testify/assert" - - mocks "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/evdev" -) - -func TestManager_Creation(t *testing.T) { - t.Run("manager created successfully with caps lock off", func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - assert.NotNil(t, m) - assert.True(t, m.state.Available) - assert.False(t, m.state.CapsLock) - }) - - t.Run("manager created successfully with caps lock on", func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - state: State{Available: true, CapsLock: true}, - closeChan: make(chan struct{}), - } - - assert.NotNil(t, m) - assert.True(t, m.state.Available) - assert.True(t, m.state.CapsLock) - }) -} - -func TestManager_GetState(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - monitoredPaths: make(map[string]bool), - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - state := m.GetState() - assert.True(t, state.Available) - assert.False(t, state.CapsLock) -} - -func TestManager_Subscribe(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - monitoredPaths: make(map[string]bool), - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - ch := m.Subscribe("test-client") - assert.NotNil(t, ch) - count := 0 - m.subscribers.Range(func(key string, ch chan State) bool { - count++ - return true - }) - assert.Equal(t, 1, count) -} - -func TestManager_Unsubscribe(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - monitoredPaths: make(map[string]bool), - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - ch := m.Subscribe("test-client") - count := 0 - m.subscribers.Range(func(key string, ch chan State) bool { - count++ - return true - }) - assert.Equal(t, 1, count) - - m.Unsubscribe("test-client") - count = 0 - m.subscribers.Range(func(key string, ch chan State) bool { - count++ - return true - }) - assert.Equal(t, 0, count) - - select { - case _, ok := <-ch: - assert.False(t, ok, "channel should be closed") - default: - t.Error("channel should be closed") - } -} - -func TestManager_UpdateCapsLock(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - monitoredPaths: make(map[string]bool), - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - ch := m.Subscribe("test-client") - - ledStateOn := evdev.StateMap{ledCapslockKey: true} - mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledStateOn, nil).Once() - - go func() { - m.readAndUpdateCapsLockState(0) - }() - - newState := <-ch - assert.True(t, newState.CapsLock) - - ledStateOff := evdev.StateMap{ledCapslockKey: false} - mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledStateOff, nil).Once() - - go func() { - m.readAndUpdateCapsLockState(0) - }() - - newState = <-ch - assert.False(t, newState.CapsLock) -} - -func TestManager_Close(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().Close().Return(nil).Once() - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - monitoredPaths: make(map[string]bool), - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - ch1 := m.Subscribe("client1") - ch2 := m.Subscribe("client2") - - m.Close() - - select { - case _, ok := <-ch1: - assert.False(t, ok, "channel 1 should be closed") - default: - t.Error("channel 1 should be closed") - } - - select { - case _, ok := <-ch2: - assert.False(t, ok, "channel 2 should be closed") - default: - t.Error("channel 2 should be closed") - } - - count := 0 - m.subscribers.Range(func(key string, ch chan State) bool { - count++ - return true - }) - assert.Equal(t, 0, count) - - m.Close() -} - -func TestIsKeyboard(t *testing.T) { - tests := []struct { - name string - devName string - expected bool - }{ - {"keyboard in name", "AT Translated Set 2 keyboard", true}, - {"kbd in name", "USB kbd", true}, - {"input and key", "input key device", true}, - {"random device", "Mouse", false}, - {"empty name", "", false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().Name().Return(tt.devName, nil).Once() - - if !tt.expected { - mockDevice.EXPECT().State(evdev.EvType(evKeyType)).Return(evdev.StateMap{}, nil).Maybe() - } - - result := isKeyboard(mockDevice) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestIsKeyboard_ErrorHandling(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().Name().Return("", errors.New("device error")).Once() - - result := isKeyboard(mockDevice) - assert.False(t, result) -} - -func TestManager_MonitorDevice(t *testing.T) { - t.Run("caps lock key press updates state", func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - - capsLockEvent := &evdev.InputEvent{ - Type: evKeyType, - Code: keyCapslockKey, - Value: keyStateOn, - } - - ledState := evdev.StateMap{ledCapslockKey: true} - - mockDevice.EXPECT().ReadOne().Return(capsLockEvent, nil).Once() - mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once() - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("stop")).Maybe() - mockDevice.EXPECT().Close().Return(nil).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - ch := m.Subscribe("test") - - go m.monitorDevice(mockDevice, 0) - - state := <-ch - assert.True(t, state.CapsLock) - - m.Close() - }) -} - -func TestIsClosedError(t *testing.T) { - tests := []struct { - name string - err error - expected bool - }{ - {"nil error", nil, false}, - {"closed error", errors.New("device closed"), true}, - {"bad file descriptor", errors.New("bad file descriptor"), true}, - {"other error", errors.New("some other error"), false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := isClosedError(tt.err) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestNotifySubscribers(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().ReadOne().Return(nil, errors.New("test")).Maybe() - mockDevice.EXPECT().Close().Return(nil).Maybe() - - m := &Manager{ - devices: []EvdevDevice{mockDevice}, - monitoredPaths: make(map[string]bool), - state: State{Available: true, CapsLock: false}, - closeChan: make(chan struct{}), - } - - ch1 := m.Subscribe("client1") - ch2 := m.Subscribe("client2") - - newState := State{Available: true, CapsLock: true} - go m.notifySubscribers(newState) - - state1 := <-ch1 - state2 := <-ch2 - - assert.Equal(t, newState, state1) - assert.Equal(t, newState, state2) - - m.Close() -} - -func TestReadInitialCapsLockState(t *testing.T) { - t.Run("caps lock is on", func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - ledState := evdev.StateMap{ - ledCapslockKey: true, - } - mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once() - - result := readInitialCapsLockState(mockDevice) - assert.True(t, result) - }) - - t.Run("caps lock is off", func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - ledState := evdev.StateMap{ - ledCapslockKey: false, - } - mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(ledState, nil).Once() - - result := readInitialCapsLockState(mockDevice) - assert.False(t, result) - }) - - t.Run("error reading LED state", func(t *testing.T) { - mockDevice := mocks.NewMockEvdevDevice(t) - mockDevice.EXPECT().State(evdev.EvType(evLedType)).Return(nil, errors.New("read error")).Once() - - result := readInitialCapsLockState(mockDevice) - assert.False(t, result) - }) -} - -func TestHasInputGroupAccess(t *testing.T) { - result := hasInputGroupAccess() - t.Logf("hasInputGroupAccess: %v", result) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/models.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/models.go deleted file mode 100644 index df8ec9e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/evdev/models.go +++ /dev/null @@ -1,6 +0,0 @@ -package evdev - -type State struct { - Available bool `json:"available"` - CapsLock bool `json:"capsLock"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/handlers.go deleted file mode 100644 index 3f7e17f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/handlers.go +++ /dev/null @@ -1,134 +0,0 @@ -package extworkspace - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "extworkspace manager not initialized") - return - } - - switch req.Method { - case "extworkspace.getState": - handleGetState(conn, req, manager) - case "extworkspace.activateWorkspace": - handleActivateWorkspace(conn, req, manager) - case "extworkspace.deactivateWorkspace": - handleDeactivateWorkspace(conn, req, manager) - case "extworkspace.removeWorkspace": - handleRemoveWorkspace(conn, req, manager) - case "extworkspace.createWorkspace": - handleCreateWorkspace(conn, req, manager) - case "extworkspace.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - state := manager.GetState() - models.Respond(conn, req.ID, state) -} - -func handleActivateWorkspace(conn net.Conn, req models.Request, manager *Manager) { - groupID := models.GetOr(req, "groupID", "") - workspaceID, ok := models.Get[string](req, "workspaceID") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'workspaceID' parameter") - return - } - - if err := manager.ActivateWorkspace(groupID, workspaceID); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "workspace activated"}) -} - -func handleDeactivateWorkspace(conn net.Conn, req models.Request, manager *Manager) { - groupID := models.GetOr(req, "groupID", "") - workspaceID, ok := models.Get[string](req, "workspaceID") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'workspaceID' parameter") - return - } - - if err := manager.DeactivateWorkspace(groupID, workspaceID); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "workspace deactivated"}) -} - -func handleRemoveWorkspace(conn net.Conn, req models.Request, manager *Manager) { - groupID := models.GetOr(req, "groupID", "") - workspaceID, ok := models.Get[string](req, "workspaceID") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'workspaceID' parameter") - return - } - - if err := manager.RemoveWorkspace(groupID, workspaceID); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "workspace removed"}) -} - -func handleCreateWorkspace(conn net.Conn, req models.Request, manager *Manager) { - groupID, ok := models.Get[string](req, "groupID") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'groupID' parameter") - return - } - - workspaceName, ok := models.Get[string](req, "name") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - if err := manager.CreateWorkspace(groupID, workspaceName); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "workspace create requested"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/manager.go deleted file mode 100644 index b791b39..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/manager.go +++ /dev/null @@ -1,598 +0,0 @@ -package extworkspace - -import ( - "fmt" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_workspace" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -func CheckCapability() bool { - display, err := wlclient.Connect("") - if err != nil { - return false - } - defer display.Destroy() - - registry, err := display.GetRegistry() - if err != nil { - return false - } - defer registry.Destroy() - - found := false - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - if e.Interface == ext_workspace.ExtWorkspaceManagerV1InterfaceName { - found = true - } - }) - - // Roundtrip to ensure all registry events are processed - if err := display.Roundtrip(); err != nil { - return false - } - - return found -} - -func NewManager(display wlclient.WaylandDisplay) (*Manager, error) { - m := &Manager{ - display: display, - ctx: display.Context(), - cmdq: make(chan cmd, 128), - stopChan: make(chan struct{}), - - dirty: make(chan struct{}, 1), - } - - m.wg.Add(1) - go m.waylandActor() - - if err := m.setupRegistry(); err != nil { - close(m.stopChan) - m.wg.Wait() - return nil, err - } - - m.updateState() - - m.notifierWg.Add(1) - go m.notifier() - - return m, nil -} - -func (m *Manager) post(fn func()) { - select { - case m.cmdq <- cmd{fn: fn}: - default: - log.Warn("ExtWorkspace actor command queue full, dropping command") - } -} - -func (m *Manager) waylandActor() { - defer m.wg.Done() - - for { - select { - case <-m.stopChan: - return - case c := <-m.cmdq: - c.fn() - } - } -} - -func (m *Manager) setupRegistry() error { - log.Info("ExtWorkspace: starting registry setup") - - registry, err := m.display.GetRegistry() - if err != nil { - return fmt.Errorf("failed to get registry: %w", err) - } - m.registry = registry - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - if e.Interface == "wl_output" { - output := wlclient.NewOutput(m.ctx) - if err := registry.Bind(e.Name, e.Interface, 4, output); err == nil { - outputID := output.ID() - - output.SetNameHandler(func(ev wlclient.OutputNameEvent) { - m.outputNames.Store(outputID, ev.Name) - log.Debugf("ExtWorkspace: Output %d (%s) name received", outputID, ev.Name) - m.post(func() { - m.updateState() - }) - }) - } - return - } - - if e.Interface == ext_workspace.ExtWorkspaceManagerV1InterfaceName { - log.Infof("ExtWorkspace: found %s", ext_workspace.ExtWorkspaceManagerV1InterfaceName) - manager := ext_workspace.NewExtWorkspaceManagerV1(m.ctx) - version := e.Version - if version > 1 { - version = 1 - } - - manager.SetWorkspaceGroupHandler(func(e ext_workspace.ExtWorkspaceManagerV1WorkspaceGroupEvent) { - m.handleWorkspaceGroup(e) - }) - - manager.SetWorkspaceHandler(func(e ext_workspace.ExtWorkspaceManagerV1WorkspaceEvent) { - m.handleWorkspace(e) - }) - - manager.SetDoneHandler(func(e ext_workspace.ExtWorkspaceManagerV1DoneEvent) { - log.Debug("ExtWorkspace: done event received") - m.post(func() { - m.updateState() - }) - }) - - manager.SetFinishedHandler(func(e ext_workspace.ExtWorkspaceManagerV1FinishedEvent) { - log.Info("ExtWorkspace: finished event received") - }) - - if err := registry.Bind(e.Name, e.Interface, version, manager); err == nil { - m.manager = manager - log.Info("ExtWorkspace: manager bound successfully") - } else { - log.Errorf("ExtWorkspace: failed to bind manager: %v", err) - } - } - }) - - log.Info("ExtWorkspace: registry setup complete (events will be processed async)") - return nil -} - -func (m *Manager) handleWorkspaceGroup(e ext_workspace.ExtWorkspaceManagerV1WorkspaceGroupEvent) { - handle := e.WorkspaceGroup - groupID := handle.ID() - - log.Debugf("ExtWorkspace: New workspace group (id=%d)", groupID) - - group := &workspaceGroupState{ - id: groupID, - handle: handle, - outputIDs: make(map[uint32]bool), - workspaceIDs: make([]uint32, 0), - } - - m.groups.Store(groupID, group) - - handle.SetCapabilitiesHandler(func(e ext_workspace.ExtWorkspaceGroupHandleV1CapabilitiesEvent) { - log.Debugf("ExtWorkspace: Group %d capabilities: %d", groupID, e.Capabilities) - }) - - handle.SetOutputEnterHandler(func(e ext_workspace.ExtWorkspaceGroupHandleV1OutputEnterEvent) { - outputID := e.Output.ID() - log.Debugf("ExtWorkspace: Group %d output enter (output=%d)", groupID, outputID) - - m.post(func() { - group.outputIDs[outputID] = true - m.updateState() - }) - }) - - handle.SetOutputLeaveHandler(func(e ext_workspace.ExtWorkspaceGroupHandleV1OutputLeaveEvent) { - outputID := e.Output.ID() - log.Debugf("ExtWorkspace: Group %d output leave (output=%d)", groupID, outputID) - m.post(func() { - delete(group.outputIDs, outputID) - m.updateState() - }) - }) - - handle.SetWorkspaceEnterHandler(func(e ext_workspace.ExtWorkspaceGroupHandleV1WorkspaceEnterEvent) { - workspaceID := e.Workspace.ID() - log.Debugf("ExtWorkspace: Group %d workspace enter (workspace=%d)", groupID, workspaceID) - - m.post(func() { - if ws, ok := m.workspaces.Load(workspaceID); ok { - ws.groupID = groupID - } - - group.workspaceIDs = append(group.workspaceIDs, workspaceID) - m.updateState() - }) - }) - - handle.SetWorkspaceLeaveHandler(func(e ext_workspace.ExtWorkspaceGroupHandleV1WorkspaceLeaveEvent) { - workspaceID := e.Workspace.ID() - log.Debugf("ExtWorkspace: Group %d workspace leave (workspace=%d)", groupID, workspaceID) - - m.post(func() { - if ws, ok := m.workspaces.Load(workspaceID); ok { - ws.groupID = 0 - } - - for i, id := range group.workspaceIDs { - if id == workspaceID { - group.workspaceIDs = append(group.workspaceIDs[:i], group.workspaceIDs[i+1:]...) - break - } - } - m.updateState() - }) - }) - - handle.SetRemovedHandler(func(e ext_workspace.ExtWorkspaceGroupHandleV1RemovedEvent) { - log.Debugf("ExtWorkspace: Group %d removed", groupID) - - m.post(func() { - group.removed = true - - m.groups.Delete(groupID) - - m.wlMutex.Lock() - handle.Destroy() - m.wlMutex.Unlock() - - m.updateState() - }) - }) -} - -func (m *Manager) handleWorkspace(e ext_workspace.ExtWorkspaceManagerV1WorkspaceEvent) { - handle := e.Workspace - workspaceID := handle.ID() - - log.Debugf("ExtWorkspace: New workspace (proxy_id=%d)", workspaceID) - - ws := &workspaceState{ - id: workspaceID, - handle: handle, - coordinates: make([]uint32, 0), - } - - m.workspaces.Store(workspaceID, ws) - - handle.SetIdHandler(func(e ext_workspace.ExtWorkspaceHandleV1IdEvent) { - log.Debugf("ExtWorkspace: Workspace %d id: %s", workspaceID, e.Id) - m.post(func() { - ws.workspaceID = e.Id - m.updateState() - }) - }) - - handle.SetNameHandler(func(e ext_workspace.ExtWorkspaceHandleV1NameEvent) { - log.Debugf("ExtWorkspace: Workspace %d name: %s", workspaceID, e.Name) - m.post(func() { - ws.name = e.Name - m.updateState() - }) - }) - - handle.SetCoordinatesHandler(func(e ext_workspace.ExtWorkspaceHandleV1CoordinatesEvent) { - coords := make([]uint32, 0) - for i := 0; i < len(e.Coordinates); i += 4 { - if i+4 <= len(e.Coordinates) { - val := uint32(e.Coordinates[i]) | - uint32(e.Coordinates[i+1])<<8 | - uint32(e.Coordinates[i+2])<<16 | - uint32(e.Coordinates[i+3])<<24 - coords = append(coords, val) - } - } - log.Debugf("ExtWorkspace: Workspace %d coordinates: %v", workspaceID, coords) - m.post(func() { - ws.coordinates = coords - m.updateState() - }) - }) - - handle.SetStateHandler(func(e ext_workspace.ExtWorkspaceHandleV1StateEvent) { - log.Debugf("ExtWorkspace: Workspace %d state: %d", workspaceID, e.State) - m.post(func() { - ws.state = e.State - m.updateState() - }) - }) - - handle.SetCapabilitiesHandler(func(e ext_workspace.ExtWorkspaceHandleV1CapabilitiesEvent) { - log.Debugf("ExtWorkspace: Workspace %d capabilities: %d", workspaceID, e.Capabilities) - }) - - handle.SetRemovedHandler(func(e ext_workspace.ExtWorkspaceHandleV1RemovedEvent) { - log.Debugf("ExtWorkspace: Workspace %d removed", workspaceID) - - m.post(func() { - ws.removed = true - - m.workspaces.Delete(workspaceID) - - m.wlMutex.Lock() - handle.Destroy() - m.wlMutex.Unlock() - - m.updateState() - }) - }) -} - -func (m *Manager) updateState() { - groups := make([]*WorkspaceGroup, 0) - - m.groups.Range(func(key uint32, group *workspaceGroupState) bool { - if group.removed { - return true - } - - outputs := make([]string, 0) - for outputID := range group.outputIDs { - if name, ok := m.outputNames.Load(outputID); ok && name != "" { - outputs = append(outputs, name) - } - } - - workspaces := make([]*Workspace, 0) - for _, wsID := range group.workspaceIDs { - ws, exists := m.workspaces.Load(wsID) - if !exists { - continue - } - if ws.removed { - continue - } - - workspace := &Workspace{ - ID: ws.workspaceID, - Name: ws.name, - Coordinates: ws.coordinates, - State: ws.state, - Active: ws.state&uint32(ext_workspace.ExtWorkspaceHandleV1StateActive) != 0, - Urgent: ws.state&uint32(ext_workspace.ExtWorkspaceHandleV1StateUrgent) != 0, - Hidden: ws.state&uint32(ext_workspace.ExtWorkspaceHandleV1StateHidden) != 0, - } - workspaces = append(workspaces, workspace) - } - - groupState := &WorkspaceGroup{ - ID: fmt.Sprintf("group-%d", group.id), - Outputs: outputs, - Workspaces: workspaces, - } - groups = append(groups, groupState) - return true - }) - - newState := State{ - Groups: groups, - } - - m.stateMutex.Lock() - m.state = &newState - m.stateMutex.Unlock() - - m.notifySubscribers() -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - - currentState := m.GetState() - - if m.lastNotified != nil && !stateChanged(m.lastNotified, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- currentState: - default: - log.Warn("ExtWorkspace: subscriber channel full, dropping update") - } - return true - }) - - stateCopy := currentState - m.lastNotified = &stateCopy - pending = false - } - } -} - -func (m *Manager) ActivateWorkspace(groupID, workspaceID string) error { - errChan := make(chan error, 1) - - m.post(func() { - var targetGroupID uint32 - if groupID != "" { - var parsedID uint32 - if _, err := fmt.Sscanf(groupID, "group-%d", &parsedID); err == nil { - targetGroupID = parsedID - } - } - - var found bool - m.workspaces.Range(func(key uint32, ws *workspaceState) bool { - if targetGroupID != 0 && ws.groupID != targetGroupID { - return true - } - if ws.workspaceID == workspaceID || ws.name == workspaceID { - m.wlMutex.Lock() - err := ws.handle.Activate() - if err == nil { - err = m.manager.Commit() - } - m.wlMutex.Unlock() - errChan <- err - found = true - return false - } - return true - }) - - if !found { - errChan <- fmt.Errorf("workspace not found: %s in group %s", workspaceID, groupID) - } - }) - - return <-errChan -} - -func (m *Manager) DeactivateWorkspace(groupID, workspaceID string) error { - errChan := make(chan error, 1) - - m.post(func() { - var targetGroupID uint32 - if groupID != "" { - var parsedID uint32 - if _, err := fmt.Sscanf(groupID, "group-%d", &parsedID); err == nil { - targetGroupID = parsedID - } - } - - var found bool - m.workspaces.Range(func(key uint32, ws *workspaceState) bool { - if targetGroupID != 0 && ws.groupID != targetGroupID { - return true - } - if ws.workspaceID == workspaceID || ws.name == workspaceID { - m.wlMutex.Lock() - err := ws.handle.Deactivate() - if err == nil { - err = m.manager.Commit() - } - m.wlMutex.Unlock() - errChan <- err - found = true - return false - } - return true - }) - - if !found { - errChan <- fmt.Errorf("workspace not found: %s in group %s", workspaceID, groupID) - } - }) - - return <-errChan -} - -func (m *Manager) RemoveWorkspace(groupID, workspaceID string) error { - errChan := make(chan error, 1) - - m.post(func() { - var targetGroupID uint32 - if groupID != "" { - var parsedID uint32 - if _, err := fmt.Sscanf(groupID, "group-%d", &parsedID); err == nil { - targetGroupID = parsedID - } - } - - var found bool - m.workspaces.Range(func(key uint32, ws *workspaceState) bool { - if targetGroupID != 0 && ws.groupID != targetGroupID { - return true - } - if ws.workspaceID == workspaceID || ws.name == workspaceID { - m.wlMutex.Lock() - err := ws.handle.Remove() - if err == nil { - err = m.manager.Commit() - } - m.wlMutex.Unlock() - errChan <- err - found = true - return false - } - return true - }) - - if !found { - errChan <- fmt.Errorf("workspace not found: %s in group %s", workspaceID, groupID) - } - }) - - return <-errChan -} - -func (m *Manager) CreateWorkspace(groupID, workspaceName string) error { - errChan := make(chan error, 1) - - m.post(func() { - var found bool - m.groups.Range(func(key uint32, group *workspaceGroupState) bool { - if fmt.Sprintf("group-%d", group.id) == groupID { - m.wlMutex.Lock() - err := group.handle.CreateWorkspace(workspaceName) - if err == nil { - err = m.manager.Commit() - } - m.wlMutex.Unlock() - errChan <- err - found = true - return false - } - return true - }) - - if !found { - errChan <- fmt.Errorf("workspace group not found: %s", groupID) - } - }) - - return <-errChan -} - -func (m *Manager) Close() { - close(m.stopChan) - m.wg.Wait() - m.notifierWg.Wait() - - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - m.workspaces.Range(func(key uint32, ws *workspaceState) bool { - if ws.handle != nil { - ws.handle.Destroy() - } - m.workspaces.Delete(key) - return true - }) - - m.groups.Range(func(key uint32, group *workspaceGroupState) bool { - if group.handle != nil { - group.handle.Destroy() - } - m.groups.Delete(key) - return true - }) - - if m.manager != nil { - m.manager.Stop() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/manager_test.go deleted file mode 100644 index b20d457..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/manager_test.go +++ /dev/null @@ -1,392 +0,0 @@ -package extworkspace - -import ( - "errors" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - mocks_wlclient "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/wlclient" -) - -func TestStateChanged_BothNil(t *testing.T) { - assert.True(t, stateChanged(nil, nil)) -} - -func TestStateChanged_OneNil(t *testing.T) { - s := &State{Groups: []*WorkspaceGroup{}} - assert.True(t, stateChanged(s, nil)) - assert.True(t, stateChanged(nil, s)) -} - -func TestStateChanged_GroupCountDiffers(t *testing.T) { - a := &State{Groups: []*WorkspaceGroup{{ID: "group-1"}}} - b := &State{Groups: []*WorkspaceGroup{}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_GroupIDDiffers(t *testing.T) { - a := &State{Groups: []*WorkspaceGroup{{ID: "group-1", Outputs: []string{}, Workspaces: []*Workspace{}}}} - b := &State{Groups: []*WorkspaceGroup{{ID: "group-2", Outputs: []string{}, Workspaces: []*Workspace{}}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputCountDiffers(t *testing.T) { - a := &State{Groups: []*WorkspaceGroup{{ID: "group-1", Outputs: []string{"eDP-1"}, Workspaces: []*Workspace{}}}} - b := &State{Groups: []*WorkspaceGroup{{ID: "group-1", Outputs: []string{}, Workspaces: []*Workspace{}}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputNameDiffers(t *testing.T) { - a := &State{Groups: []*WorkspaceGroup{{ID: "group-1", Outputs: []string{"eDP-1"}, Workspaces: []*Workspace{}}}} - b := &State{Groups: []*WorkspaceGroup{{ID: "group-1", Outputs: []string{"HDMI-A-1"}, Workspaces: []*Workspace{}}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_WorkspaceCountDiffers(t *testing.T) { - a := &State{Groups: []*WorkspaceGroup{{ - ID: "group-1", - Outputs: []string{}, - Workspaces: []*Workspace{{ID: "1", Name: "ws1"}}, - }}} - b := &State{Groups: []*WorkspaceGroup{{ - ID: "group-1", - Outputs: []string{}, - Workspaces: []*Workspace{}, - }}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_WorkspaceFieldsDiffer(t *testing.T) { - a := &State{Groups: []*WorkspaceGroup{{ - ID: "group-1", - Outputs: []string{}, - Workspaces: []*Workspace{{ - ID: "1", Name: "ws1", State: 0, Active: false, Urgent: false, Hidden: false, - }}, - }}} - b := &State{Groups: []*WorkspaceGroup{{ - ID: "group-1", - Outputs: []string{}, - Workspaces: []*Workspace{{ - ID: "2", Name: "ws1", State: 0, Active: false, Urgent: false, Hidden: false, - }}, - }}} - assert.True(t, stateChanged(a, b)) - - b.Groups[0].Workspaces[0].ID = "1" - b.Groups[0].Workspaces[0].Name = "ws2" - assert.True(t, stateChanged(a, b)) - - b.Groups[0].Workspaces[0].Name = "ws1" - b.Groups[0].Workspaces[0].State = 1 - assert.True(t, stateChanged(a, b)) - - b.Groups[0].Workspaces[0].State = 0 - b.Groups[0].Workspaces[0].Active = true - assert.True(t, stateChanged(a, b)) - - b.Groups[0].Workspaces[0].Active = false - b.Groups[0].Workspaces[0].Urgent = true - assert.True(t, stateChanged(a, b)) - - b.Groups[0].Workspaces[0].Urgent = false - b.Groups[0].Workspaces[0].Hidden = true - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_WorkspaceCoordinatesDiffer(t *testing.T) { - a := &State{Groups: []*WorkspaceGroup{{ - ID: "group-1", - Outputs: []string{}, - Workspaces: []*Workspace{{ - ID: "1", Name: "ws1", Coordinates: []uint32{0, 0}, - }}, - }}} - b := &State{Groups: []*WorkspaceGroup{{ - ID: "group-1", - Outputs: []string{}, - Workspaces: []*Workspace{{ - ID: "1", Name: "ws1", Coordinates: []uint32{1, 0}, - }}, - }}} - assert.True(t, stateChanged(a, b)) - - b.Groups[0].Workspaces[0].Coordinates = []uint32{0} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_Equal(t *testing.T) { - a := &State{Groups: []*WorkspaceGroup{{ - ID: "group-1", - Outputs: []string{"eDP-1", "HDMI-A-1"}, - Workspaces: []*Workspace{ - {ID: "1", Name: "ws1", Coordinates: []uint32{0, 0}, State: 1, Active: true}, - {ID: "2", Name: "ws2", Coordinates: []uint32{1, 0}, State: 0, Active: false}, - }, - }}} - b := &State{Groups: []*WorkspaceGroup{{ - ID: "group-1", - Outputs: []string{"eDP-1", "HDMI-A-1"}, - Workspaces: []*Workspace{ - {ID: "1", Name: "ws1", Coordinates: []uint32{0, 0}, State: 1, Active: true}, - {ID: "2", Name: "ws2", Coordinates: []uint32{1, 0}, State: 0, Active: false}, - }, - }}} - assert.False(t, stateChanged(a, b)) -} - -func TestManager_ConcurrentGetState(t *testing.T) { - m := &Manager{ - state: &State{ - Groups: []*WorkspaceGroup{{ID: "group-1", Outputs: []string{"eDP-1"}}}, - }, - } - - var wg sync.WaitGroup - const goroutines = 50 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - s := m.GetState() - _ = s.Groups - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.stateMutex.Lock() - m.state = &State{ - Groups: []*WorkspaceGroup{{ID: "group-1", Outputs: []string{"eDP-1"}}}, - } - m.stateMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentSubscriberAccess(t *testing.T) { - m := &Manager{ - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - var wg sync.WaitGroup - const goroutines = 20 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - subID := string(rune('a' + id)) - ch := m.Subscribe(subID) - assert.NotNil(t, ch) - time.Sleep(time.Millisecond) - m.Unsubscribe(subID) - }(i) - } - - wg.Wait() -} - -func TestManager_SyncmapGroupsConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - state := &workspaceGroupState{ - id: key, - outputIDs: map[uint32]bool{1: true}, - workspaceIDs: []uint32{uint32(j)}, - } - m.groups.Store(key, state) - - if loaded, ok := m.groups.Load(key); ok { - assert.Equal(t, key, loaded.id) - } - - m.groups.Range(func(k uint32, v *workspaceGroupState) bool { - _ = v.id - _ = v.outputIDs - return true - }) - } - - m.groups.Delete(key) - }(i) - } - - wg.Wait() -} - -func TestManager_SyncmapWorkspacesConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - state := &workspaceState{ - id: key, - workspaceID: "ws-1", - name: "workspace", - state: uint32(j % 4), - coordinates: []uint32{uint32(j), 0}, - } - m.workspaces.Store(key, state) - - if loaded, ok := m.workspaces.Load(key); ok { - assert.Equal(t, key, loaded.id) - } - - m.workspaces.Range(func(k uint32, v *workspaceState) bool { - _ = v.name - _ = v.state - return true - }) - } - - m.workspaces.Delete(key) - }(i) - } - - wg.Wait() -} - -func TestManager_SyncmapOutputNamesConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - m.outputNames.Store(key, "eDP-1") - - if loaded, ok := m.outputNames.Load(key); ok { - assert.NotEmpty(t, loaded) - } - - m.outputNames.Range(func(k uint32, v string) bool { - _ = v - return true - }) - } - - m.outputNames.Delete(key) - }(i) - } - - wg.Wait() -} - -func TestManager_NotifySubscribersNonBlocking(t *testing.T) { - m := &Manager{ - dirty: make(chan struct{}, 1), - } - - for i := 0; i < 10; i++ { - m.notifySubscribers() - } - - assert.Len(t, m.dirty, 1) -} - -func TestManager_PostQueueFull(t *testing.T) { - m := &Manager{ - cmdq: make(chan cmd, 2), - stopChan: make(chan struct{}), - } - - m.post(func() {}) - m.post(func() {}) - m.post(func() {}) - m.post(func() {}) - - assert.Len(t, m.cmdq, 2) -} - -func TestManager_GetStateNilState(t *testing.T) { - m := &Manager{} - - s := m.GetState() - assert.NotNil(t, s.Groups) - assert.Empty(t, s.Groups) -} - -func TestWorkspace_Fields(t *testing.T) { - ws := Workspace{ - ID: "ws-1", - Name: "workspace 1", - Coordinates: []uint32{0, 0}, - State: 1, - Active: true, - Urgent: false, - Hidden: false, - } - - assert.Equal(t, "ws-1", ws.ID) - assert.Equal(t, "workspace 1", ws.Name) - assert.True(t, ws.Active) - assert.False(t, ws.Urgent) - assert.False(t, ws.Hidden) -} - -func TestWorkspaceGroup_Fields(t *testing.T) { - group := WorkspaceGroup{ - ID: "group-1", - Outputs: []string{"eDP-1", "HDMI-A-1"}, - Workspaces: []*Workspace{ - {ID: "ws-1", Name: "workspace 1"}, - }, - } - - assert.Equal(t, "group-1", group.ID) - assert.Len(t, group.Outputs, 2) - assert.Len(t, group.Workspaces, 1) -} - -func TestNewManager_GetRegistryError(t *testing.T) { - mockDisplay := mocks_wlclient.NewMockWaylandDisplay(t) - - mockDisplay.EXPECT().Context().Return(nil) - mockDisplay.EXPECT().GetRegistry().Return(nil, errors.New("failed to get registry")) - - _, err := NewManager(mockDisplay) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to get registry") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/types.go deleted file mode 100644 index 903a1cb..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/extworkspace/types.go +++ /dev/null @@ -1,169 +0,0 @@ -package extworkspace - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_workspace" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type Workspace struct { - ID string `json:"id"` - Name string `json:"name"` - Coordinates []uint32 `json:"coordinates"` - State uint32 `json:"state"` - Active bool `json:"active"` - Urgent bool `json:"urgent"` - Hidden bool `json:"hidden"` -} - -type WorkspaceGroup struct { - ID string `json:"id"` - Outputs []string `json:"outputs"` - Workspaces []*Workspace `json:"workspaces"` -} - -type State struct { - Groups []*WorkspaceGroup `json:"groups"` -} - -type cmd struct { - fn func() -} - -type Manager struct { - display wlclient.WaylandDisplay - ctx *wlclient.Context - registry *wlclient.Registry - manager *ext_workspace.ExtWorkspaceManagerV1 - - outputNames syncmap.Map[uint32, string] - - groups syncmap.Map[uint32, *workspaceGroupState] - - workspaces syncmap.Map[uint32, *workspaceState] - - wlMutex sync.Mutex - cmdq chan cmd - stopChan chan struct{} - wg sync.WaitGroup - - subscribers syncmap.Map[string, chan State] - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotified *State - - stateMutex sync.RWMutex - state *State -} - -type workspaceGroupState struct { - id uint32 - handle *ext_workspace.ExtWorkspaceGroupHandleV1 - outputIDs map[uint32]bool - workspaceIDs []uint32 - removed bool -} - -type workspaceState struct { - id uint32 - handle *ext_workspace.ExtWorkspaceHandleV1 - workspaceID string - name string - coordinates []uint32 - state uint32 - groupID uint32 - removed bool -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{ - Groups: []*WorkspaceGroup{}, - } - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - - m.subscribers.Store(id, ch) - - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if ch, ok := m.subscribers.LoadAndDelete(id); ok { - close(ch) - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func stateChanged(old, new *State) bool { - if old == nil || new == nil { - return true - } - if len(old.Groups) != len(new.Groups) { - return true - } - - for i, newGroup := range new.Groups { - if i >= len(old.Groups) { - return true - } - oldGroup := old.Groups[i] - if oldGroup.ID != newGroup.ID { - return true - } - if len(oldGroup.Outputs) != len(newGroup.Outputs) { - return true - } - for j, newOutput := range newGroup.Outputs { - if j >= len(oldGroup.Outputs) { - return true - } - if oldGroup.Outputs[j] != newOutput { - return true - } - } - if len(oldGroup.Workspaces) != len(newGroup.Workspaces) { - return true - } - for j, newWs := range newGroup.Workspaces { - if j >= len(oldGroup.Workspaces) { - return true - } - oldWs := oldGroup.Workspaces[j] - if oldWs.ID != newWs.ID || oldWs.Name != newWs.Name || oldWs.State != newWs.State { - return true - } - if oldWs.Active != newWs.Active || oldWs.Urgent != newWs.Urgent || oldWs.Hidden != newWs.Hidden { - return true - } - if len(oldWs.Coordinates) != len(newWs.Coordinates) { - return true - } - for k, coord := range newWs.Coordinates { - if k >= len(oldWs.Coordinates) { - return true - } - if oldWs.Coordinates[k] != coord { - return true - } - } - } - } - - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions.go deleted file mode 100644 index 52515d1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions.go +++ /dev/null @@ -1,112 +0,0 @@ -package freedesktop - -import ( - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/godbus/dbus/v5" -) - -func (m *Manager) SetIconFile(iconPath string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetIconFile", 0, iconPath).Err - if err != nil { - return fmt.Errorf("failed to set icon file: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetRealName(name string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetRealName", 0, name).Err - if err != nil { - return fmt.Errorf("failed to set real name: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetEmail(email string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetEmail", 0, email).Err - if err != nil { - return fmt.Errorf("failed to set email: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetLanguage(language string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetLanguage", 0, language).Err - if err != nil { - return fmt.Errorf("failed to set language: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetLocation(location string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetLocation", 0, location).Err - if err != nil { - return fmt.Errorf("failed to set location: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) GetUserIconFile(username string) (string, error) { - if m.systemConn == nil { - return "", fmt.Errorf("accounts service not available") - } - - accountsManager := m.systemConn.Object(dbusAccountsDest, dbus.ObjectPath(dbusAccountsPath)) - - var userPath dbus.ObjectPath - err := accountsManager.Call(dbusAccountsInterface+".FindUserByName", 0, username).Store(&userPath) - if err != nil { - return "", fmt.Errorf("user not found: %w", err) - } - - userObj := m.systemConn.Object(dbusAccountsDest, userPath) - variant, err := userObj.GetProperty(dbusAccountsUserInterface + ".IconFile") - if err != nil { - return "", err - } - - var iconFile string - if err := variant.Store(&iconFile); err != nil { - return "", err - } - - return iconFile, nil -} - -func (m *Manager) SetIconTheme(iconTheme string) error { - if err := utils.GsettingsSet("org.gnome.desktop.interface", "icon-theme", iconTheme); err != nil { - return fmt.Errorf("failed to set icon theme: %w", err) - } - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions_test.go deleted file mode 100644 index e2c21e8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package freedesktop - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_SetIconFile(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetIconFile("/path/to/icon.png") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetRealName(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetRealName("New Name") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetEmail(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetEmail("test@example.com") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetLanguage(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetLanguage("en_US.UTF-8") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetLocation(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetLocation("Test Location") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_GetUserIconFile(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - iconFile, err := manager.GetUserIconFile("testuser") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - assert.Empty(t, iconFile) - }) -} - -func TestManager_UpdateAccountsState(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.updateAccountsState() - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_UpdateSettingsState(t *testing.T) { - t.Run("settings not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.updateSettingsState() - assert.Error(t, err) - assert.Contains(t, err.Error(), "settings portal not available") - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/constants.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/constants.go deleted file mode 100644 index 12db87b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/constants.go +++ /dev/null @@ -1,23 +0,0 @@ -package freedesktop - -const ( - dbusAccountsDest = "org.freedesktop.Accounts" - dbusAccountsPath = "/org/freedesktop/Accounts" - dbusAccountsInterface = "org.freedesktop.Accounts" - dbusAccountsUserInterface = "org.freedesktop.Accounts.User" - - dbusPortalDest = "org.freedesktop.portal.Desktop" - dbusPortalPath = "/org/freedesktop/portal/desktop" - dbusPortalSettingsInterface = "org.freedesktop.portal.Settings" - - dbusPropsInterface = "org.freedesktop.DBus.Properties" - - dbusScreensaverName = "org.freedesktop.ScreenSaver" - dbusScreensaverPath = "/ScreenSaver" - dbusScreensaverPath2 = "/org/freedesktop/ScreenSaver" - dbusScreensaverInterface = "org.freedesktop.ScreenSaver" - - dbusGnomeScreensaverName = "org.gnome.ScreenSaver" - dbusGnomeScreensaverPath = "/org/gnome/ScreenSaver" - dbusGnomeScreensaverInterface = "org.gnome.ScreenSaver" -) diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers.go deleted file mode 100644 index c3090fa..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers.go +++ /dev/null @@ -1,154 +0,0 @@ -package freedesktop - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - switch req.Method { - case "freedesktop.getState": - handleGetState(conn, req, manager) - case "freedesktop.accounts.setIconFile": - handleSetIconFile(conn, req, manager) - case "freedesktop.accounts.setRealName": - handleSetRealName(conn, req, manager) - case "freedesktop.accounts.setEmail": - handleSetEmail(conn, req, manager) - case "freedesktop.accounts.setLanguage": - handleSetLanguage(conn, req, manager) - case "freedesktop.accounts.setLocation": - handleSetLocation(conn, req, manager) - case "freedesktop.accounts.getUserIconFile": - handleGetUserIconFile(conn, req, manager) - case "freedesktop.settings.getColorScheme": - handleGetColorScheme(conn, req, manager) - case "freedesktop.settings.setIconTheme": - handleSetIconTheme(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleSetIconFile(conn net.Conn, req models.Request, manager *Manager) { - iconPath, err := params.String(req.Params, "path") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetIconFile(iconPath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "icon file set"}) -} - -func handleSetRealName(conn net.Conn, req models.Request, manager *Manager) { - name, err := params.String(req.Params, "name") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetRealName(name); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "real name set"}) -} - -func handleSetEmail(conn net.Conn, req models.Request, manager *Manager) { - email, err := params.String(req.Params, "email") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetEmail(email); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "email set"}) -} - -func handleSetLanguage(conn net.Conn, req models.Request, manager *Manager) { - language, err := params.String(req.Params, "language") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetLanguage(language); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "language set"}) -} - -func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) { - location, err := params.String(req.Params, "location") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetLocation(location); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "location set"}) -} - -func handleGetUserIconFile(conn net.Conn, req models.Request, manager *Manager) { - username, err := params.String(req.Params, "username") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - iconFile, err := manager.GetUserIconFile(username) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Value: iconFile}) -} - -func handleGetColorScheme(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.updateSettingsState(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - state := manager.GetState() - models.Respond(conn, req.ID, map[string]uint32{"colorScheme": state.Settings.ColorScheme}) -} - -func handleSetIconTheme(conn net.Conn, req models.Request, manager *Manager) { - iconTheme, err := params.String(req.Params, "iconTheme") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetIconTheme(iconTheme); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "icon theme set"}) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers_test.go deleted file mode 100644 index f2cb5a2..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers_test.go +++ /dev/null @@ -1,581 +0,0 @@ -package freedesktop - -import ( - "bytes" - "encoding/json" - "net" - "sync" - "testing" - - mockdbus "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -type mockNetConn struct { - net.Conn - readBuf *bytes.Buffer - writeBuf *bytes.Buffer - closed bool -} - -func newMockNetConn() *mockNetConn { - return &mockNetConn{ - readBuf: &bytes.Buffer{}, - writeBuf: &bytes.Buffer{}, - } -} - -func (m *mockNetConn) Read(b []byte) (n int, err error) { - return m.readBuf.Read(b) -} - -func (m *mockNetConn) Write(b []byte) (n int, err error) { - return m.writeBuf.Write(b) -} - -func (m *mockNetConn) Close() error { - m.closed = true - return nil -} - -func mockGetAllAccountsProperties() *dbus.Call { - props := map[string]dbus.Variant{ - "IconFile": dbus.MakeVariant("/path/to/icon.png"), - "RealName": dbus.MakeVariant("Test"), - "UserName": dbus.MakeVariant("test"), - "AccountType": dbus.MakeVariant(int32(0)), - "HomeDirectory": dbus.MakeVariant("/home/test"), - "Shell": dbus.MakeVariant("/bin/bash"), - "Email": dbus.MakeVariant(""), - "Language": dbus.MakeVariant(""), - "Location": dbus.MakeVariant(""), - "Locked": dbus.MakeVariant(false), - "PasswordMode": dbus.MakeVariant(int32(1)), - } - return &dbus.Call{Err: nil, Body: []any{props}} -} - -func TestRespondError_Freedesktop(t *testing.T) { - conn := newMockNetConn() - models.RespondError(conn, 123, "test error") - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Equal(t, "test error", resp.Error) - assert.Nil(t, resp.Result) -} - -func TestRespond_Freedesktop(t *testing.T) { - conn := newMockNetConn() - result := models.SuccessResult{Success: true, Message: "test"} - models.Respond(conn, 123, result) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "test", resp.Result.Message) -} - -func TestHandleGetState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - RealName: "Test User", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "freedesktop.getState"} - - handleGetState(conn, req, manager) - - var resp models.Response[FreedeskState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Accounts.Available) - assert.Equal(t, "testuser", resp.Result.Accounts.UserName) - assert.True(t, resp.Result.Settings.Available) - assert.Equal(t, uint32(1), resp.Result.Settings.ColorScheme) -} - -func TestHandleSetIconFile(t *testing.T) { - t.Run("missing path parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]any{}, - } - - handleSetIconFile(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'path' parameter") - }) - - t.Run("successful set icon file", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetIconFile", dbus.Flags(0), "/path/to/icon.png").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]any{ - "path": "/path/to/icon.png", - }, - } - - handleSetIconFile(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "icon file set", resp.Result.Message) - }) - - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]any{ - "path": "/path/to/icon.png", - }, - } - - handleSetIconFile(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "accounts service not available") - }) -} - -func TestHandleSetRealName(t *testing.T) { - t.Run("missing name parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setRealName", - Params: map[string]any{}, - } - - handleSetRealName(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'name' parameter") - }) - - t.Run("successful set real name", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetRealName", dbus.Flags(0), "New Name").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setRealName", - Params: map[string]any{ - "name": "New Name", - }, - } - - handleSetRealName(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "real name set", resp.Result.Message) - }) -} - -func TestHandleSetEmail(t *testing.T) { - t.Run("missing email parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setEmail", - Params: map[string]any{}, - } - - handleSetEmail(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'email' parameter") - }) - - t.Run("successful set email", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetEmail", dbus.Flags(0), "test@example.com").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setEmail", - Params: map[string]any{ - "email": "test@example.com", - }, - } - - handleSetEmail(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "email set", resp.Result.Message) - }) -} - -func TestHandleSetLanguage(t *testing.T) { - t.Run("missing language parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setLanguage", - Params: map[string]any{}, - } - - handleSetLanguage(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'language' parameter") - }) -} - -func TestHandleSetLocation(t *testing.T) { - t.Run("missing location parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setLocation", - Params: map[string]any{}, - } - - handleSetLocation(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'location' parameter") - }) -} - -func TestHandleGetUserIconFile(t *testing.T) { - t.Run("missing username parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.getUserIconFile", - Params: map[string]any{}, - } - - handleGetUserIconFile(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'username' parameter") - }) - - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.getUserIconFile", - Params: map[string]any{ - "username": "testuser", - }, - } - - handleGetUserIconFile(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "accounts service not available") - }) -} - -func TestHandleGetColorScheme(t *testing.T) { - t.Run("settings not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "freedesktop.settings.getColorScheme"} - - handleGetColorScheme(conn, req, manager) - - var resp models.Response[map[string]uint32] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "settings portal not available") - }) - - t.Run("successful get color scheme", func(t *testing.T) { - mockSettingsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{ - Err: nil, - Body: []any{dbus.MakeVariant(uint32(1))}, - } - mockSettingsObj.EXPECT().Call("org.freedesktop.portal.Settings.ReadOne", dbus.Flags(0), "org.freedesktop.appearance", "color-scheme").Return(mockCall) - - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - settingsObj: mockSettingsObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "freedesktop.settings.getColorScheme"} - - handleGetColorScheme(conn, req, manager) - - var resp models.Response[map[string]uint32] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Equal(t, uint32(1), (*resp.Result)["colorScheme"]) - }) -} - -func TestHandleRequest(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - }, - stateMutex: sync.RWMutex{}, - } - - t.Run("unknown method", func(t *testing.T) { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.unknown", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "unknown method") - }) - - t.Run("valid method - getState", func(t *testing.T) { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.getState", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[FreedeskState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - }) - - t.Run("all method routes", func(t *testing.T) { - tests := []string{ - "freedesktop.accounts.setIconFile", - "freedesktop.accounts.setRealName", - "freedesktop.accounts.setEmail", - "freedesktop.accounts.setLanguage", - "freedesktop.accounts.setLocation", - "freedesktop.accounts.getUserIconFile", - "freedesktop.settings.getColorScheme", - } - - for _, method := range tests { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: method, - Params: map[string]any{}, - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - // Will have errors due to missing params or service unavailable - // but the method routing should work - } - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager.go deleted file mode 100644 index 054f859..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager.go +++ /dev/null @@ -1,206 +0,0 @@ -package freedesktop - -import ( - "context" - "fmt" - "os" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil" - "github.com/godbus/dbus/v5" -) - -func NewManager() (*Manager, error) { - systemConn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - sessionConn, err := dbus.ConnectSessionBus() - if err != nil { - sessionConn = nil - } - - m := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{}, - Settings: SettingsState{}, - Screensaver: ScreensaverState{}, - }, - stateMutex: sync.RWMutex{}, - systemConn: systemConn, - sessionConn: sessionConn, - currentUID: uint64(os.Getuid()), - } - - m.initializeAccounts() - m.initializeSettings() - m.initializeScreensaver() - - return m, nil -} - -func (m *Manager) initializeAccounts() error { - accountsManager := m.systemConn.Object(dbusAccountsDest, dbus.ObjectPath(dbusAccountsPath)) - - var userPath dbus.ObjectPath - err := accountsManager.Call(dbusAccountsInterface+".FindUserById", 0, int64(m.currentUID)).Store(&userPath) - if err != nil { - m.stateMutex.Lock() - m.state.Accounts.Available = false - m.stateMutex.Unlock() - return err - } - - m.accountsObj = m.systemConn.Object(dbusAccountsDest, userPath) - - m.stateMutex.Lock() - m.state.Accounts.Available = true - m.state.Accounts.UserPath = string(userPath) - m.state.Accounts.UID = m.currentUID - m.stateMutex.Unlock() - - if err := m.updateAccountsState(); err != nil { - return fmt.Errorf("failed to update accounts state: %w", err) - } - - return nil -} - -func (m *Manager) initializeSettings() error { - if m.sessionConn == nil { - m.stateMutex.Lock() - m.state.Settings.Available = false - m.stateMutex.Unlock() - return fmt.Errorf("no session bus connection") - } - - m.settingsObj = m.sessionConn.Object(dbusPortalDest, dbus.ObjectPath(dbusPortalPath)) - - var variant dbus.Variant - err := m.settingsObj.Call(dbusPortalSettingsInterface+".ReadOne", 0, "org.freedesktop.appearance", "color-scheme").Store(&variant) - if err != nil { - m.stateMutex.Lock() - m.state.Settings.Available = false - m.stateMutex.Unlock() - return err - } - - m.stateMutex.Lock() - m.state.Settings.Available = true - m.stateMutex.Unlock() - - if err := m.updateSettingsState(); err != nil { - return fmt.Errorf("failed to update settings state: %w", err) - } - - return nil -} - -func (m *Manager) updateAccountsState() error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - ctx := context.Background() - props, err := m.getAccountProperties(ctx) - if err != nil { - return err - } - - m.stateMutex.Lock() - defer m.stateMutex.Unlock() - - m.state.Accounts.IconFile = dbusutil.GetOr(props, "IconFile", "") - m.state.Accounts.RealName = dbusutil.GetOr(props, "RealName", "") - m.state.Accounts.UserName = dbusutil.GetOr(props, "UserName", "") - m.state.Accounts.AccountType = dbusutil.GetOr(props, "AccountType", int32(0)) - m.state.Accounts.HomeDirectory = dbusutil.GetOr(props, "HomeDirectory", "") - m.state.Accounts.Shell = dbusutil.GetOr(props, "Shell", "") - m.state.Accounts.Email = dbusutil.GetOr(props, "Email", "") - m.state.Accounts.Language = dbusutil.GetOr(props, "Language", "") - m.state.Accounts.Location = dbusutil.GetOr(props, "Location", "") - m.state.Accounts.Locked = dbusutil.GetOr(props, "Locked", false) - m.state.Accounts.PasswordMode = dbusutil.GetOr(props, "PasswordMode", int32(0)) - - return nil -} - -func (m *Manager) updateSettingsState() error { - if !m.state.Settings.Available || m.settingsObj == nil { - return fmt.Errorf("settings portal not available") - } - - var variant dbus.Variant - err := m.settingsObj.Call(dbusPortalSettingsInterface+".ReadOne", 0, "org.freedesktop.appearance", "color-scheme").Store(&variant) - if err != nil { - return err - } - - if colorScheme, ok := dbusutil.As[uint32](variant); ok { - m.stateMutex.Lock() - m.state.Settings.ColorScheme = colorScheme - m.stateMutex.Unlock() - } - - return nil -} - -func (m *Manager) getAccountProperties(ctx context.Context) (map[string]dbus.Variant, error) { - var props map[string]dbus.Variant - err := m.accountsObj.CallWithContext(ctx, dbusPropsInterface+".GetAll", 0, dbusAccountsUserInterface).Store(&props) - if err != nil { - return nil, err - } - return props, nil -} - -func (m *Manager) GetState() FreedeskState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return *m.state -} - -func (m *Manager) Subscribe(id string) chan FreedeskState { - ch := make(chan FreedeskState, 64) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) NotifySubscribers() { - state := m.GetState() - m.subscribers.Range(func(key string, ch chan FreedeskState) bool { - select { - case ch <- state: - default: - } - return true - }) -} - -func (m *Manager) Close() { - m.subscribers.Range(func(key string, ch chan FreedeskState) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - m.screensaverSubscribers.Range(func(key string, ch chan ScreensaverState) bool { - close(ch) - m.screensaverSubscribers.Delete(key) - return true - }) - - if m.systemConn != nil { - m.systemConn.Close() - } - if m.sessionConn != nil { - m.sessionConn.Close() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager_test.go deleted file mode 100644 index b3617f5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package freedesktop - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetState(t *testing.T) { - state := &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - RealName: "Test User", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - } - - manager := &Manager{ - state: state, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.True(t, result.Accounts.Available) - assert.Equal(t, "testuser", result.Accounts.UserName) - assert.Equal(t, "Test User", result.Accounts.RealName) - assert.Equal(t, uint64(1000), result.Accounts.UID) - assert.True(t, result.Settings.Available) - assert.Equal(t, uint32(1), result.Settings.ColorScheme) -} - -func TestManager_GetState_ThreadSafe(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - }, - stateMutex: sync.RWMutex{}, - } - - done := make(chan bool) - for i := 0; i < 10; i++ { - go func() { - state := manager.GetState() - assert.True(t, state.Accounts.Available) - assert.Equal(t, "testuser", state.Accounts.UserName) - done <- true - }() - } - - for i := 0; i < 10; i++ { - <-done - } -} - -func TestManager_Close(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - systemConn: nil, - sessionConn: nil, - } - - assert.NotPanics(t, func() { - manager.Close() - }) -} - -func TestNewManager(t *testing.T) { - t.Run("attempts to create manager", func(t *testing.T) { - manager, err := NewManager() - if err != nil { - assert.Nil(t, manager) - } else { - assert.NotNil(t, manager) - assert.NotNil(t, manager.state) - assert.NotNil(t, manager.systemConn) - - manager.Close() - } - }) -} - -func TestManager_GetState_EmptyState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.False(t, result.Accounts.Available) - assert.Empty(t, result.Accounts.UserName) - assert.False(t, result.Settings.Available) - assert.Equal(t, uint32(0), result.Settings.ColorScheme) -} - -func TestManager_AccountsState_Modification(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - }, - stateMutex: sync.RWMutex{}, - } - - state := manager.GetState() - state.Accounts.UserName = "modifieduser" - - original := manager.GetState() - assert.Equal(t, "testuser", original.Accounts.UserName) -} - -func TestManager_SettingsState_Modification(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: true, - ColorScheme: 0, - }, - }, - stateMutex: sync.RWMutex{}, - } - - state := manager.GetState() - state.Settings.ColorScheme = 1 - - original := manager.GetState() - assert.Equal(t, uint32(0), original.Settings.ColorScheme) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver.go deleted file mode 100644 index 095a4c0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver.go +++ /dev/null @@ -1,349 +0,0 @@ -package freedesktop - -import ( - "fmt" - "path/filepath" - "strings" - "sync/atomic" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" - "github.com/godbus/dbus/v5/introspect" -) - -type screensaverHandler struct { - manager *Manager -} - -func screensaverIntrospectIface(ifaceName string) introspect.Interface { - return introspect.Interface{ - Name: ifaceName, - Methods: []introspect.Method{ - { - Name: "Inhibit", - Args: []introspect.Arg{ - {Name: "application_name", Type: "s", Direction: "in"}, - {Name: "reason_for_inhibit", Type: "s", Direction: "in"}, - {Name: "cookie", Type: "u", Direction: "out"}, - }, - }, - { - Name: "UnInhibit", - Args: []introspect.Arg{ - {Name: "cookie", Type: "u", Direction: "in"}, - }, - }, - { - Name: "GetActive", - Args: []introspect.Arg{ - {Name: "active", Type: "b", Direction: "out"}, - }, - }, - { - Name: "SetActive", - Args: []introspect.Arg{ - {Name: "active", Type: "b", Direction: "in"}, - }, - }, - { - Name: "Lock", - }, - }, - Signals: []introspect.Signal{ - { - Name: "ActiveChanged", - Args: []introspect.Arg{ - {Name: "new_value", Type: "b"}, - }, - }, - }, - } -} - -func (m *Manager) initializeScreensaver() error { - if m.sessionConn == nil { - m.stateMutex.Lock() - m.state.Screensaver.Available = false - m.stateMutex.Unlock() - return nil - } - - handler := &screensaverHandler{manager: m} - - m.screensaverFreedesktopClaimed = m.claimScreensaverName(handler, - dbusScreensaverName, dbusScreensaverInterface, dbusScreensaverPath, dbusScreensaverPath2) - m.screensaverGnomeClaimed = m.claimScreensaverName(handler, - dbusGnomeScreensaverName, dbusGnomeScreensaverInterface, dbusGnomeScreensaverPath) - - if !m.screensaverFreedesktopClaimed && !m.screensaverGnomeClaimed { - log.Warn("No screensaver interface could be claimed") - m.stateMutex.Lock() - m.state.Screensaver.Available = false - m.stateMutex.Unlock() - return nil - } - - go m.watchPeerDisconnects() - - m.stateMutex.Lock() - m.state.Screensaver.Available = true - m.state.Screensaver.Active = false - m.state.Screensaver.Inhibited = false - m.state.Screensaver.Inhibitors = []ScreensaverInhibitor{} - m.stateMutex.Unlock() - - log.Info("Screensaver listener initialized") - return nil -} - -func (m *Manager) claimScreensaverName(handler *screensaverHandler, name, iface string, paths ...dbus.ObjectPath) bool { - reply, err := m.sessionConn.RequestName(name, dbus.NameFlagDoNotQueue) - if err != nil { - log.Warnf("Failed to request screensaver name %s: %v", name, err) - return false - } - if reply != dbus.RequestNameReplyPrimaryOwner { - log.Warnf("Screensaver name %s already owned by another process", name) - return false - } - if err := m.exportScreensaverOnPaths(handler, iface, paths...); err != nil { - log.Warnf("Failed to export screensaver on %s: %v", name, err) - return false - } - log.Infof("Claimed %s on session bus", name) - return true -} - -// exportScreensaverOnPaths exports the handler and introspection on the given -// paths under the specified interface name. -func (m *Manager) exportScreensaverOnPaths(handler *screensaverHandler, ifaceName string, paths ...dbus.ObjectPath) error { - iface := screensaverIntrospectIface(ifaceName) - for _, path := range paths { - if err := m.sessionConn.Export(handler, path, ifaceName); err != nil { - return fmt.Errorf("export handler on %s: %w", path, err) - } - node := &introspect.Node{ - Name: string(path), - Interfaces: []introspect.Interface{ - introspect.IntrospectData, - iface, - }, - } - if err := m.sessionConn.Export(introspect.NewIntrospectable(node), path, "org.freedesktop.DBus.Introspectable"); err != nil { - log.Warnf("Failed to export introspectable on %s: %v", path, err) - } - } - return nil -} - -func (h *screensaverHandler) Inhibit(sender dbus.Sender, appName, reason string) (uint32, *dbus.Error) { - if appName == "" { - return 0, dbus.NewError("org.freedesktop.DBus.Error.InvalidArgs", []any{"application name required"}) - } - - if reason == "" { - return 0, dbus.NewError("org.freedesktop.DBus.Error.InvalidArgs", []any{"reason required"}) - } - - if strings.Contains(strings.ToLower(reason), "audio") && !strings.Contains(strings.ToLower(reason), "video") { - log.Debugf("Ignoring audio-only inhibit from %s: %s", appName, reason) - return 0, nil - } - - if idx := strings.LastIndex(appName, "/"); idx != -1 && idx < len(appName)-1 { - appName = appName[idx+1:] - } - appName = filepath.Base(appName) - - cookie := atomic.AddUint32(&h.manager.screensaverCookieCounter, 1) - - inhibitor := ScreensaverInhibitor{ - Cookie: cookie, - AppName: appName, - Reason: reason, - Peer: string(sender), - StartTime: time.Now().Unix(), - } - - h.manager.stateMutex.Lock() - h.manager.state.Screensaver.Inhibitors = append(h.manager.state.Screensaver.Inhibitors, inhibitor) - h.manager.state.Screensaver.Inhibited = len(h.manager.state.Screensaver.Inhibitors) > 0 - h.manager.stateMutex.Unlock() - - log.Infof("Screensaver inhibited by %s (%s): %s -> cookie %08X", appName, sender, reason, cookie) - - h.manager.NotifyScreensaverSubscribers() - - return cookie, nil -} - -func (h *screensaverHandler) UnInhibit(sender dbus.Sender, cookie uint32) *dbus.Error { - h.manager.stateMutex.Lock() - defer h.manager.stateMutex.Unlock() - - found := false - inhibitors := h.manager.state.Screensaver.Inhibitors - for i, inh := range inhibitors { - if inh.Cookie != cookie { - continue - } - log.Infof("Screensaver uninhibited by %s (%s) cookie %08X", inh.AppName, sender, cookie) - h.manager.state.Screensaver.Inhibitors = append(inhibitors[:i], inhibitors[i+1:]...) - found = true - break - } - - if !found { - log.Debugf("UnInhibit: no match for cookie %08X", cookie) - return nil - } - - h.manager.state.Screensaver.Inhibited = len(h.manager.state.Screensaver.Inhibitors) > 0 - - go h.manager.NotifyScreensaverSubscribers() - - return nil -} - -func (m *Manager) watchPeerDisconnects() { - if m.sessionConn == nil { - return - } - - if err := m.sessionConn.AddMatchSignal( - dbus.WithMatchInterface("org.freedesktop.DBus"), - dbus.WithMatchMember("NameOwnerChanged"), - ); err != nil { - log.Warnf("Failed to watch peer disconnects: %v", err) - return - } - - signals := make(chan *dbus.Signal, 64) - m.sessionConn.Signal(signals) - - for sig := range signals { - if sig.Name != "org.freedesktop.DBus.NameOwnerChanged" { - continue - } - if len(sig.Body) < 3 { - continue - } - - name, ok1 := sig.Body[0].(string) - newOwner, ok2 := sig.Body[2].(string) - if !ok1 || !ok2 { - continue - } - if newOwner != "" { - continue - } - - m.removeInhibitorsByPeer(name) - } -} - -func (m *Manager) removeInhibitorsByPeer(peer string) { - m.stateMutex.Lock() - defer m.stateMutex.Unlock() - - var remaining []ScreensaverInhibitor - var removed []ScreensaverInhibitor - for _, inh := range m.state.Screensaver.Inhibitors { - if inh.Peer == peer { - removed = append(removed, inh) - continue - } - remaining = append(remaining, inh) - } - - if len(removed) == 0 { - return - } - - for _, inh := range removed { - log.Infof("Screensaver: peer %s died, removing inhibitor from %s (cookie %08X)", peer, inh.AppName, inh.Cookie) - } - - m.state.Screensaver.Inhibitors = remaining - m.state.Screensaver.Inhibited = len(remaining) > 0 - - go m.NotifyScreensaverSubscribers() -} - -func (m *Manager) GetScreensaverState() ScreensaverState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return m.state.Screensaver -} - -func (m *Manager) SubscribeScreensaver(id string) chan ScreensaverState { - ch := make(chan ScreensaverState, 64) - m.screensaverSubscribers.Store(id, ch) - return ch -} - -func (m *Manager) UnsubscribeScreensaver(id string) { - if val, ok := m.screensaverSubscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) NotifyScreensaverSubscribers() { - state := m.GetScreensaverState() - m.screensaverSubscribers.Range(func(key string, ch chan ScreensaverState) bool { - select { - case ch <- state: - default: - } - return true - }) -} - -func (h *screensaverHandler) GetActive() (bool, *dbus.Error) { - h.manager.stateMutex.RLock() - active := h.manager.state.Screensaver.Active - h.manager.stateMutex.RUnlock() - return active, nil -} - -func (h *screensaverHandler) SetActive(active bool) *dbus.Error { - h.manager.SetScreenLockActive(active) - return nil -} - -func (h *screensaverHandler) Lock() *dbus.Error { - h.manager.SetScreenLockActive(true) - return nil -} - -func (m *Manager) SetScreenLockActive(active bool) { - m.stateMutex.Lock() - changed := m.state.Screensaver.Active != active - m.state.Screensaver.Active = active - m.stateMutex.Unlock() - - if !changed { - return - } - - log.Infof("Screen lock active changed: %v", active) - defer m.NotifyScreensaverSubscribers() - - if m.sessionConn == nil { - return - } - if m.screensaverFreedesktopClaimed { - if err := m.sessionConn.Emit(dbusScreensaverPath, dbusScreensaverInterface+".ActiveChanged", active); err != nil { - log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusScreensaverPath, err) - } - if err := m.sessionConn.Emit(dbusScreensaverPath2, dbusScreensaverInterface+".ActiveChanged", active); err != nil { - log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusScreensaverPath2, err) - } - } - if m.screensaverGnomeClaimed { - if err := m.sessionConn.Emit(dbusGnomeScreensaverPath, dbusGnomeScreensaverInterface+".ActiveChanged", active); err != nil { - log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusGnomeScreensaverPath, err) - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver_test.go deleted file mode 100644 index 324c17e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package freedesktop - -import ( - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestSetScreenLockActive_ChangesState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true}, - }, - stateMutex: sync.RWMutex{}, - } - - assert.False(t, manager.GetScreensaverState().Active) - - manager.SetScreenLockActive(true) - assert.True(t, manager.GetScreensaverState().Active) - - manager.SetScreenLockActive(false) - assert.False(t, manager.GetScreensaverState().Active) -} - -func TestSetScreenLockActive_NoChangeNoDuplicate(t *testing.T) { - ch := make(chan ScreensaverState, 64) - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true, Active: false}, - }, - stateMutex: sync.RWMutex{}, - } - manager.screensaverSubscribers.Store("test", ch) - defer manager.screensaverSubscribers.Delete("test") - - // Setting to same value should not notify - manager.SetScreenLockActive(false) - - select { - case <-ch: - t.Fatal("should not have received notification for no-change") - case <-time.After(50 * time.Millisecond): - // Expected: no notification - } -} - -func TestSetScreenLockActive_NotifiesSubscribers(t *testing.T) { - ch := make(chan ScreensaverState, 64) - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true, Active: false}, - }, - stateMutex: sync.RWMutex{}, - } - manager.screensaverSubscribers.Store("test", ch) - defer manager.screensaverSubscribers.Delete("test") - - manager.SetScreenLockActive(true) - - select { - case state := <-ch: - assert.True(t, state.Active) - case <-time.After(time.Second): - t.Fatal("timeout waiting for subscriber notification") - } -} - -func TestSetScreenLockActive_NilSessionConn(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true}, - }, - stateMutex: sync.RWMutex{}, - } - - assert.NotPanics(t, func() { - manager.SetScreenLockActive(true) - }) - assert.True(t, manager.GetScreensaverState().Active) -} - -func TestGetActive_ReturnsCurrentState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true, Active: true}, - }, - stateMutex: sync.RWMutex{}, - } - - handler := &screensaverHandler{manager: manager} - active, dbusErr := handler.GetActive() - assert.Nil(t, dbusErr) - assert.True(t, active) -} - -func TestScreensaverState_ActiveDefaultsFalse(t *testing.T) { - state := ScreensaverState{} - assert.False(t, state.Active) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types.go deleted file mode 100644 index 0a9517a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types.go +++ /dev/null @@ -1,66 +0,0 @@ -package freedesktop - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/godbus/dbus/v5" -) - -type AccountsState struct { - Available bool `json:"available"` - UserPath string `json:"userPath"` - IconFile string `json:"iconFile"` - RealName string `json:"realName"` - UserName string `json:"userName"` - AccountType int32 `json:"accountType"` - HomeDirectory string `json:"homeDirectory"` - Shell string `json:"shell"` - Email string `json:"email"` - Language string `json:"language"` - Location string `json:"location"` - Locked bool `json:"locked"` - PasswordMode int32 `json:"passwordMode"` - UID uint64 `json:"uid"` -} - -type SettingsState struct { - Available bool `json:"available"` - ColorScheme uint32 `json:"colorScheme"` -} - -type ScreensaverInhibitor struct { - Cookie uint32 `json:"cookie"` - AppName string `json:"appName"` - Reason string `json:"reason"` - Peer string `json:"peer"` - StartTime int64 `json:"startTime"` -} - -type ScreensaverState struct { - Available bool `json:"available"` - Active bool `json:"active"` - Inhibited bool `json:"inhibited"` - Inhibitors []ScreensaverInhibitor `json:"inhibitors"` -} - -type FreedeskState struct { - Accounts AccountsState `json:"accounts"` - Settings SettingsState `json:"settings"` - Screensaver ScreensaverState `json:"screensaver"` -} - -type Manager struct { - state *FreedeskState - stateMutex sync.RWMutex - systemConn *dbus.Conn - sessionConn *dbus.Conn - accountsObj dbus.BusObject - settingsObj dbus.BusObject - currentUID uint64 - subscribers syncmap.Map[string, chan FreedeskState] - screensaverSubscribers syncmap.Map[string, chan ScreensaverState] - screensaverCookieCounter uint32 - screensaverFreedesktopClaimed bool - screensaverGnomeClaimed bool -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types_test.go deleted file mode 100644 index deb0327..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package freedesktop - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAccountsState_Struct(t *testing.T) { - state := AccountsState{ - Available: true, - UserPath: "/org/freedesktop/Accounts/User1000", - RealName: "Test User", - UserName: "testuser", - Locked: false, - UID: 1000, - } - - assert.True(t, state.Available) - assert.Equal(t, "/org/freedesktop/Accounts/User1000", state.UserPath) - assert.Equal(t, "Test User", state.RealName) - assert.Equal(t, "testuser", state.UserName) - assert.Equal(t, uint64(1000), state.UID) - assert.False(t, state.Locked) -} - -func TestSettingsState_Struct(t *testing.T) { - state := SettingsState{ - Available: true, - ColorScheme: 1, // Dark mode - } - - assert.True(t, state.Available) - assert.Equal(t, uint32(1), state.ColorScheme) -} - -func TestFreedeskState_Struct(t *testing.T) { - state := FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 0, // Light mode - }, - } - - assert.True(t, state.Accounts.Available) - assert.Equal(t, "testuser", state.Accounts.UserName) - assert.True(t, state.Settings.Available) - assert.Equal(t, uint32(0), state.Settings.ColorScheme) -} - -func TestAccountsState_DefaultValues(t *testing.T) { - state := AccountsState{} - - assert.False(t, state.Available) - assert.Empty(t, state.UserPath) - assert.Empty(t, state.UserName) - assert.Equal(t, uint64(0), state.UID) -} - -func TestSettingsState_DefaultValues(t *testing.T) { - state := SettingsState{} - - assert.False(t, state.Available) - assert.Equal(t, uint32(0), state.ColorScheme) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/handlers.go deleted file mode 100644 index c9d556c..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/handlers.go +++ /dev/null @@ -1,61 +0,0 @@ -package location - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -type LocationEvent struct { - Type string `json:"type"` - Data State `json:"data"` -} - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - switch req.Method { - case "location.getState": - handleGetState(conn, req, manager) - case "location.subscribe": - handleSubscribe(conn, req, manager) - - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := LocationEvent{ - Type: "state_changed", - Data: initialState, - } - - if err := json.NewEncoder(conn).Encode(models.Response[LocationEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := LocationEvent{ - Type: "state_changed", - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[LocationEvent]{ - Result: &event, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/manager.go deleted file mode 100644 index 24bcd95..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/manager.go +++ /dev/null @@ -1,175 +0,0 @@ -package location - -import ( - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -func NewManager(client geolocation.Client) (*Manager, error) { - currLocation, err := client.GetLocation() - if err != nil { - log.Warnf("Failed to get initial location: %v", err) - } - - m := &Manager{ - client: client, - dirty: make(chan struct{}), - stopChan: make(chan struct{}), - - state: &State{ - Latitude: currLocation.Latitude, - Longitude: currLocation.Longitude, - }, - } - - if err := m.startSignalPump(); err != nil { - return nil, err - } - - m.notifierWg.Add(1) - go m.notifier() - - return m, nil -} - -func (m *Manager) Close() { - close(m.stopChan) - m.notifierWg.Wait() - - m.sigWG.Wait() - - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if ch, ok := m.subscribers.LoadAndDelete(id); ok { - close(ch) - } -} - -func (m *Manager) startSignalPump() error { - m.sigWG.Add(1) - go func() { - defer m.sigWG.Done() - - subscription := m.client.Subscribe("locationManager") - defer m.client.Unsubscribe("locationManager") - - for { - select { - case <-m.stopChan: - return - case location, ok := <-subscription: - if !ok { - return - } - - m.handleLocationChange(location) - } - } - }() - - return nil -} - -func (m *Manager) handleLocationChange(location geolocation.Location) { - m.stateMutex.Lock() - defer m.stateMutex.Unlock() - - m.state.Latitude = location.Latitude - m.state.Longitude = location.Longitude - - m.notifySubscribers() -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{ - Latitude: 0.0, - Longitude: 0.0, - } - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 200 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - - currentState := m.GetState() - - if m.lastNotified != nil && !stateChanged(m.lastNotified, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- currentState: - default: - log.Warn("Location: subscriber channel full, dropping update") - } - return true - }) - - stateCopy := currentState - m.lastNotified = &stateCopy - pending = false - } - } -} - -func stateChanged(old, new *State) bool { - if old == nil || new == nil { - return true - } - if old.Latitude != new.Latitude { - return true - } - if old.Longitude != new.Longitude { - return true - } - - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/types.go deleted file mode 100644 index 1f3d518..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/types.go +++ /dev/null @@ -1,28 +0,0 @@ -package location - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type State struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` -} - -type Manager struct { - state *State - stateMutex sync.RWMutex - - client geolocation.Client - - stopChan chan struct{} - sigWG sync.WaitGroup - - subscribers syncmap.Map[string, chan State] - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotified *State -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/actions.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/actions.go deleted file mode 100644 index 858248e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/actions.go +++ /dev/null @@ -1,88 +0,0 @@ -package loginctl - -import ( - "fmt" -) - -func (m *Manager) Lock() error { - if m.sessionObj == nil { - return fmt.Errorf("session object not available") - } - err := m.sessionObj.Call(dbusSessionInterface+".Lock", 0).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".Lock", 0).Err - } - if err != nil { - return fmt.Errorf("failed to lock session: %w", err) - } - } - return nil -} - -func (m *Manager) Unlock() error { - err := m.sessionObj.Call(dbusSessionInterface+".Unlock", 0).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".Unlock", 0).Err - } - if err != nil { - return fmt.Errorf("failed to unlock session: %w", err) - } - } - return nil -} - -func (m *Manager) Activate() error { - err := m.sessionObj.Call(dbusSessionInterface+".Activate", 0).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".Activate", 0).Err - } - if err != nil { - return fmt.Errorf("failed to activate session: %w", err) - } - } - return nil -} - -func (m *Manager) SetIdleHint(idle bool) error { - err := m.sessionObj.Call(dbusSessionInterface+".SetIdleHint", 0, idle).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".SetIdleHint", 0, idle).Err - } - if err != nil { - return fmt.Errorf("failed to set idle hint: %w", err) - } - } - return nil -} - -func (m *Manager) Terminate() error { - err := m.sessionObj.Call(dbusSessionInterface+".Terminate", 0).Err - if err != nil { - if refreshErr := m.refreshSessionBinding(); refreshErr == nil { - err = m.sessionObj.Call(dbusSessionInterface+".Terminate", 0).Err - } - if err != nil { - return fmt.Errorf("failed to terminate session: %w", err) - } - } - return nil -} - -func (m *Manager) SetLockBeforeSuspend(enabled bool) { - m.lockBeforeSuspend.Store(enabled) -} - -func (m *Manager) SetSleepInhibitorEnabled(enabled bool) { - m.sleepInhibitorEnabled.Store(enabled) - if enabled { - // Re-acquire inhibitor if enabled - m.acquireSleepInhibitor() - } else { - // Release inhibitor if disabled - m.releaseSleepInhibitor() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/constants.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/constants.go deleted file mode 100644 index 1a29588..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/constants.go +++ /dev/null @@ -1,10 +0,0 @@ -package loginctl - -const ( - dbusDest = "org.freedesktop.login1" - dbusPath = "/org/freedesktop/login1" - dbusManagerInterface = "org.freedesktop.login1.Manager" - dbusSessionInterface = "org.freedesktop.login1.Session" - dbusUserInterface = "org.freedesktop.login1.User" - dbusPropsInterface = "org.freedesktop.DBus.Properties" -) diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers.go deleted file mode 100644 index 564e996..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers.go +++ /dev/null @@ -1,156 +0,0 @@ -package loginctl - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - switch req.Method { - case "loginctl.getState": - handleGetState(conn, req, manager) - case "loginctl.lock": - handleLock(conn, req, manager) - case "loginctl.unlock": - handleUnlock(conn, req, manager) - case "loginctl.activate": - handleActivate(conn, req, manager) - case "loginctl.setIdleHint": - handleSetIdleHint(conn, req, manager) - case "loginctl.setLockBeforeSuspend": - handleSetLockBeforeSuspend(conn, req, manager) - case "loginctl.setSleepInhibitorEnabled": - handleSetSleepInhibitorEnabled(conn, req, manager) - case "loginctl.lockerReady": - handleLockerReady(conn, req, manager) - case "loginctl.terminate": - handleTerminate(conn, req, manager) - case "loginctl.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleLock(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.Lock(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "locked"}) -} - -func handleUnlock(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.Unlock(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "unlocked"}) -} - -func handleActivate(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.Activate(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "activated"}) -} - -func handleSetIdleHint(conn net.Conn, req models.Request, manager *Manager) { - idle, err := params.Bool(req.Params, "idle") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetIdleHint(idle); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "idle hint set"}) -} - -func handleSetLockBeforeSuspend(conn net.Conn, req models.Request, manager *Manager) { - enabled, err := params.Bool(req.Params, "enabled") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetLockBeforeSuspend(enabled) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "lock before suspend set"}) -} - -func handleSetSleepInhibitorEnabled(conn net.Conn, req models.Request, manager *Manager) { - enabled, err := params.Bool(req.Params, "enabled") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetSleepInhibitorEnabled(enabled) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "sleep inhibitor setting updated"}) -} - -func handleLockerReady(conn net.Conn, req models.Request, manager *Manager) { - manager.lockTimerMu.Lock() - if manager.lockTimer != nil { - manager.lockTimer.Stop() - manager.lockTimer = nil - } - manager.lockTimerMu.Unlock() - - id := manager.sleepCycleID.Load() - manager.releaseForCycle(id) - - if manager.inSleepCycle.Load() { - manager.signalLockerReady() - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "ok"}) -} - -func handleTerminate(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.Terminate(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "terminated"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := SessionEvent{ - Type: EventStateChanged, - Data: initialState, - } - if err := json.NewEncoder(conn).Encode(models.Response[SessionEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := SessionEvent{ - Type: EventStateChanged, - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[SessionEvent]{ - Result: &event, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers_test.go deleted file mode 100644 index d3169e4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers_test.go +++ /dev/null @@ -1,500 +0,0 @@ -package loginctl - -import ( - "bytes" - "encoding/json" - "net" - "sync" - "testing" - "time" - - mockdbus "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -type mockNetConn struct { - net.Conn - readBuf *bytes.Buffer - writeBuf *bytes.Buffer - closed bool -} - -func newMockNetConn() *mockNetConn { - return &mockNetConn{ - readBuf: &bytes.Buffer{}, - writeBuf: &bytes.Buffer{}, - } -} - -func (m *mockNetConn) Read(b []byte) (n int, err error) { - return m.readBuf.Read(b) -} - -func (m *mockNetConn) Write(b []byte) (n int, err error) { - return m.writeBuf.Write(b) -} - -func (m *mockNetConn) Close() error { - m.closed = true - return nil -} - -func TestRespondError_Loginctl(t *testing.T) { - conn := newMockNetConn() - models.RespondError(conn, 123, "test error") - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Equal(t, "test error", resp.Error) - assert.Nil(t, resp.Result) -} - -func TestRespond_Loginctl(t *testing.T) { - conn := newMockNetConn() - result := models.SuccessResult{Success: true, Message: "test"} - models.Respond(conn, 123, result) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "test", resp.Result.Message) -} - -func TestHandleGetState(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - Active: true, - SessionType: "wayland", - SessionClass: "user", - UserName: "testuser", - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.getState"} - - handleGetState(conn, req, manager) - - var resp models.Response[SessionState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Equal(t, "1", resp.Result.SessionID) - assert.False(t, resp.Result.Locked) - assert.True(t, resp.Result.Active) -} - -func TestHandleLock(t *testing.T) { - t.Run("successful lock", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.lock"} - handleLock(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "locked", resp.Result.Message) - }) - - t.Run("lock fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.lock"} - handleLock(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to lock session") - }) -} - -func TestHandleUnlock(t *testing.T) { - t.Run("successful unlock", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Unlock", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.unlock"} - handleUnlock(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "unlocked", resp.Result.Message) - }) - - t.Run("unlock fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Unlock", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.unlock"} - handleUnlock(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to unlock session") - }) -} - -func TestHandleActivate(t *testing.T) { - t.Run("successful activate", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Activate", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.activate"} - handleActivate(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "activated", resp.Result.Message) - }) - - t.Run("activate fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Activate", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.activate"} - handleActivate(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to activate session") - }) -} - -func TestHandleSetIdleHint(t *testing.T) { - t.Run("missing idle parameter", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "loginctl.setIdleHint", - Params: map[string]any{}, - } - - handleSetIdleHint(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'idle' parameter") - }) - - t.Run("successful set idle hint true", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.SetIdleHint", dbus.Flags(0), true).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "loginctl.setIdleHint", - Params: map[string]any{ - "idle": true, - }, - } - - handleSetIdleHint(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "idle hint set", resp.Result.Message) - }) - - t.Run("set idle hint fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.SetIdleHint", dbus.Flags(0), false).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "loginctl.setIdleHint", - Params: map[string]any{ - "idle": false, - }, - } - - handleSetIdleHint(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to set idle hint") - }) -} - -func TestHandleTerminate(t *testing.T) { - t.Run("successful terminate", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Terminate", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.terminate"} - handleTerminate(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "terminated", resp.Result.Message) - }) - - t.Run("terminate fails", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: assert.AnError} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Terminate", dbus.Flags(0)).Return(mockCall) - - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - sessionObj: mockSessionObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.terminate"} - handleTerminate(conn, req, manager) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "failed to terminate session") - }) -} - -func TestHandleRequest(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - }, - stateMutex: sync.RWMutex{}, - } - - t.Run("unknown method", func(t *testing.T) { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "loginctl.unknown", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "unknown method") - }) - - t.Run("valid method - getState", func(t *testing.T) { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "loginctl.getState", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[SessionState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - }) - - t.Run("lock method", func(t *testing.T) { - mockSessionObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", mock.Anything).Return(mockCall) - - manager.sessionObj = mockSessionObj - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "loginctl.lock", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - }) -} - -func TestHandleSubscribe(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "loginctl.subscribe"} - - done := make(chan bool) - go func() { - handleSubscribe(conn, req, manager) - done <- true - }() - - time.Sleep(50 * time.Millisecond) - - conn.Close() - - if conn.writeBuf.Len() > 0 { - var resp models.Response[SessionEvent] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - if err == nil { - assert.Equal(t, 123, resp.ID) - require.NotNil(t, resp.Result) - assert.Equal(t, EventStateChanged, resp.Result.Type) - assert.Equal(t, "1", resp.Result.Data.SessionID) - } - } - - select { - case <-done: - case <-time.After(100 * time.Millisecond): - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager.go deleted file mode 100644 index 0c51e64..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager.go +++ /dev/null @@ -1,723 +0,0 @@ -package loginctl - -import ( - "context" - "fmt" - "os" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil" - "github.com/godbus/dbus/v5" -) - -func NewManager() (*Manager, error) { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - m := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - - stopChan: make(chan struct{}), - conn: conn, - dirty: make(chan struct{}, 1), - signals: make(chan *dbus.Signal, 256), - } - m.sleepInhibitorEnabled.Store(true) - - if err := m.initialize(); err != nil { - conn.Close() - return nil, err - } - - if err := m.acquireSleepInhibitor(); err != nil { - fmt.Fprintf(os.Stderr, "sleep inhibitor unavailable: %v\n", err) - } - - m.notifierWg.Add(1) - go m.notifier() - - if err := m.startSignalPump(); err != nil { - m.Close() - return nil, err - } - - return m, nil -} - -func (m *Manager) initialize() error { - m.managerObj = m.conn.Object(dbusDest, dbus.ObjectPath(dbusPath)) - - m.initializeFallbackDelay() - - sessionID, sessionPath, err := m.discoverSession() - if err != nil { - return fmt.Errorf("failed to get session path: %w", err) - } - - m.stateMutex.Lock() - m.state.SessionID = sessionID - m.state.SessionPath = string(sessionPath) - m.sessionPath = sessionPath - m.stateMutex.Unlock() - - m.sessionObj = m.conn.Object(dbusDest, sessionPath) - - if err := m.updateSessionState(); err != nil { - return err - } - - return nil -} - -func (m *Manager) discoverSession() (string, dbus.ObjectPath, error) { - // 1. Explicit XDG_SESSION_ID - if id := os.Getenv("XDG_SESSION_ID"); id != "" { - if path, err := m.getSession(id); err == nil { - fmt.Fprintf(os.Stderr, "loginctl: using XDG_SESSION_ID=%s\n", id) - return id, path, nil - } - } - - // 2. PID-based lookup (works when caller is inside a session cgroup) - if id, path, err := m.getSessionByPID(uint32(os.Getpid())); err == nil { - fmt.Fprintf(os.Stderr, "loginctl: found session %s via PID\n", id) - return id, path, nil - } - - // 3. User's primary display session (handles UWSM and similar) - if id, path, err := m.getUserDisplaySession(); err == nil { - fmt.Fprintf(os.Stderr, "loginctl: found session %s via User.Display\n", id) - return id, path, nil - } - - // 4. Score all sessions for current UID - if id, path, err := m.findBestSession(); err == nil { - fmt.Fprintf(os.Stderr, "loginctl: found session %s via ListSessions scoring\n", id) - return id, path, nil - } - - // 5. Last resort: "self" - path, err := m.getSession("self") - if err != nil { - return "", "", fmt.Errorf("%w", err) - } - return "self", path, nil -} - -func (m *Manager) getSession(id string) (dbus.ObjectPath, error) { - var out dbus.ObjectPath - err := m.managerObj.Call(dbusManagerInterface+".GetSession", 0, id).Store(&out) - if err != nil { - return "", err - } - return out, nil -} - -func (m *Manager) getSessionByPID(pid uint32) (string, dbus.ObjectPath, error) { - var path dbus.ObjectPath - if err := m.managerObj.Call(dbusManagerInterface+".GetSessionByPID", 0, pid).Store(&path); err != nil { - return "", "", err - } - - sessionObj := m.conn.Object(dbusDest, path) - var id dbus.Variant - if err := sessionObj.Call(dbusPropsInterface+".Get", 0, dbusSessionInterface, "Id").Store(&id); err != nil { - return "", "", err - } - return id.Value().(string), path, nil -} - -func (m *Manager) getUserDisplaySession() (string, dbus.ObjectPath, error) { - uid := uint32(os.Getuid()) - - var userPath dbus.ObjectPath - if err := m.managerObj.Call(dbusManagerInterface+".GetUser", 0, uid).Store(&userPath); err != nil { - return "", "", err - } - - userObj := m.conn.Object(dbusDest, userPath) - var display dbus.Variant - if err := userObj.Call(dbusPropsInterface+".Get", 0, dbusUserInterface, "Display").Store(&display); err != nil { - return "", "", err - } - - pair, ok := display.Value().([]any) - if !ok || len(pair) < 2 { - return "", "", fmt.Errorf("unexpected Display format") - } - - sessionID, _ := pair[0].(string) - sessionPath, _ := pair[1].(dbus.ObjectPath) - if sessionID == "" || sessionPath == "" { - return "", "", fmt.Errorf("empty Display session") - } - - return sessionID, sessionPath, nil -} - -type sessionCandidate struct { - id string - path dbus.ObjectPath -} - -func (m *Manager) findBestSession() (string, dbus.ObjectPath, error) { - // ListSessions returns a(susso): [][]any where each entry is [id, uid, name, seat, path] - var raw [][]any - if err := m.managerObj.Call(dbusManagerInterface+".ListSessions", 0).Store(&raw); err != nil { - return "", "", err - } - - uid := uint32(os.Getuid()) - var candidates []sessionCandidate - for _, entry := range raw { - if len(entry) < 5 { - continue - } - entryUID, _ := entry[1].(uint32) - if entryUID != uid { - continue - } - id, _ := entry[0].(string) - path, _ := entry[4].(dbus.ObjectPath) - if id != "" && path != "" { - candidates = append(candidates, sessionCandidate{id: id, path: path}) - } - } - if len(candidates) == 0 { - return "", "", fmt.Errorf("no sessions for uid %d", uid) - } - - bestScore := -1 - var best sessionCandidate - for _, c := range candidates { - score := m.scoreSession(c.path) - if score > bestScore { - bestScore = score - best = c - } - } - if bestScore < 0 { - return "", "", fmt.Errorf("no viable session found") - } - return best.id, best.path, nil -} - -func (m *Manager) scoreSession(path dbus.ObjectPath) int { - obj := m.conn.Object(dbusDest, path) - var props map[string]dbus.Variant - if err := obj.Call(dbusPropsInterface+".GetAll", 0, dbusSessionInterface).Store(&props); err != nil { - return -1 - } - - getStr := func(key string) string { - if v, ok := props[key]; ok { - if s, ok := v.Value().(string); ok { - return s - } - } - return "" - } - getBool := func(key string) bool { - if v, ok := props[key]; ok { - if b, ok := v.Value().(bool); ok { - return b - } - } - return false - } - getUint32 := func(key string) uint32 { - if v, ok := props[key]; ok { - if u, ok := v.Value().(uint32); ok { - return u - } - } - return 0 - } - - class := getStr("Class") - if class != "user" { - return -1 - } - if getBool("Remote") { - return -1 - } - - score := 0 - - if getBool("Active") { - score += 100 - } - - switch getStr("Type") { - case "wayland", "x11": - score += 80 - case "tty": - score += 10 - } - - if v, ok := props["Seat"]; ok { - if seatArr, ok := v.Value().([]any); ok && len(seatArr) >= 1 { - if seat, ok := seatArr[0].(string); ok && seat != "" { - score += 40 - if seat == "seat0" { - score += 10 - } - } - } - } - - if getUint32("VTNr") > 0 { - score += 20 - } - - return score -} - -func (m *Manager) refreshSessionBinding() error { - if m.managerObj == nil || m.conn == nil { - return fmt.Errorf("manager not fully initialized") - } - - sessionPath, err := m.getSession(m.state.SessionID) - if err != nil { - return fmt.Errorf("failed to get session path: %w", err) - } - - m.stateMutex.RLock() - currentPath := m.sessionPath - m.stateMutex.RUnlock() - - if sessionPath == currentPath { - return nil - } - - m.stopSignalPump() - - m.stateMutex.Lock() - m.state.SessionPath = string(sessionPath) - m.sessionPath = sessionPath - m.stateMutex.Unlock() - - m.sessionObj = m.conn.Object(dbusDest, sessionPath) - - if err := m.updateSessionState(); err != nil { - return err - } - - m.signals = make(chan *dbus.Signal, 256) - return m.startSignalPump() -} - -func (m *Manager) updateSessionState() error { - ctx := context.Background() - props, err := m.getSessionProperties(ctx) - if err != nil { - return err - } - - m.stateMutex.Lock() - defer m.stateMutex.Unlock() - - m.state.Active = dbusutil.GetOr(props, "Active", m.state.Active) - m.state.IdleHint = dbusutil.GetOr(props, "IdleHint", m.state.IdleHint) - m.state.IdleSinceHint = dbusutil.GetOr(props, "IdleSinceHint", m.state.IdleSinceHint) - if lockedHint, ok := dbusutil.Get[bool](props, "LockedHint"); ok { - m.state.LockedHint = lockedHint - m.state.Locked = lockedHint - } - m.state.SessionType = dbusutil.GetOr(props, "Type", m.state.SessionType) - m.state.SessionClass = dbusutil.GetOr(props, "Class", m.state.SessionClass) - if v, ok := props["User"]; ok { - if userArr, ok := v.Value().([]any); ok && len(userArr) >= 1 { - if uid, ok := userArr[0].(uint32); ok { - m.state.User = uid - } - } - } - m.state.UserName = dbusutil.GetOr(props, "Name", m.state.UserName) - m.state.RemoteHost = dbusutil.GetOr(props, "RemoteHost", m.state.RemoteHost) - m.state.Service = dbusutil.GetOr(props, "Service", m.state.Service) - m.state.TTY = dbusutil.GetOr(props, "TTY", m.state.TTY) - m.state.Display = dbusutil.GetOr(props, "Display", m.state.Display) - m.state.Remote = dbusutil.GetOr(props, "Remote", m.state.Remote) - if v, ok := props["Seat"]; ok { - if seatArr, ok := v.Value().([]any); ok && len(seatArr) >= 1 { - if seatID, ok := seatArr[0].(string); ok { - m.state.Seat = seatID - } - } - } - m.state.VTNr = dbusutil.GetOr(props, "VTNr", m.state.VTNr) - - return nil -} - -func (m *Manager) getSessionProperties(ctx context.Context) (map[string]dbus.Variant, error) { - var props map[string]dbus.Variant - err := m.sessionObj.CallWithContext(ctx, dbusPropsInterface+".GetAll", 0, dbusSessionInterface).Store(&props) - if err != nil { - return nil, err - } - return props, nil -} - -func (m *Manager) acquireSleepInhibitor() error { - if !m.sleepInhibitorEnabled.Load() { - return nil - } - - m.inhibitMu.Lock() - defer m.inhibitMu.Unlock() - - if m.inhibitFile != nil { - return nil - } - - if m.managerObj == nil { - return fmt.Errorf("manager object not available") - } - - file, err := m.inhibit("sleep", "DankMaterialShell", "Lock before suspend", "delay") - if err != nil { - return err - } - - m.inhibitFile = file - return nil -} - -func (m *Manager) inhibit(what, who, why, mode string) (*os.File, error) { - var fd dbus.UnixFD - err := m.managerObj.Call(dbusManagerInterface+".Inhibit", 0, what, who, why, mode).Store(&fd) - if err != nil { - return nil, err - } - return os.NewFile(uintptr(fd), "inhibit"), nil -} - -func (m *Manager) releaseSleepInhibitor() { - m.inhibitMu.Lock() - f := m.inhibitFile - m.inhibitFile = nil - m.inhibitMu.Unlock() - if f != nil { - f.Close() - } -} - -func (m *Manager) releaseForCycle(id uint64) { - if !m.inSleepCycle.Load() || m.sleepCycleID.Load() != id { - return - } - m.releaseSleepInhibitor() -} - -func (m *Manager) initializeFallbackDelay() { - var maxDelayUSec uint64 - err := m.managerObj.Call( - dbusPropsInterface+".Get", - 0, - dbusManagerInterface, - "InhibitDelayMaxUSec", - ).Store(&maxDelayUSec) - - if err != nil { - m.fallbackDelay = 2 * time.Second - return - } - - maxDelay := time.Duration(maxDelayUSec) * time.Microsecond - computed := (maxDelay * 8) / 10 - - if computed < 2*time.Second { - m.fallbackDelay = 2 * time.Second - } else if computed > 4*time.Second { - m.fallbackDelay = 4 * time.Second - } else { - m.fallbackDelay = computed - } -} - -func (m *Manager) newLockerReadyCh() chan struct{} { - m.lockerReadyChMu.Lock() - defer m.lockerReadyChMu.Unlock() - m.lockerReadyCh = make(chan struct{}) - return m.lockerReadyCh -} - -func (m *Manager) signalLockerReady() { - m.lockerReadyChMu.Lock() - ch := m.lockerReadyCh - if ch != nil { - close(ch) - m.lockerReadyCh = nil - } - m.lockerReadyChMu.Unlock() -} - -func (m *Manager) snapshotState() SessionState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return *m.state -} - -func stateChangedMeaningfully(old, new *SessionState) bool { - if old.Locked != new.Locked { - return true - } - if old.LockedHint != new.LockedHint { - return true - } - if old.Active != new.Active { - return true - } - if old.IdleHint != new.IdleHint { - return true - } - if old.PreparingForSleep != new.PreparingForSleep { - return true - } - return false -} - -func (m *Manager) GetState() SessionState { - return m.snapshotState() -} - -func (m *Manager) Subscribe(id string) chan SessionState { - ch := make(chan SessionState, 64) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - - currentState := m.snapshotState() - - if m.lastNotifiedState != nil && !stateChangedMeaningfully(m.lastNotifiedState, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan SessionState) bool { - select { - case ch <- currentState: - default: - } - return true - }) - - stateCopy := currentState - m.lastNotifiedState = &stateCopy - pending = false - } - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) startSignalPump() error { - m.conn.Signal(m.signals) - - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - m.conn.RemoveSignal(m.signals) - return err - } - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ); err != nil { - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveSignal(m.signals) - return err - } - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Unlock"), - ); err != nil { - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ) - m.conn.RemoveSignal(m.signals) - return err - } - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)), - dbus.WithMatchInterface(dbusManagerInterface), - dbus.WithMatchMember("PrepareForSleep"), - ); err != nil { - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Unlock"), - ) - m.conn.RemoveSignal(m.signals) - return err - } - - if err := m.conn.AddMatchSignal( - dbus.WithMatchObjectPath("/org/freedesktop/DBus"), - dbus.WithMatchInterface("org.freedesktop.DBus"), - dbus.WithMatchMember("NameOwnerChanged"), - ); err != nil { - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Unlock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)), - dbus.WithMatchInterface(dbusManagerInterface), - dbus.WithMatchMember("PrepareForSleep"), - ) - m.conn.RemoveSignal(m.signals) - return err - } - - m.sigWG.Add(1) - go func() { - defer m.sigWG.Done() - for { - select { - case <-m.stopChan: - return - case sig, ok := <-m.signals: - if !ok { - return - } - if sig == nil { - continue - } - m.handleDBusSignal(sig) - } - } - }() - return nil -} - -func (m *Manager) stopSignalPump() { - if m.conn == nil { - return - } - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Lock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(m.sessionPath), - dbus.WithMatchInterface(dbusSessionInterface), - dbus.WithMatchMember("Unlock"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)), - dbus.WithMatchInterface(dbusManagerInterface), - dbus.WithMatchMember("PrepareForSleep"), - ) - m.conn.RemoveMatchSignal( - dbus.WithMatchObjectPath("/org/freedesktop/DBus"), - dbus.WithMatchInterface("org.freedesktop.DBus"), - dbus.WithMatchMember("NameOwnerChanged"), - ) - - m.conn.RemoveSignal(m.signals) - close(m.signals) - - m.sigWG.Wait() -} - -func (m *Manager) Close() { - close(m.stopChan) - m.notifierWg.Wait() - - m.stopSignalPump() - - m.releaseSleepInhibitor() - - m.subscribers.Range(func(key string, ch chan SessionState) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - if m.conn != nil { - m.conn.Close() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager_test.go deleted file mode 100644 index 148ee60..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager_test.go +++ /dev/null @@ -1,296 +0,0 @@ -package loginctl - -import ( - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetState(t *testing.T) { - state := &SessionState{ - SessionID: "1", - Locked: false, - Active: true, - IdleHint: false, - SessionType: "wayland", - SessionClass: "user", - UserName: "testuser", - } - - manager := &Manager{ - state: state, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.Equal(t, "1", result.SessionID) - assert.False(t, result.Locked) - assert.True(t, result.Active) - assert.Equal(t, "wayland", result.SessionType) - assert.Equal(t, "testuser", result.UserName) -} - -func TestManager_Subscribe(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - } - - ch := manager.Subscribe("test-client") - assert.NotNil(t, ch) - assert.Equal(t, 64, cap(ch)) - - _, exists := manager.subscribers.Load("test-client") - assert.True(t, exists) -} - -func TestManager_Unsubscribe(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - } - - ch := manager.Subscribe("test-client") - - manager.Unsubscribe("test-client") - - _, ok := <-ch - assert.False(t, ok) - - _, exists := manager.subscribers.Load("test-client") - assert.False(t, exists) -} - -func TestManager_Unsubscribe_NonExistent(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - } - - // Unsubscribe a non-existent client should not panic - assert.NotPanics(t, func() { - manager.Unsubscribe("non-existent") - }) -} - -func TestManager_NotifySubscribers(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - }, - stateMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - manager.notifierWg.Add(1) - go manager.notifier() - - ch := make(chan SessionState, 10) - manager.subscribers.Store("test-client", ch) - - manager.notifySubscribers() - - select { - case state := <-ch: - assert.Equal(t, "1", state.SessionID) - assert.False(t, state.Locked) - case <-time.After(200 * time.Millisecond): - t.Fatal("did not receive state update") - } - - close(manager.stopChan) - manager.notifierWg.Wait() -} - -func TestManager_NotifySubscribers_Debounce(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - }, - stateMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - manager.notifierWg.Add(1) - go manager.notifier() - - ch := make(chan SessionState, 10) - manager.subscribers.Store("test-client", ch) - - manager.notifySubscribers() - manager.notifySubscribers() - manager.notifySubscribers() - - receivedCount := 0 - timeout := time.After(200 * time.Millisecond) - for { - select { - case <-ch: - receivedCount++ - case <-timeout: - assert.Equal(t, 1, receivedCount, "should receive exactly one debounced update") - close(manager.stopChan) - manager.notifierWg.Wait() - return - } - } -} - -func TestManager_Close(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - } - - ch1 := make(chan SessionState, 1) - ch2 := make(chan SessionState, 1) - manager.subscribers.Store("client1", ch1) - manager.subscribers.Store("client2", ch2) - - manager.Close() - - select { - case <-manager.stopChan: - case <-time.After(100 * time.Millisecond): - t.Fatal("stopChan not closed") - } - - _, ok1 := <-ch1 - _, ok2 := <-ch2 - assert.False(t, ok1, "ch1 should be closed") - assert.False(t, ok2, "ch2 should be closed") - - count := 0 - manager.subscribers.Range(func(key string, ch chan SessionState) bool { - count++ - return true - }) - assert.Equal(t, 0, count) -} - -func TestManager_GetState_ThreadSafe(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - Active: true, - }, - stateMutex: sync.RWMutex{}, - } - - done := make(chan bool) - for i := 0; i < 10; i++ { - go func() { - state := manager.GetState() - assert.Equal(t, "1", state.SessionID) - assert.True(t, state.Active) - done <- true - }() - } - - for i := 0; i < 10; i++ { - select { - case <-done: - case <-time.After(1 * time.Second): - t.Fatal("timeout waiting for goroutines") - } - } -} - -func TestStateChangedMeaningfully(t *testing.T) { - tests := []struct { - name string - old *SessionState - new *SessionState - expected bool - }{ - { - name: "no change", - old: &SessionState{Locked: false, Active: true, IdleHint: false}, - new: &SessionState{Locked: false, Active: true, IdleHint: false}, - expected: false, - }, - { - name: "locked changed", - old: &SessionState{Locked: false, Active: true, IdleHint: false}, - new: &SessionState{Locked: true, Active: true, IdleHint: false}, - expected: true, - }, - { - name: "active changed", - old: &SessionState{Locked: false, Active: true, IdleHint: false}, - new: &SessionState{Locked: false, Active: false, IdleHint: false}, - expected: true, - }, - { - name: "idle hint changed", - old: &SessionState{Locked: false, Active: true, IdleHint: false}, - new: &SessionState{Locked: false, Active: true, IdleHint: true}, - expected: true, - }, - { - name: "locked hint changed", - old: &SessionState{Locked: false, Active: true, LockedHint: false}, - new: &SessionState{Locked: false, Active: true, LockedHint: true}, - expected: true, - }, - { - name: "preparing for sleep changed", - old: &SessionState{Locked: false, Active: true, PreparingForSleep: false}, - new: &SessionState{Locked: false, Active: true, PreparingForSleep: true}, - expected: true, - }, - { - name: "non-meaningful change (username)", - old: &SessionState{Locked: false, Active: true, UserName: "user1"}, - new: &SessionState{Locked: false, Active: true, UserName: "user2"}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := stateChangedMeaningfully(tt.old, tt.new) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestManager_SnapshotState(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - SessionID: "1", - Locked: false, - Active: true, - UserName: "testuser", - }, - stateMutex: sync.RWMutex{}, - } - - snapshot := manager.snapshotState() - assert.Equal(t, "1", snapshot.SessionID) - assert.False(t, snapshot.Locked) - assert.True(t, snapshot.Active) - assert.Equal(t, "testuser", snapshot.UserName) - - snapshot.Locked = true - assert.False(t, manager.state.Locked) -} - -func TestNewManager(t *testing.T) { - t.Run("attempts to create manager", func(t *testing.T) { - manager, err := NewManager() - if err != nil { - assert.Nil(t, manager) - } else { - assert.NotNil(t, manager) - assert.NotNil(t, manager.state) - assert.NotNil(t, &manager.subscribers) - assert.NotNil(t, manager.stopChan) - - manager.Close() - } - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor.go deleted file mode 100644 index 4c0605a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor.go +++ /dev/null @@ -1,155 +0,0 @@ -package loginctl - -import ( - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil" - "github.com/godbus/dbus/v5" -) - -func (m *Manager) handleDBusSignal(sig *dbus.Signal) { - switch sig.Name { - case dbusSessionInterface + ".Lock": - m.stateMutex.Lock() - m.state.Locked = true - m.state.LockedHint = true - m.stateMutex.Unlock() - m.notifySubscribers() - - if m.sleepInhibitorEnabled.Load() && m.inSleepCycle.Load() { - id := m.sleepCycleID.Load() - m.lockTimerMu.Lock() - if m.lockTimer != nil { - m.lockTimer.Stop() - } - m.lockTimer = time.AfterFunc(m.fallbackDelay, func() { - m.releaseForCycle(id) - }) - m.lockTimerMu.Unlock() - } - - case dbusSessionInterface + ".Unlock": - m.stateMutex.Lock() - m.state.Locked = false - m.state.LockedHint = false - m.stateMutex.Unlock() - m.notifySubscribers() - - // Cancel the lock timer if it's still running - m.lockTimerMu.Lock() - if m.lockTimer != nil { - m.lockTimer.Stop() - m.lockTimer = nil - } - m.lockTimerMu.Unlock() - - // Re-acquire the sleep inhibitor (acquireSleepInhibitor checks the enabled flag) - m.acquireSleepInhibitor() - - case dbusManagerInterface + ".PrepareForSleep": - if len(sig.Body) == 0 { - return - } - preparing, _ := sig.Body[0].(bool) - - if preparing { - cycleID := m.sleepCycleID.Add(1) - m.inSleepCycle.Store(true) - - if m.lockBeforeSuspend.Load() { - m.Lock() - } - - readyCh := m.newLockerReadyCh() - go func(id uint64, ch <-chan struct{}) { - <-ch - if m.inSleepCycle.Load() && m.sleepCycleID.Load() == id { - m.releaseSleepInhibitor() - } - }(cycleID, readyCh) - } else { - m.inSleepCycle.Store(false) - m.signalLockerReady() - m.refreshSessionBinding() - m.acquireSleepInhibitor() - } - - m.stateMutex.Lock() - m.state.PreparingForSleep = preparing - m.stateMutex.Unlock() - m.notifySubscribers() - - case dbusPropsInterface + ".PropertiesChanged": - m.handlePropertiesChanged(sig) - - case "org.freedesktop.DBus.NameOwnerChanged": - if len(sig.Body) == 3 { - name, _ := sig.Body[0].(string) - oldOwner, _ := sig.Body[1].(string) - newOwner, _ := sig.Body[2].(string) - if name == dbusDest && oldOwner != "" && newOwner != "" { - m.updateSessionState() - if !m.inSleepCycle.Load() { - m.acquireSleepInhibitor() - } - m.notifySubscribers() - } - } - } -} - -func (m *Manager) handlePropertiesChanged(sig *dbus.Signal) { - if len(sig.Body) < 2 { - return - } - - iface, ok := sig.Body[0].(string) - if !ok || iface != dbusSessionInterface { - return - } - - changes, ok := sig.Body[1].(map[string]dbus.Variant) - if !ok { - return - } - - var needsUpdate bool - - for key, variant := range changes { - switch key { - case "Active": - if val, ok := dbusutil.As[bool](variant); ok { - m.stateMutex.Lock() - m.state.Active = val - m.stateMutex.Unlock() - needsUpdate = true - } - case "IdleHint": - if val, ok := dbusutil.As[bool](variant); ok { - m.stateMutex.Lock() - m.state.IdleHint = val - m.stateMutex.Unlock() - needsUpdate = true - } - case "IdleSinceHint": - if val, ok := dbusutil.As[uint64](variant); ok { - m.stateMutex.Lock() - m.state.IdleSinceHint = val - m.stateMutex.Unlock() - needsUpdate = true - } - case "LockedHint": - if val, ok := dbusutil.As[bool](variant); ok { - m.stateMutex.Lock() - m.state.LockedHint = val - m.state.Locked = val - m.stateMutex.Unlock() - needsUpdate = true - } - } - } - - if needsUpdate { - m.notifySubscribers() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor_test.go deleted file mode 100644 index ecbb57f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor_test.go +++ /dev/null @@ -1,298 +0,0 @@ -package loginctl - -import ( - "sync" - "testing" - - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" -) - -func TestManager_HandleDBusSignal_Lock(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Locked: false, - LockedHint: false, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Session.Lock", - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.Locked) - assert.True(t, manager.state.LockedHint) -} - -func TestManager_HandleDBusSignal_Unlock(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Locked: true, - LockedHint: true, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Session.Unlock", - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.False(t, manager.state.Locked) - assert.False(t, manager.state.LockedHint) -} - -func TestManager_HandleDBusSignal_PrepareForSleep(t *testing.T) { - t.Run("preparing for sleep - true", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - PreparingForSleep: false, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Manager.PrepareForSleep", - Body: []any{true}, - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.PreparingForSleep) - }) - - t.Run("preparing for sleep - false", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - PreparingForSleep: true, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Manager.PrepareForSleep", - Body: []any{false}, - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.False(t, manager.state.PreparingForSleep) - }) - - t.Run("empty body", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - PreparingForSleep: false, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.login1.Manager.PrepareForSleep", - Body: []any{}, - } - - manager.handleDBusSignal(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.False(t, manager.state.PreparingForSleep) - }) -} - -func TestManager_HandlePropertiesChanged(t *testing.T) { - t.Run("active property changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Active: false, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "Active": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.Active) - }) - - t.Run("idle hint property changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - IdleHint: false, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "IdleHint": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.IdleHint) - }) - - t.Run("idle since hint property changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - IdleSinceHint: 0, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "IdleSinceHint": dbus.MakeVariant(uint64(123456789)), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.Equal(t, uint64(123456789), manager.state.IdleSinceHint) - }) - - t.Run("locked hint property changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - LockedHint: false, - Locked: false, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "LockedHint": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.LockedHint) - assert.True(t, manager.state.Locked) - }) - - t.Run("wrong interface", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Active: false, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{ - "org.freedesktop.SomeOtherInterface", - map[string]dbus.Variant{ - "Active": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.False(t, manager.state.Active) - }) - - t.Run("empty body", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{}, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{}, - } - - assert.NotPanics(t, func() { - manager.handlePropertiesChanged(sig) - }) - }) - - t.Run("multiple properties changed", func(t *testing.T) { - manager := &Manager{ - state: &SessionState{ - Active: false, - IdleHint: false, - }, - stateMutex: sync.RWMutex{}, - dirty: make(chan struct{}, 1), - } - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{ - "org.freedesktop.login1.Session", - map[string]dbus.Variant{ - "Active": dbus.MakeVariant(true), - "IdleHint": dbus.MakeVariant(true), - }, - }, - } - - manager.handlePropertiesChanged(sig) - - manager.stateMutex.RLock() - defer manager.stateMutex.RUnlock() - assert.True(t, manager.state.Active) - assert.True(t, manager.state.IdleHint) - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types.go deleted file mode 100644 index 76c7414..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types.go +++ /dev/null @@ -1,76 +0,0 @@ -package loginctl - -import ( - "os" - "sync" - "sync/atomic" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/godbus/dbus/v5" -) - -type SessionState struct { - SessionID string `json:"sessionId"` - SessionPath string `json:"sessionPath"` - Locked bool `json:"locked"` - Active bool `json:"active"` - IdleHint bool `json:"idleHint"` - IdleSinceHint uint64 `json:"idleSinceHint"` - LockedHint bool `json:"lockedHint"` - SessionType string `json:"sessionType"` - SessionClass string `json:"sessionClass"` - User uint32 `json:"user"` - UserName string `json:"userName"` - RemoteHost string `json:"remoteHost"` - Service string `json:"service"` - TTY string `json:"tty"` - Display string `json:"display"` - Remote bool `json:"remote"` - Seat string `json:"seat"` - VTNr uint32 `json:"vtnr"` - PreparingForSleep bool `json:"preparingForSleep"` -} - -type EventType string - -const ( - EventStateChanged EventType = "state_changed" - EventLock EventType = "lock" - EventUnlock EventType = "unlock" - EventPrepareForSleep EventType = "prepare_for_sleep" - EventIdleHintChanged EventType = "idle_hint_changed" - EventLockedHintChanged EventType = "locked_hint_changed" -) - -type SessionEvent struct { - Type EventType `json:"type"` - Data SessionState `json:"data"` -} - -type Manager struct { - state *SessionState - stateMutex sync.RWMutex - subscribers syncmap.Map[string, chan SessionState] - stopChan chan struct{} - conn *dbus.Conn - sessionPath dbus.ObjectPath - managerObj dbus.BusObject - sessionObj dbus.BusObject - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotifiedState *SessionState - signals chan *dbus.Signal - sigWG sync.WaitGroup - inhibitMu sync.Mutex - inhibitFile *os.File - lockBeforeSuspend atomic.Bool - inSleepCycle atomic.Bool - sleepCycleID atomic.Uint64 - lockerReadyChMu sync.Mutex - lockerReadyCh chan struct{} - lockTimerMu sync.Mutex - lockTimer *time.Timer - sleepInhibitorEnabled atomic.Bool - fallbackDelay time.Duration -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types_test.go deleted file mode 100644 index 383300f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package loginctl - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestEventType_Constants(t *testing.T) { - assert.Equal(t, EventType("state_changed"), EventStateChanged) - assert.Equal(t, EventType("lock"), EventLock) - assert.Equal(t, EventType("unlock"), EventUnlock) - assert.Equal(t, EventType("prepare_for_sleep"), EventPrepareForSleep) - assert.Equal(t, EventType("idle_hint_changed"), EventIdleHintChanged) - assert.Equal(t, EventType("locked_hint_changed"), EventLockedHintChanged) -} - -func TestSessionState_Struct(t *testing.T) { - state := SessionState{ - SessionID: "1", - Locked: false, - Active: true, - SessionType: "wayland", - User: 1000, - UserName: "testuser", - } - - assert.Equal(t, "1", state.SessionID) - assert.True(t, state.Active) - assert.False(t, state.Locked) - assert.Equal(t, "wayland", state.SessionType) - assert.Equal(t, uint32(1000), state.User) - assert.Equal(t, "testuser", state.UserName) -} - -func TestSessionEvent_Struct(t *testing.T) { - state := SessionState{ - SessionID: "1", - Locked: true, - } - - event := SessionEvent{ - Type: EventLock, - Data: state, - } - - assert.Equal(t, EventLock, event.Type) - assert.Equal(t, "1", event.Data.SessionID) - assert.True(t, event.Data.Locked) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/matugen_handler.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/matugen_handler.go deleted file mode 100644 index 73c6ad6..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/matugen_handler.go +++ /dev/null @@ -1,79 +0,0 @@ -package server - -import ( - "context" - "net" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/matugen" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -type MatugenQueueResult struct { - Success bool `json:"success"` - Message string `json:"message,omitempty"` -} - -func handleMatugenQueue(conn net.Conn, req models.Request) { - opts := matugen.Options{ - StateDir: models.GetOr(req, "stateDir", ""), - ShellDir: models.GetOr(req, "shellDir", ""), - ConfigDir: models.GetOr(req, "configDir", ""), - Kind: models.GetOr(req, "kind", ""), - Value: models.GetOr(req, "value", ""), - Mode: matugen.ColorMode(models.GetOr(req, "mode", "")), - IconTheme: models.GetOr(req, "iconTheme", ""), - MatugenType: models.GetOr(req, "matugenType", ""), - RunUserTemplates: models.GetOr(req, "runUserTemplates", true), - StockColors: models.GetOr(req, "stockColors", ""), - SyncModeWithPortal: models.GetOr(req, "syncModeWithPortal", false), - TerminalsAlwaysDark: models.GetOr(req, "terminalsAlwaysDark", false), - SkipTemplates: models.GetOr(req, "skipTemplates", ""), - Contrast: models.GetOr(req, "contrast", 0.0), - } - - wait := models.GetOr(req, "wait", true) - - queue := matugen.GetQueue() - resultCh := queue.Submit(opts) - - if !wait { - models.Respond(conn, req.ID, MatugenQueueResult{ - Success: true, - Message: "queued", - }) - return - } - - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - defer cancel() - - select { - case result := <-resultCh: - if result.Error != nil { - if result.Error == context.Canceled { - models.Respond(conn, req.ID, MatugenQueueResult{ - Success: false, - Message: "cancelled", - }) - return - } - models.RespondError(conn, req.ID, result.Error.Error()) - return - } - models.Respond(conn, req.ID, MatugenQueueResult{ - Success: true, - Message: "completed", - }) - case <-ctx.Done(): - models.RespondError(conn, req.ID, "timeout waiting for theme generation") - } -} - -func handleMatugenStatus(conn net.Conn, req models.Request) { - queue := matugen.GetQueue() - models.Respond(conn, req.ID, map[string]bool{ - "running": queue.IsRunning(), - "pending": queue.HasPending(), - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/models/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/models/types.go deleted file mode 100644 index 2933fb5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/models/types.go +++ /dev/null @@ -1,47 +0,0 @@ -package models - -import ( - "encoding/json" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -type Request struct { - ID int `json:"id,omitempty"` - Method string `json:"method"` - Params map[string]any `json:"params,omitempty"` -} - -func Get[T any](r Request, key string) (T, bool) { - v, err := params.Get[T](r.Params, key) - return v, err == nil -} - -func GetOr[T any](r Request, key string, def T) T { - return params.GetOpt(r.Params, key, def) -} - -type Response[T any] struct { - ID int `json:"id,omitempty"` - Result *T `json:"result,omitempty"` - Error string `json:"error,omitempty"` -} - -func RespondError(conn net.Conn, id int, errMsg string) { - log.Errorf("DMS API Error: id=%d error=%s", id, errMsg) - resp := Response[any]{ID: id, Error: errMsg} - json.NewEncoder(conn).Encode(resp) -} - -func Respond[T any](conn net.Conn, id int, result T) { - resp := Response[T]{ID: id, Result: &result} - json.NewEncoder(conn).Encode(resp) -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message,omitempty"` - Value string `json:"value,omitempty"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/models/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/models/types_test.go deleted file mode 100644 index 98f9528..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/models/types_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package models - -import "testing" - -func TestGet(t *testing.T) { - req := Request{Params: map[string]any{"name": "test", "count": 42, "enabled": true}} - - name, ok := Get[string](req, "name") - if !ok || name != "test" { - t.Errorf("Get[string] = %q, %v; want 'test', true", name, ok) - } - - count, ok := Get[int](req, "count") - if !ok || count != 42 { - t.Errorf("Get[int] = %d, %v; want 42, true", count, ok) - } - - enabled, ok := Get[bool](req, "enabled") - if !ok || !enabled { - t.Errorf("Get[bool] = %v, %v; want true, true", enabled, ok) - } - - _, ok = Get[string](req, "missing") - if ok { - t.Error("Get missing key should return false") - } - - _, ok = Get[int](req, "name") - if ok { - t.Error("Get wrong type should return false") - } -} - -func TestGetOr(t *testing.T) { - req := Request{Params: map[string]any{"name": "test", "enabled": true}} - - if v := GetOr(req, "name", "default"); v != "test" { - t.Errorf("GetOr existing = %q; want 'test'", v) - } - - if v := GetOr(req, "missing", "default"); v != "default" { - t.Errorf("GetOr missing = %q; want 'default'", v) - } - - if v := GetOr(req, "enabled", false); !v { - t.Errorf("GetOr bool = %v; want true", v) - } - - if v := GetOr(req, "name", 0); v != 0 { - t.Errorf("GetOr wrong type = %d; want 0 (default)", v) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/API.md b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/API.md deleted file mode 100644 index 361ebac..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/API.md +++ /dev/null @@ -1,552 +0,0 @@ -# NetworkManager API Documentation - -## Overview - -The network manager API provides methods for managing WiFi connections, monitoring network state, and handling credential prompts through NetworkManager or iwd (and systemd-networkd for ethernet only). Communication occurs over a message-based protocol (websocket, IPC, etc.) with event subscriptions for state updates. - -## API Methods - -### network.wifi.connect - -Initiate a WiFi connection. - -**Request:** -```json -{ - "method": "network.wifi.connect", - "params": { - "ssid": "NetworkName", - "password": "optional-password", - "interactive": true - } -} -``` - -**Parameters:** -- `ssid` (string, required): Network SSID -- `password` (string, optional): Pre-shared key for WPA/WPA2/WPA3 networks -- `interactive` (boolean, optional): Enable credential prompting if authentication fails or password is missing. Automatically set to `true` when connecting to secured networks without providing a password. - -**Response:** -```json -{ - "success": true, - "message": "connecting" -} -``` - -**Behavior:** -- Returns immediately; connection happens asynchronously -- State updates delivered via `network` service subscription -- Credential prompts delivered via `network.credentials` service subscription - -### network.credentials.submit - -Submit credentials in response to a prompt. - -**Request:** -```json -{ - "method": "network.credentials.submit", - "params": { - "token": "correlation-token", - "secrets": { - "psk": "password" - }, - "save": true - } -} -``` - -**Parameters:** -- `token` (string, required): Token from credential prompt -- `secrets` (object, required): Key-value map of credential fields -- `save` (boolean, optional): Whether to persist credentials (default: false) - -**Common secret fields:** -- `psk`: Pre-shared key for WPA2/WPA3 personal networks -- `identity`: Username for 802.1X enterprise networks -- `password`: Password for 802.1X enterprise networks - -### network.credentials.cancel - -Cancel a credential prompt. - -**Request:** -```json -{ - "method": "network.credentials.cancel", - "params": { - "token": "correlation-token" - } -} -``` - -## Event Subscriptions - -### Subscribing to Events - -Subscribe to receive network state updates and credential prompts: - -```json -{ - "method": "subscribe", - "params": { - "services": ["network", "network.credentials"] - } -} -``` - -Both services are required for full connection handling. Missing `network.credentials` means credential prompts won't be received. - -### network Service Events - -State updates are sent whenever network configuration changes: - -```json -{ - "service": "network", - "data": { - "networkStatus": "wifi", - "isConnecting": false, - "connectingSSID": "", - "wifiConnected": true, - "wifiSSID": "MyNetwork", - "wifiIP": "192.168.1.100", - "lastError": "" - } -} -``` - -**State fields:** -- `networkStatus`: Current connection type (`wifi`, `ethernet`, `disconnected`) -- `isConnecting`: Whether a connection attempt is in progress -- `connectingSSID`: SSID being connected to (empty when idle) -- `wifiConnected`: Whether associated with an access point -- `wifiSSID`: Currently connected network name -- `wifiIP`: Assigned IP address (empty until DHCP completes) -- `lastError`: Error message from last failed connection attempt - -### network.credentials Service Events - -Credential prompts are sent when authentication is required: - -```json -{ - "service": "network.credentials", - "data": { - "token": "unique-prompt-id", - "ssid": "NetworkName", - "setting": "802-11-wireless-security", - "fields": ["psk"], - "hints": ["wpa3", "sae"], - "reason": "Credentials required" - } -} -``` - -**Prompt fields:** -- `token`: Unique identifier for this prompt (use in submit/cancel) -- `ssid`: Network requesting credentials -- `setting`: Authentication type (`802-11-wireless-security` for personal WiFi, `802-1x` for enterprise) -- `fields`: Array of required credential field names -- `hints`: Additional context about the network type -- `reason`: Human-readable explanation (e.g., "Previous password was incorrect") - -## Connection Flow - -### Typical Timeline - -``` -T+0ms Call network.wifi.connect -T+10ms Receive {"success": true, "message": "connecting"} -T+100ms State update: isConnecting=true, connectingSSID="Network" -T+500ms Credential prompt (if needed) -T+1000ms Submit credentials -T+3000ms State update: wifiConnected=true, wifiIP="192.168.x.x" -``` - -### State Machine - -``` -IDLE - | - | network.wifi.connect - v -CONNECTING (isConnecting=true, connectingSSID set) - | - +-- Needs credentials - | | - | v - | PROMPTING (credential prompt event) - | | - | | network.credentials.submit - | v - | back to CONNECTING - | - +-- Success - | | - | v - | CONNECTED (wifiConnected=true, wifiIP set, isConnecting=false) - | - +-- Failure - | - v - ERROR (isConnecting=false, !wifiConnected, lastError set) -``` - -## Connection Success Detection - -A connection is successful when all of the following are true: - -1. `wifiConnected` is `true` -2. `wifiIP` is set and non-empty -3. `wifiSSID` matches the target network -4. `isConnecting` is `false` - -Do not rely on `wifiConnected` alone - the device may be associated with an access point but not have an IP address yet. - -**Example:** -```javascript -function isConnectionComplete(state, targetSSID) { - return state.wifiConnected && - state.wifiIP && - state.wifiIP !== "" && - state.wifiSSID === targetSSID && - !state.isConnecting; -} -``` - -## Error Handling - -### Error Detection - -Errors occur when a connection attempt stops without success: - -```javascript -function checkForFailure(state, wasConnecting, targetSSID) { - // Was connecting, now idle, but not connected - if (wasConnecting && - !state.isConnecting && - state.connectingSSID === "" && - !state.wifiConnected) { - return state.lastError || "Connection failed"; - } - return null; -} -``` - -### Common Error Scenarios - -#### Wrong Password - -**Detection methods:** - -1. Quick failure (< 3 seconds from start) -2. `lastError` contains "password", "auth", or "secrets" -3. Second credential prompt with `reason: "Previous password was incorrect"` - -**Handling:** -```javascript -if (prompt.reason === "Previous password was incorrect") { - // Show error, clear password field, re-focus input -} -``` - -#### Network Out of Range - -**Detection:** -- `lastError` contains "not-found" or "connection-attempt-failed" - -#### Connection Timeout - -**Detection:** -- `isConnecting` remains true for > 30 seconds - -**Implementation:** -```javascript -let timeout = setTimeout(() => { - if (currentState.isConnecting) { - handleTimeout(); - } -}, 30000); -``` - -#### DHCP Failure - -**Detection:** -- `wifiConnected` is true -- `wifiIP` is empty after 15+ seconds - -### Error Message Translation - -Map technical errors to user-friendly messages: - -| lastError value | Meaning | User message | -|----------------|---------|--------------| -| `secrets-required` | Password needed | "Please enter password" | -| `authentication-failed` | Wrong password | "Incorrect password" | -| `connection-removed` | Profile deleted | "Network configuration removed" | -| `connection-attempt-failed` | Generic failure | "Failed to connect" | -| `network-not-found` | Out of range | "Network not found" | -| `(timeout)` | Timeout | "Connection timed out" | - -## Credential Handling - -### Secret Agent Architecture - -The credential system uses a broker pattern: - -``` -NetworkManager -> SecretAgent -> PromptBroker -> UI -> User - ^ - | - User Response - | -NetworkManager <- SecretAgent <- PromptBroker <- UI -``` - -### Implementing a Broker - -```go -type CustomBroker struct { - ui UIInterface - pending map[string]chan network.PromptReply -} - -func (b *CustomBroker) Ask(ctx context.Context, req network.PromptRequest) (string, error) { - token := generateToken() - b.pending[token] = make(chan network.PromptReply, 1) - - // Send to UI - b.ui.ShowCredentialPrompt(token, req) - - return token, nil -} - -func (b *CustomBroker) Wait(ctx context.Context, token string) (network.PromptReply, error) { - select { - case <-ctx.Done(): - return network.PromptReply{}, errors.New("timeout") - case reply := <-b.pending[token]: - return reply, nil - } -} - -func (b *CustomBroker) Resolve(token string, reply network.PromptReply) error { - if ch, ok := b.pending[token]; ok { - ch <- reply - close(ch) - delete(b.pending, token) - } - return nil -} -``` - -### Credential Field Types - -**Personal WiFi (802-11-wireless-security):** -- Fields: `["psk"]` -- UI: Single password input - -**Enterprise WiFi (802-1x):** -- Fields: `["identity", "password"]` -- UI: Username and password inputs - -### Building Secrets Object - -```javascript -function buildSecrets(setting, fields, formData) { - let secrets = {}; - - if (setting === "802-11-wireless-security") { - secrets.psk = formData.password; - } else if (setting === "802-1x") { - secrets.identity = formData.username; - secrets.password = formData.password; - } - - return secrets; -} -``` - -## Best Practices - -### Track Target Network - -Always store which network you're connecting to: - -```javascript -let targetSSID = null; - -function connect(ssid) { - targetSSID = ssid; - // send request -} - -function onStateUpdate(state) { - if (!targetSSID) return; - - if (state.wifiSSID === targetSSID && state.wifiConnected && state.wifiIP) { - // Success for the network we care about - targetSSID = null; - } -} -``` - -### Implement Timeouts - -Never wait indefinitely for a connection: - -```javascript -const CONNECTION_TIMEOUT = 30000; // 30 seconds -const DHCP_TIMEOUT = 15000; // 15 seconds - -let timer = setTimeout(() => { - if (stillConnecting) { - handleTimeout(); - } -}, CONNECTION_TIMEOUT); -``` - -### Handle Credential Re-prompts - -Wrong passwords trigger a second prompt: - -```javascript -function onCredentialPrompt(prompt) { - if (prompt.reason.includes("incorrect")) { - // Show error, but keep dialog open - showError("Wrong password"); - clearPasswordField(); - } else { - // First time prompt - showDialog(prompt); - } -} -``` - -### Clean Up State - -Reset tracking variables on success, failure, or cancellation: - -```javascript -function cleanup() { - clearTimeout(timer); - targetSSID = null; - closeDialogs(); -} -``` - -### Subscribe to Both Services - -Missing `network.credentials` means prompts won't arrive: - -```javascript -// Correct -services: ["network", "network.credentials"] - -// Wrong - will miss credential prompts -services: ["network"] -``` - -## Testing - -### Connection Test Checklist - -- [ ] Connect to open network -- [ ] Connect to WPA2 network with password provided -- [ ] Connect to WPA2 network without password (triggers prompt) -- [ ] Enter wrong password (verify error and re-prompt) -- [ ] Cancel credential prompt -- [ ] Connection timeout after 30 seconds -- [ ] DHCP timeout detection -- [ ] Network out of range -- [ ] Reconnect to already-configured network - -### Verifying Secret Agent Setup - -Check connection profile flags: -```bash -nmcli connection show "NetworkName" | grep flags -# Should show: 802-11-wireless-security.psk-flags: 1 (agent-owned) -``` - -Check agent registration in logs: -``` -INFO: Registered with NetworkManager as secret agent -``` - -## Security - -- Never log credential values (passwords, PSKs) -- Clear password fields when dialogs close -- Implement prompt timeouts (default: 2 minutes) -- Validate user input before submission -- Use secure channels for credential transmission - -## Troubleshooting - -### Credential prompt doesn't appear - -**Check:** -- Subscribed to both `network` and `network.credentials` -- Connection has `interactive: true` -- Secret flags set to AGENT_OWNED (value: 1) -- Broker registered successfully - -### Connection succeeds without prompting - -**Cause:** NetworkManager found saved credentials - -**Solution:** Delete existing connection first, or use different credentials - -### State updates seem delayed - -**Expected behavior:** State changes occur in rapid succession during connection - -**Solution:** Debounce UI updates; only act on final state - -### Multiple rapid credential prompts - -**Cause:** Connection profile has incorrect flags or conflicting agents - -**Solution:** -- Check only one agent is running -- Verify psk-flags value -- Check NetworkManager logs for agent conflicts - -## Data Structures Reference - -### PromptRequest -```go -type PromptRequest struct { - SSID string `json:"ssid"` - SettingName string `json:"setting"` - Fields []string `json:"fields"` - Hints []string `json:"hints"` - Reason string `json:"reason"` -} -``` - -### PromptReply -```go -type PromptReply struct { - Secrets map[string]string `json:"secrets"` - Save bool `json:"save"` - Cancel bool `json:"cancel"` -} -``` - -### NetworkState -```go -type NetworkState struct { - NetworkStatus string `json:"networkStatus"` - IsConnecting bool `json:"isConnecting"` - ConnectingSSID string `json:"connectingSSID"` - WifiConnected bool `json:"wifiConnected"` - WifiSSID string `json:"wifiSSID"` - WifiIP string `json:"wifiIP"` - LastError string `json:"lastError"` -} -``` diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_iwd.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_iwd.go deleted file mode 100644 index 5d49f04..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_iwd.go +++ /dev/null @@ -1,306 +0,0 @@ -package network - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/godbus/dbus/v5" -) - -const ( - iwdAgentManagerPath = "/net/connman/iwd" - iwdAgentManagerIface = "net.connman.iwd.AgentManager" - iwdAgentInterface = "net.connman.iwd.Agent" - iwdAgentObjectPath = "/com/danklinux/iwdagent" -) - -type ConnectionStateChecker interface { - IsConnectingTo(ssid string) bool -} - -type IWDAgent struct { - conn *dbus.Conn - objPath dbus.ObjectPath - prompts PromptBroker - onUserCanceled func() - onPromptRetry func(ssid string) - lastRequestSSID string - stateChecker ConnectionStateChecker -} - -const iwdAgentIntrospectXML = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - -` - -func NewIWDAgent(conn *dbus.Conn, prompts PromptBroker) (*IWDAgent, error) { - if conn == nil { - return nil, fmt.Errorf("dbus connection is nil") - } - - agent := &IWDAgent{ - conn: conn, - objPath: dbus.ObjectPath(iwdAgentObjectPath), - prompts: prompts, - } - - if err := conn.Export(agent, agent.objPath, iwdAgentInterface); err != nil { - return nil, fmt.Errorf("failed to export IWD agent: %w", err) - } - - if err := conn.Export(agent, agent.objPath, "org.freedesktop.DBus.Introspectable"); err != nil { - return nil, fmt.Errorf("failed to export introspection: %w", err) - } - - mgr := conn.Object("net.connman.iwd", dbus.ObjectPath(iwdAgentManagerPath)) - call := mgr.Call(iwdAgentManagerIface+".RegisterAgent", 0, agent.objPath) - if call.Err != nil { - return nil, fmt.Errorf("failed to register agent with iwd: %w", call.Err) - } - - return agent, nil -} - -func (a *IWDAgent) Close() { - if a.conn != nil { - mgr := a.conn.Object("net.connman.iwd", dbus.ObjectPath(iwdAgentManagerPath)) - mgr.Call(iwdAgentManagerIface+".UnregisterAgent", 0, a.objPath) - } -} - -func (a *IWDAgent) SetStateChecker(checker ConnectionStateChecker) { - a.stateChecker = checker -} - -func (a *IWDAgent) getNetworkName(networkPath dbus.ObjectPath) string { - netObj := a.conn.Object("net.connman.iwd", networkPath) - nameVar, err := netObj.GetProperty("net.connman.iwd.Network.Name") - if err == nil { - if name, ok := nameVar.Value().(string); ok { - return name - } - } - return string(networkPath) -} - -func (a *IWDAgent) RequestPassphrase(network dbus.ObjectPath) (string, *dbus.Error) { - ssid := a.getNetworkName(network) - - if a.stateChecker != nil && !a.stateChecker.IsConnectingTo(ssid) { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.prompts == nil { - if a.onUserCanceled != nil { - a.onUserCanceled() - } - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.lastRequestSSID == ssid { - if a.onPromptRetry != nil { - a.onPromptRetry(ssid) - } - } - a.lastRequestSSID = ssid - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - SSID: ssid, - Fields: []string{"psk"}, - }) - if err != nil { - if a.onUserCanceled != nil { - a.onUserCanceled() - } - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - reply, err := a.prompts.Wait(ctx, token) - if err != nil { - if reply.Cancel || errors.Is(err, errdefs.ErrSecretPromptCancelled) { - if a.onUserCanceled != nil { - a.onUserCanceled() - } - } - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if passphrase, ok := reply.Secrets["psk"]; ok { - return passphrase, nil - } - - if a.onUserCanceled != nil { - a.onUserCanceled() - } - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) -} - -func (a *IWDAgent) RequestPrivateKeyPassphrase(network dbus.ObjectPath) (string, *dbus.Error) { - ssid := a.getNetworkName(network) - - if a.stateChecker != nil && !a.stateChecker.IsConnectingTo(ssid) { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.prompts == nil { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.lastRequestSSID == ssid { - if a.onPromptRetry != nil { - a.onPromptRetry(ssid) - } - } - a.lastRequestSSID = ssid - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - SSID: ssid, - Fields: []string{"private-key-password"}, - }) - if err != nil { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - reply, err := a.prompts.Wait(ctx, token) - if err != nil || reply.Cancel { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if passphrase, ok := reply.Secrets["private-key-password"]; ok { - return passphrase, nil - } - - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) -} - -func (a *IWDAgent) RequestUserNameAndPassword(network dbus.ObjectPath) (string, string, *dbus.Error) { - ssid := a.getNetworkName(network) - - if a.stateChecker != nil && !a.stateChecker.IsConnectingTo(ssid) { - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.prompts == nil { - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.lastRequestSSID == ssid { - if a.onPromptRetry != nil { - a.onPromptRetry(ssid) - } - } - a.lastRequestSSID = ssid - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - SSID: ssid, - Fields: []string{"identity", "password"}, - }) - if err != nil { - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - reply, err := a.prompts.Wait(ctx, token) - if err != nil || reply.Cancel { - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - username, hasUser := reply.Secrets["identity"] - password, hasPass := reply.Secrets["password"] - - if hasUser && hasPass { - return username, password, nil - } - - return "", "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) -} - -func (a *IWDAgent) RequestUserPassword(network dbus.ObjectPath, user string) (string, *dbus.Error) { - ssid := a.getNetworkName(network) - - if a.stateChecker != nil && !a.stateChecker.IsConnectingTo(ssid) { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.prompts == nil { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if a.lastRequestSSID == ssid { - if a.onPromptRetry != nil { - a.onPromptRetry(ssid) - } - } - a.lastRequestSSID = ssid - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - SSID: ssid, - Fields: []string{"password"}, - }) - if err != nil { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - reply, err := a.prompts.Wait(ctx, token) - if err != nil || reply.Cancel { - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) - } - - if password, ok := reply.Secrets["password"]; ok { - return password, nil - } - - return "", dbus.NewError("net.connman.iwd.Agent.Error.Canceled", nil) -} - -func (a *IWDAgent) Cancel(reason string) *dbus.Error { - return nil -} - -func (a *IWDAgent) Release() *dbus.Error { - return nil -} - -func (a *IWDAgent) Introspect() (string, *dbus.Error) { - return iwdAgentIntrospectXML, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_networkmanager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_networkmanager.go deleted file mode 100644 index 088b7fa..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_networkmanager.go +++ /dev/null @@ -1,897 +0,0 @@ -package network - -import ( - "context" - "errors" - "fmt" - "strconv" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" - "golang.org/x/text/cases" - "golang.org/x/text/language" -) - -const ( - nmAgentManagerPath = "/org/freedesktop/NetworkManager/AgentManager" - nmAgentManagerIface = "org.freedesktop.NetworkManager.AgentManager" - nmSecretAgentIface = "org.freedesktop.NetworkManager.SecretAgent" - agentObjectPath = "/org/freedesktop/NetworkManager/SecretAgent" - agentIdentifier = "com.danklinux.NMAgent" -) - -type SecretAgent struct { - conn *dbus.Conn - objPath dbus.ObjectPath - id string - prompts PromptBroker - manager *Manager - backend *NetworkManagerBackend -} - -type ( - nmVariantMap map[string]dbus.Variant - nmSettingMap map[string]nmVariantMap -) - -const introspectXML = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - -` - -func NewSecretAgent(prompts PromptBroker, manager *Manager, backend *NetworkManagerBackend) (*SecretAgent, error) { - c, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - sa := &SecretAgent{ - conn: c, - objPath: dbus.ObjectPath(agentObjectPath), - id: agentIdentifier, - prompts: prompts, - manager: manager, - backend: backend, - } - - if err := c.Export(sa, sa.objPath, nmSecretAgentIface); err != nil { - c.Close() - return nil, fmt.Errorf("failed to export secret agent: %w", err) - } - - if err := c.Export(sa, sa.objPath, "org.freedesktop.DBus.Introspectable"); err != nil { - c.Close() - return nil, fmt.Errorf("failed to export introspection: %w", err) - } - - mgr := c.Object("org.freedesktop.NetworkManager", dbus.ObjectPath(nmAgentManagerPath)) - call := mgr.Call(nmAgentManagerIface+".Register", 0, sa.id) - if call.Err != nil { - c.Close() - return nil, fmt.Errorf("failed to register agent with NetworkManager: %w", call.Err) - } - - log.Infof("[SecretAgent] Registered with NetworkManager (id=%s, unique name=%s, fixed path=%s)", sa.id, c.Names()[0], sa.objPath) - return sa, nil -} - -func (a *SecretAgent) Close() { - if a.conn != nil { - mgr := a.conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath(nmAgentManagerPath)) - mgr.Call(nmAgentManagerIface+".Unregister", 0, a.id) - a.conn.Close() - } -} - -func (a *SecretAgent) GetSecrets( - conn map[string]nmVariantMap, - path dbus.ObjectPath, - settingName string, - hints []string, - flags uint32, -) (nmSettingMap, *dbus.Error) { - log.Infof("[SecretAgent] GetSecrets called: path=%s, setting=%s, hints=%v, flags=%d", - path, settingName, hints, flags) - - connType, displayName, vpnSvc := readConnTypeAndName(conn) - ssid := readSSID(conn) - fields := fieldsNeeded(settingName, hints, conn) - vpnPasswordFlags := readVPNPasswordFlags(conn, settingName) - - log.Infof("[SecretAgent] connType=%s, name=%s, vpnSvc=%s, fields=%v, flags=%d, vpnPasswordFlags=%d", connType, displayName, vpnSvc, fields, flags, vpnPasswordFlags) - - if a.backend != nil { - a.backend.stateMutex.RLock() - isConnecting := a.backend.state.IsConnecting - connectingSSID := a.backend.state.ConnectingSSID - isConnectingVPN := a.backend.state.IsConnectingVPN - connectingVPNUUID := a.backend.state.ConnectingVPNUUID - a.backend.stateMutex.RUnlock() - - switch connType { - case "802-11-wireless": - // If we're connecting to a WiFi network, only respond if it's the one we're connecting to - if isConnecting && connectingSSID != ssid { - log.Infof("[SecretAgent] Ignoring WiFi request for SSID '%s' - we're connecting to '%s'", ssid, connectingSSID) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } - case "vpn", "wireguard": - var connUuid string - if c, ok := conn["connection"]; ok { - if v, ok := c["uuid"]; ok { - if s, ok2 := v.Value().(string); ok2 { - connUuid = s - } - } - } - - // If we're connecting to a VPN, only respond if it's the one we're connecting to - // This prevents interfering with nmcli/other tools when our app isn't connecting - if isConnectingVPN && connUuid != connectingVPNUUID { - log.Infof("[SecretAgent] Ignoring VPN request for UUID '%s' - we're connecting to '%s'", connUuid, connectingVPNUUID) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } - } - } - - if len(fields) == 0 { - if settingName == "vpn" { - if a.backend != nil { - a.backend.stateMutex.RLock() - isConnectingVPN := a.backend.state.IsConnectingVPN - a.backend.stateMutex.RUnlock() - - if !isConnectingVPN { - log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } - - fields = inferVPNFields(conn, vpnSvc) - log.Infof("[SecretAgent] VPN with empty hints but we're connecting - inferred fields: %v", fields) - } else { - log.Infof("[SecretAgent] VPN with empty hints - deferring to other agents for %s", vpnSvc) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } - } - - if len(fields) == 0 { - const ( - NM_SETTING_SECRET_FLAG_NONE = 0 - NM_SETTING_SECRET_FLAG_AGENT_OWNED = 1 - NM_SETTING_SECRET_FLAG_NOT_SAVED = 2 - NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 4 - ) - - var passwordFlags uint32 = 0xFFFF - switch settingName { - case "802-11-wireless-security": - if wifiSecSettings, ok := conn["802-11-wireless-security"]; ok { - if flagsVariant, ok := wifiSecSettings["psk-flags"]; ok { - if pwdFlags, ok := flagsVariant.Value().(uint32); ok { - passwordFlags = pwdFlags - } - } - } - case "802-1x": - if dot1xSettings, ok := conn["802-1x"]; ok { - if flagsVariant, ok := dot1xSettings["password-flags"]; ok { - if pwdFlags, ok := flagsVariant.Value().(uint32); ok { - passwordFlags = pwdFlags - } - } - } - } - - if passwordFlags == 0xFFFF { - log.Warnf("[SecretAgent] Could not determine password-flags for empty hints - returning NoSecrets error") - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } else if passwordFlags&NM_SETTING_SECRET_FLAG_NOT_REQUIRED != 0 { - log.Infof("[SecretAgent] Secrets not required (flags=%d)", passwordFlags) - out := nmSettingMap{} - out[settingName] = nmVariantMap{} - return out, nil - } else if passwordFlags&NM_SETTING_SECRET_FLAG_AGENT_OWNED != 0 { - switch settingName { - case "802-11-wireless-security": - fields = []string{"psk"} - case "802-1x": - fields = infer8021xFields(conn) - default: - log.Warnf("[SecretAgent] Agent-owned secrets for unhandled setting %s (flags=%d)", settingName, passwordFlags) - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.NoSecrets", nil) - } - log.Infof("[SecretAgent] Agent-owned secrets, inferred fields: %v", fields) - } else { - log.Infof("[SecretAgent] No secrets needed, using system stored secrets (flags=%d)", passwordFlags) - out := nmSettingMap{} - out[settingName] = nmVariantMap{} - return out, nil - } - } - } - - reason := reasonFromFlags(flags) - if a.manager != nil && connType == "802-11-wireless" && a.manager.WasRecentlyFailed(ssid) { - reason = "wrong-password" - } - if settingName == "vpn" && isPKCS11Auth(conn, vpnSvc) { - reason = "pkcs11" - } - - var connId, connUuid string - if c, ok := conn["connection"]; ok { - if v, ok := c["id"]; ok { - if s, ok2 := v.Value().(string); ok2 { - connId = s - } - } - if v, ok := c["uuid"]; ok { - if s, ok2 := v.Value().(string); ok2 { - connUuid = s - } - } - } - - if settingName == "vpn" && a.backend != nil { - // Check for cached PKCS11 PIN first - isPKCS11Request := len(fields) == 1 && fields[0] == "key_pass" - if isPKCS11Request { - a.backend.cachedPKCS11Mu.Lock() - cached := a.backend.cachedPKCS11PIN - if cached != nil && cached.ConnectionUUID == connUuid { - a.backend.cachedPKCS11PIN = nil - a.backend.cachedPKCS11Mu.Unlock() - - log.Infof("[SecretAgent] Using cached PKCS11 PIN") - - out := nmSettingMap{} - vpnSec := nmVariantMap{} - vpnSec["secrets"] = dbus.MakeVariant(map[string]string{"key_pass": cached.PIN}) - out[settingName] = vpnSec - - return out, nil - } - a.backend.cachedPKCS11Mu.Unlock() - } - - // Check for cached VPN password - a.backend.cachedVPNCredsMu.Lock() - cached := a.backend.cachedVPNCreds - if cached != nil && cached.ConnectionUUID == connUuid { - a.backend.cachedVPNCreds = nil - a.backend.cachedVPNCredsMu.Unlock() - - log.Infof("[SecretAgent] Using cached password from pre-activation prompt") - - out := nmSettingMap{} - vpnSec := nmVariantMap{} - vpnSec["secrets"] = dbus.MakeVariant(map[string]string{"password": cached.Password}) - out[settingName] = vpnSec - - if cached.SavePassword { - a.backend.pendingVPNSaveMu.Lock() - a.backend.pendingVPNSave = &pendingVPNCredentials{ - ConnectionPath: string(path), - Password: cached.Password, - SavePassword: true, - } - a.backend.pendingVPNSaveMu.Unlock() - } - - return out, nil - } - a.backend.cachedVPNCredsMu.Unlock() - - a.backend.cachedGPSamlMu.Lock() - cachedGPSaml := a.backend.cachedGPSamlCookie - if cachedGPSaml != nil && cachedGPSaml.ConnectionUUID == connUuid { - a.backend.cachedGPSamlMu.Unlock() - - log.Infof("[SecretAgent] Using cached GlobalProtect SAML cookie for %s", connUuid) - - return buildGPSamlSecretsResponse(settingName, cachedGPSaml.Cookie, cachedGPSaml.Host, cachedGPSaml.Fingerprint), nil - } - a.backend.cachedGPSamlMu.Unlock() - - if len(fields) == 1 && fields[0] == "gp-saml" { - gateway := "" - protocol := "" - if vpnSettings, ok := conn["vpn"]; ok { - if dataVariant, ok := vpnSettings["data"]; ok { - if dataMap, ok := dataVariant.Value().(map[string]string); ok { - if gw, ok := dataMap["gateway"]; ok { - gateway = gw - } - if proto, ok := dataMap["protocol"]; ok && proto != "" { - protocol = proto - } - } - } - } - - if protocol != "gp" { - return nil, dbus.MakeFailedError(fmt.Errorf("gp-saml auth only supported for GlobalProtect (protocol=gp), got: %s", protocol)) - } - - log.Infof("[SecretAgent] Starting GlobalProtect SAML authentication for gateway=%s", gateway) - - samlCtx, samlCancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer samlCancel() - - authResult, err := a.backend.runGlobalProtectSAMLAuth(samlCtx, gateway, protocol) - if err != nil { - log.Warnf("[SecretAgent] GlobalProtect SAML authentication failed: %v", err) - return nil, dbus.MakeFailedError(fmt.Errorf("GlobalProtect SAML authentication failed: %w", err)) - } - - log.Infof("[SecretAgent] GlobalProtect SAML authentication successful, returning cookie to NetworkManager") - - a.backend.cachedGPSamlMu.Lock() - a.backend.cachedGPSamlCookie = &cachedGPSamlCookie{ - ConnectionUUID: connUuid, - Cookie: authResult.Cookie, - Host: authResult.Host, - User: authResult.User, - Fingerprint: authResult.Fingerprint, - } - a.backend.cachedGPSamlMu.Unlock() - - return buildGPSamlSecretsResponse(settingName, authResult.Cookie, authResult.Host, authResult.Fingerprint), nil - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - token, err := a.prompts.Ask(ctx, PromptRequest{ - Name: displayName, - SSID: ssid, - ConnType: connType, - VpnService: vpnSvc, - SettingName: settingName, - Fields: fields, - FieldsInfo: buildFieldsInfo(settingName, fields, vpnSvc), - Hints: hints, - Reason: reason, - ConnectionId: connId, - ConnectionUuid: connUuid, - ConnectionPath: string(path), - }) - if err != nil { - log.Warnf("[SecretAgent] Failed to create prompt: %v", err) - return nil, dbus.MakeFailedError(err) - } - - log.Infof("[SecretAgent] Waiting for user input (token=%s)", token) - reply, err := a.prompts.Wait(ctx, token) - if err != nil { - log.Warnf("[SecretAgent] Prompt failed or cancelled: %v", err) - - // Clear connecting state immediately on cancellation - if a.backend != nil { - a.backend.stateMutex.Lock() - wasConnecting := a.backend.state.IsConnecting - wasConnectingVPN := a.backend.state.IsConnectingVPN - cancelledSSID := a.backend.state.ConnectingSSID - cancelledVPNUUID := a.backend.state.ConnectingVPNUUID - if wasConnecting || wasConnectingVPN { - log.Infof("[SecretAgent] Clearing connecting state due to cancelled prompt") - a.backend.state.IsConnecting = false - a.backend.state.ConnectingSSID = "" - a.backend.state.IsConnectingVPN = false - a.backend.state.ConnectingVPNUUID = "" - } - a.backend.stateMutex.Unlock() - - // If this was a WiFi connection that was just cancelled, remove the connection profile - // (it was created with AddConnection but activation was cancelled) - if wasConnecting && cancelledSSID != "" && connType == "802-11-wireless" { - log.Infof("[SecretAgent] Removing connection profile for cancelled WiFi connection: %s", cancelledSSID) - if err := a.backend.ForgetWiFiNetwork(cancelledSSID); err != nil { - log.Warnf("[SecretAgent] Failed to remove cancelled connection profile: %v", err) - } - } - - // If this was a VPN connection that was cancelled, deactivate it - if wasConnectingVPN && cancelledVPNUUID != "" { - log.Infof("[SecretAgent] Deactivating cancelled VPN connection: %s", cancelledVPNUUID) - if err := a.backend.DisconnectVPN(cancelledVPNUUID); err != nil { - log.Warnf("[SecretAgent] Failed to deactivate cancelled VPN: %v", err) - } - } - - if (wasConnecting || wasConnectingVPN) && a.backend.onStateChange != nil { - a.backend.onStateChange() - } - } - - if reply.Cancel || errors.Is(err, errdefs.ErrSecretPromptCancelled) { - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.UserCanceled", nil) - } - - if errors.Is(err, errdefs.ErrSecretPromptTimeout) { - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.Failed", nil) - } - return nil, dbus.NewError("org.freedesktop.NetworkManager.SecretAgent.Error.Failed", nil) - } - - log.Infof("[SecretAgent] User provided secrets, save=%v", reply.Save) - - out := nmSettingMap{} - sec := nmVariantMap{} - - var vpnUsername string - for k, v := range reply.Secrets { - if settingName == "vpn" && k == "username" { - vpnUsername = v - } - sec[k] = dbus.MakeVariant(v) - } - - // Check if this is PKCS11 auth (key_pass) - pin, isPKCS11 := reply.Secrets["key_pass"] - - switch settingName { - case "vpn": - // VPN secrets must be wrapped in a "secrets" key per NM spec - secretsDict := make(map[string]string) - for k, v := range reply.Secrets { - if k != "username" { - secretsDict[k] = v - } - } - vpnSec := nmVariantMap{} - vpnSec["secrets"] = dbus.MakeVariant(secretsDict) - out[settingName] = vpnSec - log.Infof("[SecretAgent] Returning VPN secrets with %d fields for %s", len(secretsDict), vpnSvc) - - // Cache PKCS11 PIN in case GetSecrets is called again during activation - if isPKCS11 && a.backend != nil { - a.backend.cachedPKCS11Mu.Lock() - a.backend.cachedPKCS11PIN = &cachedPKCS11PIN{ - ConnectionUUID: connUuid, - PIN: pin, - } - a.backend.cachedPKCS11Mu.Unlock() - log.Infof("[SecretAgent] Cached PKCS11 PIN for potential re-request") - } - case "802-1x": - secretsOnly := nmVariantMap{} - for k, v := range reply.Secrets { - switch k { - case "password", "private-key-password", "phase2-private-key-password", "pin": - secretsOnly[k] = dbus.MakeVariant(v) - } - } - out[settingName] = secretsOnly - - if identity, ok := reply.Secrets["identity"]; ok && identity != "" { - a.save8021xIdentity(path, identity) - } - log.Infof("[SecretAgent] Returning 802-1x enterprise secrets with %d fields", len(secretsOnly)) - default: - out[settingName] = sec - } - if settingName == "vpn" && a.backend != nil && !isPKCS11 && (vpnUsername != "" || reply.Save) { - pw := reply.Secrets["password"] - a.backend.pendingVPNSaveMu.Lock() - a.backend.pendingVPNSave = &pendingVPNCredentials{ - ConnectionPath: string(path), - Username: vpnUsername, - Password: pw, - SavePassword: reply.Save, - } - a.backend.pendingVPNSaveMu.Unlock() - log.Infof("[SecretAgent] Queued credentials persist for after connection succeeds") - } - - return out, nil -} - -func (a *SecretAgent) DeleteSecrets(conn map[string]nmVariantMap, path dbus.ObjectPath) *dbus.Error { - ssid := readSSID(conn) - log.Infof("[SecretAgent] DeleteSecrets called: path=%s, SSID=%s", path, ssid) - return nil -} - -func (a *SecretAgent) DeleteSecrets2(path dbus.ObjectPath, setting string) *dbus.Error { - log.Infof("[SecretAgent] DeleteSecrets2 (alternate) called: path=%s, setting=%s", path, setting) - return nil -} - -func (a *SecretAgent) CancelGetSecrets(path dbus.ObjectPath, settingName string) *dbus.Error { - log.Infof("[SecretAgent] CancelGetSecrets called: path=%s, setting=%s", path, settingName) - - if a.prompts != nil { - if err := a.prompts.Cancel(string(path), settingName); err != nil { - log.Warnf("[SecretAgent] Failed to cancel prompt: %v", err) - } - } - - return nil -} - -func (a *SecretAgent) Introspect() (string, *dbus.Error) { - return introspectXML, nil -} - -func (a *SecretAgent) save8021xIdentity(path dbus.ObjectPath, identity string) { - connObj := a.conn.Object("org.freedesktop.NetworkManager", path) - var existing map[string]map[string]dbus.Variant - if err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.GetSettings", 0).Store(&existing); err != nil { - log.Warnf("[SecretAgent] Failed to get settings for identity save: %v", err) - return - } - - settings := make(map[string]map[string]dbus.Variant) - if connSection, ok := existing["connection"]; ok { - settings["connection"] = connSection - } - - dot1x, ok := existing["802-1x"] - if !ok { - dot1x = make(map[string]dbus.Variant) - } - dot1x["identity"] = dbus.MakeVariant(identity) - settings["802-1x"] = dot1x - - var result map[string]dbus.Variant - if err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.Update2", 0, - settings, uint32(0x1), map[string]dbus.Variant{}).Store(&result); err != nil { - log.Warnf("[SecretAgent] Failed to save 802.1x identity: %v", err) - return - } - log.Infof("[SecretAgent] Saved 802.1x identity to connection profile") -} - -func readSSID(conn map[string]nmVariantMap) string { - if w, ok := conn["802-11-wireless"]; ok { - if v, ok := w["ssid"]; ok { - if b, ok := v.Value().([]byte); ok { - return string(b) - } - if s, ok := v.Value().(string); ok { - return s - } - } - } - return "" -} - -func readConnTypeAndName(conn map[string]nmVariantMap) (string, string, string) { - var connType, name, svc string - if c, ok := conn["connection"]; ok { - if v, ok := c["type"]; ok { - if s, ok2 := v.Value().(string); ok2 { - connType = s - } - } - if v, ok := c["id"]; ok { - if s, ok2 := v.Value().(string); ok2 { - name = s - } - } - } - if vpn, ok := conn["vpn"]; ok { - if v, ok := vpn["service-type"]; ok { - if s, ok2 := v.Value().(string); ok2 { - svc = s - } - } - } - if name == "" && connType == "802-11-wireless" { - name = readSSID(conn) - } - return connType, name, svc -} - -func fieldsNeeded(setting string, hints []string, conn map[string]nmVariantMap) []string { - switch setting { - case "802-11-wireless-security": - return []string{"psk"} - case "802-1x": - if len(hints) > 0 { - return hints - } - return infer8021xFields(conn) - case "vpn": - return hints - default: - return []string{} - } -} - -func infer8021xFields(conn map[string]nmVariantMap) []string { - dot1x, ok := conn["802-1x"] - if !ok { - return []string{"identity", "password"} - } - - var fields []string - - if v, ok := dot1x["identity"]; ok { - if id, ok := v.Value().(string); ok && id != "" { - // identity already stored, don't ask again - } else { - fields = append(fields, "identity") - } - } else { - fields = append(fields, "identity") - } - - var eapMethods []string - if v, ok := dot1x["eap"]; ok { - if methods, ok := v.Value().([]string); ok { - eapMethods = methods - } - } - - switch { - case len(eapMethods) > 0 && eapMethods[0] == "tls": - fields = append(fields, "private-key-password") - default: - fields = append(fields, "password") - } - - return fields -} - -func buildFieldsInfo(setting string, fields []string, vpnService string) []FieldInfo { - result := make([]FieldInfo, 0, len(fields)) - for _, f := range fields { - info := FieldInfo{Name: f} - switch setting { - case "802-11-wireless-security": - info.Label = "Password" - info.IsSecret = true - case "802-1x": - switch f { - case "identity": - info.Label = "Username" - info.IsSecret = false - case "password": - info.Label = "Password" - info.IsSecret = true - default: - info.Label = f - info.IsSecret = true - } - case "vpn": - info.Label, info.IsSecret = vpnFieldMeta(f, vpnService) - default: - info.Label = f - info.IsSecret = true - } - result = append(result, info) - } - return result -} - -func inferVPNFields(conn map[string]nmVariantMap, vpnService string) []string { - fields := []string{"password"} - - vpnSettings, ok := conn["vpn"] - if !ok { - return fields - } - - dataVariant, ok := vpnSettings["data"] - if !ok { - return fields - } - - dataMap, ok := dataVariant.Value().(map[string]string) - if !ok { - return fields - } - - connType := dataMap["connection-type"] - - switch { - case strings.Contains(vpnService, "openconnect"): - protocol := dataMap["protocol"] - authType := dataMap["authtype"] - username := dataMap["username"] - - if authType == "cert" && strings.HasPrefix(dataMap["usercert"], "pkcs11:") { - return []string{"key_pass"} - } - - if needsExternalBrowserAuth(protocol, authType, username, dataMap) { - switch protocol { - case "gp": - log.Infof("[SecretAgent] GlobalProtect SAML auth detected") - return []string{"gp-saml"} - default: - log.Infof("[SecretAgent] External browser auth detected for protocol '%s' but only GlobalProtect (gp) SAML is currently supported, falling back to credentials", protocol) - } - } - - if username == "" { - fields = []string{"username", "password"} - } - case strings.Contains(vpnService, "openvpn"): - if connType == "password" || connType == "password-tls" { - if dataMap["username"] == "" { - fields = []string{"username", "password"} - } - } - case strings.Contains(vpnService, "vpnc"), strings.Contains(vpnService, "l2tp"), - strings.Contains(vpnService, "pptp"): - if dataMap["username"] == "" { - fields = []string{"username", "password"} - } - } - - return fields -} - -func needsExternalBrowserAuth(protocol, authType, username string, data map[string]string) bool { - if method, ok := data["saml-auth-method"]; ok { - if method == "REDIRECT" || method == "POST" { - return true - } - } - - if authType != "" && authType != "password" && authType != "cert" { - return true - } - - switch protocol { - case "gp": - if authType == "" && username == "" { - return true - } - } - - return false -} - -func vpnFieldMeta(field, vpnService string) (label string, isSecret bool) { - switch field { - case "gp-saml": - return "GlobalProtect SAML/SSO", false - case "key_pass": - return "PIN", true - case "password": - return "Password", true - case "Xauth password": - return "IPSec Password", true - case "IPSec secret": - return "IPSec Pre-Shared Key", true - case "cert-pass": - return "Certificate Password", true - case "http-proxy-password": - return "HTTP Proxy Password", true - case "username": - return "Username", false - case "Xauth username": - return "IPSec Username", false - case "proxy-password": - return "Proxy Password", true - case "private-key-password": - return "Private Key Password", true - } - titleCaser := cases.Title(language.English) - if strings.HasSuffix(field, "password") || strings.HasSuffix(field, "secret") || - strings.HasSuffix(field, "pass") || strings.HasSuffix(field, "psk") { - return titleCaser.String(strings.ReplaceAll(field, "-", " ")), true - } - return titleCaser.String(strings.ReplaceAll(field, "-", " ")), false -} - -func isPKCS11Auth(conn map[string]nmVariantMap, vpnService string) bool { - if !strings.Contains(vpnService, "openconnect") { - return false - } - vpnSettings, ok := conn["vpn"] - if !ok { - return false - } - dataVariant, ok := vpnSettings["data"] - if !ok { - return false - } - dataMap, ok := dataVariant.Value().(map[string]string) - if !ok { - return false - } - return dataMap["authtype"] == "cert" && strings.HasPrefix(dataMap["usercert"], "pkcs11:") -} - -func readVPNPasswordFlags(conn map[string]nmVariantMap, settingName string) uint32 { - if settingName != "vpn" { - return 0xFFFF - } - - vpnSettings, ok := conn["vpn"] - if !ok { - return 0xFFFF - } - - dataVariant, ok := vpnSettings["data"] - if !ok { - return 0xFFFF - } - - dataMap, ok := dataVariant.Value().(map[string]string) - if !ok { - return 0xFFFF - } - - flagsStr, ok := dataMap["password-flags"] - if !ok { - return 0xFFFF - } - - flags64, err := strconv.ParseUint(flagsStr, 10, 32) - if err != nil { - return 0xFFFF - } - - return uint32(flags64) -} - -func reasonFromFlags(flags uint32) string { - const ( - NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0 - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1 - NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 0x2 - NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 0x4 - NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE = 0x8 - NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM = 0x80000000 - NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS = 0x40000000 - ) - - if flags&NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW != 0 { - return "wrong-password" - } - if flags&NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED != 0 { - return "user-requested" - } - return "required" -} - -func buildGPSamlSecretsResponse(settingName, cookie, host, fingerprint string) nmSettingMap { - out := nmSettingMap{} - vpnSec := nmVariantMap{} - - secrets := map[string]string{ - "cookie": cookie, - "gateway": host, - "gwcert": fingerprint, - } - vpnSec["secrets"] = dbus.MakeVariant(secrets) - - out[settingName] = vpnSec - return out -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_networkmanager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_networkmanager_test.go deleted file mode 100644 index 2040986..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/agent_networkmanager_test.go +++ /dev/null @@ -1,355 +0,0 @@ -package network - -import ( - "testing" - - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" -) - -func TestNeedsExternalBrowserAuth(t *testing.T) { - tests := []struct { - name string - protocol string - authType string - username string - data map[string]string - expected bool - }{ - { - name: "GP with saml-auth-method REDIRECT", - protocol: "gp", - authType: "password", - username: "user", - data: map[string]string{"saml-auth-method": "REDIRECT"}, - expected: true, - }, - { - name: "GP with saml-auth-method POST", - protocol: "gp", - authType: "password", - username: "user", - data: map[string]string{"saml-auth-method": "POST"}, - expected: true, - }, - { - name: "GP with no authtype and no username", - protocol: "gp", - authType: "", - username: "", - data: map[string]string{}, - expected: true, - }, - { - name: "GP with username and password authtype", - protocol: "gp", - authType: "password", - username: "john", - data: map[string]string{}, - expected: false, - }, - { - name: "GP with username but no authtype", - protocol: "gp", - authType: "", - username: "john", - data: map[string]string{}, - expected: false, - }, - { - name: "GP with authtype but no username - should detect SAML", - protocol: "gp", - authType: "", - username: "", - data: map[string]string{}, - expected: true, - }, - { - name: "pulse with SAML", - protocol: "pulse", - authType: "", - username: "", - data: map[string]string{"saml-auth-method": "REDIRECT"}, - expected: true, - }, - { - name: "fortinet with non-password authtype", - protocol: "fortinet", - authType: "saml", - username: "", - data: map[string]string{}, - expected: true, - }, - { - name: "anyconnect with cert", - protocol: "anyconnect", - authType: "cert", - username: "", - data: map[string]string{}, - expected: false, - }, - { - name: "anyconnect with password", - protocol: "anyconnect", - authType: "password", - username: "user", - data: map[string]string{}, - expected: false, - }, - { - name: "empty protocol", - protocol: "", - authType: "", - username: "", - data: map[string]string{}, - expected: false, - }, - { - name: "GP with cert authtype", - protocol: "gp", - authType: "cert", - username: "", - data: map[string]string{}, - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := needsExternalBrowserAuth(tt.protocol, tt.authType, tt.username, tt.data) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestBuildGPSamlSecretsResponse(t *testing.T) { - tests := []struct { - name string - settingName string - cookie string - host string - fingerprint string - }{ - { - name: "all fields populated", - settingName: "vpn", - cookie: "authcookie=abc123&portal=GATE", - host: "vpn.example.com", - fingerprint: "pin-sha256:ABCD1234", - }, - { - name: "empty fingerprint", - settingName: "vpn", - cookie: "authcookie=xyz", - host: "10.0.0.1", - fingerprint: "", - }, - { - name: "complex cookie with special chars", - settingName: "vpn", - cookie: "authcookie=077058d3bc81&portal=PANGP_GW_01-N&user=john.doe@example.com&domain=Default&preferred-ip=192.168.1.100", - host: "connect.seclore.com", - fingerprint: "pin-sha256:xp3scfzy3rOgQEXnfPiYKrUk7D66a8b8O+gEXaMPleE=", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := buildGPSamlSecretsResponse(tt.settingName, tt.cookie, tt.host, tt.fingerprint) - - assert.NotNil(t, result) - assert.Contains(t, result, tt.settingName) - - vpnSec := result[tt.settingName] - assert.NotNil(t, vpnSec) - - secretsVariant, ok := vpnSec["secrets"] - assert.True(t, ok, "secrets key should exist") - - secrets, ok := secretsVariant.Value().(map[string]string) - assert.True(t, ok, "secrets should be map[string]string") - - assert.Equal(t, tt.cookie, secrets["cookie"]) - assert.Equal(t, tt.host, secrets["gateway"]) - assert.Equal(t, tt.fingerprint, secrets["gwcert"]) - }) - } -} - -func TestVpnFieldMeta_GPSaml(t *testing.T) { - label, isSecret := vpnFieldMeta("gp-saml", "org.freedesktop.NetworkManager.openconnect") - - assert.Equal(t, "GlobalProtect SAML/SSO", label) - assert.False(t, isSecret, "gp-saml should not be marked as secret") -} - -func TestVpnFieldMeta_StandardFields(t *testing.T) { - tests := []struct { - field string - vpnService string - expectedLabel string - expectedSecret bool - }{ - { - field: "username", - vpnService: "org.freedesktop.NetworkManager.openconnect", - expectedLabel: "Username", - expectedSecret: false, - }, - { - field: "password", - vpnService: "org.freedesktop.NetworkManager.openconnect", - expectedLabel: "Password", - expectedSecret: true, - }, - { - field: "key_pass", - vpnService: "org.freedesktop.NetworkManager.openconnect", - expectedLabel: "PIN", - expectedSecret: true, - }, - } - - for _, tt := range tests { - t.Run(tt.field, func(t *testing.T) { - label, isSecret := vpnFieldMeta(tt.field, tt.vpnService) - assert.Equal(t, tt.expectedLabel, label) - assert.Equal(t, tt.expectedSecret, isSecret) - }) - } -} - -func TestInferVPNFields_GPSaml(t *testing.T) { - tests := []struct { - name string - vpnService string - dataMap map[string]string - expectedLen int - shouldHave []string - }{ - { - name: "GP with no authtype and no username - should require SAML", - vpnService: "org.freedesktop.NetworkManager.openconnect", - dataMap: map[string]string{ - "protocol": "gp", - "gateway": "vpn.example.com", - }, - expectedLen: 1, - shouldHave: []string{"gp-saml"}, - }, - { - name: "GP with saml-auth-method REDIRECT", - vpnService: "org.freedesktop.NetworkManager.openconnect", - dataMap: map[string]string{ - "protocol": "gp", - "gateway": "vpn.example.com", - "saml-auth-method": "REDIRECT", - "username": "john", - }, - expectedLen: 1, - shouldHave: []string{"gp-saml"}, - }, - { - name: "GP with saml-auth-method POST", - vpnService: "org.freedesktop.NetworkManager.openconnect", - dataMap: map[string]string{ - "protocol": "gp", - "gateway": "vpn.example.com", - "saml-auth-method": "POST", - }, - expectedLen: 1, - shouldHave: []string{"gp-saml"}, - }, - { - name: "GP with username and password authtype - should use credentials", - vpnService: "org.freedesktop.NetworkManager.openconnect", - dataMap: map[string]string{ - "protocol": "gp", - "gateway": "vpn.example.com", - "authtype": "password", - "username": "john", - }, - expectedLen: 1, - shouldHave: []string{"password"}, - }, - { - name: "GP with username but no authtype - password only", - vpnService: "org.freedesktop.NetworkManager.openconnect", - dataMap: map[string]string{ - "protocol": "gp", - "gateway": "vpn.example.com", - "username": "john", - }, - expectedLen: 1, - shouldHave: []string{"password"}, - }, - { - name: "GP with PKCS11 cert", - vpnService: "org.freedesktop.NetworkManager.openconnect", - dataMap: map[string]string{ - "protocol": "gp", - "gateway": "vpn.example.com", - "authtype": "cert", - "usercert": "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II", - }, - expectedLen: 1, - shouldHave: []string{"key_pass"}, - }, - { - name: "non-GP protocol (anyconnect)", - vpnService: "org.freedesktop.NetworkManager.openconnect", - dataMap: map[string]string{ - "protocol": "anyconnect", - "gateway": "vpn.example.com", - }, - expectedLen: 2, - shouldHave: []string{"username", "password"}, - }, - { - name: "OpenVPN with username", - vpnService: "org.freedesktop.NetworkManager.openvpn", - dataMap: map[string]string{ - "connection-type": "password", - "username": "john", - }, - expectedLen: 1, - shouldHave: []string{"password"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Convert dataMap to nmVariantMap - vpnSettings := make(nmVariantMap) - vpnSettings["data"] = dbus.MakeVariant(tt.dataMap) - vpnSettings["service-type"] = dbus.MakeVariant(tt.vpnService) - - conn := make(map[string]nmVariantMap) - conn["vpn"] = vpnSettings - - fields := inferVPNFields(conn, tt.vpnService) - - assert.Len(t, fields, tt.expectedLen, "unexpected number of fields") - if len(tt.shouldHave) > 0 { - for _, expected := range tt.shouldHave { - assert.Contains(t, fields, expected, "should contain field: %s", expected) - } - } - }) - } -} - -func TestNmVariantMap(t *testing.T) { - // Test that nmVariantMap and nmSettingMap work correctly - settingMap := make(nmSettingMap) - variantMap := make(nmVariantMap) - - variantMap["test-key"] = dbus.MakeVariant("test-value") - settingMap["test-setting"] = variantMap - - assert.Contains(t, settingMap, "test-setting") - assert.Contains(t, settingMap["test-setting"], "test-key") - - value := settingMap["test-setting"]["test-key"].Value() - assert.Equal(t, "test-value", value) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend.go deleted file mode 100644 index f1bb04d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend.go +++ /dev/null @@ -1,80 +0,0 @@ -package network - -type Backend interface { - Initialize() error - Close() - - GetWiFiEnabled() (bool, error) - SetWiFiEnabled(enabled bool) error - - ScanWiFi() error - ScanWiFiDevice(device string) error - GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) - GetWiFiQRCodeContent(ssid string) (string, error) - GetWiFiDevices() []WiFiDevice - - ConnectWiFi(req ConnectionRequest) error - DisconnectWiFi() error - DisconnectWiFiDevice(device string) error - ForgetWiFiNetwork(ssid string) error - SetWiFiAutoconnect(ssid string, autoconnect bool) error - - GetEthernetDevices() []EthernetDevice - GetWiredConnections() ([]WiredConnection, error) - GetWiredNetworkDetails(uuid string) (*WiredNetworkInfoResponse, error) - ConnectEthernet() error - DisconnectEthernet() error - DisconnectEthernetDevice(device string) error - ActivateWiredConnection(uuid string) error - - ListVPNProfiles() ([]VPNProfile, error) - ListActiveVPN() ([]VPNActive, error) - ConnectVPN(uuidOrName string, singleActive bool) error - DisconnectVPN(uuidOrName string) error - DisconnectAllVPN() error - ClearVPNCredentials(uuidOrName string) error - ListVPNPlugins() ([]VPNPlugin, error) - ImportVPN(filePath string, name string) (*VPNImportResult, error) - GetVPNConfig(uuidOrName string) (*VPNConfig, error) - UpdateVPNConfig(uuid string, updates map[string]any) error - SetVPNCredentials(uuid string, username string, password string, save bool) error - DeleteVPN(uuidOrName string) error - - GetCurrentState() (*BackendState, error) - - StartMonitoring(onStateChange func()) error - StopMonitoring() - - GetPromptBroker() PromptBroker - SetPromptBroker(broker PromptBroker) error - SubmitCredentials(token string, secrets map[string]string, save bool) error - CancelCredentials(token string) error -} - -type BackendState struct { - Backend string - NetworkStatus NetworkStatus - EthernetIP string - EthernetDevice string - EthernetConnected bool - EthernetConnectionUuid string - EthernetDevices []EthernetDevice - WiFiIP string - WiFiDevice string - WiFiConnected bool - WiFiEnabled bool - WiFiSSID string - WiFiBSSID string - WiFiSignal uint8 - WiFiNetworks []WiFiNetwork - WiFiDevices []WiFiDevice - WiredConnections []WiredConnection - VPNProfiles []VPNProfile - VPNActive []VPNActive - IsConnecting bool - ConnectingSSID string - ConnectingDevice string - IsConnectingVPN bool - ConnectingVPNUUID string - LastError string -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_hybrid_iwd_networkd.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_hybrid_iwd_networkd.go deleted file mode 100644 index ce883ba..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_hybrid_iwd_networkd.go +++ /dev/null @@ -1,245 +0,0 @@ -package network - -import ( - "fmt" -) - -type HybridIwdNetworkdBackend struct { - wifi *IWDBackend - l3 *SystemdNetworkdBackend - onStateChange func() -} - -func NewHybridIwdNetworkdBackend(w *IWDBackend, n *SystemdNetworkdBackend) (*HybridIwdNetworkdBackend, error) { - return &HybridIwdNetworkdBackend{ - wifi: w, - l3: n, - }, nil -} - -func (b *HybridIwdNetworkdBackend) Initialize() error { - if err := b.wifi.Initialize(); err != nil { - return fmt.Errorf("iwd init: %w", err) - } - if err := b.l3.Initialize(); err != nil { - return fmt.Errorf("networkd init: %w", err) - } - return nil -} - -func (b *HybridIwdNetworkdBackend) Close() { - b.wifi.Close() - b.l3.Close() -} - -func (b *HybridIwdNetworkdBackend) StartMonitoring(onStateChange func()) error { - b.onStateChange = onStateChange - - mergedCallback := func() { - ws, _ := b.wifi.GetCurrentState() - ls, _ := b.l3.GetCurrentState() - - if ws != nil && ls != nil && ws.WiFiDevice != "" && ls.WiFiIP != "" { - b.wifi.MarkIPConfigSeen() - } - - if b.onStateChange != nil { - b.onStateChange() - } - } - - if err := b.wifi.StartMonitoring(mergedCallback); err != nil { - return fmt.Errorf("wifi monitoring: %w", err) - } - if err := b.l3.StartMonitoring(mergedCallback); err != nil { - return fmt.Errorf("l3 monitoring: %w", err) - } - - return nil -} - -func (b *HybridIwdNetworkdBackend) StopMonitoring() { - b.wifi.StopMonitoring() - b.l3.StopMonitoring() -} - -func (b *HybridIwdNetworkdBackend) GetCurrentState() (*BackendState, error) { - ws, err := b.wifi.GetCurrentState() - if err != nil { - return nil, err - } - ls, err := b.l3.GetCurrentState() - if err != nil { - return nil, err - } - - merged := *ws - merged.Backend = "iwd+networkd" - - merged.WiFiIP = ls.WiFiIP - merged.EthernetConnected = ls.EthernetConnected - merged.EthernetIP = ls.EthernetIP - merged.EthernetDevice = ls.EthernetDevice - merged.EthernetConnectionUuid = ls.EthernetConnectionUuid - merged.WiredConnections = ls.WiredConnections - merged.EthernetDevices = ls.EthernetDevices - - if ls.EthernetConnected && ls.EthernetIP != "" { - merged.NetworkStatus = StatusEthernet - } else if ws.WiFiConnected && ls.WiFiIP != "" { - merged.NetworkStatus = StatusWiFi - } else { - merged.NetworkStatus = StatusDisconnected - } - - return &merged, nil -} - -func (b *HybridIwdNetworkdBackend) GetWiFiEnabled() (bool, error) { - return b.wifi.GetWiFiEnabled() -} - -func (b *HybridIwdNetworkdBackend) SetWiFiEnabled(enabled bool) error { - return b.wifi.SetWiFiEnabled(enabled) -} - -func (b *HybridIwdNetworkdBackend) ScanWiFi() error { - return b.wifi.ScanWiFi() -} - -func (b *HybridIwdNetworkdBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) { - return b.wifi.GetWiFiNetworkDetails(ssid) -} - -func (b *HybridIwdNetworkdBackend) GetWiFiQRCodeContent(ssid string) (string, error) { - return b.wifi.GetWiFiQRCodeContent(ssid) -} - -func (b *HybridIwdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error { - if err := b.wifi.ConnectWiFi(req); err != nil { - return err - } - - ws, err := b.wifi.GetCurrentState() - if err == nil && ws.WiFiDevice != "" { - b.l3.EnsureDhcpUp(ws.WiFiDevice) //nolint:errcheck - } - - return nil -} - -func (b *HybridIwdNetworkdBackend) DisconnectWiFi() error { - return b.wifi.DisconnectWiFi() -} - -func (b *HybridIwdNetworkdBackend) ForgetWiFiNetwork(ssid string) error { - return b.wifi.ForgetWiFiNetwork(ssid) -} - -func (b *HybridIwdNetworkdBackend) GetWiredConnections() ([]WiredConnection, error) { - return b.l3.GetWiredConnections() -} - -func (b *HybridIwdNetworkdBackend) GetWiredNetworkDetails(uuid string) (*WiredNetworkInfoResponse, error) { - return b.l3.GetWiredNetworkDetails(uuid) -} - -func (b *HybridIwdNetworkdBackend) ConnectEthernet() error { - return b.l3.ConnectEthernet() -} - -func (b *HybridIwdNetworkdBackend) DisconnectEthernet() error { - return b.l3.DisconnectEthernet() -} - -func (b *HybridIwdNetworkdBackend) DisconnectEthernetDevice(device string) error { - return b.l3.DisconnectEthernetDevice(device) -} - -func (b *HybridIwdNetworkdBackend) GetEthernetDevices() []EthernetDevice { - return b.l3.GetEthernetDevices() -} - -func (b *HybridIwdNetworkdBackend) ActivateWiredConnection(uuid string) error { - return b.l3.ActivateWiredConnection(uuid) -} - -func (b *HybridIwdNetworkdBackend) ListVPNProfiles() ([]VPNProfile, error) { - return []VPNProfile{}, nil -} - -func (b *HybridIwdNetworkdBackend) ListActiveVPN() ([]VPNActive, error) { - return []VPNActive{}, nil -} - -func (b *HybridIwdNetworkdBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) DisconnectVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) DisconnectAllVPN() error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) ClearVPNCredentials(uuidOrName string) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) ListVPNPlugins() ([]VPNPlugin, error) { - return []VPNPlugin{}, nil -} - -func (b *HybridIwdNetworkdBackend) ImportVPN(filePath string, name string) (*VPNImportResult, error) { - return nil, fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) GetVPNConfig(uuidOrName string) (*VPNConfig, error) { - return nil, fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) UpdateVPNConfig(uuid string, updates map[string]any) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) DeleteVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} - -func (b *HybridIwdNetworkdBackend) GetPromptBroker() PromptBroker { - return b.wifi.GetPromptBroker() -} - -func (b *HybridIwdNetworkdBackend) SetPromptBroker(broker PromptBroker) error { - return b.wifi.SetPromptBroker(broker) -} - -func (b *HybridIwdNetworkdBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - return b.wifi.SubmitCredentials(token, secrets, save) -} - -func (b *HybridIwdNetworkdBackend) CancelCredentials(token string) error { - return b.wifi.CancelCredentials(token) -} - -func (b *HybridIwdNetworkdBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - return b.wifi.SetWiFiAutoconnect(ssid, autoconnect) -} - -func (b *HybridIwdNetworkdBackend) ScanWiFiDevice(device string) error { - return b.wifi.ScanWiFiDevice(device) -} - -func (b *HybridIwdNetworkdBackend) DisconnectWiFiDevice(device string) error { - return b.wifi.DisconnectWiFiDevice(device) -} - -func (b *HybridIwdNetworkdBackend) GetWiFiDevices() []WiFiDevice { - return b.wifi.GetWiFiDevices() -} - -func (b *HybridIwdNetworkdBackend) SetVPNCredentials(uuid, username, password string, save bool) error { - return fmt.Errorf("VPN not supported in hybrid mode") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_hybrid_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_hybrid_test.go deleted file mode 100644 index cd061ed..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_hybrid_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestHybridIwdNetworkdBackend_New(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - - hybrid, err := NewHybridIwdNetworkdBackend(wifi, l3) - assert.NoError(t, err) - assert.NotNil(t, hybrid) - assert.NotNil(t, hybrid.wifi) - assert.NotNil(t, hybrid.l3) -} - -func TestHybridIwdNetworkdBackend_GetCurrentState_MergesState(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - wifi.state.WiFiConnected = true - wifi.state.WiFiSSID = "TestNetwork" - wifi.state.WiFiBSSID = "00:11:22:33:44:55" - wifi.state.WiFiSignal = 75 - wifi.state.WiFiDevice = "wlan0" - - l3.state.WiFiIP = "192.168.1.100" - l3.state.EthernetConnected = false - - state, err := hybrid.GetCurrentState() - assert.NoError(t, err) - assert.NotNil(t, state) - assert.Equal(t, "iwd+networkd", state.Backend) - assert.Equal(t, "TestNetwork", state.WiFiSSID) - assert.Equal(t, "00:11:22:33:44:55", state.WiFiBSSID) - assert.Equal(t, uint8(75), state.WiFiSignal) - assert.Equal(t, "192.168.1.100", state.WiFiIP) - assert.True(t, state.WiFiConnected) - assert.False(t, state.EthernetConnected) - assert.Equal(t, StatusWiFi, state.NetworkStatus) -} - -func TestHybridIwdNetworkdBackend_GetCurrentState_EthernetPriority(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - wifi.state.WiFiConnected = true - wifi.state.WiFiSSID = "TestNetwork" - - l3.state.WiFiIP = "192.168.1.100" - l3.state.EthernetConnected = true - l3.state.EthernetIP = "192.168.1.50" - l3.state.EthernetDevice = "eth0" - - state, err := hybrid.GetCurrentState() - assert.NoError(t, err) - assert.Equal(t, StatusEthernet, state.NetworkStatus) - assert.Equal(t, "192.168.1.50", state.EthernetIP) - assert.Equal(t, "eth0", state.EthernetDevice) -} - -func TestHybridIwdNetworkdBackend_GetCurrentState_WiFiNoIP(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - wifi.state.WiFiConnected = true - wifi.state.WiFiSSID = "TestNetwork" - - l3.state.WiFiIP = "" - l3.state.EthernetConnected = false - - state, err := hybrid.GetCurrentState() - assert.NoError(t, err) - assert.Equal(t, StatusDisconnected, state.NetworkStatus) - assert.True(t, state.WiFiConnected) - assert.Empty(t, state.WiFiIP) -} - -func TestHybridIwdNetworkdBackend_WiFiDelegation(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - enabled, err := hybrid.GetWiFiEnabled() - assert.NoError(t, err) - assert.True(t, enabled) - - state, err := hybrid.GetCurrentState() - assert.NoError(t, err) - assert.NotNil(t, state) - assert.Equal(t, "iwd+networkd", state.Backend) -} - -func TestHybridIwdNetworkdBackend_WiredDelegation(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - conns, err := hybrid.GetWiredConnections() - assert.NoError(t, err) - assert.Empty(t, conns) -} - -func TestHybridIwdNetworkdBackend_VPNNotSupported(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - profiles, err := hybrid.ListVPNProfiles() - assert.NoError(t, err) - assert.Empty(t, profiles) - - active, err := hybrid.ListActiveVPN() - assert.NoError(t, err) - assert.Empty(t, active) - - err = hybrid.ConnectVPN("test", false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestHybridIwdNetworkdBackend_PromptBrokerDelegation(t *testing.T) { - wifi, _ := NewIWDBackend() - l3, _ := NewSystemdNetworkdBackend() - hybrid, _ := NewHybridIwdNetworkdBackend(wifi, l3) - - broker := hybrid.GetPromptBroker() - assert.Nil(t, broker) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd.go deleted file mode 100644 index f73aaf4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd.go +++ /dev/null @@ -1,238 +0,0 @@ -package network - -import ( - "fmt" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - iwdBusName = "net.connman.iwd" - iwdObjectPath = "/" - iwdAdapterInterface = "net.connman.iwd.Adapter" - iwdDeviceInterface = "net.connman.iwd.Device" - iwdStationInterface = "net.connman.iwd.Station" - iwdNetworkInterface = "net.connman.iwd.Network" - iwdKnownNetworkInterface = "net.connman.iwd.KnownNetwork" - dbusObjectManager = "org.freedesktop.DBus.ObjectManager" - dbusPropertiesInterface = "org.freedesktop.DBus.Properties" -) - -type connectAttempt struct { - ssid string - netPath dbus.ObjectPath - start time.Time - deadline time.Time - sawAuthish bool - connectedAt time.Time - sawIPConfig bool - sawPromptRetry bool - finalized bool - mu sync.Mutex -} - -type IWDBackend struct { - conn *dbus.Conn - state *BackendState - stateMutex sync.RWMutex - promptBroker PromptBroker - onStateChange func() - - devicePath dbus.ObjectPath - stationPath dbus.ObjectPath - adapterPath dbus.ObjectPath - - iwdAgent *IWDAgent - - stopChan chan struct{} - sigWG sync.WaitGroup - curAttempt *connectAttempt - attemptMutex sync.RWMutex - recentScans map[string]time.Time - recentScansMu sync.Mutex -} - -func NewIWDBackend() (*IWDBackend, error) { - backend := &IWDBackend{ - state: &BackendState{ - Backend: "iwd", - WiFiEnabled: true, - }, - stopChan: make(chan struct{}), - recentScans: make(map[string]time.Time), - } - - return backend, nil -} - -func (b *IWDBackend) Initialize() error { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("failed to connect to system bus: %w", err) - } - b.conn = conn - - if err := b.discoverDevices(); err != nil { - conn.Close() - return fmt.Errorf("failed to discover iwd devices: %w", err) - } - - if err := b.updateState(); err != nil { - conn.Close() - return fmt.Errorf("failed to get initial state: %w", err) - } - - return nil -} - -func (b *IWDBackend) Close() { - close(b.stopChan) - b.sigWG.Wait() - - if b.iwdAgent != nil { - b.iwdAgent.Close() - } - - if b.conn != nil { - b.conn.Close() - } -} - -func (b *IWDBackend) discoverDevices() error { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return fmt.Errorf("failed to get managed objects: %w", err) - } - - for path, interfaces := range objects { - if _, hasStation := interfaces[iwdStationInterface]; hasStation { - b.stationPath = path - } - if _, hasDevice := interfaces[iwdDeviceInterface]; hasDevice { - b.devicePath = path - - if devProps, ok := interfaces[iwdDeviceInterface]; ok { - if nameVar, ok := devProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok { - b.stateMutex.Lock() - b.state.WiFiDevice = name - b.stateMutex.Unlock() - } - } - } - } - if _, hasAdapter := interfaces[iwdAdapterInterface]; hasAdapter { - b.adapterPath = path - } - } - - if b.stationPath == "" || b.devicePath == "" { - return fmt.Errorf("no WiFi device found") - } - - return nil -} - -func (b *IWDBackend) GetCurrentState() (*BackendState, error) { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - - state := *b.state - state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...) - state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...) - state.WiFiDevices = b.getWiFiDevicesLocked() - - return &state, nil -} - -func (b *IWDBackend) OnUserCanceledPrompt() { - b.stateMutex.RLock() - cancelledSSID := b.state.ConnectingSSID - b.stateMutex.RUnlock() - - b.setConnectError("user-canceled") - - if cancelledSSID != "" { - if err := b.ForgetWiFiNetwork(cancelledSSID); err != nil { - log.Warnf("failed to forget cancelled WiFi network %s: %v", cancelledSSID, err) - } - } - - if b.onStateChange != nil { - b.onStateChange() - } -} - -func (b *IWDBackend) OnPromptRetry(ssid string) { - b.attemptMutex.RLock() - att := b.curAttempt - b.attemptMutex.RUnlock() - - if att != nil && att.ssid == ssid { - att.mu.Lock() - att.sawPromptRetry = true - att.mu.Unlock() - } -} - -func (b *IWDBackend) MarkIPConfigSeen() { - b.attemptMutex.RLock() - att := b.curAttempt - b.attemptMutex.RUnlock() - if att != nil { - att.mu.Lock() - att.sawIPConfig = true - att.mu.Unlock() - } -} - -func (b *IWDBackend) GetPromptBroker() PromptBroker { - return b.promptBroker -} - -func (b *IWDBackend) SetPromptBroker(broker PromptBroker) error { - if broker == nil { - return fmt.Errorf("broker cannot be nil") - } - - b.promptBroker = broker - return nil -} - -func (b *IWDBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return b.promptBroker.Resolve(token, PromptReply{ - Secrets: secrets, - Save: save, - Cancel: false, - }) -} - -func (b *IWDBackend) CancelCredentials(token string) error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return b.promptBroker.Resolve(token, PromptReply{ - Cancel: true, - }) -} - -func (b *IWDBackend) StopMonitoring() { - select { - case <-b.stopChan: - return - default: - close(b.stopChan) - } - b.sigWG.Wait() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_signals.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_signals.go deleted file mode 100644 index bde4753..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_signals.go +++ /dev/null @@ -1,355 +0,0 @@ -package network - -import ( - "fmt" - "time" - - "github.com/godbus/dbus/v5" -) - -func (b *IWDBackend) StartMonitoring(onStateChange func()) error { - b.onStateChange = onStateChange - - if b.promptBroker != nil { - agent, err := NewIWDAgent(b.conn, b.promptBroker) - if err != nil { - return fmt.Errorf("failed to start IWD agent: %w", err) - } - agent.onUserCanceled = b.OnUserCanceledPrompt - agent.onPromptRetry = b.OnPromptRetry - b.iwdAgent = agent - } - - sigChan := make(chan *dbus.Signal, 100) - b.conn.Signal(sigChan) - - if b.devicePath != "" { - err := b.conn.AddMatchSignal( - dbus.WithMatchObjectPath(b.devicePath), - dbus.WithMatchInterface(dbusPropertiesInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - if err != nil { - return fmt.Errorf("failed to add device signal match: %w", err) - } - } - - if b.stationPath != "" { - err := b.conn.AddMatchSignal( - dbus.WithMatchObjectPath(b.stationPath), - dbus.WithMatchInterface(dbusPropertiesInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - if err != nil { - return fmt.Errorf("failed to add station signal match: %w", err) - } - } - - b.sigWG.Add(1) - go b.signalHandler(sigChan) - - return nil -} - -func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) { - defer b.sigWG.Done() - - for { - select { - case <-b.stopChan: - b.conn.RemoveSignal(sigChan) - close(sigChan) - return - - case sig := <-sigChan: - if sig == nil { - return - } - - if sig.Name != dbusPropertiesInterface+".PropertiesChanged" { - continue - } - - if len(sig.Body) < 2 { - continue - } - - iface, ok := sig.Body[0].(string) - if !ok { - continue - } - - changed, ok := sig.Body[1].(map[string]dbus.Variant) - if !ok { - continue - } - - stateChanged := false - - switch iface { - case iwdDeviceInterface: - if sig.Path == b.devicePath { - if poweredVar, ok := changed["Powered"]; ok { - if powered, ok := poweredVar.Value().(bool); ok { - b.stateMutex.Lock() - if b.state.WiFiEnabled != powered { - b.state.WiFiEnabled = powered - stateChanged = true - } - b.stateMutex.Unlock() - } - } - } - - case iwdStationInterface: - if sig.Path == b.stationPath { - if scanningVar, ok := changed["Scanning"]; ok { - if scanning, ok := scanningVar.Value().(bool); ok && !scanning { - networks, err := b.updateWiFiNetworks() - if err == nil { - b.stateMutex.Lock() - b.state.WiFiNetworks = networks - b.stateMutex.Unlock() - stateChanged = true - } - - b.stateMutex.RLock() - wifiConnected := b.state.WiFiConnected - b.stateMutex.RUnlock() - - if wifiConnected { - stationObj := b.conn.Object(iwdBusName, b.stationPath) - connNetVar, err := stationObj.GetProperty(iwdStationInterface + ".ConnectedNetwork") - if err == nil && connNetVar.Value() != nil { - if netPath, ok := connNetVar.Value().(dbus.ObjectPath); ok && netPath != "/" { - var orderedNetworks [][]dbus.Variant - err = stationObj.Call(iwdStationInterface+".GetOrderedNetworks", 0).Store(&orderedNetworks) - if err == nil { - for _, netData := range orderedNetworks { - if len(netData) < 2 { - continue - } - currentNetPath, ok := netData[0].Value().(dbus.ObjectPath) - if !ok || currentNetPath != netPath { - continue - } - signalStrength, ok := netData[1].Value().(int16) - if !ok { - continue - } - signalDbm := signalStrength / 100 - signal := uint8(signalDbm + 100) - if signalDbm > 0 { - signal = 100 - } else if signalDbm < -100 { - signal = 0 - } - b.stateMutex.Lock() - if b.state.WiFiSignal != signal { - b.state.WiFiSignal = signal - stateChanged = true - } - b.stateMutex.Unlock() - break - } - } - } - } - } - } - } - - if stateVar, ok := changed["State"]; ok { - if state, ok := stateVar.Value().(string); ok { - b.attemptMutex.RLock() - att := b.curAttempt - b.attemptMutex.RUnlock() - - var connPath dbus.ObjectPath - if v, ok := changed["ConnectedNetwork"]; ok { - if v.Value() != nil { - if p, ok := v.Value().(dbus.ObjectPath); ok { - connPath = p - } - } - } - if connPath == "" { - station := b.conn.Object(iwdBusName, b.stationPath) - if cnVar, err := station.GetProperty(iwdStationInterface + ".ConnectedNetwork"); err == nil && cnVar.Value() != nil { - cnVar.Store(&connPath) - } - } - - b.stateMutex.RLock() - prevConnected := b.state.WiFiConnected - prevSSID := b.state.WiFiSSID - b.stateMutex.RUnlock() - - targetPath := dbus.ObjectPath("") - if att != nil { - targetPath = att.netPath - } - - isTarget := att != nil && targetPath != "" && connPath == targetPath - - if att != nil { - switch state { - case "authenticating", "associating", "associated", "roaming": - att.mu.Lock() - att.sawAuthish = true - att.mu.Unlock() - } - } - - if att != nil && state == "connected" && isTarget { - att.mu.Lock() - if att.connectedAt.IsZero() { - att.connectedAt = time.Now() - } - att.mu.Unlock() - } - - if att != nil && state == "configuring" { - att.mu.Lock() - att.sawIPConfig = true - att.mu.Unlock() - } - - switch state { - case "connected": - b.stateMutex.Lock() - b.state.WiFiConnected = true - b.state.NetworkStatus = StatusWiFi - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = "" - b.stateMutex.Unlock() - - if connPath != "" && connPath != "/" { - netObj := b.conn.Object(iwdBusName, connPath) - if nameVar, err := netObj.GetProperty(iwdNetworkInterface + ".Name"); err == nil { - if name, ok := nameVar.Value().(string); ok { - b.stateMutex.Lock() - b.state.WiFiSSID = name - b.stateMutex.Unlock() - } - } - } - - stateChanged = true - - if att != nil && isTarget { - go func(attLocal *connectAttempt, tgt dbus.ObjectPath) { - time.Sleep(3 * time.Second) - station := b.conn.Object(iwdBusName, b.stationPath) - var nowState string - if stVar, err := station.GetProperty(iwdStationInterface + ".State"); err == nil { - stVar.Store(&nowState) - } - var nowConn dbus.ObjectPath - if cnVar, err := station.GetProperty(iwdStationInterface + ".ConnectedNetwork"); err == nil && cnVar.Value() != nil { - cnVar.Store(&nowConn) - } - - if nowState == "connected" && nowConn == tgt { - b.finalizeAttempt(attLocal, "") - b.attemptMutex.Lock() - if b.curAttempt == attLocal { - b.curAttempt = nil - } - b.attemptMutex.Unlock() - } - }(att, targetPath) - } - - case "disconnecting", "disconnected": - if att != nil { - wasConnectedToTarget := prevConnected && prevSSID == att.ssid - if wasConnectedToTarget || isTarget { - code := b.classifyAttempt(att) - b.finalizeAttempt(att, code) - b.attemptMutex.Lock() - if b.curAttempt == att { - b.curAttempt = nil - } - b.attemptMutex.Unlock() - } - } - - b.stateMutex.Lock() - b.state.WiFiConnected = false - if state == "disconnected" { - b.state.NetworkStatus = StatusDisconnected - } - b.stateMutex.Unlock() - stateChanged = true - } - } - } - - if connNetVar, ok := changed["ConnectedNetwork"]; ok { - if netPath, ok := connNetVar.Value().(dbus.ObjectPath); ok && netPath != "/" { - netObj := b.conn.Object(iwdBusName, netPath) - nameVar, err := netObj.GetProperty(iwdNetworkInterface + ".Name") - if err == nil { - if name, ok := nameVar.Value().(string); ok { - b.stateMutex.Lock() - if b.state.WiFiSSID != name { - b.state.WiFiSSID = name - stateChanged = true - } - b.stateMutex.Unlock() - } - } - - stationObj := b.conn.Object(iwdBusName, b.stationPath) - var orderedNetworks [][]dbus.Variant - err = stationObj.Call(iwdStationInterface+".GetOrderedNetworks", 0).Store(&orderedNetworks) - if err == nil { - for _, netData := range orderedNetworks { - if len(netData) < 2 { - continue - } - currentNetPath, ok := netData[0].Value().(dbus.ObjectPath) - if !ok || currentNetPath != netPath { - continue - } - signalStrength, ok := netData[1].Value().(int16) - if !ok { - continue - } - signalDbm := signalStrength / 100 - signal := uint8(signalDbm + 100) - if signalDbm > 0 { - signal = 100 - } else if signalDbm < -100 { - signal = 0 - } - b.stateMutex.Lock() - if b.state.WiFiSignal != signal { - b.state.WiFiSignal = signal - stateChanged = true - } - b.stateMutex.Unlock() - break - } - } - } else { - b.stateMutex.Lock() - if b.state.WiFiSSID != "" { - b.state.WiFiSSID = "" - b.state.WiFiSignal = 0 - stateChanged = true - } - b.stateMutex.Unlock() - } - } - } - } - - if stateChanged && b.onStateChange != nil { - b.onStateChange() - } - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_test.go deleted file mode 100644 index 829e413..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package network - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestIWDBackend_MarkIPConfigSeen(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/net/connman/iwd/0/1/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.attemptMutex.Lock() - backend.curAttempt = att - backend.attemptMutex.Unlock() - - backend.MarkIPConfigSeen() - - att.mu.Lock() - assert.True(t, att.sawIPConfig, "sawIPConfig should be true after MarkIPConfigSeen") - att.mu.Unlock() -} - -func TestIWDBackend_MarkIPConfigSeen_NoAttempt(t *testing.T) { - backend, _ := NewIWDBackend() - - backend.attemptMutex.Lock() - backend.curAttempt = nil - backend.attemptMutex.Unlock() - - backend.MarkIPConfigSeen() -} - -func TestIWDBackend_OnPromptRetry(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/net/connman/iwd/0/1/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.attemptMutex.Lock() - backend.curAttempt = att - backend.attemptMutex.Unlock() - - backend.OnPromptRetry("TestNetwork") - - att.mu.Lock() - assert.True(t, att.sawPromptRetry, "sawPromptRetry should be true after OnPromptRetry") - att.mu.Unlock() -} - -func TestIWDBackend_OnPromptRetry_WrongSSID(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/net/connman/iwd/0/1/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.attemptMutex.Lock() - backend.curAttempt = att - backend.attemptMutex.Unlock() - - backend.OnPromptRetry("DifferentNetwork") - - att.mu.Lock() - assert.False(t, att.sawPromptRetry, "sawPromptRetry should remain false for different SSID") - att.mu.Unlock() -} - -func TestIWDBackend_ClassifyAttempt_BadCredentials_PromptRetry(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now().Add(-5 * time.Second), - deadline: time.Now().Add(10 * time.Second), - sawPromptRetry: true, - } - - code := backend.classifyAttempt(att) - assert.Equal(t, "bad-credentials", code) -} - -func TestIWDBackend_ClassifyAttempt_DhcpTimeout(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now().Add(-13 * time.Second), - deadline: time.Now().Add(2 * time.Second), - sawAuthish: true, - sawIPConfig: false, - } - - code := backend.classifyAttempt(att) - assert.Equal(t, "dhcp-timeout", code) -} - -func TestIWDBackend_ClassifyAttempt_AssocTimeout(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now().Add(-5 * time.Second), - deadline: time.Now().Add(10 * time.Second), - } - - backend.recentScansMu.Lock() - backend.recentScans["TestNetwork"] = time.Now() - backend.recentScansMu.Unlock() - - code := backend.classifyAttempt(att) - assert.Equal(t, "assoc-timeout", code) -} - -func TestIWDBackend_ClassifyAttempt_NoSuchSSID(t *testing.T) { - backend, _ := NewIWDBackend() - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now().Add(-5 * time.Second), - deadline: time.Now().Add(10 * time.Second), - } - - code := backend.classifyAttempt(att) - assert.Equal(t, "no-such-ssid", code) -} - -func TestIWDBackend_MapIwdDBusError(t *testing.T) { - backend, _ := NewIWDBackend() - - testCases := []struct { - name string - expected string - }{ - {"net.connman.iwd.Error.AlreadyConnected", "already-connected"}, - {"net.connman.iwd.Error.AuthenticationFailed", "bad-credentials"}, - {"net.connman.iwd.Error.InvalidKey", "bad-credentials"}, - {"net.connman.iwd.Error.IncorrectPassphrase", "bad-credentials"}, - {"net.connman.iwd.Error.NotFound", "no-such-ssid"}, - {"net.connman.iwd.Error.NotSupported", "connection-failed"}, - {"net.connman.iwd.Agent.Error.Canceled", "user-canceled"}, - {"net.connman.iwd.Error.Unknown", "connection-failed"}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - code := backend.mapIwdDBusError(tc.name) - assert.Equal(t, tc.expected, code) - }) - } -} - -func TestConnectAttempt_Finalization(t *testing.T) { - backend, _ := NewIWDBackend() - backend.state = &BackendState{} - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.finalizeAttempt(att, "bad-credentials") - - att.mu.Lock() - assert.True(t, att.finalized) - att.mu.Unlock() - - backend.stateMutex.RLock() - assert.False(t, backend.state.IsConnecting) - assert.Empty(t, backend.state.ConnectingSSID) - assert.Equal(t, "bad-credentials", backend.state.LastError) - backend.stateMutex.RUnlock() -} - -func TestConnectAttempt_DoubleFinalization(t *testing.T) { - backend, _ := NewIWDBackend() - backend.state = &BackendState{} - - att := &connectAttempt{ - ssid: "TestNetwork", - netPath: "/test", - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - backend.finalizeAttempt(att, "bad-credentials") - backend.finalizeAttempt(att, "dhcp-timeout") - - backend.stateMutex.RLock() - assert.Equal(t, "bad-credentials", backend.state.LastError) - backend.stateMutex.RUnlock() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_unimplemented.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_unimplemented.go deleted file mode 100644 index f5a8b03..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_unimplemented.go +++ /dev/null @@ -1,133 +0,0 @@ -package network - -import ( - "fmt" - "os" -) - -func (b *IWDBackend) GetWiredConnections() ([]WiredConnection, error) { - return nil, fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) GetWiredNetworkDetails(uuid string) (*WiredNetworkInfoResponse, error) { - return nil, fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) ConnectEthernet() error { - return fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) DisconnectEthernet() error { - return fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) DisconnectEthernetDevice(device string) error { - return fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) GetEthernetDevices() []EthernetDevice { - return []EthernetDevice{} -} - -func (b *IWDBackend) ActivateWiredConnection(uuid string) error { - return fmt.Errorf("wired connections not supported by iwd") -} - -func (b *IWDBackend) ListVPNProfiles() ([]VPNProfile, error) { - return nil, fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ListActiveVPN() ([]VPNActive, error) { - return nil, fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) DisconnectVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) DisconnectAllVPN() error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ClearVPNCredentials(uuidOrName string) error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ListVPNPlugins() ([]VPNPlugin, error) { - return nil, fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ImportVPN(filePath string, name string) (*VPNImportResult, error) { - return nil, fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) GetVPNConfig(uuidOrName string) (*VPNConfig, error) { - return nil, fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) UpdateVPNConfig(uuid string, updates map[string]any) error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) DeleteVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) SetVPNCredentials(uuid, username, password string, save bool) error { - return fmt.Errorf("VPN not supported by iwd backend") -} - -func (b *IWDBackend) ScanWiFiDevice(device string) error { - return b.ScanWiFi() -} - -func (b *IWDBackend) DisconnectWiFiDevice(device string) error { - return b.DisconnectWiFi() -} - -func (b *IWDBackend) GetWiFiDevices() []WiFiDevice { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - return b.getWiFiDevicesLocked() -} - -func (b *IWDBackend) getWiFiDevicesLocked() []WiFiDevice { - if b.state.WiFiDevice == "" { - return nil - } - - stateStr := "disconnected" - if b.state.WiFiConnected { - stateStr = "connected" - } - - return []WiFiDevice{{ - Name: b.state.WiFiDevice, - State: stateStr, - Connected: b.state.WiFiConnected, - SSID: b.state.WiFiSSID, - Signal: b.state.WiFiSignal, - IP: b.state.WiFiIP, - Networks: b.state.WiFiNetworks, - }} -} - -func (b *IWDBackend) GetWiFiQRCodeContent(ssid string) (string, error) { - path := iwdConfigPath(ssid) - - data, err := os.ReadFile(path) - if err != nil { - return "", fmt.Errorf("no saved iwd config for `%s`: %w", ssid, err) - } - - passphrase, err := parseIWDPassphrase(string(data)) - if err != nil { - return "", fmt.Errorf("failed to read passphrase for `%s`: %w", ssid, err) - } - - return FormatWiFiQRString("WPA", ssid, passphrase), nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_wifi.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_wifi.go deleted file mode 100644 index b3bea13..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_iwd_wifi.go +++ /dev/null @@ -1,662 +0,0 @@ -package network - -import ( - "fmt" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/godbus/dbus/v5" -) - -func (b *IWDBackend) updateState() error { - if b.devicePath == "" { - return nil - } - - obj := b.conn.Object(iwdBusName, b.devicePath) - - poweredVar, err := obj.GetProperty(iwdDeviceInterface + ".Powered") - if err == nil { - if powered, ok := poweredVar.Value().(bool); ok { - b.stateMutex.Lock() - b.state.WiFiEnabled = powered - b.stateMutex.Unlock() - } - } - - if b.stationPath == "" { - return nil - } - - stationObj := b.conn.Object(iwdBusName, b.stationPath) - - stateVar, err := stationObj.GetProperty(iwdStationInterface + ".State") - if err == nil { - if state, ok := stateVar.Value().(string); ok { - b.stateMutex.Lock() - b.state.WiFiConnected = (state == "connected") - if state == "connected" { - b.state.NetworkStatus = StatusWiFi - } else { - b.state.NetworkStatus = StatusDisconnected - } - b.stateMutex.Unlock() - } - } - - connNetVar, err := stationObj.GetProperty(iwdStationInterface + ".ConnectedNetwork") - if err == nil && connNetVar.Value() != nil { - if netPath, ok := connNetVar.Value().(dbus.ObjectPath); ok && netPath != "/" { - netObj := b.conn.Object(iwdBusName, netPath) - - nameVar, err := netObj.GetProperty(iwdNetworkInterface + ".Name") - if err == nil { - if name, ok := nameVar.Value().(string); ok { - b.stateMutex.Lock() - b.state.WiFiSSID = name - b.stateMutex.Unlock() - } - } - - var orderedNetworks [][]dbus.Variant - err = stationObj.Call(iwdStationInterface+".GetOrderedNetworks", 0).Store(&orderedNetworks) - if err == nil { - for _, netData := range orderedNetworks { - if len(netData) < 2 { - continue - } - currentNetPath, ok := netData[0].Value().(dbus.ObjectPath) - if !ok || currentNetPath != netPath { - continue - } - signalStrength, ok := netData[1].Value().(int16) - if !ok { - continue - } - signalDbm := signalStrength / 100 - signal := uint8(signalDbm + 100) - if signalDbm > 0 { - signal = 100 - } else if signalDbm < -100 { - signal = 0 - } - b.stateMutex.Lock() - b.state.WiFiSignal = signal - b.stateMutex.Unlock() - break - } - } - } - } - - networks, err := b.updateWiFiNetworks() - if err == nil { - b.stateMutex.Lock() - b.state.WiFiNetworks = networks - b.stateMutex.Unlock() - } - - return nil -} - -func (b *IWDBackend) GetWiFiEnabled() (bool, error) { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - return b.state.WiFiEnabled, nil -} - -func (b *IWDBackend) SetWiFiEnabled(enabled bool) error { - if b.devicePath == "" { - return fmt.Errorf("no WiFi device available") - } - - obj := b.conn.Object(iwdBusName, b.devicePath) - call := obj.Call(dbusPropertiesInterface+".Set", 0, iwdDeviceInterface, "Powered", dbus.MakeVariant(enabled)) - if call.Err != nil { - return fmt.Errorf("failed to set WiFi enabled: %w", call.Err) - } - - b.stateMutex.Lock() - b.state.WiFiEnabled = enabled - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *IWDBackend) ScanWiFi() error { - if b.stationPath == "" { - return fmt.Errorf("no WiFi device available") - } - - obj := b.conn.Object(iwdBusName, b.stationPath) - - scanningVar, err := obj.GetProperty(iwdStationInterface + ".Scanning") - if err != nil { - return fmt.Errorf("failed to check scanning state: %w", err) - } - - if scanning, ok := scanningVar.Value().(bool); ok && scanning { - return fmt.Errorf("scan already in progress") - } - - call := obj.Call(iwdStationInterface+".Scan", 0) - if call.Err != nil { - return fmt.Errorf("scan request failed: %w", call.Err) - } - - return nil -} - -func (b *IWDBackend) updateWiFiNetworks() ([]WiFiNetwork, error) { - if b.stationPath == "" { - return nil, fmt.Errorf("no WiFi device available") - } - - obj := b.conn.Object(iwdBusName, b.stationPath) - - var orderedNetworks [][]dbus.Variant - err := obj.Call(iwdStationInterface+".GetOrderedNetworks", 0).Store(&orderedNetworks) - if err != nil { - return nil, fmt.Errorf("failed to get networks: %w", err) - } - - knownNetworks, err := b.getKnownNetworks() - if err != nil { - knownNetworks = make(map[string]bool) - } - - autoconnectMap, err := b.getAutoconnectSettings() - if err != nil { - autoconnectMap = make(map[string]bool) - } - - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - wifiConnected := b.state.WiFiConnected - b.stateMutex.RUnlock() - - networks := make([]WiFiNetwork, 0, len(orderedNetworks)) - for _, netData := range orderedNetworks { - if len(netData) < 2 { - continue - } - - networkPath, ok := netData[0].Value().(dbus.ObjectPath) - if !ok { - continue - } - - signalStrength, ok := netData[1].Value().(int16) - if !ok { - continue - } - - netObj := b.conn.Object(iwdBusName, networkPath) - - nameVar, err := netObj.GetProperty(iwdNetworkInterface + ".Name") - if err != nil { - continue - } - name, ok := nameVar.Value().(string) - if !ok { - continue - } - - typeVar, err := netObj.GetProperty(iwdNetworkInterface + ".Type") - if err != nil { - continue - } - netType, ok := typeVar.Value().(string) - if !ok { - continue - } - - signalDbm := signalStrength / 100 - signal := uint8(signalDbm + 100) - if signalDbm > 0 { - signal = 100 - } else if signalDbm < -100 { - signal = 0 - } - - secured := netType != "open" - - network := WiFiNetwork{ - SSID: name, - Signal: signal, - Secured: secured, - Connected: wifiConnected && name == currentSSID, - Saved: knownNetworks[name], - Autoconnect: autoconnectMap[name], - Enterprise: netType == "8021x", - } - - networks = append(networks, network) - } - - sortWiFiNetworks(networks) - - b.stateMutex.Lock() - b.state.WiFiNetworks = networks - b.stateMutex.Unlock() - - now := time.Now() - b.recentScansMu.Lock() - for _, net := range networks { - b.recentScans[net.SSID] = now - } - b.recentScansMu.Unlock() - - return networks, nil -} - -func (b *IWDBackend) getKnownNetworks() (map[string]bool, error) { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return nil, err - } - - known := make(map[string]bool) - for _, interfaces := range objects { - if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok { - if nameVar, ok := knownProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok { - known[name] = true - } - } - } - } - - return known, nil -} - -func (b *IWDBackend) getAutoconnectSettings() (map[string]bool, error) { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return nil, err - } - - autoconnectMap := make(map[string]bool) - for _, interfaces := range objects { - if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok { - if nameVar, ok := knownProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok { - autoconnect := true - if acVar, ok := knownProps["AutoConnect"]; ok { - if ac, ok := acVar.Value().(bool); ok { - autoconnect = ac - } - } - autoconnectMap[name] = autoconnect - } - } - } - } - - return autoconnectMap, nil -} - -func (b *IWDBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) { - b.stateMutex.RLock() - networks := b.state.WiFiNetworks - b.stateMutex.RUnlock() - - var found *WiFiNetwork - for i := range networks { - if networks[i].SSID == ssid { - found = &networks[i] - break - } - } - - if found == nil { - return nil, fmt.Errorf("network not found: %s", ssid) - } - - return &NetworkInfoResponse{ - SSID: ssid, - Bands: []WiFiNetwork{*found}, - }, nil -} - -func (b *IWDBackend) setConnectError(code string) { - b.stateMutex.Lock() - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = code - b.stateMutex.Unlock() -} - -func (b *IWDBackend) seenInRecentScan(ssid string) bool { - b.recentScansMu.Lock() - defer b.recentScansMu.Unlock() - lastSeen, ok := b.recentScans[ssid] - return ok && time.Since(lastSeen) < 30*time.Second -} - -func (b *IWDBackend) classifyAttempt(att *connectAttempt) string { - att.mu.Lock() - defer att.mu.Unlock() - - if att.sawPromptRetry { - return errdefs.ErrBadCredentials - } - - if !att.connectedAt.IsZero() && !att.sawIPConfig { - connDuration := time.Since(att.connectedAt) - if connDuration > 500*time.Millisecond && connDuration < 3*time.Second { - return errdefs.ErrBadCredentials - } - } - - if (att.sawAuthish || !att.connectedAt.IsZero()) && !att.sawIPConfig { - if time.Since(att.start) > 12*time.Second { - return errdefs.ErrDhcpTimeout - } - } - - if !att.sawAuthish && att.connectedAt.IsZero() { - if !b.seenInRecentScan(att.ssid) { - return errdefs.ErrNoSuchSSID - } - return errdefs.ErrAssocTimeout - } - - return errdefs.ErrAssocTimeout -} - -func (b *IWDBackend) finalizeAttempt(att *connectAttempt, code string) { - att.mu.Lock() - if att.finalized { - att.mu.Unlock() - return - } - att.finalized = true - att.mu.Unlock() - - b.stateMutex.Lock() - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = code - b.stateMutex.Unlock() - - b.updateState() - - if b.onStateChange != nil { - b.onStateChange() - } -} - -func (b *IWDBackend) startAttemptWatchdog(att *connectAttempt) { - b.sigWG.Add(1) - go func() { - defer b.sigWG.Done() - - ticker := time.NewTicker(250 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - att.mu.Lock() - finalized := att.finalized - att.mu.Unlock() - - if finalized || time.Now().After(att.deadline) { - if !finalized { - b.finalizeAttempt(att, b.classifyAttempt(att)) - } - return - } - - station := b.conn.Object(iwdBusName, b.stationPath) - stVar, err := station.GetProperty(iwdStationInterface + ".State") - if err != nil { - continue - } - state, _ := stVar.Value().(string) - - cnVar, err := station.GetProperty(iwdStationInterface + ".ConnectedNetwork") - if err != nil { - continue - } - var connPath dbus.ObjectPath - if cnVar.Value() != nil { - connPath, _ = cnVar.Value().(dbus.ObjectPath) - } - - att.mu.Lock() - if connPath == att.netPath && state == "connected" && att.connectedAt.IsZero() { - att.connectedAt = time.Now() - } - if state == "configuring" { - att.sawIPConfig = true - } - att.mu.Unlock() - - case <-b.stopChan: - return - } - } - }() -} - -func (b *IWDBackend) mapIwdDBusError(name string) string { - switch name { - case "net.connman.iwd.Error.AlreadyConnected": - return errdefs.ErrAlreadyConnected - case "net.connman.iwd.Error.AuthenticationFailed", - "net.connman.iwd.Error.InvalidKey", - "net.connman.iwd.Error.IncorrectPassphrase": - return errdefs.ErrBadCredentials - case "net.connman.iwd.Error.NotFound": - return errdefs.ErrNoSuchSSID - case "net.connman.iwd.Error.NotSupported": - return errdefs.ErrConnectionFailed - case "net.connman.iwd.Agent.Error.Canceled": - return errdefs.ErrUserCanceled - default: - return errdefs.ErrConnectionFailed - } -} - -func (b *IWDBackend) ConnectWiFi(req ConnectionRequest) error { - if b.stationPath == "" { - b.setConnectError(errdefs.ErrWifiDisabled) - if b.onStateChange != nil { - b.onStateChange() - } - return fmt.Errorf("no WiFi device available") - } - - networkPath, err := b.findNetworkPath(req.SSID) - if err != nil { - b.setConnectError(errdefs.ErrNoSuchSSID) - if b.onStateChange != nil { - b.onStateChange() - } - return fmt.Errorf("network not found: %w", err) - } - - att := &connectAttempt{ - ssid: req.SSID, - netPath: networkPath, - start: time.Now(), - deadline: time.Now().Add(15 * time.Second), - } - - b.attemptMutex.Lock() - b.curAttempt = att - b.attemptMutex.Unlock() - - b.stateMutex.Lock() - b.state.IsConnecting = true - b.state.ConnectingSSID = req.SSID - b.state.LastError = "" - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - netObj := b.conn.Object(iwdBusName, networkPath) - go func() { - call := netObj.Call(iwdNetworkInterface+".Connect", 0) - if call.Err != nil { - var code string - if dbusErr, ok := call.Err.(dbus.Error); ok { - code = b.mapIwdDBusError(dbusErr.Name) - } else if dbusErrPtr, ok := call.Err.(*dbus.Error); ok { - code = b.mapIwdDBusError(dbusErrPtr.Name) - } else { - code = errdefs.ErrConnectionFailed - } - - att.mu.Lock() - if att.sawPromptRetry { - code = errdefs.ErrBadCredentials - } - att.mu.Unlock() - - b.finalizeAttempt(att, code) - return - } - - b.startAttemptWatchdog(att) - }() - - return nil -} - -func (b *IWDBackend) findNetworkPath(ssid string) (dbus.ObjectPath, error) { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return "", err - } - - for path, interfaces := range objects { - if netProps, ok := interfaces[iwdNetworkInterface]; ok { - if nameVar, ok := netProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok && name == ssid { - return path, nil - } - } - } - } - - return "", fmt.Errorf("network not found") -} - -func (b *IWDBackend) DisconnectWiFi() error { - if b.stationPath == "" { - return fmt.Errorf("no WiFi device available") - } - - obj := b.conn.Object(iwdBusName, b.stationPath) - call := obj.Call(iwdStationInterface+".Disconnect", 0) - if call.Err != nil { - return fmt.Errorf("failed to disconnect: %w", call.Err) - } - - b.updateState() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *IWDBackend) ForgetWiFiNetwork(ssid string) error { - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - isConnected := b.state.WiFiConnected - b.stateMutex.RUnlock() - - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return err - } - - for path, interfaces := range objects { - if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok { - if nameVar, ok := knownProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok && name == ssid { - knownObj := b.conn.Object(iwdBusName, path) - call := knownObj.Call(iwdKnownNetworkInterface+".Forget", 0) - if call.Err != nil { - return fmt.Errorf("failed to forget network: %w", call.Err) - } - - if isConnected && currentSSID == ssid { - b.stateMutex.Lock() - b.state.WiFiConnected = false - b.state.WiFiSSID = "" - b.state.WiFiSignal = 0 - b.state.WiFiIP = "" - b.state.NetworkStatus = StatusDisconnected - b.stateMutex.Unlock() - } - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil - } - } - } - } - - return fmt.Errorf("network not found") -} - -func (b *IWDBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - obj := b.conn.Object(iwdBusName, iwdObjectPath) - - var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant - err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects) - if err != nil { - return err - } - - for path, interfaces := range objects { - if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok { - if nameVar, ok := knownProps["Name"]; ok { - if name, ok := nameVar.Value().(string); ok && name == ssid { - knownObj := b.conn.Object(iwdBusName, path) - call := knownObj.Call(dbusPropertiesInterface+".Set", 0, iwdKnownNetworkInterface, "AutoConnect", dbus.MakeVariant(autoconnect)) - if call.Err != nil { - return fmt.Errorf("failed to set autoconnect: %w", call.Err) - } - - b.updateState() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil - } - } - } - } - - return fmt.Errorf("network not found") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd.go deleted file mode 100644 index 802cbf0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd.go +++ /dev/null @@ -1,301 +0,0 @@ -package network - -import ( - "fmt" - "net" - "strings" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - networkdBusName = "org.freedesktop.network1" - networkdManagerPath = "/org/freedesktop/network1" - networkdManagerIface = "org.freedesktop.network1.Manager" - networkdLinkIface = "org.freedesktop.network1.Link" -) - -type linkInfo struct { - ifindex int32 - name string - path dbus.ObjectPath - opState string -} - -type SystemdNetworkdBackend struct { - conn *dbus.Conn - managerPath dbus.ObjectPath - links map[string]*linkInfo - linksMutex sync.RWMutex - state *BackendState - stateMutex sync.RWMutex - onStateChange func() - stopChan chan struct{} - signals chan *dbus.Signal - sigWG sync.WaitGroup -} - -func NewSystemdNetworkdBackend() (*SystemdNetworkdBackend, error) { - return &SystemdNetworkdBackend{ - managerPath: networkdManagerPath, - links: make(map[string]*linkInfo), - state: &BackendState{ - Backend: "networkd", - WiFiNetworks: []WiFiNetwork{}, - }, - stopChan: make(chan struct{}), - }, nil -} - -func (b *SystemdNetworkdBackend) Initialize() error { - c, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("connect bus: %w", err) - } - b.conn = c - - if err := b.enumerateLinks(); err != nil { - c.Close() - return fmt.Errorf("enumerate links: %w", err) - } - - if err := b.updateState(); err != nil { - c.Close() - return fmt.Errorf("update initial state: %w", err) - } - - return nil -} - -func (b *SystemdNetworkdBackend) Close() { - close(b.stopChan) - b.StopMonitoring() - - if b.conn != nil { - b.conn.Close() - } -} - -func (b *SystemdNetworkdBackend) enumerateLinks() error { - obj := b.conn.Object(networkdBusName, b.managerPath) - - var links []struct { - Ifindex int32 - Name string - Path dbus.ObjectPath - } - err := obj.Call(networkdManagerIface+".ListLinks", 0).Store(&links) - if err != nil { - return fmt.Errorf("ListLinks: %w", err) - } - - b.linksMutex.Lock() - defer b.linksMutex.Unlock() - - for _, l := range links { - b.links[l.Name] = &linkInfo{ - ifindex: l.Ifindex, - name: l.Name, - path: l.Path, - } - log.Debugf("networkd: enumerated link %s (ifindex=%d, path=%s)", l.Name, l.Ifindex, l.Path) - } - - return nil -} - -func (b *SystemdNetworkdBackend) updateState() error { - b.linksMutex.RLock() - defer b.linksMutex.RUnlock() - - var wiredIface *linkInfo - var wifiIface *linkInfo - - for name, link := range b.links { - if b.isVirtualInterface(name) { - continue - } - - linkObj := b.conn.Object(networkdBusName, link.path) - opStateVar, err := linkObj.GetProperty(networkdLinkIface + ".OperationalState") - if err == nil { - if opState, ok := opStateVar.Value().(string); ok { - link.opState = opState - } - } - - if strings.HasPrefix(name, "wlan") || strings.HasPrefix(name, "wlp") { - if wifiIface == nil || link.opState == "routable" || link.opState == "carrier" { - wifiIface = link - } - } else if !b.isVirtualInterface(name) { - if wiredIface == nil || link.opState == "routable" || link.opState == "carrier" { - wiredIface = link - } - } - } - - var wiredConns []WiredConnection - var ethernetDevices []EthernetDevice - for name, link := range b.links { - if b.isVirtualInterface(name) || strings.HasPrefix(name, "wlan") || strings.HasPrefix(name, "wlp") { - continue - } - - active := link.opState == "routable" || link.opState == "carrier" - wiredConns = append(wiredConns, WiredConnection{ - Path: link.path, - ID: name, - UUID: "wired:" + name, - Type: "ethernet", - IsActive: active, - }) - - var ip string - var hwAddr string - if iface, err := net.InterfaceByName(name); err == nil { - hwAddr = iface.HardwareAddr.String() - if addrs := b.getAddresses(name); len(addrs) > 0 { - ip = addrs[0] - } - } - - stateStr := "disconnected" - switch link.opState { - case "routable": - stateStr = "routable" - case "carrier": - stateStr = "carrier" - case "degraded": - stateStr = "degraded" - case "no-carrier": - stateStr = "no-carrier" - case "off": - stateStr = "off" - } - - ethernetDevices = append(ethernetDevices, EthernetDevice{ - Name: name, - HwAddress: hwAddr, - State: stateStr, - Connected: active, - IP: ip, - }) - } - - b.stateMutex.Lock() - defer b.stateMutex.Unlock() - - b.state.NetworkStatus = StatusDisconnected - b.state.EthernetConnected = false - b.state.EthernetIP = "" - b.state.WiFiConnected = false - b.state.WiFiIP = "" - b.state.WiredConnections = wiredConns - b.state.EthernetDevices = ethernetDevices - - if wiredIface != nil { - b.state.EthernetDevice = wiredIface.name - log.Debugf("networkd: wired interface %s opState=%s", wiredIface.name, wiredIface.opState) - if wiredIface.opState == "routable" || wiredIface.opState == "carrier" { - b.state.EthernetConnected = true - b.state.NetworkStatus = StatusEthernet - - if addrs := b.getAddresses(wiredIface.name); len(addrs) > 0 { - b.state.EthernetIP = addrs[0] - log.Debugf("networkd: ethernet IP %s on %s", addrs[0], wiredIface.name) - } - } - } - - if wifiIface != nil { - b.state.WiFiDevice = wifiIface.name - log.Debugf("networkd: wifi interface %s opState=%s", wifiIface.name, wifiIface.opState) - if wifiIface.opState == "routable" || wifiIface.opState == "carrier" { - b.state.WiFiConnected = true - - if addrs := b.getAddresses(wifiIface.name); len(addrs) > 0 { - b.state.WiFiIP = addrs[0] - log.Debugf("networkd: wifi IP %s on %s", addrs[0], wifiIface.name) - if b.state.NetworkStatus == StatusDisconnected { - b.state.NetworkStatus = StatusWiFi - } - } - } - } - - return nil -} - -func (b *SystemdNetworkdBackend) isVirtualInterface(name string) bool { - virtualPrefixes := []string{ - "lo", "docker", "veth", "virbr", "br-", "vnet", "tun", "tap", - "vboxnet", "vmnet", "kube", "cni", "flannel", "cali", - } - for _, prefix := range virtualPrefixes { - if strings.HasPrefix(name, prefix) { - return true - } - } - return false -} - -func (b *SystemdNetworkdBackend) getAddresses(ifname string) []string { - iface, err := net.InterfaceByName(ifname) - if err != nil { - return nil - } - - addrs, err := iface.Addrs() - if err != nil { - return nil - } - - var result []string - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok { - if ipv4 := ipnet.IP.To4(); ipv4 != nil { - result = append(result, ipv4.String()) - } - } - } - return result -} - -func (b *SystemdNetworkdBackend) GetCurrentState() (*BackendState, error) { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - s := *b.state - return &s, nil -} - -func (b *SystemdNetworkdBackend) GetPromptBroker() PromptBroker { - return nil -} - -func (b *SystemdNetworkdBackend) SetPromptBroker(broker PromptBroker) error { - return nil -} - -func (b *SystemdNetworkdBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - return fmt.Errorf("credentials not needed by networkd backend") -} - -func (b *SystemdNetworkdBackend) CancelCredentials(token string) error { - return fmt.Errorf("credentials not needed by networkd backend") -} - -func (b *SystemdNetworkdBackend) EnsureDhcpUp(ifname string) error { - b.linksMutex.RLock() - link, exists := b.links[ifname] - b.linksMutex.RUnlock() - - if !exists { - return fmt.Errorf("interface %s not found", ifname) - } - - linkObj := b.conn.Object(networkdBusName, link.path) - return linkObj.Call(networkdLinkIface+".Reconfigure", 0).Err -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_ethernet.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_ethernet.go deleted file mode 100644 index 8f9b225..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_ethernet.go +++ /dev/null @@ -1,120 +0,0 @@ -package network - -import ( - "fmt" - "net" - "strings" -) - -func (b *SystemdNetworkdBackend) GetWiredConnections() ([]WiredConnection, error) { - b.linksMutex.RLock() - defer b.linksMutex.RUnlock() - - var conns []WiredConnection - for name, link := range b.links { - if b.isVirtualInterface(name) || strings.HasPrefix(name, "wlan") || strings.HasPrefix(name, "wlp") { - continue - } - - active := link.opState == "routable" || link.opState == "carrier" - conns = append(conns, WiredConnection{ - Path: link.path, - ID: name, - UUID: "wired:" + name, - Type: "ethernet", - IsActive: active, - }) - } - - return conns, nil -} - -func (b *SystemdNetworkdBackend) GetWiredNetworkDetails(id string) (*WiredNetworkInfoResponse, error) { - ifname := strings.TrimPrefix(id, "wired:") - - b.linksMutex.RLock() - _, exists := b.links[ifname] - b.linksMutex.RUnlock() - - if !exists { - return nil, fmt.Errorf("interface %s not found", ifname) - } - - iface, err := net.InterfaceByName(ifname) - if err != nil { - return nil, fmt.Errorf("get interface: %w", err) - } - - addrs, _ := iface.Addrs() - var ipv4s, ipv6s []string - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok { - if ipv4 := ipnet.IP.To4(); ipv4 != nil { - ipv4s = append(ipv4s, ipnet.String()) - } else if ipv6 := ipnet.IP.To16(); ipv6 != nil { - ipv6s = append(ipv6s, ipnet.String()) - } - } - } - - return &WiredNetworkInfoResponse{ - UUID: id, - IFace: ifname, - HwAddr: iface.HardwareAddr.String(), - IPv4: WiredIPConfig{ - IPs: ipv4s, - }, - IPv6: WiredIPConfig{ - IPs: ipv6s, - }, - }, nil -} - -func (b *SystemdNetworkdBackend) ConnectEthernet() error { - b.linksMutex.RLock() - var primaryWired *linkInfo - for name, l := range b.links { - if strings.HasPrefix(name, "lo") || strings.HasPrefix(name, "wlan") || strings.HasPrefix(name, "wlp") { - continue - } - primaryWired = l - break - } - b.linksMutex.RUnlock() - - if primaryWired == nil { - return fmt.Errorf("no wired interface found") - } - - linkObj := b.conn.Object(networkdBusName, primaryWired.path) - return linkObj.Call(networkdLinkIface+".Reconfigure", 0).Err -} - -func (b *SystemdNetworkdBackend) DisconnectEthernet() error { - return fmt.Errorf("not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ActivateWiredConnection(id string) error { - ifname := strings.TrimPrefix(id, "wired:") - - b.linksMutex.RLock() - link, exists := b.links[ifname] - b.linksMutex.RUnlock() - - if !exists { - return fmt.Errorf("interface %s not found", ifname) - } - - linkObj := b.conn.Object(networkdBusName, link.path) - return linkObj.Call(networkdLinkIface+".Reconfigure", 0).Err -} - -func (b *SystemdNetworkdBackend) GetEthernetDevices() []EthernetDevice { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - return append([]EthernetDevice(nil), b.state.EthernetDevices...) -} - -func (b *SystemdNetworkdBackend) DisconnectEthernetDevice(device string) error { - return fmt.Errorf("not supported by networkd backend") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_signals.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_signals.go deleted file mode 100644 index dc642ed..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_signals.go +++ /dev/null @@ -1,68 +0,0 @@ -package network - -import ( - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" -) - -func (b *SystemdNetworkdBackend) StartMonitoring(onStateChange func()) error { - b.onStateChange = onStateChange - - b.signals = make(chan *dbus.Signal, 64) - b.conn.Signal(b.signals) - - matchRules := []string{ - "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path_namespace='/org/freedesktop/network1'", - "type='signal',interface='org.freedesktop.network1.Manager'", - } - - for _, rule := range matchRules { - if err := b.conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule).Err; err != nil { - return fmt.Errorf("add match %q: %w", rule, err) - } - } - - b.sigWG.Add(1) - go b.signalLoop() - - return nil -} - -func (b *SystemdNetworkdBackend) StopMonitoring() { - b.sigWG.Wait() -} - -func (b *SystemdNetworkdBackend) signalLoop() { - defer b.sigWG.Done() - - for { - select { - case <-b.stopChan: - return - case sig := <-b.signals: - if sig == nil { - continue - } - - if sig.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" { - if len(sig.Body) < 2 { - continue - } - iface, ok := sig.Body[0].(string) - if !ok || iface != networkdLinkIface { - continue - } - - b.enumerateLinks() - if err := b.updateState(); err != nil { - log.Warnf("networkd state update failed: %v", err) - } - if b.onStateChange != nil { - b.onStateChange() - } - } - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_test.go deleted file mode 100644 index 18c5281..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestSystemdNetworkdBackend_New(t *testing.T) { - backend, err := NewSystemdNetworkdBackend() - assert.NoError(t, err) - assert.NotNil(t, backend) - assert.Equal(t, "networkd", backend.state.Backend) - assert.NotNil(t, backend.links) - assert.NotNil(t, backend.stopChan) -} - -func TestSystemdNetworkdBackend_GetCurrentState(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - backend.state.NetworkStatus = StatusEthernet - backend.state.EthernetConnected = true - backend.state.EthernetIP = "192.168.1.100" - - state, err := backend.GetCurrentState() - assert.NoError(t, err) - assert.NotNil(t, state) - assert.Equal(t, StatusEthernet, state.NetworkStatus) - assert.True(t, state.EthernetConnected) - assert.Equal(t, "192.168.1.100", state.EthernetIP) -} - -func TestSystemdNetworkdBackend_WiFiNotSupported(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - err := backend.ScanWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - req := ConnectionRequest{SSID: "test"} - err = backend.ConnectWiFi(req) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.DisconnectWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.ForgetWiFiNetwork("test") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - _, err = backend.GetWiFiNetworkDetails("test") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestSystemdNetworkdBackend_VPNNotSupported(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - profiles, err := backend.ListVPNProfiles() - assert.NoError(t, err) - assert.Empty(t, profiles) - - active, err := backend.ListActiveVPN() - assert.NoError(t, err) - assert.Empty(t, active) - - err = backend.ConnectVPN("test", false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.DisconnectVPN("test") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.DisconnectAllVPN() - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") - - err = backend.ClearVPNCredentials("test") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestSystemdNetworkdBackend_PromptBroker(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - broker := backend.GetPromptBroker() - assert.Nil(t, broker) - - err := backend.SetPromptBroker(nil) - assert.NoError(t, err) - - err = backend.SubmitCredentials("token", nil, false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not needed") - - err = backend.CancelCredentials("token") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not needed") -} - -func TestSystemdNetworkdBackend_GetWiFiEnabled(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - enabled, err := backend.GetWiFiEnabled() - assert.NoError(t, err) - assert.True(t, enabled) -} - -func TestSystemdNetworkdBackend_SetWiFiEnabled(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - err := backend.SetWiFiEnabled(false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestSystemdNetworkdBackend_DisconnectEthernet(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - err := backend.DisconnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} - -func TestSystemdNetworkdBackend_GetEthernetDevices(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - backend.state.EthernetDevices = []EthernetDevice{ - {Name: "enp0s3", State: "routable", Connected: true}, - {Name: "enp0s8", State: "no-carrier", Connected: false}, - } - - devices := backend.GetEthernetDevices() - assert.Len(t, devices, 2) - assert.Equal(t, "enp0s3", devices[0].Name) - assert.True(t, devices[0].Connected) -} - -func TestSystemdNetworkdBackend_DisconnectEthernetDevice(t *testing.T) { - backend, _ := NewSystemdNetworkdBackend() - - err := backend.DisconnectEthernetDevice("enp0s3") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not supported") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_unimplemented.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_unimplemented.go deleted file mode 100644 index 695c3c5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkd_unimplemented.go +++ /dev/null @@ -1,99 +0,0 @@ -package network - -import "fmt" - -func (b *SystemdNetworkdBackend) GetWiFiEnabled() (bool, error) { - return true, nil -} - -func (b *SystemdNetworkdBackend) SetWiFiEnabled(enabled bool) error { - return fmt.Errorf("WiFi control not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ScanWiFi() error { - return fmt.Errorf("WiFi scan not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) { - return nil, fmt.Errorf("WiFi details not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) GetWiFiQRCodeContent(ssid string) (string, error) { - return "", fmt.Errorf("WiFi QR Code not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ConnectWiFi(req ConnectionRequest) error { - return fmt.Errorf("WiFi connect not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) DisconnectWiFi() error { - return fmt.Errorf("WiFi disconnect not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ForgetWiFiNetwork(ssid string) error { - return fmt.Errorf("WiFi forget not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ListVPNProfiles() ([]VPNProfile, error) { - return []VPNProfile{}, nil -} - -func (b *SystemdNetworkdBackend) ListActiveVPN() ([]VPNActive, error) { - return []VPNActive{}, nil -} - -func (b *SystemdNetworkdBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) DisconnectVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) DisconnectAllVPN() error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ClearVPNCredentials(uuidOrName string) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ListVPNPlugins() ([]VPNPlugin, error) { - return []VPNPlugin{}, nil -} - -func (b *SystemdNetworkdBackend) ImportVPN(filePath string, name string) (*VPNImportResult, error) { - return nil, fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) GetVPNConfig(uuidOrName string) (*VPNConfig, error) { - return nil, fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) UpdateVPNConfig(uuid string, updates map[string]any) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) DeleteVPN(uuidOrName string) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) SetVPNCredentials(uuid, username, password string, save bool) error { - return fmt.Errorf("VPN not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - return fmt.Errorf("WiFi autoconnect not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) ScanWiFiDevice(device string) error { - return fmt.Errorf("WiFi scan not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) DisconnectWiFiDevice(device string) error { - return fmt.Errorf("WiFi disconnect not supported by networkd backend") -} - -func (b *SystemdNetworkdBackend) GetWiFiDevices() []WiFiDevice { - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager.go deleted file mode 100644 index 2233975..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager.go +++ /dev/null @@ -1,405 +0,0 @@ -package network - -import ( - "fmt" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/Wifx/gonetworkmanager/v2" - "github.com/godbus/dbus/v5" -) - -const ( - dbusNMPath = "/org/freedesktop/NetworkManager" - dbusNMInterface = "org.freedesktop.NetworkManager" - dbusNMDeviceInterface = "org.freedesktop.NetworkManager.Device" - dbusNMWiredInterface = "org.freedesktop.NetworkManager.Device.Wired" - dbusNMWirelessInterface = "org.freedesktop.NetworkManager.Device.Wireless" - dbusNMAccessPointInterface = "org.freedesktop.NetworkManager.AccessPoint" - dbusPropsInterface = "org.freedesktop.DBus.Properties" - - NmDeviceStateReasonWrongPassword = 8 - NmDeviceStateReasonSupplicantTimeout = 24 - NmDeviceStateReasonSupplicantFailed = 25 - NmDeviceStateReasonSecretsRequired = 7 - NmDeviceStateReasonNoSecrets = 6 - NmDeviceStateReasonNoSsid = 10 - NmDeviceStateReasonDhcpClientFailed = 14 - NmDeviceStateReasonIpConfigUnavailable = 18 - NmDeviceStateReasonSupplicantDisconnect = 23 - NmDeviceStateReasonCarrier = 40 - NmDeviceStateReasonNewActivation = 60 -) - -type wifiDeviceInfo struct { - device gonetworkmanager.Device - wireless gonetworkmanager.DeviceWireless - name string - hwAddress string -} - -type ethernetDeviceInfo struct { - device gonetworkmanager.Device - wired gonetworkmanager.DeviceWired - name string - hwAddress string -} - -type NetworkManagerBackend struct { - nmConn any - ethernetDevice any - ethernetDevices map[string]*ethernetDeviceInfo - wifiDevice any - settings any - wifiDev any - wifiDevices map[string]*wifiDeviceInfo - - dbusConn *dbus.Conn - signals chan *dbus.Signal - sigWG sync.WaitGroup - stopChan chan struct{} - - secretAgent *SecretAgent - promptBroker PromptBroker - - state *BackendState - stateMutex sync.RWMutex - - lastFailedSSID string - lastFailedTime int64 - failedMutex sync.RWMutex - - pendingVPNSave *pendingVPNCredentials - pendingVPNSaveMu sync.Mutex - cachedVPNCreds *cachedVPNCredentials - cachedVPNCredsMu sync.Mutex - cachedPKCS11PIN *cachedPKCS11PIN - cachedPKCS11Mu sync.Mutex - cachedGPSamlCookie *cachedGPSamlCookie - cachedGPSamlMu sync.Mutex - - onStateChange func() -} - -type pendingVPNCredentials struct { - ConnectionPath string - Username string - Password string - SavePassword bool -} - -type cachedVPNCredentials struct { - ConnectionUUID string - Password string - SavePassword bool -} - -type cachedPKCS11PIN struct { - ConnectionUUID string - PIN string -} - -type cachedGPSamlCookie struct { - ConnectionUUID string - Cookie string - Host string - User string - Fingerprint string -} - -func NewNetworkManagerBackend(nmConn ...gonetworkmanager.NetworkManager) (*NetworkManagerBackend, error) { - var nm gonetworkmanager.NetworkManager - var err error - - if len(nmConn) > 0 && nmConn[0] != nil { - // Use injected connection (for testing) - nm = nmConn[0] - } else { - // Create real connection - nm, err = gonetworkmanager.NewNetworkManager() - if err != nil { - return nil, fmt.Errorf("failed to connect to NetworkManager: %w", err) - } - } - - backend := &NetworkManagerBackend{ - nmConn: nm, - stopChan: make(chan struct{}), - ethernetDevices: make(map[string]*ethernetDeviceInfo), - wifiDevices: make(map[string]*wifiDeviceInfo), - state: &BackendState{ - Backend: "networkmanager", - }, - } - - return backend, nil -} - -func (b *NetworkManagerBackend) Initialize() error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - if s, err := gonetworkmanager.NewSettings(); err == nil { - b.settings = s - } - - devices, err := nm.GetDevices() - if err != nil { - return fmt.Errorf("failed to get devices: %w", err) - } - - for _, dev := range devices { - devType, err := dev.GetPropertyDeviceType() - if err != nil { - continue - } - - switch devType { - case gonetworkmanager.NmDeviceTypeEthernet: - if managed, _ := dev.GetPropertyManaged(); !managed { - continue - } - iface, err := dev.GetPropertyInterface() - if err != nil { - continue - } - w, err := gonetworkmanager.NewDeviceWired(dev.GetPath()) - if err != nil { - continue - } - hwAddr, _ := w.GetPropertyHwAddress() - - b.ethernetDevices[iface] = ðernetDeviceInfo{ - device: dev, - wired: w, - name: iface, - hwAddress: hwAddr, - } - - if b.ethernetDevice == nil { - b.ethernetDevice = dev - } - if err := b.updateEthernetState(); err != nil { - continue - } - _, err = b.listEthernetConnections() - if err != nil { - return fmt.Errorf("failed to get wired configurations: %w", err) - } - - case gonetworkmanager.NmDeviceTypeWifi: - iface, err := dev.GetPropertyInterface() - if err != nil { - continue - } - w, err := gonetworkmanager.NewDeviceWireless(dev.GetPath()) - if err != nil { - continue - } - hwAddr, _ := w.GetPropertyHwAddress() - - b.wifiDevices[iface] = &wifiDeviceInfo{ - device: dev, - wireless: w, - name: iface, - hwAddress: hwAddr, - } - - if b.wifiDevice == nil { - b.wifiDevice = dev - b.wifiDev = w - } - } - } - - wifiEnabled, err := nm.GetPropertyWirelessEnabled() - if err == nil { - b.stateMutex.Lock() - b.state.WiFiEnabled = wifiEnabled - b.stateMutex.Unlock() - } - - if err := b.updateWiFiState(); err != nil { - log.Warnf("Failed to update WiFi state: %v", err) - } - - if wifiEnabled { - if _, err := b.updateWiFiNetworks(); err != nil { - log.Warnf("Failed to get initial networks: %v", err) - } - b.updateAllWiFiDevices() - } - - b.updateAllEthernetDevices() - - if err := b.updatePrimaryConnection(); err != nil { - return err - } - - if _, err := b.ListVPNProfiles(); err != nil { - log.Warnf("Failed to get initial VPN profiles: %v", err) - } - - if _, err := b.ListActiveVPN(); err != nil { - log.Warnf("Failed to get initial active VPNs: %v", err) - } - - return nil -} - -func (b *NetworkManagerBackend) Close() { - close(b.stopChan) - b.StopMonitoring() - - if b.secretAgent != nil { - b.secretAgent.Close() - } -} - -func (b *NetworkManagerBackend) GetCurrentState() (*BackendState, error) { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - - state := *b.state - state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...) - state.WiFiDevices = append([]WiFiDevice(nil), b.state.WiFiDevices...) - state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...) - state.EthernetDevices = append([]EthernetDevice(nil), b.state.EthernetDevices...) - state.VPNProfiles = append([]VPNProfile(nil), b.state.VPNProfiles...) - state.VPNActive = append([]VPNActive(nil), b.state.VPNActive...) - - return &state, nil -} - -func (b *NetworkManagerBackend) StartMonitoring(onStateChange func()) error { - b.onStateChange = onStateChange - - if err := b.startSecretAgent(); err != nil { - return fmt.Errorf("failed to start secret agent: %w", err) - } - - if err := b.startSignalPump(); err != nil { - return err - } - - return nil -} - -func (b *NetworkManagerBackend) StopMonitoring() { - b.stopSignalPump() -} - -func (b *NetworkManagerBackend) GetPromptBroker() PromptBroker { - return b.promptBroker -} - -func (b *NetworkManagerBackend) SetPromptBroker(broker PromptBroker) error { - if broker == nil { - return fmt.Errorf("broker cannot be nil") - } - - hadAgent := b.secretAgent != nil - - b.promptBroker = broker - - if b.secretAgent != nil { - b.secretAgent.Close() - b.secretAgent = nil - } - - if hadAgent { - return b.startSecretAgent() - } - - return nil -} - -func (b *NetworkManagerBackend) SubmitCredentials(token string, secrets map[string]string, save bool) error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return b.promptBroker.Resolve(token, PromptReply{ - Secrets: secrets, - Save: save, - Cancel: false, - }) -} - -func (b *NetworkManagerBackend) CancelCredentials(token string) error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not initialized") - } - - return b.promptBroker.Resolve(token, PromptReply{ - Cancel: true, - }) -} - -func (b *NetworkManagerBackend) ensureWiFiDevice() error { - if b.wifiDev != nil { - return nil - } - - if b.wifiDevice == nil { - return fmt.Errorf("no WiFi device available") - } - - dev := b.wifiDevice.(gonetworkmanager.Device) - wifiDev, err := gonetworkmanager.NewDeviceWireless(dev.GetPath()) - if err != nil { - return fmt.Errorf("failed to get wireless device: %w", err) - } - b.wifiDev = wifiDev - return nil -} - -func (b *NetworkManagerBackend) startSecretAgent() error { - if b.promptBroker == nil { - return fmt.Errorf("prompt broker not set") - } - - agent, err := NewSecretAgent(b.promptBroker, nil, b) - if err != nil { - return err - } - - b.secretAgent = agent - return nil -} - -func (b *NetworkManagerBackend) getActiveConnections() (map[string]bool, error) { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeUUIDs := make(map[string]bool) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return activeUUIDs, fmt.Errorf("failed to get active connections: %w", err) - } - - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "802-3-ethernet" { - continue - } - - state, err := activeConn.GetPropertyState() - if err != nil { - continue - } - if state < 1 || state > 2 { - continue - } - - uuid, err := activeConn.GetPropertyUUID() - if err != nil { - continue - } - activeUUIDs[uuid] = true - } - return activeUUIDs, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_ethernet.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_ethernet.go deleted file mode 100644 index 47f785b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_ethernet.go +++ /dev/null @@ -1,402 +0,0 @@ -package network - -import ( - "fmt" - "net" - "strconv" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/Wifx/gonetworkmanager/v2" -) - -func (b *NetworkManagerBackend) GetWiredConnections() ([]WiredConnection, error) { - return b.listEthernetConnections() -} - -func (b *NetworkManagerBackend) GetWiredNetworkDetails(uuid string) (*WiredNetworkInfoResponse, error) { - if b.ethernetDevice == nil { - return nil, fmt.Errorf("no ethernet device available") - } - - dev := b.ethernetDevice.(gonetworkmanager.Device) - - iface, _ := dev.GetPropertyInterface() - driver, _ := dev.GetPropertyDriver() - - hwAddr := "Not available" - var speed uint32 = 0 - wiredDevice, err := gonetworkmanager.NewDeviceWired(dev.GetPath()) - if err == nil { - hwAddr, _ = wiredDevice.GetPropertyHwAddress() - speed, _ = wiredDevice.GetPropertySpeed() - } - var ipv4Config WiredIPConfig - var ipv6Config WiredIPConfig - - activeConn, err := dev.GetPropertyActiveConnection() - if err == nil && activeConn != nil { - ip4Config, err := activeConn.GetPropertyIP4Config() - if err == nil && ip4Config != nil { - var ips []string - addresses, err := ip4Config.GetPropertyAddressData() - if err == nil && len(addresses) > 0 { - for _, addr := range addresses { - ips = append(ips, fmt.Sprintf("%s/%s", addr.Address, strconv.Itoa(int(addr.Prefix)))) - } - } - - gateway, _ := ip4Config.GetPropertyGateway() - dnsAddrs := "" - dns, err := ip4Config.GetPropertyNameserverData() - if err == nil && len(dns) > 0 { - for _, d := range dns { - if len(dnsAddrs) > 0 { - dnsAddrs = strings.Join([]string{dnsAddrs, d.Address}, "; ") - } else { - dnsAddrs = d.Address - } - } - } - - ipv4Config = WiredIPConfig{ - IPs: ips, - Gateway: gateway, - DNS: dnsAddrs, - } - } - - ip6Config, err := activeConn.GetPropertyIP6Config() - if err == nil && ip6Config != nil { - var ips []string - addresses, err := ip6Config.GetPropertyAddressData() - if err == nil && len(addresses) > 0 { - for _, addr := range addresses { - ips = append(ips, fmt.Sprintf("%s/%s", addr.Address, strconv.Itoa(int(addr.Prefix)))) - } - } - - gateway, _ := ip6Config.GetPropertyGateway() - dnsAddrs := "" - dns, err := ip6Config.GetPropertyNameservers() - if err == nil && len(dns) > 0 { - for _, d := range dns { - if len(d) == 16 { - ip := net.IP(d) - if len(dnsAddrs) > 0 { - dnsAddrs = strings.Join([]string{dnsAddrs, ip.String()}, "; ") - } else { - dnsAddrs = ip.String() - } - } - } - } - - ipv6Config = WiredIPConfig{ - IPs: ips, - Gateway: gateway, - DNS: dnsAddrs, - } - } - } - - return &WiredNetworkInfoResponse{ - UUID: uuid, - IFace: iface, - Driver: driver, - HwAddr: hwAddr, - Speed: strconv.Itoa(int(speed)), - IPv4: ipv4Config, - IPv6: ipv6Config, - }, nil -} - -func (b *NetworkManagerBackend) ConnectEthernet() error { - if b.ethernetDevice == nil { - return fmt.Errorf("no ethernet device available") - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - dev := b.ethernetDevice.(gonetworkmanager.Device) - - settingsMgr, err := gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - if connMeta, ok := connSettings["connection"]; ok { - if connType, ok := connMeta["type"].(string); ok && connType == "802-3-ethernet" { - _, err := nm.ActivateConnection(conn, dev, nil) - if err != nil { - return fmt.Errorf("failed to activate ethernet: %w", err) - } - - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil - } - } - } - - settings := make(map[string]map[string]any) - settings["connection"] = map[string]any{ - "id": "Wired connection", - "type": "802-3-ethernet", - } - - _, err = nm.AddAndActivateConnection(settings, dev) - if err != nil { - return fmt.Errorf("failed to create and activate ethernet: %w", err) - } - - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) DisconnectEthernet() error { - if b.ethernetDevice == nil { - return fmt.Errorf("no ethernet device available") - } - - dev := b.ethernetDevice.(gonetworkmanager.Device) - - err := dev.Disconnect() - if err != nil { - return fmt.Errorf("failed to disconnect: %w", err) - } - - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) ActivateWiredConnection(uuid string) error { - if b.ethernetDevice == nil { - return fmt.Errorf("no ethernet device available") - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - dev := b.ethernetDevice.(gonetworkmanager.Device) - - settingsMgr, err := gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - var targetConnection gonetworkmanager.Connection - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - if connectionSettings, ok := settings["connection"]; ok { - if connUUID, ok := connectionSettings["uuid"].(string); ok && connUUID == uuid { - targetConnection = conn - break - } - } - } - - if targetConnection == nil { - return fmt.Errorf("connection with UUID %s not found", uuid) - } - - _, err = nm.ActivateConnection(targetConnection, dev, nil) - if err != nil { - return fmt.Errorf("error activation connection: %w", err) - } - - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) listEthernetConnections() ([]WiredConnection, error) { - if b.ethernetDevice == nil { - return nil, fmt.Errorf("no ethernet device available") - } - - s := b.settings - if s == nil { - s, err := gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - wiredConfigs := make([]WiredConnection, 0) - activeUUIDs, err := b.getActiveConnections() - - if err != nil { - return nil, fmt.Errorf("failed to get active wired connections: %w", err) - } - - currentUuid := "" - for _, connection := range connections { - path := connection.GetPath() - settings, err := connection.GetSettings() - if err != nil { - log.Errorf("unable to get settings for %s: %v", path, err) - continue - } - - connectionSettings := settings["connection"] - connType, _ := connectionSettings["type"].(string) - connID, _ := connectionSettings["id"].(string) - connUUID, _ := connectionSettings["uuid"].(string) - - if connType == "802-3-ethernet" { - wiredConfigs = append(wiredConfigs, WiredConnection{ - Path: path, - ID: connID, - UUID: connUUID, - Type: connType, - IsActive: activeUUIDs[connUUID], - }) - if activeUUIDs[connUUID] { - currentUuid = connUUID - } - } - } - - b.stateMutex.Lock() - b.state.EthernetConnectionUuid = currentUuid - b.state.WiredConnections = wiredConfigs - b.stateMutex.Unlock() - - return wiredConfigs, nil -} - -func (b *NetworkManagerBackend) GetEthernetDevices() []EthernetDevice { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - return append([]EthernetDevice(nil), b.state.EthernetDevices...) -} - -func (b *NetworkManagerBackend) DisconnectEthernetDevice(device string) error { - info, ok := b.ethernetDevices[device] - if !ok { - return fmt.Errorf("ethernet device %s not found", device) - } - - if err := info.device.Disconnect(); err != nil { - return fmt.Errorf("failed to disconnect %s: %w", device, err) - } - - b.updateAllEthernetDevices() - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) updateAllEthernetDevices() { - devices := make([]EthernetDevice, 0, len(b.ethernetDevices)) - - for name, info := range b.ethernetDevices { - state, _ := info.device.GetPropertyState() - connected := state == gonetworkmanager.NmDeviceStateActivated - driver, _ := info.device.GetPropertyDriver() - - var ip string - var speed uint32 = 0 - if connected { - ip = b.getDeviceIP(info.device) - } - if info.wired != nil { - speed, _ = info.wired.GetPropertySpeed() - } - - stateStr := "disconnected" - switch state { - case gonetworkmanager.NmDeviceStateActivated: - stateStr = "activated" - case gonetworkmanager.NmDeviceStatePrepare: - stateStr = "preparing" - case gonetworkmanager.NmDeviceStateConfig: - stateStr = "configuring" - case gonetworkmanager.NmDeviceStateIpConfig: - stateStr = "ip-config" - case gonetworkmanager.NmDeviceStateIpCheck: - stateStr = "ip-check" - case gonetworkmanager.NmDeviceStateSecondaries: - stateStr = "secondaries" - case gonetworkmanager.NmDeviceStateDeactivating: - stateStr = "deactivating" - case gonetworkmanager.NmDeviceStateFailed: - stateStr = "failed" - case gonetworkmanager.NmDeviceStateUnavailable: - stateStr = "unavailable" - case gonetworkmanager.NmDeviceStateUnmanaged: - stateStr = "unmanaged" - } - - devices = append(devices, EthernetDevice{ - Name: name, - HwAddress: info.hwAddress, - State: stateStr, - Connected: connected, - IP: ip, - Speed: speed, - Driver: driver, - }) - } - - b.stateMutex.Lock() - b.state.EthernetDevices = devices - b.stateMutex.Unlock() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_ethernet_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_ethernet_test.go deleted file mode 100644 index 0901913..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_ethernet_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package network - -import ( - "testing" - - mock_gonetworkmanager "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_GetWiredConnections_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.ethernetDevice = nil - _, err = backend.GetWiredConnections() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_GetWiredNetworkDetails_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.ethernetDevice = nil - _, err = backend.GetWiredNetworkDetails("test-uuid") - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_ConnectEthernet_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.ethernetDevice = nil - err = backend.ConnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_DisconnectEthernet_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.ethernetDevice = nil - err = backend.DisconnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_ActivateWiredConnection_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.ethernetDevice = nil - err = backend.ActivateWiredConnection("test-uuid") - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_ActivateWiredConnection_NotFound(t *testing.T) { - t.Skip("ActivateWiredConnection creates a new Settings instance internally, cannot be fully mocked") -} - -func TestNetworkManagerBackend_ListEthernetConnections_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.ethernetDevice = nil - _, err = backend.listEthernetConnections() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestNetworkManagerBackend_GetEthernetDevices_Empty(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - devices := backend.GetEthernetDevices() - assert.Empty(t, devices) -} - -func TestNetworkManagerBackend_GetEthernetDevices_WithState(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.state.EthernetDevices = []EthernetDevice{ - {Name: "enp0s3", HwAddress: "00:11:22:33:44:55", State: "activated", Connected: true, IP: "192.168.1.100"}, - {Name: "enp0s8", HwAddress: "00:11:22:33:44:66", State: "disconnected", Connected: false}, - } - - devices := backend.GetEthernetDevices() - assert.Len(t, devices, 2) - assert.Equal(t, "enp0s3", devices[0].Name) - assert.True(t, devices[0].Connected) - assert.Equal(t, "enp0s8", devices[1].Name) - assert.False(t, devices[1].Connected) -} - -func TestNetworkManagerBackend_DisconnectEthernetDevice_NotFound(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - err = backend.DisconnectEthernetDevice("nonexistent") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not found") -} - -func TestNetworkManagerBackend_UpdateAllEthernetDevices_Empty(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.updateAllEthernetDevices() - assert.Empty(t, backend.state.EthernetDevices) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_gp_saml.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_gp_saml.go deleted file mode 100644 index b1fa7fb..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_gp_saml.go +++ /dev/null @@ -1,203 +0,0 @@ -package network - -import ( - "bufio" - "context" - "fmt" - "os/exec" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -type gpSamlAuthResult struct { - Cookie string - Host string - User string - Fingerprint string -} - -// runGlobalProtectSAMLAuth handles GlobalProtect SAML/SSO authentication using gp-saml-gui. -// Only supports protocol=gp. Other protocols need their own implementations. -func (b *NetworkManagerBackend) runGlobalProtectSAMLAuth(ctx context.Context, gateway, protocol string) (*gpSamlAuthResult, error) { - if gateway == "" { - return nil, fmt.Errorf("GP SAML auth: gateway is empty") - } - if protocol != "gp" { - return nil, fmt.Errorf("only GlobalProtect (protocol=gp) SAML is supported, got: %s", protocol) - } - - log.Infof("[GP-SAML] Starting GlobalProtect SAML authentication with gp-saml-gui for gateway=%s", gateway) - - gpSamlPath, err := exec.LookPath("gp-saml-gui") - if err != nil { - return nil, fmt.Errorf("GlobalProtect SAML requires gp-saml-gui (install: pip install gp-saml-gui): %w", err) - } - - args := []string{ - "--gateway", - "--allow-insecure-crypto", - gateway, - } - - cmd := exec.CommandContext(ctx, gpSamlPath, args...) - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, fmt.Errorf("GP SAML auth: failed to create stdout pipe: %w", err) - } - - stderr, err := cmd.StderrPipe() - if err != nil { - return nil, fmt.Errorf("GP SAML auth: failed to create stderr pipe: %w", err) - } - - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("GP SAML auth: failed to start gp-saml-gui: %w", err) - } - - go func() { - scanner := bufio.NewScanner(stderr) - for scanner.Scan() { - log.Debugf("[GP-SAML] gp-saml-gui: %s", scanner.Text()) - } - }() - - result := &gpSamlAuthResult{Host: gateway} - var allOutput []string - - scanner := bufio.NewScanner(stdout) - for scanner.Scan() { - line := scanner.Text() - allOutput = append(allOutput, line) - log.Infof("[GP-SAML] stdout: %s", line) - - switch { - case strings.HasPrefix(line, "COOKIE="): - result.Cookie = unshellQuote(strings.TrimPrefix(line, "COOKIE=")) - case strings.HasPrefix(line, "HOST="): - result.Host = unshellQuote(strings.TrimPrefix(line, "HOST=")) - case strings.HasPrefix(line, "USER="): - result.User = unshellQuote(strings.TrimPrefix(line, "USER=")) - case strings.HasPrefix(line, "FINGERPRINT="): - result.Fingerprint = unshellQuote(strings.TrimPrefix(line, "FINGERPRINT=")) - default: - parseGPSamlFromCommandLine(line, result) - } - } - - if err := cmd.Wait(); err != nil { - if ctx.Err() != nil { - return nil, fmt.Errorf("GP SAML auth timed out or was cancelled: %w", ctx.Err()) - } - if result.Cookie == "" { - return nil, fmt.Errorf("GP SAML auth failed: %w (output: %s)", err, strings.Join(allOutput, "\n")) - } - log.Warnf("[GP-SAML] gp-saml-gui exited with error but cookie was captured: %v", err) - } - - if result.Cookie == "" { - return nil, fmt.Errorf("GP SAML auth: no cookie in gp-saml-gui output") - } - - log.Infof("[GP-SAML] Got prelogin-cookie from gp-saml-gui, converting to openconnect cookie via --authenticate") - - // Convert prelogin-cookie to full openconnect cookie format - ocResult, err := convertGPPreloginCookie(ctx, gateway, result.Cookie, result.User) - if err != nil { - return nil, fmt.Errorf("GP SAML auth: failed to convert prelogin-cookie: %w", err) - } - - result.Cookie = ocResult.Cookie - result.Host = ocResult.Host - result.Fingerprint = ocResult.Fingerprint - - log.Infof("[GP-SAML] Authentication successful: user=%s, host=%s, cookie_len=%d, has_fingerprint=%v", - result.User, result.Host, len(result.Cookie), result.Fingerprint != "") - return result, nil -} - -func convertGPPreloginCookie(ctx context.Context, gateway, preloginCookie, user string) (*gpSamlAuthResult, error) { - ocPath, err := exec.LookPath("openconnect") - if err != nil { - return nil, fmt.Errorf("openconnect not found: %w", err) - } - - args := []string{ - "--protocol=gp", - "--usergroup=gateway:prelogin-cookie", - "--user=" + user, - "--passwd-on-stdin", - "--allow-insecure-crypto", - "--authenticate", - gateway, - } - - cmd := exec.CommandContext(ctx, ocPath, args...) - cmd.Stdin = strings.NewReader(preloginCookie) - - output, err := cmd.CombinedOutput() - if err != nil { - return nil, fmt.Errorf("openconnect --authenticate failed: %w\noutput: %s", err, string(output)) - } - - result := &gpSamlAuthResult{} - for _, line := range strings.Split(string(output), "\n") { - line = strings.TrimSpace(line) - switch { - case strings.HasPrefix(line, "COOKIE="): - result.Cookie = unshellQuote(strings.TrimPrefix(line, "COOKIE=")) - case strings.HasPrefix(line, "HOST="): - result.Host = unshellQuote(strings.TrimPrefix(line, "HOST=")) - case strings.HasPrefix(line, "FINGERPRINT="): - result.Fingerprint = unshellQuote(strings.TrimPrefix(line, "FINGERPRINT=")) - case strings.HasPrefix(line, "CONNECT_URL="): - connectURL := unshellQuote(strings.TrimPrefix(line, "CONNECT_URL=")) - if connectURL != "" && result.Host == "" { - result.Host = connectURL - } - } - } - - if result.Cookie == "" { - return nil, fmt.Errorf("no COOKIE in openconnect --authenticate output: %s", string(output)) - } - - log.Infof("[GP-SAML] openconnect --authenticate: cookie_len=%d, host=%s, fingerprint=%s", - len(result.Cookie), result.Host, result.Fingerprint) - - return result, nil -} - -func unshellQuote(s string) string { - if len(s) >= 2 { - if (s[0] == '\'' && s[len(s)-1] == '\'') || - (s[0] == '"' && s[len(s)-1] == '"') { - return s[1 : len(s)-1] - } - } - return s -} - -func parseGPSamlFromCommandLine(line string, result *gpSamlAuthResult) { - if !strings.Contains(line, "openconnect") { - return - } - - for _, part := range strings.Fields(line) { - switch { - case strings.HasPrefix(part, "--cookie="): - if result.Cookie == "" { - result.Cookie = strings.TrimPrefix(part, "--cookie=") - } - case strings.HasPrefix(part, "--servercert="): - if result.Fingerprint == "" { - result.Fingerprint = strings.TrimPrefix(part, "--servercert=") - } - case strings.HasPrefix(part, "--user="): - if result.User == "" { - result.User = strings.TrimPrefix(part, "--user=") - } - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_gp_saml_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_gp_saml_test.go deleted file mode 100644 index 5655815..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_gp_saml_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestUnshellQuote(t *testing.T) { - tests := []struct { - name string - input string - expected string - }{ - { - name: "single quoted", - input: "'hello world'", - expected: "hello world", - }, - { - name: "double quoted", - input: `"hello world"`, - expected: "hello world", - }, - { - name: "unquoted", - input: "hello", - expected: "hello", - }, - { - name: "empty single quotes", - input: "''", - expected: "", - }, - { - name: "empty double quotes", - input: `""`, - expected: "", - }, - { - name: "single quote only", - input: "'", - expected: "'", - }, - { - name: "mismatched quotes", - input: "'hello\"", - expected: "'hello\"", - }, - { - name: "with special chars", - input: "'cookie=abc123&user=john'", - expected: "cookie=abc123&user=john", - }, - { - name: "complex cookie", - input: `'authcookie=077058d3bc81&portal=PANGP_GW_01-N&user=john.doe@example.com&domain=Default&preferred-ip=192.168.1.100'`, - expected: "authcookie=077058d3bc81&portal=PANGP_GW_01-N&user=john.doe@example.com&domain=Default&preferred-ip=192.168.1.100", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := unshellQuote(tt.input) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestParseGPSamlFromCommandLine(t *testing.T) { - tests := []struct { - name string - line string - initialResult *gpSamlAuthResult - expectedCookie string - expectedUser string - expectedFP string - }{ - { - name: "full openconnect command", - line: "openconnect --protocol=gp --cookie=AUTH123 --servercert=pin-sha256:ABC --user=john", - initialResult: &gpSamlAuthResult{}, - expectedCookie: "AUTH123", - expectedUser: "john", - expectedFP: "pin-sha256:ABC", - }, - { - name: "with equals signs in cookie", - line: "openconnect --cookie=authcookie=xyz123&portal=GATE --user=jane", - initialResult: &gpSamlAuthResult{}, - expectedCookie: "authcookie=xyz123&portal=GATE", - expectedUser: "jane", - expectedFP: "", - }, - { - name: "non-openconnect line", - line: "some other output", - initialResult: &gpSamlAuthResult{}, - expectedCookie: "", - expectedUser: "", - expectedFP: "", - }, - { - name: "preserves existing values", - line: "openconnect --user=newuser", - initialResult: &gpSamlAuthResult{Cookie: "existing", Fingerprint: "existing-fp"}, - expectedCookie: "existing", - expectedUser: "newuser", - expectedFP: "existing-fp", - }, - { - name: "only updates empty fields", - line: "openconnect --cookie=NEW --user=NEW", - initialResult: &gpSamlAuthResult{Cookie: "OLD"}, - expectedCookie: "OLD", - expectedUser: "NEW", - expectedFP: "", - }, - { - name: "real gp-saml-gui output", - line: "openconnect --protocol=gp --user=john.doe@example.com --os=linux-64 --usergroup=gateway:prelogin-cookie --passwd-on-stdin", - initialResult: &gpSamlAuthResult{}, - expectedCookie: "", - expectedUser: "john.doe@example.com", - expectedFP: "", - }, - { - name: "with server cert flag", - line: "openconnect --servercert=pin-sha256:xp3scfzy3rOgQEXnfPiYKrUk7D66a8b8O+gEXaMPleE= vpn.example.com", - initialResult: &gpSamlAuthResult{}, - expectedCookie: "", - expectedUser: "", - expectedFP: "pin-sha256:xp3scfzy3rOgQEXnfPiYKrUk7D66a8b8O+gEXaMPleE=", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := tt.initialResult - parseGPSamlFromCommandLine(tt.line, result) - - assert.Equal(t, tt.expectedCookie, result.Cookie, "cookie mismatch") - assert.Equal(t, tt.expectedUser, result.User, "user mismatch") - assert.Equal(t, tt.expectedFP, result.Fingerprint, "fingerprint mismatch") - }) - } -} - -func TestParseGPSamlFromCommandLine_MultipleLines(t *testing.T) { - // Simulate gp-saml-gui output with command line suggestion - lines := []string{ - "", - "SAML REDIRECT", - "Got SAML Login URL", - "POST to ACS endpoint...", - "Got 'prelogin-cookie': 'FAKE_cookie_12345'", - "openconnect --protocol=gp --user=john.doe@example.com --usergroup=gateway:prelogin-cookie --passwd-on-stdin vpn.example.com", - "", - } - - result := &gpSamlAuthResult{} - for _, line := range lines { - parseGPSamlFromCommandLine(line, result) - } - - assert.Equal(t, "john.doe@example.com", result.User) - assert.Empty(t, result.Cookie, "cookie should not be parsed from command line") - assert.Empty(t, result.Fingerprint) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_signals.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_signals.go deleted file mode 100644 index 5274ed0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_signals.go +++ /dev/null @@ -1,530 +0,0 @@ -package network - -import ( - "github.com/Wifx/gonetworkmanager/v2" - "github.com/godbus/dbus/v5" -) - -func (b *NetworkManagerBackend) startSignalPump() error { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return err - } - b.dbusConn = conn - - signals := make(chan *dbus.Signal, 256) - b.signals = signals - conn.Signal(signals) - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - conn.RemoveSignal(signals) - conn.Close() - return err - } - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")), - dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"), - dbus.WithMatchMember("NewConnection"), - ); err != nil { - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - conn.RemoveSignal(signals) - conn.Close() - return err - } - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")), - dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"), - dbus.WithMatchMember("ConnectionRemoved"), - ); err != nil { - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - conn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")), - dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"), - dbus.WithMatchMember("NewConnection"), - ) - conn.RemoveSignal(signals) - conn.Close() - return err - } - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusNMInterface), - dbus.WithMatchMember("DeviceAdded"), - ); err != nil { - conn.RemoveSignal(signals) - conn.Close() - return err - } - - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusNMInterface), - dbus.WithMatchMember("DeviceRemoved"), - ); err != nil { - conn.RemoveSignal(signals) - conn.Close() - return err - } - - for _, info := range b.wifiDevices { - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(info.device.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - conn.RemoveSignal(signals) - conn.Close() - return err - } - } - - for _, info := range b.ethernetDevices { - if err := conn.AddMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(info.device.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ); err != nil { - conn.RemoveSignal(signals) - conn.Close() - return err - } - } - - b.sigWG.Add(1) - go func() { - defer b.sigWG.Done() - for { - select { - case <-b.stopChan: - return - case sig, ok := <-signals: - if !ok { - return - } - if sig == nil { - continue - } - b.handleDBusSignal(sig) - } - } - }() - return nil -} - -func (b *NetworkManagerBackend) stopSignalPump() { - if b.dbusConn == nil { - return - } - - b.dbusConn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - - for _, info := range b.wifiDevices { - b.dbusConn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(info.device.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - } - - for _, info := range b.ethernetDevices { - b.dbusConn.RemoveMatchSignal( - dbus.WithMatchObjectPath(dbus.ObjectPath(info.device.GetPath())), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - } - - if b.signals != nil { - b.dbusConn.RemoveSignal(b.signals) - close(b.signals) - } - - b.sigWG.Wait() - - b.dbusConn.Close() -} - -func (b *NetworkManagerBackend) handleDBusSignal(sig *dbus.Signal) { - if sig.Name == "org.freedesktop.NetworkManager.Settings.NewConnection" || - sig.Name == "org.freedesktop.NetworkManager.Settings.ConnectionRemoved" { - b.ListVPNProfiles() - if b.onStateChange != nil { - b.onStateChange() - } - return - } - - if sig.Name == "org.freedesktop.NetworkManager.DeviceAdded" { - if len(sig.Body) >= 1 { - if devicePath, ok := sig.Body[0].(dbus.ObjectPath); ok { - b.handleDeviceAdded(devicePath) - } - } - return - } - - if sig.Name == "org.freedesktop.NetworkManager.DeviceRemoved" { - if len(sig.Body) >= 1 { - if devicePath, ok := sig.Body[0].(dbus.ObjectPath); ok { - b.handleDeviceRemoved(devicePath) - } - } - return - } - - if len(sig.Body) < 2 { - return - } - - iface, ok := sig.Body[0].(string) - if !ok { - return - } - - changes, ok := sig.Body[1].(map[string]dbus.Variant) - if !ok { - return - } - - switch iface { - case dbusNMInterface: - b.handleNetworkManagerChange(changes) - - case dbusNMDeviceInterface: - b.handleDeviceChange(sig.Path, changes) - - case dbusNMWiredInterface: - b.handleWiredChange(changes) - - case dbusNMWirelessInterface: - b.handleWiFiChange(changes) - - case dbusNMAccessPointInterface: - b.handleAccessPointChange(changes) - } -} - -func (b *NetworkManagerBackend) handleNetworkManagerChange(changes map[string]dbus.Variant) { - var needsUpdate bool - - for key := range changes { - switch key { - case "PrimaryConnection", "State", "ActiveConnections": - needsUpdate = true - case "WirelessEnabled": - nm := b.nmConn.(gonetworkmanager.NetworkManager) - if enabled, err := nm.GetPropertyWirelessEnabled(); err == nil { - b.stateMutex.Lock() - b.state.WiFiEnabled = enabled - b.stateMutex.Unlock() - needsUpdate = true - } - default: - continue - } - } - - if needsUpdate { - b.updatePrimaryConnection() - if _, exists := changes["State"]; exists { - b.updateEthernetState() - b.updateWiFiState() - } - if _, exists := changes["ActiveConnections"]; exists { - b.updateVPNConnectionState() - b.ListActiveVPN() - } - if b.onStateChange != nil { - b.onStateChange() - } - } -} - -func (b *NetworkManagerBackend) handleDeviceChange(devicePath dbus.ObjectPath, changes map[string]dbus.Variant) { - var needsUpdate bool - var stateChanged bool - var managedChanged bool - - for key := range changes { - switch key { - case "State": - stateChanged = true - needsUpdate = true - case "Ip4Config": - needsUpdate = true - case "Managed": - managedChanged = true - default: - continue - } - } - - if managedChanged { - if managedVariant, ok := changes["Managed"]; ok { - if managed, ok := managedVariant.Value().(bool); ok && managed { - b.handleDeviceAdded(devicePath) - return - } - } - } - - if !needsUpdate { - return - } - - b.updateAllEthernetDevices() - b.updateEthernetState() - b.updateAllWiFiDevices() - b.updateWiFiState() - if stateChanged { - b.listEthernetConnections() - b.updatePrimaryConnection() - } - if b.onStateChange != nil { - b.onStateChange() - } -} - -func (b *NetworkManagerBackend) handleWiredChange(changes map[string]dbus.Variant) { - var needsUpdate bool - - for key := range changes { - switch key { - case "Carrier", "Speed", "HwAddress": - needsUpdate = true - default: - continue - } - } - - if !needsUpdate { - return - } - - b.updateAllEthernetDevices() - b.updateEthernetState() - b.updatePrimaryConnection() - if b.onStateChange != nil { - b.onStateChange() - } -} - -func (b *NetworkManagerBackend) handleWiFiChange(changes map[string]dbus.Variant) { - var needsStateUpdate bool - var needsNetworkUpdate bool - - for key := range changes { - switch key { - case "ActiveAccessPoint": - needsStateUpdate = true - needsNetworkUpdate = true - case "AccessPoints": - needsNetworkUpdate = true - default: - continue - } - } - - if needsStateUpdate { - b.updateWiFiState() - } - if needsNetworkUpdate { - b.updateWiFiNetworks() - } - if needsStateUpdate || needsNetworkUpdate { - if b.onStateChange != nil { - b.onStateChange() - } - } -} - -func (b *NetworkManagerBackend) handleAccessPointChange(changes map[string]dbus.Variant) { - _, hasStrength := changes["Strength"] - if !hasStrength { - return - } - - b.stateMutex.RLock() - oldSignal := b.state.WiFiSignal - b.stateMutex.RUnlock() - - b.updateWiFiState() - - b.stateMutex.RLock() - newSignal := b.state.WiFiSignal - b.stateMutex.RUnlock() - - if signalChangeSignificant(oldSignal, newSignal) { - if b.onStateChange != nil { - b.onStateChange() - } - } -} - -func (b *NetworkManagerBackend) handleDeviceAdded(devicePath dbus.ObjectPath) { - dev, err := gonetworkmanager.NewDevice(devicePath) - if err != nil { - return - } - - devType, err := dev.GetPropertyDeviceType() - if err != nil { - return - } - - if devType != gonetworkmanager.NmDeviceTypeEthernet && devType != gonetworkmanager.NmDeviceTypeWifi { - return - } - - if b.dbusConn != nil { - b.dbusConn.AddMatchSignal( - dbus.WithMatchObjectPath(devicePath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - } - - managed, _ := dev.GetPropertyManaged() - if !managed { - return - } - - iface, err := dev.GetPropertyInterface() - if err != nil { - return - } - - switch devType { - case gonetworkmanager.NmDeviceTypeEthernet: - w, err := gonetworkmanager.NewDeviceWired(devicePath) - if err != nil { - return - } - hwAddr, _ := w.GetPropertyHwAddress() - - b.ethernetDevices[iface] = ðernetDeviceInfo{ - device: dev, - wired: w, - name: iface, - hwAddress: hwAddr, - } - - if b.ethernetDevice == nil { - b.ethernetDevice = dev - } - - b.updateAllEthernetDevices() - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - case gonetworkmanager.NmDeviceTypeWifi: - w, err := gonetworkmanager.NewDeviceWireless(devicePath) - if err != nil { - return - } - hwAddr, _ := w.GetPropertyHwAddress() - - b.wifiDevices[iface] = &wifiDeviceInfo{ - device: dev, - wireless: w, - name: iface, - hwAddress: hwAddr, - } - - if b.wifiDevice == nil { - b.wifiDevice = dev - b.wifiDev = w - } - - b.updateAllWiFiDevices() - b.updateWiFiState() - } - - if b.onStateChange != nil { - b.onStateChange() - } -} - -func (b *NetworkManagerBackend) handleDeviceRemoved(devicePath dbus.ObjectPath) { - if b.dbusConn != nil { - b.dbusConn.RemoveMatchSignal( - dbus.WithMatchObjectPath(devicePath), - dbus.WithMatchInterface(dbusPropsInterface), - dbus.WithMatchMember("PropertiesChanged"), - ) - } - - for iface, info := range b.ethernetDevices { - if info.device.GetPath() == devicePath { - delete(b.ethernetDevices, iface) - - if b.ethernetDevice != nil { - dev := b.ethernetDevice.(gonetworkmanager.Device) - if dev.GetPath() == devicePath { - b.ethernetDevice = nil - for _, remaining := range b.ethernetDevices { - b.ethernetDevice = remaining.device - break - } - } - } - - b.updateAllEthernetDevices() - b.updateEthernetState() - b.listEthernetConnections() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - return - } - } - - for iface, info := range b.wifiDevices { - if info.device.GetPath() == devicePath { - delete(b.wifiDevices, iface) - - if b.wifiDevice != nil { - dev := b.wifiDevice.(gonetworkmanager.Device) - if dev.GetPath() == devicePath { - b.wifiDevice = nil - b.wifiDev = nil - for _, remaining := range b.wifiDevices { - b.wifiDevice = remaining.device - b.wifiDev = remaining.wireless - break - } - } - } - - b.updateAllWiFiDevices() - b.updateWiFiState() - - if b.onStateChange != nil { - b.onStateChange() - } - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_signals_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_signals_test.go deleted file mode 100644 index 6b58684..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_signals_test.go +++ /dev/null @@ -1,255 +0,0 @@ -package network - -import ( - "testing" - - mock_gonetworkmanager "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/Wifx/gonetworkmanager/v2" - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_HandleDBusSignal_NewConnection(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - sig := &dbus.Signal{ - Name: "org.freedesktop.NetworkManager.Settings.NewConnection", - Body: []any{"/org/freedesktop/NetworkManager/Settings/1"}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleDBusSignal_ConnectionRemoved(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - sig := &dbus.Signal{ - Name: "org.freedesktop.NetworkManager.Settings.ConnectionRemoved", - Body: []any{"/org/freedesktop/NetworkManager/Settings/1"}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleDBusSignal_InvalidBody(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{"only-one-element"}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleDBusSignal_InvalidInterface(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{123, map[string]dbus.Variant{}}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleDBusSignal_InvalidChanges(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - sig := &dbus.Signal{ - Name: "org.freedesktop.DBus.Properties.PropertiesChanged", - Body: []any{dbusNMInterface, "not-a-map"}, - } - - assert.NotPanics(t, func() { - backend.handleDBusSignal(sig) - }) -} - -func TestNetworkManagerBackend_HandleNetworkManagerChange(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil).Maybe() - mockNM.EXPECT().GetPropertyPrimaryConnection().Return(nil, nil).Maybe() - - changes := map[string]dbus.Variant{ - "PrimaryConnection": dbus.MakeVariant("/"), - "State": dbus.MakeVariant(uint32(70)), - } - - assert.NotPanics(t, func() { - backend.handleNetworkManagerChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleNetworkManagerChange_WirelessEnabled(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyWirelessEnabled().Return(true, nil) - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil).Maybe() - mockNM.EXPECT().GetPropertyPrimaryConnection().Return(nil, nil).Maybe() - - changes := map[string]dbus.Variant{ - "WirelessEnabled": dbus.MakeVariant(true), - } - - assert.NotPanics(t, func() { - backend.handleNetworkManagerChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleNetworkManagerChange_ActiveConnections(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - mockNM.EXPECT().GetPropertyPrimaryConnection().Return(nil, nil).Maybe() - - changes := map[string]dbus.Variant{ - "ActiveConnections": dbus.MakeVariant([]any{}), - } - - assert.NotPanics(t, func() { - backend.handleNetworkManagerChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleDeviceChange(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil).Maybe() - mockNM.EXPECT().GetPropertyPrimaryConnection().Return(nil, nil).Maybe() - - changes := map[string]dbus.Variant{ - "State": dbus.MakeVariant(uint32(100)), - } - - assert.NotPanics(t, func() { - backend.handleDeviceChange("/org/freedesktop/NetworkManager/Devices/1", changes) - }) -} - -func TestNetworkManagerBackend_HandleDeviceChange_Ip4Config(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - changes := map[string]dbus.Variant{ - "Ip4Config": dbus.MakeVariant("/"), - } - - assert.NotPanics(t, func() { - backend.handleDeviceChange("/org/freedesktop/NetworkManager/Devices/1", changes) - }) -} - -func TestNetworkManagerBackend_HandleWiFiChange_ActiveAccessPoint(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - changes := map[string]dbus.Variant{ - "ActiveAccessPoint": dbus.MakeVariant("/"), - } - - assert.NotPanics(t, func() { - backend.handleWiFiChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleWiFiChange_AccessPoints(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - changes := map[string]dbus.Variant{ - "AccessPoints": dbus.MakeVariant([]any{}), - } - - assert.NotPanics(t, func() { - backend.handleWiFiChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleAccessPointChange_NoStrength(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - changes := map[string]dbus.Variant{ - "SomeOtherProperty": dbus.MakeVariant("value"), - } - - assert.NotPanics(t, func() { - backend.handleAccessPointChange(changes) - }) -} - -func TestNetworkManagerBackend_HandleAccessPointChange_WithStrength(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.stateMutex.Lock() - backend.state.WiFiSignal = 50 - backend.stateMutex.Unlock() - - changes := map[string]dbus.Variant{ - "Strength": dbus.MakeVariant(uint8(80)), - } - - assert.NotPanics(t, func() { - backend.handleAccessPointChange(changes) - }) -} - -func TestNetworkManagerBackend_StopSignalPump_NoConnection(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.dbusConn = nil - assert.NotPanics(t, func() { - backend.stopSignalPump() - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_state.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_state.go deleted file mode 100644 index ba77f6d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_state.go +++ /dev/null @@ -1,277 +0,0 @@ -package network - -import ( - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/Wifx/gonetworkmanager/v2" -) - -func (b *NetworkManagerBackend) updatePrimaryConnection() error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return err - } - - hasActiveVPN := false - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - if connType == "vpn" || connType == "wireguard" { - state, _ := activeConn.GetPropertyState() - if state == 2 { - hasActiveVPN = true - break - } - } - } - - if hasActiveVPN { - b.stateMutex.Lock() - b.state.NetworkStatus = StatusVPN - b.stateMutex.Unlock() - return nil - } - - primaryConn, err := nm.GetPropertyPrimaryConnection() - if err != nil { - return err - } - - if primaryConn == nil || primaryConn.GetPath() == "/" { - b.stateMutex.Lock() - b.state.NetworkStatus = StatusDisconnected - b.stateMutex.Unlock() - return nil - } - - connType, err := primaryConn.GetPropertyType() - if err != nil { - return err - } - - b.stateMutex.Lock() - switch connType { - case "802-3-ethernet": - b.state.NetworkStatus = StatusEthernet - case "802-11-wireless": - b.state.NetworkStatus = StatusWiFi - case "vpn", "wireguard": - b.state.NetworkStatus = StatusVPN - default: - b.state.NetworkStatus = StatusDisconnected - } - b.stateMutex.Unlock() - - return nil -} - -func (b *NetworkManagerBackend) updateEthernetState() error { - var connectedDevice string - var connectedIP string - var anyConnected bool - - for name, info := range b.ethernetDevices { - state, err := info.device.GetPropertyState() - if err != nil { - continue - } - - if state == gonetworkmanager.NmDeviceStateActivated { - anyConnected = true - connectedDevice = name - connectedIP = b.getDeviceIP(info.device) - break - } - } - - if !anyConnected && b.ethernetDevice != nil { - dev := b.ethernetDevice.(gonetworkmanager.Device) - iface, _ := dev.GetPropertyInterface() - connectedDevice = iface - } - - b.stateMutex.Lock() - b.state.EthernetDevice = connectedDevice - b.state.EthernetConnected = anyConnected - b.state.EthernetIP = connectedIP - b.stateMutex.Unlock() - - return nil -} - -func (b *NetworkManagerBackend) getDeviceStateReason(dev gonetworkmanager.Device) uint32 { - path := dev.GetPath() - obj := b.dbusConn.Object("org.freedesktop.NetworkManager", path) - - variant, err := obj.GetProperty(dbusNMDeviceInterface + ".StateReason") - if err != nil { - return 0 - } - - if stateReasonStruct, ok := variant.Value().([]any); ok && len(stateReasonStruct) >= 2 { - if reason, ok := stateReasonStruct[1].(uint32); ok { - return reason - } - } - - return 0 -} - -func (b *NetworkManagerBackend) classifyNMStateReason(reason uint32) string { - switch reason { - case NmDeviceStateReasonWrongPassword, - NmDeviceStateReasonSupplicantTimeout, - NmDeviceStateReasonSupplicantFailed, - NmDeviceStateReasonSecretsRequired: - return errdefs.ErrBadCredentials - case NmDeviceStateReasonNoSecrets: - return errdefs.ErrUserCanceled - case NmDeviceStateReasonNoSsid: - return errdefs.ErrNoSuchSSID - case NmDeviceStateReasonDhcpClientFailed, - NmDeviceStateReasonIpConfigUnavailable: - return errdefs.ErrDhcpTimeout - case NmDeviceStateReasonSupplicantDisconnect, - NmDeviceStateReasonCarrier: - return errdefs.ErrAssocTimeout - default: - return errdefs.ErrConnectionFailed - } -} - -func (b *NetworkManagerBackend) updateWiFiState() error { - if b.wifiDevice == nil { - return nil - } - - dev := b.wifiDevice.(gonetworkmanager.Device) - - iface, err := dev.GetPropertyInterface() - if err != nil { - return err - } - - state, err := dev.GetPropertyState() - if err != nil { - return err - } - - connected := state == gonetworkmanager.NmDeviceStateActivated - failed := state == gonetworkmanager.NmDeviceStateFailed - disconnected := state == gonetworkmanager.NmDeviceStateDisconnected - - var ip, ssid, bssid string - var signal uint8 - - if connected { - if err := b.ensureWiFiDevice(); err == nil && b.wifiDev != nil { - w := b.wifiDev.(gonetworkmanager.DeviceWireless) - activeAP, err := w.GetPropertyActiveAccessPoint() - if err == nil && activeAP != nil && activeAP.GetPath() != "/" { - ssid, _ = activeAP.GetPropertySSID() - signal, _ = activeAP.GetPropertyStrength() - bssid, _ = activeAP.GetPropertyHWAddress() - } - } - - ip = b.getDeviceIP(dev) - } - - b.stateMutex.RLock() - wasConnecting := b.state.IsConnecting - connectingSSID := b.state.ConnectingSSID - b.stateMutex.RUnlock() - - var reasonCode string - if wasConnecting && connectingSSID != "" && (failed || (disconnected && !connected)) { - reason := b.getDeviceStateReason(dev) - - if reason == NmDeviceStateReasonNewActivation || reason == 0 { - return nil - } - - log.Warnf("[updateWiFiState] Connection failed: SSID=%s, state=%d, reason=%d", connectingSSID, state, reason) - - reasonCode = b.classifyNMStateReason(reason) - - if reasonCode == errdefs.ErrConnectionFailed { - b.failedMutex.RLock() - if b.lastFailedSSID == connectingSSID { - elapsed := time.Now().Unix() - b.lastFailedTime - if elapsed < 5 { - reasonCode = errdefs.ErrBadCredentials - } - } - b.failedMutex.RUnlock() - } - } - - var forgetSSID string - - b.stateMutex.Lock() - - wasConnecting = b.state.IsConnecting - connectingSSID = b.state.ConnectingSSID - - if wasConnecting && connectingSSID != "" { - switch { - case connected && ssid == connectingSSID: - log.Infof("[updateWiFiState] Connection successful: %s", ssid) - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = "" - case failed || (disconnected && !connected): - log.Warnf("[updateWiFiState] Connection failed: SSID=%s, state=%d", connectingSSID, state) - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.LastError = reasonCode - - if reasonCode == errdefs.ErrUserCanceled { - forgetSSID = connectingSSID - } - - b.failedMutex.Lock() - b.lastFailedSSID = connectingSSID - b.lastFailedTime = time.Now().Unix() - b.failedMutex.Unlock() - } - } - - b.state.WiFiDevice = iface - b.state.WiFiConnected = connected - b.state.WiFiIP = ip - b.state.WiFiSSID = ssid - b.state.WiFiBSSID = bssid - b.state.WiFiSignal = signal - - b.stateMutex.Unlock() - - if forgetSSID != "" { - log.Infof("[updateWiFiState] User cancelled authentication, removing connection profile for %s", forgetSSID) - if err := b.ForgetWiFiNetwork(forgetSSID); err != nil { - log.Warnf("[updateWiFiState] Failed to remove cancelled connection: %v", err) - } - } - - return nil -} - -func (b *NetworkManagerBackend) getDeviceIP(dev gonetworkmanager.Device) string { - ip4Config, err := dev.GetPropertyIP4Config() - if err != nil || ip4Config == nil { - return "" - } - - addresses, err := ip4Config.GetPropertyAddressData() - if err != nil || len(addresses) == 0 { - return "" - } - - return addresses[0].Address -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_state_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_state_test.go deleted file mode 100644 index 86aa48f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_state_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package network - -import ( - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - mock_gonetworkmanager "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/Wifx/gonetworkmanager/v2" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_UpdatePrimaryConnection(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - mockNM.EXPECT().GetPropertyPrimaryConnection().Return(nil, nil) - - err = backend.updatePrimaryConnection() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_UpdateEthernetState_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.ethernetDevice = nil - err = backend.updateEthernetState() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_UpdateWiFiState_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - err = backend.updateWiFiState() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_ClassifyNMStateReason(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - testCases := []struct { - reason uint32 - expected string - }{ - {NmDeviceStateReasonWrongPassword, errdefs.ErrBadCredentials}, - {NmDeviceStateReasonNoSecrets, errdefs.ErrUserCanceled}, - {NmDeviceStateReasonSupplicantTimeout, errdefs.ErrBadCredentials}, - {NmDeviceStateReasonDhcpClientFailed, errdefs.ErrDhcpTimeout}, - {NmDeviceStateReasonNoSsid, errdefs.ErrNoSuchSSID}, - {999, errdefs.ErrConnectionFailed}, - } - - for _, tc := range testCases { - result := backend.classifyNMStateReason(tc.reason) - assert.Equal(t, tc.expected, result) - } -} - -func TestNetworkManagerBackend_GetDeviceIP_NoConfig(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockDevice := mock_gonetworkmanager.NewMockDevice(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockDevice.EXPECT().GetPropertyIP4Config().Return(nil, nil) - - ip := backend.getDeviceIP(mockDevice) - assert.Empty(t, ip) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_test.go deleted file mode 100644 index 1391e4a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package network - -import ( - "testing" - - mock_gonetworkmanager "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_New(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - assert.NotNil(t, backend) - assert.Equal(t, "networkmanager", backend.state.Backend) - assert.NotNil(t, backend.stopChan) - assert.NotNil(t, backend.state) -} - -func TestNetworkManagerBackend_GetCurrentState(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.state.NetworkStatus = StatusWiFi - backend.state.WiFiConnected = true - backend.state.WiFiSSID = "TestNetwork" - backend.state.WiFiIP = "192.168.1.100" - backend.state.WiFiNetworks = []WiFiNetwork{ - {SSID: "TestNetwork", Signal: 80, Connected: true}, - } - backend.state.WiredConnections = []WiredConnection{ - {ID: "Wired connection 1", UUID: "test-uuid"}, - } - - state, err := backend.GetCurrentState() - assert.NoError(t, err) - assert.NotNil(t, state) - assert.Equal(t, StatusWiFi, state.NetworkStatus) - assert.True(t, state.WiFiConnected) - assert.Equal(t, "TestNetwork", state.WiFiSSID) - assert.Len(t, state.WiFiNetworks, 1) - assert.Len(t, state.WiredConnections, 1) - - assert.NotSame(t, &backend.state.WiFiNetworks, &state.WiFiNetworks) - assert.NotSame(t, &backend.state.WiredConnections, &state.WiredConnections) -} - -func TestNetworkManagerBackend_SetPromptBroker_Nil(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - err = backend.SetPromptBroker(nil) - assert.Error(t, err) - assert.Contains(t, err.Error(), "cannot be nil") -} - -func TestNetworkManagerBackend_SubmitCredentials_NoBroker(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.promptBroker = nil - err = backend.SubmitCredentials("token", map[string]string{"password": "test"}, false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not initialized") -} - -func TestNetworkManagerBackend_CancelCredentials_NoBroker(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.promptBroker = nil - err = backend.CancelCredentials("token") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not initialized") -} - -func TestNetworkManagerBackend_EnsureWiFiDevice_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - backend.wifiDev = nil - - err = backend.ensureWiFiDevice() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_EnsureWiFiDevice_AlreadySet(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDev = "dummy-device" - - err = backend.ensureWiFiDevice() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_StartSecretAgent_NoBroker(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.promptBroker = nil - err = backend.startSecretAgent() - assert.Error(t, err) - assert.Contains(t, err.Error(), "prompt broker not set") -} - -func TestNetworkManagerBackend_Close(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - assert.NotPanics(t, func() { - backend.Close() - }) -} - -func TestNetworkManagerBackend_GetPromptBroker(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - broker := backend.GetPromptBroker() - assert.Nil(t, broker) -} - -func TestNetworkManagerBackend_StopMonitoring_NoSignals(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - assert.NotPanics(t, func() { - backend.StopMonitoring() - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_vpn.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_vpn.go deleted file mode 100644 index 261fcef..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_vpn.go +++ /dev/null @@ -1,1394 +0,0 @@ -package network - -import ( - "bufio" - "context" - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" - "sort" - "strings" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/Wifx/gonetworkmanager/v2" - "github.com/godbus/dbus/v5" -) - -func (b *NetworkManagerBackend) ListVPNProfiles() ([]VPNProfile, error) { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - profiles := []VPNProfile{} - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - autoconnect, _ := connMeta["autoconnect"].(bool) - - profile := VPNProfile{ - Name: connID, - UUID: connUUID, - Type: connType, - Autoconnect: autoconnect, - } - - if connType == "vpn" { - if vpnSettings, ok := settings["vpn"]; ok { - if svcType, ok := vpnSettings["service-type"].(string); ok { - profile.ServiceType = svcType - } - // Get full data map - if data, ok := vpnSettings["data"].(map[string]string); ok { - profile.Data = data - if remote, ok := data["remote"]; ok { - profile.RemoteHost = remote - } - if username, ok := data["username"]; ok { - profile.Username = username - } - } - } - } - - profiles = append(profiles, profile) - } - - sort.Slice(profiles, func(i, j int) bool { - return strings.ToLower(profiles[i].Name) < strings.ToLower(profiles[j].Name) - }) - - b.stateMutex.Lock() - b.state.VPNProfiles = profiles - b.stateMutex.Unlock() - - return profiles, nil -} - -func (b *NetworkManagerBackend) ListActiveVPN() ([]VPNActive, error) { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return nil, fmt.Errorf("failed to get active connections: %w", err) - } - - active := []VPNActive{} - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "vpn" && connType != "wireguard" { - continue - } - - uuid, _ := activeConn.GetPropertyUUID() - id, _ := activeConn.GetPropertyID() - state, _ := activeConn.GetPropertyState() - - var stateStr string - switch state { - case 0: - stateStr = "unknown" - case 1: - stateStr = "activating" - case 2: - stateStr = "activated" - case 3: - stateStr = "deactivating" - case 4: - stateStr = "deactivated" - } - - vpnActive := VPNActive{ - Name: id, - UUID: uuid, - State: stateStr, - Type: connType, - Plugin: "", - } - - // Get VPN device - devices, _ := activeConn.GetPropertyDevices() - if len(devices) > 0 { - if iface, err := devices[0].GetPropertyInterface(); err == nil { - vpnActive.Device = iface - } - } - - // Get VPN IP from IP4Config - if ip4Config, err := activeConn.GetPropertyIP4Config(); err == nil && ip4Config != nil { - if addrData, err := ip4Config.GetPropertyAddressData(); err == nil && len(addrData) > 0 { - vpnActive.IP = addrData[0].Address - } - if gw, err := ip4Config.GetPropertyGateway(); err == nil { - vpnActive.Gateway = gw - } - } - - // Get MTU from device - if len(devices) > 0 { - if mtu, err := devices[0].GetPropertyMtu(); err == nil { - vpnActive.MTU = mtu - } - } - - if connType == "vpn" { - conn, _ := activeConn.GetPropertyConnection() - if conn != nil { - connSettings, err := conn.GetSettings() - if err == nil { - if vpnSettings, ok := connSettings["vpn"]; ok { - if svcType, ok := vpnSettings["service-type"].(string); ok { - vpnActive.Plugin = svcType - } - // Get full data map - if data, ok := vpnSettings["data"].(map[string]string); ok { - vpnActive.Data = data - if remote, ok := data["remote"]; ok { - vpnActive.RemoteHost = remote - } - if username, ok := data["username"]; ok { - vpnActive.Username = username - } - } - } - } - } - } - - active = append(active, vpnActive) - } - - b.stateMutex.Lock() - b.state.VPNActive = active - b.stateMutex.Unlock() - - return active, nil -} - -func (b *NetworkManagerBackend) ConnectVPN(uuidOrName string, singleActive bool) error { - if singleActive { - active, err := b.ListActiveVPN() - if err == nil && len(active) > 0 { - alreadyConnected := false - for _, vpn := range active { - if vpn.UUID == uuidOrName || vpn.Name == uuidOrName { - alreadyConnected = true - break - } - } - - if !alreadyConnected { - if err := b.DisconnectAllVPN(); err != nil { - log.Warnf("Failed to disconnect existing VPNs: %v", err) - } - time.Sleep(500 * time.Millisecond) - } else { - return nil - } - } - } - - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - var targetConn gonetworkmanager.Connection - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - if connUUID == uuidOrName || connID == uuidOrName { - targetConn = conn - break - } - } - - if targetConn == nil { - return fmt.Errorf("VPN connection not found: %s", uuidOrName) - } - - targetSettings, err := targetConn.GetSettings() - if err != nil { - return fmt.Errorf("failed to get connection settings: %w", err) - } - - var targetUUID string - var connName string - if connMeta, ok := targetSettings["connection"]; ok { - if uuid, ok := connMeta["uuid"].(string); ok { - targetUUID = uuid - } - if id, ok := connMeta["id"].(string); ok { - connName = id - } - } - - var vpnServiceType string - var vpnData map[string]string - if vpnSettings, ok := targetSettings["vpn"]; ok { - if svc, ok := vpnSettings["service-type"].(string); ok { - vpnServiceType = svc - } - if data, ok := vpnSettings["data"].(map[string]string); ok { - vpnData = data - } - } - - authAction := detectVPNAuthAction(vpnServiceType, vpnData) - - switch authAction { - case "openvpn_username": - if b.promptBroker == nil { - return fmt.Errorf("OpenVPN password authentication requires interactive prompt") - } - if err := b.handleOpenVPNUsernameAuth(targetConn, connName, targetUUID, vpnServiceType); err != nil { - return err - } - case "gp_saml": - gateway := vpnData["gateway"] - protocol := vpnData["protocol"] - if protocol != "gp" { - return fmt.Errorf("GlobalProtect SAML authentication only supported for protocol=gp, got: %s", protocol) - } - - log.Infof("[ConnectVPN] GlobalProtect SAML/SSO authentication required for %s (gateway=%s)", connName, gateway) - - samlCtx, samlCancel := context.WithTimeout(context.Background(), 5*time.Minute) - authResult, err := b.runGlobalProtectSAMLAuth(samlCtx, gateway, protocol) - samlCancel() - if err != nil { - errMsg := err.Error() - switch { - case strings.Contains(errMsg, "not installed"): - return fmt.Errorf("gp-saml-gui is not installed (required for GlobalProtect SAML/SSO VPN)") - case strings.Contains(errMsg, "timed out") || strings.Contains(errMsg, "cancelled"): - return fmt.Errorf("GlobalProtect SAML authentication timed out — please try again") - case strings.Contains(errMsg, "no cookie"): - return fmt.Errorf("GlobalProtect SAML login did not complete — browser was closed before authentication finished") - case strings.Contains(errMsg, "convert prelogin-cookie"): - return fmt.Errorf("GlobalProtect VPN authentication succeeded but cookie exchange failed: %w", err) - default: - return fmt.Errorf("GlobalProtect SAML authentication failed: %w", err) - } - } - - b.cachedGPSamlMu.Lock() - b.cachedGPSamlCookie = &cachedGPSamlCookie{ - ConnectionUUID: targetUUID, - Cookie: authResult.Cookie, - Host: authResult.Host, - User: authResult.User, - Fingerprint: authResult.Fingerprint, - } - b.cachedGPSamlMu.Unlock() - - if err := targetConn.ClearSecrets(); err != nil { - log.Warnf("[ConnectVPN] ClearSecrets failed (non-fatal): %v", err) - } else { - log.Infof("[ConnectVPN] Cleared stale stored secrets for %s", connName) - } - - log.Infof("[ConnectVPN] GlobalProtect SAML cookie cached for %s, proceeding with activation", connName) - } - - b.stateMutex.Lock() - b.state.IsConnectingVPN = true - b.state.ConnectingVPNUUID = targetUUID - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - _, err = nm.ActivateConnection(targetConn, nil, nil) - if err != nil { - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - return fmt.Errorf("failed to activate VPN: %w", err) - } - - return nil -} - -func detectVPNAuthAction(serviceType string, data map[string]string) string { - if data == nil { - return "" - } - - switch { - case strings.Contains(serviceType, "openconnect"): - protocol := data["protocol"] - if needsExternalBrowserAuth(protocol, data["authtype"], data["username"], data) { - switch protocol { - case "gp": - return "gp_saml" - default: - log.Infof("[VPN] External browser auth detected for protocol '%s' but only GlobalProtect (gp) is currently supported", protocol) - } - } - case strings.Contains(serviceType, "openvpn"): - connType := data["connection-type"] - username := data["username"] - if (connType == "password" || connType == "password-tls") && username == "" { - return "openvpn_username" - } - } - return "" -} - -func (b *NetworkManagerBackend) handleOpenVPNUsernameAuth(targetConn gonetworkmanager.Connection, connName, targetUUID, vpnServiceType string) error { - log.Infof("[ConnectVPN] OpenVPN requires username in vpn.data - prompting before activation") - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() - - token, err := b.promptBroker.Ask(ctx, PromptRequest{ - Name: connName, - ConnType: "vpn", - VpnService: vpnServiceType, - SettingName: "vpn", - Fields: []string{"username", "password"}, - FieldsInfo: []FieldInfo{{Name: "username", Label: "Username", IsSecret: false}, {Name: "password", Label: "Password", IsSecret: true}}, - Reason: "required", - ConnectionId: connName, - ConnectionUuid: targetUUID, - ConnectionPath: string(targetConn.GetPath()), - }) - if err != nil { - return fmt.Errorf("failed to request credentials: %w", err) - } - - reply, err := b.promptBroker.Wait(ctx, token) - if err != nil { - return fmt.Errorf("credentials prompt failed: %w", err) - } - - if reply.Cancel { - return fmt.Errorf("user cancelled authentication") - } - - username := reply.Secrets["username"] - password := reply.Secrets["password"] - if username == "" { - return nil - } - - connObj := b.dbusConn.Object("org.freedesktop.NetworkManager", targetConn.GetPath()) - var existingSettings map[string]map[string]dbus.Variant - if err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.GetSettings", 0).Store(&existingSettings); err != nil { - return fmt.Errorf("failed to get settings for username save: %w", err) - } - - settings := make(map[string]map[string]dbus.Variant) - if connSection, ok := existingSettings["connection"]; ok { - settings["connection"] = connSection - } - vpn := existingSettings["vpn"] - var data map[string]string - if dataVariant, ok := vpn["data"]; ok { - if dm, ok := dataVariant.Value().(map[string]string); ok { - data = make(map[string]string) - for k, v := range dm { - data[k] = v - } - } else { - data = make(map[string]string) - } - } else { - data = make(map[string]string) - } - data["username"] = username - - vpn["data"] = dbus.MakeVariant(data) - settings["vpn"] = vpn - - var result map[string]dbus.Variant - if err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.Update2", 0, - settings, uint32(0x1), map[string]dbus.Variant{}).Store(&result); err != nil { - return fmt.Errorf("failed to save username: %w", err) - } - log.Infof("[ConnectVPN] Username saved to connection") - - if password != "" { - b.cachedVPNCredsMu.Lock() - b.cachedVPNCreds = &cachedVPNCredentials{ - ConnectionUUID: targetUUID, - Password: password, - SavePassword: reply.Save, - } - b.cachedVPNCredsMu.Unlock() - log.Infof("[ConnectVPN] Cached password for GetSecrets") - } - - return nil -} - -func (b *NetworkManagerBackend) DisconnectVPN(uuidOrName string) error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return fmt.Errorf("failed to get active connections: %w", err) - } - - log.Debugf("[DisconnectVPN] Looking for VPN: %s", uuidOrName) - - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "vpn" && connType != "wireguard" { - continue - } - - uuid, _ := activeConn.GetPropertyUUID() - id, _ := activeConn.GetPropertyID() - state, _ := activeConn.GetPropertyState() - - log.Debugf("[DisconnectVPN] Found active VPN: uuid=%s id=%s state=%d", uuid, id, state) - - if uuid == uuidOrName || id == uuidOrName { - log.Infof("[DisconnectVPN] Deactivating VPN: %s (state=%d)", id, state) - if err := nm.DeactivateConnection(activeConn); err != nil { - return fmt.Errorf("failed to deactivate VPN: %w", err) - } - b.ListActiveVPN() - if b.onStateChange != nil { - b.onStateChange() - } - return nil - } - } - - log.Warnf("[DisconnectVPN] VPN not found in active connections: %s", uuidOrName) - - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("VPN connection not active and cannot access settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("VPN connection not active: %s", uuidOrName) - } - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - if connUUID == uuidOrName || connID == uuidOrName { - log.Infof("[DisconnectVPN] VPN connection exists but not active: %s", connID) - return nil - } - } - - return fmt.Errorf("VPN connection not found: %s", uuidOrName) -} - -func (b *NetworkManagerBackend) DisconnectAllVPN() error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return fmt.Errorf("failed to get active connections: %w", err) - } - - var lastErr error - var disconnected bool - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "vpn" && connType != "wireguard" { - continue - } - - if err := nm.DeactivateConnection(activeConn); err != nil { - lastErr = err - log.Warnf("Failed to deactivate VPN connection: %v", err) - } else { - disconnected = true - } - } - - if disconnected { - b.ListActiveVPN() - if b.onStateChange != nil { - b.onStateChange() - } - } - - return lastErr -} - -func (b *NetworkManagerBackend) ClearVPNCredentials(uuidOrName string) error { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - if connUUID == uuidOrName || connID == uuidOrName { - if connType == "vpn" { - if vpnSettings, ok := settings["vpn"]; ok { - delete(vpnSettings, "secrets") - - if dataMap, ok := vpnSettings["data"].(map[string]string); ok { - dataMap["password-flags"] = "1" - vpnSettings["data"] = dataMap - } - } - } - - if err := conn.Update(settings); err != nil { - return fmt.Errorf("failed to update connection: %w", err) - } - - if err := conn.ClearSecrets(); err != nil { - log.Warnf("ClearSecrets call failed (may not be critical): %v", err) - } - - log.Infof("Cleared credentials for VPN: %s", connID) - return nil - } - } - - return fmt.Errorf("VPN connection not found: %s", uuidOrName) -} - -func (b *NetworkManagerBackend) updateVPNConnectionState() { - b.stateMutex.RLock() - isConnectingVPN := b.state.IsConnectingVPN - connectingVPNUUID := b.state.ConnectingVPNUUID - b.stateMutex.RUnlock() - - if !isConnectingVPN || connectingVPNUUID == "" { - return - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - activeConns, err := nm.GetPropertyActiveConnections() - if err != nil { - return - } - - foundConnection := false - for _, activeConn := range activeConns { - connType, err := activeConn.GetPropertyType() - if err != nil { - continue - } - - if connType != "vpn" && connType != "wireguard" { - continue - } - - connUUID, err := activeConn.GetPropertyUUID() - if err != nil { - continue - } - - state, _ := activeConn.GetPropertyState() - stateReason, _ := activeConn.GetPropertyStateFlags() - - if connUUID == connectingVPNUUID { - foundConnection = true - - switch state { - case 2: - log.Infof("[updateVPNConnectionState] VPN connection successful: %s", connUUID) - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.state.LastError = "" - b.stateMutex.Unlock() - - // Clear cached PKCS11 PIN and SAML cookie on success - b.cachedPKCS11Mu.Lock() - b.cachedPKCS11PIN = nil - b.cachedPKCS11Mu.Unlock() - b.cachedGPSamlMu.Lock() - b.cachedGPSamlCookie = nil - b.cachedGPSamlMu.Unlock() - - b.pendingVPNSaveMu.Lock() - pending := b.pendingVPNSave - b.pendingVPNSave = nil - b.pendingVPNSaveMu.Unlock() - - if pending != nil { - go b.saveVPNCredentials(pending) - } - return - case 4: - log.Warnf("[updateVPNConnectionState] VPN connection failed/deactivated: %s (state=%d, flags=%d)", connUUID, state, stateReason) - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.state.LastError = "VPN connection failed" - b.stateMutex.Unlock() - - // Clear cached PKCS11 PIN and SAML cookie on failure - b.cachedPKCS11Mu.Lock() - b.cachedPKCS11PIN = nil - b.cachedPKCS11Mu.Unlock() - b.cachedGPSamlMu.Lock() - b.cachedGPSamlCookie = nil - b.cachedGPSamlMu.Unlock() - return - } - } - } - - if !foundConnection { - log.Warnf("[updateVPNConnectionState] VPN connection no longer exists: %s", connectingVPNUUID) - b.stateMutex.Lock() - b.state.IsConnectingVPN = false - b.state.ConnectingVPNUUID = "" - b.state.LastError = "VPN connection failed" - b.stateMutex.Unlock() - - // Clear cached PKCS11 PIN and SAML cookie - b.cachedPKCS11Mu.Lock() - b.cachedPKCS11PIN = nil - b.cachedPKCS11Mu.Unlock() - b.cachedGPSamlMu.Lock() - b.cachedGPSamlCookie = nil - b.cachedGPSamlMu.Unlock() - } -} - -func (b *NetworkManagerBackend) saveVPNCredentials(creds *pendingVPNCredentials) { - log.Infof("[saveVPNCredentials] Saving credentials for %s (username=%v, savePassword=%v)", - creds.ConnectionPath, creds.Username != "", creds.SavePassword) - - connObj := b.dbusConn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath(creds.ConnectionPath)) - var existingSettings map[string]map[string]dbus.Variant - if err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.GetSettings", 0).Store(&existingSettings); err != nil { - log.Warnf("[saveVPNCredentials] GetSettings failed: %v", err) - return - } - - settings := make(map[string]map[string]dbus.Variant) - if connSection, ok := existingSettings["connection"]; ok { - settings["connection"] = connSection - } - - vpn, ok := existingSettings["vpn"] - if !ok { - vpn = make(map[string]dbus.Variant) - } - - // Get existing data map - var data map[string]string - if dataVariant, ok := vpn["data"]; ok { - if dm, ok := dataVariant.Value().(map[string]string); ok { - data = make(map[string]string) - for k, v := range dm { - data[k] = v - } - } else { - data = make(map[string]string) - } - } else { - data = make(map[string]string) - } - - // Always save username if provided - if creds.Username != "" { - data["username"] = creds.Username - log.Infof("[saveVPNCredentials] Saving username") - } - - // Save password if requested - if creds.SavePassword { - data["password-flags"] = "0" - secs := make(map[string]string) - secs["password"] = creds.Password - vpn["secrets"] = dbus.MakeVariant(secs) - log.Infof("[saveVPNCredentials] Saving password with password-flags=0") - } - - vpn["data"] = dbus.MakeVariant(data) - settings["vpn"] = vpn - - var result map[string]dbus.Variant - if err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.Update2", 0, - settings, uint32(0x1), map[string]dbus.Variant{}).Store(&result); err != nil { - log.Warnf("[saveVPNCredentials] Update2 failed: %v", err) - } else { - log.Infof("[saveVPNCredentials] Successfully saved credentials") - } -} - -func (b *NetworkManagerBackend) ListVPNPlugins() ([]VPNPlugin, error) { - plugins := []VPNPlugin{} - pluginDirs := []string{ - "/usr/lib/NetworkManager/VPN", - "/usr/lib64/NetworkManager/VPN", - "/etc/NetworkManager/VPN", - } - - seen := make(map[string]bool) - - for _, dir := range pluginDirs { - entries, err := os.ReadDir(dir) - if err != nil { - continue - } - - for _, entry := range entries { - if !strings.HasSuffix(entry.Name(), ".name") { - continue - } - - filePath := filepath.Join(dir, entry.Name()) - plugin, err := parseVPNPluginFile(filePath) - if err != nil { - log.Debugf("Failed to parse VPN plugin file %s: %v", filePath, err) - continue - } - - if seen[plugin.ServiceType] { - continue - } - seen[plugin.ServiceType] = true - plugins = append(plugins, *plugin) - } - } - - sort.Slice(plugins, func(i, j int) bool { - return strings.ToLower(plugins[i].Name) < strings.ToLower(plugins[j].Name) - }) - - return plugins, nil -} - -func parseVPNPluginFile(path string) (*VPNPlugin, error) { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - plugin := &VPNPlugin{} - scanner := bufio.NewScanner(file) - - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "[") { - continue - } - - parts := strings.SplitN(line, "=", 2) - if len(parts) != 2 { - continue - } - - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - - switch key { - case "name": - plugin.Name = value - case "service": - plugin.ServiceType = value - case "program": - plugin.Program = value - case "supports": - plugin.Supports = strings.Split(value, ",") - for i := range plugin.Supports { - plugin.Supports[i] = strings.TrimSpace(plugin.Supports[i]) - } - } - } - - if plugin.ServiceType == "" { - return nil, fmt.Errorf("plugin file missing service type") - } - - plugin.FileExtensions = getVPNFileExtensions(plugin.ServiceType) - - return plugin, nil -} - -func getVPNFileExtensions(serviceType string) []string { - switch { - case strings.Contains(serviceType, "openvpn"): - return []string{".ovpn", ".conf"} - case strings.Contains(serviceType, "wireguard"): - return []string{".conf"} - case strings.Contains(serviceType, "vpnc"), strings.Contains(serviceType, "cisco"): - return []string{".pcf", ".conf"} - case strings.Contains(serviceType, "openconnect"): - return []string{".conf"} - case strings.Contains(serviceType, "pptp"): - return []string{".conf"} - case strings.Contains(serviceType, "l2tp"): - return []string{".conf"} - case strings.Contains(serviceType, "strongswan"), strings.Contains(serviceType, "ipsec"): - return []string{".conf", ".sswan"} - default: - return []string{".conf"} - } -} - -func (b *NetworkManagerBackend) ImportVPN(filePath string, name string) (*VPNImportResult, error) { - if _, err := os.Stat(filePath); os.IsNotExist(err) { - return &VPNImportResult{ - Success: false, - Error: fmt.Sprintf("file not found: %s", filePath), - }, nil - } - - ext := strings.ToLower(filepath.Ext(filePath)) - - switch ext { - case ".ovpn", ".conf": - return b.importVPNWithNmcli(filePath, name) - default: - return b.importVPNWithNmcli(filePath, name) - } -} - -func (b *NetworkManagerBackend) importVPNWithNmcli(filePath string, name string) (*VPNImportResult, error) { - vpnTypes := []string{"openvpn", "wireguard", "vpnc", "pptp", "l2tp", "openconnect", "strongswan"} - - var allErrors []error - var outputStr string - for _, vpnType := range vpnTypes { - cmd := exec.Command("nmcli", "connection", "import", "type", vpnType, "file", filePath) - output, err := cmd.CombinedOutput() - if err == nil { - outputStr = string(output) - break - } - allErrors = append(allErrors, fmt.Errorf("%s: %s", vpnType, strings.TrimSpace(string(output)))) - } - - if len(allErrors) == len(vpnTypes) { - return &VPNImportResult{ - Success: false, - Error: errors.Join(allErrors...).Error(), - }, nil - } - var connUUID, connName string - - lines := strings.Split(outputStr, "\n") - for _, line := range lines { - if strings.Contains(line, "successfully added") { - parts := strings.Fields(line) - for i, part := range parts { - if part == "(" && i+1 < len(parts) { - connUUID = strings.TrimSuffix(parts[i+1], ")") - break - } - } - } - } - - if name != "" && connUUID != "" { - renameCmd := exec.Command("nmcli", "connection", "modify", connUUID, "connection.id", name) - if err := renameCmd.Run(); err != nil { - log.Warnf("Failed to rename imported VPN: %v", err) - } else { - connName = name - } - } - - if connUUID == "" { - s := b.settings - if s == nil { - var settingsErr error - s, settingsErr = gonetworkmanager.NewSettings() - if settingsErr == nil { - b.settings = s - } - } - - if s != nil { - settingsMgr := s.(gonetworkmanager.Settings) - connections, _ := settingsMgr.ListConnections() - baseName := strings.TrimSuffix(filepath.Base(filePath), filepath.Ext(filePath)) - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - connMeta, ok := settings["connection"] - if !ok { - continue - } - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - connID, _ := connMeta["id"].(string) - if strings.Contains(connID, baseName) || (name != "" && connID == name) { - connUUID, _ = connMeta["uuid"].(string) - connName = connID - break - } - } - } - } - - b.ListVPNProfiles() - - if b.onStateChange != nil { - b.onStateChange() - } - - return &VPNImportResult{ - Success: true, - UUID: connUUID, - Name: connName, - }, nil -} - -func (b *NetworkManagerBackend) GetVPNConfig(uuidOrName string) (*VPNConfig, error) { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - if connUUID != uuidOrName && connID != uuidOrName { - continue - } - - autoconnect := true - if ac, ok := connMeta["autoconnect"].(bool); ok { - autoconnect = ac - } - - config := &VPNConfig{ - UUID: connUUID, - Name: connID, - Type: connType, - Autoconnect: autoconnect, - Data: make(map[string]string), - } - - if connType == "vpn" { - if vpnSettings, ok := settings["vpn"]; ok { - if svcType, ok := vpnSettings["service-type"].(string); ok { - config.ServiceType = svcType - } - if dataMap, ok := vpnSettings["data"].(map[string]string); ok { - for k, v := range dataMap { - if !strings.Contains(strings.ToLower(k), "password") && - !strings.Contains(strings.ToLower(k), "secret") && - !strings.Contains(strings.ToLower(k), "key") { - config.Data[k] = v - } - } - } - } - } - - return config, nil - } - - return nil, fmt.Errorf("VPN connection not found: %s", uuidOrName) -} - -func (b *NetworkManagerBackend) UpdateVPNConfig(connUUID string, updates map[string]any) error { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - existingUUID, _ := connMeta["uuid"].(string) - if existingUUID != connUUID { - continue - } - - if name, ok := updates["name"].(string); ok && name != "" { - connMeta["id"] = name - } - - if autoconnect, ok := updates["autoconnect"].(bool); ok { - connMeta["autoconnect"] = autoconnect - } - - if data, ok := updates["data"].(map[string]any); ok { - if vpnSettings, ok := settings["vpn"]; ok { - existingData, _ := vpnSettings["data"].(map[string]string) - if existingData == nil { - existingData = make(map[string]string) - } - for k, v := range data { - if strVal, ok := v.(string); ok { - existingData[k] = strVal - } - } - vpnSettings["data"] = existingData - } - } - - if ipv4, ok := settings["ipv4"]; ok { - delete(ipv4, "addresses") - delete(ipv4, "routes") - delete(ipv4, "dns") - } - if ipv6, ok := settings["ipv6"]; ok { - delete(ipv6, "addresses") - delete(ipv6, "routes") - delete(ipv6, "dns") - } - - if err := conn.Update(settings); err != nil { - return fmt.Errorf("failed to update connection: %w", err) - } - - b.ListVPNProfiles() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil - } - - return fmt.Errorf("VPN connection not found: %s", connUUID) -} - -func (b *NetworkManagerBackend) SetVPNCredentials(connUUID string, username string, password string, saveToKeyring bool) error { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - existingUUID, _ := connMeta["uuid"].(string) - if existingUUID != connUUID { - continue - } - - vpnSettings, ok := settings["vpn"] - if !ok { - vpnSettings = make(map[string]any) - settings["vpn"] = vpnSettings - } - - existingData, _ := vpnSettings["data"].(map[string]string) - if existingData == nil { - existingData = make(map[string]string) - } - - if username != "" { - existingData["username"] = username - } - - if saveToKeyring { - existingData["password-flags"] = "0" - } else { - existingData["password-flags"] = "2" - } - - vpnSettings["data"] = existingData - - if password != "" { - secrets := make(map[string]string) - secrets["password"] = password - vpnSettings["secrets"] = secrets - } - - if ipv4, ok := settings["ipv4"]; ok { - delete(ipv4, "addresses") - delete(ipv4, "routes") - delete(ipv4, "dns") - } - if ipv6, ok := settings["ipv6"]; ok { - delete(ipv6, "addresses") - delete(ipv6, "routes") - delete(ipv6, "dns") - } - - if err := conn.Update(settings); err != nil { - return fmt.Errorf("failed to update connection: %w", err) - } - - log.Infof("Updated VPN credentials for %s (save=%v)", connUUID, saveToKeyring) - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil - } - - return fmt.Errorf("VPN connection not found: %s", connUUID) -} - -func (b *NetworkManagerBackend) DeleteVPN(uuidOrName string) error { - active, _ := b.ListActiveVPN() - for _, vpn := range active { - if vpn.UUID == uuidOrName || vpn.Name == uuidOrName { - if err := b.DisconnectVPN(uuidOrName); err != nil { - log.Warnf("Failed to disconnect VPN before deletion: %v", err) - } - time.Sleep(200 * time.Millisecond) - break - } - } - - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return fmt.Errorf("failed to get connections: %w", err) - } - - for _, conn := range connections { - settings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := settings["connection"] - if !ok { - continue - } - - connType, _ := connMeta["type"].(string) - if connType != "vpn" && connType != "wireguard" { - continue - } - - connID, _ := connMeta["id"].(string) - connUUID, _ := connMeta["uuid"].(string) - - if connUUID == uuidOrName || connID == uuidOrName { - if err := conn.Delete(); err != nil { - return fmt.Errorf("failed to delete VPN: %w", err) - } - - b.ListVPNProfiles() - - if b.onStateChange != nil { - b.onStateChange() - } - - log.Infof("Deleted VPN connection: %s (%s)", connID, connUUID) - return nil - } - } - - return fmt.Errorf("VPN connection not found: %s", uuidOrName) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_vpn_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_vpn_test.go deleted file mode 100644 index eebd433..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_vpn_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package network - -import ( - "testing" - - mock_gonetworkmanager "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/Wifx/gonetworkmanager/v2" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_ListVPNProfiles(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockSettings := mock_gonetworkmanager.NewMockSettings(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - backend.settings = mockSettings - - mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{}, nil) - - profiles, err := backend.ListVPNProfiles() - assert.NoError(t, err) - assert.Empty(t, profiles) -} - -func TestNetworkManagerBackend_ListActiveVPN(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - - active, err := backend.ListActiveVPN() - assert.NoError(t, err) - assert.Empty(t, active) -} - -func TestNetworkManagerBackend_ConnectVPN_NotFound(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockSettings := mock_gonetworkmanager.NewMockSettings(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - backend.settings = mockSettings - - mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{}, nil) - - err = backend.ConnectVPN("non-existent-vpn-12345", false) - assert.Error(t, err) - assert.Contains(t, err.Error(), "not found") -} - -func TestNetworkManagerBackend_ConnectVPN_SingleActive_NoActiveVPN(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockSettings := mock_gonetworkmanager.NewMockSettings(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - backend.settings = mockSettings - - mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{}, nil) - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - - err = backend.ConnectVPN("non-existent-vpn-12345", true) - assert.Error(t, err) -} - -func TestNetworkManagerBackend_DisconnectVPN_NotActive(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - - err = backend.DisconnectVPN("non-existent-vpn-12345") - assert.Error(t, err) -} - -func TestNetworkManagerBackend_DisconnectAllVPN(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil) - - err = backend.DisconnectAllVPN() - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_ClearVPNCredentials_NotFound(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockSettings := mock_gonetworkmanager.NewMockSettings(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - backend.settings = mockSettings - - mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{}, nil) - - err = backend.ClearVPNCredentials("non-existent-vpn-12345") - assert.Error(t, err) - assert.Contains(t, err.Error(), "not found") -} - -func TestNetworkManagerBackend_UpdateVPNConnectionState_NotConnecting(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.stateMutex.Lock() - backend.state.IsConnectingVPN = false - backend.state.ConnectingVPNUUID = "" - backend.stateMutex.Unlock() - - assert.NotPanics(t, func() { - backend.updateVPNConnectionState() - }) -} - -func TestNetworkManagerBackend_UpdateVPNConnectionState_EmptyUUID(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.stateMutex.Lock() - backend.state.IsConnectingVPN = true - backend.state.ConnectingVPNUUID = "" - backend.stateMutex.Unlock() - - assert.NotPanics(t, func() { - backend.updateVPNConnectionState() - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_wifi.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_wifi.go deleted file mode 100644 index 6f593b1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_wifi.go +++ /dev/null @@ -1,1173 +0,0 @@ -package network - -import ( - "bytes" - "fmt" - "sort" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/Wifx/gonetworkmanager/v2" -) - -func (b *NetworkManagerBackend) GetWiFiEnabled() (bool, error) { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - return nm.GetPropertyWirelessEnabled() -} - -func (b *NetworkManagerBackend) SetWiFiEnabled(enabled bool) error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - err := nm.SetPropertyWirelessEnabled(enabled) - if err != nil { - return fmt.Errorf("failed to set WiFi enabled: %w", err) - } - - b.stateMutex.Lock() - b.state.WiFiEnabled = enabled - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) ScanWiFi() error { - if b.wifiDevice == nil { - return fmt.Errorf("no WiFi device available") - } - - b.stateMutex.RLock() - enabled := b.state.WiFiEnabled - b.stateMutex.RUnlock() - - if !enabled { - return fmt.Errorf("WiFi is disabled") - } - - if err := b.ensureWiFiDevice(); err != nil { - return err - } - - w := b.wifiDev.(gonetworkmanager.DeviceWireless) - err := w.RequestScan() - if err != nil { - return fmt.Errorf("scan request failed: %w", err) - } - - _, err = b.updateWiFiNetworks() - return err -} - -func (b *NetworkManagerBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) { - if b.wifiDevice == nil { - return nil, fmt.Errorf("no WiFi device available") - } - - if err := b.ensureWiFiDevice(); err != nil { - return nil, err - } - wifiDev := b.wifiDev - - w := wifiDev.(gonetworkmanager.DeviceWireless) - apPaths, err := w.GetAccessPoints() - if err != nil { - return nil, fmt.Errorf("failed to get access points: %w", err) - } - - s := b.settings - if s == nil { - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - savedSSIDs := make(map[string]bool) - autoconnectMap := make(map[string]bool) - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - if connMeta, ok := connSettings["connection"]; ok { - if connType, ok := connMeta["type"].(string); ok && connType == "802-11-wireless" { - if wifiSettings, ok := connSettings["802-11-wireless"]; ok { - if ssidBytes, ok := wifiSettings["ssid"].([]byte); ok { - savedSSID := string(ssidBytes) - savedSSIDs[savedSSID] = true - autoconnect := true - if ac, ok := connMeta["autoconnect"].(bool); ok { - autoconnect = ac - } - autoconnectMap[savedSSID] = autoconnect - } - } - } - } - } - - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - currentBSSID := b.state.WiFiBSSID - b.stateMutex.RUnlock() - - var bands []WiFiNetwork - - for _, ap := range apPaths { - apSSID, err := ap.GetPropertySSID() - if err != nil || apSSID != ssid { - continue - } - - strength, _ := ap.GetPropertyStrength() - flags, _ := ap.GetPropertyFlags() - wpaFlags, _ := ap.GetPropertyWPAFlags() - rsnFlags, _ := ap.GetPropertyRSNFlags() - freq, _ := ap.GetPropertyFrequency() - maxBitrate, _ := ap.GetPropertyMaxBitrate() - bssid, _ := ap.GetPropertyHWAddress() - mode, _ := ap.GetPropertyMode() - - secured := flags != uint32(gonetworkmanager.Nm80211APFlagsNone) || - wpaFlags != uint32(gonetworkmanager.Nm80211APSecNone) || - rsnFlags != uint32(gonetworkmanager.Nm80211APSecNone) - - enterprise := (rsnFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) || - (wpaFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) - - var modeStr string - switch mode { - case gonetworkmanager.Nm80211ModeAdhoc: - modeStr = "adhoc" - case gonetworkmanager.Nm80211ModeInfra: - modeStr = "infrastructure" - case gonetworkmanager.Nm80211ModeAp: - modeStr = "ap" - default: - modeStr = "unknown" - } - - channel := frequencyToChannel(freq) - - isConnected := ssid == currentSSID && bssid == currentBSSID - rate := maxBitrate / 1000 - if isConnected { - if devBitrate, err := w.GetPropertyBitrate(); err == nil && devBitrate > 0 { - rate = devBitrate / 1000 - } - } - - network := WiFiNetwork{ - SSID: ssid, - BSSID: bssid, - Signal: strength, - Secured: secured, - Enterprise: enterprise, - Connected: isConnected, - Saved: savedSSIDs[ssid], - Autoconnect: autoconnectMap[ssid], - Frequency: freq, - Mode: modeStr, - Rate: rate, - Channel: channel, - } - - bands = append(bands, network) - } - - if len(bands) == 0 { - return nil, fmt.Errorf("network not found: %s", ssid) - } - - sort.Slice(bands, func(i, j int) bool { - if bands[i].Connected && !bands[j].Connected { - return true - } - if !bands[i].Connected && bands[j].Connected { - return false - } - return bands[i].Signal > bands[j].Signal - }) - - return &NetworkInfoResponse{ - SSID: ssid, - Bands: bands, - }, nil -} - -func (b *NetworkManagerBackend) GetWiFiQRCodeContent(ssid string) (string, error) { - conn, err := b.findConnection(ssid) - if err != nil { - return "", fmt.Errorf("no saved connection for `%s`: %w", ssid, err) - } - - connSettings, err := conn.GetSettings() - if err != nil { - return "", fmt.Errorf("failed to get settings for `%s`: %w", ssid, err) - } - - secSettings, ok := connSettings["802-11-wireless-security"] - if !ok { - return "", fmt.Errorf("network `%s` has no security settings", ssid) - } - - keyMgmt, ok := secSettings["key-mgmt"].(string) - if !ok { - return "", fmt.Errorf("failed to identify security type of network `%s`", ssid) - } - - var securityType string - switch keyMgmt { - case "none": - authAlg, _ := secSettings["auth-alg"].(string) - switch authAlg { - case "open": - securityType = "nopass" - default: - securityType = "WEP" - } - case "ieee8021x": - securityType = "WEP" - default: - securityType = "WPA" - } - - if securityType != "WPA" { - return "", fmt.Errorf("QR code generation only supports WPA connections, `%s` uses %s", ssid, securityType) - } - - secrets, err := conn.GetSecrets("802-11-wireless-security") - if err != nil { - return "", fmt.Errorf("failed to retrieve connection secrets for `%s`: %w", ssid, err) - } - - secSecrets, ok := secrets["802-11-wireless-security"] - if !ok { - return "", fmt.Errorf("failed to retrieve password for `%s`", ssid) - } - - psk, ok := secSecrets["psk"].(string) - if !ok { - return "", fmt.Errorf("failed to retrieve password for `%s`", ssid) - } - - return FormatWiFiQRString(securityType, ssid, psk), nil -} - -func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error { - devInfo, err := b.getWifiDeviceForConnection(req.Device) - if err != nil { - return err - } - - b.stateMutex.RLock() - alreadyConnected := b.state.WiFiConnected && b.state.WiFiSSID == req.SSID - b.stateMutex.RUnlock() - - if alreadyConnected && !req.Interactive && req.Device == "" { - return nil - } - - b.stateMutex.Lock() - b.state.IsConnecting = true - b.state.ConnectingSSID = req.SSID - b.state.ConnectingDevice = req.Device - b.state.LastError = "" - b.stateMutex.Unlock() - - if b.onStateChange != nil { - b.onStateChange() - } - - nm := b.nmConn.(gonetworkmanager.NetworkManager) - - existingConn, err := b.findConnection(req.SSID) - if err == nil && existingConn != nil { - _, err := nm.ActivateConnection(existingConn, devInfo.device, nil) - if err != nil { - log.Warnf("[ConnectWiFi] Failed to activate existing connection: %v", err) - b.stateMutex.Lock() - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.ConnectingDevice = "" - b.state.LastError = fmt.Sprintf("failed to activate connection: %v", err) - b.stateMutex.Unlock() - if b.onStateChange != nil { - b.onStateChange() - } - return fmt.Errorf("failed to activate connection: %w", err) - } - - return nil - } - - if err := b.createAndConnectWiFiOnDevice(req, devInfo); err != nil { - log.Warnf("[ConnectWiFi] Failed to create and connect: %v", err) - b.stateMutex.Lock() - b.state.IsConnecting = false - b.state.ConnectingSSID = "" - b.state.ConnectingDevice = "" - b.state.LastError = err.Error() - b.stateMutex.Unlock() - if b.onStateChange != nil { - b.onStateChange() - } - return err - } - - return nil -} - -func (b *NetworkManagerBackend) DisconnectWiFi() error { - if b.wifiDevice == nil { - return fmt.Errorf("no WiFi device available") - } - - dev := b.wifiDevice.(gonetworkmanager.Device) - - err := dev.Disconnect() - if err != nil { - return fmt.Errorf("failed to disconnect: %w", err) - } - - b.updateWiFiState() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) ForgetWiFiNetwork(ssid string) error { - conn, err := b.findConnection(ssid) - if err != nil { - return fmt.Errorf("connection not found: %w", err) - } - - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - isConnected := b.state.WiFiConnected - b.stateMutex.RUnlock() - - err = conn.Delete() - if err != nil { - return fmt.Errorf("failed to delete connection: %w", err) - } - - if isConnected && currentSSID == ssid { - b.stateMutex.Lock() - b.state.WiFiConnected = false - b.state.WiFiSSID = "" - b.state.WiFiBSSID = "" - b.state.WiFiSignal = 0 - b.state.WiFiIP = "" - b.state.NetworkStatus = StatusDisconnected - b.stateMutex.Unlock() - } - - b.updateWiFiNetworks() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) IsConnectingTo(ssid string) bool { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - return b.state.IsConnecting && b.state.ConnectingSSID == ssid -} - -func (b *NetworkManagerBackend) updateWiFiNetworks() ([]WiFiNetwork, error) { - if b.wifiDevice == nil { - return nil, fmt.Errorf("no WiFi device available") - } - - if err := b.ensureWiFiDevice(); err != nil { - return nil, err - } - wifiDev := b.wifiDev - - w := wifiDev.(gonetworkmanager.DeviceWireless) - apPaths, err := w.GetAccessPoints() - if err != nil { - return nil, fmt.Errorf("failed to get access points: %w", err) - } - - s := b.settings - if s == nil { - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return nil, fmt.Errorf("failed to get connections: %w", err) - } - - savedSSIDs := make(map[string]bool) - autoconnectMap := make(map[string]bool) - hiddenSSIDs := make(map[string]bool) - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := connSettings["connection"] - if !ok { - continue - } - - connType, ok := connMeta["type"].(string) - if !ok || connType != "802-11-wireless" { - continue - } - - wifiSettings, ok := connSettings["802-11-wireless"] - if !ok { - continue - } - - ssidBytes, ok := wifiSettings["ssid"].([]byte) - if !ok { - continue - } - - ssid := string(ssidBytes) - savedSSIDs[ssid] = true - autoconnect := true - if ac, ok := connMeta["autoconnect"].(bool); ok { - autoconnect = ac - } - autoconnectMap[ssid] = autoconnect - - if hidden, ok := wifiSettings["hidden"].(bool); ok && hidden { - hiddenSSIDs[ssid] = true - } - } - - b.stateMutex.RLock() - currentSSID := b.state.WiFiSSID - wifiConnected := b.state.WiFiConnected - wifiSignal := b.state.WiFiSignal - wifiBSSID := b.state.WiFiBSSID - b.stateMutex.RUnlock() - - seenSSIDs := make(map[string]*WiFiNetwork) - networks := []WiFiNetwork{} - - for _, ap := range apPaths { - ssid, err := ap.GetPropertySSID() - if err != nil || ssid == "" { - continue - } - - if existing, exists := seenSSIDs[ssid]; exists { - strength, _ := ap.GetPropertyStrength() - if strength > existing.Signal { - existing.Signal = strength - freq, _ := ap.GetPropertyFrequency() - existing.Frequency = freq - bssid, _ := ap.GetPropertyHWAddress() - existing.BSSID = bssid - } - continue - } - - strength, _ := ap.GetPropertyStrength() - flags, _ := ap.GetPropertyFlags() - wpaFlags, _ := ap.GetPropertyWPAFlags() - rsnFlags, _ := ap.GetPropertyRSNFlags() - freq, _ := ap.GetPropertyFrequency() - maxBitrate, _ := ap.GetPropertyMaxBitrate() - bssid, _ := ap.GetPropertyHWAddress() - mode, _ := ap.GetPropertyMode() - - secured := flags != uint32(gonetworkmanager.Nm80211APFlagsNone) || - wpaFlags != uint32(gonetworkmanager.Nm80211APSecNone) || - rsnFlags != uint32(gonetworkmanager.Nm80211APSecNone) - - enterprise := (rsnFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) || - (wpaFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) - - var modeStr string - switch mode { - case gonetworkmanager.Nm80211ModeAdhoc: - modeStr = "adhoc" - case gonetworkmanager.Nm80211ModeInfra: - modeStr = "infrastructure" - case gonetworkmanager.Nm80211ModeAp: - modeStr = "ap" - default: - modeStr = "unknown" - } - - channel := frequencyToChannel(freq) - - isConnected := ssid == currentSSID - rate := maxBitrate / 1000 - if isConnected { - if devBitrate, err := w.GetPropertyBitrate(); err == nil && devBitrate > 0 { - rate = devBitrate / 1000 - } - } - - network := WiFiNetwork{ - SSID: ssid, - BSSID: bssid, - Signal: strength, - Secured: secured, - Enterprise: enterprise, - Connected: isConnected, - Saved: savedSSIDs[ssid], - Autoconnect: autoconnectMap[ssid], - Hidden: hiddenSSIDs[ssid], - Frequency: freq, - Mode: modeStr, - Rate: rate, - Channel: channel, - } - - seenSSIDs[ssid] = &network - networks = append(networks, network) - } - - if wifiConnected && currentSSID != "" { - if _, exists := seenSSIDs[currentSSID]; !exists { - hiddenNetwork := WiFiNetwork{ - SSID: currentSSID, - BSSID: wifiBSSID, - Signal: wifiSignal, - Secured: true, - Connected: true, - Saved: savedSSIDs[currentSSID], - Autoconnect: autoconnectMap[currentSSID], - Hidden: true, - Mode: "infrastructure", - } - networks = append(networks, hiddenNetwork) - } - } - - sortWiFiNetworks(networks) - - b.stateMutex.Lock() - b.state.WiFiNetworks = networks - b.stateMutex.Unlock() - - return networks, nil -} - -func (b *NetworkManagerBackend) findConnection(ssid string) (gonetworkmanager.Connection, error) { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return nil, err - } - b.settings = s - } - - settings := s.(gonetworkmanager.Settings) - connections, err := settings.ListConnections() - if err != nil { - return nil, err - } - - ssidBytes := []byte(ssid) - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - if connMeta, ok := connSettings["connection"]; ok { - if connType, ok := connMeta["type"].(string); ok && connType == "802-11-wireless" { - if wifiSettings, ok := connSettings["802-11-wireless"]; ok { - if candidateSSID, ok := wifiSettings["ssid"].([]byte); ok { - if bytes.Equal(candidateSSID, ssidBytes) { - return conn, nil - } - } - } - } - } - } - - return nil, fmt.Errorf("connection not found") -} - -func (b *NetworkManagerBackend) createAndConnectWiFi(req ConnectionRequest) error { - devInfo, err := b.getWifiDeviceForConnection(req.Device) - if err != nil { - return err - } - return b.createAndConnectWiFiOnDevice(req, devInfo) -} - -func (b *NetworkManagerBackend) createAndConnectWiFiOnDevice(req ConnectionRequest, devInfo *wifiDeviceInfo) error { - nm := b.nmConn.(gonetworkmanager.NetworkManager) - dev := devInfo.device - w := devInfo.wireless - - var targetAP gonetworkmanager.AccessPoint - var flags, wpaFlags, rsnFlags uint32 - - if !req.Hidden { - apPaths, err := w.GetAccessPoints() - if err != nil { - return fmt.Errorf("failed to get access points: %w", err) - } - - for _, ap := range apPaths { - ssid, err := ap.GetPropertySSID() - if err != nil || ssid != req.SSID { - continue - } - targetAP = ap - break - } - - if targetAP == nil { - return fmt.Errorf("access point not found: %s", req.SSID) - } - - flags, _ = targetAP.GetPropertyFlags() - wpaFlags, _ = targetAP.GetPropertyWPAFlags() - rsnFlags, _ = targetAP.GetPropertyRSNFlags() - } - - const KeyMgmt8021x = uint32(512) - const KeyMgmtPsk = uint32(256) - const KeyMgmtSae = uint32(1024) - - var isEnterprise, isPsk, isSae, secured bool - - switch { - case req.Hidden: - secured = req.Password != "" || req.Username != "" - isEnterprise = req.Username != "" - isPsk = req.Password != "" && !isEnterprise - default: - isEnterprise = (wpaFlags&KeyMgmt8021x) != 0 || (rsnFlags&KeyMgmt8021x) != 0 - isPsk = (wpaFlags&KeyMgmtPsk) != 0 || (rsnFlags&KeyMgmtPsk) != 0 - isSae = (wpaFlags&KeyMgmtSae) != 0 || (rsnFlags&KeyMgmtSae) != 0 - secured = flags != uint32(gonetworkmanager.Nm80211APFlagsNone) || - wpaFlags != uint32(gonetworkmanager.Nm80211APSecNone) || - rsnFlags != uint32(gonetworkmanager.Nm80211APSecNone) - } - - if isEnterprise { - log.Infof("[createAndConnectWiFi] Enterprise network detected (802.1x) - SSID: %s, interactive: %v", - req.SSID, req.Interactive) - } - - settings := make(map[string]map[string]any) - - settings["connection"] = map[string]any{ - "id": req.SSID, - "type": "802-11-wireless", - "autoconnect": true, - } - - settings["ipv4"] = map[string]any{"method": "auto"} - settings["ipv6"] = map[string]any{"method": "auto"} - - if secured { - wifiSettings := map[string]any{ - "ssid": []byte(req.SSID), - "mode": "infrastructure", - "security": "802-11-wireless-security", - } - if req.Hidden { - wifiSettings["hidden"] = true - } - settings["802-11-wireless"] = wifiSettings - - switch { - case isEnterprise || req.Username != "": - settings["802-11-wireless-security"] = map[string]any{ - "key-mgmt": "wpa-eap", - } - - eapMethod := "peap" - if req.EAPMethod != "" { - eapMethod = req.EAPMethod - } - - phase2Auth := "mschapv2" - if req.Phase2Auth != "" { - phase2Auth = req.Phase2Auth - } - - useSystemCACerts := false - if req.UseSystemCACerts != nil { - useSystemCACerts = *req.UseSystemCACerts - } - - x := map[string]any{ - "eap": []string{eapMethod}, - "system-ca-certs": useSystemCACerts, - "password-flags": uint32(0), - } - - switch eapMethod { - case "peap", "ttls": - x["phase2-auth"] = phase2Auth - case "tls": - if req.ClientCertPath != "" { - x["client-cert"] = []byte("file://" + req.ClientCertPath) - } - if req.PrivateKeyPath != "" { - x["private-key"] = []byte("file://" + req.PrivateKeyPath) - } - } - - if req.Username != "" { - x["identity"] = req.Username - } - if req.Password != "" { - x["password"] = req.Password - } - if req.AnonymousIdentity != "" { - x["anonymous-identity"] = req.AnonymousIdentity - } - if req.DomainSuffixMatch != "" { - x["domain-suffix-match"] = req.DomainSuffixMatch - } - if req.CACertPath != "" { - x["ca-cert"] = []byte("file://" + req.CACertPath) - } - - settings["802-1x"] = x - - log.Infof("[createAndConnectWiFi] WPA-EAP settings: eap=%s, phase2-auth=%s, identity=%s, interactive=%v, system-ca-certs=%v, domain-suffix-match=%q", - eapMethod, phase2Auth, req.Username, req.Interactive, useSystemCACerts, req.DomainSuffixMatch) - - case isPsk: - sec := map[string]any{ - "key-mgmt": "wpa-psk", - "psk-flags": uint32(0), - } - if !req.Interactive { - sec["psk"] = req.Password - } - settings["802-11-wireless-security"] = sec - - case isSae: - sec := map[string]any{ - "key-mgmt": "sae", - "pmf": int32(3), - "psk-flags": uint32(0), - } - if !req.Interactive { - sec["psk"] = req.Password - } - settings["802-11-wireless-security"] = sec - - default: - return fmt.Errorf("secured network but not SAE/PSK/802.1X (rsn=0x%x wpa=0x%x)", rsnFlags, wpaFlags) - } - } else { - wifiSettings := map[string]any{ - "ssid": []byte(req.SSID), - "mode": "infrastructure", - } - if req.Hidden { - wifiSettings["hidden"] = true - } - settings["802-11-wireless"] = wifiSettings - } - - if req.Interactive { - s := b.settings - if s == nil { - var settingsErr error - s, settingsErr = gonetworkmanager.NewSettings() - if settingsErr != nil { - return fmt.Errorf("failed to get settings manager: %w", settingsErr) - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - conn, err := settingsMgr.AddConnection(settings) - if err != nil { - return fmt.Errorf("failed to add connection: %w", err) - } - - if isEnterprise { - log.Infof("[createAndConnectWiFi] Enterprise connection added, activating (secret agent will be called)") - } - - if req.Hidden { - _, err = nm.ActivateConnection(conn, dev, nil) - } else { - _, err = nm.ActivateWirelessConnection(conn, dev, targetAP) - } - if err != nil { - return fmt.Errorf("failed to activate connection: %w", err) - } - - log.Infof("[createAndConnectWiFi] Connection activation initiated, waiting for NetworkManager state changes...") - } else { - var err error - if req.Hidden { - _, err = nm.AddAndActivateConnection(settings, dev) - } else { - _, err = nm.AddAndActivateWirelessConnection(settings, dev, targetAP) - } - if err != nil { - return fmt.Errorf("failed to connect: %w", err) - } - log.Infof("[createAndConnectWiFi] Connection activation initiated, waiting for NetworkManager state changes...") - } - - return nil -} - -func (b *NetworkManagerBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - conn, err := b.findConnection(ssid) - if err != nil { - return fmt.Errorf("connection not found: %w", err) - } - - settings, err := conn.GetSettings() - if err != nil { - return fmt.Errorf("failed to get connection settings: %w", err) - } - - if connMeta, ok := settings["connection"]; ok { - connMeta["autoconnect"] = autoconnect - } else { - return fmt.Errorf("connection metadata not found") - } - - if ipv4, ok := settings["ipv4"]; ok { - delete(ipv4, "addresses") - delete(ipv4, "routes") - delete(ipv4, "dns") - } - - if ipv6, ok := settings["ipv6"]; ok { - delete(ipv6, "addresses") - delete(ipv6, "routes") - delete(ipv6, "dns") - } - - err = conn.Update(settings) - if err != nil { - return fmt.Errorf("failed to update connection: %w", err) - } - - b.updateWiFiNetworks() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) ScanWiFiDevice(device string) error { - devInfo, ok := b.wifiDevices[device] - if !ok { - return fmt.Errorf("WiFi device not found: %s", device) - } - - b.stateMutex.RLock() - enabled := b.state.WiFiEnabled - b.stateMutex.RUnlock() - - if !enabled { - return fmt.Errorf("WiFi is disabled") - } - - if err := devInfo.wireless.RequestScan(); err != nil { - return fmt.Errorf("scan request failed: %w", err) - } - - b.updateAllWiFiDevices() - return nil -} - -func (b *NetworkManagerBackend) DisconnectWiFiDevice(device string) error { - devInfo, ok := b.wifiDevices[device] - if !ok { - return fmt.Errorf("WiFi device not found: %s", device) - } - - if err := devInfo.device.Disconnect(); err != nil { - return fmt.Errorf("failed to disconnect: %w", err) - } - - b.updateWiFiState() - b.updateAllWiFiDevices() - b.updatePrimaryConnection() - - if b.onStateChange != nil { - b.onStateChange() - } - - return nil -} - -func (b *NetworkManagerBackend) GetWiFiDevices() []WiFiDevice { - b.stateMutex.RLock() - defer b.stateMutex.RUnlock() - return append([]WiFiDevice(nil), b.state.WiFiDevices...) -} - -func (b *NetworkManagerBackend) updateAllWiFiDevices() { - s := b.settings - if s == nil { - var err error - s, err = gonetworkmanager.NewSettings() - if err != nil { - return - } - b.settings = s - } - - settingsMgr := s.(gonetworkmanager.Settings) - connections, err := settingsMgr.ListConnections() - if err != nil { - return - } - - savedSSIDs := make(map[string]bool) - autoconnectMap := make(map[string]bool) - hiddenSSIDs := make(map[string]bool) - for _, conn := range connections { - connSettings, err := conn.GetSettings() - if err != nil { - continue - } - - connMeta, ok := connSettings["connection"] - if !ok { - continue - } - - connType, ok := connMeta["type"].(string) - if !ok || connType != "802-11-wireless" { - continue - } - - wifiSettings, ok := connSettings["802-11-wireless"] - if !ok { - continue - } - - ssidBytes, ok := wifiSettings["ssid"].([]byte) - if !ok { - continue - } - - ssid := string(ssidBytes) - savedSSIDs[ssid] = true - autoconnect := true - if ac, ok := connMeta["autoconnect"].(bool); ok { - autoconnect = ac - } - autoconnectMap[ssid] = autoconnect - - if hidden, ok := wifiSettings["hidden"].(bool); ok && hidden { - hiddenSSIDs[ssid] = true - } - } - - var devices []WiFiDevice - - for name, devInfo := range b.wifiDevices { - state, _ := devInfo.device.GetPropertyState() - connected := state == gonetworkmanager.NmDeviceStateActivated - - var ssid, bssid, ip string - var signal uint8 - - if connected { - if activeAP, err := devInfo.wireless.GetPropertyActiveAccessPoint(); err == nil && activeAP != nil && activeAP.GetPath() != "/" { - ssid, _ = activeAP.GetPropertySSID() - signal, _ = activeAP.GetPropertyStrength() - bssid, _ = activeAP.GetPropertyHWAddress() - } - ip = b.getDeviceIP(devInfo.device) - } - - stateStr := "disconnected" - switch state { - case gonetworkmanager.NmDeviceStateActivated: - stateStr = "connected" - case gonetworkmanager.NmDeviceStateConfig, gonetworkmanager.NmDeviceStateIpConfig: - stateStr = "connecting" - case gonetworkmanager.NmDeviceStatePrepare: - stateStr = "preparing" - case gonetworkmanager.NmDeviceStateDeactivating: - stateStr = "disconnecting" - } - - apPaths, err := devInfo.wireless.GetAccessPoints() - var networks []WiFiNetwork - if err == nil { - seenSSIDs := make(map[string]*WiFiNetwork) - for _, ap := range apPaths { - apSSID, err := ap.GetPropertySSID() - if err != nil || apSSID == "" { - continue - } - - if existing, exists := seenSSIDs[apSSID]; exists { - strength, _ := ap.GetPropertyStrength() - if strength > existing.Signal { - existing.Signal = strength - freq, _ := ap.GetPropertyFrequency() - existing.Frequency = freq - apBSSID, _ := ap.GetPropertyHWAddress() - existing.BSSID = apBSSID - } - continue - } - - strength, _ := ap.GetPropertyStrength() - flags, _ := ap.GetPropertyFlags() - wpaFlags, _ := ap.GetPropertyWPAFlags() - rsnFlags, _ := ap.GetPropertyRSNFlags() - freq, _ := ap.GetPropertyFrequency() - maxBitrate, _ := ap.GetPropertyMaxBitrate() - apBSSID, _ := ap.GetPropertyHWAddress() - mode, _ := ap.GetPropertyMode() - - secured := flags != uint32(gonetworkmanager.Nm80211APFlagsNone) || - wpaFlags != uint32(gonetworkmanager.Nm80211APSecNone) || - rsnFlags != uint32(gonetworkmanager.Nm80211APSecNone) - - enterprise := (rsnFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) || - (wpaFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) - - var modeStr string - switch mode { - case gonetworkmanager.Nm80211ModeAdhoc: - modeStr = "adhoc" - case gonetworkmanager.Nm80211ModeInfra: - modeStr = "infrastructure" - case gonetworkmanager.Nm80211ModeAp: - modeStr = "ap" - default: - modeStr = "unknown" - } - - channel := frequencyToChannel(freq) - - isConnected := connected && apSSID == ssid - rate := maxBitrate / 1000 - if isConnected { - if devBitrate, err := devInfo.wireless.GetPropertyBitrate(); err == nil && devBitrate > 0 { - rate = devBitrate / 1000 - } - } - - network := WiFiNetwork{ - SSID: apSSID, - BSSID: apBSSID, - Signal: strength, - Secured: secured, - Enterprise: enterprise, - Connected: isConnected, - Saved: savedSSIDs[apSSID], - Autoconnect: autoconnectMap[apSSID], - Hidden: hiddenSSIDs[apSSID], - Frequency: freq, - Mode: modeStr, - Rate: rate, - Channel: channel, - Device: name, - } - - seenSSIDs[apSSID] = &network - networks = append(networks, network) - } - - if connected && ssid != "" { - if _, exists := seenSSIDs[ssid]; !exists { - hiddenNetwork := WiFiNetwork{ - SSID: ssid, - BSSID: bssid, - Signal: signal, - Secured: true, - Connected: true, - Saved: savedSSIDs[ssid], - Autoconnect: autoconnectMap[ssid], - Hidden: true, - Mode: "infrastructure", - Device: name, - } - networks = append(networks, hiddenNetwork) - } - } - - sortWiFiNetworks(networks) - } - - devices = append(devices, WiFiDevice{ - Name: name, - HwAddress: devInfo.hwAddress, - State: stateStr, - Connected: connected, - SSID: ssid, - BSSID: bssid, - Signal: signal, - IP: ip, - Networks: networks, - }) - } - - sort.Slice(devices, func(i, j int) bool { - return devices[i].Name < devices[j].Name - }) - - b.stateMutex.Lock() - b.state.WiFiDevices = devices - b.stateMutex.Unlock() -} - -func (b *NetworkManagerBackend) getWifiDeviceForConnection(deviceName string) (*wifiDeviceInfo, error) { - if deviceName != "" { - devInfo, ok := b.wifiDevices[deviceName] - if !ok { - return nil, fmt.Errorf("WiFi device not found: %s", deviceName) - } - return devInfo, nil - } - - if b.wifiDevice == nil { - return nil, fmt.Errorf("no WiFi device available") - } - - dev := b.wifiDevice.(gonetworkmanager.Device) - iface, _ := dev.GetPropertyInterface() - if devInfo, ok := b.wifiDevices[iface]; ok { - return devInfo, nil - } - - return nil, fmt.Errorf("no WiFi device available") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_wifi_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_wifi_test.go deleted file mode 100644 index 79c2c5b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/backend_networkmanager_wifi_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package network - -import ( - "testing" - - mock_gonetworkmanager "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2" - "github.com/stretchr/testify/assert" -) - -func TestNetworkManagerBackend_GetWiFiEnabled(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().GetPropertyWirelessEnabled().Return(true, nil) - - enabled, err := backend.GetWiFiEnabled() - assert.NoError(t, err) - assert.True(t, enabled) -} - -func TestNetworkManagerBackend_SetWiFiEnabled(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - mockNM.EXPECT().SetPropertyWirelessEnabled(true).Return(nil) - - err = backend.SetWiFiEnabled(true) - assert.NoError(t, err) - - backend.stateMutex.RLock() - assert.True(t, backend.state.WiFiEnabled) - backend.stateMutex.RUnlock() -} - -func TestNetworkManagerBackend_ScanWiFi_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - err = backend.ScanWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_ScanWiFi_Disabled(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockDeviceWireless := mock_gonetworkmanager.NewMockDeviceWireless(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = mockDeviceWireless - backend.wifiDev = mockDeviceWireless - - backend.stateMutex.Lock() - backend.state.WiFiEnabled = false - backend.stateMutex.Unlock() - - err = backend.ScanWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "WiFi is disabled") -} - -func TestNetworkManagerBackend_GetWiFiNetworkDetails_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - _, err = backend.GetWiFiNetworkDetails("TestNetwork") - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_ConnectWiFi_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - req := ConnectionRequest{SSID: "TestNetwork", Password: "password"} - err = backend.ConnectWiFi(req) - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_ConnectWiFi_AlreadyConnected(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - mockDeviceWireless := mock_gonetworkmanager.NewMockDeviceWireless(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = mockDeviceWireless - backend.wifiDev = mockDeviceWireless - backend.wifiDevices = map[string]*wifiDeviceInfo{ - "wlan0": { - device: nil, - wireless: mockDeviceWireless, - name: "wlan0", - hwAddress: "00:11:22:33:44:55", - }, - } - - mockDeviceWireless.EXPECT().GetPropertyInterface().Return("wlan0", nil) - - backend.stateMutex.Lock() - backend.state.WiFiConnected = true - backend.state.WiFiSSID = "TestNetwork" - backend.state.WiFiDevice = "wlan0" - backend.stateMutex.Unlock() - - req := ConnectionRequest{SSID: "TestNetwork", Password: "password"} - err = backend.ConnectWiFi(req) - assert.NoError(t, err) -} - -func TestNetworkManagerBackend_DisconnectWiFi_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - err = backend.DisconnectWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_IsConnectingTo(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.stateMutex.Lock() - backend.state.IsConnecting = true - backend.state.ConnectingSSID = "TestNetwork" - backend.stateMutex.Unlock() - - assert.True(t, backend.IsConnectingTo("TestNetwork")) - assert.False(t, backend.IsConnectingTo("OtherNetwork")) -} - -func TestNetworkManagerBackend_IsConnectingTo_NotConnecting(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.stateMutex.Lock() - backend.state.IsConnecting = false - backend.state.ConnectingSSID = "" - backend.stateMutex.Unlock() - - assert.False(t, backend.IsConnectingTo("TestNetwork")) -} - -func TestNetworkManagerBackend_UpdateWiFiNetworks_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - _, err = backend.updateWiFiNetworks() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestNetworkManagerBackend_FindConnection_NoSettings(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.settings = nil - _, err = backend.findConnection("NonExistentNetwork") - assert.Error(t, err) -} - -func TestNetworkManagerBackend_CreateAndConnectWiFi_NoDevice(t *testing.T) { - mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) - - backend, err := NewNetworkManagerBackend(mockNM) - assert.NoError(t, err) - - backend.wifiDevice = nil - backend.wifiDev = nil - req := ConnectionRequest{SSID: "TestNetwork", Password: "password"} - err = backend.createAndConnectWiFi(req) - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/broker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/broker.go deleted file mode 100644 index 5290fb5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/broker.go +++ /dev/null @@ -1,22 +0,0 @@ -package network - -import ( - "context" - "crypto/rand" - "encoding/hex" -) - -type PromptBroker interface { - Ask(ctx context.Context, req PromptRequest) (token string, err error) - Wait(ctx context.Context, token string) (PromptReply, error) - Resolve(token string, reply PromptReply) error - Cancel(path string, setting string) error -} - -func generateToken() (string, error) { - bytes := make([]byte, 16) - if _, err := rand.Read(bytes); err != nil { - return "", err - } - return hex.EncodeToString(bytes), nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/connection_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/connection_test.go deleted file mode 100644 index 7b37731..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/connection_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package network_test - -import ( - "errors" - "testing" - - mocks_network "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/network" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/network" - "github.com/stretchr/testify/assert" -) - -func TestConnectionRequest_Validation(t *testing.T) { - t.Run("basic WiFi connection", func(t *testing.T) { - req := network.ConnectionRequest{ - SSID: "TestNetwork", - Password: "testpass123", - } - - assert.NotEmpty(t, req.SSID) - assert.NotEmpty(t, req.Password) - assert.Empty(t, req.Username) - }) - - t.Run("enterprise WiFi connection", func(t *testing.T) { - req := network.ConnectionRequest{ - SSID: "EnterpriseNetwork", - Password: "testpass123", - Username: "testuser", - } - - assert.NotEmpty(t, req.SSID) - assert.NotEmpty(t, req.Password) - assert.NotEmpty(t, req.Username) - }) - - t.Run("open WiFi connection", func(t *testing.T) { - req := network.ConnectionRequest{ - SSID: "OpenNetwork", - } - - assert.NotEmpty(t, req.SSID) - assert.Empty(t, req.Password) - assert.Empty(t, req.Username) - }) -} - -func TestManager_ConnectWiFi_NoDevice(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - req := network.ConnectionRequest{ - SSID: "TestNetwork", - Password: "testpass123", - } - backend.EXPECT().ConnectWiFi(req).Return(errors.New("no WiFi device available")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.ConnectWiFi(req) - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestManager_DisconnectWiFi_NoDevice(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - backend.EXPECT().DisconnectWiFi().Return(errors.New("no WiFi device available")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.DisconnectWiFi() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no WiFi device available") -} - -func TestManager_ForgetWiFiNetwork_NotFound(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - backend.EXPECT().ForgetWiFiNetwork("NonExistentNetwork").Return(errors.New("connection not found")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.ForgetWiFiNetwork("NonExistentNetwork") - assert.Error(t, err) - assert.Contains(t, err.Error(), "connection not found") -} - -func TestManager_ConnectEthernet_NoDevice(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - backend.EXPECT().ConnectEthernet().Return(errors.New("no ethernet device available")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.ConnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -func TestManager_DisconnectEthernet_NoDevice(t *testing.T) { - backend := mocks_network.NewMockBackend(t) - backend.EXPECT().DisconnectEthernet().Return(errors.New("no ethernet device available")) - - manager := network.NewTestManager(backend, &network.NetworkState{}) - - err := manager.DisconnectEthernet() - assert.Error(t, err) - assert.Contains(t, err.Error(), "no ethernet device available") -} - -// Note: More comprehensive tests for connection operations would require -// mocking the NetworkManager D-Bus interfaces, which is beyond the scope -// of these unit tests. The tests above cover the basic error cases and -// validation logic. Integration tests would be needed for full coverage. diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/detect.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/detect.go deleted file mode 100644 index 6031e42..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/detect.go +++ /dev/null @@ -1,89 +0,0 @@ -package network - -import ( - "fmt" - - "github.com/godbus/dbus/v5" -) - -type BackendType int - -const ( - BackendNone BackendType = iota - BackendNetworkManager - BackendIwd - BackendConnMan - BackendNetworkd -) - -func nameHasOwner(bus *dbus.Conn, name string) (bool, error) { - obj := bus.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") - var owned bool - if err := obj.Call("org.freedesktop.DBus.NameHasOwner", 0, name).Store(&owned); err != nil { - return false, err - } - return owned, nil -} - -type DetectResult struct { - Backend BackendType - HasNM bool - HasIwd bool - HasConnMan bool - HasWpaSupp bool - HasNetworkd bool - ChosenReason string -} - -func DetectNetworkStack() (*DetectResult, error) { - bus, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("connect system bus: %w", err) - } - defer bus.Close() - - hasNM, _ := nameHasOwner(bus, "org.freedesktop.NetworkManager") - hasIwd, _ := nameHasOwner(bus, "net.connman.iwd") - hasConn, _ := nameHasOwner(bus, "net.connman") - hasWpa, _ := nameHasOwner(bus, "fi.w1.wpa_supplicant1") - hasNetworkd, _ := nameHasOwner(bus, "org.freedesktop.network1") - - res := &DetectResult{ - HasNM: hasNM, - HasIwd: hasIwd, - HasConnMan: hasConn, - HasWpaSupp: hasWpa, - HasNetworkd: hasNetworkd, - } - - switch { - case hasNM: - res.Backend = BackendNetworkManager - if hasIwd { - res.ChosenReason = "NetworkManager present; iwd also running (likely NM's Wi-Fi backend). Using NM API." - } else { - res.ChosenReason = "NetworkManager present. Using NM API." - } - case hasConn && hasIwd: - res.Backend = BackendConnMan - res.ChosenReason = "ConnMan + iwd detected. Use ConnMan API (iwd is its Wi-Fi daemon)." - case hasIwd && hasNetworkd: - res.Backend = BackendNetworkd - res.ChosenReason = "iwd + systemd-networkd detected. Using iwd for Wi-Fi association and networkd for IP/DHCP." - case hasIwd: - res.Backend = BackendIwd - res.ChosenReason = "iwd detected without NM/ConnMan. Using iwd API." - case hasNetworkd: - res.Backend = BackendNetworkd - res.ChosenReason = "systemd-networkd detected (no NM/ConnMan). Using networkd for L3 and wired." - default: - res.Backend = BackendNone - if hasWpa { - res.ChosenReason = "No NM/ConnMan/iwd; wpa_supplicant present. Consider a wpa_supplicant path." - } else { - res.ChosenReason = "No known network manager bus names found." - } - } - - return res, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/detect_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/detect_test.go deleted file mode 100644 index 87e7635..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/detect_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package network - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestBackendType_Constants(t *testing.T) { - assert.Equal(t, BackendType(0), BackendNone) - assert.Equal(t, BackendType(1), BackendNetworkManager) - assert.Equal(t, BackendType(2), BackendIwd) - assert.Equal(t, BackendType(3), BackendConnMan) - assert.Equal(t, BackendType(4), BackendNetworkd) -} - -func TestDetectResult_HasNetworkdField(t *testing.T) { - result := &DetectResult{ - Backend: BackendNetworkd, - HasNetworkd: true, - HasIwd: true, - } - - assert.True(t, result.HasNetworkd) - assert.True(t, result.HasIwd) - assert.Equal(t, BackendNetworkd, result.Backend) -} - -func TestDetectNetworkStack_Integration(t *testing.T) { - result, err := DetectNetworkStack() - - if err != nil && strings.Contains(err.Error(), "connect system bus") { - t.Skipf("system D-Bus unavailable: %v", err) - } - - assert.NoError(t, err) - if assert.NotNil(t, result) { - assert.NotEmpty(t, result.ChosenReason) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/handlers.go deleted file mode 100644 index 2b3e75e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/handlers.go +++ /dev/null @@ -1,630 +0,0 @@ -package network - -import ( - "encoding/json" - "fmt" - "net" - "os" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - switch req.Method { - case "network.getState": - handleGetState(conn, req, manager) - case "network.wifi.scan": - handleScanWiFi(conn, req, manager) - case "network.wifi.networks": - handleGetWiFiNetworks(conn, req, manager) - case "network.wifi.connect": - handleConnectWiFi(conn, req, manager) - case "network.wifi.disconnect": - handleDisconnectWiFi(conn, req, manager) - case "network.wifi.forget": - handleForgetWiFi(conn, req, manager) - case "network.wifi.toggle": - handleToggleWiFi(conn, req, manager) - case "network.wifi.enable": - handleEnableWiFi(conn, req, manager) - case "network.wifi.disable": - handleDisableWiFi(conn, req, manager) - case "network.ethernet.connect.config": - handleConnectEthernetSpecificConfig(conn, req, manager) - case "network.ethernet.connect": - handleConnectEthernet(conn, req, manager) - case "network.ethernet.disconnect": - handleDisconnectEthernet(conn, req, manager) - case "network.preference.set": - handleSetPreference(conn, req, manager) - case "network.info": - handleGetNetworkInfo(conn, req, manager) - case "network.qrcode": - handleGetNetworkQRCode(conn, req, manager) - case "network.delete-qrcode": - handleDeleteQRCode(conn, req, manager) - case "network.ethernet.info": - handleGetWiredNetworkInfo(conn, req, manager) - case "network.subscribe": - handleSubscribe(conn, req, manager) - case "network.credentials.submit": - handleCredentialsSubmit(conn, req, manager) - case "network.credentials.cancel": - handleCredentialsCancel(conn, req, manager) - case "network.vpn.profiles": - handleListVPNProfiles(conn, req, manager) - case "network.vpn.active": - handleListActiveVPN(conn, req, manager) - case "network.vpn.connect": - handleConnectVPN(conn, req, manager) - case "network.vpn.disconnect": - handleDisconnectVPN(conn, req, manager) - case "network.vpn.disconnectAll": - handleDisconnectAllVPN(conn, req, manager) - case "network.vpn.clearCredentials": - handleClearVPNCredentials(conn, req, manager) - case "network.vpn.plugins": - handleListVPNPlugins(conn, req, manager) - case "network.vpn.import": - handleImportVPN(conn, req, manager) - case "network.vpn.getConfig": - handleGetVPNConfig(conn, req, manager) - case "network.vpn.updateConfig": - handleUpdateVPNConfig(conn, req, manager) - case "network.vpn.delete": - handleDeleteVPN(conn, req, manager) - case "network.vpn.setCredentials": - handleSetVPNCredentials(conn, req, manager) - case "network.wifi.setAutoconnect": - handleSetWiFiAutoconnect(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleCredentialsSubmit(conn net.Conn, req models.Request, manager *Manager) { - token, err := params.String(req.Params, "token") - if err != nil { - log.Warnf("handleCredentialsSubmit: missing or invalid token parameter") - models.RespondError(conn, req.ID, err.Error()) - return - } - - secrets, err := params.StringMap(req.Params, "secrets") - if err != nil { - log.Warnf("handleCredentialsSubmit: missing or invalid secrets parameter") - models.RespondError(conn, req.ID, err.Error()) - return - } - - save := params.BoolOpt(req.Params, "save", true) - - if err := manager.SubmitCredentials(token, secrets, save); err != nil { - log.Warnf("handleCredentialsSubmit: failed to submit credentials: %v", err) - models.RespondError(conn, req.ID, err.Error()) - return - } - - log.Infof("handleCredentialsSubmit: credentials submitted successfully") - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "credentials submitted"}) -} - -func handleCredentialsCancel(conn net.Conn, req models.Request, manager *Manager) { - token, err := params.String(req.Params, "token") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.CancelCredentials(token); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "credentials cancelled"}) -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleScanWiFi(conn net.Conn, req models.Request, manager *Manager) { - device := params.StringOpt(req.Params, "device", "") - var err error - if device != "" { - err = manager.ScanWiFiDevice(device) - } else { - err = manager.ScanWiFi() - } - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "scanning"}) -} - -func handleGetWiFiNetworks(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetWiFiNetworks()) -} - -func handleConnectWiFi(conn net.Conn, req models.Request, manager *Manager) { - ssid, err := params.String(req.Params, "ssid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - var connReq ConnectionRequest - connReq.SSID = ssid - connReq.Password = params.StringOpt(req.Params, "password", "") - connReq.Username = params.StringOpt(req.Params, "username", "") - connReq.Device = params.StringOpt(req.Params, "device", "") - - if interactive, ok := models.Get[bool](req, "interactive"); ok { - connReq.Interactive = interactive - } else { - state := manager.GetState() - alreadyConnected := state.WiFiConnected && state.WiFiSSID == ssid - - if alreadyConnected && connReq.Device == "" { - connReq.Interactive = false - } else { - networkInfo, err := manager.GetNetworkInfo(ssid) - isSaved := err == nil && networkInfo.Saved - - if isSaved { - connReq.Interactive = false - } else if err == nil && networkInfo.Secured && connReq.Password == "" && connReq.Username == "" { - connReq.Interactive = true - } - } - } - - connReq.AnonymousIdentity = params.StringOpt(req.Params, "anonymousIdentity", "") - connReq.DomainSuffixMatch = params.StringOpt(req.Params, "domainSuffixMatch", "") - connReq.EAPMethod = params.StringOpt(req.Params, "eapMethod", "") - connReq.Phase2Auth = params.StringOpt(req.Params, "phase2Auth", "") - connReq.CACertPath = params.StringOpt(req.Params, "caCertPath", "") - connReq.ClientCertPath = params.StringOpt(req.Params, "clientCertPath", "") - connReq.PrivateKeyPath = params.StringOpt(req.Params, "privateKeyPath", "") - - if useSystemCACerts, ok := models.Get[bool](req, "useSystemCACerts"); ok { - connReq.UseSystemCACerts = &useSystemCACerts - } - - if err := manager.ConnectWiFi(connReq); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"}) -} - -func handleDisconnectWiFi(conn net.Conn, req models.Request, manager *Manager) { - device := params.StringOpt(req.Params, "device", "") - var err error - if device != "" { - err = manager.DisconnectWiFiDevice(device) - } else { - err = manager.DisconnectWiFi() - } - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "disconnected"}) -} - -func handleForgetWiFi(conn net.Conn, req models.Request, manager *Manager) { - ssid, err := params.String(req.Params, "ssid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.ForgetWiFiNetwork(ssid); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "forgotten"}) -} - -func handleToggleWiFi(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.ToggleWiFi(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - state := manager.GetState() - models.Respond(conn, req.ID, map[string]bool{"enabled": state.WiFiEnabled}) -} - -func handleEnableWiFi(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.EnableWiFi(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, map[string]bool{"enabled": true}) -} - -func handleDisableWiFi(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.DisableWiFi(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, map[string]bool{"enabled": false}) -} - -func handleConnectEthernetSpecificConfig(conn net.Conn, req models.Request, manager *Manager) { - uuid, err := params.String(req.Params, "uuid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - if err := manager.activateConnection(uuid); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"}) -} - -func handleConnectEthernet(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.ConnectEthernet(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "connecting"}) -} - -func handleDisconnectEthernet(conn net.Conn, req models.Request, manager *Manager) { - device := params.StringOpt(req.Params, "device", "") - var err error - if device != "" { - err = manager.DisconnectEthernetDevice(device) - } else { - err = manager.DisconnectEthernet() - } - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "disconnected"}) -} - -func handleSetPreference(conn net.Conn, req models.Request, manager *Manager) { - preference, err := params.String(req.Params, "preference") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetConnectionPreference(ConnectionPreference(preference)); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, map[string]string{"preference": preference}) -} - -func handleGetNetworkInfo(conn net.Conn, req models.Request, manager *Manager) { - ssid, err := params.String(req.Params, "ssid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - network, err := manager.GetNetworkInfoDetailed(ssid) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, network) -} - -func handleGetNetworkQRCode(conn net.Conn, req models.Request, manager *Manager) { - ssid, err := params.String(req.Params, "ssid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - content, err := manager.GetNetworkQRCode(ssid) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, content) -} - -func handleDeleteQRCode(conn net.Conn, req models.Request, _ *Manager) { - path, err := params.String(req.Params, "path") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if !isValidQRCodePath(path) { - models.RespondError(conn, req.ID, "invalid QR code path") - return - } - - if err := os.Remove(path); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "QR code file deleted"}) -} - -func handleGetWiredNetworkInfo(conn net.Conn, req models.Request, manager *Manager) { - uuid, err := params.String(req.Params, "uuid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - network, err := manager.GetWiredNetworkInfoDetailed(uuid) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, network) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - event := NetworkEvent{ - Type: EventStateChanged, - Data: initialState, - } - if err := json.NewEncoder(conn).Encode(models.Response[NetworkEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - return - } - - for state := range stateChan { - event := NetworkEvent{ - Type: EventStateChanged, - Data: state, - } - if err := json.NewEncoder(conn).Encode(models.Response[NetworkEvent]{ - Result: &event, - }); err != nil { - return - } - } -} - -func handleListVPNProfiles(conn net.Conn, req models.Request, manager *Manager) { - profiles, err := manager.ListVPNProfiles() - if err != nil { - log.Warnf("handleListVPNProfiles: failed to list profiles: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list VPN profiles: %v", err)) - return - } - - models.Respond(conn, req.ID, profiles) -} - -func handleListActiveVPN(conn net.Conn, req models.Request, manager *Manager) { - active, err := manager.ListActiveVPN() - if err != nil { - log.Warnf("handleListActiveVPN: failed to list active VPNs: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list active VPNs: %v", err)) - return - } - - models.Respond(conn, req.ID, active) -} - -func handleConnectVPN(conn net.Conn, req models.Request, manager *Manager) { - uuidOrName, ok := params.StringAlt(req.Params, "uuidOrName", "name", "uuid") - if !ok { - log.Warnf("handleConnectVPN: missing uuidOrName/name/uuid parameter") - models.RespondError(conn, req.ID, "missing 'uuidOrName', 'name', or 'uuid' parameter") - return - } - - singleActive := params.BoolOpt(req.Params, "singleActive", true) - - if err := manager.ConnectVPN(uuidOrName, singleActive); err != nil { - log.Warnf("handleConnectVPN: failed to connect: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to connect VPN: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN connection initiated"}) -} - -func handleDisconnectVPN(conn net.Conn, req models.Request, manager *Manager) { - uuidOrName, ok := params.StringAlt(req.Params, "uuidOrName", "name", "uuid") - if !ok { - log.Warnf("handleDisconnectVPN: missing uuidOrName/name/uuid parameter") - models.RespondError(conn, req.ID, "missing 'uuidOrName', 'name', or 'uuid' parameter") - return - } - - if err := manager.DisconnectVPN(uuidOrName); err != nil { - log.Warnf("handleDisconnectVPN: failed to disconnect: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to disconnect VPN: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN disconnected"}) -} - -func handleDisconnectAllVPN(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.DisconnectAllVPN(); err != nil { - log.Warnf("handleDisconnectAllVPN: failed: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to disconnect all VPNs: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "All VPNs disconnected"}) -} - -func handleClearVPNCredentials(conn net.Conn, req models.Request, manager *Manager) { - uuidOrName, ok := params.StringAlt(req.Params, "uuid", "name", "uuidOrName") - if !ok { - log.Warnf("handleClearVPNCredentials: missing uuidOrName/name/uuid parameter") - models.RespondError(conn, req.ID, "missing uuidOrName/name/uuid parameter") - return - } - - if err := manager.ClearVPNCredentials(uuidOrName); err != nil { - log.Warnf("handleClearVPNCredentials: failed: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to clear VPN credentials: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN credentials cleared"}) -} - -func handleSetWiFiAutoconnect(conn net.Conn, req models.Request, manager *Manager) { - ssid, err := params.String(req.Params, "ssid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - autoconnect, err := params.Bool(req.Params, "autoconnect") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetWiFiAutoconnect(ssid, autoconnect); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to set autoconnect: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "autoconnect updated"}) -} - -func handleListVPNPlugins(conn net.Conn, req models.Request, manager *Manager) { - plugins, err := manager.ListVPNPlugins() - if err != nil { - log.Warnf("handleListVPNPlugins: failed to list plugins: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list VPN plugins: %v", err)) - return - } - - models.Respond(conn, req.ID, plugins) -} - -func handleImportVPN(conn net.Conn, req models.Request, manager *Manager) { - filePath, ok := params.StringAlt(req.Params, "file", "path") - if !ok { - models.RespondError(conn, req.ID, "missing 'file' or 'path' parameter") - return - } - - name := params.StringOpt(req.Params, "name", "") - - result, err := manager.ImportVPN(filePath, name) - if err != nil { - log.Warnf("handleImportVPN: failed to import: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to import VPN: %v", err)) - return - } - - models.Respond(conn, req.ID, result) -} - -func handleGetVPNConfig(conn net.Conn, req models.Request, manager *Manager) { - uuidOrName, ok := params.StringAlt(req.Params, "uuid", "name", "uuidOrName") - if !ok { - models.RespondError(conn, req.ID, "missing 'uuid', 'name', or 'uuidOrName' parameter") - return - } - - config, err := manager.GetVPNConfig(uuidOrName) - if err != nil { - log.Warnf("handleGetVPNConfig: failed to get config: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to get VPN config: %v", err)) - return - } - - models.Respond(conn, req.ID, config) -} - -func handleUpdateVPNConfig(conn net.Conn, req models.Request, manager *Manager) { - connUUID, err := params.String(req.Params, "uuid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - updates := make(map[string]any) - - if name, ok := models.Get[string](req, "name"); ok { - updates["name"] = name - } - if autoconnect, ok := models.Get[bool](req, "autoconnect"); ok { - updates["autoconnect"] = autoconnect - } - if data, ok := models.Get[map[string]any](req, "data"); ok { - updates["data"] = data - } - - if len(updates) == 0 { - models.RespondError(conn, req.ID, "no updates provided") - return - } - - if err := manager.UpdateVPNConfig(connUUID, updates); err != nil { - log.Warnf("handleUpdateVPNConfig: failed to update: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to update VPN config: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN config updated"}) -} - -func handleDeleteVPN(conn net.Conn, req models.Request, manager *Manager) { - uuidOrName, ok := params.StringAlt(req.Params, "uuid", "name", "uuidOrName") - if !ok { - models.RespondError(conn, req.ID, "missing 'uuid', 'name', or 'uuidOrName' parameter") - return - } - - if err := manager.DeleteVPN(uuidOrName); err != nil { - log.Warnf("handleDeleteVPN: failed to delete: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to delete VPN: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN deleted"}) -} - -func handleSetVPNCredentials(conn net.Conn, req models.Request, manager *Manager) { - connUUID, err := params.String(req.Params, "uuid") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - username := params.StringOpt(req.Params, "username", "") - password := params.StringOpt(req.Params, "password", "") - save := params.BoolOpt(req.Params, "save", true) - - if err := manager.SetVPNCredentials(connUUID, username, password, save); err != nil { - log.Warnf("handleSetVPNCredentials: failed to set credentials: %v", err) - models.RespondError(conn, req.ID, fmt.Sprintf("failed to set VPN credentials: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "VPN credentials set"}) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/handlers_test.go deleted file mode 100644 index ea8ae33..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/handlers_test.go +++ /dev/null @@ -1,269 +0,0 @@ -package network - -import ( - "bytes" - "encoding/json" - "net" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type mockNetConn struct { - net.Conn - readBuf *bytes.Buffer - writeBuf *bytes.Buffer - closed bool -} - -func newMockNetConn() *mockNetConn { - return &mockNetConn{ - readBuf: &bytes.Buffer{}, - writeBuf: &bytes.Buffer{}, - } -} - -func (m *mockNetConn) Read(b []byte) (n int, err error) { - return m.readBuf.Read(b) -} - -func (m *mockNetConn) Write(b []byte) (n int, err error) { - return m.writeBuf.Write(b) -} - -func (m *mockNetConn) Close() error { - m.closed = true - return nil -} - -func TestRespondError_Network(t *testing.T) { - conn := newMockNetConn() - models.RespondError(conn, 123, "test error") - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Equal(t, "test error", resp.Error) - assert.Nil(t, resp.Result) -} - -func TestRespond_Network(t *testing.T) { - conn := newMockNetConn() - result := models.SuccessResult{Success: true, Message: "test"} - models.Respond(conn, 123, result) - - var resp models.Response[models.SuccessResult] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.True(t, resp.Result.Success) - assert.Equal(t, "test", resp.Result.Message) -} - -func TestHandleGetState(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - WiFiSSID: "TestNetwork", - WiFiConnected: true, - }, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "network.getState"} - - handleGetState(conn, req, manager) - - var resp models.Response[NetworkState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Equal(t, StatusWiFi, resp.Result.NetworkStatus) - assert.Equal(t, "TestNetwork", resp.Result.WiFiSSID) -} - -func TestHandleGetWiFiNetworks(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - WiFiNetworks: []WiFiNetwork{ - {SSID: "Network1", Signal: 90}, - {SSID: "Network2", Signal: 80}, - }, - }, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "network.wifi.networks"} - - handleGetWiFiNetworks(conn, req, manager) - - var resp models.Response[[]WiFiNetwork] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Len(t, *resp.Result, 2) - assert.Equal(t, "Network1", (*resp.Result)[0].SSID) -} - -func TestHandleConnectWiFi(t *testing.T) { - t.Run("missing ssid parameter", func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "network.wifi.connect", - Params: map[string]any{}, - } - - handleConnectWiFi(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'ssid' parameter") - }) -} - -func TestHandleSetPreference(t *testing.T) { - t.Run("missing preference parameter", func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "network.preference.set", - Params: map[string]any{}, - } - - handleSetPreference(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'preference' parameter") - }) -} - -func TestHandleGetNetworkInfo(t *testing.T) { - t.Run("missing ssid parameter", func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "network.info", - Params: map[string]any{}, - } - - handleGetNetworkInfo(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "missing or invalid 'ssid' parameter") - }) -} - -func TestHandleRequest(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - }, - } - - t.Run("unknown method", func(t *testing.T) { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "network.unknown", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[any] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "unknown method") - }) - - t.Run("valid method - getState", func(t *testing.T) { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "network.getState", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[NetworkState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - }) -} - -func TestHandleSubscribe(t *testing.T) { - // This test is complex due to the streaming nature of subscriptions - // Better suited as an integration test - t.Skip("Subscription test requires connection lifecycle management - integration test needed") -} - -func TestManager_Subscribe_Unsubscribe(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - t.Run("subscribe creates channel", func(t *testing.T) { - ch := manager.Subscribe("client1") - assert.NotNil(t, ch) - count := 0 - manager.subscribers.Range(func(key string, ch chan NetworkState) bool { - count++ - return true - }) - assert.Equal(t, 1, count) - }) - - t.Run("unsubscribe removes channel", func(t *testing.T) { - manager.Unsubscribe("client1") - count := 0 - manager.subscribers.Range(func(key string, ch chan NetworkState) bool { count++; return true }) - assert.Equal(t, 0, count) - }) - - t.Run("unsubscribe non-existent client is safe", func(t *testing.T) { - assert.NotPanics(t, func() { - manager.Unsubscribe("non-existent") - }) - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/helpers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/helpers.go deleted file mode 100644 index 4669f52..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/helpers.go +++ /dev/null @@ -1,53 +0,0 @@ -package network - -import "sort" - -func frequencyToChannel(freq uint32) uint32 { - if freq >= 2412 && freq <= 2484 { - if freq == 2484 { - return 14 - } - return (freq-2412)/5 + 1 - } - - if freq >= 5170 && freq <= 5825 { - return (freq-5170)/5 + 34 - } - - if freq >= 5955 && freq <= 7115 { - return (freq-5955)/5 + 1 - } - - return 0 -} - -func sortWiFiNetworks(networks []WiFiNetwork) { - sort.Slice(networks, func(i, j int) bool { - if networks[i].Connected && !networks[j].Connected { - return true - } - if !networks[i].Connected && networks[j].Connected { - return false - } - - if networks[i].Saved && !networks[j].Saved { - return true - } - if !networks[i].Saved && networks[j].Saved { - return false - } - - if !networks[i].Secured && networks[j].Secured { - if networks[i].Signal >= 50 { - return true - } - } - if networks[i].Secured && !networks[j].Secured { - if networks[j].Signal >= 50 { - return false - } - } - - return networks[i].Signal > networks[j].Signal - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/manager.go deleted file mode 100644 index 5c6657e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/manager.go +++ /dev/null @@ -1,626 +0,0 @@ -package network - -import ( - "fmt" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/yeqown/go-qrcode/v2" - "github.com/yeqown/go-qrcode/writer/standard" -) - -func NewManager() (*Manager, error) { - detection, err := DetectNetworkStack() - if err != nil { - return nil, fmt.Errorf("failed to detect network stack: %w", err) - } - - log.Infof("Network backend detection: %s", detection.ChosenReason) - - var backend Backend - switch detection.Backend { - case BackendNetworkManager: - nm, err := NewNetworkManagerBackend() - if err != nil { - return nil, fmt.Errorf("failed to create NetworkManager backend: %w", err) - } - backend = nm - - case BackendIwd: - iwd, err := NewIWDBackend() - if err != nil { - return nil, fmt.Errorf("failed to create iwd backend: %w", err) - } - backend = iwd - - case BackendNetworkd: - if detection.HasIwd && !detection.HasNM { - wifi, err := NewIWDBackend() - if err != nil { - return nil, fmt.Errorf("failed to create iwd backend: %w", err) - } - l3, err := NewSystemdNetworkdBackend() - if err != nil { - return nil, fmt.Errorf("failed to create networkd backend: %w", err) - } - hybrid, err := NewHybridIwdNetworkdBackend(wifi, l3) - if err != nil { - return nil, fmt.Errorf("failed to create hybrid backend: %w", err) - } - backend = hybrid - } else { - nd, err := NewSystemdNetworkdBackend() - if err != nil { - return nil, fmt.Errorf("failed to create networkd backend: %w", err) - } - backend = nd - } - - default: - return nil, fmt.Errorf("no supported network backend found: %s", detection.ChosenReason) - } - - m := &Manager{ - backend: backend, - state: &NetworkState{ - NetworkStatus: StatusDisconnected, - Preference: PreferenceAuto, - WiFiNetworks: []WiFiNetwork{}, - }, - stateMutex: sync.RWMutex{}, - - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - broker := NewSubscriptionBroker(m.broadcastCredentialPrompt) - if err := backend.SetPromptBroker(broker); err != nil { - return nil, fmt.Errorf("failed to set prompt broker: %w", err) - } - - if err := backend.Initialize(); err != nil { - return nil, fmt.Errorf("failed to initialize backend: %w", err) - } - - if err := m.syncStateFromBackend(); err != nil { - return nil, fmt.Errorf("failed to sync initial state: %w", err) - } - - m.notifierWg.Add(1) - go m.notifier() - - if err := backend.StartMonitoring(m.onBackendStateChange); err != nil { - m.Close() - return nil, fmt.Errorf("failed to start monitoring: %w", err) - } - - return m, nil -} - -func (m *Manager) syncStateFromBackend() error { - backendState, err := m.backend.GetCurrentState() - if err != nil { - return err - } - - m.stateMutex.Lock() - m.state.Backend = backendState.Backend - m.state.NetworkStatus = backendState.NetworkStatus - m.state.EthernetIP = backendState.EthernetIP - m.state.EthernetDevice = backendState.EthernetDevice - m.state.EthernetConnected = backendState.EthernetConnected - m.state.EthernetConnectionUuid = backendState.EthernetConnectionUuid - m.state.EthernetDevices = backendState.EthernetDevices - m.state.WiFiIP = backendState.WiFiIP - m.state.WiFiDevice = backendState.WiFiDevice - m.state.WiFiConnected = backendState.WiFiConnected - m.state.WiFiEnabled = backendState.WiFiEnabled - m.state.WiFiSSID = backendState.WiFiSSID - m.state.WiFiBSSID = backendState.WiFiBSSID - m.state.WiFiSignal = backendState.WiFiSignal - m.state.WiFiNetworks = backendState.WiFiNetworks - m.state.WiFiDevices = backendState.WiFiDevices - m.state.WiredConnections = backendState.WiredConnections - m.state.VPNProfiles = backendState.VPNProfiles - m.state.VPNActive = backendState.VPNActive - m.state.IsConnecting = backendState.IsConnecting - m.state.ConnectingSSID = backendState.ConnectingSSID - m.state.ConnectingDevice = backendState.ConnectingDevice - m.state.LastError = backendState.LastError - m.stateMutex.Unlock() - - return nil -} - -func (m *Manager) onBackendStateChange() { - if err := m.syncStateFromBackend(); err != nil { - log.Errorf("failed to sync state from backend: %v", err) - } - m.notifySubscribers() -} - -func signalChangeSignificant(old, new uint8) bool { - if old == 0 || new == 0 { - return true - } - diff := int(new) - int(old) - if diff < 0 { - diff = -diff - } - return diff >= 5 -} - -func (m *Manager) snapshotState() NetworkState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - s := *m.state - s.WiFiNetworks = append([]WiFiNetwork(nil), m.state.WiFiNetworks...) - s.WiFiDevices = append([]WiFiDevice(nil), m.state.WiFiDevices...) - s.WiredConnections = append([]WiredConnection(nil), m.state.WiredConnections...) - s.EthernetDevices = append([]EthernetDevice(nil), m.state.EthernetDevices...) - s.VPNProfiles = append([]VPNProfile(nil), m.state.VPNProfiles...) - s.VPNActive = append([]VPNActive(nil), m.state.VPNActive...) - return s -} - -func stateChangedMeaningfully(old, new *NetworkState) bool { - if old.NetworkStatus != new.NetworkStatus { - return true - } - if old.Preference != new.Preference { - return true - } - if old.EthernetConnected != new.EthernetConnected { - return true - } - if old.EthernetIP != new.EthernetIP { - return true - } - if old.WiFiConnected != new.WiFiConnected { - return true - } - if old.WiFiEnabled != new.WiFiEnabled { - return true - } - if old.WiFiSSID != new.WiFiSSID { - return true - } - if old.WiFiBSSID != new.WiFiBSSID { - return true - } - if old.WiFiIP != new.WiFiIP { - return true - } - if !signalChangeSignificant(old.WiFiSignal, new.WiFiSignal) { - if old.WiFiSignal != new.WiFiSignal { - return false - } - } else if old.WiFiSignal != new.WiFiSignal { - return true - } - if old.IsConnecting != new.IsConnecting { - return true - } - if old.ConnectingSSID != new.ConnectingSSID { - return true - } - if old.LastError != new.LastError { - return true - } - if len(old.WiFiNetworks) != len(new.WiFiNetworks) { - return true - } - if len(old.WiFiDevices) != len(new.WiFiDevices) { - return true - } - if len(old.WiredConnections) != len(new.WiredConnections) { - return true - } - if len(old.EthernetDevices) != len(new.EthernetDevices) { - return true - } - - for i := range old.WiFiNetworks { - oldNet := &old.WiFiNetworks[i] - newNet := &new.WiFiNetworks[i] - if oldNet.SSID != newNet.SSID { - return true - } - if oldNet.Connected != newNet.Connected { - return true - } - if oldNet.Saved != newNet.Saved { - return true - } - if oldNet.Autoconnect != newNet.Autoconnect { - return true - } - } - - for i := range old.WiredConnections { - oldNet := &old.WiredConnections[i] - newNet := &new.WiredConnections[i] - if oldNet.ID != newNet.ID { - return true - } - if oldNet.IsActive != newNet.IsActive { - return true - } - } - - for i := range old.EthernetDevices { - oldDev := &old.EthernetDevices[i] - newDev := &new.EthernetDevices[i] - if oldDev.Name != newDev.Name { - return true - } - if oldDev.Connected != newDev.Connected { - return true - } - if oldDev.State != newDev.State { - return true - } - if oldDev.IP != newDev.IP { - return true - } - } - - // Check VPN profiles count - if len(old.VPNProfiles) != len(new.VPNProfiles) { - return true - } - - // Check active VPN connections count or state - if len(old.VPNActive) != len(new.VPNActive) { - return true - } - - // Check if any active VPN changed - for i := range old.VPNActive { - oldVPN := &old.VPNActive[i] - newVPN := &new.VPNActive[i] - if oldVPN.UUID != newVPN.UUID { - return true - } - if oldVPN.State != newVPN.State { - return true - } - } - - return false -} - -func (m *Manager) GetState() NetworkState { - return m.snapshotState() -} - -func (m *Manager) Subscribe(id string) chan NetworkState { - ch := make(chan NetworkState, 64) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) SubscribeCredentials(id string) chan CredentialPrompt { - ch := make(chan CredentialPrompt, 16) - m.credentialSubscribers.Store(id, ch) - return ch -} - -func (m *Manager) UnsubscribeCredentials(id string) { - if ch, ok := m.credentialSubscribers.LoadAndDelete(id); ok { - close(ch) - } -} - -func (m *Manager) broadcastCredentialPrompt(prompt CredentialPrompt) { - m.credentialSubscribers.Range(func(key string, ch chan CredentialPrompt) bool { - select { - case ch <- prompt: - default: - } - return true - }) -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - - currentState := m.snapshotState() - - if m.lastNotifiedState != nil && !stateChangedMeaningfully(m.lastNotifiedState, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan NetworkState) bool { - select { - case ch <- currentState: - default: - } - return true - }) - - stateCopy := currentState - m.lastNotifiedState = &stateCopy - pending = false - } - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) SetPromptBroker(broker PromptBroker) error { - return m.backend.SetPromptBroker(broker) -} - -func (m *Manager) SubmitCredentials(token string, secrets map[string]string, save bool) error { - return m.backend.SubmitCredentials(token, secrets, save) -} - -func (m *Manager) CancelCredentials(token string) error { - return m.backend.CancelCredentials(token) -} - -func (m *Manager) GetPromptBroker() PromptBroker { - return m.backend.GetPromptBroker() -} - -func (m *Manager) Close() { - close(m.stopChan) - m.notifierWg.Wait() - - if m.backend != nil { - m.backend.Close() - } - - m.subscribers.Range(func(key string, ch chan NetworkState) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) -} - -func (m *Manager) ScanWiFi() error { - return m.backend.ScanWiFi() -} - -func (m *Manager) GetWiFiNetworks() []WiFiNetwork { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - networks := make([]WiFiNetwork, len(m.state.WiFiNetworks)) - copy(networks, m.state.WiFiNetworks) - return networks -} - -func (m *Manager) GetNetworkInfo(ssid string) (*WiFiNetwork, error) { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - - for _, network := range m.state.WiFiNetworks { - if network.SSID == ssid { - return &network, nil - } - } - - return nil, fmt.Errorf("network not found: %s", ssid) -} - -func (m *Manager) GetNetworkInfoDetailed(ssid string) (*NetworkInfoResponse, error) { - return m.backend.GetWiFiNetworkDetails(ssid) -} - -func (m *Manager) GetNetworkQRCode(ssid string) ([2]string, error) { - content, err := m.backend.GetWiFiQRCodeContent(ssid) - if err != nil { - return [2]string{}, err - } - - qrc, err := qrcode.New(content) - if err != nil { - return [2]string{}, fmt.Errorf("failed to create QR code for `%s`: %w", ssid, err) - } - - pathThemed, pathNormal := qrCodePaths(ssid) - - wThemed, err := standard.New( - pathThemed, - standard.WithBuiltinImageEncoder(standard.PNG_FORMAT), - standard.WithBgTransparent(), - standard.WithFgColorRGBHex("#ffffff"), - ) - if err != nil { - return [2]string{}, fmt.Errorf("failed to create QR code writer: %w", err) - } - if err := qrc.Save(wThemed); err != nil { - return [2]string{}, fmt.Errorf("failed to save QR code for `%s`: %w", ssid, err) - } - - wNormal, err := standard.New(pathNormal, standard.WithBuiltinImageEncoder(standard.PNG_FORMAT)) - if err != nil { - return [2]string{}, fmt.Errorf("failed to create QR code writer: %w", err) - } - if err := qrc.Save(wNormal); err != nil { - return [2]string{}, fmt.Errorf("failed to save QR code for `%s`: %w", ssid, err) - } - - return [2]string{pathThemed, pathNormal}, nil -} - -func (m *Manager) ToggleWiFi() error { - enabled, err := m.backend.GetWiFiEnabled() - if err != nil { - return fmt.Errorf("failed to get WiFi state: %w", err) - } - - err = m.backend.SetWiFiEnabled(!enabled) - if err != nil { - return fmt.Errorf("failed to toggle WiFi: %w", err) - } - - return nil -} - -func (m *Manager) EnableWiFi() error { - err := m.backend.SetWiFiEnabled(true) - if err != nil { - return fmt.Errorf("failed to enable WiFi: %w", err) - } - - return nil -} - -func (m *Manager) DisableWiFi() error { - err := m.backend.SetWiFiEnabled(false) - if err != nil { - return fmt.Errorf("failed to disable WiFi: %w", err) - } - - return nil -} - -func (m *Manager) ConnectWiFi(req ConnectionRequest) error { - return m.backend.ConnectWiFi(req) -} - -func (m *Manager) DisconnectWiFi() error { - return m.backend.DisconnectWiFi() -} - -func (m *Manager) ForgetWiFiNetwork(ssid string) error { - return m.backend.ForgetWiFiNetwork(ssid) -} - -func (m *Manager) GetWiredConfigs() []WiredConnection { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - configs := make([]WiredConnection, len(m.state.WiredConnections)) - copy(configs, m.state.WiredConnections) - return configs -} - -func (m *Manager) GetWiredNetworkInfoDetailed(uuid string) (*WiredNetworkInfoResponse, error) { - return m.backend.GetWiredNetworkDetails(uuid) -} - -func (m *Manager) ConnectEthernet() error { - return m.backend.ConnectEthernet() -} - -func (m *Manager) DisconnectEthernet() error { - return m.backend.DisconnectEthernet() -} - -func (m *Manager) DisconnectEthernetDevice(device string) error { - return m.backend.DisconnectEthernetDevice(device) -} - -func (m *Manager) GetEthernetDevices() []EthernetDevice { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - devices := make([]EthernetDevice, len(m.state.EthernetDevices)) - copy(devices, m.state.EthernetDevices) - return devices -} - -func (m *Manager) activateConnection(uuid string) error { - return m.backend.ActivateWiredConnection(uuid) -} - -func (m *Manager) ListVPNProfiles() ([]VPNProfile, error) { - return m.backend.ListVPNProfiles() -} - -func (m *Manager) ListActiveVPN() ([]VPNActive, error) { - return m.backend.ListActiveVPN() -} - -func (m *Manager) ConnectVPN(uuidOrName string, singleActive bool) error { - return m.backend.ConnectVPN(uuidOrName, singleActive) -} - -func (m *Manager) DisconnectVPN(uuidOrName string) error { - return m.backend.DisconnectVPN(uuidOrName) -} - -func (m *Manager) DisconnectAllVPN() error { - return m.backend.DisconnectAllVPN() -} - -func (m *Manager) ClearVPNCredentials(uuidOrName string) error { - return m.backend.ClearVPNCredentials(uuidOrName) -} - -func (m *Manager) ListVPNPlugins() ([]VPNPlugin, error) { - return m.backend.ListVPNPlugins() -} - -func (m *Manager) ImportVPN(filePath string, name string) (*VPNImportResult, error) { - return m.backend.ImportVPN(filePath, name) -} - -func (m *Manager) GetVPNConfig(uuidOrName string) (*VPNConfig, error) { - return m.backend.GetVPNConfig(uuidOrName) -} - -func (m *Manager) UpdateVPNConfig(uuid string, updates map[string]any) error { - return m.backend.UpdateVPNConfig(uuid, updates) -} - -func (m *Manager) DeleteVPN(uuidOrName string) error { - return m.backend.DeleteVPN(uuidOrName) -} - -func (m *Manager) SetVPNCredentials(uuid, username, password string, save bool) error { - return m.backend.SetVPNCredentials(uuid, username, password, save) -} - -func (m *Manager) SetWiFiAutoconnect(ssid string, autoconnect bool) error { - return m.backend.SetWiFiAutoconnect(ssid, autoconnect) -} - -func (m *Manager) GetWiFiDevices() []WiFiDevice { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - devices := make([]WiFiDevice, len(m.state.WiFiDevices)) - copy(devices, m.state.WiFiDevices) - return devices -} - -func (m *Manager) ScanWiFiDevice(device string) error { - return m.backend.ScanWiFiDevice(device) -} - -func (m *Manager) DisconnectWiFiDevice(device string) error { - return m.backend.DisconnectWiFiDevice(device) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/manager_test.go deleted file mode 100644 index 79545c5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/manager_test.go +++ /dev/null @@ -1,191 +0,0 @@ -package network - -import ( - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetState(t *testing.T) { - state := &NetworkState{ - NetworkStatus: StatusWiFi, - WiFiSSID: "TestNetwork", - WiFiConnected: true, - } - - manager := &Manager{ - state: state, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.Equal(t, StatusWiFi, result.NetworkStatus) - assert.Equal(t, "TestNetwork", result.WiFiSSID) - assert.True(t, result.WiFiConnected) -} - -func TestManager_NotifySubscribers(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - }, - stateMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - manager.notifierWg.Add(1) - go manager.notifier() - - ch := make(chan NetworkState, 10) - manager.subscribers.Store("test-client", ch) - - manager.notifySubscribers() - - select { - case state := <-ch: - assert.Equal(t, StatusWiFi, state.NetworkStatus) - case <-time.After(200 * time.Millisecond): - t.Fatal("did not receive state update") - } - - close(manager.stopChan) - manager.notifierWg.Wait() -} - -func TestManager_NotifySubscribers_Debounce(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - }, - stateMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - manager.notifierWg.Add(1) - go manager.notifier() - - ch := make(chan NetworkState, 10) - manager.subscribers.Store("test-client", ch) - - manager.notifySubscribers() - manager.notifySubscribers() - manager.notifySubscribers() - - receivedCount := 0 - timeout := time.After(200 * time.Millisecond) - for { - select { - case <-ch: - receivedCount++ - case <-timeout: - assert.Equal(t, 1, receivedCount, "should receive exactly one debounced update") - close(manager.stopChan) - manager.notifierWg.Wait() - return - } - } -} - -func TestManager_Close(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - stateMutex: sync.RWMutex{}, - stopChan: make(chan struct{}), - } - - ch1 := make(chan NetworkState, 1) - ch2 := make(chan NetworkState, 1) - manager.subscribers.Store("client1", ch1) - manager.subscribers.Store("client2", ch2) - - manager.Close() - - select { - case <-manager.stopChan: - case <-time.After(100 * time.Millisecond): - t.Fatal("stopChan not closed") - } - - _, ok1 := <-ch1 - _, ok2 := <-ch2 - assert.False(t, ok1, "ch1 should be closed") - assert.False(t, ok2, "ch2 should be closed") - - count := 0 - manager.subscribers.Range(func(key string, ch chan NetworkState) bool { count++; return true }) - assert.Equal(t, 0, count) -} - -func TestManager_Subscribe(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - ch := manager.Subscribe("test-client") - assert.NotNil(t, ch) - assert.Equal(t, 64, cap(ch)) - - _, exists := manager.subscribers.Load("test-client") - assert.True(t, exists) -} - -func TestManager_Unsubscribe(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - ch := manager.Subscribe("test-client") - - manager.Unsubscribe("test-client") - - _, ok := <-ch - assert.False(t, ok) - - _, exists := manager.subscribers.Load("test-client") - assert.False(t, exists) -} - -func TestNewManager(t *testing.T) { - t.Run("attempts to create manager", func(t *testing.T) { - manager, err := NewManager() - if err != nil { - assert.Nil(t, manager) - } else { - assert.NotNil(t, manager) - assert.NotNil(t, manager.state) - assert.NotNil(t, manager.subscribers) - assert.NotNil(t, manager.stopChan) - - manager.Close() - } - }) -} - -func TestManager_GetState_ThreadSafe(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - NetworkStatus: StatusWiFi, - WiFiSSID: "TestNetwork", - }, - stateMutex: sync.RWMutex{}, - } - - done := make(chan bool) - for i := 0; i < 10; i++ { - go func() { - state := manager.GetState() - assert.Equal(t, StatusWiFi, state.NetworkStatus) - done <- true - }() - } - - for i := 0; i < 10; i++ { - select { - case <-done: - case <-time.After(1 * time.Second): - t.Fatal("timeout waiting for goroutines") - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/monitor.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/monitor.go deleted file mode 100644 index 1ae2e9d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/monitor.go +++ /dev/null @@ -1 +0,0 @@ -package network diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/priority.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/priority.go deleted file mode 100644 index 8ee7fdf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/priority.go +++ /dev/null @@ -1,187 +0,0 @@ -package network - -import ( - "fmt" - "os/exec" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - priorityHigh = int32(100) - priorityLow = int32(10) - priorityDefault = int32(0) - - metricPreferred = int64(100) - metricNonPreferred = int64(300) - metricDefault = int64(100) -) - -func (m *Manager) SetConnectionPreference(pref ConnectionPreference) error { - switch pref { - case PreferenceWiFi, PreferenceEthernet, PreferenceAuto: - default: - return fmt.Errorf("invalid preference: %s", pref) - } - - m.stateMutex.Lock() - m.state.Preference = pref - m.stateMutex.Unlock() - - if _, ok := m.backend.(*NetworkManagerBackend); !ok { - m.notifySubscribers() - return nil - } - - switch pref { - case PreferenceWiFi: - return m.prioritizeWiFi() - case PreferenceEthernet: - return m.prioritizeEthernet() - case PreferenceAuto: - return m.balancePriorities() - } - - return nil -} - -func (m *Manager) prioritizeWiFi() error { - if err := m.setConnectionPriority("802-11-wireless", priorityHigh, metricPreferred); err != nil { - log.Warnf("Failed to set WiFi priority: %v", err) - } - - if err := m.setConnectionPriority("802-3-ethernet", priorityLow, metricNonPreferred); err != nil { - log.Warnf("Failed to set Ethernet priority: %v", err) - } - - m.reapplyActiveConnections() - m.notifySubscribers() - return nil -} - -func (m *Manager) prioritizeEthernet() error { - if err := m.setConnectionPriority("802-3-ethernet", priorityHigh, metricPreferred); err != nil { - log.Warnf("Failed to set Ethernet priority: %v", err) - } - - if err := m.setConnectionPriority("802-11-wireless", priorityLow, metricNonPreferred); err != nil { - log.Warnf("Failed to set WiFi priority: %v", err) - } - - m.reapplyActiveConnections() - m.notifySubscribers() - return nil -} - -func (m *Manager) balancePriorities() error { - if err := m.setConnectionPriority("802-3-ethernet", priorityDefault, metricDefault); err != nil { - log.Warnf("Failed to reset Ethernet priority: %v", err) - } - - if err := m.setConnectionPriority("802-11-wireless", priorityDefault, metricDefault); err != nil { - log.Warnf("Failed to reset WiFi priority: %v", err) - } - - m.reapplyActiveConnections() - m.notifySubscribers() - return nil -} - -func (m *Manager) reapplyActiveConnections() { - m.stateMutex.RLock() - ethDev := m.state.EthernetDevice - wifiDev := m.state.WiFiDevice - m.stateMutex.RUnlock() - - if ethDev != "" { - exec.Command("nmcli", "dev", "reapply", ethDev).Run() - } - if wifiDev != "" { - exec.Command("nmcli", "dev", "reapply", wifiDev).Run() - } -} - -func (m *Manager) setConnectionPriority(connType string, autoconnectPriority int32, routeMetric int64) error { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("failed to connect to system bus: %w", err) - } - defer conn.Close() - - settingsObj := conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager/Settings") - - var connPaths []dbus.ObjectPath - if err := settingsObj.Call("org.freedesktop.NetworkManager.Settings.ListConnections", 0).Store(&connPaths); err != nil { - return fmt.Errorf("failed to list connections: %w", err) - } - - for _, connPath := range connPaths { - connObj := conn.Object("org.freedesktop.NetworkManager", connPath) - - var settings map[string]map[string]dbus.Variant - if err := connObj.Call("org.freedesktop.NetworkManager.Settings.Connection.GetSettings", 0).Store(&settings); err != nil { - continue - } - - connSection, ok := settings["connection"] - if !ok { - continue - } - - typeVariant, ok := connSection["type"] - if !ok { - continue - } - - cType, ok := typeVariant.Value().(string) - if !ok || cType != connType { - continue - } - - connName := "" - if idVariant, ok := connSection["id"]; ok { - connName, _ = idVariant.Value().(string) - } - - if connName == "" { - continue - } - - if err := exec.Command("nmcli", "con", "mod", connName, - "connection.autoconnect-priority", fmt.Sprintf("%d", autoconnectPriority), - "ipv4.route-metric", fmt.Sprintf("%d", routeMetric), - "ipv6.route-metric", fmt.Sprintf("%d", routeMetric)).Run(); err != nil { - log.Warnf("Failed to set priority for %s: %v", connName, err) - continue - } - - log.Infof("Updated %v: autoconnect-priority=%d, route-metric=%d", connName, autoconnectPriority, routeMetric) - } - - return nil -} - -func (m *Manager) GetConnectionPreference() ConnectionPreference { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return m.state.Preference -} - -func (m *Manager) WasRecentlyFailed(ssid string) bool { - nm, ok := m.backend.(*NetworkManagerBackend) - if !ok { - return false - } - - nm.failedMutex.RLock() - defer nm.failedMutex.RUnlock() - - if nm.lastFailedSSID != ssid { - return false - } - - elapsed := time.Now().Unix() - nm.lastFailedTime - return elapsed < 10 -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/priority_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/priority_test.go deleted file mode 100644 index c0c65e3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/priority_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_SetConnectionPreference(t *testing.T) { - t.Run("invalid preference", func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - Preference: PreferenceAuto, - }, - } - - err := manager.SetConnectionPreference(ConnectionPreference("invalid")) - assert.Error(t, err) - assert.Contains(t, err.Error(), "invalid preference") - }) -} - -func TestManager_GetConnectionPreference(t *testing.T) { - tests := []struct { - name string - preference ConnectionPreference - }{ - {"auto", PreferenceAuto}, - {"wifi", PreferenceWiFi}, - {"ethernet", PreferenceEthernet}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - Preference: tt.preference, - }, - } - - result := manager.GetConnectionPreference() - assert.Equal(t, tt.preference, result) - }) - } -} - -// Note: Full testing of priority operations would require mocking NetworkManager -// D-Bus interfaces. The tests above cover the basic logic and error handling. -// Integration tests would be needed for complete coverage of network connection -// priority updates and reactivation. diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/subscription_broker.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/subscription_broker.go deleted file mode 100644 index 1b150a2..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/subscription_broker.go +++ /dev/null @@ -1,125 +0,0 @@ -package network - -import ( - "context" - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type SubscriptionBroker struct { - pending syncmap.Map[string, chan PromptReply] - requests syncmap.Map[string, PromptRequest] - pathSettingToToken syncmap.Map[string, string] - broadcastPrompt func(CredentialPrompt) -} - -func NewSubscriptionBroker(broadcastPrompt func(CredentialPrompt)) PromptBroker { - return &SubscriptionBroker{ - broadcastPrompt: broadcastPrompt, - } -} - -func (b *SubscriptionBroker) Ask(ctx context.Context, req PromptRequest) (string, error) { - pathSettingKey := fmt.Sprintf("%s:%s", req.ConnectionPath, req.SettingName) - - if existingToken, alreadyPending := b.pathSettingToToken.Load(pathSettingKey); alreadyPending { - log.Infof("[SubscriptionBroker] Duplicate prompt for %s, returning existing token", pathSettingKey) - return existingToken, nil - } - - token, err := generateToken() - if err != nil { - return "", err - } - - replyChan := make(chan PromptReply, 1) - b.pending.Store(token, replyChan) - b.requests.Store(token, req) - b.pathSettingToToken.Store(pathSettingKey, token) - - if b.broadcastPrompt != nil { - prompt := CredentialPrompt{ - Token: token, - Name: req.Name, - SSID: req.SSID, - ConnType: req.ConnType, - VpnService: req.VpnService, - Setting: req.SettingName, - Fields: req.Fields, - FieldsInfo: req.FieldsInfo, - Hints: req.Hints, - Reason: req.Reason, - ConnectionId: req.ConnectionId, - ConnectionUuid: req.ConnectionUuid, - } - b.broadcastPrompt(prompt) - } - - return token, nil -} - -func (b *SubscriptionBroker) Wait(ctx context.Context, token string) (PromptReply, error) { - replyChan, exists := b.pending.Load(token) - if !exists { - return PromptReply{}, fmt.Errorf("unknown token: %s", token) - } - - select { - case <-ctx.Done(): - b.cleanup(token) - return PromptReply{}, errdefs.ErrSecretPromptTimeout - case reply := <-replyChan: - b.cleanup(token) - if reply.Cancel { - return reply, errdefs.ErrSecretPromptCancelled - } - return reply, nil - } -} - -func (b *SubscriptionBroker) Resolve(token string, reply PromptReply) error { - replyChan, exists := b.pending.Load(token) - if !exists { - log.Warnf("[SubscriptionBroker] Resolve: unknown or expired token: %s", token) - return fmt.Errorf("unknown or expired token: %s", token) - } - - select { - case replyChan <- reply: - return nil - default: - log.Warnf("[SubscriptionBroker] Resolve: failed to deliver reply for token %s (channel full or closed)", token) - return fmt.Errorf("failed to deliver reply for token: %s", token) - } -} - -func (b *SubscriptionBroker) cleanup(token string) { - if req, exists := b.requests.Load(token); exists { - pathSettingKey := fmt.Sprintf("%s:%s", req.ConnectionPath, req.SettingName) - b.pathSettingToToken.Delete(pathSettingKey) - } - - b.pending.Delete(token) - b.requests.Delete(token) -} - -func (b *SubscriptionBroker) Cancel(path string, setting string) error { - pathSettingKey := fmt.Sprintf("%s:%s", path, setting) - - token, exists := b.pathSettingToToken.Load(pathSettingKey) - if !exists { - log.Infof("[SubscriptionBroker] Cancel: no pending prompt for %s", pathSettingKey) - return nil - } - - log.Infof("[SubscriptionBroker] Cancelling prompt for %s (token=%s)", pathSettingKey, token) - - reply := PromptReply{ - Cancel: true, - } - - return b.Resolve(token, reply) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/testing.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/testing.go deleted file mode 100644 index a6c4a00..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/testing.go +++ /dev/null @@ -1,14 +0,0 @@ -package network - -// NewTestManager creates a Manager for testing with a provided backend -func NewTestManager(backend Backend, state *NetworkState) *Manager { - if state == nil { - state = &NetworkState{} - } - return &Manager{ - backend: backend, - state: state, - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/types.go deleted file mode 100644 index 93448cf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/types.go +++ /dev/null @@ -1,267 +0,0 @@ -package network - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/godbus/dbus/v5" -) - -type NetworkStatus string - -const ( - StatusDisconnected NetworkStatus = "disconnected" - StatusEthernet NetworkStatus = "ethernet" - StatusWiFi NetworkStatus = "wifi" - StatusVPN NetworkStatus = "vpn" -) - -type ConnectionPreference string - -const ( - PreferenceAuto ConnectionPreference = "auto" - PreferenceWiFi ConnectionPreference = "wifi" - PreferenceEthernet ConnectionPreference = "ethernet" -) - -type WiFiNetwork struct { - SSID string `json:"ssid"` - BSSID string `json:"bssid"` - Signal uint8 `json:"signal"` - Secured bool `json:"secured"` - Enterprise bool `json:"enterprise"` - Connected bool `json:"connected"` - Saved bool `json:"saved"` - Autoconnect bool `json:"autoconnect"` - Hidden bool `json:"hidden"` - Frequency uint32 `json:"frequency"` - Mode string `json:"mode"` - Rate uint32 `json:"rate"` - Channel uint32 `json:"channel"` - Device string `json:"device,omitempty"` -} - -type WiFiDevice struct { - Name string `json:"name"` - HwAddress string `json:"hwAddress"` - State string `json:"state"` - Connected bool `json:"connected"` - SSID string `json:"ssid,omitempty"` - BSSID string `json:"bssid,omitempty"` - Signal uint8 `json:"signal,omitempty"` - IP string `json:"ip,omitempty"` - Networks []WiFiNetwork `json:"networks"` -} - -type EthernetDevice struct { - Name string `json:"name"` - HwAddress string `json:"hwAddress"` - State string `json:"state"` - Connected bool `json:"connected"` - IP string `json:"ip,omitempty"` - Speed uint32 `json:"speed,omitempty"` - Driver string `json:"driver,omitempty"` -} - -type VPNProfile struct { - Name string `json:"name"` - UUID string `json:"uuid"` - Type string `json:"type"` - ServiceType string `json:"serviceType"` - RemoteHost string `json:"remoteHost,omitempty"` - Username string `json:"username,omitempty"` - Autoconnect bool `json:"autoconnect"` - Data map[string]string `json:"data,omitempty"` -} - -type VPNActive struct { - Name string `json:"name"` - UUID string `json:"uuid"` - Device string `json:"device,omitempty"` - State string `json:"state,omitempty"` - Type string `json:"type"` - Plugin string `json:"serviceType"` - IP string `json:"ip,omitempty"` - Gateway string `json:"gateway,omitempty"` - RemoteHost string `json:"remoteHost,omitempty"` - Username string `json:"username,omitempty"` - MTU uint32 `json:"mtu,omitempty"` - Data map[string]string `json:"data,omitempty"` -} - -type VPNState struct { - Profiles []VPNProfile `json:"profiles"` - Active []VPNActive `json:"activeConnections"` -} - -type NetworkState struct { - Backend string `json:"backend"` - NetworkStatus NetworkStatus `json:"networkStatus"` - Preference ConnectionPreference `json:"preference"` - EthernetIP string `json:"ethernetIP"` - EthernetDevice string `json:"ethernetDevice"` - EthernetConnected bool `json:"ethernetConnected"` - EthernetConnectionUuid string `json:"ethernetConnectionUuid"` - EthernetDevices []EthernetDevice `json:"ethernetDevices"` - WiFiIP string `json:"wifiIP"` - WiFiDevice string `json:"wifiDevice"` - WiFiConnected bool `json:"wifiConnected"` - WiFiEnabled bool `json:"wifiEnabled"` - WiFiSSID string `json:"wifiSSID"` - WiFiBSSID string `json:"wifiBSSID"` - WiFiSignal uint8 `json:"wifiSignal"` - WiFiNetworks []WiFiNetwork `json:"wifiNetworks"` - WiFiDevices []WiFiDevice `json:"wifiDevices"` - WiredConnections []WiredConnection `json:"wiredConnections"` - VPNProfiles []VPNProfile `json:"vpnProfiles"` - VPNActive []VPNActive `json:"vpnActive"` - IsConnecting bool `json:"isConnecting"` - ConnectingSSID string `json:"connectingSSID"` - ConnectingDevice string `json:"connectingDevice,omitempty"` - LastError string `json:"lastError"` -} - -type ConnectionRequest struct { - SSID string `json:"ssid"` - Password string `json:"password,omitempty"` - Username string `json:"username,omitempty"` - AnonymousIdentity string `json:"anonymousIdentity,omitempty"` - DomainSuffixMatch string `json:"domainSuffixMatch,omitempty"` - Interactive bool `json:"interactive,omitempty"` - Hidden bool `json:"hidden,omitempty"` - Device string `json:"device,omitempty"` - EAPMethod string `json:"eapMethod,omitempty"` - Phase2Auth string `json:"phase2Auth,omitempty"` - CACertPath string `json:"caCertPath,omitempty"` - ClientCertPath string `json:"clientCertPath,omitempty"` - PrivateKeyPath string `json:"privateKeyPath,omitempty"` - UseSystemCACerts *bool `json:"useSystemCACerts,omitempty"` -} - -type WiredConnection struct { - Path dbus.ObjectPath `json:"path"` - ID string `json:"id"` - UUID string `json:"uuid"` - Type string `json:"type"` - IsActive bool `json:"isActive"` -} - -type PriorityUpdate struct { - Preference ConnectionPreference `json:"preference"` -} - -type Manager struct { - backend Backend - state *NetworkState - stateMutex sync.RWMutex - subscribers syncmap.Map[string, chan NetworkState] - stopChan chan struct{} - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotifiedState *NetworkState - credentialSubscribers syncmap.Map[string, chan CredentialPrompt] -} - -type EventType string - -const ( - EventStateChanged EventType = "state_changed" - EventNetworksUpdated EventType = "networks_updated" - EventConnecting EventType = "connecting" - EventConnected EventType = "connected" - EventDisconnected EventType = "disconnected" - EventError EventType = "error" -) - -type NetworkEvent struct { - Type EventType `json:"type"` - Data NetworkState `json:"data"` -} - -type PromptRequest struct { - Name string `json:"name"` - SSID string `json:"ssid"` - ConnType string `json:"connType"` - VpnService string `json:"vpnService"` - SettingName string `json:"setting"` - Fields []string `json:"fields"` - FieldsInfo []FieldInfo `json:"fieldsInfo"` - Hints []string `json:"hints"` - Reason string `json:"reason"` - ConnectionId string `json:"connectionId"` - ConnectionUuid string `json:"connectionUuid"` - ConnectionPath string `json:"connectionPath"` -} - -type PromptReply struct { - Secrets map[string]string `json:"secrets"` - Save bool `json:"save"` - Cancel bool `json:"cancel"` -} - -type FieldInfo struct { - Name string `json:"name"` - Label string `json:"label"` - IsSecret bool `json:"isSecret"` -} - -type CredentialPrompt struct { - Token string `json:"token"` - Name string `json:"name"` - SSID string `json:"ssid"` - ConnType string `json:"connType"` - VpnService string `json:"vpnService"` - Setting string `json:"setting"` - Fields []string `json:"fields"` - FieldsInfo []FieldInfo `json:"fieldsInfo"` - Hints []string `json:"hints"` - Reason string `json:"reason"` - ConnectionId string `json:"connectionId"` - ConnectionUuid string `json:"connectionUuid"` -} - -type NetworkInfoResponse struct { - SSID string `json:"ssid"` - Bands []WiFiNetwork `json:"bands"` -} - -type WiredNetworkInfoResponse struct { - UUID string `json:"uuid"` - IFace string `json:"iface"` - Driver string `json:"driver"` - HwAddr string `json:"hwAddr"` - Speed string `json:"speed"` - IPv4 WiredIPConfig `json:"IPv4s"` - IPv6 WiredIPConfig `json:"IPv6s"` -} - -type WiredIPConfig struct { - IPs []string `json:"ips"` - Gateway string `json:"gateway"` - DNS string `json:"dns"` -} - -type VPNPlugin struct { - Name string `json:"name"` - ServiceType string `json:"serviceType"` - Program string `json:"program,omitempty"` - Supports []string `json:"supports,omitempty"` - FileExtensions []string `json:"fileExtensions"` -} - -type VPNConfig struct { - UUID string `json:"uuid"` - Name string `json:"name"` - Type string `json:"type"` - ServiceType string `json:"serviceType,omitempty"` - Autoconnect bool `json:"autoconnect"` - Data map[string]string `json:"data,omitempty"` -} - -type VPNImportResult struct { - Success bool `json:"success"` - UUID string `json:"uuid,omitempty"` - Name string `json:"name,omitempty"` - ServiceType string `json:"serviceType,omitempty"` - Error string `json:"error,omitempty"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/types_test.go deleted file mode 100644 index 1c3e0bf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/types_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package network - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNetworkStatus_Constants(t *testing.T) { - assert.Equal(t, NetworkStatus("disconnected"), StatusDisconnected) - assert.Equal(t, NetworkStatus("ethernet"), StatusEthernet) - assert.Equal(t, NetworkStatus("wifi"), StatusWiFi) -} - -func TestConnectionPreference_Constants(t *testing.T) { - assert.Equal(t, ConnectionPreference("auto"), PreferenceAuto) - assert.Equal(t, ConnectionPreference("wifi"), PreferenceWiFi) - assert.Equal(t, ConnectionPreference("ethernet"), PreferenceEthernet) -} - -func TestEventType_Constants(t *testing.T) { - assert.Equal(t, EventType("state_changed"), EventStateChanged) - assert.Equal(t, EventType("networks_updated"), EventNetworksUpdated) - assert.Equal(t, EventType("connecting"), EventConnecting) - assert.Equal(t, EventType("connected"), EventConnected) - assert.Equal(t, EventType("disconnected"), EventDisconnected) - assert.Equal(t, EventType("error"), EventError) -} - -func TestWiFiNetwork_JSON(t *testing.T) { - network := WiFiNetwork{ - SSID: "TestNetwork", - BSSID: "00:11:22:33:44:55", - Signal: 85, - Secured: true, - Enterprise: false, - Connected: true, - Saved: true, - Frequency: 2437, - Mode: "infrastructure", - Rate: 300, - Channel: 6, - } - - data, err := json.Marshal(network) - require.NoError(t, err) - - var decoded WiFiNetwork - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, network.SSID, decoded.SSID) - assert.Equal(t, network.BSSID, decoded.BSSID) - assert.Equal(t, network.Signal, decoded.Signal) - assert.Equal(t, network.Secured, decoded.Secured) - assert.Equal(t, network.Enterprise, decoded.Enterprise) - assert.Equal(t, network.Connected, decoded.Connected) - assert.Equal(t, network.Saved, decoded.Saved) - assert.Equal(t, network.Frequency, decoded.Frequency) - assert.Equal(t, network.Mode, decoded.Mode) - assert.Equal(t, network.Rate, decoded.Rate) - assert.Equal(t, network.Channel, decoded.Channel) -} - -func TestNetworkState_JSON(t *testing.T) { - state := NetworkState{ - NetworkStatus: StatusWiFi, - Preference: PreferenceAuto, - EthernetIP: "192.168.1.100", - EthernetDevice: "eth0", - EthernetConnected: false, - WiFiIP: "192.168.1.101", - WiFiDevice: "wlan0", - WiFiConnected: true, - WiFiEnabled: true, - WiFiSSID: "TestNetwork", - WiFiBSSID: "00:11:22:33:44:55", - WiFiSignal: 85, - WiFiNetworks: []WiFiNetwork{ - {SSID: "Network1", Signal: 90}, - {SSID: "Network2", Signal: 60}, - }, - IsConnecting: false, - ConnectingSSID: "", - LastError: "", - } - - data, err := json.Marshal(state) - require.NoError(t, err) - - var decoded NetworkState - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, state.NetworkStatus, decoded.NetworkStatus) - assert.Equal(t, state.Preference, decoded.Preference) - assert.Equal(t, state.WiFiIP, decoded.WiFiIP) - assert.Equal(t, state.WiFiSSID, decoded.WiFiSSID) - assert.Equal(t, len(state.WiFiNetworks), len(decoded.WiFiNetworks)) -} - -func TestConnectionRequest_JSON(t *testing.T) { - t.Run("with password", func(t *testing.T) { - req := ConnectionRequest{ - SSID: "TestNetwork", - Password: "testpass123", - } - - data, err := json.Marshal(req) - require.NoError(t, err) - - var decoded ConnectionRequest - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, req.SSID, decoded.SSID) - assert.Equal(t, req.Password, decoded.Password) - assert.Empty(t, decoded.Username) - }) - - t.Run("with username and password (enterprise)", func(t *testing.T) { - req := ConnectionRequest{ - SSID: "EnterpriseNetwork", - Password: "testpass123", - Username: "testuser", - } - - data, err := json.Marshal(req) - require.NoError(t, err) - - var decoded ConnectionRequest - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, req.SSID, decoded.SSID) - assert.Equal(t, req.Password, decoded.Password) - assert.Equal(t, req.Username, decoded.Username) - }) -} - -func TestPriorityUpdate_JSON(t *testing.T) { - update := PriorityUpdate{ - Preference: PreferenceWiFi, - } - - data, err := json.Marshal(update) - require.NoError(t, err) - - var decoded PriorityUpdate - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, update.Preference, decoded.Preference) -} - -func TestNetworkEvent_JSON(t *testing.T) { - event := NetworkEvent{ - Type: EventStateChanged, - Data: NetworkState{ - NetworkStatus: StatusWiFi, - WiFiSSID: "TestNetwork", - WiFiConnected: true, - }, - } - - data, err := json.Marshal(event) - require.NoError(t, err) - - var decoded NetworkEvent - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, event.Type, decoded.Type) - assert.Equal(t, event.Data.NetworkStatus, decoded.Data.NetworkStatus) - assert.Equal(t, event.Data.WiFiSSID, decoded.Data.WiFiSSID) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wifi_qrcode.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wifi_qrcode.go deleted file mode 100644 index 680b6d1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wifi_qrcode.go +++ /dev/null @@ -1,59 +0,0 @@ -package network - -import ( - "fmt" - "path/filepath" - "regexp" - "strings" -) - -const qrCodeTmpPrefix = "/tmp/dank-wifi-qrcode-" - -func FormatWiFiQRString(securityType, ssid, password string) string { - return fmt.Sprintf("WIFI:T:%s;S:%s;P:%s;;", securityType, ssid, password) -} - -func qrCodePaths(ssid string) (themed, normal string) { - safe := sanitizeSSIDForPath(ssid) - themed = fmt.Sprintf("%s%s-themed.png", qrCodeTmpPrefix, safe) - normal = fmt.Sprintf("%s%s-normal.png", qrCodeTmpPrefix, safe) - return -} - -func isValidQRCodePath(path string) bool { - clean := filepath.Clean(path) - return strings.HasPrefix(clean, qrCodeTmpPrefix) && strings.HasSuffix(clean, ".png") -} - -var safePathChar = regexp.MustCompile(`[^a-zA-Z0-9_-]`) - -func sanitizeSSIDForPath(ssid string) string { - return safePathChar.ReplaceAllString(ssid, "_") -} - -var iwdVerbatimSSID = regexp.MustCompile(`^[a-zA-Z0-9 _-]+$`) - -func iwdConfigPath(ssid string) string { - switch { - case iwdVerbatimSSID.MatchString(ssid): - return fmt.Sprintf("/var/lib/iwd/%s.psk", ssid) - default: - return fmt.Sprintf("/var/lib/iwd/=%x.psk", []byte(ssid)) - } -} - -func parseIWDPassphrase(data string) (string, error) { - inSecurity := false - for _, line := range strings.Split(data, "\n") { - line = strings.TrimSpace(line) - switch { - case line == "[Security]": - inSecurity = true - case strings.HasPrefix(line, "["): - inSecurity = false - case inSecurity && strings.HasPrefix(line, "Passphrase="): - return strings.TrimPrefix(line, "Passphrase="), nil - } - } - return "", fmt.Errorf("no passphrase found in iwd config") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wifi_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wifi_test.go deleted file mode 100644 index 2256b60..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wifi_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestFrequencyToChannel(t *testing.T) { - tests := []struct { - name string - frequency uint32 - channel uint32 - }{ - {"2.4 GHz channel 1", 2412, 1}, - {"2.4 GHz channel 6", 2437, 6}, - {"2.4 GHz channel 11", 2462, 11}, - {"2.4 GHz channel 14", 2484, 14}, - {"5 GHz channel 36", 5180, 36}, - {"5 GHz channel 40", 5200, 40}, - {"5 GHz channel 165", 5825, 165}, - {"6 GHz channel 1", 5955, 1}, - {"6 GHz channel 233", 7115, 233}, - {"Unknown frequency", 1000, 0}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := frequencyToChannel(tt.frequency) - assert.Equal(t, tt.channel, result) - }) - } -} - -func TestSortWiFiNetworks(t *testing.T) { - t.Run("connected network comes first", func(t *testing.T) { - networks := []WiFiNetwork{ - {SSID: "Network1", Signal: 90, Connected: false}, - {SSID: "Network2", Signal: 80, Connected: true}, - {SSID: "Network3", Signal: 70, Connected: false}, - } - - sortWiFiNetworks(networks) - - assert.Equal(t, "Network2", networks[0].SSID) - assert.True(t, networks[0].Connected) - }) - - t.Run("sorts by signal strength", func(t *testing.T) { - networks := []WiFiNetwork{ - {SSID: "Weak", Signal: 40, Secured: true}, - {SSID: "Strong", Signal: 90, Secured: true}, - {SSID: "Medium", Signal: 60, Secured: true}, - } - - sortWiFiNetworks(networks) - - assert.Equal(t, "Strong", networks[0].SSID) - assert.Equal(t, "Medium", networks[1].SSID) - assert.Equal(t, "Weak", networks[2].SSID) - }) - - t.Run("prioritizes open networks with good signal", func(t *testing.T) { - networks := []WiFiNetwork{ - {SSID: "SecureWeak", Signal: 40, Secured: true}, - {SSID: "OpenStrong", Signal: 60, Secured: false}, - {SSID: "SecureStrong", Signal: 90, Secured: true}, - } - - sortWiFiNetworks(networks) - - assert.Equal(t, "OpenStrong", networks[0].SSID) - - openIdx := -1 - weakSecureIdx := -1 - for i, n := range networks { - if n.SSID == "OpenStrong" { - openIdx = i - } - if n.SSID == "SecureWeak" { - weakSecureIdx = i - } - } - assert.Less(t, openIdx, weakSecureIdx, "OpenStrong should come before SecureWeak") - }) - - t.Run("prioritizes saved networks after connected", func(t *testing.T) { - networks := []WiFiNetwork{ - {SSID: "UnsavedStrong", Signal: 95, Saved: false}, - {SSID: "SavedMedium", Signal: 60, Saved: true}, - {SSID: "SavedWeak", Signal: 50, Saved: true}, - {SSID: "UnsavedMedium", Signal: 70, Saved: false}, - } - - sortWiFiNetworks(networks) - - assert.Equal(t, "SavedMedium", networks[0].SSID) - assert.Equal(t, "SavedWeak", networks[1].SSID) - assert.Equal(t, "UnsavedStrong", networks[2].SSID) - assert.Equal(t, "UnsavedMedium", networks[3].SSID) - }) -} - -func TestManager_GetWiFiNetworks(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - WiFiNetworks: []WiFiNetwork{ - {SSID: "Network1", Signal: 90}, - {SSID: "Network2", Signal: 80}, - }, - }, - } - - networks := manager.GetWiFiNetworks() - - assert.Len(t, networks, 2) - assert.Equal(t, "Network1", networks[0].SSID) - assert.Equal(t, "Network2", networks[1].SSID) - - networks[0].SSID = "Modified" - assert.Equal(t, "Network1", manager.state.WiFiNetworks[0].SSID) -} - -func TestManager_GetNetworkInfo(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - WiFiNetworks: []WiFiNetwork{ - {SSID: "Network1", Signal: 90, BSSID: "00:11:22:33:44:55"}, - {SSID: "Network2", Signal: 80, BSSID: "AA:BB:CC:DD:EE:FF"}, - }, - }, - } - - t.Run("finds existing network", func(t *testing.T) { - network, err := manager.GetNetworkInfo("Network1") - assert.NoError(t, err) - assert.NotNil(t, network) - assert.Equal(t, "Network1", network.SSID) - assert.Equal(t, uint8(90), network.Signal) - }) - - t.Run("returns error for non-existent network", func(t *testing.T) { - network, err := manager.GetNetworkInfo("NonExistent") - assert.Error(t, err) - assert.Nil(t, network) - assert.Contains(t, err.Error(), "network not found") - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wired_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wired_test.go deleted file mode 100644 index 5c346e7..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/network/wired_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package network - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetWiredConfigs(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - EthernetConnected: true, - WiredConnections: []WiredConnection{ - {ID: "Test", IsActive: true}, - }, - }, - } - - configs := manager.GetWiredConfigs() - - assert.Len(t, configs, 1) - assert.Equal(t, "Test", configs[0].ID) -} - -func TestManager_GetEthernetDevices(t *testing.T) { - manager := &Manager{ - state: &NetworkState{ - EthernetDevices: []EthernetDevice{ - {Name: "enp0s3", Connected: true, IP: "192.168.1.100"}, - {Name: "enp0s8", Connected: false}, - }, - }, - } - - devices := manager.GetEthernetDevices() - - assert.Len(t, devices, 2) - assert.Equal(t, "enp0s3", devices[0].Name) - assert.True(t, devices[0].Connected) - assert.Equal(t, "enp0s8", devices[1].Name) - assert.False(t, devices[1].Connected) -} - -func TestManager_GetEthernetDevices_Empty(t *testing.T) { - manager := &Manager{ - state: &NetworkState{}, - } - - devices := manager.GetEthernetDevices() - assert.Empty(t, devices) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/params/params.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/params/params.go deleted file mode 100644 index 1f592c6..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/params/params.go +++ /dev/null @@ -1,113 +0,0 @@ -package params - -import "fmt" - -func Get[T any](params map[string]any, key string) (T, error) { - val, ok := params[key].(T) - if !ok { - var zero T - return zero, fmt.Errorf("missing or invalid '%s' parameter", key) - } - return val, nil -} - -func GetOpt[T any](params map[string]any, key string, def T) T { - if val, ok := params[key].(T); ok { - return val - } - return def -} - -func String(params map[string]any, key string) (string, error) { - return Get[string](params, key) -} - -func StringNonEmpty(params map[string]any, key string) (string, error) { - val, err := Get[string](params, key) - if err != nil || val == "" { - return "", fmt.Errorf("missing or invalid '%s' parameter", key) - } - return val, nil -} - -func StringOpt(params map[string]any, key string, def string) string { - return GetOpt(params, key, def) -} - -func Int(params map[string]any, key string) (int, error) { - val, err := Get[float64](params, key) - if err != nil { - return 0, err - } - return int(val), nil -} - -func IntOpt(params map[string]any, key string, def int) int { - if val, ok := params[key].(float64); ok { - return int(val) - } - return def -} - -func Float(params map[string]any, key string) (float64, error) { - return Get[float64](params, key) -} - -func FloatOpt(params map[string]any, key string, def float64) float64 { - return GetOpt(params, key, def) -} - -func Bool(params map[string]any, key string) (bool, error) { - return Get[bool](params, key) -} - -func BoolOpt(params map[string]any, key string, def bool) bool { - return GetOpt(params, key, def) -} - -func StringMap(params map[string]any, key string) (map[string]string, error) { - rawMap, err := Get[map[string]any](params, key) - if err != nil { - return nil, err - } - result := make(map[string]string) - for k, v := range rawMap { - if str, ok := v.(string); ok { - result[k] = str - } - } - return result, nil -} - -func StringMapOpt(params map[string]any, key string) map[string]string { - rawMap, ok := params[key].(map[string]any) - if !ok { - return nil - } - result := make(map[string]string) - for k, v := range rawMap { - if str, ok := v.(string); ok { - result[k] = str - } - } - return result -} - -func Any(params map[string]any, key string) (any, bool) { - val, ok := params[key] - return val, ok -} - -func AnyMap(params map[string]any, key string) (map[string]any, bool) { - val, ok := params[key].(map[string]any) - return val, ok -} - -func StringAlt(params map[string]any, keys ...string) (string, bool) { - for _, key := range keys { - if val, ok := params[key].(string); ok { - return val, true - } - } - return "", false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/params/params_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/params/params_test.go deleted file mode 100644 index 99847ff..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/params/params_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package params - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGet(t *testing.T) { - p := map[string]any{"key": "value"} - val, err := Get[string](p, "key") - assert.NoError(t, err) - assert.Equal(t, "value", val) - - _, err = Get[string](p, "missing") - assert.Error(t, err) - - _, err = Get[int](p, "key") - assert.Error(t, err) -} - -func TestGetOpt(t *testing.T) { - p := map[string]any{"key": "value"} - assert.Equal(t, "value", GetOpt(p, "key", "default")) - assert.Equal(t, "default", GetOpt(p, "missing", "default")) -} - -func TestString(t *testing.T) { - p := map[string]any{"s": "hello", "n": 123} - val, err := String(p, "s") - assert.NoError(t, err) - assert.Equal(t, "hello", val) - - _, err = String(p, "n") - assert.Error(t, err) -} - -func TestStringNonEmpty(t *testing.T) { - p := map[string]any{"s": "hello", "empty": ""} - val, err := StringNonEmpty(p, "s") - assert.NoError(t, err) - assert.Equal(t, "hello", val) - - _, err = StringNonEmpty(p, "empty") - assert.Error(t, err) - - _, err = StringNonEmpty(p, "missing") - assert.Error(t, err) -} - -func TestStringOpt(t *testing.T) { - p := map[string]any{"s": "hello"} - assert.Equal(t, "hello", StringOpt(p, "s", "default")) - assert.Equal(t, "default", StringOpt(p, "missing", "default")) -} - -func TestInt(t *testing.T) { - p := map[string]any{"n": float64(42), "s": "str"} - val, err := Int(p, "n") - assert.NoError(t, err) - assert.Equal(t, 42, val) - - _, err = Int(p, "s") - assert.Error(t, err) -} - -func TestIntOpt(t *testing.T) { - p := map[string]any{"n": float64(42)} - assert.Equal(t, 42, IntOpt(p, "n", 0)) - assert.Equal(t, 99, IntOpt(p, "missing", 99)) -} - -func TestFloat(t *testing.T) { - p := map[string]any{"f": 3.14, "s": "str"} - val, err := Float(p, "f") - assert.NoError(t, err) - assert.Equal(t, 3.14, val) - - _, err = Float(p, "s") - assert.Error(t, err) -} - -func TestFloatOpt(t *testing.T) { - p := map[string]any{"f": 3.14} - assert.Equal(t, 3.14, FloatOpt(p, "f", 0)) - assert.Equal(t, 1.0, FloatOpt(p, "missing", 1.0)) -} - -func TestBool(t *testing.T) { - p := map[string]any{"b": true, "s": "str"} - val, err := Bool(p, "b") - assert.NoError(t, err) - assert.True(t, val) - - _, err = Bool(p, "s") - assert.Error(t, err) -} - -func TestBoolOpt(t *testing.T) { - p := map[string]any{"b": true} - assert.True(t, BoolOpt(p, "b", false)) - assert.True(t, BoolOpt(p, "missing", true)) -} - -func TestStringMap(t *testing.T) { - p := map[string]any{ - "m": map[string]any{"a": "1", "b": "2", "c": 3}, - } - val, err := StringMap(p, "m") - assert.NoError(t, err) - assert.Equal(t, map[string]string{"a": "1", "b": "2"}, val) - - _, err = StringMap(p, "missing") - assert.Error(t, err) -} - -func TestStringMapOpt(t *testing.T) { - p := map[string]any{ - "m": map[string]any{"a": "1"}, - } - assert.Equal(t, map[string]string{"a": "1"}, StringMapOpt(p, "m")) - assert.Nil(t, StringMapOpt(p, "missing")) -} - -func TestAny(t *testing.T) { - p := map[string]any{"k": 123} - val, ok := Any(p, "k") - assert.True(t, ok) - assert.Equal(t, 123, val) - - _, ok = Any(p, "missing") - assert.False(t, ok) -} - -func TestAnyMap(t *testing.T) { - inner := map[string]any{"nested": true} - p := map[string]any{"m": inner} - val, ok := AnyMap(p, "m") - assert.True(t, ok) - assert.Equal(t, inner, val) - - _, ok = AnyMap(p, "missing") - assert.False(t, ok) -} - -func TestStringAlt(t *testing.T) { - p := map[string]any{"b": "found"} - val, ok := StringAlt(p, "a", "b", "c") - assert.True(t, ok) - assert.Equal(t, "found", val) - - _, ok = StringAlt(p, "x", "y") - assert.False(t, ok) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/handlers.go deleted file mode 100644 index 33f6564..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/handlers.go +++ /dev/null @@ -1,27 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleRequest(conn net.Conn, req models.Request) { - switch req.Method { - case "plugins.list": - HandleList(conn, req) - case "plugins.listInstalled": - HandleListInstalled(conn, req) - case "plugins.install": - HandleInstall(conn, req) - case "plugins.uninstall": - HandleUninstall(conn, req) - case "plugins.update": - HandleUpdate(conn, req) - case "plugins.search": - HandleSearch(conn, req) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/handlers_test.go deleted file mode 100644 index 70202e3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/handlers_test.go +++ /dev/null @@ -1,196 +0,0 @@ -package plugins - -import ( - "encoding/json" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/net" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestHandleList(t *testing.T) { - conn := net.NewMockConn(t) - conn.EXPECT().Write(mock.Anything).Return(0, nil).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.list", - Params: map[string]any{}, - } - - HandleList(conn, req) -} - -func TestHandleListInstalled(t *testing.T) { - conn := net.NewMockConn(t) - conn.EXPECT().Write(mock.Anything).Return(0, nil).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.listInstalled", - Params: map[string]any{}, - } - - HandleListInstalled(conn, req) -} - -func TestHandleInstallMissingName(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.install", - Params: map[string]any{}, - } - - HandleInstall(conn, req) - - var resp models.Response[SuccessResult] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) - assert.Contains(t, resp.Error, "missing or invalid 'name' parameter") -} - -func TestHandleInstallInvalidName(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.install", - Params: map[string]any{ - "name": 123, - }, - } - - HandleInstall(conn, req) - - var resp models.Response[SuccessResult] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) -} - -func TestHandleUninstallMissingName(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.uninstall", - Params: map[string]any{}, - } - - HandleUninstall(conn, req) - - var resp models.Response[SuccessResult] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) -} - -func TestHandleUpdateMissingName(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.update", - Params: map[string]any{}, - } - - HandleUpdate(conn, req) - - var resp models.Response[SuccessResult] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) -} - -func TestHandleSearchMissingQuery(t *testing.T) { - conn := net.NewMockConn(t) - var written []byte - conn.EXPECT().Write(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - written = b - return len(b), nil - }).Maybe() - - req := models.Request{ - ID: 123, - Method: "plugins.search", - Params: map[string]any{}, - } - - HandleSearch(conn, req) - - var resp models.Response[[]PluginInfo] - err := json.Unmarshal(written, &resp) - assert.NoError(t, err) - assert.NotEmpty(t, resp.Error) -} - -func TestSortPluginInfoByFirstParty(t *testing.T) { - plugins := []PluginInfo{ - {Name: "third-party", Repo: "https://github.com/other/test"}, - {Name: "first-party", Repo: "https://github.com/AvengeMedia/test"}, - } - - SortPluginInfoByFirstParty(plugins) - - assert.Equal(t, "first-party", plugins[0].Name) - assert.Equal(t, "third-party", plugins[1].Name) -} - -func TestPluginInfoJSON(t *testing.T) { - info := PluginInfo{ - Name: "test", - Description: "test description", - Installed: true, - FirstParty: true, - } - - data, err := json.Marshal(info) - assert.NoError(t, err) - - var unmarshaled PluginInfo - err = json.Unmarshal(data, &unmarshaled) - assert.NoError(t, err) - assert.Equal(t, info.Name, unmarshaled.Name) - assert.Equal(t, info.Installed, unmarshaled.Installed) -} - -func TestSuccessResult(t *testing.T) { - result := SuccessResult{ - Success: true, - Message: "test message", - } - - data, err := json.Marshal(result) - assert.NoError(t, err) - - var unmarshaled SuccessResult - err = json.Unmarshal(data, &unmarshaled) - assert.NoError(t, err) - assert.True(t, unmarshaled.Success) - assert.Equal(t, "test message", unmarshaled.Message) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/install.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/install.go deleted file mode 100644 index 2c13a0e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/install.go +++ /dev/null @@ -1,69 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleInstall(conn net.Conn, req models.Request) { - idOrName, ok := models.Get[string](req, "name") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - // First, try to find by ID (preferred method) - var plugin *plugins.Plugin - for _, p := range pluginList { - if p.ID == idOrName { - plugin = &p - break - } - } - - // Fallback to name for backward compatibility - if plugin == nil { - for _, p := range pluginList { - if p.Name == idOrName { - plugin = &p - break - } - } - } - - if plugin == nil { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not found: %s", idOrName)) - return - } - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - if err := manager.Install(*plugin); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to install plugin: %v", err)) - return - } - - models.Respond(conn, req.ID, SuccessResult{ - Success: true, - Message: fmt.Sprintf("plugin installed: %s", plugin.Name), - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/list.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/list.go deleted file mode 100644 index e44d034..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/list.go +++ /dev/null @@ -1,53 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleList(conn net.Conn, req models.Request) { - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - result := make([]PluginInfo, len(pluginList)) - for i, p := range pluginList { - installed, _ := manager.IsInstalled(p) - result[i] = PluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - Installed: installed, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - Featured: p.Featured, - RequiresDMS: p.RequiresDMS, - } - } - - models.Respond(conn, req.ID, result) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/list_installed.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/list_installed.go deleted file mode 100644 index b771b0e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/list_installed.go +++ /dev/null @@ -1,77 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleListInstalled(conn net.Conn, req models.Request) { - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - installedNames, err := manager.ListInstalled() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list installed plugins: %v", err)) - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - allPlugins, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - pluginMap := make(map[string]plugins.Plugin) - for _, p := range allPlugins { - pluginMap[p.ID] = p - } - - result := make([]PluginInfo, 0, len(installedNames)) - for _, id := range installedNames { - if plugin, ok := pluginMap[id]; ok { - hasUpdate := false - if hasUpdates, err := manager.HasUpdates(id, plugin); err == nil { - hasUpdate = hasUpdates - } - - result = append(result, PluginInfo{ - ID: plugin.ID, - Name: plugin.Name, - Category: plugin.Category, - Author: plugin.Author, - Description: plugin.Description, - Repo: plugin.Repo, - Path: plugin.Path, - Capabilities: plugin.Capabilities, - Compositors: plugin.Compositors, - Dependencies: plugin.Dependencies, - FirstParty: strings.HasPrefix(plugin.Repo, "https://github.com/AvengeMedia"), - HasUpdate: hasUpdate, - RequiresDMS: plugin.RequiresDMS, - }) - } else { - result = append(result, PluginInfo{ - ID: id, - Name: id, - Note: "not in registry", - }) - } - } - - SortPluginInfoByFirstParty(result) - - models.Respond(conn, req.ID, result) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/search.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/search.go deleted file mode 100644 index 9dbea6d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/search.go +++ /dev/null @@ -1,74 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleSearch(conn net.Conn, req models.Request) { - query, ok := models.Get[string](req, "query") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'query' parameter") - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list plugins: %v", err)) - return - } - - searchResults := plugins.FuzzySearch(query, pluginList) - - if category := models.GetOr(req, "category", ""); category != "" { - searchResults = plugins.FilterByCategory(category, searchResults) - } - - if compositor := models.GetOr(req, "compositor", ""); compositor != "" { - searchResults = plugins.FilterByCompositor(compositor, searchResults) - } - - if capability := models.GetOr(req, "capability", ""); capability != "" { - searchResults = plugins.FilterByCapability(capability, searchResults) - } - - searchResults = plugins.SortByFirstParty(searchResults) - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - result := make([]PluginInfo, len(searchResults)) - for i, p := range searchResults { - installed, _ := manager.IsInstalled(p) - result[i] = PluginInfo{ - ID: p.ID, - Name: p.Name, - Category: p.Category, - Author: p.Author, - Description: p.Description, - Repo: p.Repo, - Path: p.Path, - Capabilities: p.Capabilities, - Compositors: p.Compositors, - Dependencies: p.Dependencies, - Installed: installed, - FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"), - RequiresDMS: p.RequiresDMS, - } - } - - models.Respond(conn, req.ID, result) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/types.go deleted file mode 100644 index 8290f72..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/types.go +++ /dev/null @@ -1,25 +0,0 @@ -package plugins - -type PluginInfo struct { - ID string `json:"id"` - Name string `json:"name"` - Category string `json:"category,omitempty"` - Author string `json:"author,omitempty"` - Description string `json:"description,omitempty"` - Repo string `json:"repo,omitempty"` - Path string `json:"path,omitempty"` - Capabilities []string `json:"capabilities,omitempty"` - Compositors []string `json:"compositors,omitempty"` - Dependencies []string `json:"dependencies,omitempty"` - Installed bool `json:"installed,omitempty"` - FirstParty bool `json:"firstParty,omitempty"` - Featured bool `json:"featured,omitempty"` - Note string `json:"note,omitempty"` - HasUpdate bool `json:"hasUpdate,omitempty"` - RequiresDMS string `json:"requires_dms,omitempty"` -} - -type SuccessResult struct { - Success bool `json:"success"` - Message string `json:"message"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/uninstall.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/uninstall.go deleted file mode 100644 index 7a3d698..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/uninstall.go +++ /dev/null @@ -1,66 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleUninstall(conn net.Conn, req models.Request) { - name, ok := models.Get[string](req, "name") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - // First try to find in registry (by name or ID) - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, _ := registry.List() - plugin := plugins.FindByIDOrName(name, pluginList) - - // If found in registry, use that - if plugin != nil { - installed, err := manager.IsInstalled(*plugin) - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to check if plugin is installed: %v", err)) - return - } - if !installed { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not installed: %s", name)) - return - } - if err := manager.Uninstall(*plugin); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to uninstall plugin: %v", err)) - return - } - models.Respond(conn, req.ID, SuccessResult{ - Success: true, - Message: fmt.Sprintf("plugin uninstalled: %s", plugin.Name), - }) - return - } - - // Not in registry - try to find and uninstall from installed plugins directly - if err := manager.UninstallByIDOrName(name); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not found: %s", name)) - return - } - - models.Respond(conn, req.ID, SuccessResult{ - Success: true, - Message: fmt.Sprintf("plugin uninstalled: %s", name), - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/update.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/update.go deleted file mode 100644 index 4ea762a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/update.go +++ /dev/null @@ -1,64 +0,0 @@ -package plugins - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleUpdate(conn net.Conn, req models.Request) { - name, ok := models.Get[string](req, "name") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - manager, err := plugins.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - registry, err := plugins.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - pluginList, _ := registry.List() - plugin := plugins.FindByIDOrName(name, pluginList) - - if plugin != nil { - installed, err := manager.IsInstalled(*plugin) - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to check if plugin is installed: %v", err)) - return - } - if !installed { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not installed: %s", name)) - return - } - if err := manager.Update(*plugin); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to update plugin: %v", err)) - return - } - models.Respond(conn, req.ID, SuccessResult{ - Success: true, - Message: fmt.Sprintf("plugin updated: %s", plugin.Name), - }) - return - } - - // Not in registry - try to update from installed plugins directly - if err := manager.UpdateByIDOrName(name); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("plugin not found: %s", name)) - return - } - - models.Respond(conn, req.ID, SuccessResult{ - Success: true, - Message: fmt.Sprintf("plugin updated: %s", name), - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/utils.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/utils.go deleted file mode 100644 index 8b89f44..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/plugins/utils.go +++ /dev/null @@ -1,17 +0,0 @@ -package plugins - -import ( - "sort" - "strings" -) - -func SortPluginInfoByFirstParty(pluginInfos []PluginInfo) { - sort.SliceStable(pluginInfos, func(i, j int) bool { - isFirstPartyI := strings.HasPrefix(pluginInfos[i].Repo, "https://github.com/AvengeMedia") - isFirstPartyJ := strings.HasPrefix(pluginInfos[j].Repo, "https://github.com/AvengeMedia") - if isFirstPartyI != isFirstPartyJ { - return isFirstPartyI - } - return false - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/router.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/router.go deleted file mode 100644 index 6b8c10b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/router.go +++ /dev/null @@ -1,250 +0,0 @@ -package server - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/apppicker" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/bluez" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/brightness" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups" - serverDbus "github.com/AvengeMedia/DankMaterialShell/core/internal/server/dbus" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/dwl" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/evdev" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/extworkspace" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/freedesktop" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/location" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/network" - serverPlugins "github.com/AvengeMedia/DankMaterialShell/core/internal/server/plugins" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/thememode" - serverThemes "github.com/AvengeMedia/DankMaterialShell/core/internal/server/themes" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput" -) - -func RouteRequest(conn net.Conn, req models.Request) { - if strings.HasPrefix(req.Method, "network.") { - if networkManager == nil { - models.RespondError(conn, req.ID, "network manager not initialized") - return - } - network.HandleRequest(conn, req, networkManager) - return - } - - if strings.HasPrefix(req.Method, "plugins.") { - serverPlugins.HandleRequest(conn, req) - return - } - - if strings.HasPrefix(req.Method, "themes.") { - serverThemes.HandleRequest(conn, req) - return - } - - if strings.HasPrefix(req.Method, "theme.auto.") { - if themeModeManager == nil { - models.RespondError(conn, req.ID, "theme mode manager not initialized") - return - } - thememode.HandleRequest(conn, req, themeModeManager) - return - } - - if strings.HasPrefix(req.Method, "loginctl.") { - if loginctlManager == nil { - models.RespondError(conn, req.ID, "loginctl manager not initialized") - return - } - loginctl.HandleRequest(conn, req, loginctlManager) - return - } - - if strings.HasPrefix(req.Method, "freedesktop.") { - if freedesktopManager == nil { - models.RespondError(conn, req.ID, "freedesktop manager not initialized") - return - } - freedesktop.HandleRequest(conn, req, freedesktopManager) - return - } - - if strings.HasPrefix(req.Method, "wayland.") { - if waylandManager == nil { - models.RespondError(conn, req.ID, "wayland manager not initialized") - return - } - wayland.HandleRequest(conn, req, waylandManager) - return - } - - if strings.HasPrefix(req.Method, "bluetooth.") { - if bluezManager == nil { - models.RespondError(conn, req.ID, "bluetooth manager not initialized") - return - } - bluez.HandleRequest(conn, req, bluezManager) - return - } - - if strings.HasPrefix(req.Method, "browser.") || strings.HasPrefix(req.Method, "apppicker.") { - if appPickerManager == nil { - models.RespondError(conn, req.ID, "apppicker manager not initialized") - return - } - apppicker.HandleRequest(conn, req, appPickerManager) - return - } - - if strings.HasPrefix(req.Method, "cups.") { - if cupsManager == nil { - models.RespondError(conn, req.ID, "CUPS manager not initialized") - return - } - cups.HandleRequest(conn, req, cupsManager) - return - } - - if strings.HasPrefix(req.Method, "dwl.") { - if dwlManager == nil { - models.RespondError(conn, req.ID, "dwl manager not initialized") - return - } - dwl.HandleRequest(conn, req, dwlManager) - return - } - - if strings.HasPrefix(req.Method, "brightness.") { - if brightnessManager == nil { - models.RespondError(conn, req.ID, "brightness manager not initialized") - return - } - brightness.HandleRequest(conn, req, brightnessManager) - return - } - - if strings.HasPrefix(req.Method, "extworkspace.") { - if extWorkspaceManager == nil { - if extWorkspaceAvailable.Load() { - extWorkspaceInitMutex.Lock() - if extWorkspaceManager == nil { - if err := InitializeExtWorkspaceManager(); err != nil { - extWorkspaceInitMutex.Unlock() - models.RespondError(conn, req.ID, "extworkspace manager not available") - return - } - } - extWorkspaceInitMutex.Unlock() - } else { - models.RespondError(conn, req.ID, "extworkspace manager not initialized") - return - } - } - extworkspace.HandleRequest(conn, req, extWorkspaceManager) - return - } - - if strings.HasPrefix(req.Method, "wlroutput.") { - if wlrOutputManager == nil { - models.RespondError(conn, req.ID, "wlroutput manager not initialized") - return - } - wlroutput.HandleRequest(conn, req, wlrOutputManager) - return - } - - if strings.HasPrefix(req.Method, "evdev.") { - if evdevManager == nil { - models.RespondError(conn, req.ID, "evdev manager not initialized") - return - } - evdev.HandleRequest(conn, req, evdevManager) - return - } - - if strings.HasPrefix(req.Method, "dbus.") { - if dbusManager == nil { - models.RespondError(conn, req.ID, "dbus manager not initialized") - return - } - serverDbus.HandleRequest(conn, req, dbusManager, dbusClientID) - return - } - - if strings.HasPrefix(req.Method, "clipboard.") { - switch req.Method { - case "clipboard.getConfig": - cfg := clipboard.LoadConfig() - models.Respond(conn, req.ID, cfg) - return - case "clipboard.setConfig": - handleClipboardSetConfig(conn, req) - return - } - if clipboardManager == nil { - models.RespondError(conn, req.ID, "clipboard manager not initialized") - return - } - clipboard.HandleRequest(conn, req, clipboardManager) - return - } - - if strings.HasPrefix(req.Method, "location.") { - if locationManager == nil { - models.RespondError(conn, req.ID, "location manager not initialized") - return - } - location.HandleRequest(conn, req, locationManager) - return - } - - switch req.Method { - case "ping": - models.Respond(conn, req.ID, "pong") - case "getServerInfo": - info := getServerInfo() - models.Respond(conn, req.ID, info) - case "subscribe": - handleSubscribe(conn, req) - case "matugen.queue": - handleMatugenQueue(conn, req) - case "matugen.status": - handleMatugenStatus(conn, req) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleClipboardSetConfig(conn net.Conn, req models.Request) { - cfg := clipboard.LoadConfig() - - if v, ok := models.Get[float64](req, "maxHistory"); ok { - cfg.MaxHistory = int(v) - } - if v, ok := models.Get[float64](req, "maxEntrySize"); ok { - cfg.MaxEntrySize = int64(v) - } - if v, ok := models.Get[float64](req, "autoClearDays"); ok { - cfg.AutoClearDays = int(v) - } - if v, ok := models.Get[bool](req, "clearAtStartup"); ok { - cfg.ClearAtStartup = v - } - if v, ok := models.Get[bool](req, "disabled"); ok { - cfg.Disabled = v - } - if v, ok := models.Get[float64](req, "maxPinned"); ok { - cfg.MaxPinned = int(v) - } - - if err := clipboard.SaveConfig(cfg); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "config updated"}) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go deleted file mode 100644 index 58acb62..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go +++ /dev/null @@ -1,1758 +0,0 @@ -package server - -import ( - "bufio" - "encoding/json" - "fmt" - "net" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "sync/atomic" - "syscall" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/apppicker" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/bluez" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/brightness" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups" - serverDbus "github.com/AvengeMedia/DankMaterialShell/core/internal/server/dbus" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/dwl" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/evdev" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/extworkspace" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/freedesktop" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/location" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/network" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/thememode" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/trayrecovery" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlcontext" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -const APIVersion = 24 - -var CLIVersion = "dev" - -type Capabilities struct { - Capabilities []string `json:"capabilities"` -} - -type ServerInfo struct { - APIVersion int `json:"apiVersion"` - CLIVersion string `json:"cliVersion,omitempty"` - Capabilities []string `json:"capabilities"` -} - -type ServiceEvent struct { - Service string `json:"service"` - Data any `json:"data"` -} - -var networkManager *network.Manager -var loginctlManager *loginctl.Manager -var freedesktopManager *freedesktop.Manager -var waylandManager *wayland.Manager -var bluezManager *bluez.Manager -var appPickerManager *apppicker.Manager -var cupsManager *cups.Manager -var dwlManager *dwl.Manager -var extWorkspaceManager *extworkspace.Manager -var brightnessManager *brightness.Manager -var wlrOutputManager *wlroutput.Manager -var evdevManager *evdev.Manager -var clipboardManager *clipboard.Manager -var dbusManager *serverDbus.Manager -var wlContext *wlcontext.SharedContext -var themeModeManager *thememode.Manager -var trayRecoveryManager *trayrecovery.Manager -var locationManager *location.Manager -var geoClientInstance geolocation.Client - -const dbusClientID = "dms-dbus-client" - -var capabilitySubscribers syncmap.Map[string, chan ServerInfo] -var cupsSubscribers syncmap.Map[string, bool] -var cupsSubscriberCount atomic.Int32 -var extWorkspaceAvailable atomic.Bool -var extWorkspaceInitMutex sync.Mutex - -func getSocketDir() string { - if runtime := os.Getenv("XDG_RUNTIME_DIR"); runtime != "" { - return runtime - } - - if os.Getuid() == 0 { - if _, err := os.Stat("/run"); err == nil { - return "/run/dankdots" - } - return "/var/run/dankdots" - } - - return os.TempDir() -} - -func GetSocketPath() string { - return filepath.Join(getSocketDir(), fmt.Sprintf("danklinux-%d.sock", os.Getpid())) -} - -func FindSocket() (string, error) { - dir := getSocketDir() - entries, err := os.ReadDir(dir) - if err != nil { - return "", err - } - - for _, entry := range entries { - if strings.HasPrefix(entry.Name(), "danklinux-") && strings.HasSuffix(entry.Name(), ".sock") { - return filepath.Join(dir, entry.Name()), nil - } - } - return "", fmt.Errorf("no dms socket found") -} - -func cleanupStaleSockets() { - dir := getSocketDir() - entries, err := os.ReadDir(dir) - if err != nil { - return - } - - for _, entry := range entries { - if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".sock") { - continue - } - - pidStr := strings.TrimPrefix(entry.Name(), "danklinux-") - pidStr = strings.TrimSuffix(pidStr, ".sock") - pid, err := strconv.Atoi(pidStr) - if err != nil { - continue - } - - process, err := os.FindProcess(pid) - if err != nil { - socketPath := filepath.Join(dir, entry.Name()) - os.Remove(socketPath) - log.Debugf("Removed stale socket: %s", socketPath) - continue - } - - err = process.Signal(syscall.Signal(0)) - if err != nil { - socketPath := filepath.Join(dir, entry.Name()) - os.Remove(socketPath) - log.Debugf("Removed stale socket: %s", socketPath) - } - } -} - -func InitializeNetworkManager() error { - manager, err := network.NewManager() - if err != nil { - log.Warnf("Failed to initialize network manager: %v", err) - return err - } - - networkManager = manager - - log.Info("Network manager initialized") - return nil -} - -func InitializeLoginctlManager() error { - manager, err := loginctl.NewManager() - if err != nil { - log.Warnf("Failed to initialize loginctl manager: %v", err) - return err - } - - loginctlManager = manager - - log.Info("Loginctl manager initialized") - return nil -} - -func InitializeFreedeskManager() error { - manager, err := freedesktop.NewManager() - if err != nil { - log.Warnf("Failed to initialize freedesktop manager: %v", err) - return err - } - - freedesktopManager = manager - - log.Info("Freedesktop manager initialized") - return nil -} - -func InitializeWaylandManager() error { - log.Info("Attempting to initialize Wayland gamma control...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - config := wayland.DefaultConfig() - manager, err := wayland.NewManager(wlContext.Display(), config) - if err != nil { - log.Errorf("Failed to initialize wayland manager: %v", err) - return err - } - - waylandManager = manager - - log.Info("Wayland gamma control initialized successfully") - return nil -} - -func InitializeBluezManager() error { - manager, err := bluez.NewManager() - if err != nil { - log.Warnf("Failed to initialize bluez manager: %v", err) - return err - } - - bluezManager = manager - - log.Info("Bluez manager initialized") - return nil -} - -func InitializeAppPickerManager() error { - manager := apppicker.NewManager() - appPickerManager = manager - log.Info("AppPicker manager initialized") - return nil -} - -func InitializeCupsManager() error { - manager, err := cups.NewManager() - if err != nil { - log.Warnf("Failed to initialize cups manager: %v", err) - return err - } - - cupsManager = manager - - log.Info("CUPS manager initialized") - return nil -} - -func InitializeDwlManager() error { - log.Info("Attempting to initialize DWL IPC...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - manager, err := dwl.NewManager(wlContext.Display()) - if err != nil { - log.Debug("Failed to initialize dwl manager: %v", err) - return err - } - - dwlManager = manager - - log.Info("DWL IPC initialized successfully") - return nil -} - -func InitializeBrightnessManager() error { - manager, err := brightness.NewManager() - if err != nil { - log.Warnf("Failed to initialize brightness manager: %v", err) - return err - } - - brightnessManager = manager - - log.Info("Brightness manager initialized") - return nil -} - -func InitializeExtWorkspaceManager() error { - log.Info("Attempting to initialize ExtWorkspace...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - manager, err := extworkspace.NewManager(wlContext.Display()) - if err != nil { - log.Debug("Failed to initialize extworkspace manager: %v", err) - return err - } - - extWorkspaceManager = manager - - log.Info("ExtWorkspace initialized successfully") - return nil -} - -func InitializeWlrOutputManager() error { - log.Info("Attempting to initialize WlrOutput management...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - manager, err := wlroutput.NewManager(wlContext.Display()) - if err != nil { - log.Debug("Failed to initialize wlroutput manager: %v", err) - return err - } - - wlrOutputManager = manager - - log.Info("WlrOutput management initialized successfully") - return nil -} - -func InitializeEvdevManager() error { - manager, err := evdev.InitializeManager() - if err != nil { - log.Warnf("Failed to initialize evdev manager: %v", err) - return err - } - - evdevManager = manager - - log.Info("Evdev manager initialized") - return nil -} - -func InitializeClipboardManager() error { - log.Info("Attempting to initialize clipboard manager...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - config := clipboard.LoadConfig() - manager, err := clipboard.NewManager(wlContext, config) - if err != nil { - log.Errorf("Failed to initialize clipboard manager: %v", err) - return err - } - - clipboardManager = manager - - log.Info("Clipboard manager initialized successfully") - return nil -} - -func InitializeDbusManager() error { - manager, err := serverDbus.NewManager() - if err != nil { - log.Warnf("Failed to initialize dbus manager: %v", err) - return err - } - - dbusManager = manager - - log.Info("DBus manager initialized") - return nil -} - -func InitializeThemeModeManager() error { - manager := thememode.NewManager() - themeModeManager = manager - - log.Info("Theme mode automation manager initialized") - return nil -} - -func InitializeTrayRecoveryManager() error { - manager, err := trayrecovery.NewManager() - if err != nil { - return err - } - - trayRecoveryManager = manager - - log.Info("TrayRecovery manager initialized") - return nil -} - -func InitializeLocationManager(geoClient geolocation.Client) error { - manager, err := location.NewManager(geoClient) - if err != nil { - log.Warnf("Failed to initialize location manager: %v", err) - return err - } - - locationManager = manager - - log.Info("Location manager initialized") - return nil -} - -func handleConnection(conn net.Conn) { - defer conn.Close() - - caps := getCapabilities() - capsData, _ := json.Marshal(caps) - conn.Write(capsData) - conn.Write([]byte("\n")) - scanner := bufio.NewScanner(conn) - for scanner.Scan() { - line := scanner.Bytes() - - var req models.Request - if err := json.Unmarshal(line, &req); err != nil { - log.Warnf("handleConnection: Failed to unmarshal JSON: %v, line: %s", err, string(line)) - models.RespondError(conn, 0, "invalid json") - continue - } - - go RouteRequest(conn, req) - } -} - -func getCapabilities() Capabilities { - caps := []string{"plugins"} - - if networkManager != nil { - caps = append(caps, "network") - } - - if loginctlManager != nil { - caps = append(caps, "loginctl") - } - - if freedesktopManager != nil { - caps = append(caps, "freedesktop") - } - - if waylandManager != nil { - caps = append(caps, "gamma") - } - - if bluezManager != nil { - caps = append(caps, "bluetooth") - } - - if appPickerManager != nil { - caps = append(caps, "browser") - } - - if cupsManager != nil { - caps = append(caps, "cups") - } - - if dwlManager != nil { - caps = append(caps, "dwl") - } - - if extWorkspaceAvailable.Load() { - caps = append(caps, "extworkspace") - } - - if brightnessManager != nil { - caps = append(caps, "brightness") - } - - if wlrOutputManager != nil { - caps = append(caps, "wlroutput") - } - - if evdevManager != nil { - caps = append(caps, "evdev") - } - - if clipboardManager != nil { - caps = append(caps, "clipboard") - } - - if themeModeManager != nil { - caps = append(caps, "theme.auto") - } - - if dbusManager != nil { - caps = append(caps, "dbus") - } - - return Capabilities{Capabilities: caps} -} - -func getServerInfo() ServerInfo { - caps := []string{"plugins"} - - if networkManager != nil { - caps = append(caps, "network") - } - - if loginctlManager != nil { - caps = append(caps, "loginctl") - } - - if freedesktopManager != nil { - caps = append(caps, "freedesktop") - } - - if waylandManager != nil { - caps = append(caps, "gamma") - } - - if bluezManager != nil { - caps = append(caps, "bluetooth") - } - - if appPickerManager != nil { - caps = append(caps, "browser") - } - - if cupsManager != nil { - caps = append(caps, "cups") - } - - if dwlManager != nil { - caps = append(caps, "dwl") - } - - if extWorkspaceAvailable.Load() { - caps = append(caps, "extworkspace") - } - - if brightnessManager != nil { - caps = append(caps, "brightness") - } - - if wlrOutputManager != nil { - caps = append(caps, "wlroutput") - } - - if evdevManager != nil { - caps = append(caps, "evdev") - } - - if clipboardManager != nil { - caps = append(caps, "clipboard") - } - - if themeModeManager != nil { - caps = append(caps, "theme.auto") - } - - if locationManager != nil { - caps = append(caps, "location") - } - - if dbusManager != nil { - caps = append(caps, "dbus") - } - - return ServerInfo{ - APIVersion: APIVersion, - CLIVersion: CLIVersion, - Capabilities: caps, - } -} - -func notifyCapabilityChange() { - info := getServerInfo() - capabilitySubscribers.Range(func(key string, ch chan ServerInfo) bool { - select { - case ch <- info: - default: - } - return true - }) -} - -func handleSubscribe(conn net.Conn, req models.Request) { - clientID := fmt.Sprintf("meta-client-%p", conn) - - var services []string - if servicesParam, ok := models.Get[[]any](req, "services"); ok { - for _, s := range servicesParam { - if str, ok := s.(string); ok { - services = append(services, str) - } - } - } - - if len(services) == 0 { - services = []string{"all"} - } - - subscribeAll := false - for _, s := range services { - if s == "all" { - subscribeAll = true - break - } - } - - var wg sync.WaitGroup - eventChan := make(chan ServiceEvent, 256) - stopChan := make(chan struct{}) - - capChan := make(chan ServerInfo, 64) - capabilitySubscribers.Store(clientID+"-capabilities", capChan) - - wg.Add(1) - go func() { - defer wg.Done() - defer capabilitySubscribers.Delete(clientID + "-capabilities") - - for { - select { - case info, ok := <-capChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "server", Data: info}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - - shouldSubscribe := func(service string) bool { - if subscribeAll { - return true - } - for _, s := range services { - if s == service { - return true - } - } - return false - } - - if shouldSubscribe("network") && networkManager != nil { - wg.Add(1) - netChan := networkManager.Subscribe(clientID + "-network") - go func() { - defer wg.Done() - defer networkManager.Unsubscribe(clientID + "-network") - - initialState := networkManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "network", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-netChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "network", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("network.credentials") && networkManager != nil { - wg.Add(1) - credChan := networkManager.SubscribeCredentials(clientID + "-credentials") - go func() { - defer wg.Done() - defer networkManager.UnsubscribeCredentials(clientID + "-credentials") - - for { - select { - case prompt, ok := <-credChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "network.credentials", Data: prompt}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("loginctl") && loginctlManager != nil { - wg.Add(1) - loginChan := loginctlManager.Subscribe(clientID + "-loginctl") - go func() { - defer wg.Done() - defer loginctlManager.Unsubscribe(clientID + "-loginctl") - - initialState := loginctlManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "loginctl", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-loginChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "loginctl", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("freedesktop") && freedesktopManager != nil { - wg.Add(1) - freedesktopChan := freedesktopManager.Subscribe(clientID + "-freedesktop") - go func() { - defer wg.Done() - defer freedesktopManager.Unsubscribe(clientID + "-freedesktop") - - initialState := freedesktopManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "freedesktop", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-freedesktopChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "freedesktop", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("freedesktop.screensaver") && freedesktopManager != nil { - wg.Add(1) - screensaverChan := freedesktopManager.SubscribeScreensaver(clientID + "-screensaver") - go func() { - defer wg.Done() - defer freedesktopManager.UnsubscribeScreensaver(clientID + "-screensaver") - - initialState := freedesktopManager.GetScreensaverState() - select { - case eventChan <- ServiceEvent{Service: "freedesktop.screensaver", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-screensaverChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "freedesktop.screensaver", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("gamma") && waylandManager != nil { - wg.Add(1) - waylandChan := waylandManager.Subscribe(clientID + "-gamma") - go func() { - defer wg.Done() - defer waylandManager.Unsubscribe(clientID + "-gamma") - - initialState := waylandManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "gamma", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-waylandChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "gamma", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("theme.auto") && themeModeManager != nil { - wg.Add(1) - themeAutoChan := themeModeManager.Subscribe(clientID + "-theme-auto") - go func() { - defer wg.Done() - defer themeModeManager.Unsubscribe(clientID + "-theme-auto") - - initialState := themeModeManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "theme.auto", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-themeAutoChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "theme.auto", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("bluetooth") && bluezManager != nil { - wg.Add(1) - bluezChan := bluezManager.Subscribe(clientID + "-bluetooth") - go func() { - defer wg.Done() - defer bluezManager.Unsubscribe(clientID + "-bluetooth") - - initialState := bluezManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "bluetooth", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-bluezChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "bluetooth", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("bluetooth.pairing") && bluezManager != nil { - wg.Add(1) - pairingChan := bluezManager.SubscribePairing(clientID + "-pairing") - go func() { - defer wg.Done() - defer bluezManager.UnsubscribePairing(clientID + "-pairing") - - for { - select { - case prompt, ok := <-pairingChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "bluetooth.pairing", Data: prompt}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("browser") && appPickerManager != nil { - wg.Add(1) - appPickerChan := appPickerManager.Subscribe(clientID + "-browser") - go func() { - defer wg.Done() - defer appPickerManager.Unsubscribe(clientID + "-browser") - - for { - select { - case event, ok := <-appPickerChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "browser.open_requested", Data: event}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("cups") { - cupsSubscribers.Store(clientID+"-cups", true) - count := cupsSubscriberCount.Add(1) - - if count == 1 { - if err := InitializeCupsManager(); err != nil { - log.Warnf("Failed to initialize CUPS manager for subscription: %v", err) - } else { - notifyCapabilityChange() - } - } - - if cupsManager != nil { - wg.Add(1) - cupsChan := cupsManager.Subscribe(clientID + "-cups") - go func() { - defer wg.Done() - defer func() { - cupsManager.Unsubscribe(clientID + "-cups") - cupsSubscribers.Delete(clientID + "-cups") - count := cupsSubscriberCount.Add(-1) - - if count == 0 { - log.Info("Last CUPS subscriber disconnected, shutting down CUPS manager") - if cupsManager != nil { - cupsManager.Close() - cupsManager = nil - notifyCapabilityChange() - } - } - }() - - initialState := cupsManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "cups", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-cupsChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "cups", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - } - - if shouldSubscribe("dwl") && dwlManager != nil { - wg.Add(1) - dwlChan := dwlManager.Subscribe(clientID + "-dwl") - go func() { - defer wg.Done() - defer dwlManager.Unsubscribe(clientID + "-dwl") - - initialState := dwlManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "dwl", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-dwlChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "dwl", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("extworkspace") { - if extWorkspaceManager == nil && extWorkspaceAvailable.Load() { - extWorkspaceInitMutex.Lock() - if extWorkspaceManager == nil { - if err := InitializeExtWorkspaceManager(); err != nil { - log.Warnf("Failed to initialize ExtWorkspace manager for subscription: %v", err) - } - } - extWorkspaceInitMutex.Unlock() - } - - if extWorkspaceManager != nil { - wg.Add(1) - extWorkspaceChan := extWorkspaceManager.Subscribe(clientID + "-extworkspace") - go func() { - defer wg.Done() - defer extWorkspaceManager.Unsubscribe(clientID + "-extworkspace") - - initialState := extWorkspaceManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "extworkspace", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-extWorkspaceChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "extworkspace", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - } - - if shouldSubscribe("brightness") && brightnessManager != nil { - wg.Add(2) - brightnessStateChan := brightnessManager.Subscribe(clientID + "-brightness-state") - brightnessUpdateChan := brightnessManager.SubscribeUpdates(clientID + "-brightness-updates") - - go func() { - defer wg.Done() - defer brightnessManager.Unsubscribe(clientID + "-brightness-state") - - initialState := brightnessManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "brightness", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-brightnessStateChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "brightness", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - - go func() { - defer wg.Done() - defer brightnessManager.UnsubscribeUpdates(clientID + "-brightness-updates") - - for { - select { - case update, ok := <-brightnessUpdateChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "brightness.update", Data: update}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("wlroutput") && wlrOutputManager != nil { - wg.Add(1) - wlrOutputChan := wlrOutputManager.Subscribe(clientID + "-wlroutput") - go func() { - defer wg.Done() - defer wlrOutputManager.Unsubscribe(clientID + "-wlroutput") - - initialState := wlrOutputManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "wlroutput", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-wlrOutputChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "wlroutput", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("evdev") && evdevManager != nil { - wg.Add(1) - evdevChan := evdevManager.Subscribe(clientID + "-evdev") - go func() { - defer wg.Done() - defer evdevManager.Unsubscribe(clientID + "-evdev") - - initialState := evdevManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "evdev", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-evdevChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "evdev", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("clipboard") && clipboardManager != nil { - wg.Add(1) - clipboardChan := clipboardManager.Subscribe(clientID + "-clipboard") - go func() { - defer wg.Done() - defer clipboardManager.Unsubscribe(clientID + "-clipboard") - - initialState := clipboardManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "clipboard", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-clipboardChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "clipboard", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("dbus") && dbusManager != nil { - wg.Add(1) - dbusChan := dbusManager.SubscribeSignals(dbusClientID) - go func() { - defer wg.Done() - defer dbusManager.UnsubscribeSignals(dbusClientID) - - for { - select { - case event, ok := <-dbusChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "dbus", Data: event}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - go func() { - wg.Wait() - close(eventChan) - }() - - info := getServerInfo() - if err := json.NewEncoder(conn).Encode(models.Response[ServiceEvent]{ - ID: req.ID, - Result: &ServiceEvent{Service: "server", Data: info}, - }); err != nil { - close(stopChan) - return - } - - for event := range eventChan { - if err := json.NewEncoder(conn).Encode(models.Response[ServiceEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - close(stopChan) - return - } - } -} - -func cleanupManagers() { - if networkManager != nil { - networkManager.Close() - } - if loginctlManager != nil { - loginctlManager.Close() - } - if freedesktopManager != nil { - freedesktopManager.Close() - } - if waylandManager != nil { - waylandManager.Close() - } - if bluezManager != nil { - bluezManager.Close() - } - if appPickerManager != nil { - appPickerManager.Close() - } - if cupsManager != nil { - cupsManager.Close() - } - if dwlManager != nil { - dwlManager.Close() - } - if extWorkspaceManager != nil { - extWorkspaceManager.Close() - } - if brightnessManager != nil { - brightnessManager.Close() - } - if wlrOutputManager != nil { - wlrOutputManager.Close() - } - if evdevManager != nil { - evdevManager.Close() - } - if clipboardManager != nil { - clipboardManager.Close() - } - if dbusManager != nil { - dbusManager.Close() - } - if themeModeManager != nil { - themeModeManager.Close() - } - if trayRecoveryManager != nil { - trayRecoveryManager.Close() - } - if wlContext != nil { - wlContext.Close() - } - if locationManager != nil { - locationManager.Close() - } - if geoClientInstance != nil { - geoClientInstance.Close() - } -} - -func Start(printDocs bool) error { - cleanupStaleSockets() - - socketPath := GetSocketPath() - os.Remove(socketPath) - - listener, err := net.Listen("unix", socketPath) - if err != nil { - return err - } - defer listener.Close() - defer cleanupManagers() - - log.Infof("DMS API Server listening on: %s", socketPath) - log.Infof("API Version: %d", APIVersion) - log.Info("Protocol: JSON over Unix socket") - log.Info("Request format: {\"id\": , \"method\": \"...\", \"params\": {...}}") - log.Info("Response format: {\"id\": , \"result\": {...}} or {\"id\": , \"error\": \"...\"}") - log.Info("") - if printDocs { - log.Info("Available methods:") - log.Info(" ping - Test connection") - log.Info(" getServerInfo - Get server info (API version and capabilities)") - log.Info(" subscribe - Subscribe to multiple services (params: services [default: all])") - log.Info("Plugins:") - log.Info(" plugins.list - List all plugins") - log.Info(" plugins.listInstalled - List installed plugins") - log.Info(" plugins.install - Install plugin (params: name)") - log.Info(" plugins.uninstall - Uninstall plugin (params: name)") - log.Info(" plugins.update - Update plugin (params: name)") - log.Info(" plugins.search - Search plugins (params: query, category?, compositor?, capability?)") - log.Info("Network:") - log.Info(" network.getState - Get current network state") - log.Info(" network.wifi.scan - Scan for WiFi networks (params: device?)") - log.Info(" network.wifi.networks - Get WiFi network list") - log.Info(" network.wifi.connect - Connect to WiFi (params: ssid, password?, username?, device?, eapMethod?, phase2Auth?, caCertPath?, clientCertPath?, privateKeyPath?, useSystemCACerts?)") - log.Info(" network.wifi.disconnect - Disconnect WiFi (params: device?)") - log.Info(" network.wifi.forget - Forget network (params: ssid)") - log.Info(" network.wifi.toggle - Toggle WiFi radio") - log.Info(" network.wifi.enable - Enable WiFi") - log.Info(" network.wifi.disable - Disable WiFi") - log.Info(" network.wifi.setAutoconnect - Set network autoconnect (params: ssid, autoconnect)") - log.Info(" network.ethernet.connect - Connect Ethernet") - log.Info(" network.ethernet.connect.config - Connect Ethernet to a specific configuration") - log.Info(" network.ethernet.disconnect - Disconnect Ethernet") - log.Info(" network.vpn.profiles - List VPN profiles") - log.Info(" network.vpn.active - List active VPN connections") - log.Info(" network.vpn.connect - Connect VPN (params: uuidOrName|name|uuid, singleActive?)") - log.Info(" network.vpn.disconnect - Disconnect VPN (params: uuidOrName|name|uuid)") - log.Info(" network.vpn.disconnectAll - Disconnect all VPNs") - log.Info(" network.vpn.clearCredentials - Clear saved VPN credentials (params: uuidOrName|name|uuid)") - log.Info(" network.vpn.plugins - List available VPN plugins") - log.Info(" network.vpn.import - Import VPN from file (params: file|path, name?)") - log.Info(" network.vpn.getConfig - Get VPN configuration (params: uuid|name|uuidOrName)") - log.Info(" network.vpn.updateConfig - Update VPN configuration (params: uuid, name?, autoconnect?, data?)") - log.Info(" network.vpn.delete - Delete VPN connection (params: uuid|name|uuidOrName)") - log.Info(" network.preference.set - Set preference (params: preference [auto|wifi|ethernet])") - log.Info(" network.info - Get network info (params: ssid)") - log.Info(" network.credentials.submit - Submit credentials for prompt (params: token, secrets, save?)") - log.Info(" network.credentials.cancel - Cancel credential prompt (params: token)") - log.Info(" network.subscribe - Subscribe to network state changes (streaming)") - log.Info("Loginctl:") - log.Info(" loginctl.getState - Get current session state") - log.Info(" loginctl.lock - Lock session") - log.Info(" loginctl.unlock - Unlock session") - log.Info(" loginctl.activate - Activate session") - log.Info(" loginctl.setIdleHint - Set idle hint (params: idle)") - log.Info(" loginctl.setLockBeforeSuspend - Set lock before suspend (params: enabled)") - log.Info(" loginctl.setSleepInhibitorEnabled - Enable/disable sleep inhibitor (params: enabled)") - log.Info(" loginctl.lockerReady - Signal locker UI is ready (releases sleep inhibitor)") - log.Info(" loginctl.terminate - Terminate session") - log.Info(" loginctl.subscribe - Subscribe to session state changes (streaming)") - log.Info("Freedesktop:") - log.Info(" freedesktop.getState - Get accounts & settings state") - log.Info(" freedesktop.accounts.setIconFile - Set profile icon (params: path)") - log.Info(" freedesktop.accounts.setRealName - Set real name (params: name)") - log.Info(" freedesktop.accounts.setEmail - Set email (params: email)") - log.Info(" freedesktop.accounts.setLanguage - Set language (params: language)") - log.Info(" freedesktop.accounts.setLocation - Set location (params: location)") - log.Info(" freedesktop.accounts.getUserIconFile - Get user icon (params: username)") - log.Info(" freedesktop.settings.getColorScheme - Get color scheme") - log.Info(" freedesktop.settings.setIconTheme - Set icon theme (params: iconTheme)") - log.Info("Wayland:") - log.Info(" wayland.gamma.getState - Get current gamma control state") - log.Info(" wayland.gamma.setTemperature - Set temperature range (params: low, high)") - log.Info(" wayland.gamma.setLocation - Set location (params: latitude, longitude)") - log.Info(" wayland.gamma.setManualTimes - Set manual times (params: sunrise, sunset)") - log.Info(" wayland.gamma.setGamma - Set gamma value (params: gamma)") - log.Info(" wayland.gamma.setEnabled - Enable/disable gamma control (params: enabled)") - log.Info(" wayland.gamma.subscribe - Subscribe to gamma state changes (streaming)") - log.Info("Theme automation:") - log.Info(" theme.auto.getState - Get current theme automation state") - log.Info(" theme.auto.setEnabled - Enable/disable theme automation (params: enabled)") - log.Info(" theme.auto.setMode - Set automation mode (params: mode [time|location])") - log.Info(" theme.auto.setSchedule - Set time schedule (params: startHour, startMinute, endHour, endMinute)") - log.Info(" theme.auto.setLocation - Set location (params: latitude, longitude)") - log.Info(" theme.auto.setUseIPLocation - Use IP location (params: use)") - log.Info(" theme.auto.trigger - Trigger immediate re-evaluation") - log.Info(" theme.auto.subscribe - Subscribe to theme automation state changes (streaming)") - log.Info("Bluetooth:") - log.Info(" bluetooth.getState - Get current bluetooth state") - log.Info(" bluetooth.startDiscovery - Start device discovery") - log.Info(" bluetooth.stopDiscovery - Stop device discovery") - log.Info(" bluetooth.setPowered - Set adapter power state (params: powered)") - log.Info(" bluetooth.pair - Pair with device (params: device)") - log.Info(" bluetooth.connect - Connect to device (params: device)") - log.Info(" bluetooth.disconnect - Disconnect from device (params: device)") - log.Info(" bluetooth.remove - Remove/unpair device (params: device)") - log.Info(" bluetooth.trust - Trust device (params: device)") - log.Info(" bluetooth.untrust - Untrust device (params: device)") - log.Info(" bluetooth.pairing.submit - Submit pairing response (params: token, secrets?, accept?)") - log.Info(" bluetooth.pairing.cancel - Cancel pairing prompt (params: token)") - log.Info(" bluetooth.subscribe - Subscribe to bluetooth state changes (streaming)") - log.Info("CUPS:") - log.Info(" cups.getPrinters - Get printers list") - log.Info(" cups.getJobs - Get non-completed jobs list (params: printerName)") - log.Info(" cups.pausePrinter - Pause printer (params: printerName)") - log.Info(" cups.resumePrinter - Resume printer (params: printerName)") - log.Info(" cups.cancelJob - Cancel job (params: printerName, jobID)") - log.Info(" cups.purgeJobs - Cancel all jobs (params: printerName)") - log.Info("DWL:") - log.Info(" dwl.getState - Get current dwl state (tags, windows, layouts, keyboard)") - log.Info(" dwl.setTags - Set active tags (params: output, tagmask, toggleTagset)") - log.Info(" dwl.setClientTags - Set focused client tags (params: output, andTags, xorTags)") - log.Info(" dwl.setLayout - Set layout (params: output, index)") - log.Info(" dwl.subscribe - Subscribe to dwl state changes (streaming)") - log.Info(" Output state includes:") - log.Info(" - tags : Tag states (active, clients, focused)") - log.Info(" - layoutSymbol : Current layout name") - log.Info(" - title : Focused window title") - log.Info(" - appId : Focused window app ID") - log.Info(" - kbLayout : Current keyboard layout") - log.Info(" - keymode : Current keybind mode") - log.Info("ExtWorkspace:") - log.Info(" extworkspace.getState - Get current workspace state (groups, workspaces)") - log.Info(" extworkspace.activateWorkspace - Activate workspace (params: groupID, workspaceID)") - log.Info(" extworkspace.deactivateWorkspace - Deactivate workspace (params: groupID, workspaceID)") - log.Info(" extworkspace.removeWorkspace - Remove workspace (params: groupID, workspaceID)") - log.Info(" extworkspace.createWorkspace - Create workspace (params: groupID, name)") - log.Info(" extworkspace.subscribe - Subscribe to workspace state changes (streaming)") - log.Info("Brightness:") - log.Info(" brightness.getState - Get current brightness state for all devices") - log.Info(" brightness.setBrightness - Set device brightness (params: device, percent)") - log.Info(" brightness.increment - Increment device brightness (params: device, step?)") - log.Info(" brightness.decrement - Decrement device brightness (params: device, step?)") - log.Info(" brightness.rescan - Rescan for brightness devices (e.g., after plugging in monitor)") - log.Info(" brightness.subscribe - Subscribe to brightness state changes (streaming)") - log.Info(" Subscription events:") - log.Info(" - brightness : Full device list (on rescan, DDC discovery, device changes)") - log.Info(" - brightness.update: Single device update (on brightness change for efficiency)") - log.Info("WlrOutput:") - log.Info(" wlroutput.getState - Get current output configuration state") - log.Info(" wlroutput.applyConfiguration - Apply output configuration (params: heads)") - log.Info(" wlroutput.testConfiguration - Test output configuration without applying (params: heads)") - log.Info(" wlroutput.subscribe - Subscribe to output state changes (streaming)") - log.Info(" Head configuration params:") - log.Info(" - name : Output name (required)") - log.Info(" - enabled : Enable/disable output (required)") - log.Info(" - modeId : Mode ID from available modes (optional)") - log.Info(" - customMode : Custom mode {width, height, refresh} (optional)") - log.Info(" - position : Position {x, y} (optional)") - log.Info(" - transform : Transform value (optional)") - log.Info(" - scale : Scale value (optional)") - log.Info(" - adaptiveSync : Adaptive sync state (optional)") - log.Info("Evdev:") - log.Info(" evdev.getState - Get current evdev state (caps lock)") - log.Info(" evdev.subscribe - Subscribe to evdev state changes (streaming)") - log.Info("Clipboard:") - log.Info(" clipboard.getState - Get clipboard state (enabled, history, current)") - log.Info(" clipboard.getHistory - Get clipboard history with previews") - log.Info(" clipboard.getEntry - Get full entry by ID (params: id)") - log.Info(" clipboard.deleteEntry - Delete entry by ID (params: id)") - log.Info(" clipboard.clearHistory - Clear all clipboard history") - log.Info(" clipboard.copy - Copy text to clipboard (params: text)") - log.Info(" clipboard.paste - Get current clipboard text") - log.Info(" clipboard.search - Search history (params: query?, mimeType?, isImage?, limit?, offset?, before?, after?)") - log.Info(" clipboard.getConfig - Get clipboard configuration") - log.Info(" clipboard.setConfig - Set configuration (params: maxHistory?, maxEntrySize?, autoClearDays?, clearAtStartup?)") - log.Info(" clipboard.subscribe - Subscribe to clipboard state changes (streaming)") - log.Info("Location:") - log.Info(" location.getState - Get current location state") - log.Info(" location.subscribe - Subscribe to location changes (streaming)") - log.Info("") - } - log.Info("Initializing managers...") - log.Info("") - - go func() { - ticker := time.NewTicker(30 * time.Second) - defer ticker.Stop() - - if err := InitializeNetworkManager(); err != nil { - log.Warnf("Network manager unavailable: %v", err) - } else { - notifyCapabilityChange() - return - } - - for range ticker.C { - if networkManager != nil { - return - } - if err := InitializeNetworkManager(); err == nil { - log.Info("Network manager initialized") - notifyCapabilityChange() - return - } - } - }() - - loginctlReady := make(chan struct{}) - freedesktopReady := make(chan struct{}) - - go func() { - defer close(loginctlReady) - if err := InitializeLoginctlManager(); err != nil { - log.Warnf("Loginctl manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - defer close(freedesktopReady) - if err := InitializeFreedeskManager(); err != nil { - log.Warnf("Freedesktop manager unavailable: %v", err) - } else if freedesktopManager != nil { - freedesktopManager.NotifySubscribers() - notifyCapabilityChange() - } - }() - - // Bridge loginctl lock state to the freedesktop/gnome screensaver - // ActiveChanged signal so apps like Bitwarden can detect screen lock. - go func() { - <-loginctlReady - <-freedesktopReady - - if loginctlManager == nil || freedesktopManager == nil { - return - } - - ch := loginctlManager.Subscribe("dms-lock-bridge") - defer loginctlManager.Unsubscribe("dms-lock-bridge") - - initial := loginctlManager.GetState() - lastLocked := initial.Locked - freedesktopManager.SetScreenLockActive(lastLocked) - - for state := range ch { - if state.Locked != lastLocked { - lastLocked = state.Locked - freedesktopManager.SetScreenLockActive(lastLocked) - } - } - }() - - if err := InitializeWaylandManager(); err != nil { - log.Warnf("Wayland manager unavailable: %v", err) - } - - if err := InitializeThemeModeManager(); err != nil { - log.Warnf("Theme mode manager unavailable: %v", err) - } else { - notifyCapabilityChange() - go func() { - <-loginctlReady - if loginctlManager == nil { - return - } - themeModeManager.WatchLoginctl(loginctlManager) - }() - } - - go func() { - <-loginctlReady - if loginctlManager == nil { - return - } - if err := InitializeTrayRecoveryManager(); err != nil { - log.Warnf("TrayRecovery manager unavailable: %v", err) - } else { - trayRecoveryManager.WatchLoginctl(loginctlManager) - } - }() - - go func() { - geoClient := geolocation.NewClient() - geoClientInstance = geoClient - - if waylandManager != nil { - waylandManager.SetGeoClient(geoClient) - } - if themeModeManager != nil { - themeModeManager.SetGeoClient(geoClient) - } - - if err := InitializeLocationManager(geoClient); err != nil { - log.Warnf("Location manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - if err := InitializeBluezManager(); err != nil { - log.Warnf("Bluez manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - if err := InitializeAppPickerManager(); err != nil { - log.Debugf("AppPicker manager unavailable: %v", err) - } - - if err := InitializeDwlManager(); err != nil { - log.Debugf("DWL manager unavailable: %v", err) - } - - if extworkspace.CheckCapability() { - extWorkspaceAvailable.Store(true) - log.Info("ExtWorkspace capability detected and will be available on subscription") - } else { - log.Debug("ExtWorkspace capability not available") - extWorkspaceAvailable.Store(false) - } - - if err := InitializeWlrOutputManager(); err != nil { - log.Debugf("WlrOutput manager unavailable: %v", err) - } - - fatalErrChan := make(chan error, 1) - if wlrOutputManager != nil { - go func() { - err := <-wlrOutputManager.FatalError() - fatalErrChan <- fmt.Errorf("WlrOutput fatal error: %w", err) - }() - } - if wlContext != nil { - go func() { - err := <-wlContext.FatalError() - fatalErrChan <- fmt.Errorf("wayland context fatal error: %w", err) - }() - } - - go func() { - if err := InitializeBrightnessManager(); err != nil { - log.Warnf("Brightness manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - if err := InitializeEvdevManager(); err != nil { - log.Debugf("Evdev manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - if err := InitializeClipboardManager(); err != nil { - log.Warnf("Clipboard manager unavailable: %v", err) - } - if wlContext != nil { - wlContext.Start() - log.Info("Wayland event dispatcher started") - } - }() - - go func() { - if err := InitializeDbusManager(); err != nil { - log.Warnf("DBus manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - log.Info("") - log.Infof("Ready! Capabilities: %v", getCapabilities().Capabilities) - - listenerErrChan := make(chan error, 1) - - go func() { - for { - conn, err := listener.Accept() - if err != nil { - listenerErrChan <- err - return - } - go handleConnection(conn) - } - }() - - select { - case err := <-listenerErrChan: - return err - case err := <-fatalErrChan: - return err - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server_test.go deleted file mode 100644 index 404f624..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package server - -import ( - "encoding/json" - "fmt" - "net" - "os" - "path/filepath" - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/network" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetSocketDir(t *testing.T) { - tests := []struct { - name string - xdgRuntimeDir string - uid int - expectedSubstr string - }{ - { - name: "uses XDG_RUNTIME_DIR when set", - xdgRuntimeDir: "/run/user/1000", - expectedSubstr: "/run/user/1000", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.xdgRuntimeDir != "" { - t.Setenv("XDG_RUNTIME_DIR", tt.xdgRuntimeDir) - } - - result := getSocketDir() - assert.Contains(t, result, tt.expectedSubstr) - }) - } -} - -func TestGetSocketPath(t *testing.T) { - path := GetSocketPath() - assert.Contains(t, path, "danklinux-") - assert.Contains(t, path, ".sock") - assert.Contains(t, path, fmt.Sprintf("%d", os.Getpid())) -} - -func TestGetCapabilities(t *testing.T) { - originalNetworkManager := networkManager - defer func() { networkManager = originalNetworkManager }() - - t.Run("capabilities without network manager", func(t *testing.T) { - networkManager = nil - caps := getCapabilities() - assert.Contains(t, caps.Capabilities, "plugins") - assert.NotContains(t, caps.Capabilities, "network") - }) - - t.Run("capabilities with network manager", func(t *testing.T) { - networkManager = &network.Manager{} - caps := getCapabilities() - assert.Contains(t, caps.Capabilities, "plugins") - assert.Contains(t, caps.Capabilities, "network") - }) -} - -type mockConn struct { - net.Conn - written []byte -} - -func (m *mockConn) Write(b []byte) (n int, err error) { - m.written = append(m.written, b...) - return len(b), nil -} - -func (m *mockConn) Close() error { - return nil -} - -func TestRespondError(t *testing.T) { - conn := &mockConn{} - models.RespondError(conn, 123, "test error") - - var resp models.Response[any] - err := json.Unmarshal(conn.written, &resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Equal(t, "test error", resp.Error) - assert.Nil(t, resp.Result) -} - -func TestRespond(t *testing.T) { - conn := &mockConn{} - result := map[string]string{"foo": "bar"} - models.Respond(conn, 123, result) - - var resp models.Response[map[string]string] - err := json.Unmarshal(conn.written, &resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - require.NotNil(t, resp.Result) - assert.Equal(t, "bar", (*resp.Result)["foo"]) -} - -func TestRequest_JSON(t *testing.T) { - jsonStr := `{"id":123,"method":"test.method","params":{"key":"value"}}` - var req models.Request - err := json.Unmarshal([]byte(jsonStr), &req) - require.NoError(t, err) - - assert.Equal(t, 123, req.ID) - assert.Equal(t, "test.method", req.Method) - assert.Equal(t, "value", req.Params["key"]) -} - -func TestResponse_JSON(t *testing.T) { - t.Run("success response", func(t *testing.T) { - result := "success" - resp := models.Response[string]{ - ID: 123, - Result: &result, - } - - data, err := json.Marshal(resp) - require.NoError(t, err) - - var decoded models.Response[string] - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, 123, decoded.ID) - assert.Equal(t, "success", *decoded.Result) - assert.Empty(t, decoded.Error) - }) - - t.Run("error response", func(t *testing.T) { - resp := models.Response[any]{ - ID: 123, - Error: "test error", - } - - data, err := json.Marshal(resp) - require.NoError(t, err) - - var decoded models.Response[any] - err = json.Unmarshal(data, &decoded) - require.NoError(t, err) - - assert.Equal(t, 123, decoded.ID) - assert.Equal(t, "test error", decoded.Error) - assert.Nil(t, decoded.Result) - }) -} - -func TestCleanupStaleSockets(t *testing.T) { - tempDir := t.TempDir() - t.Setenv("XDG_RUNTIME_DIR", tempDir) - - staleSocket := filepath.Join(tempDir, "danklinux-4194305.sock") - err := os.WriteFile(staleSocket, []byte{}, 0o600) - require.NoError(t, err) - - activeSocket := filepath.Join(tempDir, fmt.Sprintf("danklinux-%d.sock", os.Getpid())) - err = os.WriteFile(activeSocket, []byte{}, 0o600) - require.NoError(t, err) - - cleanupStaleSockets() - - _, err = os.Stat(staleSocket) - assert.True(t, os.IsNotExist(err)) - - _, err = os.Stat(activeSocket) - assert.NoError(t, err) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/handlers.go deleted file mode 100644 index 6891875..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/handlers.go +++ /dev/null @@ -1,154 +0,0 @@ -package thememode - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "theme mode manager not initialized") - return - } - - switch req.Method { - case "theme.auto.getState": - handleGetState(conn, req, manager) - case "theme.auto.setEnabled": - handleSetEnabled(conn, req, manager) - case "theme.auto.setMode": - handleSetMode(conn, req, manager) - case "theme.auto.setSchedule": - handleSetSchedule(conn, req, manager) - case "theme.auto.setLocation": - handleSetLocation(conn, req, manager) - case "theme.auto.setUseIPLocation": - handleSetUseIPLocation(conn, req, manager) - case "theme.auto.trigger": - handleTrigger(conn, req, manager) - case "theme.auto.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleSetEnabled(conn net.Conn, req models.Request, manager *Manager) { - enabled, err := params.Bool(req.Params, "enabled") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetEnabled(enabled) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto enabled set"}) -} - -func handleSetMode(conn net.Conn, req models.Request, manager *Manager) { - mode, err := params.String(req.Params, "mode") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if mode != "time" && mode != "location" { - models.RespondError(conn, req.ID, "invalid mode") - return - } - - manager.SetMode(mode) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto mode set"}) -} - -func handleSetSchedule(conn net.Conn, req models.Request, manager *Manager) { - startHour, err := params.Int(req.Params, "startHour") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - startMinute, err := params.Int(req.Params, "startMinute") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - endHour, err := params.Int(req.Params, "endHour") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - endMinute, err := params.Int(req.Params, "endMinute") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.ValidateSchedule(startHour, startMinute, endHour, endMinute); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetSchedule(startHour, startMinute, endHour, endMinute) - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) { - lat, err := params.Float(req.Params, "latitude") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - lon, err := params.Float(req.Params, "longitude") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetLocation(lat, lon) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto location set"}) -} - -func handleSetUseIPLocation(conn net.Conn, req models.Request, manager *Manager) { - use, err := params.Bool(req.Params, "use") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetUseIPLocation(use) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto IP location set"}) -} - -func handleTrigger(conn net.Conn, req models.Request, manager *Manager) { - manager.TriggerUpdate() - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto update triggered"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/manager.go deleted file mode 100644 index f7644ad..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/manager.go +++ /dev/null @@ -1,462 +0,0 @@ -package thememode - -import ( - "errors" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -const ( - defaultStartHour = 18 - defaultStartMinute = 0 - defaultEndHour = 6 - defaultEndMinute = 0 - defaultElevationTwilight = -6.0 - defaultElevationDaylight = 3.0 -) - -type Manager struct { - config Config - configMutex sync.RWMutex - - state *State - stateMutex sync.RWMutex - - subscribers syncmap.Map[string, chan State] - - locationMutex sync.RWMutex - cachedIPLat *float64 - cachedIPLon *float64 - - geoClient geolocation.Client - - stopChan chan struct{} - updateTrigger chan struct{} - wg sync.WaitGroup -} - -func NewManager() *Manager { - m := &Manager{ - config: Config{ - Enabled: false, - Mode: "time", - StartHour: defaultStartHour, - StartMinute: defaultStartMinute, - EndHour: defaultEndHour, - EndMinute: defaultEndMinute, - ElevationTwilight: defaultElevationTwilight, - ElevationDaylight: defaultElevationDaylight, - }, - stopChan: make(chan struct{}), - updateTrigger: make(chan struct{}, 1), - } - - m.updateState(time.Now()) - - m.wg.Add(1) - go m.schedulerLoop() - - return m -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{Config: m.getConfig()} - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) SetEnabled(enabled bool) { - m.configMutex.Lock() - if m.config.Enabled == enabled { - m.configMutex.Unlock() - return - } - m.config.Enabled = enabled - m.configMutex.Unlock() - m.TriggerUpdate() -} - -func (m *Manager) SetMode(mode string) { - m.configMutex.Lock() - if m.config.Mode == mode { - m.configMutex.Unlock() - return - } - m.config.Mode = mode - m.configMutex.Unlock() - m.TriggerUpdate() -} - -func (m *Manager) SetSchedule(startHour, startMinute, endHour, endMinute int) { - m.configMutex.Lock() - changed := m.config.StartHour != startHour || - m.config.StartMinute != startMinute || - m.config.EndHour != endHour || - m.config.EndMinute != endMinute - if !changed { - m.configMutex.Unlock() - return - } - m.config.StartHour = startHour - m.config.StartMinute = startMinute - m.config.EndHour = endHour - m.config.EndMinute = endMinute - m.configMutex.Unlock() - m.TriggerUpdate() -} - -func (m *Manager) SetLocation(lat, lon float64) { - m.configMutex.Lock() - if m.config.Latitude != nil && m.config.Longitude != nil && - *m.config.Latitude == lat && *m.config.Longitude == lon && !m.config.UseIPLocation { - m.configMutex.Unlock() - return - } - m.config.Latitude = &lat - m.config.Longitude = &lon - m.config.UseIPLocation = false - m.configMutex.Unlock() - - m.locationMutex.Lock() - m.cachedIPLat = nil - m.cachedIPLon = nil - m.locationMutex.Unlock() - - m.TriggerUpdate() -} - -func (m *Manager) SetUseIPLocation(use bool) { - m.configMutex.Lock() - if m.config.UseIPLocation == use { - m.configMutex.Unlock() - return - } - m.config.UseIPLocation = use - if use { - m.config.Latitude = nil - m.config.Longitude = nil - } - m.configMutex.Unlock() - - if use { - m.locationMutex.Lock() - m.cachedIPLat = nil - m.cachedIPLon = nil - m.locationMutex.Unlock() - } - - m.TriggerUpdate() -} - -func (m *Manager) TriggerUpdate() { - select { - case m.updateTrigger <- struct{}{}: - default: - } -} - -func (m *Manager) Close() { - select { - case <-m.stopChan: - return - default: - close(m.stopChan) - } - m.wg.Wait() - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) -} - -func (m *Manager) WatchLoginctl(lm *loginctl.Manager) { - ch := lm.Subscribe("thememode") - m.wg.Add(1) - go func() { - defer m.wg.Done() - defer lm.Unsubscribe("thememode") - for { - select { - case <-m.stopChan: - return - case state, ok := <-ch: - if !ok { - return - } - if state.PreparingForSleep { - continue - } - m.TriggerUpdate() - } - } - }() -} - -func (m *Manager) schedulerLoop() { - defer m.wg.Done() - - var timer *time.Timer - for { - config := m.getConfig() - now := time.Now() - var isLight bool - var next time.Time - if config.Enabled { - isLight, next = m.computeSchedule(now, config) - } else { - m.stateMutex.RLock() - if m.state != nil { - isLight = m.state.IsLight - } - m.stateMutex.RUnlock() - next = now.Add(24 * time.Hour) - } - - m.updateStateWithValues(config, isLight, next) - - waitDur := time.Until(next) - if !config.Enabled { - waitDur = 24 * time.Hour - } - if waitDur < time.Second { - waitDur = time.Second - } - - if timer != nil { - timer.Stop() - } - timer = time.NewTimer(waitDur) - - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.updateTrigger: - timer.Stop() - continue - case <-timer.C: - continue - } - } -} - -func (m *Manager) updateState(now time.Time) { - config := m.getConfig() - var isLight bool - var next time.Time - if config.Enabled { - isLight, next = m.computeSchedule(now, config) - } else { - m.stateMutex.RLock() - if m.state != nil { - isLight = m.state.IsLight - } - m.stateMutex.RUnlock() - next = now.Add(24 * time.Hour) - } - m.updateStateWithValues(config, isLight, next) -} - -func (m *Manager) updateStateWithValues(config Config, isLight bool, next time.Time) { - newState := State{ - Config: config, - IsLight: isLight, - NextTransition: next, - } - - m.stateMutex.Lock() - if m.state != nil && statesEqual(m.state, &newState) { - m.stateMutex.Unlock() - return - } - m.state = &newState - m.stateMutex.Unlock() - - m.notifySubscribers() -} - -func (m *Manager) notifySubscribers() { - state := m.GetState() - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- state: - default: - } - return true - }) -} - -func (m *Manager) getConfig() Config { - m.configMutex.RLock() - defer m.configMutex.RUnlock() - return m.config -} - -func (m *Manager) SetGeoClient(client geolocation.Client) { - m.geoClient = client -} - -func (m *Manager) getLocation(config Config) (*float64, *float64) { - if config.Latitude != nil && config.Longitude != nil { - return config.Latitude, config.Longitude - } - if !config.UseIPLocation { - return nil, nil - } - if m.geoClient == nil { - return nil, nil - } - - m.locationMutex.RLock() - if m.cachedIPLat != nil && m.cachedIPLon != nil { - lat, lon := m.cachedIPLat, m.cachedIPLon - m.locationMutex.RUnlock() - return lat, lon - } - m.locationMutex.RUnlock() - - location, err := m.geoClient.GetLocation() - if err != nil { - return nil, nil - } - - m.locationMutex.Lock() - m.cachedIPLat = &location.Latitude - m.cachedIPLon = &location.Longitude - m.locationMutex.Unlock() - - return m.cachedIPLat, m.cachedIPLon -} - -func statesEqual(a, b *State) bool { - if a == nil || b == nil { - return a == b - } - if a.IsLight != b.IsLight || !a.NextTransition.Equal(b.NextTransition) { - return false - } - return a.Config == b.Config -} - -func (m *Manager) computeSchedule(now time.Time, config Config) (bool, time.Time) { - switch config.Mode { - case "location": - return m.computeLocationSchedule(now, config) - default: - return computeTimeSchedule(now, config) - } -} - -func computeTimeSchedule(now time.Time, config Config) (bool, time.Time) { - startMinutes := config.StartHour*60 + config.StartMinute - endMinutes := config.EndHour*60 + config.EndMinute - currentMinutes := now.Hour()*60 + now.Minute() - - startTime := time.Date(now.Year(), now.Month(), now.Day(), config.StartHour, config.StartMinute, 0, 0, now.Location()) - endTime := time.Date(now.Year(), now.Month(), now.Day(), config.EndHour, config.EndMinute, 0, 0, now.Location()) - - if startMinutes == endMinutes { - next := startTime - if !next.After(now) { - next = next.Add(24 * time.Hour) - } - return true, next - } - - if startMinutes < endMinutes { - if currentMinutes < startMinutes { - return true, startTime - } - if currentMinutes >= endMinutes { - return true, startTime.Add(24 * time.Hour) - } - return false, endTime - } - - if currentMinutes >= startMinutes { - return false, endTime.Add(24 * time.Hour) - } - if currentMinutes < endMinutes { - return false, endTime - } - return true, startTime -} - -func (m *Manager) computeLocationSchedule(now time.Time, config Config) (bool, time.Time) { - lat, lon := m.getLocation(config) - if lat == nil || lon == nil { - currentIsLight := false - m.stateMutex.RLock() - if m.state != nil { - currentIsLight = m.state.IsLight - } - m.stateMutex.RUnlock() - return currentIsLight, now.Add(10 * time.Minute) - } - - times, cond := wayland.CalculateSunTimesWithTwilight(*lat, *lon, now, config.ElevationTwilight, config.ElevationDaylight) - switch cond { - case wayland.SunMidnightSun: - return true, startOfNextDay(now) - case wayland.SunPolarNight: - return false, startOfNextDay(now) - } - - if now.Before(times.Sunrise) { - return false, times.Sunrise - } - if now.Before(times.Sunset) { - return true, times.Sunset - } - - nextDay := startOfNextDay(now) - nextTimes, nextCond := wayland.CalculateSunTimesWithTwilight(*lat, *lon, nextDay, config.ElevationTwilight, config.ElevationDaylight) - switch nextCond { - case wayland.SunMidnightSun: - return true, startOfNextDay(nextDay) - case wayland.SunPolarNight: - return false, startOfNextDay(nextDay) - } - - return false, nextTimes.Sunrise -} - -func startOfNextDay(t time.Time) time.Time { - next := t.Add(24 * time.Hour) - return time.Date(next.Year(), next.Month(), next.Day(), 0, 0, 0, 0, next.Location()) -} - -func validateHourMinute(hour, minute int) bool { - return hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 -} - -func (m *Manager) ValidateSchedule(startHour, startMinute, endHour, endMinute int) error { - if !validateHourMinute(startHour, startMinute) || !validateHourMinute(endHour, endMinute) { - return errInvalidTime - } - return nil -} - -var errInvalidTime = errors.New("invalid schedule time") diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/types.go deleted file mode 100644 index 72e6468..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/types.go +++ /dev/null @@ -1,23 +0,0 @@ -package thememode - -import "time" - -type Config struct { - Enabled bool `json:"enabled"` - Mode string `json:"mode"` - StartHour int `json:"startHour"` - StartMinute int `json:"startMinute"` - EndHour int `json:"endHour"` - EndMinute int `json:"endMinute"` - Latitude *float64 `json:"latitude,omitempty"` - Longitude *float64 `json:"longitude,omitempty"` - UseIPLocation bool `json:"useIPLocation"` - ElevationTwilight float64 `json:"elevationTwilight"` - ElevationDaylight float64 `json:"elevationDaylight"` -} - -type State struct { - Config Config `json:"config"` - IsLight bool `json:"isLight"` - NextTransition time.Time `json:"nextTransition"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/handlers.go deleted file mode 100644 index b3b308a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/handlers.go +++ /dev/null @@ -1,27 +0,0 @@ -package themes - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -func HandleRequest(conn net.Conn, req models.Request) { - switch req.Method { - case "themes.list": - HandleList(conn, req) - case "themes.listInstalled": - HandleListInstalled(conn, req) - case "themes.install": - HandleInstall(conn, req) - case "themes.uninstall": - HandleUninstall(conn, req) - case "themes.update": - HandleUpdate(conn, req) - case "themes.search": - HandleSearch(conn, req) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/install.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/install.go deleted file mode 100644 index 26d22ba..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/install.go +++ /dev/null @@ -1,52 +0,0 @@ -package themes - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/themes" -) - -func HandleInstall(conn net.Conn, req models.Request) { - idOrName, ok := models.Get[string](req, "name") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - registry, err := themes.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - themeList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list themes: %v", err)) - return - } - - theme := themes.FindByIDOrName(idOrName, themeList) - if theme == nil { - models.RespondError(conn, req.ID, fmt.Sprintf("theme not found: %s", idOrName)) - return - } - - manager, err := themes.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - registryThemeDir := registry.GetThemeDir(theme.SourceDir) - if err := manager.Install(*theme, registryThemeDir); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to install theme: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{ - Success: true, - Message: fmt.Sprintf("theme installed: %s", theme.Name), - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/list.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/list.go deleted file mode 100644 index 9564be1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/list.go +++ /dev/null @@ -1,54 +0,0 @@ -package themes - -import ( - "fmt" - "net" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/themes" -) - -func HandleList(conn net.Conn, req models.Request) { - registry, err := themes.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - themeList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list themes: %v", err)) - return - } - - manager, err := themes.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - result := make([]ThemeInfo, len(themeList)) - for i, t := range themeList { - installed, _ := manager.IsInstalled(t) - info := ThemeInfo{ - ID: t.ID, - Name: t.Name, - Version: t.Version, - Author: t.Author, - Description: t.Description, - PreviewPath: t.PreviewPath, - SourceDir: t.SourceDir, - Installed: installed, - FirstParty: isFirstParty(t.Author), - } - addVariantsInfo(&info, t.Variants) - result[i] = info - } - - models.Respond(conn, req.ID, result) -} - -func isFirstParty(author string) bool { - return strings.EqualFold(author, "Avenge Media") || strings.EqualFold(author, "AvengeMedia") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/list_installed.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/list_installed.go deleted file mode 100644 index 49be81b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/list_installed.go +++ /dev/null @@ -1,149 +0,0 @@ -package themes - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/themes" -) - -func addVariantsInfo(info *ThemeInfo, variants *themes.ThemeVariants) { - if variants == nil { - return - } - - if variants.Type == "multi" { - if len(variants.Flavors) == 0 && len(variants.Accents) == 0 { - return - } - info.HasVariants = true - info.Variants = &VariantsInfo{ - Type: "multi", - Flavors: make([]FlavorInfo, len(variants.Flavors)), - Accents: make([]AccentInfo, len(variants.Accents)), - } - if variants.Defaults != nil { - info.Variants.Defaults = &MultiDefaults{ - Dark: variants.Defaults.Dark, - Light: variants.Defaults.Light, - } - } - for i, f := range variants.Flavors { - mode := "" - switch { - case f.Dark.Primary != "" && f.Light.Primary != "": - mode = "both" - case f.Dark.Primary != "": - mode = "dark" - case f.Light.Primary != "": - mode = "light" - default: - if f.Dark.Surface != "" { - mode = "dark" - } else if f.Light.Surface != "" { - mode = "light" - } - } - info.Variants.Flavors[i] = FlavorInfo{ID: f.ID, Name: f.Name, Mode: mode} - } - for i, a := range variants.Accents { - color := "" - if colors, ok := a.FlavorColors["mocha"]; ok && colors.Primary != "" { - color = colors.Primary - } else if colors, ok := a.FlavorColors["latte"]; ok && colors.Primary != "" { - color = colors.Primary - } else { - for _, c := range a.FlavorColors { - if c.Primary != "" { - color = c.Primary - break - } - } - } - info.Variants.Accents[i] = AccentInfo{ID: a.ID, Name: a.Name, Color: color} - } - return - } - - if len(variants.Options) == 0 { - return - } - info.HasVariants = true - info.Variants = &VariantsInfo{ - Default: variants.Default, - Options: make([]VariantInfo, len(variants.Options)), - } - for i, v := range variants.Options { - info.Variants.Options[i] = VariantInfo{ID: v.ID, Name: v.Name} - } -} - -func HandleListInstalled(conn net.Conn, req models.Request) { - manager, err := themes.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - installedIDs, err := manager.ListInstalled() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list installed themes: %v", err)) - return - } - - themeMap := make(map[string]themes.Theme) - if registry, err := themes.NewRegistry(); err == nil { - if allThemes, err := registry.List(); err == nil { - for _, t := range allThemes { - themeMap[t.ID] = t - } - } - } - - result := make([]ThemeInfo, 0, len(installedIDs)) - for _, id := range installedIDs { - if theme, ok := themeMap[id]; ok { - hasUpdate := false - if hasUpdates, err := manager.HasUpdates(id, theme); err == nil { - hasUpdate = hasUpdates - } - - info := ThemeInfo{ - ID: theme.ID, - Name: theme.Name, - Version: theme.Version, - Author: theme.Author, - Description: theme.Description, - SourceDir: id, - FirstParty: isFirstParty(theme.Author), - HasUpdate: hasUpdate, - } - addVariantsInfo(&info, theme.Variants) - result = append(result, info) - } else { - installed, err := manager.GetInstalledTheme(id) - if err != nil { - result = append(result, ThemeInfo{ - ID: id, - Name: id, - SourceDir: id, - }) - continue - } - info := ThemeInfo{ - ID: installed.ID, - Name: installed.Name, - Version: installed.Version, - Author: installed.Author, - Description: installed.Description, - SourceDir: id, - FirstParty: isFirstParty(installed.Author), - } - addVariantsInfo(&info, installed.Variants) - result = append(result, info) - } - } - - models.Respond(conn, req.ID, result) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/search.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/search.go deleted file mode 100644 index 2823fe0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/search.go +++ /dev/null @@ -1,53 +0,0 @@ -package themes - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/themes" -) - -func HandleSearch(conn net.Conn, req models.Request) { - query, ok := models.Get[string](req, "query") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'query' parameter") - return - } - - registry, err := themes.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - themeList, err := registry.List() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to list themes: %v", err)) - return - } - - searchResults := themes.FuzzySearch(query, themeList) - - manager, err := themes.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - result := make([]ThemeInfo, len(searchResults)) - for i, t := range searchResults { - installed, _ := manager.IsInstalled(t) - result[i] = ThemeInfo{ - ID: t.ID, - Name: t.Name, - Version: t.Version, - Author: t.Author, - Description: t.Description, - Installed: installed, - FirstParty: isFirstParty(t.Author), - } - } - - models.Respond(conn, req.ID, result) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/types.go deleted file mode 100644 index 0ad19b4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/types.go +++ /dev/null @@ -1,47 +0,0 @@ -package themes - -type VariantInfo struct { - ID string `json:"id"` - Name string `json:"name"` -} - -type FlavorInfo struct { - ID string `json:"id"` - Name string `json:"name"` - Mode string `json:"mode,omitempty"` -} - -type AccentInfo struct { - ID string `json:"id"` - Name string `json:"name"` - Color string `json:"color,omitempty"` -} - -type MultiDefaults struct { - Dark map[string]string `json:"dark,omitempty"` - Light map[string]string `json:"light,omitempty"` -} - -type VariantsInfo struct { - Type string `json:"type,omitempty"` - Default string `json:"default,omitempty"` - Defaults *MultiDefaults `json:"defaults,omitempty"` - Options []VariantInfo `json:"options,omitempty"` - Flavors []FlavorInfo `json:"flavors,omitempty"` - Accents []AccentInfo `json:"accents,omitempty"` -} - -type ThemeInfo struct { - ID string `json:"id"` - Name string `json:"name"` - Version string `json:"version"` - Author string `json:"author,omitempty"` - Description string `json:"description,omitempty"` - PreviewPath string `json:"previewPath,omitempty"` - SourceDir string `json:"sourceDir,omitempty"` - Installed bool `json:"installed,omitempty"` - FirstParty bool `json:"firstParty,omitempty"` - HasUpdate bool `json:"hasUpdate,omitempty"` - HasVariants bool `json:"hasVariants,omitempty"` - Variants *VariantsInfo `json:"variants,omitempty"` -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/uninstall.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/uninstall.go deleted file mode 100644 index 9b453e4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/uninstall.go +++ /dev/null @@ -1,63 +0,0 @@ -package themes - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/themes" -) - -func HandleUninstall(conn net.Conn, req models.Request) { - idOrName, ok := models.Get[string](req, "name") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - manager, err := themes.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - registry, err := themes.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - themeList, _ := registry.List() - theme := themes.FindByIDOrName(idOrName, themeList) - - if theme != nil { - installed, err := manager.IsInstalled(*theme) - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to check if theme is installed: %v", err)) - return - } - if !installed { - models.RespondError(conn, req.ID, fmt.Sprintf("theme not installed: %s", idOrName)) - return - } - if err := manager.Uninstall(*theme); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to uninstall theme: %v", err)) - return - } - models.Respond(conn, req.ID, models.SuccessResult{ - Success: true, - Message: fmt.Sprintf("theme uninstalled: %s", theme.Name), - }) - return - } - - if err := manager.UninstallByID(idOrName); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("theme not found: %s", idOrName)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{ - Success: true, - Message: fmt.Sprintf("theme uninstalled: %s", idOrName), - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/update.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/update.go deleted file mode 100644 index fb9f81f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/themes/update.go +++ /dev/null @@ -1,57 +0,0 @@ -package themes - -import ( - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/themes" -) - -func HandleUpdate(conn net.Conn, req models.Request) { - idOrName, ok := models.Get[string](req, "name") - if !ok { - models.RespondError(conn, req.ID, "missing or invalid 'name' parameter") - return - } - - manager, err := themes.NewManager() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create manager: %v", err)) - return - } - - registry, err := themes.NewRegistry() - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to create registry: %v", err)) - return - } - - themeList, _ := registry.List() - theme := themes.FindByIDOrName(idOrName, themeList) - - if theme == nil { - models.RespondError(conn, req.ID, fmt.Sprintf("theme not found in registry: %s", idOrName)) - return - } - - installed, err := manager.IsInstalled(*theme) - if err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to check if theme is installed: %v", err)) - return - } - if !installed { - models.RespondError(conn, req.ID, fmt.Sprintf("theme not installed: %s", idOrName)) - return - } - - if err := manager.Update(*theme); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("failed to update theme: %v", err)) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{ - Success: true, - Message: fmt.Sprintf("theme updated: %s", theme.Name), - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/manager.go deleted file mode 100644 index 3610f1b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/manager.go +++ /dev/null @@ -1,93 +0,0 @@ -package trayrecovery - -import ( - "fmt" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl" - "github.com/godbus/dbus/v5" -) - -const resumeDelay = 3 * time.Second - -type Manager struct { - conn *dbus.Conn - stopChan chan struct{} - wg sync.WaitGroup -} - -func NewManager() (*Manager, error) { - conn, err := dbus.ConnectSessionBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to session bus: %w", err) - } - - m := &Manager{ - conn: conn, - stopChan: make(chan struct{}), - } - - // Run a startup scan after a delay — covers the case where the process - // was killed during suspend and restarted by systemd (Type=dbus). - // The fresh process never sees the PrepareForSleep true→false transition, - // so the loginctl watcher alone is not enough. - go m.scheduleRecovery() - - return m, nil -} - -// WatchLoginctl subscribes to loginctl session state changes and triggers -// tray recovery after resume from suspend (PrepareForSleep false transition). -// This handles the case where the process survives suspend. -func (m *Manager) WatchLoginctl(lm *loginctl.Manager) { - ch := lm.Subscribe("tray-recovery") - m.wg.Add(1) - go func() { - defer m.wg.Done() - defer lm.Unsubscribe("tray-recovery") - - wasSleeping := false - for { - select { - case <-m.stopChan: - return - case state, ok := <-ch: - if !ok { - return - } - if state.PreparingForSleep { - wasSleeping = true - continue - } - if wasSleeping { - wasSleeping = false - go m.scheduleRecovery() - } - } - } - }() -} - -func (m *Manager) scheduleRecovery() { - select { - case <-time.After(resumeDelay): - m.recoverTrayItems() - case <-m.stopChan: - } -} - -func (m *Manager) Close() { - select { - case <-m.stopChan: - return - default: - close(m.stopChan) - } - m.wg.Wait() - if m.conn != nil { - m.conn.Close() - } - log.Info("TrayRecovery manager closed") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/recovery.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/recovery.go deleted file mode 100644 index c7b98fb..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/trayrecovery/recovery.go +++ /dev/null @@ -1,262 +0,0 @@ -package trayrecovery - -import ( - "context" - "strings" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" -) - -const ( - sniWatcherDest = "org.kde.StatusNotifierWatcher" - sniWatcherPath = "/StatusNotifierWatcher" - sniWatcherIface = "org.kde.StatusNotifierWatcher" - sniItemIface = "org.kde.StatusNotifierItem" - dbusIface = "org.freedesktop.DBus" - propsIface = "org.freedesktop.DBus.Properties" - probeTimeout = 300 * time.Millisecond - connProbeTimeout = 150 * time.Millisecond - batchSize = 30 -) - -var excludedPrefixes = []string{ - "org.freedesktop.", - "org.gnome.", - "org.kde.StatusNotifier", - "com.canonical.AppMenu", - "org.mpris.", - "org.pipewire.", - "org.pulseaudio", - "fi.epitaph", - "quickshell", - "org.kde.quickshell", -} - -func (m *Manager) recoverTrayItems() { - registeredItems := m.getRegisteredItems() - allNames := m.getDBusNames() - if allNames == nil { - return - } - - registeredConnIDs := m.buildRegisteredConnIDs(registeredItems) - - count := len(registeredItems) - log.Infof("TrayRecoveryService: scanning DBus for unregistered SNI items (%d already registered)...", count) - - m.scanWellKnownNames(allNames, registeredItems, registeredConnIDs) - m.scanConnectionIDs(allNames, registeredItems, registeredConnIDs) -} - -func (m *Manager) getRegisteredItems() []string { - obj := m.conn.Object(sniWatcherDest, sniWatcherPath) - variant, err := obj.GetProperty(sniWatcherIface + ".RegisteredStatusNotifierItems") - if err != nil { - log.Warnf("TrayRecoveryService: failed to get registered items: %v", err) - return nil - } - - switch v := variant.Value().(type) { - case []string: - return v - case []any: - items := make([]string, 0, len(v)) - for _, elem := range v { - if s, ok := elem.(string); ok { - items = append(items, s) - } - } - return items - } - return nil -} - -func (m *Manager) getDBusNames() []string { - var names []string - err := m.conn.BusObject().Call(dbusIface+".ListNames", 0).Store(&names) - if err != nil { - log.Warnf("TrayRecoveryService: failed to list bus names: %v", err) - return nil - } - return names -} - -func (m *Manager) getNameOwner(name string) string { - var owner string - err := m.conn.BusObject().Call(dbusIface+".GetNameOwner", 0, name).Store(&owner) - if err != nil { - return "" - } - return owner -} - -// buildRegisteredConnIDs resolves every registered SNI item (well-known name -// or :1.xxx connection ID) to a canonical connection ID. This prevents -// duplicates in both directions. -func (m *Manager) buildRegisteredConnIDs(registeredItems []string) map[string]bool { - connIDs := make(map[string]bool, len(registeredItems)) - for _, item := range registeredItems { - name := extractName(item) - if strings.HasPrefix(name, ":1.") { - connIDs[name] = true - } else { - owner := m.getNameOwner(name) - if owner != "" { - connIDs[owner] = true - } - } - } - return connIDs -} - -// scanWellKnownNames probes well-known names (e.g. DinoX, nm-applet) for -// unregistered SNI items and re-registers them. -func (m *Manager) scanWellKnownNames(allNames []string, registeredItems []string, registeredConnIDs map[string]bool) { - registeredRaw := strings.Join(registeredItems, "\n") - - for _, name := range allNames { - if strings.HasPrefix(name, ":") { - continue - } - - if strings.Contains(registeredRaw, name) { - continue - } - - // Skip if this name's connection ID is already in the registered set - // (handles the case where the app registered via connection ID instead) - connForName := m.getNameOwner(name) - if connForName != "" && registeredConnIDs[connForName] { - continue - } - - if isExcludedName(name) { - continue - } - - short := shortName(name) - objectPaths := []string{ - "/StatusNotifierItem", - "/org/ayatana/NotificationItem/" + short, - } - - for _, objPath := range objectPaths { - if m.probeSNI(name, objPath, probeTimeout) { - m.registerSNI(name) - // Update set so the connection-ID section won't double-register this app - if connForName != "" { - registeredConnIDs[connForName] = true - } - break - } - } - } -} - -// scanConnectionIDs probes all :1.xxx connections in parallel for unregistered -// SNI items (e.g. Vesktop, Electron apps). Most non-SNI connections return an -// error instantly, so this is fast. -func (m *Manager) scanConnectionIDs(allNames []string, registeredItems []string, registeredConnIDs map[string]bool) { - registeredRaw := strings.Join(registeredItems, "\n") - registeredLower := strings.ToLower(registeredRaw) - - var wg sync.WaitGroup - sem := make(chan struct{}, batchSize) - - for _, name := range allNames { - if !strings.HasPrefix(name, ":1.") { - continue - } - if registeredConnIDs[name] { - continue - } - - sem <- struct{}{} - wg.Add(1) - go func(conn string) { - defer wg.Done() - defer func() { <-sem }() - - sniID := m.getSNIId(conn, connProbeTimeout) - if sniID == "" { - return - } - - // Skip if an item with the same Id is already registered (case-insensitive) - if strings.Contains(registeredLower, strings.ToLower(sniID)) { - return - } - - m.registerSNI(conn) - log.Infof("TrayRecovery: re-registered %s (Id: %s)", conn, sniID) - }(name) - } - wg.Wait() -} - -func (m *Manager) probeSNI(dest, path string, timeout time.Duration) bool { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - obj := m.conn.Object(dest, dbus.ObjectPath(path)) - var props map[string]dbus.Variant - err := obj.CallWithContext(ctx, propsIface+".GetAll", 0, sniItemIface).Store(&props) - if err != nil { - return false - } - - _, hasID := props["Id"] - return hasID -} - -func (m *Manager) getSNIId(dest string, timeout time.Duration) string { - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - obj := m.conn.Object(dest, "/StatusNotifierItem") - var variant dbus.Variant - err := obj.CallWithContext(ctx, propsIface+".Get", 0, sniItemIface, "Id").Store(&variant) - if err != nil { - return "" - } - - id, _ := variant.Value().(string) - return id -} - -func (m *Manager) registerSNI(name string) { - obj := m.conn.Object(sniWatcherDest, sniWatcherPath) - call := obj.Call(sniWatcherIface+".RegisterStatusNotifierItem", 0, name) - if call.Err != nil { - log.Warnf("TrayRecovery: failed to register %s: %v", name, call.Err) - return - } - log.Infof("TrayRecovery: re-registered %s", name) -} - -func extractName(item string) string { - if idx := strings.IndexByte(item, '/'); idx != -1 { - return item[:idx] - } - return item -} - -func shortName(name string) string { - parts := strings.Split(name, ".") - if len(parts) > 0 { - return parts[len(parts)-1] - } - return name -} - -func isExcludedName(name string) bool { - for _, prefix := range excludedPrefixes { - if strings.HasPrefix(name, prefix) { - return true - } - } - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/gamma.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/gamma.go deleted file mode 100644 index 1fff03f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/gamma.go +++ /dev/null @@ -1,167 +0,0 @@ -package wayland - -import ( - "math" -) - -type GammaRamp struct { - Red []uint16 - Green []uint16 - Blue []uint16 -} - -type rgb struct { - r, g, b float64 -} - -type xyz struct { - x, y, z float64 -} - -func illuminantD(temp int) (float64, float64, bool) { - var x float64 - switch { - case temp >= 2500 && temp <= 7000: - t := float64(temp) - x = 0.244063 + 0.09911e3/t + 2.9678e6/(t*t) - 4.6070e9/(t*t*t) - case temp > 7000 && temp <= 25000: - t := float64(temp) - x = 0.237040 + 0.24748e3/t + 1.9018e6/(t*t) - 2.0064e9/(t*t*t) - default: - return 0, 0, false - } - y := -3*(x*x) + 2.870*x - 0.275 - return x, y, true -} - -func planckianLocus(temp int) (float64, float64, bool) { - var x, y float64 - switch { - case temp >= 1667 && temp <= 4000: - t := float64(temp) - x = -0.2661239e9/(t*t*t) - 0.2343589e6/(t*t) + 0.8776956e3/t + 0.179910 - if temp <= 2222 { - y = -1.1064814*(x*x*x) - 1.34811020*(x*x) + 2.18555832*x - 0.20219683 - } else { - y = -0.9549476*(x*x*x) - 1.37418593*(x*x) + 2.09137015*x - 0.16748867 - } - case temp > 4000 && temp < 25000: - t := float64(temp) - x = -3.0258469e9/(t*t*t) + 2.1070379e6/(t*t) + 0.2226347e3/t + 0.240390 - y = 3.0817580*(x*x*x) - 5.87338670*(x*x) + 3.75112997*x - 0.37001483 - default: - return 0, 0, false - } - return x, y, true -} - -func srgbGamma(value, gamma float64) float64 { - if value <= 0.0031308 { - return 12.92 * value - } - return math.Pow(1.055*value, 1.0/gamma) - 0.055 -} - -func clamp01(v float64) float64 { - switch { - case v > 1.0: - return 1.0 - case v < 0.0: - return 0.0 - default: - return v - } -} - -func xyzToSRGB(c xyz) rgb { - return rgb{ - r: srgbGamma(clamp01(3.2404542*c.x-1.5371385*c.y-0.4985314*c.z), 2.2), - g: srgbGamma(clamp01(-0.9692660*c.x+1.8760108*c.y+0.0415560*c.z), 2.2), - b: srgbGamma(clamp01(0.0556434*c.x-0.2040259*c.y+1.0572252*c.z), 2.2), - } -} - -func normalizeRGB(c *rgb) { - maxw := math.Max(c.r, math.Max(c.g, c.b)) - if maxw > 0 { - c.r /= maxw - c.g /= maxw - c.b /= maxw - } -} - -func calcWhitepoint(temp int) rgb { - if temp == 6500 { - return rgb{r: 1.0, g: 1.0, b: 1.0} - } - - var wp xyz - - switch { - case temp >= 25000: - x, y, _ := illuminantD(25000) - wp.x = x - wp.y = y - case temp >= 4000: - x, y, _ := illuminantD(temp) - wp.x = x - wp.y = y - case temp >= 2500: - x1, y1, _ := illuminantD(temp) - x2, y2, _ := planckianLocus(temp) - factor := float64(4000-temp) / 1500.0 - sineFactor := (math.Cos(math.Pi*factor) + 1.0) / 2.0 - wp.x = x1*sineFactor + x2*(1.0-sineFactor) - wp.y = y1*sineFactor + y2*(1.0-sineFactor) - default: - t := temp - if t < 1667 { - t = 1667 - } - x, y, _ := planckianLocus(t) - wp.x = x - wp.y = y - } - - wp.z = 1.0 - wp.x - wp.y - - wpRGB := xyzToSRGB(wp) - normalizeRGB(&wpRGB) - return wpRGB -} - -func GenerateGammaRamp(size uint32, temp int, gamma float64) GammaRamp { - ramp := GammaRamp{ - Red: make([]uint16, size), - Green: make([]uint16, size), - Blue: make([]uint16, size), - } - - wp := calcWhitepoint(temp) - - for i := uint32(0); i < size; i++ { - val := float64(i) / float64(size-1) - ramp.Red[i] = uint16(clamp01(math.Pow(val*wp.r, 1.0/gamma)) * 65535.0) - ramp.Green[i] = uint16(clamp01(math.Pow(val*wp.g, 1.0/gamma)) * 65535.0) - ramp.Blue[i] = uint16(clamp01(math.Pow(val*wp.b, 1.0/gamma)) * 65535.0) - } - - return ramp -} - -func GenerateIdentityRamp(size uint32) GammaRamp { - ramp := GammaRamp{ - Red: make([]uint16, size), - Green: make([]uint16, size), - Blue: make([]uint16, size), - } - - for i := uint32(0); i < size; i++ { - val := uint16((float64(i) / float64(size-1)) * 65535.0) - ramp.Red[i] = val - ramp.Green[i] = val - ramp.Blue[i] = val - } - - return ramp -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/gamma_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/gamma_test.go deleted file mode 100644 index 0bf8e71..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/gamma_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package wayland - -import ( - "testing" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -func TestGenerateGammaRamp(t *testing.T) { - tests := []struct { - name string - size uint32 - temp int - gamma float64 - }{ - {"small_warm", 16, 6500, 1.0}, - {"small_cool", 16, 4000, 1.0}, - {"large_warm", 256, 6500, 1.0}, - {"large_cool", 256, 4000, 1.0}, - {"custom_gamma", 64, 5500, 1.2}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ramp := GenerateGammaRamp(tt.size, tt.temp, tt.gamma) - - if len(ramp.Red) != int(tt.size) { - t.Errorf("expected %d red values, got %d", tt.size, len(ramp.Red)) - } - if len(ramp.Green) != int(tt.size) { - t.Errorf("expected %d green values, got %d", tt.size, len(ramp.Green)) - } - if len(ramp.Blue) != int(tt.size) { - t.Errorf("expected %d blue values, got %d", tt.size, len(ramp.Blue)) - } - - if ramp.Red[0] != 0 || ramp.Green[0] != 0 || ramp.Blue[0] != 0 { - t.Errorf("first values should be 0, got R:%d G:%d B:%d", - ramp.Red[0], ramp.Green[0], ramp.Blue[0]) - } - - lastIdx := tt.size - 1 - if ramp.Red[lastIdx] == 0 || ramp.Green[lastIdx] == 0 || ramp.Blue[lastIdx] == 0 { - t.Errorf("last values should be non-zero, got R:%d G:%d B:%d", - ramp.Red[lastIdx], ramp.Green[lastIdx], ramp.Blue[lastIdx]) - } - - for i := uint32(1); i < tt.size; i++ { - if ramp.Red[i] < ramp.Red[i-1] { - t.Errorf("red ramp not monotonic at index %d", i) - } - } - }) - } -} - -func TestCalcWhitepoint(t *testing.T) { - tests := []struct { - name string - temp int - }{ - {"very_warm", 6500}, - {"neutral", 5500}, - {"cool", 4000}, - {"very_cool", 3000}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - wp := calcWhitepoint(tt.temp) - - if wp.r < 0 || wp.r > 1 { - t.Errorf("red out of range: %f", wp.r) - } - if wp.g < 0 || wp.g > 1 { - t.Errorf("green out of range: %f", wp.g) - } - if wp.b < 0 || wp.b > 1 { - t.Errorf("blue out of range: %f", wp.b) - } - }) - } -} - -func TestWhitepointProgression(t *testing.T) { - temps := []int{3000, 4000, 5000, 6000, 6500} - - var prevBlue float64 - for i, temp := range temps { - wp := calcWhitepoint(temp) - if i > 0 && wp.b < prevBlue { - t.Errorf("blue should increase with temperature, %d->%d: %f->%f", - temps[i-1], temp, prevBlue, wp.b) - } - prevBlue = wp.b - } -} - -func TestClamp(t *testing.T) { - tests := []struct { - val float64 - min float64 - max float64 - expected float64 - }{ - {5, 0, 10, 5}, - {-5, 0, 10, 0}, - {15, 0, 10, 10}, - {0, 0, 10, 0}, - {10, 0, 10, 10}, - } - - for _, tt := range tests { - result := utils.Clamp(tt.val, tt.min, tt.max) - if result != tt.expected { - t.Errorf("clamp(%f, %f, %f) = %f, want %f", - tt.val, tt.min, tt.max, result, tt.expected) - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/geolocation.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/geolocation.go deleted file mode 100644 index ba99b5f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/geolocation.go +++ /dev/null @@ -1,50 +0,0 @@ -package wayland - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -type ipAPIResponse struct { - Lat float64 `json:"lat"` - Lon float64 `json:"lon"` - City string `json:"city"` -} - -func FetchIPLocation() (*float64, *float64, error) { - client := &http.Client{ - Timeout: 10 * time.Second, - } - - resp, err := client.Get("http://ip-api.com/json/") - if err != nil { - return nil, nil, fmt.Errorf("failed to fetch IP location: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, nil, fmt.Errorf("ip-api.com returned status %d", resp.StatusCode) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, nil, fmt.Errorf("failed to read response: %w", err) - } - - var data ipAPIResponse - if err := json.Unmarshal(body, &data); err != nil { - return nil, nil, fmt.Errorf("failed to parse response: %w", err) - } - - if data.Lat == 0 && data.Lon == 0 { - return nil, nil, fmt.Errorf("missing location data in response") - } - - log.Infof("Fetched IP-based location: %s (%.4f, %.4f)", data.City, data.Lat, data.Lon) - return &data.Lat, &data.Lon, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/handlers.go deleted file mode 100644 index 76b08b5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/handlers.go +++ /dev/null @@ -1,182 +0,0 @@ -package wayland - -import ( - "encoding/json" - "fmt" - "net" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "wayland manager not initialized") - return - } - - switch req.Method { - case "wayland.gamma.getState": - handleGetState(conn, req, manager) - case "wayland.gamma.setTemperature": - handleSetTemperature(conn, req, manager) - case "wayland.gamma.setLocation": - handleSetLocation(conn, req, manager) - case "wayland.gamma.setManualTimes": - handleSetManualTimes(conn, req, manager) - case "wayland.gamma.setUseIPLocation": - handleSetUseIPLocation(conn, req, manager) - case "wayland.gamma.setGamma": - handleSetGamma(conn, req, manager) - case "wayland.gamma.setEnabled": - handleSetEnabled(conn, req, manager) - case "wayland.gamma.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleSetTemperature(conn net.Conn, req models.Request, manager *Manager) { - var lowTemp, highTemp int - - if temp, ok := models.Get[float64](req, "temp"); ok { - lowTemp = int(temp) - highTemp = int(temp) - } else { - low, err := params.Float(req.Params, "low") - if err != nil { - models.RespondError(conn, req.ID, "missing temperature parameters (provide 'temp' or both 'low' and 'high')") - return - } - high, err := params.Float(req.Params, "high") - if err != nil { - models.RespondError(conn, req.ID, "missing temperature parameters (provide 'temp' or both 'low' and 'high')") - return - } - lowTemp = int(low) - highTemp = int(high) - } - - if err := manager.SetTemperature(lowTemp, highTemp); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "temperature set"}) -} - -func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) { - lat, err := params.Float(req.Params, "latitude") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - lon, err := params.Float(req.Params, "longitude") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetLocation(lat, lon); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "location set"}) -} - -func handleSetManualTimes(conn net.Conn, req models.Request, manager *Manager) { - sunriseStr, sunriseOK := models.Get[string](req, "sunrise") - sunsetStr, sunsetOK := models.Get[string](req, "sunset") - - if !sunriseOK || !sunsetOK || sunriseStr == "" || sunsetStr == "" { - manager.ClearManualTimes() - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "manual times cleared"}) - return - } - - sunrise, err := time.Parse("15:04", sunriseStr) - if err != nil { - models.RespondError(conn, req.ID, "invalid sunrise format (use HH:MM)") - return - } - - sunset, err := time.Parse("15:04", sunsetStr) - if err != nil { - models.RespondError(conn, req.ID, "invalid sunset format (use HH:MM)") - return - } - - if err := manager.SetManualTimes(sunrise, sunset); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "manual times set"}) -} - -func handleSetUseIPLocation(conn net.Conn, req models.Request, manager *Manager) { - use, err := params.Bool(req.Params, "use") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetUseIPLocation(use) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "IP location preference set"}) -} - -func handleSetGamma(conn net.Conn, req models.Request, manager *Manager) { - gamma, err := params.Float(req.Params, "gamma") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetGamma(gamma); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "gamma set"}) -} - -func handleSetEnabled(conn net.Conn, req models.Request, manager *Manager) { - enabled, err := params.Bool(req.Params, "enabled") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetEnabled(enabled) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "enabled state set"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/manager.go deleted file mode 100644 index 3540e3d..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/manager.go +++ /dev/null @@ -1,1256 +0,0 @@ -package wayland - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "os" - "slices" - "syscall" - "time" - - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "github.com/godbus/dbus/v5" - "golang.org/x/sys/unix" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_gamma_control" -) - -const animKelvinStep = 25 - -func NewManager(display wlclient.WaylandDisplay, config Config) (*Manager, error) { - if err := config.Validate(); err != nil { - return nil, err - } - - if config.ElevationTwilight == 0 { - config.ElevationTwilight = -6.0 - } - if config.ElevationDaylight == 0 { - config.ElevationDaylight = 3.0 - } - - m := &Manager{ - config: config, - display: display, - ctx: display.Context(), - cmdq: make(chan cmd, 128), - stopChan: make(chan struct{}), - updateTrigger: make(chan struct{}, 1), - dirty: make(chan struct{}, 1), - dbusSignal: make(chan *dbus.Signal, 16), - } - - if err := m.setupRegistry(); err != nil { - return nil, err - } - - if err := m.setupDBusMonitor(); err != nil { - log.Warnf("Failed to setup D-Bus monitoring: %v", err) - } - - m.alive = true - m.recalcSchedule(time.Now()) - m.updateStateFromSchedule() - - m.notifierWg.Add(1) - go m.notifier() - - m.wg.Add(1) - go m.schedulerLoop() - - if m.dbusConn != nil { - m.wg.Add(1) - go m.dbusMonitor() - } - - m.wg.Add(1) - go m.waylandActor() - - if config.Enabled { - m.post(func() { - log.Info("Gamma control enabled at startup") - gammaMgr := m.gammaControl.(*wlr_gamma_control.ZwlrGammaControlManagerV1) - m.availOutputsMu.RLock() - outs := slices.Clone(m.availableOutputs) - m.availOutputsMu.RUnlock() - if err := m.setupOutputControls(outs, gammaMgr); err != nil { - log.Errorf("Failed to initialize gamma controls: %v", err) - return - } - m.controlsInitialized = true - }) - } - - return m, nil -} - -func (m *Manager) post(fn func()) { - select { - case m.cmdq <- cmd{fn: fn}: - default: - log.Warn("Actor command queue full") - } -} - -func (m *Manager) waylandActor() { - defer m.wg.Done() - for { - select { - case <-m.stopChan: - return - case c := <-m.cmdq: - c.fn() - } - } -} - -func (m *Manager) anyOutputReady() bool { - anyReady := false - m.outputs.Range(func(_ uint32, out *outputState) bool { - if out.rampSize > 0 && !out.failed { - anyReady = true - return false // stop iteration - } - return true - }) - return anyReady -} - -func (m *Manager) setupDBusMonitor() error { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return fmt.Errorf("system bus: %w", err) - } - - matchRule := "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep',path='/org/freedesktop/login1'" - if err := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, matchRule).Err; err != nil { - conn.Close() - return fmt.Errorf("add match: %w", err) - } - - conn.Signal(m.dbusSignal) - m.dbusConn = conn - return nil -} - -func (m *Manager) setupRegistry() error { - registry, err := m.display.GetRegistry() - if err != nil { - return fmt.Errorf("get registry: %w", err) - } - m.registry = registry - - outputs := make([]*wlclient.Output, 0) - outputNames := make(map[uint32]string) - var gammaMgr *wlr_gamma_control.ZwlrGammaControlManagerV1 - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - switch e.Interface { - case wlr_gamma_control.ZwlrGammaControlManagerV1InterfaceName: - manager := wlr_gamma_control.NewZwlrGammaControlManagerV1(m.ctx) - version := e.Version - if version > 1 { - version = 1 - } - if err := registry.Bind(e.Name, e.Interface, version, manager); err == nil { - gammaMgr = manager - } - case "wl_output": - output := wlclient.NewOutput(m.ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := registry.Bind(e.Name, e.Interface, version, output); err != nil { - return - } - outputID := output.ID() - output.SetNameHandler(func(ev wlclient.OutputNameEvent) { - outputNames[outputID] = ev.Name - }) - if gammaMgr != nil { - outputs = append(outputs, output) - m.addAvailableOutput(output) - } - m.outputRegNames.Store(outputID, e.Name) - - m.configMutex.RLock() - enabled := m.config.Enabled - m.configMutex.RUnlock() - - if enabled && m.controlsInitialized { - m.post(func() { - if err := m.addOutputControl(output); err != nil { - log.Warnf("Failed to add output control: %v", err) - } - }) - } - } - }) - - registry.SetGlobalRemoveHandler(func(e wlclient.RegistryGlobalRemoveEvent) { - m.post(func() { - var foundID uint32 - var foundOut *outputState - m.outputs.Range(func(id uint32, out *outputState) bool { - if out.registryName == e.Name { - foundID = id - foundOut = out - return false - } - return true - }) - if foundOut == nil { - return - } - if foundOut.gammaControl != nil { - foundOut.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1).Destroy() - foundOut.gammaControl = nil - } - m.removeAvailableOutput(foundOut.output) - if foundOut.output != nil && !foundOut.output.IsZombie() { - _ = foundOut.output.Release() - } - m.outputs.Delete(foundID) - - hasOutputs := false - m.outputs.Range(func(_ uint32, _ *outputState) bool { - hasOutputs = true - return false - }) - if !hasOutputs { - m.controlsInitialized = false - } - }) - }) - - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("roundtrip 1: %w", err) - } - if err := m.display.Roundtrip(); err != nil { - return fmt.Errorf("roundtrip 2: %w", err) - } - - if gammaMgr == nil { - return errdefs.ErrNoGammaControl - } - if len(outputs) == 0 { - return fmt.Errorf("no outputs") - } - - physicalOutputs := make([]*wlclient.Output, 0, len(outputs)) - for _, output := range outputs { - name := outputNames[output.ID()] - if len(name) >= 9 && name[:9] == "HEADLESS-" { - continue - } - physicalOutputs = append(physicalOutputs, output) - } - - m.gammaControl = gammaMgr - m.availableOutputs = physicalOutputs - return nil -} - -func (m *Manager) setupOutputControls(outputs []*wlclient.Output, manager *wlr_gamma_control.ZwlrGammaControlManagerV1) error { - for _, output := range outputs { - control, err := manager.GetGammaControl(output) - if err != nil { - continue - } - outputID := output.ID() - registryName, _ := m.outputRegNames.Load(outputID) - outState := &outputState{ - id: outputID, - registryName: registryName, - output: output, - gammaControl: control, - } - m.setupControlHandlers(outState, control) - m.outputs.Store(outputID, outState) - } - return nil -} - -func (m *Manager) setupControlHandlers(state *outputState, control *wlr_gamma_control.ZwlrGammaControlV1) { - outputID := state.id - - control.SetGammaSizeHandler(func(e wlr_gamma_control.ZwlrGammaControlV1GammaSizeEvent) { - size := e.Size - m.post(func() { - if out, ok := m.outputs.Load(outputID); ok { - out.rampSize = size - out.failed = false - out.retryCount = 0 - } - m.lastAppliedTemp = 0 - m.applyCurrentTemp("gamma_size") - }) - }) - - control.SetFailedHandler(func(_ wlr_gamma_control.ZwlrGammaControlV1FailedEvent) { - m.post(func() { - out, ok := m.outputs.Load(outputID) - if !ok { - return - } - if ctrl, ok := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1); ok && ctrl != nil && !ctrl.IsZombie() { - ctrl.Destroy() - } - out.gammaControl = nil - out.failed = true - out.rampSize = 0 - out.retryCount++ - out.lastFailTime = time.Now() - - if !m.outputStillValid(out) { - return - } - - backoff := time.Duration(300<= 10: - return nil - case !m.outputStillValid(out): - return nil - } - if _, ok := m.outputs.Load(out.id); !ok { - return nil - } - - gammaMgr, ok := m.gammaControl.(*wlr_gamma_control.ZwlrGammaControlManagerV1) - if !ok { - return fmt.Errorf("no gamma manager") - } - - if existing, ok := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1); ok && existing != nil && !existing.IsZombie() { - existing.Destroy() - out.gammaControl = nil - } - - control, err := gammaMgr.GetGammaControl(out.output) - if err != nil { - if isConnectionDeadErr(err) { - m.markConnectionDead(err) - } - return err - } - - m.setupControlHandlers(out, control) - out.gammaControl = control - out.failed = false - return nil -} - -func (m *Manager) markConnectionDead(err error) { - if m.connectionDead.Swap(true) { - return - } - log.Errorf("gamma: wayland connection appears dead (%v); pausing gamma operations", err) -} - -func (m *Manager) recalcSchedule(now time.Time) { - m.configMutex.RLock() - config := m.config - m.configMutex.RUnlock() - - m.scheduleMutex.Lock() - defer m.scheduleMutex.Unlock() - - dayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) - alreadyValid := !m.schedule.times.Sunrise.IsZero() - if m.schedule.calcDay.Equal(dayStart) && alreadyValid { - return - } - - var times SunTimes - var cond SunCondition - - if config.ManualSunrise != nil && config.ManualSunset != nil { - dur := time.Hour - if config.ManualDuration != nil { - dur = *config.ManualDuration - } - sunrise := time.Date(now.Year(), now.Month(), now.Day(), - config.ManualSunrise.Hour(), config.ManualSunrise.Minute(), config.ManualSunrise.Second(), 0, now.Location()) - sunset := time.Date(now.Year(), now.Month(), now.Day(), - config.ManualSunset.Hour(), config.ManualSunset.Minute(), config.ManualSunset.Second(), 0, now.Location()) - times = SunTimes{ - Dawn: sunrise.Add(-dur), - Sunrise: sunrise, - Sunset: sunset, - Night: sunset.Add(dur), - } - cond = SunNormal - } else { - lat, lon := m.getLocation() - if lat == nil || lon == nil { - m.gammaState = StateStatic - return - } - times, cond = CalculateSunTimesWithTwilight(*lat, *lon, now, config.ElevationTwilight, config.ElevationDaylight) - } - - m.schedule.calcDay = dayStart - m.schedule.times = times - m.schedule.condition = cond - - switch cond { - case SunNormal: - m.gammaState = StateNormal - tempDiff := config.HighTemp - config.LowTemp - if tempDiff > 0 { - dawnDur := times.Sunrise.Sub(times.Dawn) - nightDur := times.Night.Sub(times.Sunset) - m.schedule.dawnStepTime = time.Duration(max(1, int(dawnDur.Seconds())*animKelvinStep/tempDiff)) * time.Second - m.schedule.nightStepTime = time.Duration(max(1, int(nightDur.Seconds())*animKelvinStep/tempDiff)) * time.Second - } - case SunMidnightSun: - m.gammaState = StateStatic - case SunPolarNight: - m.gammaState = StateStatic - } -} - -func (m *Manager) SetGeoClient(client geolocation.Client) { - m.geoClient = client -} - -func (m *Manager) getLocation() (*float64, *float64) { - m.configMutex.RLock() - config := m.config - m.configMutex.RUnlock() - - if config.Latitude != nil && config.Longitude != nil { - return config.Latitude, config.Longitude - } - if !config.UseIPLocation { - return nil, nil - } - if m.geoClient == nil { - return nil, nil - } - - m.locationMutex.RLock() - if m.cachedIPLat != nil && m.cachedIPLon != nil { - lat, lon := m.cachedIPLat, m.cachedIPLon - m.locationMutex.RUnlock() - return lat, lon - } - m.locationMutex.RUnlock() - - location, err := m.geoClient.GetLocation() - if err != nil { - return nil, nil - } - - m.locationMutex.Lock() - m.cachedIPLat = &location.Latitude - m.cachedIPLon = &location.Longitude - m.locationMutex.Unlock() - return m.cachedIPLat, m.cachedIPLon -} - -func (m *Manager) hasValidSchedule() bool { - m.scheduleMutex.RLock() - defer m.scheduleMutex.RUnlock() - return !m.schedule.times.Sunrise.IsZero() -} - -func (m *Manager) getSunPosition(now time.Time) float64 { - m.scheduleMutex.RLock() - sched := m.schedule - state := m.gammaState - m.scheduleMutex.RUnlock() - - if sched.times.Sunrise.IsZero() { - return 1.0 - } - - switch state { - case StateStatic: - if sched.condition == SunMidnightSun { - return 1.0 - } - return 0.0 - case StateNormal: - return m.getSunPositionNormal(now, sched.times) - } - return 1.0 -} - -func (m *Manager) getSunPositionNormal(now time.Time, times SunTimes) float64 { - if now.Before(times.Dawn) { - return 0.0 - } - if now.Before(times.Sunrise) { - return interpolate(now, times.Dawn, times.Sunrise) - } - if now.Before(times.Sunset) { - return 1.0 - } - if now.Before(times.Night) { - return interpolate(now, times.Night, times.Sunset) - } - return 0.0 -} - -func interpolate(now time.Time, start, stop time.Time) float64 { - if start.Equal(stop) { - return 1.0 - } - pos := float64(now.Sub(start)) / float64(stop.Sub(start)) - switch { - case pos > 1.0: - return 1.0 - case pos < 0.0: - return 0.0 - default: - return pos - } -} - -func (m *Manager) getTempFromPosition(pos float64) int { - m.configMutex.RLock() - low, high := m.config.LowTemp, m.config.HighTemp - m.configMutex.RUnlock() - return low + int(float64(high-low)*pos) -} - -func (m *Manager) getNextDeadline(now time.Time) time.Time { - m.scheduleMutex.RLock() - sched := m.schedule - state := m.gammaState - m.scheduleMutex.RUnlock() - - switch state { - case StateStatic: - return m.tomorrow(now) - case StateNormal: - return m.getDeadlineNormal(now, sched) - default: - return m.tomorrow(now) - } -} - -func (m *Manager) getDeadlineNormal(now time.Time, sched sunSchedule) time.Time { - times := sched.times - switch { - case now.Before(times.Dawn): - return times.Dawn - case now.Before(times.Sunrise): - return now.Add(sched.dawnStepTime) - case now.Before(times.Sunset): - return times.Sunset - case now.Before(times.Night): - return now.Add(sched.nightStepTime) - default: - return m.tomorrowDawn(now) - } -} - -func (m *Manager) tomorrowDawn(now time.Time) time.Time { - tomorrow := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location()) - - m.configMutex.RLock() - config := m.config - m.configMutex.RUnlock() - - if config.ManualSunrise != nil { - dur := time.Hour - if config.ManualDuration != nil { - dur = *config.ManualDuration - } - return time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), - config.ManualSunrise.Hour(), config.ManualSunrise.Minute(), config.ManualSunrise.Second(), 0, tomorrow.Location()).Add(-dur) - } - - lat, lon := m.getLocation() - if lat == nil || lon == nil { - return tomorrow - } - - times, cond := CalculateSunTimesWithTwilight(*lat, *lon, tomorrow, config.ElevationTwilight, config.ElevationDaylight) - if cond != SunNormal { - return tomorrow - } - return times.Dawn -} - -func (m *Manager) tomorrow(now time.Time) time.Time { - return time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location()) -} - -func (m *Manager) schedulerLoop() { - defer m.wg.Done() - - m.configMutex.RLock() - enabled := m.config.Enabled - m.configMutex.RUnlock() - - if enabled { - m.post(func() { m.applyCurrentTemp("startup") }) - } - - var timer *time.Timer - for { - m.configMutex.RLock() - enabled := m.config.Enabled - m.configMutex.RUnlock() - - now := time.Now() - m.recalcSchedule(now) - - var waitDur time.Duration - if enabled { - deadline := m.getNextDeadline(now) - waitDur = time.Until(deadline) - if waitDur < time.Second { - waitDur = time.Second - } - } else { - waitDur = 24 * time.Hour - } - - if timer != nil { - timer.Stop() - } - timer = time.NewTimer(waitDur) - - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.updateTrigger: - timer.Stop() - m.scheduleMutex.Lock() - m.schedule.calcDay = time.Time{} - m.scheduleMutex.Unlock() - m.recalcSchedule(time.Now()) - m.updateStateFromSchedule() - m.configMutex.RLock() - enabled := m.config.Enabled - m.configMutex.RUnlock() - if enabled { - m.post(func() { m.applyCurrentTemp("updateTrigger") }) - } - case <-timer.C: - m.configMutex.RLock() - enabled := m.config.Enabled - m.configMutex.RUnlock() - if enabled { - m.post(func() { m.applyCurrentTemp("timer") }) - } - } - } -} - -func (m *Manager) applyCurrentTemp(_ string) { - if !m.controlsInitialized || !m.anyOutputReady() { - return - } - - // Ensure schedule is up-to-date (handles display wake after overnight sleep) - m.recalcSchedule(time.Now()) - - m.configMutex.RLock() - low, high := m.config.LowTemp, m.config.HighTemp - m.configMutex.RUnlock() - - if low == high { - m.applyGamma(low) - m.updateStateFromSchedule() - return - } - - if !m.hasValidSchedule() { - m.updateStateFromSchedule() - return - } - - now := time.Now() - pos := m.getSunPosition(now) - temp := m.getTempFromPosition(pos) - - m.applyGamma(temp) - m.updateStateFromSchedule() -} - -func (m *Manager) applyGamma(temp int) { - m.configMutex.RLock() - gamma := m.config.Gamma - m.configMutex.RUnlock() - - switch { - case m.connectionDead.Load(): - return - case !m.controlsInitialized: - return - case m.lastAppliedTemp == temp && m.lastAppliedGamma == gamma: - return - } - - var outs []*outputState - m.outputs.Range(func(_ uint32, out *outputState) bool { - outs = append(outs, out) - return true - }) - if len(outs) == 0 { - return - } - - type job struct { - out *outputState - data []byte - } - var jobs []job - - for _, out := range outs { - switch { - case out.failed: - continue - case out.rampSize == 0: - continue - case out.gammaControl == nil: - continue - case !m.outputStillValid(out): - continue - } - ramp := GenerateGammaRamp(out.rampSize, temp, gamma) - buf := bytes.NewBuffer(make([]byte, 0, int(out.rampSize)*6)) - for _, v := range ramp.Red { - binary.Write(buf, binary.LittleEndian, v) - } - for _, v := range ramp.Green { - binary.Write(buf, binary.LittleEndian, v) - } - for _, v := range ramp.Blue { - binary.Write(buf, binary.LittleEndian, v) - } - jobs = append(jobs, job{out: out, data: buf.Bytes()}) - } - - for _, j := range jobs { - err := m.setGammaBytes(j.out, j.data) - if err == nil { - continue - } - log.Warnf("gamma: failed to set output %d: %v", j.out.id, err) - j.out.failed = true - j.out.rampSize = 0 - if isConnectionDeadErr(err) { - m.markConnectionDead(err) - return - } - } - - m.lastAppliedTemp = temp - m.lastAppliedGamma = gamma -} - -func (m *Manager) setGammaBytes(out *outputState, data []byte) error { - if out.gammaControl == nil { - return fmt.Errorf("no gamma control") - } - ctrl, ok := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1) - if !ok || ctrl == nil || ctrl.IsZombie() { - return fmt.Errorf("gamma control invalid") - } - - fd, err := MemfdCreate("gamma-ramp", 0) - if err != nil { - return err - } - defer syscall.Close(fd) - - if err := syscall.Ftruncate(fd, int64(len(data))); err != nil { - return err - } - - dupFd, err := syscall.Dup(fd) - if err != nil { - return err - } - f := os.NewFile(uintptr(dupFd), "gamma") - defer f.Close() - - if _, err := f.Write(data); err != nil { - return err - } - syscall.Seek(fd, 0, 0) - - return ctrl.SetGamma(fd) -} - -func (m *Manager) updateStateFromSchedule() { - now := time.Now() - - m.configMutex.RLock() - config := m.config - m.configMutex.RUnlock() - - m.scheduleMutex.RLock() - times := m.schedule.times - m.scheduleMutex.RUnlock() - - var pos float64 - var temp int - var isDay bool - var deadline time.Time - - if times.Sunrise.IsZero() { - pos = 1.0 - temp = config.HighTemp - isDay = true - deadline = m.tomorrow(now) - } else { - pos = m.getSunPosition(now) - temp = m.getTempFromPosition(pos) - deadline = m.getNextDeadline(now) - isDay = now.After(times.Sunrise) && now.Before(times.Sunset) - } - - newState := State{ - Config: config, - CurrentTemp: temp, - NextTransition: deadline, - SunriseTime: times.Sunrise, - SunsetTime: times.Sunset, - DawnTime: times.Dawn, - NightTime: times.Night, - IsDay: isDay, - SunPosition: pos, - } - - m.stateMutex.Lock() - m.state = &newState - m.stateMutex.Unlock() - - m.notifySubscribers() -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - currentState := m.GetState() - if m.lastNotified != nil && !stateChanged(m.lastNotified, ¤tState) { - pending = false - continue - } - m.subscribers.Range(func(_ string, ch chan State) bool { - select { - case ch <- currentState: - default: - } - return true - }) - stateCopy := currentState - m.lastNotified = &stateCopy - pending = false - } - } -} - -func (m *Manager) dbusMonitor() { - defer m.wg.Done() - for { - select { - case <-m.stopChan: - return - case sig := <-m.dbusSignal: - if sig == nil { - continue - } - m.handleDBusSignal(sig) - } - } -} - -func (m *Manager) handleDBusSignal(sig *dbus.Signal) { - switch { - case sig.Name != "org.freedesktop.login1.Manager.PrepareForSleep": - return - case len(sig.Body) == 0: - return - } - preparing, ok := sig.Body[0].(bool) - if !ok || preparing { - return - } - m.configMutex.RLock() - enabled := m.config.Enabled - m.configMutex.RUnlock() - if !enabled { - return - } - time.AfterFunc(500*time.Millisecond, func() { - m.post(m.handleResume) - }) -} - -func (m *Manager) handleResume() { - m.configMutex.RLock() - stillEnabled := m.config.Enabled - m.configMutex.RUnlock() - - switch { - case !stillEnabled: - return - case !m.controlsInitialized: - return - case m.connectionDead.Load(): - return - } - - // Compositors (Niri, Hyprland, wlroots-based) re-apply the cached gamma - // ramp to DRM on resume; gamma_control objects stay valid. We just need - // to force a resend so the schedule catches up with the current time of - // day — the original #1235 regression was caused by lastAppliedTemp - // matching and the send being skipped. - m.recalcSchedule(time.Now()) - m.lastAppliedTemp = 0 - m.applyCurrentTemp("resume") -} - -func (m *Manager) triggerUpdate() { - select { - case m.updateTrigger <- struct{}{}: - default: - } -} - -func (m *Manager) SetConfig(config Config) error { - if err := config.Validate(); err != nil { - return err - } - m.configMutex.Lock() - m.config = config - m.configMutex.Unlock() - m.triggerUpdate() - return nil -} - -func (m *Manager) SetTemperature(low, high int) error { - m.configMutex.Lock() - if m.config.LowTemp == low && m.config.HighTemp == high { - m.configMutex.Unlock() - return nil - } - m.config.LowTemp = low - m.config.HighTemp = high - err := m.config.Validate() - m.configMutex.Unlock() - if err != nil { - return err - } - m.triggerUpdate() - return nil -} - -func (m *Manager) SetLocation(lat, lon float64) error { - m.configMutex.Lock() - if m.config.Latitude != nil && m.config.Longitude != nil && - *m.config.Latitude == lat && *m.config.Longitude == lon && !m.config.UseIPLocation { - m.configMutex.Unlock() - return nil - } - m.config.Latitude = &lat - m.config.Longitude = &lon - m.config.UseIPLocation = false - err := m.config.Validate() - m.configMutex.Unlock() - if err != nil { - return err - } - m.triggerUpdate() - return nil -} - -func (m *Manager) SetUseIPLocation(use bool) { - m.configMutex.Lock() - if m.config.UseIPLocation == use { - m.configMutex.Unlock() - return - } - m.config.UseIPLocation = use - if use { - m.config.Latitude = nil - m.config.Longitude = nil - } - m.configMutex.Unlock() - - if use { - m.locationMutex.Lock() - m.cachedIPLat = nil - m.cachedIPLon = nil - m.locationMutex.Unlock() - } - m.triggerUpdate() -} - -func (m *Manager) SetManualTimes(sunrise, sunset time.Time) error { - m.configMutex.Lock() - if m.config.ManualSunrise != nil && m.config.ManualSunset != nil && - m.config.ManualSunrise.Hour() == sunrise.Hour() && m.config.ManualSunrise.Minute() == sunrise.Minute() && - m.config.ManualSunset.Hour() == sunset.Hour() && m.config.ManualSunset.Minute() == sunset.Minute() { - m.configMutex.Unlock() - return nil - } - m.config.ManualSunrise = &sunrise - m.config.ManualSunset = &sunset - err := m.config.Validate() - m.configMutex.Unlock() - if err != nil { - return err - } - m.triggerUpdate() - return nil -} - -func (m *Manager) ClearManualTimes() { - m.configMutex.Lock() - if m.config.ManualSunrise == nil && m.config.ManualSunset == nil { - m.configMutex.Unlock() - return - } - m.config.ManualSunrise = nil - m.config.ManualSunset = nil - m.configMutex.Unlock() - m.triggerUpdate() -} - -func (m *Manager) SetGamma(gamma float64) error { - m.configMutex.Lock() - if m.config.Gamma == gamma { - m.configMutex.Unlock() - return nil - } - m.config.Gamma = gamma - err := m.config.Validate() - m.configMutex.Unlock() - if err != nil { - return err - } - m.triggerUpdate() - return nil -} - -func (m *Manager) SetEnabled(enabled bool) { - m.configMutex.Lock() - wasEnabled := m.config.Enabled - if wasEnabled == enabled { - m.configMutex.Unlock() - return - } - m.config.Enabled = enabled - highTemp := m.config.HighTemp - m.configMutex.Unlock() - - switch { - case enabled && !m.controlsInitialized: - m.post(func() { - gammaMgr := m.gammaControl.(*wlr_gamma_control.ZwlrGammaControlManagerV1) - m.availOutputsMu.RLock() - outs := slices.Clone(m.availableOutputs) - m.availOutputsMu.RUnlock() - if err := m.setupOutputControls(outs, gammaMgr); err != nil { - log.Errorf("gamma: failed to create controls: %v", err) - return - } - m.controlsInitialized = true - m.triggerUpdate() - }) - case enabled && !wasEnabled: - m.triggerUpdate() - case !enabled && m.controlsInitialized: - m.post(func() { - m.outputs.Range(func(id uint32, out *outputState) bool { - if out.gammaControl != nil { - out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1).Destroy() - } - return true - }) - m.outputs.Range(func(key uint32, _ *outputState) bool { - m.outputs.Delete(key) - return true - }) - m.controlsInitialized = false - }) - _ = highTemp - } -} - -func (m *Manager) Close() { - close(m.stopChan) - m.wg.Wait() - m.notifierWg.Wait() - - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - m.outputs.Range(func(_ uint32, out *outputState) bool { - if ctrl, ok := out.gammaControl.(*wlr_gamma_control.ZwlrGammaControlV1); ok { - ctrl.Destroy() - } - return true - }) - m.outputs.Range(func(key uint32, _ *outputState) bool { - m.outputs.Delete(key) - return true - }) - - if manager, ok := m.gammaControl.(*wlr_gamma_control.ZwlrGammaControlManagerV1); ok { - manager.Destroy() - } - - if m.dbusConn != nil { - m.dbusConn.RemoveSignal(m.dbusSignal) - m.dbusConn.Close() - } -} - -func MemfdCreate(name string, flags int) (int, error) { - fd, err := unix.MemfdCreate(name, flags) - if err != nil { - return -1, err - } - return fd, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/manager_test.go deleted file mode 100644 index 8c00129..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/manager_test.go +++ /dev/null @@ -1,414 +0,0 @@ -package wayland - -import ( - "errors" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - mocks_wlclient "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/wlclient" -) - -func TestManager_ActorSerializesOutputStateAccess(t *testing.T) { - m := &Manager{ - cmdq: make(chan cmd, 8192), - stopChan: make(chan struct{}), - } - - m.wg.Add(1) - go m.waylandActor() - - state := &outputState{ - id: 1, - registryName: 100, - rampSize: 256, - } - m.outputs.Store(state.id, state) - - var wg sync.WaitGroup - const goroutines = 50 - const iterations = 100 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.post(func() { - if out, ok := m.outputs.Load(state.id); ok { - out.rampSize = uint32(j) - out.failed = j%2 == 0 - out.retryCount = j - out.lastFailTime = time.Now() - } - }) - } - }(i) - } - - wg.Wait() - - done := make(chan struct{}) - m.post(func() { close(done) }) - <-done - - close(m.stopChan) - m.wg.Wait() -} - -func TestManager_ConcurrentSubscriberAccess(t *testing.T) { - m := &Manager{ - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - updateTrigger: make(chan struct{}, 1), - } - - var wg sync.WaitGroup - const goroutines = 20 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - subID := string(rune('a' + id)) - ch := m.Subscribe(subID) - assert.NotNil(t, ch) - time.Sleep(time.Millisecond) - m.Unsubscribe(subID) - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentGetState(t *testing.T) { - m := &Manager{ - state: &State{ - CurrentTemp: 5000, - IsDay: true, - }, - } - - var wg sync.WaitGroup - const goroutines = 50 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - s := m.GetState() - assert.GreaterOrEqual(t, s.CurrentTemp, 0) - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.stateMutex.Lock() - m.state = &State{ - CurrentTemp: 4000 + i*100, - IsDay: j%2 == 0, - } - m.stateMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentConfigAccess(t *testing.T) { - m := &Manager{ - config: DefaultConfig(), - } - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.configMutex.RLock() - _ = m.config.LowTemp - _ = m.config.HighTemp - _ = m.config.Enabled - m.configMutex.RUnlock() - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.configMutex.Lock() - m.config.LowTemp = 3000 + j - m.config.HighTemp = 7000 - j - m.config.Enabled = j%2 == 0 - m.configMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_SyncmapOutputsConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - state := &outputState{ - id: key, - rampSize: uint32(j), - failed: j%2 == 0, - } - m.outputs.Store(key, state) - - if loaded, ok := m.outputs.Load(key); ok { - assert.Equal(t, key, loaded.id) - } - - m.outputs.Range(func(k uint32, v *outputState) bool { - _ = v.rampSize - _ = v.failed - return true - }) - } - - m.outputs.Delete(key) - }(i) - } - - wg.Wait() -} - -func TestManager_LocationCacheConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 20 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.locationMutex.RLock() - _ = m.cachedIPLat - _ = m.cachedIPLon - m.locationMutex.RUnlock() - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - lat := float64(40 + i) - lon := float64(-74 + j) - m.locationMutex.Lock() - m.cachedIPLat = &lat - m.cachedIPLon = &lon - m.locationMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ScheduleConcurrentAccess(t *testing.T) { - now := time.Now() - m := &Manager{ - schedule: sunSchedule{ - times: SunTimes{ - Dawn: now, - Sunrise: now.Add(time.Hour), - Sunset: now.Add(12 * time.Hour), - Night: now.Add(13 * time.Hour), - }, - }, - } - - var wg sync.WaitGroup - const goroutines = 20 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.scheduleMutex.RLock() - _ = m.schedule.times.Dawn - _ = m.schedule.times.Sunrise - _ = m.schedule.times.Sunset - _ = m.schedule.condition - m.scheduleMutex.RUnlock() - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.scheduleMutex.Lock() - m.schedule.times.Dawn = time.Now() - m.schedule.times.Sunrise = time.Now().Add(time.Hour) - m.schedule.condition = SunNormal - m.scheduleMutex.Unlock() - } - }() - } - - wg.Wait() -} - -func TestInterpolate_EdgeCases(t *testing.T) { - now := time.Now() - - tests := []struct { - name string - now time.Time - start time.Time - stop time.Time - expected float64 - }{ - { - name: "same start and stop", - now: now, - start: now, - stop: now, - expected: 1.0, - }, - { - name: "now before start", - now: now, - start: now.Add(time.Hour), - stop: now.Add(2 * time.Hour), - expected: 0.0, - }, - { - name: "now after stop", - now: now.Add(3 * time.Hour), - start: now, - stop: now.Add(time.Hour), - expected: 1.0, - }, - { - name: "now at midpoint", - now: now.Add(30 * time.Minute), - start: now, - stop: now.Add(time.Hour), - expected: 0.5, - }, - { - name: "now equals start", - now: now, - start: now, - stop: now.Add(time.Hour), - expected: 0.0, - }, - { - name: "now equals stop", - now: now.Add(time.Hour), - start: now, - stop: now.Add(time.Hour), - expected: 1.0, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := interpolate(tt.now, tt.start, tt.stop) - assert.InDelta(t, tt.expected, result, 0.01) - }) - } -} - -func TestGenerateGammaRamp_ZeroSize(t *testing.T) { - ramp := GenerateGammaRamp(0, 5000, 1.0) - assert.Empty(t, ramp.Red) - assert.Empty(t, ramp.Green) - assert.Empty(t, ramp.Blue) -} - -func TestGenerateGammaRamp_ValidSizes(t *testing.T) { - sizes := []uint32{1, 256, 1024} - temps := []int{1000, 4000, 6500, 10000} - gammas := []float64{0.5, 1.0, 2.0} - - for _, size := range sizes { - for _, temp := range temps { - for _, gamma := range gammas { - ramp := GenerateGammaRamp(size, temp, gamma) - assert.Len(t, ramp.Red, int(size)) - assert.Len(t, ramp.Green, int(size)) - assert.Len(t, ramp.Blue, int(size)) - } - } - } -} - -func TestNotifySubscribers_NonBlocking(t *testing.T) { - m := &Manager{ - dirty: make(chan struct{}, 1), - } - - for i := 0; i < 10; i++ { - m.notifySubscribers() - } - - assert.Len(t, m.dirty, 1) -} - -func TestNewManager_GetRegistryError(t *testing.T) { - mockDisplay := mocks_wlclient.NewMockWaylandDisplay(t) - - mockDisplay.EXPECT().Context().Return(nil) - mockDisplay.EXPECT().GetRegistry().Return(nil, errors.New("failed to get registry")) - - config := DefaultConfig() - _, err := NewManager(mockDisplay, config) - assert.Error(t, err) - assert.Contains(t, err.Error(), "get registry") -} - -func TestNewManager_InvalidConfig(t *testing.T) { - mockDisplay := mocks_wlclient.NewMockWaylandDisplay(t) - - config := Config{ - LowTemp: 500, - HighTemp: 6500, - Gamma: 1.0, - } - - _, err := NewManager(mockDisplay, config) - assert.Error(t, err) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/suncalc.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/suncalc.go deleted file mode 100644 index c978a8e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/suncalc.go +++ /dev/null @@ -1,122 +0,0 @@ -package wayland - -import ( - "math" - "time" -) - -const ( - degToRad = math.Pi / 180.0 - radToDeg = 180.0 / math.Pi -) - -type SunCondition int - -const ( - SunNormal SunCondition = iota - SunMidnightSun - SunPolarNight -) - -type SunTimes struct { - Dawn time.Time - Sunrise time.Time - Sunset time.Time - Night time.Time -} - -func daysInYear(year int) int { - if (year%4 == 0 && year%100 != 0) || year%400 == 0 { - return 366 - } - return 365 -} - -func dateOrbitAngle(t time.Time) float64 { - return 2 * math.Pi / float64(daysInYear(t.Year())) * float64(t.YearDay()-1) -} - -func equationOfTime(orbitAngle float64) float64 { - return 4 * (0.000075 + - 0.001868*math.Cos(orbitAngle) - - 0.032077*math.Sin(orbitAngle) - - 0.014615*math.Cos(2*orbitAngle) - - 0.040849*math.Sin(2*orbitAngle)) -} - -func sunDeclination(orbitAngle float64) float64 { - return 0.006918 - - 0.399912*math.Cos(orbitAngle) + - 0.070257*math.Sin(orbitAngle) - - 0.006758*math.Cos(2*orbitAngle) + - 0.000907*math.Sin(2*orbitAngle) - - 0.002697*math.Cos(3*orbitAngle) + - 0.00148*math.Sin(3*orbitAngle) -} - -func sunHourAngle(latRad, declination, targetSunRad float64) float64 { - return math.Acos(math.Cos(targetSunRad)/ - math.Cos(latRad)*math.Cos(declination) - - math.Tan(latRad)*math.Tan(declination)) -} - -func hourAngleToSeconds(hourAngle, eqtime float64) float64 { - return radToDeg * (4.0*math.Pi - 4*hourAngle - eqtime) * 60 -} - -func sunCondition(latRad, declination float64) SunCondition { - signLat := latRad >= 0 - signDecl := declination >= 0 - if signLat == signDecl { - return SunMidnightSun - } - return SunPolarNight -} - -func CalculateSunTimesWithTwilight(lat, lon float64, date time.Time, elevTwilight, elevDaylight float64) (SunTimes, SunCondition) { - latRad := lat * degToRad - elevTwilightRad := (90.833 - elevTwilight) * degToRad - elevDaylightRad := (90.833 - elevDaylight) * degToRad - - utc := date.UTC() - orbitAngle := dateOrbitAngle(utc) - decl := sunDeclination(orbitAngle) - eqtime := equationOfTime(orbitAngle) - - haTwilight := sunHourAngle(latRad, decl, elevTwilightRad) - haDaylight := sunHourAngle(latRad, decl, elevDaylightRad) - - if math.IsNaN(haTwilight) || math.IsNaN(haDaylight) { - cond := sunCondition(latRad, decl) - return SunTimes{}, cond - } - - dayStart := time.Date(utc.Year(), utc.Month(), utc.Day(), 0, 0, 0, 0, time.UTC) - lonOffset := time.Duration(-lon*4) * time.Minute - - dawnSecs := hourAngleToSeconds(math.Abs(haTwilight), eqtime) - sunriseSecs := hourAngleToSeconds(math.Abs(haDaylight), eqtime) - sunsetSecs := hourAngleToSeconds(-math.Abs(haDaylight), eqtime) - nightSecs := hourAngleToSeconds(-math.Abs(haTwilight), eqtime) - - return SunTimes{ - Dawn: dayStart.Add(time.Duration(dawnSecs)*time.Second + lonOffset).In(date.Location()), - Sunrise: dayStart.Add(time.Duration(sunriseSecs)*time.Second + lonOffset).In(date.Location()), - Sunset: dayStart.Add(time.Duration(sunsetSecs)*time.Second + lonOffset).In(date.Location()), - Night: dayStart.Add(time.Duration(nightSecs)*time.Second + lonOffset).In(date.Location()), - }, SunNormal -} - -func CalculateSunTimes(lat, lon float64, date time.Time) SunTimes { - times, cond := CalculateSunTimesWithTwilight(lat, lon, date, -6.0, 3.0) - switch cond { - case SunMidnightSun: - dayStart := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) - dayEnd := dayStart.Add(24*time.Hour - time.Second) - return SunTimes{Dawn: dayStart, Sunrise: dayStart, Sunset: dayEnd, Night: dayEnd} - case SunPolarNight: - dayStart := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) - return SunTimes{Dawn: dayStart, Sunrise: dayStart, Sunset: dayStart, Night: dayStart} - } - return times -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/suncalc_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/suncalc_test.go deleted file mode 100644 index b2815d9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/suncalc_test.go +++ /dev/null @@ -1,387 +0,0 @@ -package wayland - -import ( - "math" - "testing" - "time" -) - -func calculateTemperature(config Config, now time.Time) int { - if !config.Enabled { - return config.HighTemp - } - - var sunrise, sunset time.Time - - if config.ManualSunrise != nil && config.ManualSunset != nil { - year, month, day := now.Date() - loc := now.Location() - - sunrise = time.Date(year, month, day, - config.ManualSunrise.Hour(), - config.ManualSunrise.Minute(), - config.ManualSunrise.Second(), 0, loc) - sunset = time.Date(year, month, day, - config.ManualSunset.Hour(), - config.ManualSunset.Minute(), - config.ManualSunset.Second(), 0, loc) - - if sunset.Before(sunrise) { - sunset = sunset.Add(24 * time.Hour) - } - } else if config.UseIPLocation { - lat, lon, err := FetchIPLocation() - if err != nil { - return config.HighTemp - } - times := CalculateSunTimes(*lat, *lon, now) - sunrise = times.Sunrise - sunset = times.Sunset - } else if config.Latitude != nil && config.Longitude != nil { - times := CalculateSunTimes(*config.Latitude, *config.Longitude, now) - sunrise = times.Sunrise - sunset = times.Sunset - } else { - return config.HighTemp - } - - if now.Before(sunrise) || now.After(sunset) { - return config.LowTemp - } - return config.HighTemp -} - -func calculateNextTransition(config Config, now time.Time) time.Time { - if !config.Enabled { - return now.Add(24 * time.Hour) - } - - var sunrise, sunset time.Time - - if config.ManualSunrise != nil && config.ManualSunset != nil { - year, month, day := now.Date() - loc := now.Location() - - sunrise = time.Date(year, month, day, - config.ManualSunrise.Hour(), - config.ManualSunrise.Minute(), - config.ManualSunrise.Second(), 0, loc) - sunset = time.Date(year, month, day, - config.ManualSunset.Hour(), - config.ManualSunset.Minute(), - config.ManualSunset.Second(), 0, loc) - - if sunset.Before(sunrise) { - sunset = sunset.Add(24 * time.Hour) - } - } else if config.UseIPLocation { - lat, lon, err := FetchIPLocation() - if err != nil { - return now.Add(24 * time.Hour) - } - times := CalculateSunTimes(*lat, *lon, now) - sunrise = times.Sunrise - sunset = times.Sunset - } else if config.Latitude != nil && config.Longitude != nil { - times := CalculateSunTimes(*config.Latitude, *config.Longitude, now) - sunrise = times.Sunrise - sunset = times.Sunset - } else { - return now.Add(24 * time.Hour) - } - - if now.Before(sunrise) { - return sunrise - } - if now.Before(sunset) { - return sunset - } - - if config.ManualSunrise != nil && config.ManualSunset != nil { - year, month, day := now.Add(24 * time.Hour).Date() - loc := now.Location() - nextSunrise := time.Date(year, month, day, - config.ManualSunrise.Hour(), - config.ManualSunrise.Minute(), - config.ManualSunrise.Second(), 0, loc) - return nextSunrise - } - - if config.UseIPLocation { - lat, lon, err := FetchIPLocation() - if err != nil { - return now.Add(24 * time.Hour) - } - nextDayTimes := CalculateSunTimes(*lat, *lon, now.Add(24*time.Hour)) - return nextDayTimes.Sunrise - } - - if config.Latitude != nil && config.Longitude != nil { - nextDayTimes := CalculateSunTimes(*config.Latitude, *config.Longitude, now.Add(24*time.Hour)) - return nextDayTimes.Sunrise - } - - return now.Add(24 * time.Hour) -} - -func TestCalculateSunTimes(t *testing.T) { - tests := []struct { - name string - lat float64 - lon float64 - date time.Time - checkFunc func(*testing.T, SunTimes) - }{ - { - name: "new_york_summer", - lat: 40.7128, - lon: -74.0060, - date: time.Date(2024, 6, 21, 12, 0, 0, 0, time.Local), - checkFunc: func(t *testing.T, times SunTimes) { - if times.Sunrise.Hour() < 4 || times.Sunrise.Hour() > 6 { - t.Logf("sunrise: %v", times.Sunrise) - } - if times.Sunset.Hour() < 19 || times.Sunset.Hour() > 21 { - t.Logf("sunset: %v", times.Sunset) - } - if !times.Sunset.After(times.Sunrise) { - t.Error("sunset should be after sunrise") - } - }, - }, - { - name: "london_winter", - lat: 51.5074, - lon: -0.1278, - date: time.Date(2024, 12, 21, 12, 0, 0, 0, time.UTC), - checkFunc: func(t *testing.T, times SunTimes) { - if times.Sunrise.Hour() < 7 || times.Sunrise.Hour() > 9 { - t.Errorf("unexpected sunrise hour: %d", times.Sunrise.Hour()) - } - if times.Sunset.Hour() < 15 || times.Sunset.Hour() > 17 { - t.Errorf("unexpected sunset hour: %d", times.Sunset.Hour()) - } - }, - }, - { - name: "equator_equinox", - lat: 0.0, - lon: 0.0, - date: time.Date(2024, 3, 20, 12, 0, 0, 0, time.UTC), - checkFunc: func(t *testing.T, times SunTimes) { - if times.Sunrise.Hour() < 5 || times.Sunrise.Hour() > 7 { - t.Errorf("unexpected sunrise hour: %d", times.Sunrise.Hour()) - } - if times.Sunset.Hour() < 17 || times.Sunset.Hour() > 19 { - t.Errorf("unexpected sunset hour: %d", times.Sunset.Hour()) - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - times := CalculateSunTimes(tt.lat, tt.lon, tt.date) - tt.checkFunc(t, times) - }) - } -} - -func TestCalculateTemperature(t *testing.T) { - lat := 40.7128 - lon := -74.0060 - date := time.Date(2024, 6, 21, 0, 0, 0, 0, time.Local) - - config := Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: &lat, - Longitude: &lon, - Enabled: true, - } - - times := CalculateSunTimes(lat, lon, date) - - tests := []struct { - name string - timeFunc func() time.Time - wantTemp int - }{ - { - name: "midnight", - timeFunc: func() time.Time { return times.Sunrise.Add(-4 * time.Hour) }, - wantTemp: 4000, - }, - { - name: "sunrise", - timeFunc: func() time.Time { return times.Sunrise }, - wantTemp: 6500, - }, - { - name: "noon", - timeFunc: func() time.Time { return times.Sunrise.Add(6 * time.Hour) }, - wantTemp: 6500, - }, - { - name: "after_sunset_transition", - timeFunc: func() time.Time { return times.Sunset.Add(2 * time.Hour) }, - wantTemp: 4000, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - temp := calculateTemperature(config, tt.timeFunc()) - - if math.Abs(float64(temp-tt.wantTemp)) > 500 { - t.Errorf("temperature = %d, want approximately %d", temp, tt.wantTemp) - } - }) - } -} - -func TestCalculateTemperatureManualTimes(t *testing.T) { - sunrise := time.Date(0, 1, 1, 6, 30, 0, 0, time.Local) - sunset := time.Date(0, 1, 1, 18, 30, 0, 0, time.Local) - - config := Config{ - LowTemp: 4000, - HighTemp: 6500, - ManualSunrise: &sunrise, - ManualSunset: &sunset, - Enabled: true, - } - - tests := []struct { - name string - time time.Time - want int - }{ - {"before_sunrise", time.Date(2024, 1, 1, 3, 0, 0, 0, time.Local), 4000}, - {"at_sunrise", time.Date(2024, 1, 1, 6, 30, 0, 0, time.Local), 6500}, - {"midday", time.Date(2024, 1, 1, 12, 0, 0, 0, time.Local), 6500}, - {"at_sunset", time.Date(2024, 1, 1, 18, 30, 0, 0, time.Local), 6500}, - {"after_sunset", time.Date(2024, 1, 1, 22, 0, 0, 0, time.Local), 4000}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - temp := calculateTemperature(config, tt.time) - if math.Abs(float64(temp-tt.want)) > 500 { - t.Errorf("temperature = %d, want approximately %d", temp, tt.want) - } - }) - } -} - -func TestCalculateTemperatureDisabled(t *testing.T) { - lat := 40.7128 - lon := -74.0060 - - config := Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: &lat, - Longitude: &lon, - Enabled: false, - } - - temp := calculateTemperature(config, time.Now()) - if temp != 6500 { - t.Errorf("disabled should return high temp, got %d", temp) - } -} - -func TestCalculateNextTransition(t *testing.T) { - lat := 40.7128 - lon := -74.0060 - date := time.Date(2024, 6, 21, 0, 0, 0, 0, time.Local) - - config := Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: &lat, - Longitude: &lon, - Enabled: true, - } - - times := CalculateSunTimes(lat, lon, date) - - tests := []struct { - name string - now time.Time - checkFunc func(*testing.T, time.Time) - }{ - { - name: "before_sunrise", - now: times.Sunrise.Add(-2 * time.Hour), - checkFunc: func(t *testing.T, next time.Time) { - if !next.Equal(times.Sunrise) && !next.After(times.Sunrise.Add(-1*time.Minute)) { - t.Error("next transition should be at or near sunrise") - } - }, - }, - { - name: "after_sunrise", - now: times.Sunrise.Add(2 * time.Hour), - checkFunc: func(t *testing.T, next time.Time) { - if !next.After(times.Sunrise) { - t.Error("next transition should be after sunrise") - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - next := calculateNextTransition(config, tt.now) - tt.checkFunc(t, next) - }) - } -} - -func TestSunTimesWithTwilight(t *testing.T) { - lat := 40.7128 - lon := -74.0060 - date := time.Date(2024, 6, 21, 12, 0, 0, 0, time.Local) - - times, cond := CalculateSunTimesWithTwilight(lat, lon, date, -6.0, 3.0) - - if cond != SunNormal { - t.Errorf("expected SunNormal, got %v", cond) - } - if !times.Dawn.Before(times.Sunrise) { - t.Error("dawn should be before sunrise") - } - if !times.Sunrise.Before(times.Sunset) { - t.Error("sunrise should be before sunset") - } - if !times.Sunset.Before(times.Night) { - t.Error("sunset should be before night") - } -} - -func TestSunConditions(t *testing.T) { - tests := []struct { - name string - lat float64 - date time.Time - expected SunCondition - }{ - { - name: "normal_conditions", - lat: 40.0, - date: time.Date(2024, 6, 21, 12, 0, 0, 0, time.UTC), - expected: SunNormal, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, cond := CalculateSunTimesWithTwilight(tt.lat, 0, tt.date, -6.0, 3.0) - if cond != tt.expected { - t.Errorf("expected condition %v, got %v", tt.expected, cond) - } - }) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/types.go deleted file mode 100644 index 31bac15..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/types.go +++ /dev/null @@ -1,216 +0,0 @@ -package wayland - -import ( - "math" - "sync" - "sync/atomic" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/godbus/dbus/v5" -) - -type GammaState int - -const ( - StateNormal GammaState = iota - StateTransition - StateStatic -) - -type Config struct { - Outputs []string - LowTemp int - HighTemp int - Latitude *float64 - Longitude *float64 - UseIPLocation bool - ManualSunrise *time.Time - ManualSunset *time.Time - ManualDuration *time.Duration - Gamma float64 - Enabled bool - ElevationTwilight float64 - ElevationDaylight float64 -} - -type State struct { - Config Config `json:"config"` - CurrentTemp int `json:"currentTemp"` - NextTransition time.Time `json:"nextTransition"` - SunriseTime time.Time `json:"sunriseTime"` - SunsetTime time.Time `json:"sunsetTime"` - DawnTime time.Time `json:"dawnTime"` - NightTime time.Time `json:"nightTime"` - IsDay bool `json:"isDay"` - SunPosition float64 `json:"sunPosition"` -} - -type cmd struct { - fn func() -} - -type sunSchedule struct { - times SunTimes - condition SunCondition - dawnStepTime time.Duration - nightStepTime time.Duration - calcDay time.Time -} - -type Manager struct { - config Config - configMutex sync.RWMutex - state *State - stateMutex sync.RWMutex - - display wlclient.WaylandDisplay - ctx *wlclient.Context - registry *wlclient.Registry - gammaControl any - availableOutputs []*wlclient.Output - availOutputsMu sync.RWMutex - outputRegNames syncmap.Map[uint32, uint32] - outputs syncmap.Map[uint32, *outputState] - controlsInitialized bool - connectionDead atomic.Bool - - cmdq chan cmd - alive bool - - stopChan chan struct{} - updateTrigger chan struct{} - wg sync.WaitGroup - - schedule sunSchedule - scheduleMutex sync.RWMutex - gammaState GammaState - - cachedIPLat *float64 - cachedIPLon *float64 - locationMutex sync.RWMutex - - subscribers syncmap.Map[string, chan State] - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotified *State - - dbusConn *dbus.Conn - dbusSignal chan *dbus.Signal - - geoClient geolocation.Client - - lastAppliedTemp int - lastAppliedGamma float64 -} - -type outputState struct { - id uint32 - registryName uint32 - output *wlclient.Output - gammaControl any - rampSize uint32 - failed bool - isVirtual bool - retryCount int - lastFailTime time.Time -} - -func DefaultConfig() Config { - return Config{ - Outputs: []string{}, - LowTemp: 4000, - HighTemp: 6500, - Gamma: 1.0, - Enabled: false, - ElevationTwilight: -6.0, - ElevationDaylight: 3.0, - } -} - -func (c *Config) Validate() error { - if c.LowTemp < 1000 || c.LowTemp > 10000 { - return errdefs.ErrInvalidTemperature - } - if c.HighTemp < 1000 || c.HighTemp > 10000 { - return errdefs.ErrInvalidTemperature - } - if c.LowTemp > c.HighTemp { - return errdefs.ErrInvalidTemperature - } - if c.Gamma <= 0 || c.Gamma > 10 { - return errdefs.ErrInvalidGamma - } - if c.Latitude != nil && (math.Abs(*c.Latitude) > 90) { - return errdefs.ErrInvalidLocation - } - if c.Longitude != nil && (math.Abs(*c.Longitude) > 180) { - return errdefs.ErrInvalidLocation - } - if (c.Latitude != nil) != (c.Longitude != nil) { - return errdefs.ErrInvalidLocation - } - if (c.ManualSunrise != nil) != (c.ManualSunset != nil) { - return errdefs.ErrInvalidManualTimes - } - return nil -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{} - } - return *m.state -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func stateChanged(old, new *State) bool { - if old == nil || new == nil { - return true - } - if old.CurrentTemp != new.CurrentTemp { - return true - } - if old.IsDay != new.IsDay { - return true - } - if !old.NextTransition.Equal(new.NextTransition) { - return true - } - if !old.SunriseTime.Equal(new.SunriseTime) { - return true - } - if !old.SunsetTime.Equal(new.SunsetTime) { - return true - } - if old.Config.Enabled != new.Config.Enabled { - return true - } - if old.SunPosition != new.SunPosition { - return true - } - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/types_test.go deleted file mode 100644 index f5bbb42..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wayland/types_test.go +++ /dev/null @@ -1,330 +0,0 @@ -package wayland - -import ( - "testing" - "time" -) - -func TestConfigValidate(t *testing.T) { - tests := []struct { - name string - config Config - wantErr bool - }{ - { - name: "valid_default", - config: DefaultConfig(), - wantErr: false, - }, - { - name: "valid_with_location", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(40.7128), - Longitude: floatPtr(-74.0060), - Gamma: 1.0, - Enabled: true, - }, - wantErr: false, - }, - { - name: "valid_manual_times", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - ManualSunrise: timePtr(time.Date(0, 1, 1, 6, 30, 0, 0, time.Local)), - ManualSunset: timePtr(time.Date(0, 1, 1, 18, 30, 0, 0, time.Local)), - Gamma: 1.0, - Enabled: true, - }, - wantErr: false, - }, - { - name: "invalid_low_temp_too_low", - config: Config{ - LowTemp: 500, - HighTemp: 6500, - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_low_temp_too_high", - config: Config{ - LowTemp: 15000, - HighTemp: 20000, - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_high_temp_too_low", - config: Config{ - LowTemp: 4000, - HighTemp: 500, - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "valid_temps_equal", - config: Config{ - LowTemp: 5000, - HighTemp: 5000, - Gamma: 1.0, - }, - wantErr: false, - }, - { - name: "invalid_temps_reversed", - config: Config{ - LowTemp: 6500, - HighTemp: 4000, - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_gamma_zero", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Gamma: 0, - }, - wantErr: true, - }, - { - name: "invalid_gamma_negative", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Gamma: -1.0, - }, - wantErr: true, - }, - { - name: "invalid_gamma_too_high", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Gamma: 15.0, - }, - wantErr: true, - }, - { - name: "invalid_latitude_too_high", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(100), - Longitude: floatPtr(0), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_latitude_too_low", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(-100), - Longitude: floatPtr(0), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_longitude_too_high", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(40), - Longitude: floatPtr(200), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_longitude_too_low", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(40), - Longitude: floatPtr(-200), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_latitude_without_longitude", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Latitude: floatPtr(40), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_longitude_without_latitude", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Longitude: floatPtr(-74), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_sunrise_without_sunset", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - ManualSunrise: timePtr(time.Date(0, 1, 1, 6, 30, 0, 0, time.Local)), - Gamma: 1.0, - }, - wantErr: true, - }, - { - name: "invalid_sunset_without_sunrise", - config: Config{ - LowTemp: 4000, - HighTemp: 6500, - ManualSunset: timePtr(time.Date(0, 1, 1, 18, 30, 0, 0, time.Local)), - Gamma: 1.0, - }, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.config.Validate() - if (err != nil) != tt.wantErr { - t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestDefaultConfig(t *testing.T) { - config := DefaultConfig() - - if config.LowTemp != 4000 { - t.Errorf("default low temp = %d, want 4000", config.LowTemp) - } - if config.HighTemp != 6500 { - t.Errorf("default high temp = %d, want 6500", config.HighTemp) - } - if config.Gamma != 1.0 { - t.Errorf("default gamma = %f, want 1.0", config.Gamma) - } - if config.Enabled { - t.Error("default should be disabled") - } - if config.Latitude != nil { - t.Error("default should not have latitude") - } - if config.Longitude != nil { - t.Error("default should not have longitude") - } -} - -func TestStateChanged(t *testing.T) { - baseState := &State{ - CurrentTemp: 5000, - NextTransition: time.Now(), - SunriseTime: time.Now().Add(6 * time.Hour), - SunsetTime: time.Now().Add(18 * time.Hour), - IsDay: true, - Config: DefaultConfig(), - } - - tests := []struct { - name string - old *State - new *State - wantChanged bool - }{ - { - name: "nil_old", - old: nil, - new: baseState, - wantChanged: true, - }, - { - name: "nil_new", - old: baseState, - new: nil, - wantChanged: true, - }, - { - name: "same_state", - old: baseState, - new: baseState, - wantChanged: false, - }, - { - name: "temp_changed", - old: baseState, - new: &State{ - CurrentTemp: 6000, - NextTransition: baseState.NextTransition, - SunriseTime: baseState.SunriseTime, - SunsetTime: baseState.SunsetTime, - IsDay: baseState.IsDay, - Config: baseState.Config, - }, - wantChanged: true, - }, - { - name: "is_day_changed", - old: baseState, - new: &State{ - CurrentTemp: baseState.CurrentTemp, - NextTransition: baseState.NextTransition, - SunriseTime: baseState.SunriseTime, - SunsetTime: baseState.SunsetTime, - IsDay: false, - Config: baseState.Config, - }, - wantChanged: true, - }, - { - name: "enabled_changed", - old: baseState, - new: &State{ - CurrentTemp: baseState.CurrentTemp, - NextTransition: baseState.NextTransition, - SunriseTime: baseState.SunriseTime, - SunsetTime: baseState.SunsetTime, - IsDay: baseState.IsDay, - Config: Config{ - LowTemp: 4000, - HighTemp: 6500, - Gamma: 1.0, - Enabled: true, - }, - }, - wantChanged: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - changed := stateChanged(tt.old, tt.new) - if changed != tt.wantChanged { - t.Errorf("stateChanged() = %v, want %v", changed, tt.wantChanged) - } - }) - } -} - -func floatPtr(f float64) *float64 { - return &f -} - -func timePtr(t time.Time) *time.Time { - return &t -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context.go deleted file mode 100644 index 9bbb30a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context.go +++ /dev/null @@ -1,200 +0,0 @@ -package wlcontext - -import ( - "fmt" - "golang.org/x/sys/unix" - "os" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type WaylandContext interface { - Display() *wlclient.Display - Post(fn func()) - FatalError() <-chan error - Start() - Close() -} - -var _ WaylandContext = (*SharedContext)(nil) - -type SharedContext struct { - display *wlclient.Display - stopChan chan struct{} - fatalError chan error - cmdQueue chan func() - wakeR int - wakeW int - wg sync.WaitGroup - mu sync.Mutex - started bool -} - -func New() (*SharedContext, error) { - display, err := wlclient.Connect("") - if err != nil { - return nil, fmt.Errorf("%w: %v", errdefs.ErrNoWaylandDisplay, err) - } - - fds := make([]int, 2) - if err := unix.Pipe(fds); err != nil { - display.Context().Close() - return nil, fmt.Errorf("failed to create wake pipe: %w", err) - } - if err := unix.SetNonblock(fds[0], true); err != nil { - unix.Close(fds[0]) - unix.Close(fds[1]) - display.Context().Close() - return nil, fmt.Errorf("failed to set wake pipe nonblock: %w", err) - } - if err := unix.SetNonblock(fds[1], true); err != nil { - unix.Close(fds[0]) - unix.Close(fds[1]) - display.Context().Close() - return nil, fmt.Errorf("failed to set wake pipe nonblock: %w", err) - } - - sc := &SharedContext{ - display: display, - stopChan: make(chan struct{}), - fatalError: make(chan error, 1), - cmdQueue: make(chan func(), 256), - wakeR: fds[0], - wakeW: fds[1], - started: false, - } - - return sc, nil -} - -func (sc *SharedContext) Start() { - sc.mu.Lock() - defer sc.mu.Unlock() - - if sc.started { - return - } - - sc.started = true - sc.wg.Add(1) - go sc.eventDispatcher() -} - -func (sc *SharedContext) Display() *wlclient.Display { - return sc.display -} - -func (sc *SharedContext) Post(fn func()) { - select { - case sc.cmdQueue <- fn: - if _, err := unix.Write(sc.wakeW, []byte{1}); err != nil && err != unix.EAGAIN { - log.Errorf("wake pipe write error: %v", err) - } - default: - } -} - -func (sc *SharedContext) FatalError() <-chan error { - return sc.fatalError -} - -func (sc *SharedContext) eventDispatcher() { - defer sc.wg.Done() - defer func() { - if r := recover(); r != nil { - err := fmt.Errorf("FATAL: Wayland event dispatcher panic: %v", r) - log.Error(err) - select { - case sc.fatalError <- err: - default: - } - } - }() - - ctx := sc.display.Context() - wlFd := ctx.Fd() - - pollFds := []unix.PollFd{ - {Fd: int32(wlFd), Events: unix.POLLIN}, - {Fd: int32(sc.wakeR), Events: unix.POLLIN}, - } - - consecutiveErrors := 0 - const maxConsecutiveErrors = 20 - - for { - sc.drainCmdQueue() - - select { - case <-sc.stopChan: - return - default: - } - - _, err := unix.Poll(pollFds, -1) - switch { - case err == unix.EINTR: - continue - case err != nil: - log.Errorf("Poll error: %v", err) - return - } - - if pollFds[1].Revents&unix.POLLIN != 0 { - var buf [64]byte - if _, err := unix.Read(sc.wakeR, buf[:]); err != nil && err != unix.EAGAIN { - log.Errorf("wake pipe read error: %v", err) - } - } - - if pollFds[0].Revents&unix.POLLIN == 0 { - continue - } - - if err := ctx.Dispatch(); err != nil && !os.IsTimeout(err) { - consecutiveErrors++ - log.Warnf("Wayland connection error (%d/%d): %v", consecutiveErrors, maxConsecutiveErrors, err) - - if consecutiveErrors >= maxConsecutiveErrors { - log.Errorf("Fatal: Wayland connection unrecoverable after %d attempts. Exiting dispatcher.", maxConsecutiveErrors) - return - } - - time.Sleep(100 * time.Millisecond * time.Duration(consecutiveErrors)) - continue - } - - consecutiveErrors = 0 - } -} - -func (sc *SharedContext) drainCmdQueue() { - for { - select { - case fn := <-sc.cmdQueue: - fn() - default: - return - } - } -} - -func (sc *SharedContext) Close() { - close(sc.stopChan) - if _, err := unix.Write(sc.wakeW, []byte{1}); err != nil && err != unix.EAGAIN { - log.Errorf("wake pipe write error on close: %v", err) - } - sc.wg.Wait() - - unix.Close(sc.wakeR) - unix.Close(sc.wakeW) - - if sc.display == nil { - return - } - sc.display.Context().Close() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context_test.go deleted file mode 100644 index 11ecfbf..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlcontext/context_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package wlcontext - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" - "golang.org/x/sys/unix" -) - -func newTestSharedContext(t *testing.T, queueSize int) *SharedContext { - t.Helper() - fds := make([]int, 2) - if err := unix.Pipe(fds); err != nil { - t.Fatalf("failed to create test pipe: %v", err) - } - t.Cleanup(func() { - unix.Close(fds[0]) - unix.Close(fds[1]) - }) - return &SharedContext{ - cmdQueue: make(chan func(), queueSize), - stopChan: make(chan struct{}), - wakeR: fds[0], - wakeW: fds[1], - } -} - -func TestSharedContext_ConcurrentPostNonBlocking(t *testing.T) { - sc := newTestSharedContext(t, 256) - - var wg sync.WaitGroup - const goroutines = 100 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - sc.Post(func() { - _ = id + j - }) - } - }(i) - } - - wg.Wait() -} - -func TestSharedContext_PostQueueFull(t *testing.T) { - sc := newTestSharedContext(t, 2) - - sc.Post(func() {}) - sc.Post(func() {}) - sc.Post(func() {}) - sc.Post(func() {}) - - assert.Len(t, sc.cmdQueue, 2) -} - -func TestSharedContext_StartMultipleTimes(t *testing.T) { - sc := newTestSharedContext(t, 256) - sc.started = true - - var wg sync.WaitGroup - const goroutines = 10 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func() { - defer wg.Done() - sc.Start() - }() - } - - wg.Wait() - - assert.True(t, sc.started) -} - -func TestSharedContext_DrainCmdQueue(t *testing.T) { - sc := newTestSharedContext(t, 256) - - counter := 0 - for i := 0; i < 10; i++ { - sc.cmdQueue <- func() { - counter++ - } - } - - sc.drainCmdQueue() - - assert.Equal(t, 10, counter) - assert.Len(t, sc.cmdQueue, 0) -} - -func TestSharedContext_DrainCmdQueueEmpty(t *testing.T) { - sc := newTestSharedContext(t, 256) - - sc.drainCmdQueue() - - assert.Len(t, sc.cmdQueue, 0) -} - -func TestSharedContext_ConcurrentDrainAndPost(t *testing.T) { - sc := newTestSharedContext(t, 256) - - var wg sync.WaitGroup - - wg.Add(1) - go func() { - defer wg.Done() - for i := 0; i < 100; i++ { - sc.Post(func() {}) - } - }() - - wg.Add(1) - go func() { - defer wg.Done() - for i := 0; i < 50; i++ { - sc.drainCmdQueue() - } - }() - - wg.Wait() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/handlers.go deleted file mode 100644 index c885e13..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/handlers.go +++ /dev/null @@ -1,282 +0,0 @@ -package wlroutput - -import ( - "encoding/json" - "fmt" - "net" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" -) - -type HeadConfig struct { - Name string `json:"name"` - Enabled bool `json:"enabled"` - ModeID *uint32 `json:"modeId,omitempty"` - CustomMode *struct { - Width int32 `json:"width"` - Height int32 `json:"height"` - Refresh int32 `json:"refresh"` - } `json:"customMode,omitempty"` - Position *struct{ X, Y int32 } `json:"position,omitempty"` - Transform *int32 `json:"transform,omitempty"` - Scale *float64 `json:"scale,omitempty"` - AdaptiveSync *uint32 `json:"adaptiveSync,omitempty"` -} - -type ConfigurationRequest struct { - Heads []HeadConfig `json:"heads"` - Test bool `json:"test"` -} - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "wlroutput manager not initialized") - return - } - - switch req.Method { - case "wlroutput.getState": - handleGetState(conn, req, manager) - case "wlroutput.applyConfiguration": - handleApplyConfiguration(conn, req, manager, false) - case "wlroutput.testConfiguration": - handleApplyConfiguration(conn, req, manager, true) - case "wlroutput.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleApplyConfiguration(conn net.Conn, req models.Request, manager *Manager, test bool) { - headsParam, ok := models.Get[any](req, "heads") - if !ok { - models.RespondError(conn, req.ID, "missing 'heads' parameter") - return - } - - headsJSON, err := json.Marshal(headsParam) - if err != nil { - models.RespondError(conn, req.ID, "invalid 'heads' parameter format") - return - } - - var heads []HeadConfig - if err := json.Unmarshal(headsJSON, &heads); err != nil { - models.RespondError(conn, req.ID, fmt.Sprintf("invalid heads configuration: %v", err)) - return - } - - if err := manager.ApplyConfiguration(heads, test); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - msg := "configuration applied" - if test { - msg = "configuration test succeeded" - } - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: msg}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} - -func (m *Manager) ApplyConfiguration(heads []HeadConfig, test bool) error { - if m.manager == nil { - return fmt.Errorf("output manager not initialized") - } - - resultChan := make(chan error, 1) - - m.post(func() { - m.wlMutex.Lock() - defer m.wlMutex.Unlock() - - config, err := m.manager.CreateConfiguration(m.serial) - if err != nil { - resultChan <- fmt.Errorf("failed to create configuration: %w", err) - return - } - - responded := false - - config.SetSucceededHandler(func(e wlr_output_management.ZwlrOutputConfigurationV1SucceededEvent) { - if responded { - return - } - responded = true - log.Info("WlrOutput: configuration succeeded") - config.Destroy() - resultChan <- nil - }) - - config.SetFailedHandler(func(e wlr_output_management.ZwlrOutputConfigurationV1FailedEvent) { - if responded { - return - } - responded = true - log.Warn("WlrOutput: configuration failed") - config.Destroy() - resultChan <- fmt.Errorf("compositor rejected configuration") - }) - - config.SetCancelledHandler(func(e wlr_output_management.ZwlrOutputConfigurationV1CancelledEvent) { - if responded { - return - } - responded = true - log.Warn("WlrOutput: configuration cancelled") - config.Destroy() - resultChan <- fmt.Errorf("configuration cancelled (outdated serial)") - }) - - time.AfterFunc(time.Second, func() { - m.post(func() { - if responded { - return - } - responded = true - config.Destroy() - resultChan <- fmt.Errorf("timeout waiting for configuration response") - }) - }) - - headsByName := make(map[string]*headState) - m.heads.Range(func(key uint32, head *headState) bool { - if !head.finished { - headsByName[head.name] = head - } - return true - }) - - for _, headCfg := range heads { - head, exists := headsByName[headCfg.Name] - if !exists { - config.Destroy() - resultChan <- fmt.Errorf("head not found: %s", headCfg.Name) - return - } - - if !headCfg.Enabled { - if err := config.DisableHead(head.handle); err != nil { - config.Destroy() - resultChan <- fmt.Errorf("failed to disable head %s: %w", headCfg.Name, err) - return - } - continue - } - - headConfig, err := config.EnableHead(head.handle) - if err != nil { - config.Destroy() - resultChan <- fmt.Errorf("failed to enable head %s: %w", headCfg.Name, err) - return - } - - if headCfg.ModeID != nil { - mode, exists := m.modes.Load(*headCfg.ModeID) - - if !exists { - config.Destroy() - resultChan <- fmt.Errorf("mode not found: %d", *headCfg.ModeID) - return - } - - if err := headConfig.SetMode(mode.handle); err != nil { - config.Destroy() - resultChan <- fmt.Errorf("failed to set mode for %s: %w", headCfg.Name, err) - return - } - } else if headCfg.CustomMode != nil { - if err := headConfig.SetCustomMode( - headCfg.CustomMode.Width, - headCfg.CustomMode.Height, - headCfg.CustomMode.Refresh, - ); err != nil { - config.Destroy() - resultChan <- fmt.Errorf("failed to set custom mode for %s: %w", headCfg.Name, err) - return - } - } - - if headCfg.Position != nil { - if err := headConfig.SetPosition(headCfg.Position.X, headCfg.Position.Y); err != nil { - config.Destroy() - resultChan <- fmt.Errorf("failed to set position for %s: %w", headCfg.Name, err) - return - } - } - - if headCfg.Transform != nil { - if err := headConfig.SetTransform(*headCfg.Transform); err != nil { - config.Destroy() - resultChan <- fmt.Errorf("failed to set transform for %s: %w", headCfg.Name, err) - return - } - } - - if headCfg.Scale != nil { - if err := headConfig.SetScale(*headCfg.Scale); err != nil { - config.Destroy() - resultChan <- fmt.Errorf("failed to set scale for %s: %w", headCfg.Name, err) - return - } - } - - if headCfg.AdaptiveSync != nil { - if err := headConfig.SetAdaptiveSync(*headCfg.AdaptiveSync); err != nil { - config.Destroy() - resultChan <- fmt.Errorf("failed to set adaptive sync for %s: %w", headCfg.Name, err) - return - } - } - } - - var applyErr error - if test { - applyErr = config.Test() - } else { - applyErr = config.Apply() - } - - if applyErr != nil { - responded = true - config.Destroy() - action := "apply" - if test { - action = "test" - } - resultChan <- fmt.Errorf("failed to %s configuration: %w", action, applyErr) - return - } - }) - - return <-resultChan -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/manager.go deleted file mode 100644 index cbd89a3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/manager.go +++ /dev/null @@ -1,492 +0,0 @@ -package wlroutput - -import ( - "fmt" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -func NewManager(display wlclient.WaylandDisplay) (*Manager, error) { - m := &Manager{ - display: display, - ctx: display.Context(), - cmdq: make(chan cmd, 512), - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - fatalError: make(chan error, 1), - } - - m.wg.Add(1) - go m.waylandActor() - - if err := m.setupRegistry(); err != nil { - close(m.stopChan) - m.wg.Wait() - return nil, err - } - - m.updateState() - - m.notifierWg.Add(1) - go m.notifier() - - return m, nil -} - -func (m *Manager) post(fn func()) { - select { - case m.cmdq <- cmd{fn: fn}: - default: - log.Warn("WlrOutput actor command queue full, dropping command") - } -} - -func (m *Manager) waylandActor() { - defer m.wg.Done() - defer func() { - if r := recover(); r != nil { - err := fmt.Errorf("waylandActor panic: %v", r) - log.Errorf("WlrOutput: %v", err) - - select { - case m.fatalError <- err: - default: - } - - select { - case <-m.stopChan: - default: - close(m.stopChan) - } - } - }() - - for { - select { - case <-m.stopChan: - return - case c := <-m.cmdq: - func() { - defer func() { - if r := recover(); r != nil { - log.Errorf("WlrOutput: command execution panic: %v", r) - } - }() - c.fn() - }() - } - } -} - -func (m *Manager) setupRegistry() error { - log.Info("WlrOutput: starting registry setup") - - registry, err := m.display.GetRegistry() - if err != nil { - return fmt.Errorf("failed to get registry: %w", err) - } - m.registry = registry - - registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) { - if e.Interface == wlr_output_management.ZwlrOutputManagerV1InterfaceName { - log.Infof("WlrOutput: found %s", wlr_output_management.ZwlrOutputManagerV1InterfaceName) - manager := wlr_output_management.NewZwlrOutputManagerV1(m.ctx) - version := e.Version - if version > 4 { - version = 4 - } - - manager.SetHeadHandler(func(e wlr_output_management.ZwlrOutputManagerV1HeadEvent) { - m.handleHead(e) - }) - - manager.SetDoneHandler(func(e wlr_output_management.ZwlrOutputManagerV1DoneEvent) { - log.Debugf("WlrOutput: done event received (serial=%d)", e.Serial) - m.serial = e.Serial - m.post(func() { - m.updateState() - }) - }) - - manager.SetFinishedHandler(func(e wlr_output_management.ZwlrOutputManagerV1FinishedEvent) { - log.Info("WlrOutput: finished event received") - }) - - if err := registry.Bind(e.Name, e.Interface, version, manager); err == nil { - m.manager = manager - log.Info("WlrOutput: manager bound successfully") - } else { - log.Errorf("WlrOutput: failed to bind manager: %v", err) - } - } - }) - - log.Info("WlrOutput: registry setup complete (events will be processed async)") - return nil -} - -func (m *Manager) handleHead(e wlr_output_management.ZwlrOutputManagerV1HeadEvent) { - handle := e.Head - headID := handle.ID() - - log.Debugf("WlrOutput: New head (id=%d)", headID) - - head := &headState{ - id: headID, - handle: handle, - modeIDs: make([]uint32, 0), - } - - m.heads.Store(headID, head) - - handle.SetNameHandler(func(e wlr_output_management.ZwlrOutputHeadV1NameEvent) { - log.Debugf("WlrOutput: Head %d name: %s", headID, e.Name) - head.name = e.Name - head.ready = true - m.post(func() { - m.updateState() - }) - }) - - handle.SetDescriptionHandler(func(e wlr_output_management.ZwlrOutputHeadV1DescriptionEvent) { - log.Debugf("WlrOutput: Head %d description: %s", headID, e.Description) - head.description = e.Description - m.post(func() { - m.updateState() - }) - }) - - handle.SetPhysicalSizeHandler(func(e wlr_output_management.ZwlrOutputHeadV1PhysicalSizeEvent) { - log.Debugf("WlrOutput: Head %d physical size: %dx%d", headID, e.Width, e.Height) - head.physicalWidth = e.Width - head.physicalHeight = e.Height - m.post(func() { - m.updateState() - }) - }) - - handle.SetModeHandler(func(e wlr_output_management.ZwlrOutputHeadV1ModeEvent) { - m.handleMode(headID, e) - }) - - handle.SetEnabledHandler(func(e wlr_output_management.ZwlrOutputHeadV1EnabledEvent) { - log.Debugf("WlrOutput: Head %d enabled: %d", headID, e.Enabled) - head.enabled = e.Enabled != 0 - m.post(func() { - m.updateState() - }) - }) - - handle.SetCurrentModeHandler(func(e wlr_output_management.ZwlrOutputHeadV1CurrentModeEvent) { - modeID := e.Mode.ID() - log.Debugf("WlrOutput: Head %d current mode: %d", headID, modeID) - head.currentModeID = modeID - m.post(func() { - m.updateState() - }) - }) - - handle.SetPositionHandler(func(e wlr_output_management.ZwlrOutputHeadV1PositionEvent) { - log.Debugf("WlrOutput: Head %d position: %d,%d", headID, e.X, e.Y) - head.x = e.X - head.y = e.Y - m.post(func() { - m.updateState() - }) - }) - - handle.SetTransformHandler(func(e wlr_output_management.ZwlrOutputHeadV1TransformEvent) { - log.Debugf("WlrOutput: Head %d transform: %d", headID, e.Transform) - head.transform = e.Transform - m.post(func() { - m.updateState() - }) - }) - - handle.SetScaleHandler(func(e wlr_output_management.ZwlrOutputHeadV1ScaleEvent) { - log.Debugf("WlrOutput: Head %d scale: %f", headID, e.Scale) - head.scale = e.Scale - m.post(func() { - m.updateState() - }) - }) - - handle.SetMakeHandler(func(e wlr_output_management.ZwlrOutputHeadV1MakeEvent) { - log.Debugf("WlrOutput: Head %d make: %s", headID, e.Make) - head.make = e.Make - m.post(func() { - m.updateState() - }) - }) - - handle.SetModelHandler(func(e wlr_output_management.ZwlrOutputHeadV1ModelEvent) { - log.Debugf("WlrOutput: Head %d model: %s", headID, e.Model) - head.model = e.Model - m.post(func() { - m.updateState() - }) - }) - - handle.SetSerialNumberHandler(func(e wlr_output_management.ZwlrOutputHeadV1SerialNumberEvent) { - log.Debugf("WlrOutput: Head %d serial: %s", headID, e.SerialNumber) - head.serialNumber = e.SerialNumber - m.post(func() { - m.updateState() - }) - }) - - handle.SetAdaptiveSyncHandler(func(e wlr_output_management.ZwlrOutputHeadV1AdaptiveSyncEvent) { - log.Debugf("WlrOutput: Head %d adaptive sync: %d", headID, e.State) - head.adaptiveSync = e.State - head.adaptiveSyncSupported = true - m.post(func() { - m.updateState() - }) - }) - - handle.SetFinishedHandler(func(e wlr_output_management.ZwlrOutputHeadV1FinishedEvent) { - log.Debugf("WlrOutput: Head %d finished", headID) - head.finished = true - - m.heads.Delete(headID) - - m.wlMutex.Lock() - handle.Release() - m.wlMutex.Unlock() - - m.post(func() { - m.updateState() - }) - }) -} - -func (m *Manager) handleMode(headID uint32, e wlr_output_management.ZwlrOutputHeadV1ModeEvent) { - handle := e.Mode - modeID := handle.ID() - - log.Debugf("WlrOutput: Head %d new mode (id=%d)", headID, modeID) - - mode := &modeState{ - id: modeID, - handle: handle, - } - - m.modes.Store(modeID, mode) - - if head, ok := m.heads.Load(headID); ok { - head.modeIDs = append(head.modeIDs, modeID) - m.heads.Store(headID, head) - } - - handle.SetSizeHandler(func(e wlr_output_management.ZwlrOutputModeV1SizeEvent) { - log.Debugf("WlrOutput: Mode %d size: %dx%d", modeID, e.Width, e.Height) - mode.width = e.Width - mode.height = e.Height - m.post(func() { - m.updateState() - }) - }) - - handle.SetRefreshHandler(func(e wlr_output_management.ZwlrOutputModeV1RefreshEvent) { - log.Debugf("WlrOutput: Mode %d refresh: %d", modeID, e.Refresh) - mode.refresh = e.Refresh - m.post(func() { - m.updateState() - }) - }) - - handle.SetPreferredHandler(func(e wlr_output_management.ZwlrOutputModeV1PreferredEvent) { - log.Debugf("WlrOutput: Mode %d preferred", modeID) - mode.preferred = true - m.post(func() { - m.updateState() - }) - }) - - handle.SetFinishedHandler(func(e wlr_output_management.ZwlrOutputModeV1FinishedEvent) { - log.Debugf("WlrOutput: Mode %d finished", modeID) - mode.finished = true - - m.modes.Delete(modeID) - - m.wlMutex.Lock() - handle.Release() - m.wlMutex.Unlock() - - m.post(func() { - m.updateState() - }) - }) -} - -func (m *Manager) updateState() { - outputs := make([]Output, 0) - - m.heads.Range(func(key uint32, head *headState) bool { - if head.finished { - return true - } - - if !head.ready { - return true - } - - modes := make([]OutputMode, 0) - var currentMode *OutputMode - - for _, modeID := range head.modeIDs { - mode, exists := m.modes.Load(modeID) - if !exists { - continue - } - if mode.finished { - continue - } - - outMode := OutputMode{ - Width: mode.width, - Height: mode.height, - Refresh: mode.refresh, - Preferred: mode.preferred, - ID: modeID, - } - modes = append(modes, outMode) - - if modeID == head.currentModeID { - currentMode = &outMode - } - } - - output := Output{ - Name: head.name, - Description: head.description, - Make: head.make, - Model: head.model, - SerialNumber: head.serialNumber, - PhysicalWidth: head.physicalWidth, - PhysicalHeight: head.physicalHeight, - Enabled: head.enabled, - X: head.x, - Y: head.y, - Transform: head.transform, - Scale: head.scale, - CurrentMode: currentMode, - Modes: modes, - AdaptiveSync: head.adaptiveSync, - AdaptiveSyncSupported: head.adaptiveSyncSupported, - ID: head.id, - } - outputs = append(outputs, output) - return true - }) - - newState := State{ - Outputs: outputs, - Serial: m.serial, - } - - m.stateMutex.Lock() - m.state = &newState - m.stateMutex.Unlock() - - m.notifySubscribers() -} - -func (m *Manager) notifier() { - defer m.notifierWg.Done() - defer func() { - if r := recover(); r != nil { - err := fmt.Errorf("notifier panic: %v", r) - log.Errorf("WlrOutput: %v", err) - - select { - case m.fatalError <- err: - default: - } - - select { - case <-m.stopChan: - default: - close(m.stopChan) - } - } - }() - - const minGap = 100 * time.Millisecond - timer := time.NewTimer(minGap) - timer.Stop() - var pending bool - - for { - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.dirty: - if pending { - continue - } - pending = true - timer.Reset(minGap) - case <-timer.C: - if !pending { - continue - } - - currentState := m.GetState() - - if m.lastNotified != nil && !stateChanged(m.lastNotified, ¤tState) { - pending = false - continue - } - - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- currentState: - default: - log.Warn("WlrOutput: subscriber channel full, dropping update") - } - return true - }) - - stateCopy := currentState - m.lastNotified = &stateCopy - pending = false - } - } -} - -func (m *Manager) Close() { - close(m.stopChan) - m.wg.Wait() - m.notifierWg.Wait() - - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - m.modes.Range(func(key uint32, mode *modeState) bool { - if mode.handle != nil { - mode.handle.Release() - } - m.modes.Delete(key) - return true - }) - - m.heads.Range(func(key uint32, head *headState) bool { - if head.handle != nil { - head.handle.Release() - } - m.heads.Delete(key) - return true - }) - - if m.manager != nil { - m.manager.Stop() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/manager_test.go deleted file mode 100644 index e90c70c..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/manager_test.go +++ /dev/null @@ -1,414 +0,0 @@ -package wlroutput - -import ( - "errors" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - mocks_wlclient "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/wlclient" -) - -func TestStateChanged_BothNil(t *testing.T) { - assert.True(t, stateChanged(nil, nil)) -} - -func TestStateChanged_OneNil(t *testing.T) { - s := &State{Serial: 1} - assert.True(t, stateChanged(s, nil)) - assert.True(t, stateChanged(nil, s)) -} - -func TestStateChanged_SerialDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{}} - b := &State{Serial: 2, Outputs: []Output{}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputCountDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1"}}} - b := &State{Serial: 1, Outputs: []Output{}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputNameDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Enabled: true}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "HDMI-A-1", Enabled: true}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputEnabledDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Enabled: true}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Enabled: false}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputPositionDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", X: 0, Y: 0}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", X: 1920, Y: 0}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputTransformDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Transform: 0}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Transform: 1}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputScaleDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Scale: 1.0}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Scale: 2.0}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_OutputAdaptiveSyncDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", AdaptiveSync: 0}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", AdaptiveSync: 1}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_CurrentModeNilVsNonNil(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", CurrentMode: nil}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", CurrentMode: &OutputMode{Width: 1920}}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_CurrentModeDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{ - Name: "eDP-1", - CurrentMode: &OutputMode{Width: 1920, Height: 1080, Refresh: 60000}, - }}} - b := &State{Serial: 1, Outputs: []Output{{ - Name: "eDP-1", - CurrentMode: &OutputMode{Width: 2560, Height: 1440, Refresh: 60000}, - }}} - assert.True(t, stateChanged(a, b)) - - b.Outputs[0].CurrentMode.Width = 1920 - b.Outputs[0].CurrentMode.Height = 1080 - b.Outputs[0].CurrentMode.Refresh = 144000 - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_ModesLengthDiffers(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Modes: []OutputMode{{Width: 1920}}}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", Modes: []OutputMode{{Width: 1920}, {Width: 1280}}}}} - assert.True(t, stateChanged(a, b)) -} - -func TestStateChanged_Equal(t *testing.T) { - mode := OutputMode{Width: 1920, Height: 1080, Refresh: 60000, Preferred: true} - a := &State{ - Serial: 5, - Outputs: []Output{{ - Name: "eDP-1", - Description: "Built-in display", - Make: "BOE", - Model: "0x0ABC", - SerialNumber: "12345", - PhysicalWidth: 309, - PhysicalHeight: 174, - Enabled: true, - X: 0, - Y: 0, - Transform: 0, - Scale: 1.0, - CurrentMode: &mode, - Modes: []OutputMode{mode}, - AdaptiveSync: 0, - }}, - } - b := &State{ - Serial: 5, - Outputs: []Output{{ - Name: "eDP-1", - Description: "Built-in display", - Make: "BOE", - Model: "0x0ABC", - SerialNumber: "12345", - PhysicalWidth: 309, - PhysicalHeight: 174, - Enabled: true, - X: 0, - Y: 0, - Transform: 0, - Scale: 1.0, - CurrentMode: &mode, - Modes: []OutputMode{mode}, - AdaptiveSync: 0, - }}, - } - assert.False(t, stateChanged(a, b)) -} - -func TestManager_ConcurrentGetState(t *testing.T) { - m := &Manager{ - state: &State{ - Serial: 1, - Outputs: []Output{{Name: "eDP-1", Enabled: true}}, - }, - } - - var wg sync.WaitGroup - const goroutines = 50 - const iterations = 100 - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { - s := m.GetState() - _ = s.Serial - _ = s.Outputs - } - }() - } - - for i := 0; i < goroutines/2; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - for j := 0; j < iterations; j++ { - m.stateMutex.Lock() - m.state = &State{ - Serial: uint32(j), - Outputs: []Output{{Name: "eDP-1", Scale: float64(j % 3)}}, - } - m.stateMutex.Unlock() - } - }(i) - } - - wg.Wait() -} - -func TestManager_ConcurrentSubscriberAccess(t *testing.T) { - m := &Manager{ - stopChan: make(chan struct{}), - dirty: make(chan struct{}, 1), - } - - var wg sync.WaitGroup - const goroutines = 20 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - subID := string(rune('a' + id)) - ch := m.Subscribe(subID) - assert.NotNil(t, ch) - time.Sleep(time.Millisecond) - m.Unsubscribe(subID) - }(i) - } - - wg.Wait() -} - -func TestManager_SyncmapHeadsConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - state := &headState{ - id: key, - name: "test-head", - enabled: j%2 == 0, - scale: float64(j % 3), - modeIDs: []uint32{uint32(j)}, - } - m.heads.Store(key, state) - - if loaded, ok := m.heads.Load(key); ok { - assert.Equal(t, key, loaded.id) - } - - m.heads.Range(func(k uint32, v *headState) bool { - _ = v.name - _ = v.enabled - return true - }) - } - - m.heads.Delete(key) - }(i) - } - - wg.Wait() -} - -func TestManager_SyncmapModesConcurrentAccess(t *testing.T) { - m := &Manager{} - - var wg sync.WaitGroup - const goroutines = 30 - const iterations = 50 - - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func(id int) { - defer wg.Done() - key := uint32(id) - - for j := 0; j < iterations; j++ { - state := &modeState{ - id: key, - width: int32(1920 + j), - height: int32(1080 + j), - refresh: 60000, - preferred: j == 0, - } - m.modes.Store(key, state) - - if loaded, ok := m.modes.Load(key); ok { - assert.Equal(t, key, loaded.id) - } - - m.modes.Range(func(k uint32, v *modeState) bool { - _ = v.width - _ = v.height - return true - }) - } - - m.modes.Delete(key) - }(i) - } - - wg.Wait() -} - -func TestManager_NotifySubscribersNonBlocking(t *testing.T) { - m := &Manager{ - dirty: make(chan struct{}, 1), - } - - for i := 0; i < 10; i++ { - m.notifySubscribers() - } - - assert.Len(t, m.dirty, 1) -} - -func TestManager_PostQueueFull(t *testing.T) { - m := &Manager{ - cmdq: make(chan cmd, 2), - stopChan: make(chan struct{}), - } - - m.post(func() {}) - m.post(func() {}) - m.post(func() {}) - m.post(func() {}) - - assert.Len(t, m.cmdq, 2) -} - -func TestManager_GetStateNilState(t *testing.T) { - m := &Manager{} - - s := m.GetState() - assert.NotNil(t, s.Outputs) - assert.Equal(t, uint32(0), s.Serial) -} - -func TestManager_FatalErrorChannel(t *testing.T) { - m := &Manager{ - fatalError: make(chan error, 1), - } - - ch := m.FatalError() - assert.NotNil(t, ch) - - m.fatalError <- assert.AnError - err := <-ch - assert.Error(t, err) -} - -func TestOutputMode_Fields(t *testing.T) { - mode := OutputMode{ - Width: 1920, - Height: 1080, - Refresh: 60000, - Preferred: true, - ID: 42, - } - - assert.Equal(t, int32(1920), mode.Width) - assert.Equal(t, int32(1080), mode.Height) - assert.Equal(t, int32(60000), mode.Refresh) - assert.True(t, mode.Preferred) - assert.Equal(t, uint32(42), mode.ID) -} - -func TestOutput_Fields(t *testing.T) { - out := Output{ - Name: "eDP-1", - Description: "Built-in display", - Make: "BOE", - Model: "0x0ABC", - SerialNumber: "12345", - PhysicalWidth: 309, - PhysicalHeight: 174, - Enabled: true, - X: 0, - Y: 0, - Transform: 0, - Scale: 1.5, - AdaptiveSync: 1, - ID: 1, - } - - assert.Equal(t, "eDP-1", out.Name) - assert.Equal(t, "Built-in display", out.Description) - assert.True(t, out.Enabled) - assert.Equal(t, float64(1.5), out.Scale) - assert.Equal(t, uint32(1), out.AdaptiveSync) -} - -func TestHeadState_ModeIDsSlice(t *testing.T) { - head := &headState{ - id: 1, - modeIDs: make([]uint32, 0), - } - - head.modeIDs = append(head.modeIDs, 1, 2, 3) - assert.Len(t, head.modeIDs, 3) - assert.Equal(t, uint32(1), head.modeIDs[0]) -} - -func TestStateChanged_BothCurrentModeNil(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", CurrentMode: nil}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1", CurrentMode: nil}}} - assert.False(t, stateChanged(a, b)) -} - -func TestStateChanged_IndexOutOfBounds(t *testing.T) { - a := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1"}}} - b := &State{Serial: 1, Outputs: []Output{{Name: "eDP-1"}, {Name: "HDMI-A-1"}}} - assert.True(t, stateChanged(a, b)) -} - -func TestNewManager_GetRegistryError(t *testing.T) { - mockDisplay := mocks_wlclient.NewMockWaylandDisplay(t) - - mockDisplay.EXPECT().Context().Return(nil) - mockDisplay.EXPECT().GetRegistry().Return(nil, errors.New("failed to get registry")) - - _, err := NewManager(mockDisplay) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to get registry") -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/types.go deleted file mode 100644 index 9c79b1a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/wlroutput/types.go +++ /dev/null @@ -1,192 +0,0 @@ -package wlroutput - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management" - wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type OutputMode struct { - Width int32 `json:"width"` - Height int32 `json:"height"` - Refresh int32 `json:"refresh"` - Preferred bool `json:"preferred"` - ID uint32 `json:"id"` -} - -type Output struct { - Name string `json:"name"` - Description string `json:"description"` - Make string `json:"make"` - Model string `json:"model"` - SerialNumber string `json:"serialNumber"` - PhysicalWidth int32 `json:"physicalWidth"` - PhysicalHeight int32 `json:"physicalHeight"` - Enabled bool `json:"enabled"` - X int32 `json:"x"` - Y int32 `json:"y"` - Transform int32 `json:"transform"` - Scale float64 `json:"scale"` - CurrentMode *OutputMode `json:"currentMode"` - Modes []OutputMode `json:"modes"` - AdaptiveSync uint32 `json:"adaptiveSync"` - AdaptiveSyncSupported bool `json:"adaptiveSyncSupported"` - ID uint32 `json:"id"` -} - -type State struct { - Outputs []Output `json:"outputs"` - Serial uint32 `json:"serial"` -} - -type cmd struct { - fn func() -} - -type Manager struct { - display wlclient.WaylandDisplay - ctx *wlclient.Context - registry *wlclient.Registry - manager *wlr_output_management.ZwlrOutputManagerV1 - - heads syncmap.Map[uint32, *headState] - modes syncmap.Map[uint32, *modeState] - - serial uint32 - - wlMutex sync.Mutex - cmdq chan cmd - stopChan chan struct{} - wg sync.WaitGroup - - subscribers syncmap.Map[string, chan State] - dirty chan struct{} - notifierWg sync.WaitGroup - lastNotified *State - - stateMutex sync.RWMutex - state *State - - fatalError chan error -} - -type headState struct { - id uint32 - handle *wlr_output_management.ZwlrOutputHeadV1 - name string - description string - make string - model string - serialNumber string - physicalWidth int32 - physicalHeight int32 - enabled bool - x int32 - y int32 - transform int32 - scale float64 - currentModeID uint32 - modeIDs []uint32 - adaptiveSync uint32 - adaptiveSyncSupported bool - finished bool - ready bool -} - -type modeState struct { - id uint32 - handle *wlr_output_management.ZwlrOutputModeV1 - width int32 - height int32 - refresh int32 - preferred bool - finished bool -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{ - Outputs: []Output{}, - Serial: 0, - } - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - - m.subscribers.Store(id, ch) - - return ch -} - -func (m *Manager) Unsubscribe(id string) { - - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - - } - -} - -func (m *Manager) notifySubscribers() { - select { - case m.dirty <- struct{}{}: - default: - } -} - -func (m *Manager) FatalError() <-chan error { - return m.fatalError -} - -func stateChanged(old, new *State) bool { - if old == nil || new == nil { - return true - } - if old.Serial != new.Serial { - return true - } - if len(old.Outputs) != len(new.Outputs) { - return true - } - for i := range new.Outputs { - if i >= len(old.Outputs) { - return true - } - oldOut := &old.Outputs[i] - newOut := &new.Outputs[i] - if oldOut.Name != newOut.Name || oldOut.Enabled != newOut.Enabled { - return true - } - if oldOut.X != newOut.X || oldOut.Y != newOut.Y { - return true - } - if oldOut.Transform != newOut.Transform || oldOut.Scale != newOut.Scale { - return true - } - if oldOut.AdaptiveSync != newOut.AdaptiveSync || oldOut.AdaptiveSyncSupported != newOut.AdaptiveSyncSupported { - return true - } - if (oldOut.CurrentMode == nil) != (newOut.CurrentMode == nil) { - return true - } - if oldOut.CurrentMode != nil && newOut.CurrentMode != nil { - if oldOut.CurrentMode.Width != newOut.CurrentMode.Width || - oldOut.CurrentMode.Height != newOut.CurrentMode.Height || - oldOut.CurrentMode.Refresh != newOut.CurrentMode.Refresh { - return true - } - } - if len(oldOut.Modes) != len(newOut.Modes) { - return true - } - } - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/manager.go deleted file mode 100644 index 2ebe7ae..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/manager.go +++ /dev/null @@ -1,258 +0,0 @@ -package themes - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/spf13/afero" -) - -type Manager struct { - fs afero.Fs - themesDir string -} - -func NewManager() (*Manager, error) { - return NewManagerWithFs(afero.NewOsFs()) -} - -func NewManagerWithFs(fs afero.Fs) (*Manager, error) { - themesDir := getThemesDir() - return &Manager{ - fs: fs, - themesDir: themesDir, - }, nil -} - -func getThemesDir() string { - configDir, err := os.UserConfigDir() - if err != nil { - log.Error("failed to get user config dir", "err", err) - return "" - } - return filepath.Join(configDir, "DankMaterialShell", "themes") -} - -func (m *Manager) IsInstalled(theme Theme) (bool, error) { - path := m.getInstalledPath(theme.ID) - exists, err := afero.Exists(m.fs, path) - if err != nil { - return false, err - } - return exists, nil -} - -func (m *Manager) getInstalledDir(themeID string) string { - return filepath.Join(m.themesDir, themeID) -} - -func (m *Manager) getInstalledPath(themeID string) string { - return filepath.Join(m.getInstalledDir(themeID), "theme.json") -} - -func (m *Manager) Install(theme Theme, registryThemeDir string) error { - themeDir := m.getInstalledDir(theme.ID) - - exists, err := afero.DirExists(m.fs, themeDir) - if err != nil { - return fmt.Errorf("failed to check if theme exists: %w", err) - } - - if exists { - return fmt.Errorf("theme already installed: %s", theme.Name) - } - - if err := m.fs.MkdirAll(themeDir, 0o755); err != nil { - return fmt.Errorf("failed to create theme directory: %w", err) - } - - data, err := json.MarshalIndent(theme, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal theme: %w", err) - } - - themePath := filepath.Join(themeDir, "theme.json") - if err := afero.WriteFile(m.fs, themePath, data, 0o644); err != nil { - return fmt.Errorf("failed to write theme file: %w", err) - } - - m.copyPreviewFiles(registryThemeDir, themeDir, theme) - return nil -} - -func (m *Manager) copyPreviewFiles(srcDir, dstDir string, theme Theme) { - previews := []string{"preview-dark.svg", "preview-light.svg"} - - if theme.Variants != nil { - for _, v := range theme.Variants.Options { - previews = append(previews, - fmt.Sprintf("preview-%s.svg", v.ID), - fmt.Sprintf("preview-%s-dark.svg", v.ID), - fmt.Sprintf("preview-%s-light.svg", v.ID), - ) - } - } - - for _, preview := range previews { - srcPath := filepath.Join(srcDir, preview) - if exists, _ := afero.Exists(m.fs, srcPath); !exists { - continue - } - data, err := afero.ReadFile(m.fs, srcPath) - if err != nil { - continue - } - dstPath := filepath.Join(dstDir, preview) - _ = afero.WriteFile(m.fs, dstPath, data, 0o644) - } -} - -func (m *Manager) InstallFromRegistry(registry *Registry, themeID string) error { - theme, err := registry.Get(themeID) - if err != nil { - return err - } - - registryThemeDir := registry.GetThemeDir(theme.SourceDir) - return m.Install(*theme, registryThemeDir) -} - -func (m *Manager) Update(theme Theme) error { - themePath := m.getInstalledPath(theme.ID) - - exists, err := afero.Exists(m.fs, themePath) - if err != nil { - return fmt.Errorf("failed to check if theme exists: %w", err) - } - - if !exists { - return fmt.Errorf("theme not installed: %s", theme.Name) - } - - data, err := json.MarshalIndent(theme, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal theme: %w", err) - } - - if err := afero.WriteFile(m.fs, themePath, data, 0o644); err != nil { - return fmt.Errorf("failed to write theme file: %w", err) - } - - return nil -} - -func (m *Manager) Uninstall(theme Theme) error { - return m.UninstallByID(theme.ID) -} - -func (m *Manager) UninstallByID(themeID string) error { - themeDir := m.getInstalledDir(themeID) - - exists, err := afero.DirExists(m.fs, themeDir) - if err != nil { - return fmt.Errorf("failed to check if theme exists: %w", err) - } - - if !exists { - return fmt.Errorf("theme not installed: %s", themeID) - } - - if err := m.fs.RemoveAll(themeDir); err != nil { - return fmt.Errorf("failed to remove theme: %w", err) - } - - return nil -} - -func (m *Manager) ListInstalled() ([]string, error) { - exists, err := afero.DirExists(m.fs, m.themesDir) - if err != nil { - return nil, err - } - - if !exists { - return []string{}, nil - } - - entries, err := afero.ReadDir(m.fs, m.themesDir) - if err != nil { - return nil, fmt.Errorf("failed to read themes directory: %w", err) - } - - var installed []string - for _, entry := range entries { - if !entry.IsDir() { - continue - } - - themeID := entry.Name() - themePath := filepath.Join(m.themesDir, themeID, "theme.json") - if exists, _ := afero.Exists(m.fs, themePath); exists { - installed = append(installed, themeID) - } - } - - return installed, nil -} - -func (m *Manager) GetInstalledTheme(themeID string) (*Theme, error) { - themePath := m.getInstalledPath(themeID) - - data, err := afero.ReadFile(m.fs, themePath) - if err != nil { - return nil, fmt.Errorf("failed to read theme file: %w", err) - } - - var theme Theme - if err := json.Unmarshal(data, &theme); err != nil { - return nil, fmt.Errorf("failed to parse theme file: %w", err) - } - - return &theme, nil -} - -func (m *Manager) HasUpdates(themeID string, registryTheme Theme) (bool, error) { - installed, err := m.GetInstalledTheme(themeID) - if err != nil { - return false, err - } - - return compareVersions(installed.Version, registryTheme.Version) < 0, nil -} - -func compareVersions(installed, registry string) int { - installedParts := strings.Split(installed, ".") - registryParts := strings.Split(registry, ".") - - maxLen := len(installedParts) - if len(registryParts) > maxLen { - maxLen = len(registryParts) - } - - for i := 0; i < maxLen; i++ { - var installedNum, registryNum int - if i < len(installedParts) { - fmt.Sscanf(installedParts[i], "%d", &installedNum) - } - if i < len(registryParts) { - fmt.Sscanf(registryParts[i], "%d", ®istryNum) - } - - if installedNum < registryNum { - return -1 - } - if installedNum > registryNum { - return 1 - } - } - - return 0 -} - -func (m *Manager) GetThemesDir() string { - return m.themesDir -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/registry.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/registry.go deleted file mode 100644 index 5a39913..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/registry.go +++ /dev/null @@ -1,309 +0,0 @@ -package themes - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - - "github.com/go-git/go-git/v6" - "github.com/spf13/afero" -) - -const registryRepo = "https://github.com/AvengeMedia/dms-plugin-registry.git" - -type ColorScheme struct { - Primary string `json:"primary,omitempty"` - PrimaryText string `json:"primaryText,omitempty"` - PrimaryContainer string `json:"primaryContainer,omitempty"` - Secondary string `json:"secondary,omitempty"` - Surface string `json:"surface,omitempty"` - SurfaceText string `json:"surfaceText,omitempty"` - SurfaceVariant string `json:"surfaceVariant,omitempty"` - SurfaceVariantText string `json:"surfaceVariantText,omitempty"` - SurfaceTint string `json:"surfaceTint,omitempty"` - Background string `json:"background,omitempty"` - BackgroundText string `json:"backgroundText,omitempty"` - Outline string `json:"outline,omitempty"` - SurfaceContainer string `json:"surfaceContainer,omitempty"` - SurfaceContainerHigh string `json:"surfaceContainerHigh,omitempty"` - SurfaceContainerHighest string `json:"surfaceContainerHighest,omitempty"` - Error string `json:"error,omitempty"` - Warning string `json:"warning,omitempty"` - Info string `json:"info,omitempty"` -} - -type ThemeVariant struct { - ID string `json:"id"` - Name string `json:"name"` - Dark ColorScheme `json:"dark,omitempty"` - Light ColorScheme `json:"light,omitempty"` -} - -type ThemeFlavor struct { - ID string `json:"id"` - Name string `json:"name"` - Dark ColorScheme `json:"dark,omitempty"` - Light ColorScheme `json:"light,omitempty"` -} - -type ThemeAccent struct { - ID string `json:"id"` - Name string `json:"name"` - FlavorColors map[string]ColorScheme `json:"-"` -} - -func (a *ThemeAccent) UnmarshalJSON(data []byte) error { - var raw map[string]json.RawMessage - if err := json.Unmarshal(data, &raw); err != nil { - return err - } - a.FlavorColors = make(map[string]ColorScheme) - var mErr error - for key, value := range raw { - switch key { - case "id": - mErr = errors.Join(mErr, json.Unmarshal(value, &a.ID)) - case "name": - mErr = errors.Join(mErr, json.Unmarshal(value, &a.Name)) - default: - var colors ColorScheme - if err := json.Unmarshal(value, &colors); err == nil { - a.FlavorColors[key] = colors - } else { - mErr = errors.Join(mErr, fmt.Errorf("failed to unmarshal flavor colors for key %s: %w", key, err)) - } - } - } - return mErr -} - -func (a ThemeAccent) MarshalJSON() ([]byte, error) { - m := map[string]any{ - "id": a.ID, - "name": a.Name, - } - for k, v := range a.FlavorColors { - m[k] = v - } - return json.Marshal(m) -} - -type MultiVariantDefaults struct { - Dark map[string]string `json:"dark,omitempty"` - Light map[string]string `json:"light,omitempty"` -} - -type ThemeVariants struct { - Type string `json:"type,omitempty"` - Default string `json:"default,omitempty"` - Defaults *MultiVariantDefaults `json:"defaults,omitempty"` - Options []ThemeVariant `json:"options,omitempty"` - Flavors []ThemeFlavor `json:"flavors,omitempty"` - Accents []ThemeAccent `json:"accents,omitempty"` -} - -type Theme struct { - ID string `json:"id"` - Name string `json:"name"` - Version string `json:"version"` - Author string `json:"author"` - Description string `json:"description"` - Dark ColorScheme `json:"dark"` - Light ColorScheme `json:"light"` - Variants *ThemeVariants `json:"variants,omitempty"` - PreviewPath string `json:"-"` - SourceDir string `json:"sourceDir,omitempty"` -} - -type GitClient interface { - PlainClone(path string, url string) error - Pull(path string) error -} - -type realGitClient struct{} - -func (g *realGitClient) PlainClone(path string, url string) error { - _, err := git.PlainClone(path, &git.CloneOptions{ - URL: url, - Progress: os.Stdout, - }) - return err -} - -func (g *realGitClient) Pull(path string) error { - repo, err := git.PlainOpen(path) - if err != nil { - return err - } - - worktree, err := repo.Worktree() - if err != nil { - return err - } - - err = worktree.Pull(&git.PullOptions{}) - if err != nil && err.Error() != "already up-to-date" { - return err - } - - return nil -} - -type Registry struct { - fs afero.Fs - cacheDir string - themes []Theme - git GitClient -} - -func NewRegistry() (*Registry, error) { - return NewRegistryWithFs(afero.NewOsFs()) -} - -func NewRegistryWithFs(fs afero.Fs) (*Registry, error) { - cacheDir := getCacheDir() - return &Registry{ - fs: fs, - cacheDir: cacheDir, - git: &realGitClient{}, - }, nil -} - -func getCacheDir() string { - return filepath.Join(os.TempDir(), "dankdots-plugin-registry") -} - -func (r *Registry) Update() error { - exists, err := afero.DirExists(r.fs, r.cacheDir) - if err != nil { - return fmt.Errorf("failed to check cache directory: %w", err) - } - - if !exists { - if err := r.fs.MkdirAll(filepath.Dir(r.cacheDir), 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - if err := r.git.PlainClone(r.cacheDir, registryRepo); err != nil { - return fmt.Errorf("failed to clone registry: %w", err) - } - } else { - if err := r.git.Pull(r.cacheDir); err != nil { - if err := r.fs.RemoveAll(r.cacheDir); err != nil { - return fmt.Errorf("failed to remove corrupted registry: %w", err) - } - - if err := r.fs.MkdirAll(filepath.Dir(r.cacheDir), 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - if err := r.git.PlainClone(r.cacheDir, registryRepo); err != nil { - return fmt.Errorf("failed to re-clone registry: %w", err) - } - } - } - - return r.loadThemes() -} - -func (r *Registry) loadThemes() error { - themesDir := filepath.Join(r.cacheDir, "themes") - - entries, err := afero.ReadDir(r.fs, themesDir) - if err != nil { - return fmt.Errorf("failed to read themes directory: %w", err) - } - - r.themes = []Theme{} - - for _, entry := range entries { - if !entry.IsDir() { - continue - } - - themeDir := filepath.Join(themesDir, entry.Name()) - themeFile := filepath.Join(themeDir, "theme.json") - - data, err := afero.ReadFile(r.fs, themeFile) - if err != nil { - continue - } - - var theme Theme - if err := json.Unmarshal(data, &theme); err != nil { - continue - } - - if theme.ID == "" { - theme.ID = entry.Name() - } - theme.SourceDir = entry.Name() - - previewPath := filepath.Join(themeDir, "preview.svg") - if exists, _ := afero.Exists(r.fs, previewPath); exists { - theme.PreviewPath = previewPath - } - - r.themes = append(r.themes, theme) - } - - return nil -} - -func (r *Registry) List() ([]Theme, error) { - if len(r.themes) == 0 { - if err := r.Update(); err != nil { - return nil, err - } - } - - return SortByFirstParty(r.themes), nil -} - -func (r *Registry) Search(query string) ([]Theme, error) { - allThemes, err := r.List() - if err != nil { - return nil, err - } - - if query == "" { - return allThemes, nil - } - - return SortByFirstParty(FuzzySearch(query, allThemes)), nil -} - -func (r *Registry) Get(idOrName string) (*Theme, error) { - themes, err := r.List() - if err != nil { - return nil, err - } - - for _, t := range themes { - if t.ID == idOrName { - return &t, nil - } - } - - for _, t := range themes { - if t.Name == idOrName { - return &t, nil - } - } - - return nil, fmt.Errorf("theme not found: %s", idOrName) -} - -func (r *Registry) GetThemeSourcePath(themeID string) string { - return filepath.Join(r.cacheDir, "themes", themeID, "theme.json") -} - -func (r *Registry) GetThemeDir(themeID string) string { - return filepath.Join(r.cacheDir, "themes", themeID) -} - -func SortByFirstParty(themes []Theme) []Theme { - return themes -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/search.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/search.go deleted file mode 100644 index 347f791..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/themes/search.go +++ /dev/null @@ -1,40 +0,0 @@ -package themes - -import ( - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -func FuzzySearch(query string, themes []Theme) []Theme { - if query == "" { - return themes - } - - queryLower := strings.ToLower(query) - return utils.Filter(themes, func(t Theme) bool { - return fuzzyMatch(queryLower, strings.ToLower(t.Name)) || - fuzzyMatch(queryLower, strings.ToLower(t.Description)) || - fuzzyMatch(queryLower, strings.ToLower(t.Author)) - }) -} - -func fuzzyMatch(query, text string) bool { - queryIdx := 0 - for _, char := range text { - if queryIdx < len(query) && char == rune(query[queryIdx]) { - queryIdx++ - } - } - return queryIdx == len(query) -} - -func FindByIDOrName(idOrName string, themes []Theme) *Theme { - if t, found := utils.Find(themes, func(t Theme) bool { return t.ID == idOrName }); found { - return &t - } - if t, found := utils.Find(themes, func(t Theme) bool { return t.Name == idOrName }); found { - return &t - } - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/app.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/app.go deleted file mode 100644 index d07430a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/app.go +++ /dev/null @@ -1,240 +0,0 @@ -package tui - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/charmbracelet/bubbles/spinner" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" -) - -type Model struct { - version string - state ApplicationState - - osInfo *distros.OSInfo - dependencies []deps.Dependency - err error - - spinner spinner.Model - passwordInput textinput.Model - width int - height int - isLoading bool - styles Styles - - logMessages []string - logChan chan string - logFilePath string - packageProgressChan chan packageInstallProgressMsg - packageProgress packageInstallProgressMsg - installationLogs []string - showDebugLogs bool - - selectedWM int - selectedTerminal int - selectedDep int - selectedConfig int - reinstallItems map[string]bool - disabledItems map[string]bool - replaceConfigs map[string]bool - skipGentooUseFlags bool - sudoPassword string - existingConfigs []ExistingConfigInfo - fingerprintFailed bool -} - -func NewModel(version string, logFilePath string) Model { - s := spinner.New() - s.Spinner = spinner.Dot - - theme := TerminalTheme() - styles := NewStyles(theme) - s.Style = styles.SpinnerStyle - - pi := textinput.New() - pi.Placeholder = "Enter sudo password" - pi.EchoMode = textinput.EchoPassword - pi.EchoCharacter = '•' - pi.Focus() - - logChan := make(chan string, 1000) - packageProgressChan := make(chan packageInstallProgressMsg, 100) - - return Model{ - version: version, - state: StateWelcome, - spinner: s, - passwordInput: pi, - isLoading: true, - styles: styles, - - logMessages: []string{}, - logChan: logChan, - logFilePath: logFilePath, - packageProgressChan: packageProgressChan, - packageProgress: packageInstallProgressMsg{ - progress: 0.0, - step: "Initializing package installation", - isComplete: false, - }, - showDebugLogs: false, - selectedWM: 0, - selectedTerminal: 0, // Default to Ghostty - selectedDep: 0, - selectedConfig: 0, - reinstallItems: make(map[string]bool), - disabledItems: make(map[string]bool), - replaceConfigs: make(map[string]bool), - installationLogs: []string{}, - } -} - -func (m Model) GetLogChan() <-chan string { - return m.logChan -} - -func (m Model) Init() tea.Cmd { - return tea.Batch( - m.spinner.Tick, - m.listenForLogs(), - m.detectOS(), - ) -} - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "ctrl+c": - return m, tea.Quit - case "ctrl+d": - // Toggle debug logs view (except during password input states) - if m.state != StatePasswordPrompt && m.state != StateFingerprintAuth { - m.showDebugLogs = !m.showDebugLogs - return m, nil - } - } - } - - if tickMsg, ok := msg.(spinner.TickMsg); ok { - var cmd tea.Cmd - m.spinner, cmd = m.spinner.Update(tickMsg) - return m, tea.Batch(cmd, m.listenForLogs()) - } - - if sizeMsg, ok := msg.(tea.WindowSizeMsg); ok { - m.width = sizeMsg.Width - m.height = sizeMsg.Height - } - - if logMsg, ok := msg.(logMsg); ok { - m.logMessages = append(m.logMessages, logMsg.message) - return m, m.listenForLogs() - } - - switch m.state { - case StateWelcome: - return m.updateWelcomeState(msg) - case StateSelectWindowManager: - return m.updateSelectWindowManagerState(msg) - case StateSelectTerminal: - return m.updateSelectTerminalState(msg) - case StateDetectingDeps: - return m.updateDetectingDepsState(msg) - case StateDependencyReview: - return m.updateDependencyReviewState(msg) - case StateGentooUseFlags: - return m.updateGentooUseFlagsState(msg) - case StateGentooGCCCheck: - return m.updateGentooGCCCheckState(msg) - case StateAuthMethodChoice: - return m.updateAuthMethodChoiceState(msg) - case StateFingerprintAuth: - return m.updateFingerprintAuthState(msg) - case StatePasswordPrompt: - return m.updatePasswordPromptState(msg) - case StateInstallingPackages: - return m.updateInstallingPackagesState(msg) - case StateConfigConfirmation: - return m.updateConfigConfirmationState(msg) - case StateDeployingConfigs: - return m.updateDeployingConfigsState(msg) - case StateInstallComplete: - return m.updateInstallCompleteState(msg) - case StateError: - return m.updateErrorState(msg) - default: - return m, m.listenForLogs() - } -} - -func (m Model) View() string { - // If debug logs are shown, show that view regardless of state - if m.showDebugLogs { - return m.viewDebugLogs() - } - - switch m.state { - case StateWelcome: - return m.viewWelcome() - case StateSelectWindowManager: - return m.viewSelectWindowManager() - case StateSelectTerminal: - return m.viewSelectTerminal() - case StateDetectingDeps: - return m.viewDetectingDeps() - case StateDependencyReview: - return m.viewDependencyReview() - case StateGentooUseFlags: - return m.viewGentooUseFlags() - case StateGentooGCCCheck: - return m.viewGentooGCCCheck() - case StateAuthMethodChoice: - return m.viewAuthMethodChoice() - case StateFingerprintAuth: - return m.viewFingerprintAuth() - case StatePasswordPrompt: - return m.viewPasswordPrompt() - case StateInstallingPackages: - return m.viewInstallingPackages() - case StateConfigConfirmation: - return m.viewConfigConfirmation() - case StateDeployingConfigs: - return m.viewDeployingConfigs() - case StateInstallComplete: - return m.viewInstallComplete() - case StateError: - return m.viewError() - default: - return m.viewWelcome() - } -} - -func (m Model) listenForLogs() tea.Cmd { - return func() tea.Msg { - select { - case msg, ok := <-m.logChan: - if !ok { - return nil - } - return logMsg{message: msg} - default: - return nil - } - } -} - -func (m Model) detectOS() tea.Cmd { - return func() tea.Msg { - info, err := distros.GetOSInfo() - osInfoMsg := &distros.OSInfo{} - if info != nil { - osInfoMsg.Distribution = info.Distribution - osInfoMsg.Version = info.Version - osInfoMsg.VersionID = info.VersionID - osInfoMsg.PrettyName = info.PrettyName - osInfoMsg.Architecture = info.Architecture - } - return osInfoCompleteMsg{info: osInfoMsg, err: err} - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/banner.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/banner.go deleted file mode 100644 index e3a2ca3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/banner.go +++ /dev/null @@ -1,21 +0,0 @@ -package tui - -import "github.com/charmbracelet/lipgloss" - -func (m Model) renderBanner() string { - logo := ` -██████╗ █████╗ ███╗ ██╗██╗ ██╗ -██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝ -██║ ██║███████║██╔██╗ ██║█████╔╝ -██║ ██║██╔══██║██║╚██╗██║██╔═██╗ -██████╔╝██║ ██║██║ ╚████║██║ ██╗ -╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ` - - theme := TerminalTheme() - style := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - MarginBottom(1) - - return style.Render(logo) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/messages.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/messages.go deleted file mode 100644 index 7c03098..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/messages.go +++ /dev/null @@ -1,39 +0,0 @@ -package tui - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" -) - -type logMsg struct { - message string -} - -type osInfoCompleteMsg struct { - info *distros.OSInfo - err error -} - -type depsDetectedMsg struct { - deps []deps.Dependency - err error -} - -type packageInstallProgressMsg struct { - progress float64 - step string - isComplete bool - needsSudo bool - commandInfo string - logOutput string - error error -} - -type packageProgressCompletedMsg struct{} - -type passwordValidMsg struct { - password string - valid bool -} - -type delayCompleteMsg struct{} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/states.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/states.go deleted file mode 100644 index 6e9e194..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/states.go +++ /dev/null @@ -1,22 +0,0 @@ -package tui - -type ApplicationState int - -const ( - StateWelcome ApplicationState = iota - StateSelectWindowManager - StateSelectTerminal - StateDetectingDeps - StateDependencyReview - StateGentooUseFlags - StateGentooGCCCheck - StateAuthMethodChoice - StateFingerprintAuth - StatePasswordPrompt - StateInstallingPackages - StateConfigConfirmation - StateDeployingConfigs - StateInstallComplete - StateFinalComplete - StateError -) diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/styles.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/styles.go deleted file mode 100644 index 45a9dae..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/styles.go +++ /dev/null @@ -1,124 +0,0 @@ -package tui - -import ( - "github.com/charmbracelet/bubbles/progress" - "github.com/charmbracelet/lipgloss" -) - -type AppTheme struct { - Primary string - Secondary string - Accent string - Text string - Subtle string - Error string - Warning string - Success string - Background string - Surface string -} - -func TerminalTheme() AppTheme { - return AppTheme{ - Primary: "6", // #625690 - purple - Secondary: "5", // #36247a - dark purple - Accent: "12", // #7060ac - light purple - Text: "7", // #2e2e2e - dark gray - Subtle: "8", // #4a4a4a - medium gray - Error: "1", // #d83636 - red - Warning: "3", // #ffff89 - yellow - Success: "2", // #53e550 - green - Background: "15", // #1a1a1a - near black - Surface: "8", // #4a4a4a - medium gray - } -} - -func NewStyles(theme AppTheme) Styles { - return Styles{ - Title: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - MarginLeft(1). - MarginBottom(1), - - Normal: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)), - - Bold: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Bold(true), - - Subtle: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)), - - Error: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Error)), - - Warning: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Warning)), - - StatusBar: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#33275e")). - Background(lipgloss.Color(theme.Primary)). - Padding(0, 1), - - Key: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true), - - SpinnerStyle: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)), - - Success: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Success)). - Bold(true), - - HighlightButton: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#33275e")). - Background(lipgloss.Color(theme.Primary)). - Padding(0, 2). - Bold(true), - - SelectedOption: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true), - - CodeBlock: lipgloss.NewStyle(). - Background(lipgloss.Color(theme.Surface)). - Foreground(lipgloss.Color(theme.Text)). - Padding(1, 2). - MarginLeft(2), - } -} - -type Styles struct { - Title lipgloss.Style - Normal lipgloss.Style - Bold lipgloss.Style - Subtle lipgloss.Style - Warning lipgloss.Style - Error lipgloss.Style - StatusBar lipgloss.Style - Key lipgloss.Style - SpinnerStyle lipgloss.Style - Success lipgloss.Style - HighlightButton lipgloss.Style - SelectedOption lipgloss.Style - CodeBlock lipgloss.Style -} - -func (s Styles) NewThemedProgress(width int) progress.Model { - theme := TerminalTheme() - prog := progress.New( - progress.WithGradient(theme.Secondary, theme.Primary), - ) - - prog.Width = width - prog.ShowPercentage = true - prog.PercentFormat = "%.0f%%" - prog.PercentageStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Bold(true) - - return prog -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_config.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_config.go deleted file mode 100644 index 14daff3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_config.go +++ /dev/null @@ -1,382 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/config" - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - tea "github.com/charmbracelet/bubbletea" -) - -type configDeploymentResult struct { - results []config.DeploymentResult - error error -} - -type ExistingConfigInfo struct { - ConfigType string - Path string - Exists bool -} - -type configCheckResult struct { - configs []ExistingConfigInfo - error error -} - -func (m Model) viewDeployingConfigs() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Deploying Configurations") - b.WriteString(title) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Setting up configuration files...") - fmt.Fprintf(&b, "%s %s", spinner, status) - b.WriteString("\n\n") - - // Show progress information - info := m.styles.Subtle.Render("• Creating backups of existing configurations\n• Deploying optimized configurations\n• Detecting system paths") - b.WriteString(info) - - // Show live log output if available - if len(m.installationLogs) > 0 { - b.WriteString("\n\n") - logHeader := m.styles.Subtle.Render("Configuration Log:") - b.WriteString(logHeader) - b.WriteString("\n") - - // Show last few lines of logs - maxLines := 5 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - } - - return b.String() -} - -func (m Model) updateDeployingConfigsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if result, ok := msg.(configDeploymentResult); ok { - if result.error != nil { - m.err = result.error - m.state = StateError - m.isLoading = false - return m, nil - } - - for _, deployResult := range result.results { - if deployResult.Deployed { - logMsg := fmt.Sprintf("✓ %s configuration deployed", deployResult.ConfigType) - if deployResult.BackupPath != "" { - logMsg += fmt.Sprintf(" (backup: %s)", deployResult.BackupPath) - } - m.installationLogs = append(m.installationLogs, logMsg) - } - } - - m.state = StateInstallComplete - m.isLoading = false - return m, nil - } - - return m, m.listenForLogs() -} - -func (m Model) deployConfigurations() tea.Cmd { - return func() tea.Msg { - // Determine the selected window manager - var wm deps.WindowManager - switch m.selectedWM { - case 0: - wm = deps.WindowManagerNiri - case 1: - wm = deps.WindowManagerHyprland - default: - wm = deps.WindowManagerNiri - } - - // Determine the selected terminal - var terminal deps.Terminal - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalKitty - case 1: - terminal = deps.TerminalAlacritty - default: - terminal = deps.TerminalKitty - } - } else { - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalGhostty - case 1: - terminal = deps.TerminalKitty - default: - terminal = deps.TerminalAlacritty - } - } - - deployer := config.NewConfigDeployer(m.logChan) - - results, err := deployer.DeployConfigurationsSelectiveWithReinstalls(context.Background(), wm, terminal, m.dependencies, m.replaceConfigs, m.reinstallItems) - - return configDeploymentResult{ - results: results, - error: err, - } - } -} - -func (m Model) viewConfigConfirmation() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Configuration Deployment") - b.WriteString(title) - b.WriteString("\n\n") - - if len(m.existingConfigs) == 0 { - // No existing configs, proceed directly - info := m.styles.Normal.Render("No existing configurations found. Proceeding with deployment...") - b.WriteString(info) - return b.String() - } - - // Show existing configurations with toggle options - for i, configInfo := range m.existingConfigs { - if configInfo.Exists { - var status string - var replaceMarker string - - shouldReplace := m.replaceConfigs[configInfo.ConfigType] - if _, exists := m.replaceConfigs[configInfo.ConfigType]; !exists { - shouldReplace = true - m.replaceConfigs[configInfo.ConfigType] = true - } - - if shouldReplace { - replaceMarker = "🔄 " - status = m.styles.Warning.Render("Will replace") - } else { - replaceMarker = "✓ " - status = m.styles.Success.Render("Keep existing") - } - - var line string - if i == m.selectedConfig { - line = fmt.Sprintf("▶ %s%-15s %s", replaceMarker, configInfo.ConfigType, status) - line += fmt.Sprintf("\n %s", configInfo.Path) - line = m.styles.SelectedOption.Render(line) - } else { - line = fmt.Sprintf(" %s%-15s %s", replaceMarker, configInfo.ConfigType, status) - line += fmt.Sprintf("\n %s", configInfo.Path) - line = m.styles.Normal.Render(line) - } - - b.WriteString(line) - b.WriteString("\n\n") - } - } - - backup := m.styles.Success.Render("✓ Replaced configurations will be backed up with timestamp") - b.WriteString(backup) - b.WriteString("\n\n") - - help := m.styles.Subtle.Render("↑/↓: Navigate, Space: Toggle replace/keep, Enter: Continue") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateConfigConfirmationState(msg tea.Msg) (tea.Model, tea.Cmd) { - if result, ok := msg.(configCheckResult); ok { - if result.error != nil { - m.err = result.error - m.state = StateError - return m, nil - } - - m.existingConfigs = result.configs - - firstExistingSet := false - for i, config := range result.configs { - if config.Exists { - m.replaceConfigs[config.ConfigType] = true - if !firstExistingSet { - m.selectedConfig = i - firstExistingSet = true - } - } - } - - hasExisting := false - for _, config := range result.configs { - if config.Exists { - hasExisting = true - break - } - } - - if !hasExisting { - // No existing configs, proceed directly to deployment - m.state = StateDeployingConfigs - return m, m.deployConfigurations() - } - - // Show confirmation view - return m, nil - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedConfig > 0 { - for i := m.selectedConfig - 1; i >= 0; i-- { - if m.existingConfigs[i].Exists { - m.selectedConfig = i - break - } - } - } - case "down": - if m.selectedConfig < len(m.existingConfigs)-1 { - for i := m.selectedConfig + 1; i < len(m.existingConfigs); i++ { - if m.existingConfigs[i].Exists { - m.selectedConfig = i - break - } - } - } - case " ": - if len(m.existingConfigs) > 0 && m.selectedConfig < len(m.existingConfigs) { - configType := m.existingConfigs[m.selectedConfig].ConfigType - if m.existingConfigs[m.selectedConfig].Exists { - m.replaceConfigs[configType] = !m.replaceConfigs[configType] - } - } - case "enter": - m.state = StateDeployingConfigs - return m, m.deployConfigurations() - } - } - - return m, nil -} - -func (m Model) checkExistingConfigurations() tea.Cmd { - return func() tea.Msg { - var configs []ExistingConfigInfo - - if m.selectedWM == 0 { - niriPath := filepath.Join(os.Getenv("HOME"), ".config", "niri", "config.kdl") - niriExists := false - if _, err := os.Stat(niriPath); err == nil { - niriExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Niri", - Path: niriPath, - Exists: niriExists, - }) - } else { - hyprlandPath := filepath.Join(os.Getenv("HOME"), ".config", "hypr", "hyprland.conf") - hyprlandExists := false - if _, err := os.Stat(hyprlandPath); err == nil { - hyprlandExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Hyprland", - Path: hyprlandPath, - Exists: hyprlandExists, - }) - } - - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - if m.selectedTerminal == 0 { - kittyPath := filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf") - kittyExists := false - if _, err := os.Stat(kittyPath); err == nil { - kittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Kitty", - Path: kittyPath, - Exists: kittyExists, - }) - } else { - alacrittyPath := filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml") - alacrittyExists := false - if _, err := os.Stat(alacrittyPath); err == nil { - alacrittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Alacritty", - Path: alacrittyPath, - Exists: alacrittyExists, - }) - } - } else { - switch m.selectedTerminal { - case 0: - ghosttyPath := filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config") - ghosttyExists := false - if _, err := os.Stat(ghosttyPath); err == nil { - ghosttyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Ghostty", - Path: ghosttyPath, - Exists: ghosttyExists, - }) - case 1: - kittyPath := filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf") - kittyExists := false - if _, err := os.Stat(kittyPath); err == nil { - kittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Kitty", - Path: kittyPath, - Exists: kittyExists, - }) - default: - alacrittyPath := filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml") - alacrittyExists := false - if _, err := os.Stat(alacrittyPath); err == nil { - alacrittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Alacritty", - Path: alacrittyPath, - Exists: alacrittyExists, - }) - } - } - - return configCheckResult{ - configs: configs, - error: nil, - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_dependencies.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_dependencies.go deleted file mode 100644 index 13f0335..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_dependencies.go +++ /dev/null @@ -1,304 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/AvengeMedia/DankMaterialShell/core/internal/greeter" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewDetectingDeps() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Detecting Dependencies") - b.WriteString(title) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Scanning system for existing packages and configurations...") - fmt.Fprintf(&b, "%s %s", spinner, status) - - return b.String() -} - -func (m Model) viewDependencyReview() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Dependency Review") - b.WriteString(title) - b.WriteString("\n\n") - - if len(m.dependencies) > 0 { - for i, dep := range m.dependencies { - var status string - var reinstallMarker string - var variantMarker string - - isDMS := dep.Name == "dms (DankMaterialShell)" - - if dep.CanToggle && dep.Variant == deps.VariantGit { - variantMarker = "[git] " - } - - if m.disabledItems[dep.Name] { - reinstallMarker = "✗ " - status = m.styles.Subtle.Render("Will skip") - } else if m.reinstallItems[dep.Name] { - reinstallMarker = "🔄 " - status = m.styles.Warning.Render("Will upgrade") - } else if isDMS { - reinstallMarker = "⚡ " - switch dep.Status { - case deps.StatusInstalled: - status = m.styles.Success.Render("✓ Required (installed)") - case deps.StatusMissing: - status = m.styles.Warning.Render("○ Required (will install)") - case deps.StatusNeedsUpdate: - status = m.styles.Warning.Render("△ Required (needs update)") - case deps.StatusNeedsReinstall: - status = m.styles.Error.Render("! Required (needs reinstall)") - } - } else { - switch dep.Status { - case deps.StatusInstalled: - status = m.styles.Subtle.Render("✓ Already installed") - case deps.StatusMissing: - status = m.styles.Warning.Render("○ Will install") - case deps.StatusNeedsUpdate: - status = m.styles.Warning.Render("△ Will install") - case deps.StatusNeedsReinstall: - status = m.styles.Error.Render("! Will install") - } - } - - note := "" - if dep.Name == "dms-greeter" { - note = m.styles.Subtle.Render(" (selection replaces your current display manager)") - } - - var line string - if i == m.selectedDep { - line = fmt.Sprintf("▶ %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status) - if dep.Version != "" { - line += fmt.Sprintf(" (%s)", dep.Version) - } - line = m.styles.SelectedOption.Render(line) + note - } else { - line = fmt.Sprintf(" %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status) - if dep.Version != "" { - line += fmt.Sprintf(" (%s)", dep.Version) - } - line = m.styles.Normal.Render(line) + note - } - - b.WriteString(line) - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("↑/↓: Navigate, Space: Toggle, G: Toggle stable/git, Enter: Continue") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateDetectingDepsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if depsMsg, ok := msg.(depsDetectedMsg); ok { - m.isLoading = false - if depsMsg.err != nil { - m.err = depsMsg.err - m.state = StateError - } else { - m.dependencies = depsMsg.deps - // dms-greeter is opt-in skipped by default - for _, dep := range depsMsg.deps { - if dep.Name == "dms-greeter" { - m.disabledItems["dms-greeter"] = true - break - } - } - m.state = StateDependencyReview - } - return m, m.listenForLogs() - } - return m, m.listenForLogs() -} - -func (m Model) updateDependencyReviewState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedDep > 0 { - m.selectedDep-- - } - case "down": - if m.selectedDep < len(m.dependencies)-1 { - m.selectedDep++ - } - case " ": - if len(m.dependencies) > 0 { - depName := m.dependencies[m.selectedDep].Name - isDMS := depName == "dms (DankMaterialShell)" - - if !isDMS { - isInstalled := m.dependencies[m.selectedDep].Status == deps.StatusInstalled || - m.dependencies[m.selectedDep].Status == deps.StatusNeedsReinstall - - if isInstalled { - m.reinstallItems[depName] = !m.reinstallItems[depName] - m.disabledItems[depName] = false - } else { - m.disabledItems[depName] = !m.disabledItems[depName] - m.reinstallItems[depName] = false - } - } - } - case "g", "G": - if len(m.dependencies) > 0 && m.dependencies[m.selectedDep].CanToggle { - if m.dependencies[m.selectedDep].Variant == deps.VariantStable { - m.dependencies[m.selectedDep].Variant = deps.VariantGit - } else { - m.dependencies[m.selectedDep].Variant = deps.VariantStable - } - } - case "enter": - // Check if on Gentoo - show USE flags screen - if m.osInfo != nil { - if config, exists := distros.Registry[m.osInfo.Distribution.ID]; exists && config.Family == distros.FamilyGentoo { - m.state = StateGentooUseFlags - return m, nil - } - } - // Check if fingerprint is enabled - if checkFingerprintEnabled() { - m.state = StateAuthMethodChoice - m.selectedConfig = 0 // Default to fingerprint - return m, nil - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - return m, nil - } - case "esc": - m.state = StateSelectWindowManager - return m, nil - } - } - return m, m.listenForLogs() -} - -func (m Model) installPackages() tea.Cmd { - return func() tea.Msg { - if m.osInfo == nil { - return packageInstallProgressMsg{ - progress: 0.0, - step: "Error: OS info not available", - isComplete: true, - } - } - - installer, err := distros.NewPackageInstaller(m.osInfo.Distribution.ID, m.logChan) - if err != nil { - return packageInstallProgressMsg{ - progress: 0.0, - step: fmt.Sprintf("Error: %s", err.Error()), - isComplete: true, - } - } - - // Convert TUI selection to deps enum - var wm deps.WindowManager - if m.selectedWM == 0 { - wm = deps.WindowManagerNiri - } else { - wm = deps.WindowManagerHyprland - } - - installerProgressChan := make(chan distros.InstallProgressMsg, 100) - - go func() { - defer close(installerProgressChan) - err := installer.InstallPackages(context.Background(), m.dependencies, wm, m.sudoPassword, m.reinstallItems, m.disabledItems, m.skipGentooUseFlags, installerProgressChan) - if err != nil { - installerProgressChan <- distros.InstallProgressMsg{ - Progress: 0.0, - Step: fmt.Sprintf("Installation error: %s", err.Error()), - IsComplete: true, - Error: err, - } - } - }() - - // Convert installer messages to TUI messages - go func() { - for msg := range installerProgressChan { - // Run optional greeter setup - if msg.Phase == distros.PhaseComplete && msg.IsComplete && msg.Error == nil { - greeterSelected := false - for _, dep := range m.dependencies { - if dep.Name == "dms-greeter" && !m.disabledItems["dms-greeter"] { - greeterSelected = true - break - } - } - if greeterSelected { - compositorName := "niri" - if m.selectedWM == 1 { - compositorName = "Hyprland" - } - m.packageProgressChan <- packageInstallProgressMsg{ - progress: 0.92, - step: "Configuring DMS greeter...", - logOutput: "Starting automated greeter setup...", - } - greeterLogFunc := func(line string) { - m.packageProgressChan <- packageInstallProgressMsg{ - progress: 0.94, - step: "Configuring DMS greeter...", - logOutput: line, - } - } - if err := greeter.AutoSetupGreeter(compositorName, m.sudoPassword, greeterLogFunc); err != nil { - m.packageProgressChan <- packageInstallProgressMsg{ - progress: 0.96, - step: "Greeter setup warning", - logOutput: fmt.Sprintf("⚠ Greeter auto-setup warning (non-fatal): %v", err), - } - } - } - } - tuiMsg := packageInstallProgressMsg{ - progress: msg.Progress, - step: msg.Step, - isComplete: msg.IsComplete, - needsSudo: msg.NeedsSudo, - commandInfo: msg.CommandInfo, - logOutput: msg.LogOutput, - error: msg.Error, - } - if msg.IsComplete { - m.logChan <- fmt.Sprintf("[DEBUG] Sending completion signal: step=%s, progress=%.2f", msg.Step, msg.Progress) - } - m.packageProgressChan <- tuiMsg - } - m.logChan <- "[DEBUG] Installer channel closed" - }() - - return packageInstallProgressMsg{ - progress: 0.05, - step: "Starting installation...", - isComplete: false, - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_gcc.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_gcc.go deleted file mode 100644 index 74195a4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_gcc.go +++ /dev/null @@ -1,103 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os/exec" - "strconv" - "strings" - - tea "github.com/charmbracelet/bubbletea" -) - -type gccVersionCheckMsg struct { - version string - major int - err error -} - -func (m Model) viewGentooGCCCheck() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("GCC Version Check Failed") - b.WriteString(title) - b.WriteString("\n\n") - - error := m.styles.Error.Render("⚠ Hyprland requires GCC 15 or newer") - b.WriteString(error) - b.WriteString("\n\n") - - info := m.styles.Normal.Render("Your current GCC version is too old. Please upgrade GCC before continuing.") - b.WriteString(info) - b.WriteString("\n\n") - - instructionsTitle := m.styles.Subtle.Render("To upgrade GCC:") - b.WriteString(instructionsTitle) - b.WriteString("\n\n") - - steps := []string{ - "1. Install GCC 15 (if not already installed):", - " sudo emerge -1av =sys-devel/gcc:15", - "", - "2. Switch to GCC 15 using gcc-config:", - " sudo gcc-config $(gcc-config -l | grep gcc-15 | awk '{print $2}' | head -1)", - "", - "3. Update environment:", - " source /etc/profile", - "", - "4. Restart this installer", - } - - for _, step := range steps { - stepLine := m.styles.Subtle.Render(step) - b.WriteString(stepLine) - b.WriteString("\n") - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Press Esc to go back, Ctrl+C to exit") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateGentooGCCCheckState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "esc": - m.state = StateSelectWindowManager - return m, nil - } - } - return m, m.listenForLogs() -} - -func (m Model) checkGCCVersion() tea.Cmd { - return func() tea.Msg { - cmd := exec.CommandContext(context.Background(), "gcc", "-dumpversion") - output, err := cmd.Output() - if err != nil { - return gccVersionCheckMsg{err: err} - } - - version := strings.TrimSpace(string(output)) - parts := strings.Split(version, ".") - if len(parts) == 0 { - return gccVersionCheckMsg{err: fmt.Errorf("invalid gcc version format")} - } - - major, err := strconv.Atoi(parts[0]) - if err != nil { - return gccVersionCheckMsg{err: err} - } - - return gccVersionCheckMsg{ - version: version, - major: major, - err: nil, - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_use_flags.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_use_flags.go deleted file mode 100644 index eb259ef..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_use_flags.go +++ /dev/null @@ -1,92 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewGentooUseFlags() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Gentoo Global USE Flags") - b.WriteString(title) - b.WriteString("\n\n") - - info := m.styles.Normal.Render("The following global USE flags will be enabled in /etc/portage/make.conf:") - b.WriteString(info) - b.WriteString("\n\n") - - for _, flag := range distros.GentooGlobalUseFlags { - flagLine := m.styles.Success.Render(fmt.Sprintf(" • %s", flag)) - b.WriteString(flagLine) - b.WriteString("\n") - } - - b.WriteString("\n") - note := m.styles.Subtle.Render("These flags ensure proper Qt6, Wayland, and compositor support.") - b.WriteString(note) - b.WriteString("\n\n") - - var toggleLine string - if m.skipGentooUseFlags { - toggleLine = "▶ [✗] Skip adding global USE flags (will use existing configuration)" - toggleLine = m.styles.Warning.Render(toggleLine) - } else { - toggleLine = " [ ] Skip adding global USE flags (will use existing configuration)" - toggleLine = m.styles.Subtle.Render(toggleLine) - } - b.WriteString(toggleLine) - b.WriteString("\n\n") - - help := m.styles.Subtle.Render("Space: Toggle skip, Enter: Continue, Esc: Go back") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateGentooUseFlagsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if gccMsg, ok := msg.(gccVersionCheckMsg); ok { - if gccMsg.err != nil || gccMsg.major < 15 { - m.state = StateGentooGCCCheck - return m, nil - } - if checkFingerprintEnabled() { - m.state = StateAuthMethodChoice - m.selectedConfig = 0 - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - } - return m, nil - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case " ": - m.skipGentooUseFlags = !m.skipGentooUseFlags - return m, nil - case "enter": - if m.selectedWM == 1 { - return m, m.checkGCCVersion() - } - if checkFingerprintEnabled() { - m.state = StateAuthMethodChoice - m.selectedConfig = 0 - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - } - return m, nil - case "esc": - m.state = StateDependencyReview - return m, nil - } - } - return m, m.listenForLogs() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_install.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_install.go deleted file mode 100644 index c110711..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_install.go +++ /dev/null @@ -1,405 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -// wrapText wraps text to the specified width -func wrapText(text string, width int) string { - if len(text) <= width { - return text - } - - var result strings.Builder - words := strings.Fields(text) - currentLine := "" - - for _, word := range words { - if len(currentLine) == 0 { - currentLine = word - } else if len(currentLine)+1+len(word) <= width { - currentLine += " " + word - } else { - result.WriteString(currentLine) - result.WriteString("\n") - currentLine = word - } - } - - if len(currentLine) > 0 { - result.WriteString(currentLine) - } - - return result.String() -} - -func (m Model) viewInstallingPackages() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Installing Packages") - b.WriteString(title) - b.WriteString("\n\n") - - if !m.packageProgress.isComplete { - spinner := m.spinner.View() - status := m.styles.Normal.Render(m.packageProgress.step) - fmt.Fprintf(&b, "%s %s", spinner, status) - b.WriteString("\n\n") - - // Show progress bar - progressBar := fmt.Sprintf("[%s%s] %.0f%%", - strings.Repeat("█", int(m.packageProgress.progress*30)), - strings.Repeat("░", 30-int(m.packageProgress.progress*30)), - m.packageProgress.progress*100) - b.WriteString(m.styles.Normal.Render(progressBar)) - b.WriteString("\n") - - // Show command info if available - if m.packageProgress.commandInfo != "" { - cmdInfo := m.styles.Subtle.Render("$ " + m.packageProgress.commandInfo) - b.WriteString(cmdInfo) - b.WriteString("\n") - } - - // Show live log output - if len(m.installationLogs) > 0 { - b.WriteString("\n") - logHeader := m.styles.Subtle.Render("Live Output:") - b.WriteString(logHeader) - b.WriteString("\n") - - // Show last few lines of accumulated logs - maxLines := 8 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - } - - // Show error if any - if m.packageProgress.error != nil { - b.WriteString("\n") - wrappedErrorMsg := wrapText("Error: "+m.packageProgress.error.Error(), 80) - errorMsg := m.styles.Error.Render(wrappedErrorMsg) - b.WriteString(errorMsg) - } - - // Show sudo prompt if needed - if m.packageProgress.needsSudo { - sudoWarning := m.styles.Warning.Render("⚠ Using provided sudo password") - b.WriteString(sudoWarning) - } - } else { - if m.packageProgress.error != nil { - wrappedFailedMsg := wrapText("✗ Installation failed: "+m.packageProgress.error.Error(), 80) - errorMsg := m.styles.Error.Render(wrappedFailedMsg) - b.WriteString(errorMsg) - } else { - success := m.styles.Success.Render("✓ Installation complete!") - b.WriteString(success) - } - } - - return b.String() -} - -func dmsPackageName(distroID string, dependencies []deps.Dependency) string { - config, ok := distros.Registry[distroID] - if !ok { - return "dms" - } - - var isGit bool - for _, dep := range dependencies { - if dep.Name == "dms (DankMaterialShell)" { - isGit = dep.Variant == deps.VariantGit - break - } - } - - switch config.Family { - case distros.FamilyArch: - if isGit { - return "dms-shell-git" - } - return "dms-shell" - case distros.FamilyFedora, distros.FamilyUbuntu, distros.FamilyDebian, distros.FamilySUSE: - if isGit { - return "dms-git" - } - return "dms" - default: - return "dms" - } -} - -func uninstallCommand(distroID string, dependencies []deps.Dependency) string { - config, ok := distros.Registry[distroID] - if !ok { - return "" - } - if config.Family == distros.FamilyGentoo { - return "rm -rf ~/.config/quickshell/dms && sudo rm /usr/local/bin/dms" - } - pkg := dmsPackageName(distroID, dependencies) - switch config.Family { - case distros.FamilyArch: - return "sudo pacman -Rs " + pkg - case distros.FamilyFedora: - return "sudo dnf remove " + pkg - case distros.FamilyUbuntu, distros.FamilyDebian: - return "sudo apt remove " + pkg - case distros.FamilySUSE: - return "sudo zypper remove " + pkg - default: - return "" - } -} - -func (m Model) viewInstallComplete() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Success.Render("Setup Complete!") - b.WriteString(title) - b.WriteString("\n\n") - - success := m.styles.Success.Render("✓ All packages installed and configurations deployed.") - b.WriteString(success) - b.WriteString("\n\n") - - accomplishments := []string{ - "• Window manager and dependencies installed", - "• Terminal and development tools configured", - "• Configuration files deployed with backups", - "• System optimized for DankMaterialShell", - } - - for _, item := range accomplishments { - b.WriteString(m.styles.Subtle.Render(item)) - b.WriteString("\n") - } - - b.WriteString("\n") - info := m.styles.Normal.Render("Your system is ready! Log out and log back in to start using\nyour new desktop environment.\nIf you do not have a greeter, login with \"niri-session\" or \"Hyprland\"") - b.WriteString(info) - b.WriteString("\n\n") - - theme := TerminalTheme() - cmdStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(theme.Accent)) - labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(theme.Subtle)) - - b.WriteString(labelStyle.Render("Troubleshooting:") + "\n") - b.WriteString(labelStyle.Render(" Disable autostart: ") + cmdStyle.Render("systemctl --user disable dms") + "\n") - b.WriteString(labelStyle.Render(" View logs: ") + cmdStyle.Render("journalctl --user -u dms") + "\n") - - if m.osInfo != nil { - if cmd := uninstallCommand(m.osInfo.Distribution.ID, m.dependencies); cmd != "" { - b.WriteString(labelStyle.Render(" Uninstall: ") + cmdStyle.Render(cmd) + "\n") - } - } - - b.WriteString("\n") - b.WriteString(m.styles.Normal.Render("Press Enter to exit.")) - - if m.logFilePath != "" { - b.WriteString("\n\n") - logInfo := m.styles.Subtle.Render(fmt.Sprintf("Full logs: %s", m.logFilePath)) - b.WriteString(logInfo) - } - - return b.String() -} - -func (m Model) viewError() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Error.Render("Installation Failed") - b.WriteString(title) - b.WriteString("\n\n") - - if m.err != nil { - wrappedError := wrapText("✗ "+m.err.Error(), 80) - error := m.styles.Error.Render(wrappedError) - b.WriteString(error) - b.WriteString("\n\n") - } - - // Show package progress error if available - if m.packageProgress.error != nil { - wrappedPackageError := wrapText("Package Installation Error: "+m.packageProgress.error.Error(), 80) - packageError := m.styles.Error.Render(wrappedPackageError) - b.WriteString(packageError) - b.WriteString("\n\n") - } - - // Show persistent installation logs - if len(m.installationLogs) > 0 { - logHeader := m.styles.Warning.Render("Installation Logs (last 15 lines):") - b.WriteString(logHeader) - b.WriteString("\n") - - maxLines := 15 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - b.WriteString("\n") - } - - hint := m.styles.Subtle.Render("Press Ctrl+D for full debug logs") - b.WriteString(hint) - b.WriteString("\n") - - if m.logFilePath != "" { - b.WriteString("\n") - logInfo := m.styles.Warning.Render(fmt.Sprintf("Full logs: %s", m.logFilePath)) - b.WriteString(logInfo) - b.WriteString("\n") - } - - help := m.styles.Subtle.Render("Press Enter to exit") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateInstallingPackagesState(msg tea.Msg) (tea.Model, tea.Cmd) { - if progressMsg, ok := msg.(packageInstallProgressMsg); ok { - m.packageProgress = progressMsg - - // Accumulate log output - if progressMsg.logOutput != "" { - m.installationLogs = append(m.installationLogs, progressMsg.logOutput) - // Keep only last 50 lines to preserve more context for debugging - if len(m.installationLogs) > 50 { - m.installationLogs = m.installationLogs[len(m.installationLogs)-50:] - } - } - - if progressMsg.isComplete { - if progressMsg.error != nil { - m.state = StateError - m.isLoading = false - } else { - m.installationLogs = []string{} - m.state = StateConfigConfirmation - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.checkExistingConfigurations()) - } - } - return m, m.listenForPackageProgress() - } - return m, m.listenForLogs() -} - -func (m Model) updateInstallCompleteState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - return m, tea.Quit - } - } - return m, m.listenForLogs() -} - -func (m Model) updateErrorState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - return m, tea.Quit - } - } - return m, m.listenForLogs() -} - -func (m Model) listenForPackageProgress() tea.Cmd { - return func() tea.Msg { - msg, ok := <-m.packageProgressChan - if !ok { - return packageProgressCompletedMsg{} - } - // Always return the message, completion will be handled in updateInstallingPackagesState - return msg - } -} - -func (m Model) viewDebugLogs() string { - var b strings.Builder - - theme := TerminalTheme() - - titleStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true) - - b.WriteString(titleStyle.Render("Debug Logs")) - b.WriteString("\n\n") - - // Combine both logMessages and installationLogs - allLogs := append([]string{}, m.logMessages...) - allLogs = append(allLogs, m.installationLogs...) - - if len(allLogs) == 0 { - b.WriteString("No logs available\n") - } else { - // Calculate available height (reserve space for header and footer) - maxHeight := m.height - 6 - if maxHeight < 10 { - maxHeight = 10 - } - - // Show the most recent logs - startIdx := 0 - if len(allLogs) > maxHeight { - startIdx = len(allLogs) - maxHeight - } - - for i := startIdx; i < len(allLogs); i++ { - if allLogs[i] != "" { - fmt.Fprintf(&b, "%d: %s\n", i, allLogs[i]) - } - } - - if startIdx > 0 { - subtleStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(theme.Subtle)) - b.WriteString(subtleStyle.Render(fmt.Sprintf("... (%d older log entries hidden)\n", startIdx))) - } - } - - b.WriteString("\n") - statusStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(theme.Accent)) - b.WriteString(statusStyle.Render("Press Ctrl+D to return, Ctrl+C to quit")) - - return b.String() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_password.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_password.go deleted file mode 100644 index deec061..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_password.go +++ /dev/null @@ -1,338 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewAuthMethodChoice() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Authentication Method") - b.WriteString(title) - b.WriteString("\n\n") - - message := "Fingerprint authentication is available.\nHow would you like to authenticate?" - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - // Option 0: Fingerprint - if m.selectedConfig == 0 { - option := m.styles.SelectedOption.Render("▶ Use Fingerprint") - b.WriteString(option) - } else { - option := m.styles.Normal.Render(" Use Fingerprint") - b.WriteString(option) - } - b.WriteString("\n") - - // Option 1: Password - if m.selectedConfig == 1 { - option := m.styles.SelectedOption.Render("▶ Use Password") - b.WriteString(option) - } else { - option := m.styles.Normal.Render(" Use Password") - b.WriteString(option) - } - b.WriteString("\n\n") - - help := m.styles.Subtle.Render("↑/↓: Navigate, Enter: Select, Esc: Back") - b.WriteString(help) - - return b.String() -} - -func (m Model) viewFingerprintAuth() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Fingerprint Authentication") - b.WriteString(title) - b.WriteString("\n\n") - - if m.fingerprintFailed { - errorMsg := m.styles.Error.Render("✗ Fingerprint authentication failed") - b.WriteString(errorMsg) - b.WriteString("\n") - retryMsg := m.styles.Subtle.Render("Returning to authentication menu...") - b.WriteString(retryMsg) - } else { - message := "Please place your finger on the fingerprint reader." - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Waiting for fingerprint...") - fmt.Fprintf(&b, "%s %s", spinner, status) - } - - return b.String() -} - -func (m Model) viewPasswordPrompt() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Password Authentication") - b.WriteString(title) - b.WriteString("\n\n") - - message := "Installation requires sudo privileges.\nPlease enter your password to continue:" - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - // Password input - b.WriteString(m.passwordInput.View()) - b.WriteString("\n") - - // Show validation status - if m.packageProgress.step == "Validating sudo password..." { - spinner := m.spinner.View() - status := m.styles.Normal.Render(m.packageProgress.step) - b.WriteString(spinner + " " + status) - b.WriteString("\n") - } else if m.packageProgress.error != nil { - errorMsg := m.styles.Error.Render("✗ " + m.packageProgress.error.Error() + ". Please try again.") - b.WriteString(errorMsg) - b.WriteString("\n") - } else if m.packageProgress.step == "Password validation failed" { - errorMsg := m.styles.Error.Render("✗ Incorrect password. Please try again.") - b.WriteString(errorMsg) - b.WriteString("\n") - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Enter: Continue, Esc: Back, Ctrl+C: Cancel") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateAuthMethodChoiceState(msg tea.Msg) (tea.Model, tea.Cmd) { - m.fingerprintFailed = false - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedConfig > 0 { - m.selectedConfig-- - } - case "down": - if m.selectedConfig < 1 { - m.selectedConfig++ - } - case "enter": - if m.selectedConfig == 0 { - m.state = StateFingerprintAuth - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.tryFingerprint()) - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - return m, nil - } - case "esc": - m.state = StateDependencyReview - return m, nil - } - } - return m, nil -} - -func (m Model) updateFingerprintAuthState(msg tea.Msg) (tea.Model, tea.Cmd) { - if validMsg, ok := msg.(passwordValidMsg); ok { - if validMsg.valid { - m.sudoPassword = "" - m.packageProgress = packageInstallProgressMsg{} - m.state = StateInstallingPackages - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.installPackages()) - } else { - m.fingerprintFailed = true - return m, m.delayThenReturn() - } - } - - if _, ok := msg.(delayCompleteMsg); ok { - m.fingerprintFailed = false - m.selectedConfig = 0 - m.state = StateAuthMethodChoice - return m, nil - } - - return m, m.listenForLogs() -} - -func (m Model) updatePasswordPromptState(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmd tea.Cmd - - if validMsg, ok := msg.(passwordValidMsg); ok { - if validMsg.valid { - // Password is valid, proceed with installation - m.sudoPassword = validMsg.password - m.passwordInput.SetValue("") // Clear password input - // Clear any error state - m.packageProgress = packageInstallProgressMsg{} - m.state = StateInstallingPackages - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.installPackages()) - } else { - // Password is invalid, show error and stay on password prompt - m.packageProgress = packageInstallProgressMsg{ - progress: 0.0, - step: "Password validation failed", - error: fmt.Errorf("incorrect password"), - logOutput: "Authentication failed", - } - m.passwordInput.SetValue("") - m.passwordInput.Focus() - return m, nil - } - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - // Don't allow multiple validation attempts while one is in progress - if m.packageProgress.step == "Validating sudo password..." { - return m, nil - } - - // Validate password first - password := m.passwordInput.Value() - if password == "" { - return m, nil // Don't proceed with empty password - } - - // Clear any previous error and show validation in progress - m.packageProgress = packageInstallProgressMsg{ - progress: 0.01, - step: "Validating sudo password...", - isComplete: false, - logOutput: "Testing password with sudo -v", - } - return m, m.validatePassword(password) - case "esc": - // Go back to dependency review - m.passwordInput.SetValue("") - m.packageProgress = packageInstallProgressMsg{} // Clear any validation state - m.state = StateDependencyReview - return m, nil - } - } - - m.passwordInput, cmd = m.passwordInput.Update(msg) - return m, cmd -} - -func checkFingerprintEnabled() bool { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - // Check if pam_fprintd.so is in PAM config - cmd := exec.CommandContext(ctx, "grep", "-q", "pam_fprintd.so", "/etc/pam.d/system-auth") - if err := cmd.Run(); err != nil { - return false - } - - // Check if fprintd-list exists and user has enrolled fingerprints - user := os.Getenv("USER") - if user == "" { - return false - } - - listCmd := exec.CommandContext(ctx, "fprintd-list", user) - output, err := listCmd.CombinedOutput() - if err != nil { - return false - } - - // If output contains "finger:" or similar, fingerprints are enrolled - return strings.Contains(string(output), "finger") -} - -func (m Model) delayThenReturn() tea.Cmd { - return func() tea.Msg { - time.Sleep(2 * time.Second) - return delayCompleteMsg{} - } -} - -func (m Model) tryFingerprint() tea.Cmd { - return func() tea.Msg { - clearCmd := exec.Command("sudo", "-k") - clearCmd.Run() - - tmpDir := os.TempDir() - askpassScript := filepath.Join(tmpDir, fmt.Sprintf("danklinux-fp-%d.sh", time.Now().UnixNano())) - - scriptContent := "#!/bin/sh\nexit 1\n" - if err := os.WriteFile(askpassScript, []byte(scriptContent), 0o700); err != nil { - return passwordValidMsg{password: "", valid: false} - } - defer os.Remove(askpassScript) - - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-A", "-v") - cmd.Env = append(os.Environ(), fmt.Sprintf("SUDO_ASKPASS=%s", askpassScript)) - - err := cmd.Run() - - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - return passwordValidMsg{password: "", valid: true} - } -} - -func (m Model) validatePassword(password string) tea.Cmd { - return func() tea.Msg { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-S", "-v") - - stdin, err := cmd.StdinPipe() - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - if err := cmd.Start(); err != nil { - return passwordValidMsg{password: "", valid: false} - } - - _, err = fmt.Fprintf(stdin, "%s\n", password) - stdin.Close() - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - err = cmd.Wait() - - if err != nil { - if ctx.Err() == context.DeadlineExceeded { - return passwordValidMsg{password: "", valid: false} - } - return passwordValidMsg{password: "", valid: false} - } - - return passwordValidMsg{password: password, valid: true} - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_selection.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_selection.go deleted file mode 100644 index e45105e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_selection.go +++ /dev/null @@ -1,224 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewSelectWindowManager() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Choose Window Manager") - b.WriteString(title) - b.WriteString("\n\n") - - options := []struct { - name string - description string - }{ - {"niri", "Scrollable-tiling Wayland compositor."}, - } - - if m.osInfo == nil || m.osInfo.Distribution.ID != "debian" { - options = append(options, struct { - name string - description string - }{"Hyprland", "Dynamic tiling Wayland compositor."}) - } - - for i, option := range options { - if i == m.selectedWM { - selected := m.styles.SelectedOption.Render("▶ " + option.name) - b.WriteString(selected) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } else { - normal := m.styles.Normal.Render(" " + option.name) - b.WriteString(normal) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } - b.WriteString("\n") - if i < len(options)-1 { - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Use ↑/↓ to navigate, Enter to select, Esc to go back") - b.WriteString(help) - - return b.String() -} - -func (m Model) viewSelectTerminal() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Choose Terminal Emulator") - b.WriteString(title) - b.WriteString("\n\n") - - var options []struct { - name string - description string - } - - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - options = []struct { - name string - description string - }{ - {"kitty", "A feature-rich, customizable terminal emulator."}, - {"alacritty", "A simple terminal emulator."}, - } - } else { - options = []struct { - name string - description string - }{ - {"ghostty", "A fast, native terminal emulator built in Zig."}, - {"kitty", "A feature-rich, customizable terminal emulator."}, - {"alacritty", "A simple terminal emulator."}, - } - } - - for i, option := range options { - if i == m.selectedTerminal { - selected := m.styles.SelectedOption.Render("▶ " + option.name) - b.WriteString(selected) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } else { - normal := m.styles.Normal.Render(" " + option.name) - b.WriteString(normal) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } - b.WriteString("\n") - if i < len(options)-1 { - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Use ↑/↓ to navigate, Enter to select, Esc to go back") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateSelectTerminalState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - maxTerminalIndex := 2 - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - maxTerminalIndex = 1 - } - - switch keyMsg.String() { - case "up": - if m.selectedTerminal > 0 { - m.selectedTerminal-- - } - case "down": - if m.selectedTerminal < maxTerminalIndex { - m.selectedTerminal++ - } - case "enter": - m.state = StateDetectingDeps - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.detectDependencies()) - case "esc": - m.state = StateSelectWindowManager - return m, m.listenForLogs() - } - } - return m, m.listenForLogs() -} - -func (m Model) updateSelectWindowManagerState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - maxWMIndex := 1 - if m.osInfo != nil && m.osInfo.Distribution.ID == "debian" { - maxWMIndex = 0 - } - - switch keyMsg.String() { - case "up": - if m.selectedWM > 0 { - m.selectedWM-- - } - case "down": - if m.selectedWM < maxWMIndex { - m.selectedWM++ - } - case "enter": - m.state = StateSelectTerminal - return m, m.listenForLogs() - case "esc": - // Go back to welcome screen - m.state = StateWelcome - return m, m.listenForLogs() - } - } - return m, m.listenForLogs() -} - -func (m Model) detectDependencies() tea.Cmd { - return func() tea.Msg { - if m.osInfo == nil { - return depsDetectedMsg{deps: nil, err: fmt.Errorf("OS info not available")} - } - - detector, err := distros.NewDependencyDetector(m.osInfo.Distribution.ID, m.logChan) - if err != nil { - return depsDetectedMsg{deps: nil, err: err} - } - - // Convert TUI selection to deps enum - var wm deps.WindowManager - if m.selectedWM == 0 { - wm = deps.WindowManagerNiri // First option is Niri - } else { - wm = deps.WindowManagerHyprland // Second option is Hyprland - } - - var terminal deps.Terminal - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalKitty - case 1: - terminal = deps.TerminalAlacritty - default: - terminal = deps.TerminalKitty - } - } else { - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalGhostty - case 1: - terminal = deps.TerminalKitty - default: - terminal = deps.TerminalAlacritty - } - } - - dependencies, err := detector.DetectDependenciesWithTerminal(context.Background(), wm, terminal) - return depsDetectedMsg{deps: dependencies, err: err} - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_welcome.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_welcome.go deleted file mode 100644 index c5ae862..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_welcome.go +++ /dev/null @@ -1,216 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -func (m Model) viewWelcome() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - theme := TerminalTheme() - - decorator := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Render("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - - titleBox := lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color(theme.Primary)). - Padding(0, 2). - MarginBottom(1) - - titleText := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("dankinstall") - - versionTag := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Italic(true). - Render(" // Dank Linux Installer") - - subtitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Italic(true). - Render("Quickstart for a Dank Desktop") - - b.WriteString(decorator) - b.WriteString("\n") - b.WriteString(titleBox.Render(titleText + versionTag)) - b.WriteString("\n") - b.WriteString(subtitle) - b.WriteString("\n\n") - - if m.osInfo != nil { - if distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - errorBox := lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color("#FF6B6B")). - Padding(1, 2). - MarginBottom(1) - - errorTitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF6B6B")). - Bold(true). - Render("⚠ UNSUPPORTED DISTRIBUTION") - - var errorMsg string - switch m.osInfo.Distribution.ID { - case "ubuntu": - errorMsg = fmt.Sprintf("Ubuntu %s is not supported.\n\nOnly Ubuntu 25.04+ is supported.\n\nPlease upgrade to Ubuntu 25.04 or later.", m.osInfo.VersionID) - case "debian": - errorMsg = fmt.Sprintf("Debian %s is not supported.\n\nOnly Debian 13+ (Trixie) is supported.\n\nPlease upgrade to Debian 13 or later.", m.osInfo.VersionID) - case "nixos": - errorMsg = "See the NixOS documentation for installation instructions: https://danklinux.com/docs/dankmaterialshell/nixos." - default: - errorMsg = fmt.Sprintf("%s is not supported.\nFeel free to request on https://github.com/AvengeMedia/DankMaterialShell", m.osInfo.PrettyName) - } - - errorMsgStyled := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Render(errorMsg) - - b.WriteString(errorBox.Render(errorTitle + "\n\n" + errorMsgStyled)) - b.WriteString("\n\n") - } else { - // System info box - sysBox := lipgloss.NewStyle(). - Border(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color(theme.Subtle)). - Padding(0, 1). - MarginBottom(1) - - // Style the distro name with its color - distroStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(m.osInfo.Distribution.HexColorCode)). - Bold(true) - distroName := distroStyle.Render(m.osInfo.PrettyName) - - archStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)) - - sysInfo := fmt.Sprintf("System: %s / %s", distroName, archStyle.Render(m.osInfo.Architecture)) - b.WriteString(sysBox.Render(sysInfo)) - b.WriteString("\n") - - // Feature list with better styling - featTitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Underline(true). - Render("WHAT YOU GET") - b.WriteString(featTitle + "\n\n") - - features := []string{ - "[shell] dms (DankMaterialShell)", - "[wm] niri or Hyprland", - "[term] Ghostty, kitty, or Alacritty", - "[style] All the themes, automatically.", - "[config] DANK defaults - keybindings, rules, animations, etc.", - } - - for i, feat := range features { - prefix := feat[:9] - content := feat[10:] - - prefixStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true) - - contentStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)) - - if i == len(features)-1 { - contentStyle = contentStyle.Bold(true) - } - - fmt.Fprintf(&b, " %s %s\n", - prefixStyle.Render(prefix), - contentStyle.Render(content)) - } - - b.WriteString("\n") - - noteStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Italic(true) - note := noteStyle.Render("* Existing configs can be replaced (and backed up) or preserved") - b.WriteString(note) - b.WriteString("\n") - - if m.osInfo.Distribution.ID == "gentoo" { - gentooNote := noteStyle.Render("* Will set per-package USE flags and unmask testing packages as needed") - b.WriteString(gentooNote) - b.WriteString("\n") - } - - b.WriteString("\n") - } - - } else if m.isLoading { - spinner := m.spinner.View() - loading := m.styles.Normal.Render("Detecting system...") - fmt.Fprintf(&b, "%s %s\n\n", spinner, loading) - } - - // Footer with better visual separation - footerDivider := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Render("───────────────────────────────────────────────────────────") - b.WriteString(footerDivider + "\n") - - if m.osInfo != nil { - ctrlKey := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("Ctrl+C") - - if distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - b.WriteString(m.styles.Subtle.Render("Press ") + ctrlKey + m.styles.Subtle.Render(" to quit")) - } else { - enterKey := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("Enter") - - b.WriteString(m.styles.Subtle.Render("Press ") + enterKey + m.styles.Subtle.Render(" to choose window manager, ") + ctrlKey + m.styles.Subtle.Render(" to quit")) - } - } else { - help := m.styles.Subtle.Render("Press Enter to continue, Ctrl+C to quit") - b.WriteString(help) - } - - return b.String() -} - -func (m Model) updateWelcomeState(msg tea.Msg) (tea.Model, tea.Cmd) { - if completeMsg, ok := msg.(osInfoCompleteMsg); ok { - m.isLoading = false - if completeMsg.err != nil { - m.err = completeMsg.err - m.state = StateError - } else { - m.osInfo = completeMsg.info - } - return m, m.listenForLogs() - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - if m.osInfo != nil && !distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - m.state = StateSelectWindowManager - return m, m.listenForLogs() - } - } - } - return m, m.listenForLogs() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/dbus.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/dbus.go deleted file mode 100644 index bafd51f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/dbus.go +++ /dev/null @@ -1,37 +0,0 @@ -package utils - -import ( - "slices" - - "github.com/godbus/dbus/v5" -) - -func IsDBusServiceAvailable(busName string) bool { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return false - } - defer conn.Close() - - obj := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") - var owned bool - if err := obj.Call("org.freedesktop.DBus.NameHasOwner", 0, busName).Store(&owned); err != nil { - return false - } - return owned -} - -func IsDBusServiceActivatable(busName string) bool { - conn, err := dbus.ConnectSystemBus() - if err != nil { - return false - } - defer conn.Close() - - obj := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") - var activatable []string - if err := obj.Call("org.freedesktop.DBus.ListActivatableNames", 0).Store(&activatable); err != nil { - return false - } - return slices.Contains(activatable, busName) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/exec.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/exec.go deleted file mode 100644 index aa1eb31..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/exec.go +++ /dev/null @@ -1,44 +0,0 @@ -package utils - -import ( - "os/exec" -) - -type AppChecker interface { - CommandExists(cmd string) bool - AnyCommandExists(cmds ...string) bool - FlatpakExists(name string) bool - AnyFlatpakExists(flatpaks ...string) bool -} - -type DefaultAppChecker struct{} - -func (DefaultAppChecker) CommandExists(cmd string) bool { - return CommandExists(cmd) -} - -func (DefaultAppChecker) AnyCommandExists(cmds ...string) bool { - return AnyCommandExists(cmds...) -} - -func (DefaultAppChecker) FlatpakExists(name string) bool { - return FlatpakExists(name) -} - -func (DefaultAppChecker) AnyFlatpakExists(flatpaks ...string) bool { - return AnyFlatpakExists(flatpaks...) -} - -func CommandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} - -func AnyCommandExists(cmds ...string) bool { - for _, cmd := range cmds { - if CommandExists(cmd) { - return true - } - } - return false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak.go deleted file mode 100644 index f1c9009..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak.go +++ /dev/null @@ -1,83 +0,0 @@ -package utils - -import ( - "bytes" - "errors" - "os/exec" - "slices" - "strings" -) - -func FlatpakInPath() bool { - _, err := exec.LookPath("flatpak") - return err == nil -} - -func FlatpakExists(name string) bool { - if !FlatpakInPath() { - return false - } - - cmd := exec.Command("flatpak", "info", name) - err := cmd.Run() - return err == nil -} - -func FlatpakSearchBySubstring(substring string) bool { - if !FlatpakInPath() { - return false - } - - cmd := exec.Command("flatpak", "list", "--app") - var stdout bytes.Buffer - cmd.Stdout = &stdout - - if err := cmd.Run(); err != nil { - return false - } - - out := stdout.String() - - for line := range strings.SplitSeq(out, "\n") { - fields := strings.Fields(line) - if len(fields) > 1 { - id := fields[1] - idParts := strings.Split(id, ".") - // We are assuming that the last part of the ID is - // the package name we're looking for. This might - // not always be true, some developers use arbitrary - // suffixes. - if len(idParts) > 0 && idParts[len(idParts)-1] == substring { - cmd := exec.Command("flatpak", "info", id) - err := cmd.Run() - return err == nil - } - } - } - return false -} - -func AnyFlatpakExists(flatpaks ...string) bool { - return slices.ContainsFunc(flatpaks, FlatpakExists) -} - -func FlatpakInstallationDir(name string) (string, error) { - if !FlatpakInPath() { - return "", errors.New("flatpak not found in PATH") - } - - cmd := exec.Command("flatpak", "info", "--show-location", name) - var stdout bytes.Buffer - cmd.Stdout = &stdout - - if err := cmd.Run(); err != nil { - return "", errors.New("flatpak not installed: " + name) - } - - location := strings.TrimSpace(stdout.String()) - if location == "" { - return "", errors.New("installation directory not found for: " + name) - } - - return location, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak_test.go deleted file mode 100644 index 97282ae..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak_test.go +++ /dev/null @@ -1,271 +0,0 @@ -package utils - -import ( - "os" - "path/filepath" - "strings" - "testing" -) - -func TestFlatpakInPathAvailable(t *testing.T) { - result := FlatpakInPath() - if !result { - t.Skip("flatpak not in PATH") - } - if !result { - t.Errorf("expected true when flatpak is in PATH") - } -} - -func TestFlatpakInPathUnavailable(t *testing.T) { - tempDir := t.TempDir() - t.Setenv("PATH", tempDir) - - result := FlatpakInPath() - if result { - t.Errorf("expected false when flatpak not in PATH, got true") - } -} - -func TestFlatpakExistsValidPackage(t *testing.T) { - if !FlatpakInPath() { - t.Skip("flatpak not in PATH") - } - - result := FlatpakExists("com.nonexistent.package.test") - if result { - t.Logf("package exists (unexpected but not an error)") - } -} - -func TestFlatpakExistsNoFlatpak(t *testing.T) { - tempDir := t.TempDir() - t.Setenv("PATH", tempDir) - - result := FlatpakExists("any.package.name") - if result { - t.Errorf("expected false when flatpak not in PATH, got true") - } -} - -func TestFlatpakSearchBySubstringNoFlatpak(t *testing.T) { - tempDir := t.TempDir() - t.Setenv("PATH", tempDir) - - result := FlatpakSearchBySubstring("test") - if result { - t.Errorf("expected false when flatpak not in PATH, got true") - } -} - -func TestFlatpakSearchBySubstringNonexistent(t *testing.T) { - if !FlatpakInPath() { - t.Skip("flatpak not in PATH") - } - - result := FlatpakSearchBySubstring("ThisIsAVeryUnlikelyPackageName12345") - if result { - t.Errorf("expected false for nonexistent package substring") - } -} - -func TestFlatpakInstallationDirNoFlatpak(t *testing.T) { - tempDir := t.TempDir() - t.Setenv("PATH", tempDir) - - _, err := FlatpakInstallationDir("any.package.name") - if err == nil { - t.Errorf("expected error when flatpak not in PATH") - } - if err != nil && !strings.Contains(err.Error(), "not found in PATH") { - t.Errorf("expected 'not found in PATH' error, got: %v", err) - } -} - -func TestFlatpakInstallationDirNonexistent(t *testing.T) { - if !FlatpakInPath() { - t.Skip("flatpak not in PATH") - } - - _, err := FlatpakInstallationDir("com.nonexistent.package.test") - if err == nil { - t.Errorf("expected error for nonexistent package") - } - if err != nil && !strings.Contains(err.Error(), "not installed") { - t.Errorf("expected 'not installed' error, got: %v", err) - } -} - -func TestFlatpakInstallationDirValid(t *testing.T) { - if !FlatpakInPath() { - t.Skip("flatpak not in PATH") - } - - // This test requires a known installed flatpak - // We can't guarantee any specific flatpak is installed, - // so we'll skip if we can't find a common one - commonFlatpaks := []string{ - "org.mozilla.firefox", - "org.gnome.Calculator", - "org.freedesktop.Platform", - } - - var testPackage string - for _, pkg := range commonFlatpaks { - if FlatpakExists(pkg) { - testPackage = pkg - break - } - } - - if testPackage == "" { - t.Skip("no common flatpak packages found for testing") - } - - result, err := FlatpakInstallationDir(testPackage) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if result == "" { - t.Errorf("expected non-empty installation directory") - } - if !strings.Contains(result, testPackage) { - t.Logf("installation directory %s doesn't contain package name (may be expected)", result) - } -} - -func TestFlatpakExistsCommandFailure(t *testing.T) { - if !FlatpakInPath() { - t.Skip("flatpak not in PATH") - } - - // Mock a failing flatpak command through PATH interception - tempDir := t.TempDir() - fakeFlatpak := filepath.Join(tempDir, "flatpak") - - script := "#!/bin/sh\nexit 1\n" - err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) - if err != nil { - t.Fatalf("failed to create fake flatpak: %v", err) - } - - originalPath := os.Getenv("PATH") - t.Setenv("PATH", tempDir+":"+originalPath) - - result := FlatpakExists("test.package") - if result { - t.Errorf("expected false when flatpak command fails, got true") - } -} - -func TestFlatpakSearchBySubstringCommandFailure(t *testing.T) { - if !FlatpakInPath() { - t.Skip("flatpak not in PATH") - } - - // Mock a failing flatpak command through PATH interception - tempDir := t.TempDir() - fakeFlatpak := filepath.Join(tempDir, "flatpak") - - script := "#!/bin/sh\nexit 1\n" - err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) - if err != nil { - t.Fatalf("failed to create fake flatpak: %v", err) - } - - originalPath := os.Getenv("PATH") - t.Setenv("PATH", tempDir+":"+originalPath) - - result := FlatpakSearchBySubstring("test") - if result { - t.Errorf("expected false when flatpak command fails, got true") - } -} - -func TestFlatpakInstallationDirCommandFailure(t *testing.T) { - if !FlatpakInPath() { - t.Skip("flatpak not in PATH") - } - - // Mock a failing flatpak command through PATH interception - tempDir := t.TempDir() - fakeFlatpak := filepath.Join(tempDir, "flatpak") - - script := "#!/bin/sh\nexit 1\n" - err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) - if err != nil { - t.Fatalf("failed to create fake flatpak: %v", err) - } - - originalPath := os.Getenv("PATH") - t.Setenv("PATH", tempDir+":"+originalPath) - - _, err = FlatpakInstallationDir("test.package") - if err == nil { - t.Errorf("expected error when flatpak command fails") - } - if err != nil && !strings.Contains(err.Error(), "not installed") { - t.Errorf("expected 'not installed' error, got: %v", err) - } -} - -func TestAnyFlatpakExistsSomeExist(t *testing.T) { - tempDir := t.TempDir() - fakeFlatpak := filepath.Join(tempDir, "flatpak") - - // Script that succeeds only for "app.exists.test" - script := `#!/bin/sh -if [ "$1" = "info" ] && [ "$2" = "app.exists.test" ]; then - exit 0 -fi -exit 1 -` - err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) - if err != nil { - t.Fatalf("failed to create fake flatpak: %v", err) - } - - originalPath := os.Getenv("PATH") - t.Setenv("PATH", tempDir+":"+originalPath) - - result := AnyFlatpakExists("com.nonexistent.flatpak", "app.exists.test", "com.another.nonexistent") - if !result { - t.Errorf("expected true when at least one flatpak exists") - } -} - -func TestAnyFlatpakExistsNoneExist(t *testing.T) { - tempDir := t.TempDir() - fakeFlatpak := filepath.Join(tempDir, "flatpak") - - script := "#!/bin/sh\nexit 1\n" - err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) - if err != nil { - t.Fatalf("failed to create fake flatpak: %v", err) - } - - originalPath := os.Getenv("PATH") - t.Setenv("PATH", tempDir+":"+originalPath) - - result := AnyFlatpakExists("com.nonexistent.flatpak1", "com.nonexistent.flatpak2") - if result { - t.Errorf("expected false when no flatpaks exist") - } -} - -func TestAnyFlatpakExistsNoFlatpak(t *testing.T) { - tempDir := t.TempDir() - t.Setenv("PATH", tempDir) - - result := AnyFlatpakExists("any.package.name", "another.package") - if result { - t.Errorf("expected false when flatpak not in PATH, got true") - } -} - -func TestAnyFlatpakExistsEmpty(t *testing.T) { - result := AnyFlatpakExists() - if result { - t.Errorf("expected false when no flatpaks specified") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group.go deleted file mode 100644 index 1a0a950..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group.go +++ /dev/null @@ -1,37 +0,0 @@ -package utils - -import ( - "os" - "strings" -) - -func HasGroup(groupName string) bool { - return HasGroupIn(groupName, "/etc/group") -} - -func HasGroupIn(groupName, path string) bool { - data, err := os.ReadFile(path) - if err != nil { - return false - } - return HasGroupData(groupName, string(data)) -} - -func HasGroupData(groupName, data string) bool { - prefix := groupName + ":" - for line := range strings.SplitSeq(data, "\n") { - if strings.HasPrefix(line, prefix) { - return true - } - } - return false -} - -func FindGroupData(data string, candidates ...string) (string, bool) { - for _, candidate := range candidates { - if HasGroupData(candidate, data) { - return candidate, true - } - } - return "", false -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group_test.go deleted file mode 100644 index 4b401dc..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package utils - -import "testing" - -const testGroupData = `root:x:0:brltty,root -sys:x:3:bin,testuser -mem:x:8: -ftp:x:11: -mail:x:12: -log:x:19: -smmsp:x:25: -proc:x:26: -games:x:50: -lock:x:54: -network:x:90: -floppy:x:94: -scanner:x:96: -power:x:98: -nobody:x:65534: -adm:x:999:daemon -wheel:x:998:testuser -utmp:x:997: -audio:x:996:brltty -disk:x:995: -input:x:994:brltty,testuser,greeter -kmem:x:993: -kvm:x:992:libvirt-qemu,qemu,testuser -lp:x:991:cups,testuser -optical:x:990: -render:x:989: -sgx:x:988: -storage:x:987: -tty:x:5:brltty -uucp:x:986:brltty -video:x:985:cosmic-greeter,greeter,testuser -users:x:984: -groups:x:983: -systemd-journal:x:982: -rfkill:x:981: -bin:x:1:daemon -daemon:x:2:bin -http:x:33: -dbus:x:81: -systemd-coredump:x:980: -systemd-network:x:979: -systemd-oom:x:978: -systemd-journal-remote:x:977: -systemd-resolve:x:976: -systemd-timesync:x:975: -tss:x:974: -uuidd:x:973: -alpm:x:972: -polkitd:x:102: -testuser:x:1000: -avahi:x:971: -git:x:970: -nvidia-persistenced:x:143: -i2c:x:969:testuser -seat:x:968: -rtkit:x:133: -brlapi:x:967:brltty -gdm:x:120: -brltty:x:966: -colord:x:965: -flatpak:x:964: -geoclue:x:963:testuser -gnome-remote-desktop:x:962: -saned:x:961: -usbmux:x:140: -cosmic-greeter:x:960: -greeter:x:959:testuser -openvpn:x:958: -nm-openvpn:x:957: -named:x:40: -_talkd:x:956: -keyd:x:955: -cups:x:209:testuser -docker:x:954:testuser -mysql:x:953: -radicale:x:952: -onepassword:x:1001: -nixbld:x:951:nixbld01,nixbld02,nixbld03,nixbld04,nixbld05,nixbld06,nixbld07,nixbld08,nixbld09,nixbld10 -virtlogin:x:940: -libvirt:x:939:testuser -libvirt-qemu:x:938: -qemu:x:937: -dnsmasq:x:936: -clock:x:935: -dms-greeter:x:1002:greeter,testuser -pcscd:x:934: -test:x:1003: -empower:x:933: -` - -func TestHasGroupData(t *testing.T) { - tests := []struct { - group string - want bool - }{ - {"greeter", true}, - {"root", true}, - {"docker", true}, - {"cosmic-greeter", true}, - {"dms-greeter", true}, - {"nonexistent", false}, - {"greet", false}, - } - - for _, tt := range tests { - if got := HasGroupData(tt.group, testGroupData); got != tt.want { - t.Errorf("HasGroupData(%q) = %v, want %v", tt.group, got, tt.want) - } - } -} - -func TestFindGroupData(t *testing.T) { - tests := []struct { - name string - candidates []string - wantGroup string - wantFound bool - }{ - {"first match wins", []string{"greeter", "greetd", "_greeter"}, "greeter", true}, - {"fallback to second", []string{"greetd", "greeter"}, "greeter", true}, - {"none found", []string{"_greetd", "greetd"}, "", false}, - {"single match", []string{"docker"}, "docker", true}, - } - - for _, tt := range tests { - got, found := FindGroupData(testGroupData, tt.candidates...) - if got != tt.wantGroup || found != tt.wantFound { - t.Errorf("%s: FindGroupData(%v) = (%q, %v), want (%q, %v)", - tt.name, tt.candidates, got, found, tt.wantGroup, tt.wantFound) - } - } -} - -func TestHasGroupDataEmpty(t *testing.T) { - if HasGroupData("greeter", "") { - t.Error("expected false for empty data") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/gsettings.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/gsettings.go deleted file mode 100644 index cc8d084..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/gsettings.go +++ /dev/null @@ -1,31 +0,0 @@ -package utils - -import ( - "fmt" - "os/exec" - "strings" -) - -func dconfPath(schema, key string) string { - return "/" + strings.ReplaceAll(schema, ".", "/") + "/" + key -} - -// GsettingsGet reads a gsettings value, falling back to dconf read. -func GsettingsGet(schema, key string) (string, error) { - if out, err := exec.Command("gsettings", "get", schema, key).Output(); err == nil { - return strings.TrimSpace(string(out)), nil - } - out, err := exec.Command("dconf", "read", dconfPath(schema, key)).Output() - if err != nil { - return "", fmt.Errorf("gsettings/dconf get failed for %s %s: %w", schema, key, err) - } - return strings.TrimSpace(string(out)), nil -} - -// GsettingsSet writes a gsettings value, falling back to dconf write. -func GsettingsSet(schema, key, value string) error { - if err := exec.Command("gsettings", "set", schema, key, value).Run(); err == nil { - return nil - } - return exec.Command("dconf", "write", dconfPath(schema, key), "'"+value+"'").Run() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/math.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/math.go deleted file mode 100644 index 408af9f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/math.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -import "golang.org/x/exp/constraints" - -func Clamp[T constraints.Ordered](val, min, max T) T { - if val < min { - return min - } - if val > max { - return max - } - return val -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths.go deleted file mode 100644 index 053097e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths.go +++ /dev/null @@ -1,70 +0,0 @@ -package utils - -import ( - "os" - "path/filepath" - "strings" -) - -func XDGStateHome() string { - if dir := os.Getenv("XDG_STATE_HOME"); dir != "" { - return dir - } - home, _ := os.UserHomeDir() - return filepath.Join(home, ".local", "state") -} - -func XDGDataHome() string { - if dir := os.Getenv("XDG_DATA_HOME"); dir != "" { - return dir - } - home, _ := os.UserHomeDir() - return filepath.Join(home, ".local", "share") -} - -func XDGCacheHome() string { - if dir, err := os.UserCacheDir(); err == nil { - return dir - } - home, _ := os.UserHomeDir() - return filepath.Join(home, ".cache") -} - -func XDGConfigHome() string { - if dir, err := os.UserConfigDir(); err == nil { - return dir - } - home, _ := os.UserHomeDir() - return filepath.Join(home, ".config") -} - -func EmacsConfigDir() string { - home, _ := os.UserHomeDir() - - emacsD := filepath.Join(home, ".emacs.d") - if info, err := os.Stat(emacsD); err == nil && info.IsDir() { - return emacsD - } - - xdgEmacs := filepath.Join(XDGConfigHome(), "emacs") - if info, err := os.Stat(xdgEmacs); err == nil && info.IsDir() { - return xdgEmacs - } - - return "" -} - -func ExpandPath(path string) (string, error) { - expanded := os.ExpandEnv(path) - expanded = filepath.Clean(expanded) - - if strings.HasPrefix(expanded, "~") { - home, err := os.UserHomeDir() - if err != nil { - return "", err - } - expanded = filepath.Join(home, expanded[1:]) - } - - return expanded, nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths_test.go deleted file mode 100644 index 964f2a7..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package utils - -import ( - "os" - "path/filepath" - "testing" -) - -func TestExpandPathTilde(t *testing.T) { - home, err := os.UserHomeDir() - if err != nil { - t.Skip("no home directory") - } - result, err := ExpandPath("~/test") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expected := filepath.Join(home, "test") - if result != expected { - t.Errorf("expected %s, got %s", expected, result) - } -} - -func TestExpandPathEnvVar(t *testing.T) { - t.Setenv("TEST_PATH_VAR", "/custom/path") - result, err := ExpandPath("$TEST_PATH_VAR/subdir") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if result != "/custom/path/subdir" { - t.Errorf("expected /custom/path/subdir, got %s", result) - } -} - -func TestExpandPathAbsolute(t *testing.T) { - result, err := ExpandPath("/absolute/path") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if result != "/absolute/path" { - t.Errorf("expected /absolute/path, got %s", result) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices.go deleted file mode 100644 index b636ddc..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices.go +++ /dev/null @@ -1,56 +0,0 @@ -package utils - -func Filter[T any](items []T, predicate func(T) bool) []T { - var result []T - for _, item := range items { - if predicate(item) { - result = append(result, item) - } - } - return result -} - -func Find[T any](items []T, predicate func(T) bool) (T, bool) { - for _, item := range items { - if predicate(item) { - return item, true - } - } - var zero T - return zero, false -} - -func Map[T, U any](items []T, transform func(T) U) []U { - result := make([]U, len(items)) - for i, item := range items { - result[i] = transform(item) - } - return result -} - -func Contains[T comparable](items []T, target T) bool { - for _, item := range items { - if item == target { - return true - } - } - return false -} - -func Any[T any](items []T, predicate func(T) bool) bool { - for _, item := range items { - if predicate(item) { - return true - } - } - return false -} - -func All[T any](items []T, predicate func(T) bool) bool { - for _, item := range items { - if !predicate(item) { - return false - } - } - return true -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices_test.go deleted file mode 100644 index 8edcee5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package utils - -import ( - "testing" -) - -func TestFilter(t *testing.T) { - nums := []int{1, 2, 3, 4, 5} - evens := Filter(nums, func(n int) bool { return n%2 == 0 }) - if len(evens) != 2 || evens[0] != 2 || evens[1] != 4 { - t.Errorf("expected [2, 4], got %v", evens) - } -} - -func TestFilterEmpty(t *testing.T) { - result := Filter([]int{1, 2, 3}, func(n int) bool { return n > 10 }) - if len(result) != 0 { - t.Errorf("expected empty slice, got %v", result) - } -} - -func TestFind(t *testing.T) { - nums := []int{1, 2, 3, 4, 5} - val, found := Find(nums, func(n int) bool { return n == 3 }) - if !found || val != 3 { - t.Errorf("expected 3, got %v (found=%v)", val, found) - } -} - -func TestFindNotFound(t *testing.T) { - nums := []int{1, 2, 3} - val, found := Find(nums, func(n int) bool { return n == 99 }) - if found || val != 0 { - t.Errorf("expected zero value not found, got %v (found=%v)", val, found) - } -} - -func TestMap(t *testing.T) { - nums := []int{1, 2, 3} - doubled := Map(nums, func(n int) int { return n * 2 }) - if len(doubled) != 3 || doubled[0] != 2 || doubled[1] != 4 || doubled[2] != 6 { - t.Errorf("expected [2, 4, 6], got %v", doubled) - } -} - -func TestMapTypeConversion(t *testing.T) { - nums := []int{1, 2, 3} - strs := Map(nums, func(n int) string { return string(rune('a' + n - 1)) }) - if strs[0] != "a" || strs[1] != "b" || strs[2] != "c" { - t.Errorf("expected [a, b, c], got %v", strs) - } -} - -func TestContains(t *testing.T) { - nums := []int{1, 2, 3} - if !Contains(nums, 2) { - t.Error("expected to contain 2") - } - if Contains(nums, 99) { - t.Error("expected not to contain 99") - } -} - -func TestAny(t *testing.T) { - nums := []int{1, 2, 3, 4, 5} - if !Any(nums, func(n int) bool { return n > 4 }) { - t.Error("expected any > 4") - } - if Any(nums, func(n int) bool { return n > 10 }) { - t.Error("expected none > 10") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/version/version.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/version/version.go deleted file mode 100644 index db8636e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/version/version.go +++ /dev/null @@ -1,268 +0,0 @@ -package version - -import ( - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" -) - -type VersionInfo struct { - Current string - Latest string - IsGit bool - IsBranch bool - IsTag bool - HasUpdate bool -} - -// VersionFetcher is an interface for fetching version information -type VersionFetcher interface { - GetCurrentVersion(dmsPath string) (string, error) - GetLatestVersion(dmsPath string) (string, error) -} - -// DefaultVersionFetcher is the default implementation that uses git/curl -type DefaultVersionFetcher struct{} - -func (d *DefaultVersionFetcher) GetCurrentVersion(dmsPath string) (string, error) { - if _, err := os.Stat(filepath.Join(dmsPath, ".git")); err == nil { - originalDir, err := os.Getwd() - if err != nil { - return "", err - } - defer func() { - if err := os.Chdir(originalDir); err != nil { - log.Warnf("failed to change back to original directory: %v", err) - } - }() - - if err := os.Chdir(dmsPath); err != nil { - return "", fmt.Errorf("failed to change to DMS directory: %w", err) - } - - tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD") - if tagOutput, err := tagCmd.Output(); err == nil { - return strings.TrimSpace(string(tagOutput)), nil - } - - branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") - if branchOutput, err := branchCmd.Output(); err == nil { - branch := strings.TrimSpace(string(branchOutput)) - revCmd := exec.Command("git", "rev-parse", "--short", "HEAD") - if revOutput, err := revCmd.Output(); err == nil { - rev := strings.TrimSpace(string(revOutput)) - return fmt.Sprintf("%s@%s", branch, rev), nil - } - return branch, nil - } - } - - cmd := exec.Command("dms", "--version") - if output, err := cmd.Output(); err == nil { - return strings.TrimSpace(string(output)), nil - } - - return "unknown", nil -} - -func (d *DefaultVersionFetcher) GetLatestVersion(dmsPath string) (string, error) { - if _, err := os.Stat(filepath.Join(dmsPath, ".git")); err == nil { - originalDir, err := os.Getwd() - if err != nil { - return "", err - } - defer func() { - if err := os.Chdir(originalDir); err != nil { - log.Warnf("failed to change back to original directory: %v", err) - } - }() - - if err := os.Chdir(dmsPath); err != nil { - return "", fmt.Errorf("failed to change to DMS directory: %w", err) - } - - currentRefCmd := exec.Command("git", "symbolic-ref", "-q", "HEAD") - currentRefOutput, _ := currentRefCmd.Output() - onBranch := len(currentRefOutput) > 0 - - if !onBranch { - tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD") - if _, err := tagCmd.Output(); err == nil { - // Add timeout to git fetch to prevent hanging - fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", "--tags", "--quiet") - if err := fetchCmd.Run(); err != nil { - log.Debugf("git fetch tags failed (continuing with local tags): %v", err) - } - - latestTagCmd := exec.Command("git", "tag", "-l", "v*", "--sort=-version:refname") - latestTagOutput, err := latestTagCmd.Output() - if err != nil { - return "", fmt.Errorf("failed to get latest tag: %w", err) - } - - tags := strings.Split(strings.TrimSpace(string(latestTagOutput)), "\n") - if len(tags) == 0 || tags[0] == "" { - return "", fmt.Errorf("no version tags found") - } - return tags[0], nil - } - } else { - branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") - branchOutput, err := branchCmd.Output() - if err != nil { - return "", fmt.Errorf("failed to get current branch: %w", err) - } - currentBranch := strings.TrimSpace(string(branchOutput)) - - // Add timeout to git fetch to prevent hanging - fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", currentBranch, "--quiet") - if err := fetchCmd.Run(); err != nil { - log.Debugf("git fetch branch failed (continuing with local ref): %v", err) - } - - remoteRevCmd := exec.Command("git", "rev-parse", "--short", fmt.Sprintf("origin/%s", currentBranch)) - remoteRevOutput, err := remoteRevCmd.Output() - if err != nil { - return "", fmt.Errorf("failed to get remote revision: %w", err) - } - remoteRev := strings.TrimSpace(string(remoteRevOutput)) - return fmt.Sprintf("%s@%s", currentBranch, remoteRev), nil - } - } - - // Add timeout to prevent hanging when GitHub is down - cmd := exec.Command("curl", "-s", "--max-time", "5", "https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest") - output, err := cmd.Output() - if err != nil { - return "", fmt.Errorf("failed to fetch latest release: %w", err) - } - - var result struct { - TagName string `json:"tag_name"` - } - if err := json.Unmarshal(output, &result); err != nil { - for _, line := range strings.Split(string(output), "\n") { - if strings.Contains(line, "\"tag_name\"") { - parts := strings.Split(line, "\"") - if len(parts) >= 4 { - return parts[3], nil - } - } - } - return "", fmt.Errorf("failed to parse latest version: %w", err) - } - - return result.TagName, nil -} - -// defaultFetcher is used by the public functions -var defaultFetcher VersionFetcher = &DefaultVersionFetcher{} - -func GetCurrentDMSVersion() (string, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("failed to get home directory: %w", err) - } - - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - if _, err := os.Stat(dmsPath); os.IsNotExist(err) { - return "", fmt.Errorf("DMS not installed") - } - - return defaultFetcher.GetCurrentVersion(dmsPath) -} - -func GetLatestDMSVersion() (string, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("failed to get home directory: %w", err) - } - - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - return defaultFetcher.GetLatestVersion(dmsPath) -} - -func GetDMSVersionInfo() (*VersionInfo, error) { - return GetDMSVersionInfoWithFetcher(defaultFetcher) -} - -func GetDMSVersionInfoWithFetcher(fetcher VersionFetcher) (*VersionInfo, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return nil, fmt.Errorf("failed to get home directory: %w", err) - } - - dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms") - if _, err := os.Stat(dmsPath); os.IsNotExist(err) { - return nil, fmt.Errorf("DMS not installed") - } - - current, err := fetcher.GetCurrentVersion(dmsPath) - if err != nil { - return nil, err - } - - latest, err := fetcher.GetLatestVersion(dmsPath) - if err != nil { - return nil, fmt.Errorf("failed to get latest version: %w", err) - } - - info := &VersionInfo{ - Current: current, - Latest: latest, - IsGit: strings.Contains(current, "@"), - IsBranch: strings.Contains(current, "@"), - IsTag: !strings.Contains(current, "@") && strings.HasPrefix(current, "v"), - } - - if info.IsBranch { - parts := strings.Split(current, "@") - latestParts := strings.Split(latest, "@") - if len(parts) == 2 && len(latestParts) == 2 { - info.HasUpdate = parts[1] != latestParts[1] - } - } else if info.IsTag { - info.HasUpdate = current != latest - } else { - info.HasUpdate = false - } - - return info, nil -} - -func CompareVersions(v1, v2 string) int { - v1 = strings.TrimPrefix(v1, "v") - v2 = strings.TrimPrefix(v2, "v") - - parts1 := strings.Split(v1, ".") - parts2 := strings.Split(v2, ".") - - maxLen := len(parts1) - if len(parts2) > maxLen { - maxLen = len(parts2) - } - - for i := 0; i < maxLen; i++ { - var p1, p2 int - if i < len(parts1) { - fmt.Sscanf(parts1[i], "%d", &p1) //nolint:errcheck - } - if i < len(parts2) { - fmt.Sscanf(parts2[i], "%d", &p2) //nolint:errcheck - } - - if p1 < p2 { - return -1 - } - if p1 > p2 { - return 1 - } - } - - return 0 -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/version/version_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/version/version_test.go deleted file mode 100644 index 1e424c1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/version/version_test.go +++ /dev/null @@ -1,362 +0,0 @@ -package version - -import ( - "os" - "os/exec" - "path/filepath" - "testing" - - mocks_version "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/version" - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" -) - -func TestCompareVersions(t *testing.T) { - tests := []struct { - v1 string - v2 string - expected int - }{ - {"v0.1.0", "v0.1.0", 0}, - {"v0.1.0", "v0.1.1", -1}, - {"v0.1.1", "v0.1.0", 1}, - {"v0.1.10", "v0.1.2", 1}, - {"v0.2.0", "v0.1.9", 1}, - {"0.1.0", "0.1.0", 0}, - {"1.0.0", "v1.0.0", 0}, - {"v1.2.3", "v1.2.4", -1}, - {"v2.0.0", "v1.9.9", 1}, - } - - for _, tt := range tests { - result := CompareVersions(tt.v1, tt.v2) - if result != tt.expected { - t.Errorf("CompareVersions(%q, %q) = %d; want %d", tt.v1, tt.v2, result, tt.expected) - } - } -} - -func TestGetDMSVersionInfo_Structure(t *testing.T) { - // Create a temp directory with a fake DMS installation - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0o755) - - // Create a .git directory to simulate git installation - os.MkdirAll(filepath.Join(dmsPath, ".git"), 0o755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - // Create mock fetcher - mockFetcher := mocks_version.NewMockVersionFetcher(t) - mockFetcher.EXPECT().GetCurrentVersion(dmsPath).Return("v0.1.0", nil) - mockFetcher.EXPECT().GetLatestVersion(dmsPath).Return("v0.1.1", nil) - - info, err := GetDMSVersionInfoWithFetcher(mockFetcher) - if err != nil { - t.Fatalf("GetDMSVersionInfoWithFetcher() failed: %v", err) - } - - if info == nil { - t.Fatal("GetDMSVersionInfoWithFetcher() returned nil") - } - - if info.Current != "v0.1.0" { - t.Errorf("Current version = %s, expected v0.1.0", info.Current) - } - - if info.Latest != "v0.1.1" { - t.Errorf("Latest version = %s, expected v0.1.1", info.Latest) - } - - if !info.HasUpdate { - t.Error("HasUpdate should be true when current != latest") - } - - if !info.IsTag { - t.Error("IsTag should be true for v0.1.0") - } - - t.Logf("Current: %s, Latest: %s, HasUpdate: %v", info.Current, info.Latest, info.HasUpdate) -} - -func TestGetDMSVersionInfo_BranchVersion(t *testing.T) { - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0o755) - os.MkdirAll(filepath.Join(dmsPath, ".git"), 0o755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - mockFetcher := mocks_version.NewMockVersionFetcher(t) - mockFetcher.EXPECT().GetCurrentVersion(dmsPath).Return("master@abc1234", nil) - mockFetcher.EXPECT().GetLatestVersion(dmsPath).Return("master@def5678", nil) - - info, err := GetDMSVersionInfoWithFetcher(mockFetcher) - if err != nil { - t.Fatalf("GetDMSVersionInfoWithFetcher() failed: %v", err) - } - - if !info.IsBranch { - t.Error("IsBranch should be true for branch@commit format") - } - - if !info.IsGit { - t.Error("IsGit should be true for branch@commit format") - } - - if !info.HasUpdate { - t.Error("HasUpdate should be true when commits differ") - } -} - -func TestGetDMSVersionInfo_NoUpdate(t *testing.T) { - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0o755) - os.MkdirAll(filepath.Join(dmsPath, ".git"), 0o755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - mockFetcher := mocks_version.NewMockVersionFetcher(t) - mockFetcher.EXPECT().GetCurrentVersion(dmsPath).Return("v0.1.0", nil) - mockFetcher.EXPECT().GetLatestVersion(dmsPath).Return("v0.1.0", nil) - - info, err := GetDMSVersionInfoWithFetcher(mockFetcher) - if err != nil { - t.Fatalf("GetDMSVersionInfoWithFetcher() failed: %v", err) - } - - if info.HasUpdate { - t.Error("HasUpdate should be false when current == latest") - } -} - -func TestGetCurrentDMSVersion_NotInstalled(t *testing.T) { - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - - tempDir := t.TempDir() - os.Setenv("HOME", tempDir) - - _, err := GetCurrentDMSVersion() - if err == nil { - t.Error("Expected error when DMS not installed, got nil") - } -} - -func TestGetCurrentDMSVersion_GitTag(t *testing.T) { - if !utils.CommandExists("git") { - t.Skip("git not available") - } - - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0o755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - exec.Command("git", "init", dmsPath).Run() - exec.Command("git", "-C", dmsPath, "config", "user.email", "test@test.com").Run() - exec.Command("git", "-C", dmsPath, "config", "user.name", "Test User").Run() - - testFile := filepath.Join(dmsPath, "test.txt") - os.WriteFile(testFile, []byte("test"), 0o644) - exec.Command("git", "-C", dmsPath, "add", ".").Run() - exec.Command("git", "-C", dmsPath, "commit", "-m", "initial").Run() - exec.Command("git", "-C", dmsPath, "tag", "v0.1.0").Run() - - version, err := GetCurrentDMSVersion() - if err != nil { - t.Fatalf("GetCurrentDMSVersion() failed: %v", err) - } - - if version != "v0.1.0" { - t.Errorf("Expected version v0.1.0, got %s", version) - } -} - -func TestGetCurrentDMSVersion_GitBranch(t *testing.T) { - if !utils.CommandExists("git") { - t.Skip("git not available") - } - - tempDir := t.TempDir() - dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms") - os.MkdirAll(dmsPath, 0o755) - - originalHome := os.Getenv("HOME") - defer os.Setenv("HOME", originalHome) - os.Setenv("HOME", tempDir) - - exec.Command("git", "init", dmsPath).Run() - exec.Command("git", "-C", dmsPath, "config", "user.email", "test@test.com").Run() - exec.Command("git", "-C", dmsPath, "config", "user.name", "Test User").Run() - exec.Command("git", "-C", dmsPath, "checkout", "-b", "master").Run() - - testFile := filepath.Join(dmsPath, "test.txt") - os.WriteFile(testFile, []byte("test"), 0o644) - exec.Command("git", "-C", dmsPath, "add", ".").Run() - exec.Command("git", "-C", dmsPath, "commit", "-m", "initial").Run() - - version, err := GetCurrentDMSVersion() - if err != nil { - t.Fatalf("GetCurrentDMSVersion() failed: %v", err) - } - - if version == "" { - t.Error("Expected non-empty version") - } - - if len(version) < 7 { - t.Errorf("Expected version with branch@commit format, got %s", version) - } -} - -func TestVersionInfo_IsGit(t *testing.T) { - tests := []struct { - current string - isGit bool - isBranch bool - isTag bool - }{ - {"v0.1.0", false, false, true}, - {"master@abc1234", true, true, false}, - {"dev@def5678", true, true, false}, - {"v0.2.0", false, false, true}, - {"unknown", false, false, false}, - } - - for _, tt := range tests { - info := &VersionInfo{ - IsGit: tt.isGit, - IsBranch: tt.isBranch, - IsTag: tt.isTag, - } - - actualIsGit := len(tt.current) > 0 && tt.current[0] != 'v' && tt.current != "unknown" - actualIsBranch := len(tt.current) > 0 && tt.current[0] != 'v' - actualIsTag := len(tt.current) > 0 && tt.current[0] == 'v' - - if tt.current == "unknown" { - actualIsGit = false - actualIsBranch = false - actualIsTag = false - } - - if info.IsGit != tt.isGit { - t.Errorf("For %s: IsGit = %v; want %v", tt.current, info.IsGit, tt.isGit) - } - if info.IsBranch != tt.isBranch { - t.Errorf("For %s: IsBranch = %v; want %v", tt.current, info.IsBranch, tt.isBranch) - } - if info.IsTag != tt.isTag { - t.Errorf("For %s: IsTag = %v; want %v", tt.current, info.IsTag, tt.isTag) - } - - _ = actualIsGit - _ = actualIsBranch - _ = actualIsTag - } -} - -func TestVersionInfo_HasUpdate_Branch(t *testing.T) { - tests := []struct { - current string - latest string - hasUpdate bool - }{ - {"master@abc1234", "master@abc1234", false}, - {"master@abc1234", "master@def5678", true}, - {"dev@abc1234", "dev@abc1234", false}, - {"dev@old1234", "dev@new5678", true}, - } - - for _, tt := range tests { - info := &VersionInfo{ - HasUpdate: tt.hasUpdate, - } - - if info.HasUpdate != tt.hasUpdate { - t.Errorf("For current=%s, latest=%s: HasUpdate = %v; want %v", - tt.current, tt.latest, info.HasUpdate, tt.hasUpdate) - } - } -} - -func TestVersionInfo_HasUpdate_Tag(t *testing.T) { - tests := []struct { - current string - latest string - hasUpdate bool - }{ - {"v0.1.0", "v0.1.0", false}, - {"v0.1.0", "v0.1.1", true}, - {"v0.1.5", "v0.1.5", false}, - {"v0.1.9", "v0.2.0", true}, - } - - for _, tt := range tests { - info := &VersionInfo{ - HasUpdate: tt.hasUpdate, - } - - if info.HasUpdate != tt.hasUpdate { - t.Errorf("For current=%s, latest=%s: HasUpdate = %v; want %v", - tt.current, tt.latest, info.HasUpdate, tt.hasUpdate) - } - } -} - -func TestGetLatestDMSVersion_FallbackParsing(t *testing.T) { - jsonResponse := `{ - "tag_name": "v0.1.17", - "name": "Release v0.1.17" - }` - - lines := []string{ - ` "tag_name": "v0.1.17",`, - ` "name": "Release v0.1.17"`, - } - - for _, line := range lines { - if len(line) > 0 && line[0:15] == ` "tag_name": "` { - parts := []string{"", "", "", "v0.1.17"} - version := parts[3] - if version != "v0.1.17" { - t.Errorf("Failed to parse version from line: %s", line) - } - } - } - - _ = jsonResponse -} - -func TestCompareVersions_EdgeCases(t *testing.T) { - tests := []struct { - v1 string - v2 string - expected int - }{ - {"", "", 0}, - {"v1", "v1", 0}, - {"v1.0", "v1", 0}, - {"v1.0.0", "v1.0", 0}, - {"v1.0.1", "v1.0", 1}, - {"v1", "v1.0.1", -1}, - } - - for _, tt := range tests { - result := CompareVersions(tt.v1, tt.v2) - if result != tt.expected { - t.Errorf("CompareVersions(%q, %q) = %d; want %d", tt.v1, tt.v2, result, tt.expected) - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/wayland/client/helpers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/wayland/client/helpers.go deleted file mode 100644 index 9075cd3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/wayland/client/helpers.go +++ /dev/null @@ -1,26 +0,0 @@ -package client - -import wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - -func Roundtrip(display *wlclient.Display, ctx *wlclient.Context) error { - callback, err := display.Sync() - if err != nil { - return err - } - - done := make(chan struct{}) - callback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) { - close(done) - }) - - for { - select { - case <-done: - return nil - default: - if err := ctx.Dispatch(); err != nil { - return err - } - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/wayland/shm/buffer.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/wayland/shm/buffer.go deleted file mode 100644 index 9cfee20..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/wayland/shm/buffer.go +++ /dev/null @@ -1,298 +0,0 @@ -package shm - -import ( - "fmt" - - "golang.org/x/sys/unix" -) - -type PixelFormat uint32 - -const ( - FormatARGB8888 PixelFormat = 0 - FormatXRGB8888 PixelFormat = 1 - FormatABGR8888 PixelFormat = 0x34324241 - FormatXBGR8888 PixelFormat = 0x34324258 - FormatRGB888 PixelFormat = 0x34324752 - FormatBGR888 PixelFormat = 0x34324742 -) - -func (f PixelFormat) BytesPerPixel() int { - switch f { - case FormatRGB888, FormatBGR888: - return 3 - default: - return 4 - } -} - -func (f PixelFormat) Is24Bit() bool { - return f == FormatRGB888 || f == FormatBGR888 -} - -type Buffer struct { - fd int - data []byte - size int - Width int - Height int - Stride int - Format PixelFormat -} - -func CreateBuffer(width, height, stride int) (*Buffer, error) { - size := stride * height - - fd, err := unix.MemfdCreate("dms-shm", 0) - if err != nil { - return nil, fmt.Errorf("memfd_create: %w", err) - } - - if err := unix.Ftruncate(fd, int64(size)); err != nil { - unix.Close(fd) - return nil, fmt.Errorf("ftruncate: %w", err) - } - - data, err := unix.Mmap(fd, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) - if err != nil { - unix.Close(fd) - return nil, fmt.Errorf("mmap: %w", err) - } - - return &Buffer{ - fd: fd, - data: data, - size: size, - Width: width, - Height: height, - Stride: stride, - }, nil -} - -func (b *Buffer) Fd() int { return b.fd } -func (b *Buffer) Size() int { return b.size } -func (b *Buffer) Data() []byte { return b.data } - -func (b *Buffer) Close() error { - var firstErr error - - if b.data != nil { - if err := unix.Munmap(b.data); err != nil && firstErr == nil { - firstErr = fmt.Errorf("munmap: %w", err) - } - b.data = nil - } - - if b.fd >= 0 { - if err := unix.Close(b.fd); err != nil && firstErr == nil { - firstErr = fmt.Errorf("close: %w", err) - } - b.fd = -1 - } - - return firstErr -} - -func (b *Buffer) ConvertTo32Bit(srcFormat PixelFormat) (*Buffer, PixelFormat, error) { - if !srcFormat.Is24Bit() { - return b, srcFormat, nil - } - - dstFormat := FormatXRGB8888 - dstStride := b.Width * 4 - - dst, err := CreateBuffer(b.Width, b.Height, dstStride) - if err != nil { - return nil, srcFormat, err - } - dst.Format = dstFormat - - srcData := b.data - dstData := dst.data - - // DRM format names are counterintuitive on little-endian: - // RGB888 memory layout: B, G, R (name is logical order, not memory) - // BGR888 memory layout: R, G, B - isBGRMemory := srcFormat == FormatRGB888 - - for y := 0; y < b.Height; y++ { - srcRow := y * b.Stride - dstRow := y * dstStride - for x := 0; x < b.Width; x++ { - si := srcRow + x*3 - di := dstRow + x*4 - if isBGRMemory { - // RGB888: src memory is B,G,R -> dst XRGB8888 memory B,G,R,X - dstData[di+0] = srcData[si+0] - dstData[di+1] = srcData[si+1] - dstData[di+2] = srcData[si+2] - } else { - // BGR888: src memory is R,G,B -> dst XRGB8888 memory B,G,R,X - dstData[di+0] = srcData[si+2] - dstData[di+1] = srcData[si+1] - dstData[di+2] = srcData[si+0] - } - dstData[di+3] = 0xFF - } - } - - return dst, dstFormat, nil -} - -func (b *Buffer) GetPixelRGBA(x, y int) (r, g, b2, a uint8) { - if x < 0 || x >= b.Width || y < 0 || y >= b.Height { - return - } - - off := y*b.Stride + x*4 - if off+3 >= len(b.data) { - return - } - - switch b.Format { - case FormatXBGR8888, FormatABGR8888: - return b.data[off], b.data[off+1], b.data[off+2], 0xFF - default: - return b.data[off+2], b.data[off+1], b.data[off], 0xFF - } -} - -func (b *Buffer) GetPixelBGRA(x, y int) (b2, g, r, a uint8) { - if x < 0 || x >= b.Width || y < 0 || y >= b.Height { - return - } - - off := y*b.Stride + x*4 - if off+3 >= len(b.data) { - return - } - - return b.data[off], b.data[off+1], b.data[off+2], b.data[off+3] -} - -func (b *Buffer) ConvertBGRAtoRGBA() { - for y := 0; y < b.Height; y++ { - rowOff := y * b.Stride - for x := 0; x < b.Width; x++ { - off := rowOff + x*4 - if off+3 >= len(b.data) { - continue - } - b.data[off], b.data[off+2] = b.data[off+2], b.data[off] - } - } -} - -func (b *Buffer) FlipVertical() { - tmp := make([]byte, b.Stride) - for y := 0; y < b.Height/2; y++ { - topOff := y * b.Stride - botOff := (b.Height - 1 - y) * b.Stride - copy(tmp, b.data[topOff:topOff+b.Stride]) - copy(b.data[topOff:topOff+b.Stride], b.data[botOff:botOff+b.Stride]) - copy(b.data[botOff:botOff+b.Stride], tmp) - } -} - -func (b *Buffer) Clear() { - for i := range b.data { - b.data[i] = 0 - } -} - -func (b *Buffer) CopyFrom(src *Buffer) { - copy(b.data, src.data) -} - -const ( - TransformNormal = 0 - Transform90 = 1 - Transform180 = 2 - Transform270 = 3 - TransformFlipped = 4 - TransformFlipped90 = 5 - TransformFlipped180 = 6 - TransformFlipped270 = 7 -) - -func (b *Buffer) ApplyTransform(transform int32) (*Buffer, error) { - if transform == TransformNormal { - return b, nil - } - - var newW, newH int - switch transform { - case Transform90, Transform270, TransformFlipped90, TransformFlipped270: - newW, newH = b.Height, b.Width - default: - newW, newH = b.Width, b.Height - } - - newBuf, err := CreateBuffer(newW, newH, newW*4) - if err != nil { - return nil, err - } - newBuf.Format = b.Format - - srcData := b.data - dstData := newBuf.data - - for sy := 0; sy < b.Height; sy++ { - for sx := 0; sx < b.Width; sx++ { - var dx, dy int - - switch transform { - case Transform90: // 90° CCW - dx = sy - dy = b.Width - 1 - sx - case Transform180: - dx = b.Width - 1 - sx - dy = b.Height - 1 - sy - case Transform270: // 270° CCW = 90° CW - dx = b.Height - 1 - sy - dy = sx - case TransformFlipped: - dx = b.Width - 1 - sx - dy = sy - case TransformFlipped90: - dx = sy - dy = sx - case TransformFlipped180: - dx = sx - dy = b.Height - 1 - sy - case TransformFlipped270: - dx = b.Height - 1 - sy - dy = b.Width - 1 - sx - default: - dx, dy = sx, sy - } - - si := sy*b.Stride + sx*4 - di := dy*newBuf.Stride + dx*4 - - if si+3 < len(srcData) && di+3 < len(dstData) { - dstData[di+0] = srcData[si+0] - dstData[di+1] = srcData[si+1] - dstData[di+2] = srcData[si+2] - dstData[di+3] = srcData[si+3] - } - } - } - - return newBuf, nil -} - -func InverseTransform(transform int32) int32 { - switch transform { - case Transform90: - return Transform270 - case Transform270: - return Transform90 - case TransformFlipped90: - return TransformFlipped270 - case TransformFlipped270: - return TransformFlipped90 - default: - return transform - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser.go deleted file mode 100644 index 918c4f9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser.go +++ /dev/null @@ -1,658 +0,0 @@ -package providers - -import ( - "fmt" - "os" - "path/filepath" - "regexp" - "strconv" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/AvengeMedia/DankMaterialShell/core/internal/windowrules" -) - -type HyprlandWindowRule struct { - MatchClass string - MatchTitle string - MatchXWayland *bool - MatchFloating *bool - MatchFullscreen *bool - MatchPinned *bool - MatchInitialised *bool - Rule string - Value string - Source string - RawLine string -} - -type HyprlandRulesParser struct { - configDir string - processedFiles map[string]bool - rules []HyprlandWindowRule - currentSource string - dmsRulesExists bool - dmsRulesIncluded bool - includeCount int - dmsIncludePos int - rulesAfterDMS int - dmsProcessed bool -} - -func NewHyprlandRulesParser(configDir string) *HyprlandRulesParser { - return &HyprlandRulesParser{ - configDir: configDir, - processedFiles: make(map[string]bool), - rules: []HyprlandWindowRule{}, - dmsIncludePos: -1, - } -} - -func (p *HyprlandRulesParser) Parse() ([]HyprlandWindowRule, error) { - expandedDir, err := utils.ExpandPath(p.configDir) - if err != nil { - return nil, err - } - - dmsRulesPath := filepath.Join(expandedDir, "dms", "windowrules.conf") - if _, err := os.Stat(dmsRulesPath); err == nil { - p.dmsRulesExists = true - } - - mainConfig := filepath.Join(expandedDir, "hyprland.conf") - if err := p.parseFile(mainConfig); err != nil { - return nil, err - } - - if p.dmsRulesExists && !p.dmsProcessed { - p.parseDMSRulesDirectly(dmsRulesPath) - } - - return p.rules, nil -} - -func (p *HyprlandRulesParser) parseDMSRulesDirectly(dmsRulesPath string) { - data, err := os.ReadFile(dmsRulesPath) - if err != nil { - return - } - - prevSource := p.currentSource - p.currentSource = dmsRulesPath - - lines := strings.Split(string(data), "\n") - for _, line := range lines { - p.parseLine(line) - } - - p.currentSource = prevSource - p.dmsProcessed = true -} - -func (p *HyprlandRulesParser) parseFile(filePath string) error { - absPath, err := filepath.Abs(filePath) - if err != nil { - return err - } - - if p.processedFiles[absPath] { - return nil - } - p.processedFiles[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return nil - } - - prevSource := p.currentSource - p.currentSource = absPath - - lines := strings.Split(string(data), "\n") - for _, line := range lines { - trimmed := strings.TrimSpace(line) - - if strings.HasPrefix(trimmed, "source") { - p.handleSource(trimmed, filepath.Dir(absPath)) - continue - } - - p.parseLine(line) - } - - p.currentSource = prevSource - return nil -} - -func (p *HyprlandRulesParser) handleSource(line string, baseDir string) { - parts := strings.SplitN(line, "=", 2) - if len(parts) < 2 { - return - } - - sourcePath := strings.TrimSpace(parts[1]) - isDMSSource := sourcePath == "dms/windowrules.conf" || strings.HasSuffix(sourcePath, "/dms/windowrules.conf") - - p.includeCount++ - if isDMSSource { - p.dmsRulesIncluded = true - p.dmsIncludePos = p.includeCount - p.dmsProcessed = true - } - - fullPath := sourcePath - if !filepath.IsAbs(sourcePath) { - fullPath = filepath.Join(baseDir, sourcePath) - } - - expanded, err := utils.ExpandPath(fullPath) - if err != nil { - return - } - - _ = p.parseFile(expanded) -} - -func (p *HyprlandRulesParser) parseLine(line string) { - trimmed := strings.TrimSpace(line) - - if strings.HasPrefix(trimmed, "windowrule") { - rule := p.parseWindowRuleLine(trimmed) - if rule != nil { - rule.Source = p.currentSource - p.rules = append(p.rules, *rule) - } - } -} - -var windowRuleV2Regex = regexp.MustCompile(`^windowrulev?2?\s*=\s*(.+)$`) - -func (p *HyprlandRulesParser) parseWindowRuleLine(line string) *HyprlandWindowRule { - matches := windowRuleV2Regex.FindStringSubmatch(line) - if len(matches) < 2 { - return nil - } - - content := strings.TrimSpace(matches[1]) - isV2 := strings.HasPrefix(line, "windowrulev2") - - rule := &HyprlandWindowRule{ - RawLine: line, - } - - if isV2 { - p.parseWindowRuleV2(content, rule) - } else { - p.parseWindowRuleV1(content, rule) - } - - return rule -} - -func (p *HyprlandRulesParser) parseWindowRuleV1(content string, rule *HyprlandWindowRule) { - parts := strings.SplitN(content, ",", 2) - if len(parts) < 2 { - return - } - - rule.Rule = strings.TrimSpace(parts[0]) - rule.MatchClass = strings.TrimSpace(parts[1]) -} - -func (p *HyprlandRulesParser) parseWindowRuleV2(content string, rule *HyprlandWindowRule) { - parts := strings.SplitN(content, ",", 2) - if len(parts) < 2 { - return - } - - ruleAndValue := strings.TrimSpace(parts[0]) - matchPart := strings.TrimSpace(parts[1]) - - if idx := strings.Index(ruleAndValue, " "); idx > 0 { - rule.Rule = ruleAndValue[:idx] - rule.Value = strings.TrimSpace(ruleAndValue[idx+1:]) - } else { - rule.Rule = ruleAndValue - } - - matchPairs := strings.Split(matchPart, ",") - for _, pair := range matchPairs { - pair = strings.TrimSpace(pair) - if colonIdx := strings.Index(pair, ":"); colonIdx > 0 { - key := strings.TrimSpace(pair[:colonIdx]) - value := strings.TrimSpace(pair[colonIdx+1:]) - - switch key { - case "class": - rule.MatchClass = value - case "title": - rule.MatchTitle = value - case "xwayland": - b := value == "1" || value == "true" - rule.MatchXWayland = &b - case "floating": - b := value == "1" || value == "true" - rule.MatchFloating = &b - case "fullscreen": - b := value == "1" || value == "true" - rule.MatchFullscreen = &b - case "pinned": - b := value == "1" || value == "true" - rule.MatchPinned = &b - case "initialised", "initialized": - b := value == "1" || value == "true" - rule.MatchInitialised = &b - } - } - } -} - -func (p *HyprlandRulesParser) HasDMSRulesIncluded() bool { - return p.dmsRulesIncluded -} - -func (p *HyprlandRulesParser) buildDMSStatus() *windowrules.DMSRulesStatus { - status := &windowrules.DMSRulesStatus{ - Exists: p.dmsRulesExists, - Included: p.dmsRulesIncluded, - IncludePosition: p.dmsIncludePos, - TotalIncludes: p.includeCount, - RulesAfterDMS: p.rulesAfterDMS, - } - - switch { - case !p.dmsRulesExists: - status.Effective = false - status.StatusMessage = "dms/windowrules.conf does not exist" - case !p.dmsRulesIncluded: - status.Effective = false - status.StatusMessage = "dms/windowrules.conf is not sourced in config" - case p.rulesAfterDMS > 0: - status.Effective = true - status.OverriddenBy = p.rulesAfterDMS - status.StatusMessage = "Some DMS rules may be overridden by config rules" - default: - status.Effective = true - status.StatusMessage = "DMS window rules are active" - } - - return status -} - -type HyprlandRulesParseResult struct { - Rules []HyprlandWindowRule - DMSRulesIncluded bool - DMSStatus *windowrules.DMSRulesStatus -} - -func ParseHyprlandWindowRules(configDir string) (*HyprlandRulesParseResult, error) { - parser := NewHyprlandRulesParser(configDir) - rules, err := parser.Parse() - if err != nil { - return nil, err - } - return &HyprlandRulesParseResult{ - Rules: rules, - DMSRulesIncluded: parser.HasDMSRulesIncluded(), - DMSStatus: parser.buildDMSStatus(), - }, nil -} - -func applyHyprlandRuleAction(actions *windowrules.Actions, rule, value string) { - t := true - switch rule { - case "float": - actions.OpenFloating = &t - case "tile": - actions.Tile = &t - case "fullscreen": - actions.OpenFullscreen = &t - case "maximize": - actions.OpenMaximized = &t - case "nofocus": - actions.NoFocus = &t - case "noborder": - actions.NoBorder = &t - case "noshadow": - actions.NoShadow = &t - case "nodim": - actions.NoDim = &t - case "noblur": - actions.NoBlur = &t - case "noanim": - actions.NoAnim = &t - case "norounding": - actions.NoRounding = &t - case "pin": - actions.Pin = &t - case "opaque": - actions.Opaque = &t - case "forcergbx": - actions.ForcergbX = &t - case "opacity": - if f, err := strconv.ParseFloat(value, 64); err == nil { - actions.Opacity = &f - } - case "size": - actions.Size = value - case "move": - actions.Move = value - case "monitor": - actions.Monitor = value - case "workspace": - actions.Workspace = value - case "idleinhibit": - actions.Idleinhibit = value - case "rounding": - if i, err := strconv.Atoi(value); err == nil { - actions.CornerRadius = &i - } - } -} - -func ConvertHyprlandRulesToWindowRules(hyprRules []HyprlandWindowRule) []windowrules.WindowRule { - result := make([]windowrules.WindowRule, 0, len(hyprRules)) - for i, hr := range hyprRules { - wr := windowrules.WindowRule{ - ID: strconv.Itoa(i), - Enabled: true, - Source: hr.Source, - MatchCriteria: windowrules.MatchCriteria{ - AppID: hr.MatchClass, - Title: hr.MatchTitle, - XWayland: hr.MatchXWayland, - IsFloating: hr.MatchFloating, - Fullscreen: hr.MatchFullscreen, - Pinned: hr.MatchPinned, - Initialised: hr.MatchInitialised, - }, - } - applyHyprlandRuleAction(&wr.Actions, hr.Rule, hr.Value) - result = append(result, wr) - } - return result -} - -type HyprlandWritableProvider struct { - configDir string -} - -func NewHyprlandWritableProvider(configDir string) *HyprlandWritableProvider { - return &HyprlandWritableProvider{configDir: configDir} -} - -func (p *HyprlandWritableProvider) Name() string { - return "hyprland" -} - -func (p *HyprlandWritableProvider) GetOverridePath() string { - expanded, _ := utils.ExpandPath(p.configDir) - return filepath.Join(expanded, "dms", "windowrules.conf") -} - -func (p *HyprlandWritableProvider) GetRuleSet() (*windowrules.RuleSet, error) { - result, err := ParseHyprlandWindowRules(p.configDir) - if err != nil { - return nil, err - } - return &windowrules.RuleSet{ - Title: "Hyprland Window Rules", - Provider: "hyprland", - Rules: ConvertHyprlandRulesToWindowRules(result.Rules), - DMSRulesIncluded: result.DMSRulesIncluded, - DMSStatus: result.DMSStatus, - }, nil -} - -func (p *HyprlandWritableProvider) SetRule(rule windowrules.WindowRule) error { - rules, err := p.LoadDMSRules() - if err != nil { - rules = []windowrules.WindowRule{} - } - - found := false - for i, r := range rules { - if r.ID == rule.ID { - rules[i] = rule - found = true - break - } - } - if !found { - rules = append(rules, rule) - } - - return p.writeDMSRules(rules) -} - -func (p *HyprlandWritableProvider) RemoveRule(id string) error { - rules, err := p.LoadDMSRules() - if err != nil { - return err - } - - newRules := make([]windowrules.WindowRule, 0, len(rules)) - for _, r := range rules { - if r.ID != id { - newRules = append(newRules, r) - } - } - - return p.writeDMSRules(newRules) -} - -func (p *HyprlandWritableProvider) ReorderRules(ids []string) error { - rules, err := p.LoadDMSRules() - if err != nil { - return err - } - - ruleMap := make(map[string]windowrules.WindowRule) - for _, r := range rules { - ruleMap[r.ID] = r - } - - newRules := make([]windowrules.WindowRule, 0, len(ids)) - for _, id := range ids { - if r, ok := ruleMap[id]; ok { - newRules = append(newRules, r) - delete(ruleMap, id) - } - } - - for _, r := range ruleMap { - newRules = append(newRules, r) - } - - return p.writeDMSRules(newRules) -} - -var dmsRuleCommentRegex = regexp.MustCompile(`^#\s*DMS-RULE:\s*id=([^,]+),\s*name=(.*)$`) - -func (p *HyprlandWritableProvider) LoadDMSRules() ([]windowrules.WindowRule, error) { - rulesPath := p.GetOverridePath() - data, err := os.ReadFile(rulesPath) - if err != nil { - if os.IsNotExist(err) { - return []windowrules.WindowRule{}, nil - } - return nil, err - } - - var rules []windowrules.WindowRule - var currentID, currentName string - lines := strings.Split(string(data), "\n") - - for _, line := range lines { - trimmed := strings.TrimSpace(line) - - if matches := dmsRuleCommentRegex.FindStringSubmatch(trimmed); matches != nil { - currentID = matches[1] - currentName = matches[2] - continue - } - - if strings.HasPrefix(trimmed, "windowrulev2") { - parser := NewHyprlandRulesParser(p.configDir) - hrule := parser.parseWindowRuleLine(trimmed) - if hrule == nil { - continue - } - - wr := windowrules.WindowRule{ - ID: currentID, - Name: currentName, - Enabled: true, - Source: rulesPath, - MatchCriteria: windowrules.MatchCriteria{ - AppID: hrule.MatchClass, - Title: hrule.MatchTitle, - XWayland: hrule.MatchXWayland, - IsFloating: hrule.MatchFloating, - Fullscreen: hrule.MatchFullscreen, - Pinned: hrule.MatchPinned, - Initialised: hrule.MatchInitialised, - }, - } - applyHyprlandRuleAction(&wr.Actions, hrule.Rule, hrule.Value) - - if wr.ID == "" { - wr.ID = hrule.MatchClass - if wr.ID == "" { - wr.ID = hrule.MatchTitle - } - } - - rules = append(rules, wr) - currentID = "" - currentName = "" - } - } - - return rules, nil -} - -func (p *HyprlandWritableProvider) writeDMSRules(rules []windowrules.WindowRule) error { - rulesPath := p.GetOverridePath() - - if err := os.MkdirAll(filepath.Dir(rulesPath), 0755); err != nil { - return err - } - - var lines []string - lines = append(lines, "# DMS Window Rules - Managed by DankMaterialShell") - lines = append(lines, "# Do not edit manually - changes may be overwritten") - lines = append(lines, "") - - for _, rule := range rules { - lines = append(lines, p.formatRuleLines(rule)...) - } - - return os.WriteFile(rulesPath, []byte(strings.Join(lines, "\n")), 0644) -} - -func (p *HyprlandWritableProvider) formatRuleLines(rule windowrules.WindowRule) []string { - var lines []string - lines = append(lines, fmt.Sprintf("# DMS-RULE: id=%s, name=%s", rule.ID, rule.Name)) - - var matchParts []string - if rule.MatchCriteria.AppID != "" { - matchParts = append(matchParts, fmt.Sprintf("class:%s", rule.MatchCriteria.AppID)) - } - if rule.MatchCriteria.Title != "" { - matchParts = append(matchParts, fmt.Sprintf("title:%s", rule.MatchCriteria.Title)) - } - if rule.MatchCriteria.XWayland != nil { - matchParts = append(matchParts, fmt.Sprintf("xwayland:%d", boolToInt(*rule.MatchCriteria.XWayland))) - } - if rule.MatchCriteria.IsFloating != nil { - matchParts = append(matchParts, fmt.Sprintf("floating:%d", boolToInt(*rule.MatchCriteria.IsFloating))) - } - if rule.MatchCriteria.Fullscreen != nil { - matchParts = append(matchParts, fmt.Sprintf("fullscreen:%d", boolToInt(*rule.MatchCriteria.Fullscreen))) - } - if rule.MatchCriteria.Pinned != nil { - matchParts = append(matchParts, fmt.Sprintf("pinned:%d", boolToInt(*rule.MatchCriteria.Pinned))) - } - - matchStr := strings.Join(matchParts, ", ") - a := rule.Actions - - if a.OpenFloating != nil && *a.OpenFloating { - lines = append(lines, fmt.Sprintf("windowrulev2 = float, %s", matchStr)) - } - if a.Tile != nil && *a.Tile { - lines = append(lines, fmt.Sprintf("windowrulev2 = tile, %s", matchStr)) - } - if a.OpenFullscreen != nil && *a.OpenFullscreen { - lines = append(lines, fmt.Sprintf("windowrulev2 = fullscreen, %s", matchStr)) - } - if a.OpenMaximized != nil && *a.OpenMaximized { - lines = append(lines, fmt.Sprintf("windowrulev2 = maximize, %s", matchStr)) - } - if a.NoFocus != nil && *a.NoFocus { - lines = append(lines, fmt.Sprintf("windowrulev2 = nofocus, %s", matchStr)) - } - if a.NoBorder != nil && *a.NoBorder { - lines = append(lines, fmt.Sprintf("windowrulev2 = noborder, %s", matchStr)) - } - if a.NoShadow != nil && *a.NoShadow { - lines = append(lines, fmt.Sprintf("windowrulev2 = noshadow, %s", matchStr)) - } - if a.NoDim != nil && *a.NoDim { - lines = append(lines, fmt.Sprintf("windowrulev2 = nodim, %s", matchStr)) - } - if a.NoBlur != nil && *a.NoBlur { - lines = append(lines, fmt.Sprintf("windowrulev2 = noblur, %s", matchStr)) - } - if a.NoAnim != nil && *a.NoAnim { - lines = append(lines, fmt.Sprintf("windowrulev2 = noanim, %s", matchStr)) - } - if a.NoRounding != nil && *a.NoRounding { - lines = append(lines, fmt.Sprintf("windowrulev2 = norounding, %s", matchStr)) - } - if a.Pin != nil && *a.Pin { - lines = append(lines, fmt.Sprintf("windowrulev2 = pin, %s", matchStr)) - } - if a.Opaque != nil && *a.Opaque { - lines = append(lines, fmt.Sprintf("windowrulev2 = opaque, %s", matchStr)) - } - if a.ForcergbX != nil && *a.ForcergbX { - lines = append(lines, fmt.Sprintf("windowrulev2 = forcergbx, %s", matchStr)) - } - if a.Opacity != nil { - lines = append(lines, fmt.Sprintf("windowrulev2 = opacity %.2f, %s", *a.Opacity, matchStr)) - } - if a.Size != "" { - lines = append(lines, fmt.Sprintf("windowrulev2 = size %s, %s", a.Size, matchStr)) - } - if a.Move != "" { - lines = append(lines, fmt.Sprintf("windowrulev2 = move %s, %s", a.Move, matchStr)) - } - if a.Monitor != "" { - lines = append(lines, fmt.Sprintf("windowrulev2 = monitor %s, %s", a.Monitor, matchStr)) - } - if a.Workspace != "" { - lines = append(lines, fmt.Sprintf("windowrulev2 = workspace %s, %s", a.Workspace, matchStr)) - } - if a.CornerRadius != nil { - lines = append(lines, fmt.Sprintf("windowrulev2 = rounding %d, %s", *a.CornerRadius, matchStr)) - } - if a.Idleinhibit != "" { - lines = append(lines, fmt.Sprintf("windowrulev2 = idleinhibit %s, %s", a.Idleinhibit, matchStr)) - } - - if len(lines) == 1 { - lines = append(lines, fmt.Sprintf("# (no actions defined for rule %s)", rule.ID)) - } - - lines = append(lines, "") - return lines -} - -func boolToInt(b bool) int { - if b { - return 1 - } - return 0 -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser_test.go deleted file mode 100644 index 5341b4b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/hyprland_parser_test.go +++ /dev/null @@ -1,280 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestParseWindowRuleV1(t *testing.T) { - parser := NewHyprlandRulesParser("") - - tests := []struct { - name string - line string - wantClass string - wantRule string - wantNil bool - }{ - { - name: "basic float rule", - line: "windowrule = float, ^(firefox)$", - wantClass: "^(firefox)$", - wantRule: "float", - }, - { - name: "tile rule", - line: "windowrule = tile, steam", - wantClass: "steam", - wantRule: "tile", - }, - { - name: "no match returns empty class", - line: "windowrule = float", - wantClass: "", - wantRule: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := parser.parseWindowRuleLine(tt.line) - if tt.wantNil { - if result != nil { - t.Errorf("expected nil, got %+v", result) - } - return - } - if result == nil { - t.Fatal("expected non-nil result") - } - if result.MatchClass != tt.wantClass { - t.Errorf("MatchClass = %q, want %q", result.MatchClass, tt.wantClass) - } - if result.Rule != tt.wantRule { - t.Errorf("Rule = %q, want %q", result.Rule, tt.wantRule) - } - }) - } -} - -func TestParseWindowRuleV2(t *testing.T) { - parser := NewHyprlandRulesParser("") - - tests := []struct { - name string - line string - wantClass string - wantTitle string - wantRule string - wantValue string - }{ - { - name: "float with class", - line: "windowrulev2 = float, class:^(firefox)$", - wantClass: "^(firefox)$", - wantRule: "float", - }, - { - name: "opacity with value", - line: "windowrulev2 = opacity 0.8, class:^(code)$", - wantClass: "^(code)$", - wantRule: "opacity", - wantValue: "0.8", - }, - { - name: "size with value and title", - line: "windowrulev2 = size 800 600, class:^(steam)$, title:Settings", - wantClass: "^(steam)$", - wantTitle: "Settings", - wantRule: "size", - wantValue: "800 600", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := parser.parseWindowRuleLine(tt.line) - if result == nil { - t.Fatal("expected non-nil result") - } - if result.MatchClass != tt.wantClass { - t.Errorf("MatchClass = %q, want %q", result.MatchClass, tt.wantClass) - } - if result.MatchTitle != tt.wantTitle { - t.Errorf("MatchTitle = %q, want %q", result.MatchTitle, tt.wantTitle) - } - if result.Rule != tt.wantRule { - t.Errorf("Rule = %q, want %q", result.Rule, tt.wantRule) - } - if result.Value != tt.wantValue { - t.Errorf("Value = %q, want %q", result.Value, tt.wantValue) - } - }) - } -} - -func TestConvertHyprlandRulesToWindowRules(t *testing.T) { - hyprRules := []HyprlandWindowRule{ - {MatchClass: "^(firefox)$", Rule: "float"}, - {MatchClass: "^(code)$", Rule: "opacity", Value: "0.9"}, - {MatchClass: "^(steam)$", Rule: "maximize"}, - } - - result := ConvertHyprlandRulesToWindowRules(hyprRules) - - if len(result) != 3 { - t.Errorf("expected 3 rules, got %d", len(result)) - } - - if result[0].MatchCriteria.AppID != "^(firefox)$" { - t.Errorf("rule 0 AppID = %q, want ^(firefox)$", result[0].MatchCriteria.AppID) - } - if result[0].Actions.OpenFloating == nil || !*result[0].Actions.OpenFloating { - t.Error("rule 0 should have OpenFloating = true") - } - - if result[1].Actions.Opacity == nil || *result[1].Actions.Opacity != 0.9 { - t.Errorf("rule 1 Opacity = %v, want 0.9", result[1].Actions.Opacity) - } - - if result[2].Actions.OpenMaximized == nil || !*result[2].Actions.OpenMaximized { - t.Error("rule 2 should have OpenMaximized = true") - } -} - -func TestHyprlandWritableProvider(t *testing.T) { - tmpDir := t.TempDir() - provider := NewHyprlandWritableProvider(tmpDir) - - if provider.Name() != "hyprland" { - t.Errorf("Name() = %q, want hyprland", provider.Name()) - } - - expectedPath := filepath.Join(tmpDir, "dms", "windowrules.conf") - if provider.GetOverridePath() != expectedPath { - t.Errorf("GetOverridePath() = %q, want %q", provider.GetOverridePath(), expectedPath) - } -} - -func TestHyprlandSetAndLoadDMSRules(t *testing.T) { - tmpDir := t.TempDir() - provider := NewHyprlandWritableProvider(tmpDir) - - rule := newTestWindowRule("test_id", "Test Rule", "^(firefox)$") - rule.Actions.OpenFloating = boolPtr(true) - - if err := provider.SetRule(rule); err != nil { - t.Fatalf("SetRule failed: %v", err) - } - - rules, err := provider.LoadDMSRules() - if err != nil { - t.Fatalf("LoadDMSRules failed: %v", err) - } - - if len(rules) != 1 { - t.Fatalf("expected 1 rule, got %d", len(rules)) - } - - if rules[0].ID != "test_id" { - t.Errorf("ID = %q, want test_id", rules[0].ID) - } - if rules[0].MatchCriteria.AppID != "^(firefox)$" { - t.Errorf("AppID = %q, want ^(firefox)$", rules[0].MatchCriteria.AppID) - } -} - -func TestHyprlandRemoveRule(t *testing.T) { - tmpDir := t.TempDir() - provider := NewHyprlandWritableProvider(tmpDir) - - rule1 := newTestWindowRule("rule1", "Rule 1", "^(app1)$") - rule1.Actions.OpenFloating = boolPtr(true) - rule2 := newTestWindowRule("rule2", "Rule 2", "^(app2)$") - rule2.Actions.OpenFloating = boolPtr(true) - - _ = provider.SetRule(rule1) - _ = provider.SetRule(rule2) - - if err := provider.RemoveRule("rule1"); err != nil { - t.Fatalf("RemoveRule failed: %v", err) - } - - rules, _ := provider.LoadDMSRules() - if len(rules) != 1 { - t.Fatalf("expected 1 rule after removal, got %d", len(rules)) - } - if rules[0].ID != "rule2" { - t.Errorf("remaining rule ID = %q, want rule2", rules[0].ID) - } -} - -func TestHyprlandReorderRules(t *testing.T) { - tmpDir := t.TempDir() - provider := NewHyprlandWritableProvider(tmpDir) - - rule1 := newTestWindowRule("rule1", "Rule 1", "^(app1)$") - rule1.Actions.OpenFloating = boolPtr(true) - rule2 := newTestWindowRule("rule2", "Rule 2", "^(app2)$") - rule2.Actions.OpenFloating = boolPtr(true) - rule3 := newTestWindowRule("rule3", "Rule 3", "^(app3)$") - rule3.Actions.OpenFloating = boolPtr(true) - - _ = provider.SetRule(rule1) - _ = provider.SetRule(rule2) - _ = provider.SetRule(rule3) - - if err := provider.ReorderRules([]string{"rule3", "rule1", "rule2"}); err != nil { - t.Fatalf("ReorderRules failed: %v", err) - } - - rules, _ := provider.LoadDMSRules() - if len(rules) != 3 { - t.Fatalf("expected 3 rules, got %d", len(rules)) - } - expectedOrder := []string{"rule3", "rule1", "rule2"} - for i, expectedID := range expectedOrder { - if rules[i].ID != expectedID { - t.Errorf("rule %d ID = %q, want %q", i, rules[i].ID, expectedID) - } - } -} - -func TestHyprlandParseConfigWithSource(t *testing.T) { - tmpDir := t.TempDir() - - mainConfig := ` -windowrulev2 = float, class:^(mainapp)$ -source = ./extra.conf -` - extraConfig := ` -windowrulev2 = tile, class:^(extraapp)$ -` - - if err := os.WriteFile(filepath.Join(tmpDir, "hyprland.conf"), []byte(mainConfig), 0644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(tmpDir, "extra.conf"), []byte(extraConfig), 0644); err != nil { - t.Fatal(err) - } - - parser := NewHyprlandRulesParser(tmpDir) - rules, err := parser.Parse() - if err != nil { - t.Fatalf("Parse failed: %v", err) - } - - if len(rules) != 2 { - t.Errorf("expected 2 rules, got %d", len(rules)) - } -} - -func TestBoolToInt(t *testing.T) { - if boolToInt(true) != 1 { - t.Error("boolToInt(true) should be 1") - } - if boolToInt(false) != 0 { - t.Error("boolToInt(false) should be 0") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser.go deleted file mode 100644 index 5bb5ec4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser.go +++ /dev/null @@ -1,875 +0,0 @@ -package providers - -import ( - "fmt" - "os" - "path/filepath" - "regexp" - "strconv" - "strings" - - "github.com/sblinch/kdl-go" - "github.com/sblinch/kdl-go/document" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/windowrules" -) - -type NiriWindowRule struct { - MatchAppID string - MatchTitle string - MatchIsFloating *bool - MatchIsActive *bool - MatchIsFocused *bool - MatchIsActiveInColumn *bool - MatchIsWindowCastTarget *bool - MatchIsUrgent *bool - MatchAtStartup *bool - Opacity *float64 - OpenFloating *bool - OpenMaximized *bool - OpenMaximizedToEdges *bool - OpenFullscreen *bool - OpenFocused *bool - OpenOnOutput string - OpenOnWorkspace string - DefaultColumnWidth string - DefaultWindowHeight string - VariableRefreshRate *bool - BlockOutFrom string - DefaultColumnDisplay string - ScrollFactor *float64 - CornerRadius *int - ClipToGeometry *bool - TiledState *bool - MinWidth *int - MaxWidth *int - MinHeight *int - MaxHeight *int - BorderColor string - FocusRingColor string - FocusRingOff *bool - BorderOff *bool - DrawBorderWithBg *bool - Source string -} - -type NiriRulesParser struct { - configDir string - processedFiles map[string]bool - rules []NiriWindowRule - currentSource string - dmsRulesIncluded bool - dmsRulesExists bool - includeCount int - dmsIncludePos int - rulesAfterDMS int - dmsProcessed bool -} - -func NewNiriRulesParser(configDir string) *NiriRulesParser { - return &NiriRulesParser{ - configDir: configDir, - processedFiles: make(map[string]bool), - rules: []NiriWindowRule{}, - dmsIncludePos: -1, - } -} - -func (p *NiriRulesParser) Parse() ([]NiriWindowRule, error) { - dmsRulesPath := filepath.Join(p.configDir, "dms", "windowrules.kdl") - if _, err := os.Stat(dmsRulesPath); err == nil { - p.dmsRulesExists = true - } - - configPath := filepath.Join(p.configDir, "config.kdl") - if err := p.parseFile(configPath); err != nil { - return nil, err - } - - if p.dmsRulesExists && !p.dmsProcessed { - p.parseDMSRulesDirectly(dmsRulesPath) - } - - return p.rules, nil -} - -func (p *NiriRulesParser) parseDMSRulesDirectly(dmsRulesPath string) { - data, err := os.ReadFile(dmsRulesPath) - if err != nil { - return - } - - doc, err := kdl.Parse(strings.NewReader(string(data))) - if err != nil { - return - } - - prevSource := p.currentSource - p.currentSource = dmsRulesPath - p.processNodes(doc.Nodes, filepath.Dir(dmsRulesPath)) - p.currentSource = prevSource - p.dmsProcessed = true -} - -func (p *NiriRulesParser) parseFile(filePath string) error { - absPath, err := filepath.Abs(filePath) - if err != nil { - return err - } - - if p.processedFiles[absPath] { - return nil - } - p.processedFiles[absPath] = true - - data, err := os.ReadFile(absPath) - if err != nil { - return nil - } - - doc, err := kdl.Parse(strings.NewReader(string(data))) - if err != nil { - return err - } - - prevSource := p.currentSource - p.currentSource = absPath - baseDir := filepath.Dir(absPath) - p.processNodes(doc.Nodes, baseDir) - p.currentSource = prevSource - - return nil -} - -func (p *NiriRulesParser) processNodes(nodes []*document.Node, baseDir string) { - for _, node := range nodes { - name := node.Name.String() - - switch name { - case "include": - p.handleInclude(node, baseDir) - case "window-rule": - p.parseWindowRuleNode(node) - } - } -} - -func (p *NiriRulesParser) handleInclude(node *document.Node, baseDir string) { - if len(node.Arguments) == 0 { - return - } - - includePath := strings.Trim(node.Arguments[0].String(), "\"") - isDMSInclude := includePath == "dms/windowrules.kdl" || strings.HasSuffix(includePath, "/dms/windowrules.kdl") - - p.includeCount++ - if isDMSInclude { - p.dmsRulesIncluded = true - p.dmsIncludePos = p.includeCount - p.dmsProcessed = true - } - - fullPath := filepath.Join(baseDir, includePath) - if filepath.IsAbs(includePath) { - fullPath = includePath - } - - _ = p.parseFile(fullPath) -} - -func (p *NiriRulesParser) parseWindowRuleNode(node *document.Node) { - if node.Children == nil { - return - } - - rule := NiriWindowRule{ - Source: p.currentSource, - } - - for _, child := range node.Children { - childName := child.Name.String() - - switch childName { - case "match": - p.parseMatchNode(child, &rule) - case "opacity": - if len(child.Arguments) > 0 { - val := child.Arguments[0].ResolvedValue() - if f, ok := val.(float64); ok { - rule.Opacity = &f - } - } - case "open-floating": - b := p.parseBoolArg(child) - rule.OpenFloating = &b - case "open-maximized": - b := p.parseBoolArg(child) - rule.OpenMaximized = &b - case "open-maximized-to-edges": - b := p.parseBoolArg(child) - rule.OpenMaximizedToEdges = &b - case "open-fullscreen": - b := p.parseBoolArg(child) - rule.OpenFullscreen = &b - case "open-focused": - b := p.parseBoolArg(child) - rule.OpenFocused = &b - case "open-on-output": - if len(child.Arguments) > 0 { - rule.OpenOnOutput = child.Arguments[0].ValueString() - } - case "open-on-workspace": - if len(child.Arguments) > 0 { - rule.OpenOnWorkspace = child.Arguments[0].ValueString() - } - case "default-column-width": - rule.DefaultColumnWidth = p.parseSizeNode(child) - case "default-window-height": - rule.DefaultWindowHeight = p.parseSizeNode(child) - case "variable-refresh-rate": - b := p.parseBoolArg(child) - rule.VariableRefreshRate = &b - case "block-out-from": - if len(child.Arguments) > 0 { - rule.BlockOutFrom = child.Arguments[0].ValueString() - } - case "default-column-display": - if len(child.Arguments) > 0 { - rule.DefaultColumnDisplay = child.Arguments[0].ValueString() - } - case "scroll-factor": - if len(child.Arguments) > 0 { - val := child.Arguments[0].ResolvedValue() - if f, ok := val.(float64); ok { - rule.ScrollFactor = &f - } - } - case "geometry-corner-radius": - if len(child.Arguments) > 0 { - val := child.Arguments[0].ResolvedValue() - if i, ok := val.(int64); ok { - intVal := int(i) - rule.CornerRadius = &intVal - } - } - case "clip-to-geometry": - b := p.parseBoolArg(child) - rule.ClipToGeometry = &b - case "tiled-state": - b := p.parseBoolArg(child) - rule.TiledState = &b - case "min-width": - if len(child.Arguments) > 0 { - val := child.Arguments[0].ResolvedValue() - if i, ok := val.(int64); ok { - intVal := int(i) - rule.MinWidth = &intVal - } - } - case "max-width": - if len(child.Arguments) > 0 { - val := child.Arguments[0].ResolvedValue() - if i, ok := val.(int64); ok { - intVal := int(i) - rule.MaxWidth = &intVal - } - } - case "min-height": - if len(child.Arguments) > 0 { - val := child.Arguments[0].ResolvedValue() - if i, ok := val.(int64); ok { - intVal := int(i) - rule.MinHeight = &intVal - } - } - case "max-height": - if len(child.Arguments) > 0 { - val := child.Arguments[0].ResolvedValue() - if i, ok := val.(int64); ok { - intVal := int(i) - rule.MaxHeight = &intVal - } - } - case "border": - p.parseBorderNode(child, &rule) - case "focus-ring": - p.parseFocusRingNode(child, &rule) - case "draw-border-with-background": - b := p.parseBoolArg(child) - rule.DrawBorderWithBg = &b - } - } - - p.rules = append(p.rules, rule) -} - -func (p *NiriRulesParser) parseSizeNode(node *document.Node) string { - if node.Children == nil { - return "" - } - for _, child := range node.Children { - name := child.Name.String() - if len(child.Arguments) > 0 { - val := child.Arguments[0].ResolvedValue() - switch name { - case "fixed": - if i, ok := val.(int64); ok { - return "fixed " + strconv.FormatInt(i, 10) - } - case "proportion": - if f, ok := val.(float64); ok { - return "proportion " + strconv.FormatFloat(f, 'f', -1, 64) - } - } - } - } - return "" -} - -func (p *NiriRulesParser) parseMatchNode(node *document.Node, rule *NiriWindowRule) { - if node.Properties == nil { - return - } - - if val, ok := node.Properties.Get("app-id"); ok { - rule.MatchAppID = val.ValueString() - } - if val, ok := node.Properties.Get("title"); ok { - rule.MatchTitle = val.ValueString() - } - if val, ok := node.Properties.Get("is-floating"); ok { - b := val.ValueString() == "true" - rule.MatchIsFloating = &b - } - if val, ok := node.Properties.Get("is-active"); ok { - b := val.ValueString() == "true" - rule.MatchIsActive = &b - } - if val, ok := node.Properties.Get("is-focused"); ok { - b := val.ValueString() == "true" - rule.MatchIsFocused = &b - } - if val, ok := node.Properties.Get("is-active-in-column"); ok { - b := val.ValueString() == "true" - rule.MatchIsActiveInColumn = &b - } - if val, ok := node.Properties.Get("is-window-cast-target"); ok { - b := val.ValueString() == "true" - rule.MatchIsWindowCastTarget = &b - } - if val, ok := node.Properties.Get("is-urgent"); ok { - b := val.ValueString() == "true" - rule.MatchIsUrgent = &b - } - if val, ok := node.Properties.Get("at-startup"); ok { - b := val.ValueString() == "true" - rule.MatchAtStartup = &b - } -} - -func (p *NiriRulesParser) parseBorderNode(node *document.Node, rule *NiriWindowRule) { - if node.Children == nil { - return - } - - for _, child := range node.Children { - switch child.Name.String() { - case "off": - b := true - rule.BorderOff = &b - case "active-color": - if len(child.Arguments) > 0 { - rule.BorderColor = child.Arguments[0].ValueString() - } - } - } -} - -func (p *NiriRulesParser) parseFocusRingNode(node *document.Node, rule *NiriWindowRule) { - if node.Children == nil { - return - } - - for _, child := range node.Children { - switch child.Name.String() { - case "off": - b := true - rule.FocusRingOff = &b - case "active-color": - if len(child.Arguments) > 0 { - rule.FocusRingColor = child.Arguments[0].ValueString() - } - } - } -} - -func (p *NiriRulesParser) parseBoolArg(node *document.Node) bool { - if len(node.Arguments) == 0 { - return true - } - return node.Arguments[0].ValueString() != "false" -} - -func (p *NiriRulesParser) HasDMSRulesIncluded() bool { - return p.dmsRulesIncluded -} - -func (p *NiriRulesParser) buildDMSStatus() *windowrules.DMSRulesStatus { - status := &windowrules.DMSRulesStatus{ - Exists: p.dmsRulesExists, - Included: p.dmsRulesIncluded, - IncludePosition: p.dmsIncludePos, - TotalIncludes: p.includeCount, - RulesAfterDMS: p.rulesAfterDMS, - } - - switch { - case !p.dmsRulesExists: - status.Effective = false - status.StatusMessage = "dms/windowrules.kdl does not exist" - case !p.dmsRulesIncluded: - status.Effective = false - status.StatusMessage = "dms/windowrules.kdl is not included in config.kdl" - case p.rulesAfterDMS > 0: - status.Effective = true - status.OverriddenBy = p.rulesAfterDMS - status.StatusMessage = "Some DMS rules may be overridden by config rules" - default: - status.Effective = true - status.StatusMessage = "DMS window rules are active" - } - - return status -} - -type NiriRulesParseResult struct { - Rules []NiriWindowRule - DMSRulesIncluded bool - DMSStatus *windowrules.DMSRulesStatus -} - -func ParseNiriWindowRules(configDir string) (*NiriRulesParseResult, error) { - parser := NewNiriRulesParser(configDir) - rules, err := parser.Parse() - if err != nil { - return nil, err - } - return &NiriRulesParseResult{ - Rules: rules, - DMSRulesIncluded: parser.HasDMSRulesIncluded(), - DMSStatus: parser.buildDMSStatus(), - }, nil -} - -func ConvertNiriRulesToWindowRules(niriRules []NiriWindowRule) []windowrules.WindowRule { - result := make([]windowrules.WindowRule, 0, len(niriRules)) - for i, nr := range niriRules { - wr := windowrules.WindowRule{ - ID: fmt.Sprintf("rule_%d", i), - Enabled: true, - Source: nr.Source, - MatchCriteria: windowrules.MatchCriteria{ - AppID: nr.MatchAppID, - Title: nr.MatchTitle, - IsFloating: nr.MatchIsFloating, - IsActive: nr.MatchIsActive, - IsFocused: nr.MatchIsFocused, - IsActiveInColumn: nr.MatchIsActiveInColumn, - IsWindowCastTarget: nr.MatchIsWindowCastTarget, - IsUrgent: nr.MatchIsUrgent, - AtStartup: nr.MatchAtStartup, - }, - Actions: windowrules.Actions{ - Opacity: nr.Opacity, - OpenFloating: nr.OpenFloating, - OpenMaximized: nr.OpenMaximized, - OpenMaximizedToEdges: nr.OpenMaximizedToEdges, - OpenFullscreen: nr.OpenFullscreen, - OpenFocused: nr.OpenFocused, - OpenOnOutput: nr.OpenOnOutput, - OpenOnWorkspace: nr.OpenOnWorkspace, - DefaultColumnWidth: nr.DefaultColumnWidth, - DefaultWindowHeight: nr.DefaultWindowHeight, - VariableRefreshRate: nr.VariableRefreshRate, - BlockOutFrom: nr.BlockOutFrom, - DefaultColumnDisplay: nr.DefaultColumnDisplay, - ScrollFactor: nr.ScrollFactor, - CornerRadius: nr.CornerRadius, - ClipToGeometry: nr.ClipToGeometry, - TiledState: nr.TiledState, - MinWidth: nr.MinWidth, - MaxWidth: nr.MaxWidth, - MinHeight: nr.MinHeight, - MaxHeight: nr.MaxHeight, - BorderColor: nr.BorderColor, - FocusRingColor: nr.FocusRingColor, - FocusRingOff: nr.FocusRingOff, - BorderOff: nr.BorderOff, - DrawBorderWithBg: nr.DrawBorderWithBg, - }, - } - result = append(result, wr) - } - return result -} - -type NiriWritableProvider struct { - configDir string -} - -func NewNiriWritableProvider(configDir string) *NiriWritableProvider { - return &NiriWritableProvider{configDir: configDir} -} - -func (p *NiriWritableProvider) Name() string { - return "niri" -} - -func (p *NiriWritableProvider) GetOverridePath() string { - return filepath.Join(p.configDir, "dms", "windowrules.kdl") -} - -func (p *NiriWritableProvider) GetRuleSet() (*windowrules.RuleSet, error) { - result, err := ParseNiriWindowRules(p.configDir) - if err != nil { - return nil, err - } - return &windowrules.RuleSet{ - Title: "Niri Window Rules", - Provider: "niri", - Rules: ConvertNiriRulesToWindowRules(result.Rules), - DMSRulesIncluded: result.DMSRulesIncluded, - DMSStatus: result.DMSStatus, - }, nil -} - -func (p *NiriWritableProvider) SetRule(rule windowrules.WindowRule) error { - rules, err := p.LoadDMSRules() - if err != nil { - rules = []windowrules.WindowRule{} - } - - found := false - for i, r := range rules { - if r.ID == rule.ID { - rules[i] = rule - found = true - break - } - } - if !found { - rules = append(rules, rule) - } - - return p.writeDMSRules(rules) -} - -func (p *NiriWritableProvider) RemoveRule(id string) error { - rules, err := p.LoadDMSRules() - if err != nil { - return err - } - - newRules := make([]windowrules.WindowRule, 0, len(rules)) - for _, r := range rules { - if r.ID != id { - newRules = append(newRules, r) - } - } - - return p.writeDMSRules(newRules) -} - -func (p *NiriWritableProvider) ReorderRules(ids []string) error { - rules, err := p.LoadDMSRules() - if err != nil { - return err - } - - ruleMap := make(map[string]windowrules.WindowRule) - for _, r := range rules { - ruleMap[r.ID] = r - } - - newRules := make([]windowrules.WindowRule, 0, len(ids)) - for _, id := range ids { - if r, ok := ruleMap[id]; ok { - newRules = append(newRules, r) - delete(ruleMap, id) - } - } - - for _, r := range ruleMap { - newRules = append(newRules, r) - } - - return p.writeDMSRules(newRules) -} - -var niriMetaCommentRegex = regexp.MustCompile(`^//\s*@id=(\S*)\s*@name=(.*)$`) - -func (p *NiriWritableProvider) LoadDMSRules() ([]windowrules.WindowRule, error) { - rulesPath := p.GetOverridePath() - data, err := os.ReadFile(rulesPath) - if err != nil { - if os.IsNotExist(err) { - return []windowrules.WindowRule{}, nil - } - return nil, err - } - - content := string(data) - lines := strings.Split(content, "\n") - - type ruleMeta struct { - id string - name string - } - var metas []ruleMeta - var currentID, currentName string - - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if matches := niriMetaCommentRegex.FindStringSubmatch(trimmed); matches != nil { - currentID = matches[1] - currentName = strings.TrimSpace(matches[2]) - continue - } - if strings.HasPrefix(trimmed, "window-rule") { - metas = append(metas, ruleMeta{id: currentID, name: currentName}) - currentID = "" - currentName = "" - } - } - - doc, err := kdl.Parse(strings.NewReader(content)) - if err != nil { - return nil, err - } - - parser := NewNiriRulesParser(p.configDir) - parser.currentSource = rulesPath - - for _, node := range doc.Nodes { - if node.Name.String() == "window-rule" { - parser.parseWindowRuleNode(node) - } - } - - var rules []windowrules.WindowRule - for i, nr := range parser.rules { - id := "" - name := "" - if i < len(metas) { - id = metas[i].id - name = metas[i].name - } - if id == "" { - id = fmt.Sprintf("dms_rule_%d", i) - } - - wr := windowrules.WindowRule{ - ID: id, - Name: name, - Enabled: true, - Source: rulesPath, - MatchCriteria: windowrules.MatchCriteria{ - AppID: nr.MatchAppID, - Title: nr.MatchTitle, - IsFloating: nr.MatchIsFloating, - IsActive: nr.MatchIsActive, - IsFocused: nr.MatchIsFocused, - IsActiveInColumn: nr.MatchIsActiveInColumn, - IsWindowCastTarget: nr.MatchIsWindowCastTarget, - IsUrgent: nr.MatchIsUrgent, - AtStartup: nr.MatchAtStartup, - }, - Actions: windowrules.Actions{ - Opacity: nr.Opacity, - OpenFloating: nr.OpenFloating, - OpenMaximized: nr.OpenMaximized, - OpenMaximizedToEdges: nr.OpenMaximizedToEdges, - OpenFullscreen: nr.OpenFullscreen, - OpenFocused: nr.OpenFocused, - OpenOnOutput: nr.OpenOnOutput, - OpenOnWorkspace: nr.OpenOnWorkspace, - DefaultColumnWidth: nr.DefaultColumnWidth, - DefaultWindowHeight: nr.DefaultWindowHeight, - VariableRefreshRate: nr.VariableRefreshRate, - BlockOutFrom: nr.BlockOutFrom, - DefaultColumnDisplay: nr.DefaultColumnDisplay, - ScrollFactor: nr.ScrollFactor, - CornerRadius: nr.CornerRadius, - ClipToGeometry: nr.ClipToGeometry, - TiledState: nr.TiledState, - MinWidth: nr.MinWidth, - MaxWidth: nr.MaxWidth, - MinHeight: nr.MinHeight, - MaxHeight: nr.MaxHeight, - BorderColor: nr.BorderColor, - FocusRingColor: nr.FocusRingColor, - FocusRingOff: nr.FocusRingOff, - BorderOff: nr.BorderOff, - DrawBorderWithBg: nr.DrawBorderWithBg, - }, - } - - rules = append(rules, wr) - } - - return rules, nil -} - -func (p *NiriWritableProvider) writeDMSRules(rules []windowrules.WindowRule) error { - rulesPath := p.GetOverridePath() - - if err := os.MkdirAll(filepath.Dir(rulesPath), 0755); err != nil { - return err - } - - var lines []string - lines = append(lines, "// DMS Window Rules - Managed by DankMaterialShell") - lines = append(lines, "// Do not edit manually - changes may be overwritten") - lines = append(lines, "") - - for _, rule := range rules { - lines = append(lines, p.formatRule(rule)) - lines = append(lines, "") - } - - return os.WriteFile(rulesPath, []byte(strings.Join(lines, "\n")), 0644) -} - -func (p *NiriWritableProvider) formatRule(rule windowrules.WindowRule) string { - var lines []string - lines = append(lines, fmt.Sprintf("// @id=%s @name=%s", rule.ID, rule.Name)) - lines = append(lines, "window-rule {") - - m := rule.MatchCriteria - if m.AppID != "" || m.Title != "" || m.IsFloating != nil || m.IsActive != nil || - m.IsFocused != nil || m.IsActiveInColumn != nil || m.IsWindowCastTarget != nil || - m.IsUrgent != nil || m.AtStartup != nil { - var matchProps []string - if m.AppID != "" { - matchProps = append(matchProps, fmt.Sprintf("app-id=%q", m.AppID)) - } - if m.Title != "" { - matchProps = append(matchProps, fmt.Sprintf("title=%q", m.Title)) - } - if m.IsFloating != nil { - matchProps = append(matchProps, fmt.Sprintf("is-floating=%t", *m.IsFloating)) - } - if m.IsActive != nil { - matchProps = append(matchProps, fmt.Sprintf("is-active=%t", *m.IsActive)) - } - if m.IsFocused != nil { - matchProps = append(matchProps, fmt.Sprintf("is-focused=%t", *m.IsFocused)) - } - if m.IsActiveInColumn != nil { - matchProps = append(matchProps, fmt.Sprintf("is-active-in-column=%t", *m.IsActiveInColumn)) - } - if m.IsWindowCastTarget != nil { - matchProps = append(matchProps, fmt.Sprintf("is-window-cast-target=%t", *m.IsWindowCastTarget)) - } - if m.IsUrgent != nil { - matchProps = append(matchProps, fmt.Sprintf("is-urgent=%t", *m.IsUrgent)) - } - if m.AtStartup != nil { - matchProps = append(matchProps, fmt.Sprintf("at-startup=%t", *m.AtStartup)) - } - lines = append(lines, " match "+strings.Join(matchProps, " ")) - } - - a := rule.Actions - if a.Opacity != nil { - lines = append(lines, fmt.Sprintf(" opacity %.2f", *a.Opacity)) - } - if a.OpenFloating != nil && *a.OpenFloating { - lines = append(lines, " open-floating true") - } - if a.OpenMaximized != nil && *a.OpenMaximized { - lines = append(lines, " open-maximized true") - } - if a.OpenMaximizedToEdges != nil && *a.OpenMaximizedToEdges { - lines = append(lines, " open-maximized-to-edges true") - } - if a.OpenFullscreen != nil && *a.OpenFullscreen { - lines = append(lines, " open-fullscreen true") - } - if a.OpenFocused != nil { - lines = append(lines, fmt.Sprintf(" open-focused %t", *a.OpenFocused)) - } - if a.OpenOnOutput != "" { - lines = append(lines, fmt.Sprintf(" open-on-output %q", a.OpenOnOutput)) - } - if a.OpenOnWorkspace != "" { - lines = append(lines, fmt.Sprintf(" open-on-workspace %q", a.OpenOnWorkspace)) - } - if a.DefaultColumnWidth != "" { - lines = append(lines, formatSizeProperty("default-column-width", a.DefaultColumnWidth)) - } - if a.DefaultWindowHeight != "" { - lines = append(lines, formatSizeProperty("default-window-height", a.DefaultWindowHeight)) - } - if a.VariableRefreshRate != nil && *a.VariableRefreshRate { - lines = append(lines, " variable-refresh-rate true") - } - if a.BlockOutFrom != "" { - lines = append(lines, fmt.Sprintf(" block-out-from %q", a.BlockOutFrom)) - } - if a.DefaultColumnDisplay != "" { - lines = append(lines, fmt.Sprintf(" default-column-display %q", a.DefaultColumnDisplay)) - } - if a.ScrollFactor != nil { - lines = append(lines, fmt.Sprintf(" scroll-factor %.2f", *a.ScrollFactor)) - } - if a.CornerRadius != nil { - lines = append(lines, fmt.Sprintf(" geometry-corner-radius %d", *a.CornerRadius)) - } - if a.ClipToGeometry != nil && *a.ClipToGeometry { - lines = append(lines, " clip-to-geometry true") - } - if a.TiledState != nil && *a.TiledState { - lines = append(lines, " tiled-state true") - } - if a.MinWidth != nil { - lines = append(lines, fmt.Sprintf(" min-width %d", *a.MinWidth)) - } - if a.MaxWidth != nil { - lines = append(lines, fmt.Sprintf(" max-width %d", *a.MaxWidth)) - } - if a.MinHeight != nil { - lines = append(lines, fmt.Sprintf(" min-height %d", *a.MinHeight)) - } - if a.MaxHeight != nil { - lines = append(lines, fmt.Sprintf(" max-height %d", *a.MaxHeight)) - } - if a.BorderOff != nil && *a.BorderOff { - lines = append(lines, " border { off; }") - } else if a.BorderColor != "" { - lines = append(lines, fmt.Sprintf(" border { active-color %q; }", a.BorderColor)) - } - if a.FocusRingOff != nil && *a.FocusRingOff { - lines = append(lines, " focus-ring { off; }") - } else if a.FocusRingColor != "" { - lines = append(lines, fmt.Sprintf(" focus-ring { active-color %q; }", a.FocusRingColor)) - } - if a.DrawBorderWithBg != nil { - lines = append(lines, fmt.Sprintf(" draw-border-with-background %t", *a.DrawBorderWithBg)) - } - - lines = append(lines, "}") - return strings.Join(lines, "\n") -} - -func formatSizeProperty(name, value string) string { - parts := strings.SplitN(value, " ", 2) - if len(parts) == 2 { - return fmt.Sprintf(" %s { %s %s; }", name, parts[0], parts[1]) - } - // Bare number without type prefix — default to "fixed" - if _, err := strconv.Atoi(value); err == nil { - return fmt.Sprintf(" %s { fixed %s; }", name, value) - } - return fmt.Sprintf(" %s { }", name) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser_test.go deleted file mode 100644 index f764b6a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/niri_parser_test.go +++ /dev/null @@ -1,335 +0,0 @@ -package providers - -import ( - "os" - "path/filepath" - "testing" -) - -func TestNiriParseBasicWindowRule(t *testing.T) { - tmpDir := t.TempDir() - - config := ` -window-rule { - match app-id="^firefox$" - opacity 0.9 - open-floating true -} -` - if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(config), 0644); err != nil { - t.Fatal(err) - } - - parser := NewNiriRulesParser(tmpDir) - rules, err := parser.Parse() - if err != nil { - t.Fatalf("Parse failed: %v", err) - } - - if len(rules) != 1 { - t.Fatalf("expected 1 rule, got %d", len(rules)) - } - - rule := rules[0] - if rule.MatchAppID != "^firefox$" { - t.Errorf("MatchAppID = %q, want ^firefox$", rule.MatchAppID) - } - if rule.Opacity == nil || *rule.Opacity != 0.9 { - t.Errorf("Opacity = %v, want 0.9", rule.Opacity) - } - if rule.OpenFloating == nil || !*rule.OpenFloating { - t.Error("OpenFloating should be true") - } -} - -func TestNiriParseMultipleRules(t *testing.T) { - tmpDir := t.TempDir() - - config := ` -window-rule { - match app-id="app1" - open-maximized true -} - -window-rule { - match app-id="app2" - open-fullscreen true -} -` - if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(config), 0644); err != nil { - t.Fatal(err) - } - - parser := NewNiriRulesParser(tmpDir) - rules, err := parser.Parse() - if err != nil { - t.Fatalf("Parse failed: %v", err) - } - - if len(rules) != 2 { - t.Fatalf("expected 2 rules, got %d", len(rules)) - } - - if rules[0].MatchAppID != "app1" { - t.Errorf("rule 0 MatchAppID = %q, want app1", rules[0].MatchAppID) - } - if rules[1].MatchAppID != "app2" { - t.Errorf("rule 1 MatchAppID = %q, want app2", rules[1].MatchAppID) - } -} - -func TestConvertNiriRulesToWindowRules(t *testing.T) { - niriRules := []NiriWindowRule{ - {MatchAppID: "^firefox$", Opacity: floatPtr(0.8)}, - {MatchAppID: "^code$", OpenFloating: boolPtr(true)}, - } - - result := ConvertNiriRulesToWindowRules(niriRules) - - if len(result) != 2 { - t.Errorf("expected 2 rules, got %d", len(result)) - } - - if result[0].MatchCriteria.AppID != "^firefox$" { - t.Errorf("rule 0 AppID = %q, want ^firefox$", result[0].MatchCriteria.AppID) - } - if result[0].Actions.Opacity == nil || *result[0].Actions.Opacity != 0.8 { - t.Errorf("rule 0 Opacity = %v, want 0.8", result[0].Actions.Opacity) - } - - if result[1].Actions.OpenFloating == nil || !*result[1].Actions.OpenFloating { - t.Error("rule 1 should have OpenFloating = true") - } -} - -func TestNiriWritableProvider(t *testing.T) { - tmpDir := t.TempDir() - provider := NewNiriWritableProvider(tmpDir) - - if provider.Name() != "niri" { - t.Errorf("Name() = %q, want niri", provider.Name()) - } - - expectedPath := filepath.Join(tmpDir, "dms", "windowrules.kdl") - if provider.GetOverridePath() != expectedPath { - t.Errorf("GetOverridePath() = %q, want %q", provider.GetOverridePath(), expectedPath) - } -} - -func TestNiriSetAndLoadDMSRules(t *testing.T) { - tmpDir := t.TempDir() - provider := NewNiriWritableProvider(tmpDir) - - rule := newTestWindowRule("test_id", "Test Rule", "^firefox$") - rule.Actions.OpenFloating = boolPtr(true) - rule.Actions.Opacity = floatPtr(0.85) - - if err := provider.SetRule(rule); err != nil { - t.Fatalf("SetRule failed: %v", err) - } - - rules, err := provider.LoadDMSRules() - if err != nil { - t.Fatalf("LoadDMSRules failed: %v", err) - } - - if len(rules) != 1 { - t.Fatalf("expected 1 rule, got %d", len(rules)) - } - - if rules[0].ID != "test_id" { - t.Errorf("ID = %q, want test_id", rules[0].ID) - } - if rules[0].MatchCriteria.AppID != "^firefox$" { - t.Errorf("AppID = %q, want ^firefox$", rules[0].MatchCriteria.AppID) - } -} - -func TestNiriRemoveRule(t *testing.T) { - tmpDir := t.TempDir() - provider := NewNiriWritableProvider(tmpDir) - - rule1 := newTestWindowRule("rule1", "Rule 1", "app1") - rule1.Actions.OpenFloating = boolPtr(true) - rule2 := newTestWindowRule("rule2", "Rule 2", "app2") - rule2.Actions.OpenFloating = boolPtr(true) - - _ = provider.SetRule(rule1) - _ = provider.SetRule(rule2) - - if err := provider.RemoveRule("rule1"); err != nil { - t.Fatalf("RemoveRule failed: %v", err) - } - - rules, _ := provider.LoadDMSRules() - if len(rules) != 1 { - t.Fatalf("expected 1 rule after removal, got %d", len(rules)) - } - if rules[0].ID != "rule2" { - t.Errorf("remaining rule ID = %q, want rule2", rules[0].ID) - } -} - -func TestNiriReorderRules(t *testing.T) { - tmpDir := t.TempDir() - provider := NewNiriWritableProvider(tmpDir) - - rule1 := newTestWindowRule("rule1", "Rule 1", "app1") - rule1.Actions.OpenFloating = boolPtr(true) - rule2 := newTestWindowRule("rule2", "Rule 2", "app2") - rule2.Actions.OpenFloating = boolPtr(true) - rule3 := newTestWindowRule("rule3", "Rule 3", "app3") - rule3.Actions.OpenFloating = boolPtr(true) - - _ = provider.SetRule(rule1) - _ = provider.SetRule(rule2) - _ = provider.SetRule(rule3) - - if err := provider.ReorderRules([]string{"rule3", "rule1", "rule2"}); err != nil { - t.Fatalf("ReorderRules failed: %v", err) - } - - rules, _ := provider.LoadDMSRules() - if len(rules) != 3 { - t.Fatalf("expected 3 rules, got %d", len(rules)) - } - expectedOrder := []string{"rule3", "rule1", "rule2"} - for i, expectedID := range expectedOrder { - if rules[i].ID != expectedID { - t.Errorf("rule %d ID = %q, want %q", i, rules[i].ID, expectedID) - } - } -} - -func TestNiriParseConfigWithInclude(t *testing.T) { - tmpDir := t.TempDir() - - mainConfig := ` -window-rule { - match app-id="mainapp" - opacity 1.0 -} - -include "extra.kdl" -` - extraConfig := ` -window-rule { - match app-id="extraapp" - open-maximized true -} -` - - if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(mainConfig), 0644); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(tmpDir, "extra.kdl"), []byte(extraConfig), 0644); err != nil { - t.Fatal(err) - } - - parser := NewNiriRulesParser(tmpDir) - rules, err := parser.Parse() - if err != nil { - t.Fatalf("Parse failed: %v", err) - } - - if len(rules) != 2 { - t.Errorf("expected 2 rules, got %d", len(rules)) - } -} - -func TestNiriParseSizeNode(t *testing.T) { - tmpDir := t.TempDir() - - config := ` -window-rule { - match app-id="testapp" - default-column-width { fixed 800; } - default-window-height { proportion 0.5; } -} -` - if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(config), 0644); err != nil { - t.Fatal(err) - } - - parser := NewNiriRulesParser(tmpDir) - rules, err := parser.Parse() - if err != nil { - t.Fatalf("Parse failed: %v", err) - } - - if len(rules) != 1 { - t.Fatalf("expected 1 rule, got %d", len(rules)) - } - - if rules[0].DefaultColumnWidth != "fixed 800" { - t.Errorf("DefaultColumnWidth = %q, want 'fixed 800'", rules[0].DefaultColumnWidth) - } - if rules[0].DefaultWindowHeight != "proportion 0.5" { - t.Errorf("DefaultWindowHeight = %q, want 'proportion 0.5'", rules[0].DefaultWindowHeight) - } -} - -func TestFormatSizeProperty(t *testing.T) { - tests := []struct { - name string - propName string - value string - want string - }{ - { - name: "fixed size", - propName: "default-column-width", - value: "fixed 800", - want: " default-column-width { fixed 800; }", - }, - { - name: "proportion", - propName: "default-window-height", - value: "proportion 0.5", - want: " default-window-height { proportion 0.5; }", - }, - { - name: "invalid format", - propName: "default-column-width", - value: "invalid", - want: " default-column-width { }", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := formatSizeProperty(tt.propName, tt.value) - if result != tt.want { - t.Errorf("formatSizeProperty(%q, %q) = %q, want %q", - tt.propName, tt.value, result, tt.want) - } - }) - } -} - -func TestNiriDMSRulesStatus(t *testing.T) { - tmpDir := t.TempDir() - - config := ` -window-rule { - match app-id="testapp" - opacity 0.9 -} -` - if err := os.WriteFile(filepath.Join(tmpDir, "config.kdl"), []byte(config), 0644); err != nil { - t.Fatal(err) - } - - result, err := ParseNiriWindowRules(tmpDir) - if err != nil { - t.Fatalf("ParseNiriWindowRules failed: %v", err) - } - - if result.DMSStatus == nil { - t.Fatal("DMSStatus should not be nil") - } - - if result.DMSStatus.Exists { - t.Error("DMSStatus.Exists should be false when dms rules file doesn't exist") - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/providers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/providers_test.go deleted file mode 100644 index de84a38..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/providers/providers_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package providers - -import "github.com/AvengeMedia/DankMaterialShell/core/internal/windowrules" - -func newTestWindowRule(id, name, appID string) windowrules.WindowRule { - return windowrules.WindowRule{ - ID: id, - Name: name, - Enabled: true, - MatchCriteria: windowrules.MatchCriteria{ - AppID: appID, - }, - } -} - -func boolPtr(b bool) *bool { - return &b -} - -func floatPtr(f float64) *float64 { - return &f -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/types.go deleted file mode 100644 index 556acb7..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/windowrules/types.go +++ /dev/null @@ -1,103 +0,0 @@ -package windowrules - -type MatchCriteria struct { - AppID string `json:"appId,omitempty"` - Title string `json:"title,omitempty"` - IsFloating *bool `json:"isFloating,omitempty"` - IsActive *bool `json:"isActive,omitempty"` - IsFocused *bool `json:"isFocused,omitempty"` - IsActiveInColumn *bool `json:"isActiveInColumn,omitempty"` - IsWindowCastTarget *bool `json:"isWindowCastTarget,omitempty"` - IsUrgent *bool `json:"isUrgent,omitempty"` - AtStartup *bool `json:"atStartup,omitempty"` - XWayland *bool `json:"xwayland,omitempty"` - Fullscreen *bool `json:"fullscreen,omitempty"` - Pinned *bool `json:"pinned,omitempty"` - Initialised *bool `json:"initialised,omitempty"` -} - -type Actions struct { - Opacity *float64 `json:"opacity,omitempty"` - OpenFloating *bool `json:"openFloating,omitempty"` - OpenMaximized *bool `json:"openMaximized,omitempty"` - OpenMaximizedToEdges *bool `json:"openMaximizedToEdges,omitempty"` - OpenFullscreen *bool `json:"openFullscreen,omitempty"` - OpenFocused *bool `json:"openFocused,omitempty"` - OpenOnOutput string `json:"openOnOutput,omitempty"` - OpenOnWorkspace string `json:"openOnWorkspace,omitempty"` - DefaultColumnWidth string `json:"defaultColumnWidth,omitempty"` - DefaultWindowHeight string `json:"defaultWindowHeight,omitempty"` - VariableRefreshRate *bool `json:"variableRefreshRate,omitempty"` - BlockOutFrom string `json:"blockOutFrom,omitempty"` - DefaultColumnDisplay string `json:"defaultColumnDisplay,omitempty"` - ScrollFactor *float64 `json:"scrollFactor,omitempty"` - CornerRadius *int `json:"cornerRadius,omitempty"` - ClipToGeometry *bool `json:"clipToGeometry,omitempty"` - TiledState *bool `json:"tiledState,omitempty"` - MinWidth *int `json:"minWidth,omitempty"` - MaxWidth *int `json:"maxWidth,omitempty"` - MinHeight *int `json:"minHeight,omitempty"` - MaxHeight *int `json:"maxHeight,omitempty"` - BorderColor string `json:"borderColor,omitempty"` - FocusRingColor string `json:"focusRingColor,omitempty"` - FocusRingOff *bool `json:"focusRingOff,omitempty"` - BorderOff *bool `json:"borderOff,omitempty"` - DrawBorderWithBg *bool `json:"drawBorderWithBackground,omitempty"` - Size string `json:"size,omitempty"` - Move string `json:"move,omitempty"` - Monitor string `json:"monitor,omitempty"` - Workspace string `json:"workspace,omitempty"` - Tile *bool `json:"tile,omitempty"` - NoFocus *bool `json:"nofocus,omitempty"` - NoBorder *bool `json:"noborder,omitempty"` - NoShadow *bool `json:"noshadow,omitempty"` - NoDim *bool `json:"nodim,omitempty"` - NoBlur *bool `json:"noblur,omitempty"` - NoAnim *bool `json:"noanim,omitempty"` - NoRounding *bool `json:"norounding,omitempty"` - Pin *bool `json:"pin,omitempty"` - Opaque *bool `json:"opaque,omitempty"` - ForcergbX *bool `json:"forcergbx,omitempty"` - Idleinhibit string `json:"idleinhibit,omitempty"` -} - -type WindowRule struct { - ID string `json:"id"` - Name string `json:"name,omitempty"` - Enabled bool `json:"enabled"` - MatchCriteria MatchCriteria `json:"matchCriteria"` - Actions Actions `json:"actions"` - Source string `json:"source,omitempty"` -} - -type DMSRulesStatus struct { - Exists bool `json:"exists"` - Included bool `json:"included"` - IncludePosition int `json:"includePosition"` - TotalIncludes int `json:"totalIncludes"` - RulesAfterDMS int `json:"rulesAfterDms"` - Effective bool `json:"effective"` - OverriddenBy int `json:"overriddenBy"` - StatusMessage string `json:"statusMessage"` -} - -type RuleSet struct { - Title string `json:"title"` - Provider string `json:"provider"` - Rules []WindowRule `json:"rules"` - DMSRulesIncluded bool `json:"dmsRulesIncluded"` - DMSStatus *DMSRulesStatus `json:"dmsStatus,omitempty"` -} - -type Provider interface { - Name() string - GetRuleSet() (*RuleSet, error) -} - -type WritableProvider interface { - Provider - SetRule(rule WindowRule) error - RemoveRule(id string) error - ReorderRules(ids []string) error - GetOverridePath() string -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go deleted file mode 100644 index 4080b66..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant.go +++ /dev/null @@ -1,95 +0,0 @@ -package dbusutil - -import "github.com/godbus/dbus/v5" - -func As[T any](v dbus.Variant) (T, bool) { - val, ok := v.Value().(T) - return val, ok -} - -func AsOr[T any](v dbus.Variant, def T) T { - if val, ok := v.Value().(T); ok { - return val - } - return def -} - -func Get[T any](m map[string]dbus.Variant, key string) (T, bool) { - v, ok := m[key] - if !ok { - var zero T - return zero, false - } - return As[T](v) -} - -func GetOr[T any](m map[string]dbus.Variant, key string, def T) T { - v, ok := m[key] - if !ok { - return def - } - return AsOr(v, def) -} - -func Normalize(v any) any { - switch val := v.(type) { - case dbus.Variant: - return Normalize(val.Value()) - case dbus.ObjectPath: - return string(val) - case []dbus.ObjectPath: - result := make([]string, len(val)) - for i, p := range val { - result[i] = string(p) - } - return result - case map[string]dbus.Variant: - result := make(map[string]any) - for k, vv := range val { - result[k] = Normalize(vv.Value()) - } - return result - case map[string]any: - result := make(map[string]any) - for k, vv := range val { - result[k] = Normalize(vv) - } - return result - case map[dbus.ObjectPath]map[string]map[string]dbus.Variant: - result := make(map[string]any) - for path, ifaces := range val { - ifaceMap := make(map[string]any) - for ifaceName, props := range ifaces { - propMap := make(map[string]any) - for propName, propVal := range props { - propMap[propName] = Normalize(propVal.Value()) - } - ifaceMap[ifaceName] = propMap - } - result[string(path)] = ifaceMap - } - return result - case []any: - result := make([]any, len(val)) - for i, item := range val { - result[i] = Normalize(item) - } - return result - case []dbus.Variant: - result := make([]any, len(val)) - for i, item := range val { - result[i] = Normalize(item.Value()) - } - return result - default: - return v - } -} - -func NormalizeSlice(values []any) []any { - result := make([]any, len(values)) - for i, v := range values { - result[i] = Normalize(v) - } - return result -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go deleted file mode 100644 index a927e54..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/dbusutil/variant_test.go +++ /dev/null @@ -1,155 +0,0 @@ -package dbusutil - -import ( - "testing" - - "github.com/godbus/dbus/v5" - "github.com/stretchr/testify/assert" -) - -func TestAs(t *testing.T) { - t.Run("string", func(t *testing.T) { - v := dbus.MakeVariant("hello") - val, ok := As[string](v) - assert.True(t, ok) - assert.Equal(t, "hello", val) - }) - - t.Run("bool", func(t *testing.T) { - v := dbus.MakeVariant(true) - val, ok := As[bool](v) - assert.True(t, ok) - assert.True(t, val) - }) - - t.Run("int32", func(t *testing.T) { - v := dbus.MakeVariant(int32(42)) - val, ok := As[int32](v) - assert.True(t, ok) - assert.Equal(t, int32(42), val) - }) - - t.Run("wrong type", func(t *testing.T) { - v := dbus.MakeVariant("hello") - _, ok := As[int](v) - assert.False(t, ok) - }) -} - -func TestAsOr(t *testing.T) { - t.Run("exists", func(t *testing.T) { - v := dbus.MakeVariant("hello") - val := AsOr(v, "default") - assert.Equal(t, "hello", val) - }) - - t.Run("wrong type uses default", func(t *testing.T) { - v := dbus.MakeVariant(123) - val := AsOr(v, "default") - assert.Equal(t, "default", val) - }) -} - -func TestGet(t *testing.T) { - m := map[string]dbus.Variant{ - "name": dbus.MakeVariant("test"), - "enabled": dbus.MakeVariant(true), - "count": dbus.MakeVariant(int32(5)), - } - - t.Run("exists", func(t *testing.T) { - val, ok := Get[string](m, "name") - assert.True(t, ok) - assert.Equal(t, "test", val) - }) - - t.Run("missing key", func(t *testing.T) { - _, ok := Get[string](m, "missing") - assert.False(t, ok) - }) - - t.Run("wrong type", func(t *testing.T) { - _, ok := Get[int](m, "name") - assert.False(t, ok) - }) -} - -func TestGetOr(t *testing.T) { - m := map[string]dbus.Variant{ - "name": dbus.MakeVariant("test"), - } - - t.Run("exists", func(t *testing.T) { - val := GetOr(m, "name", "default") - assert.Equal(t, "test", val) - }) - - t.Run("missing uses default", func(t *testing.T) { - val := GetOr(m, "missing", "default") - assert.Equal(t, "default", val) - }) - - t.Run("wrong type uses default", func(t *testing.T) { - val := GetOr(m, "name", 42) - assert.Equal(t, 42, val) - }) -} - -func TestNormalize(t *testing.T) { - t.Run("variant unwrap", func(t *testing.T) { - v := dbus.MakeVariant("hello") - result := Normalize(v) - assert.Equal(t, "hello", result) - }) - - t.Run("nested variant", func(t *testing.T) { - v := dbus.MakeVariant(dbus.MakeVariant("nested")) - result := Normalize(v) - assert.Equal(t, "nested", result) - }) - - t.Run("object path", func(t *testing.T) { - v := dbus.ObjectPath("/org/test") - result := Normalize(v) - assert.Equal(t, "/org/test", result) - }) - - t.Run("object path slice", func(t *testing.T) { - v := []dbus.ObjectPath{"/org/a", "/org/b"} - result := Normalize(v) - assert.Equal(t, []string{"/org/a", "/org/b"}, result) - }) - - t.Run("variant map", func(t *testing.T) { - v := map[string]dbus.Variant{ - "key": dbus.MakeVariant("value"), - } - result := Normalize(v) - expected := map[string]any{"key": "value"} - assert.Equal(t, expected, result) - }) - - t.Run("any slice", func(t *testing.T) { - v := []any{dbus.MakeVariant("a"), dbus.ObjectPath("/b")} - result := Normalize(v) - expected := []any{"a", "/b"} - assert.Equal(t, expected, result) - }) - - t.Run("passthrough primitives", func(t *testing.T) { - assert.Equal(t, "hello", Normalize("hello")) - assert.Equal(t, 42, Normalize(42)) - assert.Equal(t, true, Normalize(true)) - }) -} - -func TestNormalizeSlice(t *testing.T) { - input := []any{ - dbus.MakeVariant("a"), - dbus.ObjectPath("/b"), - "c", - } - result := NormalizeSlice(input) - expected := []any{"a", "/b", "c"} - assert.Equal(t, expected, result) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS deleted file mode 100644 index 7b24eab..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -// Keep this sorted - -rajveermalviya diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE deleted file mode 100644 index 4da3300..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright 2021 go-wayland authors - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md deleted file mode 100644 index 9e52d99..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Wayland implementation in Go - -[![Go Reference](https://pkg.go.dev/badge/github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland.svg)](https://pkg.go.dev/github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland) - -This module contains pure Go implementation of the Wayland protocol. -Currently only wayland-client functionality is supported. - -Go code is generated from protocol XML files using -[`go-wayland-scanner`](cmd/go-wayland-scanner/scanner.go). - -To load cursor, minimal port of `wayland-cursor` & `xcursor` in pure Go -is located at [`wayland/cursor`](wayland/cursor) & [`wayland/cursor/xcursor`](wayland/cursor/xcursor) -respectively. - -To demonstrate the functionality of this module -[`examples/imageviewer`](examples/imageviewer) contains a simple image -viewer. It demos displaying a top-level window, resizing of window, -cursor themes, pointer and keyboard. Because it's in pure Go, it can be -compiled without CGO. You can try it using the following commands: - -```sh -CGO_ENABLED=0 go install github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/examples/imageviewer@latest - -imageviewer file.jpg -``` diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate deleted file mode 100755 index 02ad975..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generate +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -cd ./wayland -go generate -x ./... diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep deleted file mode 100755 index bd660d7..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/generatep +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env sh - -# Runs go generate for each directory, but in parallel. Any arguments are appended to the -# go generate command. -# Usage: $ ./generatep [go generate arguments] -# Print all generate commands: $ ./generatep -x - -cd ./wayland || exit 1 -find . -type f -name '*.go' -exec dirname {} \; | sort -u | parallel -j 0 go generate "$1" {}/. diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go deleted file mode 100644 index fd49c20..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/client.go +++ /dev/null @@ -1,7542 +0,0 @@ -// Generated by go-wayland-scanner -// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner -// XML file : https://gitlab.freedesktop.org/wayland/wayland/-/raw/1.24.0/protocol/wayland.xml?ref_type=tags -// -// wayland Protocol Copyright: -// -// Copyright © 2008-2011 Kristian Høgsberg -// Copyright © 2010-2011 Intel Corporation -// Copyright © 2012-2013 Collabora, Ltd. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation files -// (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, -// and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice (including the -// next paragraph) shall be included in all copies or substantial -// portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package client - -import "golang.org/x/sys/unix" - -// DisplayInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const DisplayInterfaceName = "wl_display" - -// Display : core global object -// -// The core global object. This is a special singleton object. It -// is used for internal Wayland protocol features. -type Display struct { - BaseProxy - errorHandler DisplayErrorHandlerFunc - deleteIdHandler DisplayDeleteIdHandlerFunc -} - -// NewDisplay : core global object -// -// The core global object. This is a special singleton object. It -// is used for internal Wayland protocol features. -func NewDisplay(ctx *Context) *Display { - wlDisplay := &Display{} - ctx.Register(wlDisplay) - return wlDisplay -} - -// Sync : asynchronous roundtrip -// -// The sync request asks the server to emit the 'done' event -// on the returned wl_callback object. Since requests are -// handled in-order and events are delivered in-order, this can -// be used as a barrier to ensure all previous requests and the -// resulting events have been handled. -// -// The object returned by this request will be destroyed by the -// compositor after the callback is fired and as such the client must not -// attempt to use it after that point. -// -// The callback_data passed in the callback is undefined and should be ignored. -func (i *Display) Sync() (*Callback, error) { - callback := NewCallback(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], callback.ID()) - err := i.Context().WriteMsg(_reqBuf[:], nil) - return callback, err -} - -// GetRegistry : get global registry object -// -// This request creates a registry object that allows the client -// to list and bind the global objects available from the -// compositor. -// -// It should be noted that the server side resources consumed in -// response to a get_registry request can only be released when the -// client disconnects, not when the client side proxy is destroyed. -// Therefore, clients should invoke get_registry as infrequently as -// possible to avoid wasting memory. -func (i *Display) GetRegistry() (*Registry, error) { - registry := NewRegistry(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], registry.ID()) - err := i.Context().WriteMsg(_reqBuf[:], nil) - return registry, err -} - -func (i *Display) Destroy() error { - i.MarkZombie() - return nil -} - -type DisplayError uint32 - -// DisplayError : global error values -// -// These errors are global and can be emitted in response to any -// server request. -const ( - // DisplayErrorInvalidObject : server couldn't find object - DisplayErrorInvalidObject DisplayError = 0 - // DisplayErrorInvalidMethod : method doesn't exist on the specified interface or malformed request - DisplayErrorInvalidMethod DisplayError = 1 - // DisplayErrorNoMemory : server is out of memory - DisplayErrorNoMemory DisplayError = 2 - // DisplayErrorImplementation : implementation error in compositor - DisplayErrorImplementation DisplayError = 3 -) - -func (e DisplayError) Name() string { - switch e { - case DisplayErrorInvalidObject: - return "invalid_object" - case DisplayErrorInvalidMethod: - return "invalid_method" - case DisplayErrorNoMemory: - return "no_memory" - case DisplayErrorImplementation: - return "implementation" - default: - return "" - } -} - -func (e DisplayError) Value() string { - switch e { - case DisplayErrorInvalidObject: - return "0" - case DisplayErrorInvalidMethod: - return "1" - case DisplayErrorNoMemory: - return "2" - case DisplayErrorImplementation: - return "3" - default: - return "" - } -} - -func (e DisplayError) String() string { - return e.Name() + "=" + e.Value() -} - -// DisplayErrorEvent : fatal error event -// -// The error event is sent out when a fatal (non-recoverable) -// error has occurred. The object_id argument is the object -// where the error occurred, most often in response to a request -// to that object. The code identifies the error and is defined -// by the object interface. As such, each interface defines its -// own set of error codes. The message is a brief description -// of the error, for (debugging) convenience. -type DisplayErrorEvent struct { - ObjectId Proxy - Code uint32 - Message string -} -type DisplayErrorHandlerFunc func(DisplayErrorEvent) - -// SetErrorHandler : sets handler for DisplayErrorEvent -func (i *Display) SetErrorHandler(f DisplayErrorHandlerFunc) { - i.errorHandler = f -} - -// DisplayDeleteIdEvent : acknowledge object ID deletion -// -// This event is used internally by the object ID management -// logic. When a client deletes an object that it had created, -// the server will send this event to acknowledge that it has -// seen the delete request. When the client receives this event, -// it will know that it can safely reuse the object ID. -type DisplayDeleteIdEvent struct { - Id uint32 -} -type DisplayDeleteIdHandlerFunc func(DisplayDeleteIdEvent) - -// SetDeleteIdHandler : sets handler for DisplayDeleteIdEvent -func (i *Display) SetDeleteIdHandler(f DisplayDeleteIdHandlerFunc) { - i.deleteIdHandler = f -} - -func (i *Display) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.errorHandler == nil { - return - } - var e DisplayErrorEvent - l := 0 - e.ObjectId = i.Context().GetProxy(Uint32(data[l : l+4])) - l += 4 - e.Code = Uint32(data[l : l+4]) - l += 4 - messageLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.Message = String(data[l : l+messageLen]) - - i.errorHandler(e) - case 1: - var e DisplayDeleteIdEvent - l := 0 - e.Id = Uint32(data[l : l+4]) - l += 4 - - i.Context().DeleteID(e.Id) - - if i.deleteIdHandler != nil { - i.deleteIdHandler(e) - } - } -} - -// RegistryInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const RegistryInterfaceName = "wl_registry" - -// Registry : global registry object -// -// The singleton global registry object. The server has a number of -// global objects that are available to all clients. These objects -// typically represent an actual object in the server (for example, -// an input device) or they are singleton objects that provide -// extension functionality. -// -// When a client creates a registry object, the registry object -// will emit a global event for each global currently in the -// registry. Globals come and go as a result of device or -// monitor hotplugs, reconfiguration or other events, and the -// registry will send out global and global_remove events to -// keep the client up to date with the changes. To mark the end -// of the initial burst of events, the client can use the -// wl_display.sync request immediately after calling -// wl_display.get_registry. -// -// A client can bind to a global object by using the bind -// request. This creates a client-side handle that lets the object -// emit events to the client and lets the client invoke requests on -// the object. -type Registry struct { - BaseProxy - globalHandler RegistryGlobalHandlerFunc - globalRemoveHandler RegistryGlobalRemoveHandlerFunc -} - -// NewRegistry : global registry object -// -// The singleton global registry object. The server has a number of -// global objects that are available to all clients. These objects -// typically represent an actual object in the server (for example, -// an input device) or they are singleton objects that provide -// extension functionality. -// -// When a client creates a registry object, the registry object -// will emit a global event for each global currently in the -// registry. Globals come and go as a result of device or -// monitor hotplugs, reconfiguration or other events, and the -// registry will send out global and global_remove events to -// keep the client up to date with the changes. To mark the end -// of the initial burst of events, the client can use the -// wl_display.sync request immediately after calling -// wl_display.get_registry. -// -// A client can bind to a global object by using the bind -// request. This creates a client-side handle that lets the object -// emit events to the client and lets the client invoke requests on -// the object. -func NewRegistry(ctx *Context) *Registry { - wlRegistry := &Registry{} - ctx.Register(wlRegistry) - return wlRegistry -} - -// Bind : bind an object to the display -// -// Binds a new, client-created object to the server using the -// specified name as the identifier. -// -// name: unique numeric name of the object -func (i *Registry) Bind(name uint32, iface string, version uint32, id Proxy) error { - const opcode = 0 - ifaceLen := PaddedLen(len(iface) + 1) - _reqBufLen := 8 + 4 + (4 + ifaceLen) + 4 + 4 - _reqBuf := make([]byte, _reqBufLen) - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(name)) - l += 4 - PutString(_reqBuf[l:l+(4+ifaceLen)], iface) - l += (4 + ifaceLen) - PutUint32(_reqBuf[l:l+4], uint32(version)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf, nil) - return err -} - -func (i *Registry) Destroy() error { - i.MarkZombie() - return nil -} - -// RegistryGlobalEvent : announce global object -// -// Notify the client of global objects. -// -// The event notifies the client that a global object with -// the given name is now available, and it implements the -// given version of the given interface. -type RegistryGlobalEvent struct { - Name uint32 - Interface string - Version uint32 -} -type RegistryGlobalHandlerFunc func(RegistryGlobalEvent) - -// SetGlobalHandler : sets handler for RegistryGlobalEvent -func (i *Registry) SetGlobalHandler(f RegistryGlobalHandlerFunc) { - i.globalHandler = f -} - -// RegistryGlobalRemoveEvent : announce removal of global object -// -// Notify the client of removed global objects. -// -// This event notifies the client that the global identified -// by name is no longer available. If the client bound to -// the global using the bind request, the client should now -// destroy that object. -// -// The object remains valid and requests to the object will be -// ignored until the client destroys it, to avoid races between -// the global going away and a client sending a request to it. -type RegistryGlobalRemoveEvent struct { - Name uint32 -} -type RegistryGlobalRemoveHandlerFunc func(RegistryGlobalRemoveEvent) - -// SetGlobalRemoveHandler : sets handler for RegistryGlobalRemoveEvent -func (i *Registry) SetGlobalRemoveHandler(f RegistryGlobalRemoveHandlerFunc) { - i.globalRemoveHandler = f -} - -func (i *Registry) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.globalHandler == nil { - return - } - var e RegistryGlobalEvent - l := 0 - e.Name = Uint32(data[l : l+4]) - l += 4 - interfaceLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.Interface = String(data[l : l+interfaceLen]) - l += interfaceLen - e.Version = Uint32(data[l : l+4]) - l += 4 - - i.globalHandler(e) - case 1: - if i.globalRemoveHandler == nil { - return - } - var e RegistryGlobalRemoveEvent - l := 0 - e.Name = Uint32(data[l : l+4]) - l += 4 - - i.globalRemoveHandler(e) - } -} - -// CallbackInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const CallbackInterfaceName = "wl_callback" - -// Callback : callback object -// -// Clients can handle the 'done' event to get notified when -// the related request is done. -// -// Note, because wl_callback objects are created from multiple independent -// factory interfaces, the wl_callback interface is frozen at version 1. -type Callback struct { - BaseProxy - doneHandler CallbackDoneHandlerFunc -} - -// NewCallback : callback object -// -// Clients can handle the 'done' event to get notified when -// the related request is done. -// -// Note, because wl_callback objects are created from multiple independent -// factory interfaces, the wl_callback interface is frozen at version 1. -func NewCallback(ctx *Context) *Callback { - wlCallback := &Callback{} - ctx.Register(wlCallback) - return wlCallback -} - -func (i *Callback) Destroy() error { - i.MarkZombie() - return nil -} - -// CallbackDoneEvent : done event -// -// Notify the client when the related request is done. -type CallbackDoneEvent struct { - CallbackData uint32 -} -type CallbackDoneHandlerFunc func(CallbackDoneEvent) - -// SetDoneHandler : sets handler for CallbackDoneEvent -func (i *Callback) SetDoneHandler(f CallbackDoneHandlerFunc) { - i.doneHandler = f -} - -func (i *Callback) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.doneHandler == nil { - return - } - var e CallbackDoneEvent - l := 0 - e.CallbackData = Uint32(data[l : l+4]) - l += 4 - - i.doneHandler(e) - } -} - -// CompositorInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const CompositorInterfaceName = "wl_compositor" - -// Compositor : the compositor singleton -// -// A compositor. This object is a singleton global. The -// compositor is in charge of combining the contents of multiple -// surfaces into one displayable output. -type Compositor struct { - BaseProxy -} - -// NewCompositor : the compositor singleton -// -// A compositor. This object is a singleton global. The -// compositor is in charge of combining the contents of multiple -// surfaces into one displayable output. -func NewCompositor(ctx *Context) *Compositor { - wlCompositor := &Compositor{} - ctx.Register(wlCompositor) - return wlCompositor -} - -// CreateSurface : create new surface -// -// Ask the compositor to create a new surface. -func (i *Compositor) CreateSurface() (*Surface, error) { - id := NewSurface(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// CreateRegion : create new region -// -// Ask the compositor to create a new region. -func (i *Compositor) CreateRegion() (*Region, error) { - id := NewRegion(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -func (i *Compositor) Destroy() error { - i.MarkZombie() - return nil -} - -// ShmPoolInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ShmPoolInterfaceName = "wl_shm_pool" - -// ShmPool : a shared memory pool -// -// The wl_shm_pool object encapsulates a piece of memory shared -// between the compositor and client. Through the wl_shm_pool -// object, the client can allocate shared memory wl_buffer objects. -// All objects created through the same pool share the same -// underlying mapped memory. Reusing the mapped memory avoids the -// setup/teardown overhead and is useful when interactively resizing -// a surface or for many small buffers. -type ShmPool struct { - BaseProxy -} - -// NewShmPool : a shared memory pool -// -// The wl_shm_pool object encapsulates a piece of memory shared -// between the compositor and client. Through the wl_shm_pool -// object, the client can allocate shared memory wl_buffer objects. -// All objects created through the same pool share the same -// underlying mapped memory. Reusing the mapped memory avoids the -// setup/teardown overhead and is useful when interactively resizing -// a surface or for many small buffers. -func NewShmPool(ctx *Context) *ShmPool { - wlShmPool := &ShmPool{} - ctx.Register(wlShmPool) - return wlShmPool -} - -// CreateBuffer : create a buffer from the pool -// -// Create a wl_buffer object from the pool. -// -// The buffer is created offset bytes into the pool and has -// width and height as specified. The stride argument specifies -// the number of bytes from the beginning of one row to the beginning -// of the next. The format is the pixel format of the buffer and -// must be one of those advertised through the wl_shm.format event. -// -// A buffer will keep a reference to the pool it was created from -// so it is valid to destroy the pool immediately after creating -// a buffer from it. -// -// offset: buffer byte offset within the pool -// width: buffer width, in pixels -// height: buffer height, in pixels -// stride: number of bytes from the beginning of one row to the beginning of the next row -// format: buffer pixel format -func (i *ShmPool) CreateBuffer(offset, width, height, stride int32, format uint32) (*Buffer, error) { - id := NewBuffer(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(offset)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(stride)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(format)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// Destroy : destroy the pool -// -// Destroy the shared memory pool. -// -// The mmapped memory will be released when all -// buffers that have been created from this pool -// are gone. -func (i *ShmPool) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Resize : change the size of the pool mapping -// -// This request will cause the server to remap the backing memory -// for the pool from the file descriptor passed when the pool was -// created, but using the new size. This request can only be -// used to make the pool bigger. -// -// This request only changes the amount of bytes that are mmapped -// by the server and does not touch the file corresponding to the -// file descriptor passed at creation time. It is the client's -// responsibility to ensure that the file is at least as big as -// the new pool size. -// -// size: new size of the pool, in bytes -func (i *ShmPool) Resize(size int32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(size)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// ShmInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ShmInterfaceName = "wl_shm" - -// Shm : shared memory support -// -// A singleton global object that provides support for shared -// memory. -// -// Clients can create wl_shm_pool objects using the create_pool -// request. -// -// On binding the wl_shm object one or more format events -// are emitted to inform clients about the valid pixel formats -// that can be used for buffers. -type Shm struct { - BaseProxy - formatHandler ShmFormatHandlerFunc -} - -// NewShm : shared memory support -// -// A singleton global object that provides support for shared -// memory. -// -// Clients can create wl_shm_pool objects using the create_pool -// request. -// -// On binding the wl_shm object one or more format events -// are emitted to inform clients about the valid pixel formats -// that can be used for buffers. -func NewShm(ctx *Context) *Shm { - wlShm := &Shm{} - ctx.Register(wlShm) - return wlShm -} - -// CreatePool : create a shm pool -// -// Create a new wl_shm_pool object. -// -// The pool can be used to create shared memory based buffer -// objects. The server will mmap size bytes of the passed file -// descriptor, to use as backing memory for the pool. -// -// fd: file descriptor for the pool -// size: pool size, in bytes -func (i *Shm) CreatePool(fd int, size int32) (*ShmPool, error) { - id := NewShmPool(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(size)) - l += 4 - oob := unix.UnixRights(int(fd)) - err := i.Context().WriteMsg(_reqBuf[:], oob) - return id, err -} - -// Release : release the shm object -// -// Using this request a client can tell the server that it is not going to -// use the shm object anymore. -// -// Objects created via this interface remain unaffected. -func (i *Shm) Release() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type ShmError uint32 - -// ShmError : wl_shm error values -// -// These errors can be emitted in response to wl_shm requests. -const ( - // ShmErrorInvalidFormat : buffer format is not known - ShmErrorInvalidFormat ShmError = 0 - // ShmErrorInvalidStride : invalid size or stride during pool or buffer creation - ShmErrorInvalidStride ShmError = 1 - // ShmErrorInvalidFd : mmapping the file descriptor failed - ShmErrorInvalidFd ShmError = 2 -) - -func (e ShmError) Name() string { - switch e { - case ShmErrorInvalidFormat: - return "invalid_format" - case ShmErrorInvalidStride: - return "invalid_stride" - case ShmErrorInvalidFd: - return "invalid_fd" - default: - return "" - } -} - -func (e ShmError) Value() string { - switch e { - case ShmErrorInvalidFormat: - return "0" - case ShmErrorInvalidStride: - return "1" - case ShmErrorInvalidFd: - return "2" - default: - return "" - } -} - -func (e ShmError) String() string { - return e.Name() + "=" + e.Value() -} - -type ShmFormat uint32 - -// ShmFormat : pixel formats -// -// This describes the memory layout of an individual pixel. -// -// All renderers should support argb8888 and xrgb8888 but any other -// formats are optional and may not be supported by the particular -// renderer in use. -// -// The drm format codes match the macros defined in drm_fourcc.h, except -// argb8888 and xrgb8888. The formats actually supported by the compositor -// will be reported by the format event. -// -// For all wl_shm formats and unless specified in another protocol -// extension, pre-multiplied alpha is used for pixel values. -const ( - // ShmFormatArgb8888 : 32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian - ShmFormatArgb8888 ShmFormat = 0 - // ShmFormatXrgb8888 : 32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian - ShmFormatXrgb8888 ShmFormat = 1 - // ShmFormatC8 : 8-bit color index format, [7:0] C - ShmFormatC8 ShmFormat = 0x20203843 - // ShmFormatRgb332 : 8-bit RGB format, [7:0] R:G:B 3:3:2 - ShmFormatRgb332 ShmFormat = 0x38424752 - // ShmFormatBgr233 : 8-bit BGR format, [7:0] B:G:R 2:3:3 - ShmFormatBgr233 ShmFormat = 0x38524742 - // ShmFormatXrgb4444 : 16-bit xRGB format, [15:0] x:R:G:B 4:4:4:4 little endian - ShmFormatXrgb4444 ShmFormat = 0x32315258 - // ShmFormatXbgr4444 : 16-bit xBGR format, [15:0] x:B:G:R 4:4:4:4 little endian - ShmFormatXbgr4444 ShmFormat = 0x32314258 - // ShmFormatRgbx4444 : 16-bit RGBx format, [15:0] R:G:B:x 4:4:4:4 little endian - ShmFormatRgbx4444 ShmFormat = 0x32315852 - // ShmFormatBgrx4444 : 16-bit BGRx format, [15:0] B:G:R:x 4:4:4:4 little endian - ShmFormatBgrx4444 ShmFormat = 0x32315842 - // ShmFormatArgb4444 : 16-bit ARGB format, [15:0] A:R:G:B 4:4:4:4 little endian - ShmFormatArgb4444 ShmFormat = 0x32315241 - // ShmFormatAbgr4444 : 16-bit ABGR format, [15:0] A:B:G:R 4:4:4:4 little endian - ShmFormatAbgr4444 ShmFormat = 0x32314241 - // ShmFormatRgba4444 : 16-bit RBGA format, [15:0] R:G:B:A 4:4:4:4 little endian - ShmFormatRgba4444 ShmFormat = 0x32314152 - // ShmFormatBgra4444 : 16-bit BGRA format, [15:0] B:G:R:A 4:4:4:4 little endian - ShmFormatBgra4444 ShmFormat = 0x32314142 - // ShmFormatXrgb1555 : 16-bit xRGB format, [15:0] x:R:G:B 1:5:5:5 little endian - ShmFormatXrgb1555 ShmFormat = 0x35315258 - // ShmFormatXbgr1555 : 16-bit xBGR 1555 format, [15:0] x:B:G:R 1:5:5:5 little endian - ShmFormatXbgr1555 ShmFormat = 0x35314258 - // ShmFormatRgbx5551 : 16-bit RGBx 5551 format, [15:0] R:G:B:x 5:5:5:1 little endian - ShmFormatRgbx5551 ShmFormat = 0x35315852 - // ShmFormatBgrx5551 : 16-bit BGRx 5551 format, [15:0] B:G:R:x 5:5:5:1 little endian - ShmFormatBgrx5551 ShmFormat = 0x35315842 - // ShmFormatArgb1555 : 16-bit ARGB 1555 format, [15:0] A:R:G:B 1:5:5:5 little endian - ShmFormatArgb1555 ShmFormat = 0x35315241 - // ShmFormatAbgr1555 : 16-bit ABGR 1555 format, [15:0] A:B:G:R 1:5:5:5 little endian - ShmFormatAbgr1555 ShmFormat = 0x35314241 - // ShmFormatRgba5551 : 16-bit RGBA 5551 format, [15:0] R:G:B:A 5:5:5:1 little endian - ShmFormatRgba5551 ShmFormat = 0x35314152 - // ShmFormatBgra5551 : 16-bit BGRA 5551 format, [15:0] B:G:R:A 5:5:5:1 little endian - ShmFormatBgra5551 ShmFormat = 0x35314142 - // ShmFormatRgb565 : 16-bit RGB 565 format, [15:0] R:G:B 5:6:5 little endian - ShmFormatRgb565 ShmFormat = 0x36314752 - // ShmFormatBgr565 : 16-bit BGR 565 format, [15:0] B:G:R 5:6:5 little endian - ShmFormatBgr565 ShmFormat = 0x36314742 - // ShmFormatRgb888 : 24-bit RGB format, [23:0] R:G:B little endian - ShmFormatRgb888 ShmFormat = 0x34324752 - // ShmFormatBgr888 : 24-bit BGR format, [23:0] B:G:R little endian - ShmFormatBgr888 ShmFormat = 0x34324742 - // ShmFormatXbgr8888 : 32-bit xBGR format, [31:0] x:B:G:R 8:8:8:8 little endian - ShmFormatXbgr8888 ShmFormat = 0x34324258 - // ShmFormatRgbx8888 : 32-bit RGBx format, [31:0] R:G:B:x 8:8:8:8 little endian - ShmFormatRgbx8888 ShmFormat = 0x34325852 - // ShmFormatBgrx8888 : 32-bit BGRx format, [31:0] B:G:R:x 8:8:8:8 little endian - ShmFormatBgrx8888 ShmFormat = 0x34325842 - // ShmFormatAbgr8888 : 32-bit ABGR format, [31:0] A:B:G:R 8:8:8:8 little endian - ShmFormatAbgr8888 ShmFormat = 0x34324241 - // ShmFormatRgba8888 : 32-bit RGBA format, [31:0] R:G:B:A 8:8:8:8 little endian - ShmFormatRgba8888 ShmFormat = 0x34324152 - // ShmFormatBgra8888 : 32-bit BGRA format, [31:0] B:G:R:A 8:8:8:8 little endian - ShmFormatBgra8888 ShmFormat = 0x34324142 - // ShmFormatXrgb2101010 : 32-bit xRGB format, [31:0] x:R:G:B 2:10:10:10 little endian - ShmFormatXrgb2101010 ShmFormat = 0x30335258 - // ShmFormatXbgr2101010 : 32-bit xBGR format, [31:0] x:B:G:R 2:10:10:10 little endian - ShmFormatXbgr2101010 ShmFormat = 0x30334258 - // ShmFormatRgbx1010102 : 32-bit RGBx format, [31:0] R:G:B:x 10:10:10:2 little endian - ShmFormatRgbx1010102 ShmFormat = 0x30335852 - // ShmFormatBgrx1010102 : 32-bit BGRx format, [31:0] B:G:R:x 10:10:10:2 little endian - ShmFormatBgrx1010102 ShmFormat = 0x30335842 - // ShmFormatArgb2101010 : 32-bit ARGB format, [31:0] A:R:G:B 2:10:10:10 little endian - ShmFormatArgb2101010 ShmFormat = 0x30335241 - // ShmFormatAbgr2101010 : 32-bit ABGR format, [31:0] A:B:G:R 2:10:10:10 little endian - ShmFormatAbgr2101010 ShmFormat = 0x30334241 - // ShmFormatRgba1010102 : 32-bit RGBA format, [31:0] R:G:B:A 10:10:10:2 little endian - ShmFormatRgba1010102 ShmFormat = 0x30334152 - // ShmFormatBgra1010102 : 32-bit BGRA format, [31:0] B:G:R:A 10:10:10:2 little endian - ShmFormatBgra1010102 ShmFormat = 0x30334142 - // ShmFormatYuyv : packed YCbCr format, [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian - ShmFormatYuyv ShmFormat = 0x56595559 - // ShmFormatYvyu : packed YCbCr format, [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian - ShmFormatYvyu ShmFormat = 0x55595659 - // ShmFormatUyvy : packed YCbCr format, [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian - ShmFormatUyvy ShmFormat = 0x59565955 - // ShmFormatVyuy : packed YCbCr format, [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian - ShmFormatVyuy ShmFormat = 0x59555956 - // ShmFormatAyuv : packed AYCbCr format, [31:0] A:Y:Cb:Cr 8:8:8:8 little endian - ShmFormatAyuv ShmFormat = 0x56555941 - // ShmFormatNv12 : 2 plane YCbCr Cr:Cb format, 2x2 subsampled Cr:Cb plane - ShmFormatNv12 ShmFormat = 0x3231564e - // ShmFormatNv21 : 2 plane YCbCr Cb:Cr format, 2x2 subsampled Cb:Cr plane - ShmFormatNv21 ShmFormat = 0x3132564e - // ShmFormatNv16 : 2 plane YCbCr Cr:Cb format, 2x1 subsampled Cr:Cb plane - ShmFormatNv16 ShmFormat = 0x3631564e - // ShmFormatNv61 : 2 plane YCbCr Cb:Cr format, 2x1 subsampled Cb:Cr plane - ShmFormatNv61 ShmFormat = 0x3136564e - // ShmFormatYuv410 : 3 plane YCbCr format, 4x4 subsampled Cb (1) and Cr (2) planes - ShmFormatYuv410 ShmFormat = 0x39565559 - // ShmFormatYvu410 : 3 plane YCbCr format, 4x4 subsampled Cr (1) and Cb (2) planes - ShmFormatYvu410 ShmFormat = 0x39555659 - // ShmFormatYuv411 : 3 plane YCbCr format, 4x1 subsampled Cb (1) and Cr (2) planes - ShmFormatYuv411 ShmFormat = 0x31315559 - // ShmFormatYvu411 : 3 plane YCbCr format, 4x1 subsampled Cr (1) and Cb (2) planes - ShmFormatYvu411 ShmFormat = 0x31315659 - // ShmFormatYuv420 : 3 plane YCbCr format, 2x2 subsampled Cb (1) and Cr (2) planes - ShmFormatYuv420 ShmFormat = 0x32315559 - // ShmFormatYvu420 : 3 plane YCbCr format, 2x2 subsampled Cr (1) and Cb (2) planes - ShmFormatYvu420 ShmFormat = 0x32315659 - // ShmFormatYuv422 : 3 plane YCbCr format, 2x1 subsampled Cb (1) and Cr (2) planes - ShmFormatYuv422 ShmFormat = 0x36315559 - // ShmFormatYvu422 : 3 plane YCbCr format, 2x1 subsampled Cr (1) and Cb (2) planes - ShmFormatYvu422 ShmFormat = 0x36315659 - // ShmFormatYuv444 : 3 plane YCbCr format, non-subsampled Cb (1) and Cr (2) planes - ShmFormatYuv444 ShmFormat = 0x34325559 - // ShmFormatYvu444 : 3 plane YCbCr format, non-subsampled Cr (1) and Cb (2) planes - ShmFormatYvu444 ShmFormat = 0x34325659 - // ShmFormatR8 : [7:0] R - ShmFormatR8 ShmFormat = 0x20203852 - // ShmFormatR16 : [15:0] R little endian - ShmFormatR16 ShmFormat = 0x20363152 - // ShmFormatRg88 : [15:0] R:G 8:8 little endian - ShmFormatRg88 ShmFormat = 0x38384752 - // ShmFormatGr88 : [15:0] G:R 8:8 little endian - ShmFormatGr88 ShmFormat = 0x38385247 - // ShmFormatRg1616 : [31:0] R:G 16:16 little endian - ShmFormatRg1616 ShmFormat = 0x32334752 - // ShmFormatGr1616 : [31:0] G:R 16:16 little endian - ShmFormatGr1616 ShmFormat = 0x32335247 - // ShmFormatXrgb16161616F : [63:0] x:R:G:B 16:16:16:16 little endian - ShmFormatXrgb16161616F ShmFormat = 0x48345258 - // ShmFormatXbgr16161616F : [63:0] x:B:G:R 16:16:16:16 little endian - ShmFormatXbgr16161616F ShmFormat = 0x48344258 - // ShmFormatArgb16161616F : [63:0] A:R:G:B 16:16:16:16 little endian - ShmFormatArgb16161616F ShmFormat = 0x48345241 - // ShmFormatAbgr16161616F : [63:0] A:B:G:R 16:16:16:16 little endian - ShmFormatAbgr16161616F ShmFormat = 0x48344241 - // ShmFormatXyuv8888 : [31:0] X:Y:Cb:Cr 8:8:8:8 little endian - ShmFormatXyuv8888 ShmFormat = 0x56555958 - // ShmFormatVuy888 : [23:0] Cr:Cb:Y 8:8:8 little endian - ShmFormatVuy888 ShmFormat = 0x34325556 - // ShmFormatVuy101010 : Y followed by U then V, 10:10:10. - ShmFormatVuy101010 ShmFormat = 0x30335556 - // ShmFormatY210 : [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels - ShmFormatY210 ShmFormat = 0x30313259 - // ShmFormatY212 : [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels - ShmFormatY212 ShmFormat = 0x32313259 - // ShmFormatY216 : [63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels - ShmFormatY216 ShmFormat = 0x36313259 - // ShmFormatY410 : [31:0] A:Cr:Y:Cb 2:10:10:10 little endian - ShmFormatY410 ShmFormat = 0x30313459 - // ShmFormatY412 : [63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian - ShmFormatY412 ShmFormat = 0x32313459 - // ShmFormatY416 : [63:0] A:Cr:Y:Cb 16:16:16:16 little endian - ShmFormatY416 ShmFormat = 0x36313459 - // ShmFormatXvyu2101010 : [31:0] X:Cr:Y:Cb 2:10:10:10 little endian - ShmFormatXvyu2101010 ShmFormat = 0x30335658 - // ShmFormatXvyu1216161616 : [63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian - ShmFormatXvyu1216161616 ShmFormat = 0x36335658 - // ShmFormatXvyu16161616 : [63:0] X:Cr:Y:Cb 16:16:16:16 little endian - ShmFormatXvyu16161616 ShmFormat = 0x38345658 - // ShmFormatY0L0 : [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian - ShmFormatY0L0 ShmFormat = 0x304c3059 - // ShmFormatX0L0 : [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian - ShmFormatX0L0 ShmFormat = 0x304c3058 - // ShmFormatY0L2 : [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian - ShmFormatY0L2 ShmFormat = 0x324c3059 - // ShmFormatX0L2 : [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian - ShmFormatX0L2 ShmFormat = 0x324c3058 - ShmFormatYuv4208Bit ShmFormat = 0x38305559 - ShmFormatYuv42010Bit ShmFormat = 0x30315559 - ShmFormatXrgb8888A8 ShmFormat = 0x38415258 - ShmFormatXbgr8888A8 ShmFormat = 0x38414258 - ShmFormatRgbx8888A8 ShmFormat = 0x38415852 - ShmFormatBgrx8888A8 ShmFormat = 0x38415842 - ShmFormatRgb888A8 ShmFormat = 0x38413852 - ShmFormatBgr888A8 ShmFormat = 0x38413842 - ShmFormatRgb565A8 ShmFormat = 0x38413552 - ShmFormatBgr565A8 ShmFormat = 0x38413542 - // ShmFormatNv24 : non-subsampled Cr:Cb plane - ShmFormatNv24 ShmFormat = 0x3432564e - // ShmFormatNv42 : non-subsampled Cb:Cr plane - ShmFormatNv42 ShmFormat = 0x3234564e - // ShmFormatP210 : 2x1 subsampled Cr:Cb plane, 10 bit per channel - ShmFormatP210 ShmFormat = 0x30313250 - // ShmFormatP010 : 2x2 subsampled Cr:Cb plane 10 bits per channel - ShmFormatP010 ShmFormat = 0x30313050 - // ShmFormatP012 : 2x2 subsampled Cr:Cb plane 12 bits per channel - ShmFormatP012 ShmFormat = 0x32313050 - // ShmFormatP016 : 2x2 subsampled Cr:Cb plane 16 bits per channel - ShmFormatP016 ShmFormat = 0x36313050 - // ShmFormatAxbxgxrx106106106106 : [63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian - ShmFormatAxbxgxrx106106106106 ShmFormat = 0x30314241 - // ShmFormatNv15 : 2x2 subsampled Cr:Cb plane - ShmFormatNv15 ShmFormat = 0x3531564e - ShmFormatQ410 ShmFormat = 0x30313451 - ShmFormatQ401 ShmFormat = 0x31303451 - // ShmFormatXrgb16161616 : [63:0] x:R:G:B 16:16:16:16 little endian - ShmFormatXrgb16161616 ShmFormat = 0x38345258 - // ShmFormatXbgr16161616 : [63:0] x:B:G:R 16:16:16:16 little endian - ShmFormatXbgr16161616 ShmFormat = 0x38344258 - // ShmFormatArgb16161616 : [63:0] A:R:G:B 16:16:16:16 little endian - ShmFormatArgb16161616 ShmFormat = 0x38345241 - // ShmFormatAbgr16161616 : [63:0] A:B:G:R 16:16:16:16 little endian - ShmFormatAbgr16161616 ShmFormat = 0x38344241 - // ShmFormatC1 : [7:0] C0:C1:C2:C3:C4:C5:C6:C7 1:1:1:1:1:1:1:1 eight pixels/byte - ShmFormatC1 ShmFormat = 0x20203143 - // ShmFormatC2 : [7:0] C0:C1:C2:C3 2:2:2:2 four pixels/byte - ShmFormatC2 ShmFormat = 0x20203243 - // ShmFormatC4 : [7:0] C0:C1 4:4 two pixels/byte - ShmFormatC4 ShmFormat = 0x20203443 - // ShmFormatD1 : [7:0] D0:D1:D2:D3:D4:D5:D6:D7 1:1:1:1:1:1:1:1 eight pixels/byte - ShmFormatD1 ShmFormat = 0x20203144 - // ShmFormatD2 : [7:0] D0:D1:D2:D3 2:2:2:2 four pixels/byte - ShmFormatD2 ShmFormat = 0x20203244 - // ShmFormatD4 : [7:0] D0:D1 4:4 two pixels/byte - ShmFormatD4 ShmFormat = 0x20203444 - // ShmFormatD8 : [7:0] D - ShmFormatD8 ShmFormat = 0x20203844 - // ShmFormatR1 : [7:0] R0:R1:R2:R3:R4:R5:R6:R7 1:1:1:1:1:1:1:1 eight pixels/byte - ShmFormatR1 ShmFormat = 0x20203152 - // ShmFormatR2 : [7:0] R0:R1:R2:R3 2:2:2:2 four pixels/byte - ShmFormatR2 ShmFormat = 0x20203252 - // ShmFormatR4 : [7:0] R0:R1 4:4 two pixels/byte - ShmFormatR4 ShmFormat = 0x20203452 - // ShmFormatR10 : [15:0] x:R 6:10 little endian - ShmFormatR10 ShmFormat = 0x20303152 - // ShmFormatR12 : [15:0] x:R 4:12 little endian - ShmFormatR12 ShmFormat = 0x20323152 - // ShmFormatAvuy8888 : [31:0] A:Cr:Cb:Y 8:8:8:8 little endian - ShmFormatAvuy8888 ShmFormat = 0x59555641 - // ShmFormatXvuy8888 : [31:0] X:Cr:Cb:Y 8:8:8:8 little endian - ShmFormatXvuy8888 ShmFormat = 0x59555658 - // ShmFormatP030 : 2x2 subsampled Cr:Cb plane 10 bits per channel packed - ShmFormatP030 ShmFormat = 0x30333050 -) - -func (e ShmFormat) Name() string { - switch e { - case ShmFormatArgb8888: - return "argb8888" - case ShmFormatXrgb8888: - return "xrgb8888" - case ShmFormatC8: - return "c8" - case ShmFormatRgb332: - return "rgb332" - case ShmFormatBgr233: - return "bgr233" - case ShmFormatXrgb4444: - return "xrgb4444" - case ShmFormatXbgr4444: - return "xbgr4444" - case ShmFormatRgbx4444: - return "rgbx4444" - case ShmFormatBgrx4444: - return "bgrx4444" - case ShmFormatArgb4444: - return "argb4444" - case ShmFormatAbgr4444: - return "abgr4444" - case ShmFormatRgba4444: - return "rgba4444" - case ShmFormatBgra4444: - return "bgra4444" - case ShmFormatXrgb1555: - return "xrgb1555" - case ShmFormatXbgr1555: - return "xbgr1555" - case ShmFormatRgbx5551: - return "rgbx5551" - case ShmFormatBgrx5551: - return "bgrx5551" - case ShmFormatArgb1555: - return "argb1555" - case ShmFormatAbgr1555: - return "abgr1555" - case ShmFormatRgba5551: - return "rgba5551" - case ShmFormatBgra5551: - return "bgra5551" - case ShmFormatRgb565: - return "rgb565" - case ShmFormatBgr565: - return "bgr565" - case ShmFormatRgb888: - return "rgb888" - case ShmFormatBgr888: - return "bgr888" - case ShmFormatXbgr8888: - return "xbgr8888" - case ShmFormatRgbx8888: - return "rgbx8888" - case ShmFormatBgrx8888: - return "bgrx8888" - case ShmFormatAbgr8888: - return "abgr8888" - case ShmFormatRgba8888: - return "rgba8888" - case ShmFormatBgra8888: - return "bgra8888" - case ShmFormatXrgb2101010: - return "xrgb2101010" - case ShmFormatXbgr2101010: - return "xbgr2101010" - case ShmFormatRgbx1010102: - return "rgbx1010102" - case ShmFormatBgrx1010102: - return "bgrx1010102" - case ShmFormatArgb2101010: - return "argb2101010" - case ShmFormatAbgr2101010: - return "abgr2101010" - case ShmFormatRgba1010102: - return "rgba1010102" - case ShmFormatBgra1010102: - return "bgra1010102" - case ShmFormatYuyv: - return "yuyv" - case ShmFormatYvyu: - return "yvyu" - case ShmFormatUyvy: - return "uyvy" - case ShmFormatVyuy: - return "vyuy" - case ShmFormatAyuv: - return "ayuv" - case ShmFormatNv12: - return "nv12" - case ShmFormatNv21: - return "nv21" - case ShmFormatNv16: - return "nv16" - case ShmFormatNv61: - return "nv61" - case ShmFormatYuv410: - return "yuv410" - case ShmFormatYvu410: - return "yvu410" - case ShmFormatYuv411: - return "yuv411" - case ShmFormatYvu411: - return "yvu411" - case ShmFormatYuv420: - return "yuv420" - case ShmFormatYvu420: - return "yvu420" - case ShmFormatYuv422: - return "yuv422" - case ShmFormatYvu422: - return "yvu422" - case ShmFormatYuv444: - return "yuv444" - case ShmFormatYvu444: - return "yvu444" - case ShmFormatR8: - return "r8" - case ShmFormatR16: - return "r16" - case ShmFormatRg88: - return "rg88" - case ShmFormatGr88: - return "gr88" - case ShmFormatRg1616: - return "rg1616" - case ShmFormatGr1616: - return "gr1616" - case ShmFormatXrgb16161616F: - return "xrgb16161616f" - case ShmFormatXbgr16161616F: - return "xbgr16161616f" - case ShmFormatArgb16161616F: - return "argb16161616f" - case ShmFormatAbgr16161616F: - return "abgr16161616f" - case ShmFormatXyuv8888: - return "xyuv8888" - case ShmFormatVuy888: - return "vuy888" - case ShmFormatVuy101010: - return "vuy101010" - case ShmFormatY210: - return "y210" - case ShmFormatY212: - return "y212" - case ShmFormatY216: - return "y216" - case ShmFormatY410: - return "y410" - case ShmFormatY412: - return "y412" - case ShmFormatY416: - return "y416" - case ShmFormatXvyu2101010: - return "xvyu2101010" - case ShmFormatXvyu1216161616: - return "xvyu12_16161616" - case ShmFormatXvyu16161616: - return "xvyu16161616" - case ShmFormatY0L0: - return "y0l0" - case ShmFormatX0L0: - return "x0l0" - case ShmFormatY0L2: - return "y0l2" - case ShmFormatX0L2: - return "x0l2" - case ShmFormatYuv4208Bit: - return "yuv420_8bit" - case ShmFormatYuv42010Bit: - return "yuv420_10bit" - case ShmFormatXrgb8888A8: - return "xrgb8888_a8" - case ShmFormatXbgr8888A8: - return "xbgr8888_a8" - case ShmFormatRgbx8888A8: - return "rgbx8888_a8" - case ShmFormatBgrx8888A8: - return "bgrx8888_a8" - case ShmFormatRgb888A8: - return "rgb888_a8" - case ShmFormatBgr888A8: - return "bgr888_a8" - case ShmFormatRgb565A8: - return "rgb565_a8" - case ShmFormatBgr565A8: - return "bgr565_a8" - case ShmFormatNv24: - return "nv24" - case ShmFormatNv42: - return "nv42" - case ShmFormatP210: - return "p210" - case ShmFormatP010: - return "p010" - case ShmFormatP012: - return "p012" - case ShmFormatP016: - return "p016" - case ShmFormatAxbxgxrx106106106106: - return "axbxgxrx106106106106" - case ShmFormatNv15: - return "nv15" - case ShmFormatQ410: - return "q410" - case ShmFormatQ401: - return "q401" - case ShmFormatXrgb16161616: - return "xrgb16161616" - case ShmFormatXbgr16161616: - return "xbgr16161616" - case ShmFormatArgb16161616: - return "argb16161616" - case ShmFormatAbgr16161616: - return "abgr16161616" - case ShmFormatC1: - return "c1" - case ShmFormatC2: - return "c2" - case ShmFormatC4: - return "c4" - case ShmFormatD1: - return "d1" - case ShmFormatD2: - return "d2" - case ShmFormatD4: - return "d4" - case ShmFormatD8: - return "d8" - case ShmFormatR1: - return "r1" - case ShmFormatR2: - return "r2" - case ShmFormatR4: - return "r4" - case ShmFormatR10: - return "r10" - case ShmFormatR12: - return "r12" - case ShmFormatAvuy8888: - return "avuy8888" - case ShmFormatXvuy8888: - return "xvuy8888" - case ShmFormatP030: - return "p030" - default: - return "" - } -} - -func (e ShmFormat) Value() string { - switch e { - case ShmFormatArgb8888: - return "0" - case ShmFormatXrgb8888: - return "1" - case ShmFormatC8: - return "0x20203843" - case ShmFormatRgb332: - return "0x38424752" - case ShmFormatBgr233: - return "0x38524742" - case ShmFormatXrgb4444: - return "0x32315258" - case ShmFormatXbgr4444: - return "0x32314258" - case ShmFormatRgbx4444: - return "0x32315852" - case ShmFormatBgrx4444: - return "0x32315842" - case ShmFormatArgb4444: - return "0x32315241" - case ShmFormatAbgr4444: - return "0x32314241" - case ShmFormatRgba4444: - return "0x32314152" - case ShmFormatBgra4444: - return "0x32314142" - case ShmFormatXrgb1555: - return "0x35315258" - case ShmFormatXbgr1555: - return "0x35314258" - case ShmFormatRgbx5551: - return "0x35315852" - case ShmFormatBgrx5551: - return "0x35315842" - case ShmFormatArgb1555: - return "0x35315241" - case ShmFormatAbgr1555: - return "0x35314241" - case ShmFormatRgba5551: - return "0x35314152" - case ShmFormatBgra5551: - return "0x35314142" - case ShmFormatRgb565: - return "0x36314752" - case ShmFormatBgr565: - return "0x36314742" - case ShmFormatRgb888: - return "0x34324752" - case ShmFormatBgr888: - return "0x34324742" - case ShmFormatXbgr8888: - return "0x34324258" - case ShmFormatRgbx8888: - return "0x34325852" - case ShmFormatBgrx8888: - return "0x34325842" - case ShmFormatAbgr8888: - return "0x34324241" - case ShmFormatRgba8888: - return "0x34324152" - case ShmFormatBgra8888: - return "0x34324142" - case ShmFormatXrgb2101010: - return "0x30335258" - case ShmFormatXbgr2101010: - return "0x30334258" - case ShmFormatRgbx1010102: - return "0x30335852" - case ShmFormatBgrx1010102: - return "0x30335842" - case ShmFormatArgb2101010: - return "0x30335241" - case ShmFormatAbgr2101010: - return "0x30334241" - case ShmFormatRgba1010102: - return "0x30334152" - case ShmFormatBgra1010102: - return "0x30334142" - case ShmFormatYuyv: - return "0x56595559" - case ShmFormatYvyu: - return "0x55595659" - case ShmFormatUyvy: - return "0x59565955" - case ShmFormatVyuy: - return "0x59555956" - case ShmFormatAyuv: - return "0x56555941" - case ShmFormatNv12: - return "0x3231564e" - case ShmFormatNv21: - return "0x3132564e" - case ShmFormatNv16: - return "0x3631564e" - case ShmFormatNv61: - return "0x3136564e" - case ShmFormatYuv410: - return "0x39565559" - case ShmFormatYvu410: - return "0x39555659" - case ShmFormatYuv411: - return "0x31315559" - case ShmFormatYvu411: - return "0x31315659" - case ShmFormatYuv420: - return "0x32315559" - case ShmFormatYvu420: - return "0x32315659" - case ShmFormatYuv422: - return "0x36315559" - case ShmFormatYvu422: - return "0x36315659" - case ShmFormatYuv444: - return "0x34325559" - case ShmFormatYvu444: - return "0x34325659" - case ShmFormatR8: - return "0x20203852" - case ShmFormatR16: - return "0x20363152" - case ShmFormatRg88: - return "0x38384752" - case ShmFormatGr88: - return "0x38385247" - case ShmFormatRg1616: - return "0x32334752" - case ShmFormatGr1616: - return "0x32335247" - case ShmFormatXrgb16161616F: - return "0x48345258" - case ShmFormatXbgr16161616F: - return "0x48344258" - case ShmFormatArgb16161616F: - return "0x48345241" - case ShmFormatAbgr16161616F: - return "0x48344241" - case ShmFormatXyuv8888: - return "0x56555958" - case ShmFormatVuy888: - return "0x34325556" - case ShmFormatVuy101010: - return "0x30335556" - case ShmFormatY210: - return "0x30313259" - case ShmFormatY212: - return "0x32313259" - case ShmFormatY216: - return "0x36313259" - case ShmFormatY410: - return "0x30313459" - case ShmFormatY412: - return "0x32313459" - case ShmFormatY416: - return "0x36313459" - case ShmFormatXvyu2101010: - return "0x30335658" - case ShmFormatXvyu1216161616: - return "0x36335658" - case ShmFormatXvyu16161616: - return "0x38345658" - case ShmFormatY0L0: - return "0x304c3059" - case ShmFormatX0L0: - return "0x304c3058" - case ShmFormatY0L2: - return "0x324c3059" - case ShmFormatX0L2: - return "0x324c3058" - case ShmFormatYuv4208Bit: - return "0x38305559" - case ShmFormatYuv42010Bit: - return "0x30315559" - case ShmFormatXrgb8888A8: - return "0x38415258" - case ShmFormatXbgr8888A8: - return "0x38414258" - case ShmFormatRgbx8888A8: - return "0x38415852" - case ShmFormatBgrx8888A8: - return "0x38415842" - case ShmFormatRgb888A8: - return "0x38413852" - case ShmFormatBgr888A8: - return "0x38413842" - case ShmFormatRgb565A8: - return "0x38413552" - case ShmFormatBgr565A8: - return "0x38413542" - case ShmFormatNv24: - return "0x3432564e" - case ShmFormatNv42: - return "0x3234564e" - case ShmFormatP210: - return "0x30313250" - case ShmFormatP010: - return "0x30313050" - case ShmFormatP012: - return "0x32313050" - case ShmFormatP016: - return "0x36313050" - case ShmFormatAxbxgxrx106106106106: - return "0x30314241" - case ShmFormatNv15: - return "0x3531564e" - case ShmFormatQ410: - return "0x30313451" - case ShmFormatQ401: - return "0x31303451" - case ShmFormatXrgb16161616: - return "0x38345258" - case ShmFormatXbgr16161616: - return "0x38344258" - case ShmFormatArgb16161616: - return "0x38345241" - case ShmFormatAbgr16161616: - return "0x38344241" - case ShmFormatC1: - return "0x20203143" - case ShmFormatC2: - return "0x20203243" - case ShmFormatC4: - return "0x20203443" - case ShmFormatD1: - return "0x20203144" - case ShmFormatD2: - return "0x20203244" - case ShmFormatD4: - return "0x20203444" - case ShmFormatD8: - return "0x20203844" - case ShmFormatR1: - return "0x20203152" - case ShmFormatR2: - return "0x20203252" - case ShmFormatR4: - return "0x20203452" - case ShmFormatR10: - return "0x20303152" - case ShmFormatR12: - return "0x20323152" - case ShmFormatAvuy8888: - return "0x59555641" - case ShmFormatXvuy8888: - return "0x59555658" - case ShmFormatP030: - return "0x30333050" - default: - return "" - } -} - -func (e ShmFormat) String() string { - return e.Name() + "=" + e.Value() -} - -// ShmFormatEvent : pixel format description -// -// Informs the client about a valid pixel format that -// can be used for buffers. Known formats include -// argb8888 and xrgb8888. -type ShmFormatEvent struct { - Format uint32 -} -type ShmFormatHandlerFunc func(ShmFormatEvent) - -// SetFormatHandler : sets handler for ShmFormatEvent -func (i *Shm) SetFormatHandler(f ShmFormatHandlerFunc) { - i.formatHandler = f -} - -func (i *Shm) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.formatHandler == nil { - return - } - var e ShmFormatEvent - l := 0 - e.Format = Uint32(data[l : l+4]) - l += 4 - - i.formatHandler(e) - } -} - -// BufferInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const BufferInterfaceName = "wl_buffer" - -// Buffer : content for a wl_surface -// -// A buffer provides the content for a wl_surface. Buffers are -// created through factory interfaces such as wl_shm, wp_linux_buffer_params -// (from the linux-dmabuf protocol extension) or similar. It has a width and -// a height and can be attached to a wl_surface, but the mechanism by which a -// client provides and updates the contents is defined by the buffer factory -// interface. -// -// Color channels are assumed to be electrical rather than optical (in other -// words, encoded with a transfer function) unless otherwise specified. If -// the buffer uses a format that has an alpha channel, the alpha channel is -// assumed to be premultiplied into the electrical color channel values -// (after transfer function encoding) unless otherwise specified. -// -// Note, because wl_buffer objects are created from multiple independent -// factory interfaces, the wl_buffer interface is frozen at version 1. -type Buffer struct { - BaseProxy - releaseHandler BufferReleaseHandlerFunc -} - -// NewBuffer : content for a wl_surface -// -// A buffer provides the content for a wl_surface. Buffers are -// created through factory interfaces such as wl_shm, wp_linux_buffer_params -// (from the linux-dmabuf protocol extension) or similar. It has a width and -// a height and can be attached to a wl_surface, but the mechanism by which a -// client provides and updates the contents is defined by the buffer factory -// interface. -// -// Color channels are assumed to be electrical rather than optical (in other -// words, encoded with a transfer function) unless otherwise specified. If -// the buffer uses a format that has an alpha channel, the alpha channel is -// assumed to be premultiplied into the electrical color channel values -// (after transfer function encoding) unless otherwise specified. -// -// Note, because wl_buffer objects are created from multiple independent -// factory interfaces, the wl_buffer interface is frozen at version 1. -func NewBuffer(ctx *Context) *Buffer { - wlBuffer := &Buffer{} - ctx.Register(wlBuffer) - return wlBuffer -} - -// Destroy : destroy a buffer -// -// Destroy a buffer. If and how you need to release the backing -// storage is defined by the buffer factory interface. -// -// For possible side-effects to a surface, see wl_surface.attach. -func (i *Buffer) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// BufferReleaseEvent : compositor releases buffer -// -// Sent when this wl_buffer is no longer used by the compositor. -// -// For more information on when release events may or may not be sent, -// and what consequences it has, please see the description of -// wl_surface.attach. -// -// If a client receives a release event before the frame callback -// requested in the same wl_surface.commit that attaches this -// wl_buffer to a surface, then the client is immediately free to -// reuse the buffer and its backing storage, and does not need a -// second buffer for the next surface content update. Typically -// this is possible, when the compositor maintains a copy of the -// wl_surface contents, e.g. as a GL texture. This is an important -// optimization for GL(ES) compositors with wl_shm clients. -type BufferReleaseEvent struct{} -type BufferReleaseHandlerFunc func(BufferReleaseEvent) - -// SetReleaseHandler : sets handler for BufferReleaseEvent -func (i *Buffer) SetReleaseHandler(f BufferReleaseHandlerFunc) { - i.releaseHandler = f -} - -func (i *Buffer) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.releaseHandler == nil { - return - } - var e BufferReleaseEvent - - i.releaseHandler(e) - } -} - -// DataOfferInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const DataOfferInterfaceName = "wl_data_offer" - -// DataOffer : offer to transfer data -// -// A wl_data_offer represents a piece of data offered for transfer -// by another client (the source client). It is used by the -// copy-and-paste and drag-and-drop mechanisms. The offer -// describes the different mime types that the data can be -// converted to and provides the mechanism for transferring the -// data directly from the source client. -type DataOffer struct { - BaseProxy - offerHandler DataOfferOfferHandlerFunc - sourceActionsHandler DataOfferSourceActionsHandlerFunc - actionHandler DataOfferActionHandlerFunc -} - -// NewDataOffer : offer to transfer data -// -// A wl_data_offer represents a piece of data offered for transfer -// by another client (the source client). It is used by the -// copy-and-paste and drag-and-drop mechanisms. The offer -// describes the different mime types that the data can be -// converted to and provides the mechanism for transferring the -// data directly from the source client. -func NewDataOffer(ctx *Context) *DataOffer { - wlDataOffer := &DataOffer{} - ctx.Register(wlDataOffer) - return wlDataOffer -} - -// Accept : accept one of the offered mime types -// -// Indicate that the client can accept the given mime type, or -// NULL for not accepted. -// -// For objects of version 2 or older, this request is used by the -// client to give feedback whether the client can receive the given -// mime type, or NULL if none is accepted; the feedback does not -// determine whether the drag-and-drop operation succeeds or not. -// -// For objects of version 3 or newer, this request determines the -// final result of the drag-and-drop operation. If the end result -// is that no mime types were accepted, the drag-and-drop operation -// will be cancelled and the corresponding drag source will receive -// wl_data_source.cancelled. Clients may still use this event in -// conjunction with wl_data_source.action for feedback. -// -// serial: serial number of the accept request -// mimeType: mime type accepted by the client -func (i *DataOffer) Accept(serial uint32, mimeType string) error { - const opcode = 0 - mimeTypeLen := PaddedLen(len(mimeType) + 1) - _reqBufLen := 8 + 4 + (4 + mimeTypeLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType) - l += (4 + mimeTypeLen) - err := i.Context().WriteMsg(_reqBuf, nil) - return err -} - -// Receive : request that the data is transferred -// -// To transfer the offered data, the client issues this request -// and indicates the mime type it wants to receive. The transfer -// happens through the passed file descriptor (typically created -// with the pipe system call). The source client writes the data -// in the mime type representation requested and then closes the -// file descriptor. -// -// The receiving client reads from the read end of the pipe until -// EOF and then closes its end, at which point the transfer is -// complete. -// -// This request may happen multiple times for different mime types, -// both before and after wl_data_device.drop. Drag-and-drop destination -// clients may preemptively fetch data or examine it more closely to -// determine acceptance. -// -// mimeType: mime type desired by receiver -// fd: file descriptor for data transfer -func (i *DataOffer) Receive(mimeType string, fd int) error { - const opcode = 1 - mimeTypeLen := PaddedLen(len(mimeType) + 1) - _reqBufLen := 8 + (4 + mimeTypeLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType) - l += (4 + mimeTypeLen) - oob := unix.UnixRights(int(fd)) - err := i.Context().WriteMsg(_reqBuf, oob) - return err -} - -// Destroy : destroy data offer -// -// Destroy the data offer. -func (i *DataOffer) Destroy() error { - defer i.MarkZombie() - const opcode = 2 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Finish : the offer will no longer be used -// -// Notifies the compositor that the drag destination successfully -// finished the drag-and-drop operation. -// -// Upon receiving this request, the compositor will emit -// wl_data_source.dnd_finished on the drag source client. -// -// It is a client error to perform other requests than -// wl_data_offer.destroy after this one. It is also an error to perform -// this request after a NULL mime type has been set in -// wl_data_offer.accept or no action was received through -// wl_data_offer.action. -// -// If wl_data_offer.finish request is received for a non drag and drop -// operation, the invalid_finish protocol error is raised. -func (i *DataOffer) Finish() error { - const opcode = 3 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetActions : set the available/preferred drag-and-drop actions -// -// Sets the actions that the destination side client supports for -// this operation. This request may trigger the emission of -// wl_data_source.action and wl_data_offer.action events if the compositor -// needs to change the selected action. -// -// This request can be called multiple times throughout the -// drag-and-drop operation, typically in response to wl_data_device.enter -// or wl_data_device.motion events. -// -// This request determines the final result of the drag-and-drop -// operation. If the end result is that no action is accepted, -// the drag source will receive wl_data_source.cancelled. -// -// The dnd_actions argument must contain only values expressed in the -// wl_data_device_manager.dnd_actions enum, and the preferred_action -// argument must only contain one of those values set, otherwise it -// will result in a protocol error. -// -// While managing an "ask" action, the destination drag-and-drop client -// may perform further wl_data_offer.receive requests, and is expected -// to perform one last wl_data_offer.set_actions request with a preferred -// action other than "ask" (and optionally wl_data_offer.accept) before -// requesting wl_data_offer.finish, in order to convey the action selected -// by the user. If the preferred action is not in the -// wl_data_offer.source_actions mask, an error will be raised. -// -// If the "ask" action is dismissed (e.g. user cancellation), the client -// is expected to perform wl_data_offer.destroy right away. -// -// This request can only be made on drag-and-drop offers, a protocol error -// will be raised otherwise. -// -// dndActions: actions supported by the destination client -// preferredAction: action preferred by the destination client -func (i *DataOffer) SetActions(dndActions, preferredAction uint32) error { - const opcode = 4 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(dndActions)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(preferredAction)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type DataOfferError uint32 - -// DataOfferError : -const ( - // DataOfferErrorInvalidFinish : finish request was called untimely - DataOfferErrorInvalidFinish DataOfferError = 0 - // DataOfferErrorInvalidActionMask : action mask contains invalid values - DataOfferErrorInvalidActionMask DataOfferError = 1 - // DataOfferErrorInvalidAction : action argument has an invalid value - DataOfferErrorInvalidAction DataOfferError = 2 - // DataOfferErrorInvalidOffer : offer doesn't accept this request - DataOfferErrorInvalidOffer DataOfferError = 3 -) - -func (e DataOfferError) Name() string { - switch e { - case DataOfferErrorInvalidFinish: - return "invalid_finish" - case DataOfferErrorInvalidActionMask: - return "invalid_action_mask" - case DataOfferErrorInvalidAction: - return "invalid_action" - case DataOfferErrorInvalidOffer: - return "invalid_offer" - default: - return "" - } -} - -func (e DataOfferError) Value() string { - switch e { - case DataOfferErrorInvalidFinish: - return "0" - case DataOfferErrorInvalidActionMask: - return "1" - case DataOfferErrorInvalidAction: - return "2" - case DataOfferErrorInvalidOffer: - return "3" - default: - return "" - } -} - -func (e DataOfferError) String() string { - return e.Name() + "=" + e.Value() -} - -// DataOfferOfferEvent : advertise offered mime type -// -// Sent immediately after creating the wl_data_offer object. One -// event per offered mime type. -type DataOfferOfferEvent struct { - MimeType string -} -type DataOfferOfferHandlerFunc func(DataOfferOfferEvent) - -// SetOfferHandler : sets handler for DataOfferOfferEvent -func (i *DataOffer) SetOfferHandler(f DataOfferOfferHandlerFunc) { - i.offerHandler = f -} - -// DataOfferSourceActionsEvent : notify the source-side available actions -// -// This event indicates the actions offered by the data source. It -// will be sent immediately after creating the wl_data_offer object, -// or anytime the source side changes its offered actions through -// wl_data_source.set_actions. -type DataOfferSourceActionsEvent struct { - SourceActions uint32 -} -type DataOfferSourceActionsHandlerFunc func(DataOfferSourceActionsEvent) - -// SetSourceActionsHandler : sets handler for DataOfferSourceActionsEvent -func (i *DataOffer) SetSourceActionsHandler(f DataOfferSourceActionsHandlerFunc) { - i.sourceActionsHandler = f -} - -// DataOfferActionEvent : notify the selected action -// -// This event indicates the action selected by the compositor after -// matching the source/destination side actions. Only one action (or -// none) will be offered here. -// -// This event can be emitted multiple times during the drag-and-drop -// operation in response to destination side action changes through -// wl_data_offer.set_actions. -// -// This event will no longer be emitted after wl_data_device.drop -// happened on the drag-and-drop destination, the client must -// honor the last action received, or the last preferred one set -// through wl_data_offer.set_actions when handling an "ask" action. -// -// Compositors may also change the selected action on the fly, mainly -// in response to keyboard modifier changes during the drag-and-drop -// operation. -// -// The most recent action received is always the valid one. Prior to -// receiving wl_data_device.drop, the chosen action may change (e.g. -// due to keyboard modifiers being pressed). At the time of receiving -// wl_data_device.drop the drag-and-drop destination must honor the -// last action received. -// -// Action changes may still happen after wl_data_device.drop, -// especially on "ask" actions, where the drag-and-drop destination -// may choose another action afterwards. Action changes happening -// at this stage are always the result of inter-client negotiation, the -// compositor shall no longer be able to induce a different action. -// -// Upon "ask" actions, it is expected that the drag-and-drop destination -// may potentially choose a different action and/or mime type, -// based on wl_data_offer.source_actions and finally chosen by the -// user (e.g. popping up a menu with the available options). The -// final wl_data_offer.set_actions and wl_data_offer.accept requests -// must happen before the call to wl_data_offer.finish. -type DataOfferActionEvent struct { - DndAction uint32 -} -type DataOfferActionHandlerFunc func(DataOfferActionEvent) - -// SetActionHandler : sets handler for DataOfferActionEvent -func (i *DataOffer) SetActionHandler(f DataOfferActionHandlerFunc) { - i.actionHandler = f -} - -func (i *DataOffer) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.offerHandler == nil { - return - } - var e DataOfferOfferEvent - l := 0 - mimeTypeLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.MimeType = String(data[l : l+mimeTypeLen]) - l += mimeTypeLen - - i.offerHandler(e) - case 1: - if i.sourceActionsHandler == nil { - return - } - var e DataOfferSourceActionsEvent - l := 0 - e.SourceActions = Uint32(data[l : l+4]) - l += 4 - - i.sourceActionsHandler(e) - case 2: - if i.actionHandler == nil { - return - } - var e DataOfferActionEvent - l := 0 - e.DndAction = Uint32(data[l : l+4]) - l += 4 - - i.actionHandler(e) - } -} - -// DataSourceInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const DataSourceInterfaceName = "wl_data_source" - -// DataSource : offer to transfer data -// -// The wl_data_source object is the source side of a wl_data_offer. -// It is created by the source client in a data transfer and -// provides a way to describe the offered data and a way to respond -// to requests to transfer the data. -type DataSource struct { - BaseProxy - targetHandler DataSourceTargetHandlerFunc - sendHandler DataSourceSendHandlerFunc - cancelledHandler DataSourceCancelledHandlerFunc - dndDropPerformedHandler DataSourceDndDropPerformedHandlerFunc - dndFinishedHandler DataSourceDndFinishedHandlerFunc - actionHandler DataSourceActionHandlerFunc -} - -// NewDataSource : offer to transfer data -// -// The wl_data_source object is the source side of a wl_data_offer. -// It is created by the source client in a data transfer and -// provides a way to describe the offered data and a way to respond -// to requests to transfer the data. -func NewDataSource(ctx *Context) *DataSource { - wlDataSource := &DataSource{} - ctx.Register(wlDataSource) - return wlDataSource -} - -// Offer : add an offered mime type -// -// This request adds a mime type to the set of mime types -// advertised to targets. Can be called several times to offer -// multiple types. -// -// mimeType: mime type offered by the data source -func (i *DataSource) Offer(mimeType string) error { - const opcode = 0 - mimeTypeLen := PaddedLen(len(mimeType) + 1) - _reqBufLen := 8 + (4 + mimeTypeLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType) - l += (4 + mimeTypeLen) - err := i.Context().WriteMsg(_reqBuf, nil) - return err -} - -// Destroy : destroy the data source -// -// Destroy the data source. -func (i *DataSource) Destroy() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetActions : set the available drag-and-drop actions -// -// Sets the actions that the source side client supports for this -// operation. This request may trigger wl_data_source.action and -// wl_data_offer.action events if the compositor needs to change the -// selected action. -// -// The dnd_actions argument must contain only values expressed in the -// wl_data_device_manager.dnd_actions enum, otherwise it will result -// in a protocol error. -// -// This request must be made once only, and can only be made on sources -// used in drag-and-drop, so it must be performed before -// wl_data_device.start_drag. Attempting to use the source other than -// for drag-and-drop will raise a protocol error. -// -// dndActions: actions supported by the data source -func (i *DataSource) SetActions(dndActions uint32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(dndActions)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type DataSourceError uint32 - -// DataSourceError : -const ( - // DataSourceErrorInvalidActionMask : action mask contains invalid values - DataSourceErrorInvalidActionMask DataSourceError = 0 - // DataSourceErrorInvalidSource : source doesn't accept this request - DataSourceErrorInvalidSource DataSourceError = 1 -) - -func (e DataSourceError) Name() string { - switch e { - case DataSourceErrorInvalidActionMask: - return "invalid_action_mask" - case DataSourceErrorInvalidSource: - return "invalid_source" - default: - return "" - } -} - -func (e DataSourceError) Value() string { - switch e { - case DataSourceErrorInvalidActionMask: - return "0" - case DataSourceErrorInvalidSource: - return "1" - default: - return "" - } -} - -func (e DataSourceError) String() string { - return e.Name() + "=" + e.Value() -} - -// DataSourceTargetEvent : a target accepts an offered mime type -// -// Sent when a target accepts pointer_focus or motion events. If -// a target does not accept any of the offered types, type is NULL. -// -// Used for feedback during drag-and-drop. -type DataSourceTargetEvent struct { - MimeType string -} -type DataSourceTargetHandlerFunc func(DataSourceTargetEvent) - -// SetTargetHandler : sets handler for DataSourceTargetEvent -func (i *DataSource) SetTargetHandler(f DataSourceTargetHandlerFunc) { - i.targetHandler = f -} - -// DataSourceSendEvent : send the data -// -// Request for data from the client. Send the data as the -// specified mime type over the passed file descriptor, then -// close it. -type DataSourceSendEvent struct { - MimeType string - Fd int -} -type DataSourceSendHandlerFunc func(DataSourceSendEvent) - -// SetSendHandler : sets handler for DataSourceSendEvent -func (i *DataSource) SetSendHandler(f DataSourceSendHandlerFunc) { - i.sendHandler = f -} - -// DataSourceCancelledEvent : selection was cancelled -// -// This data source is no longer valid. There are several reasons why -// this could happen: -// -// - The data source has been replaced by another data source. -// - The drag-and-drop operation was performed, but the drop destination -// did not accept any of the mime types offered through -// wl_data_source.target. -// - The drag-and-drop operation was performed, but the drop destination -// did not select any of the actions present in the mask offered through -// wl_data_source.action. -// - The drag-and-drop operation was performed but didn't happen over a -// surface. -// - The compositor cancelled the drag-and-drop operation (e.g. compositor -// dependent timeouts to avoid stale drag-and-drop transfers). -// -// The client should clean up and destroy this data source. -// -// For objects of version 2 or older, wl_data_source.cancelled will -// only be emitted if the data source was replaced by another data -// source. -type DataSourceCancelledEvent struct{} -type DataSourceCancelledHandlerFunc func(DataSourceCancelledEvent) - -// SetCancelledHandler : sets handler for DataSourceCancelledEvent -func (i *DataSource) SetCancelledHandler(f DataSourceCancelledHandlerFunc) { - i.cancelledHandler = f -} - -// DataSourceDndDropPerformedEvent : the drag-and-drop operation physically finished -// -// The user performed the drop action. This event does not indicate -// acceptance, wl_data_source.cancelled may still be emitted afterwards -// if the drop destination does not accept any mime type. -// -// However, this event might however not be received if the compositor -// cancelled the drag-and-drop operation before this event could happen. -// -// Note that the data_source may still be used in the future and should -// not be destroyed here. -type DataSourceDndDropPerformedEvent struct{} -type DataSourceDndDropPerformedHandlerFunc func(DataSourceDndDropPerformedEvent) - -// SetDndDropPerformedHandler : sets handler for DataSourceDndDropPerformedEvent -func (i *DataSource) SetDndDropPerformedHandler(f DataSourceDndDropPerformedHandlerFunc) { - i.dndDropPerformedHandler = f -} - -// DataSourceDndFinishedEvent : the drag-and-drop operation concluded -// -// The drop destination finished interoperating with this data -// source, so the client is now free to destroy this data source and -// free all associated data. -// -// If the action used to perform the operation was "move", the -// source can now delete the transferred data. -type DataSourceDndFinishedEvent struct{} -type DataSourceDndFinishedHandlerFunc func(DataSourceDndFinishedEvent) - -// SetDndFinishedHandler : sets handler for DataSourceDndFinishedEvent -func (i *DataSource) SetDndFinishedHandler(f DataSourceDndFinishedHandlerFunc) { - i.dndFinishedHandler = f -} - -// DataSourceActionEvent : notify the selected action -// -// This event indicates the action selected by the compositor after -// matching the source/destination side actions. Only one action (or -// none) will be offered here. -// -// This event can be emitted multiple times during the drag-and-drop -// operation, mainly in response to destination side changes through -// wl_data_offer.set_actions, and as the data device enters/leaves -// surfaces. -// -// It is only possible to receive this event after -// wl_data_source.dnd_drop_performed if the drag-and-drop operation -// ended in an "ask" action, in which case the final wl_data_source.action -// event will happen immediately before wl_data_source.dnd_finished. -// -// Compositors may also change the selected action on the fly, mainly -// in response to keyboard modifier changes during the drag-and-drop -// operation. -// -// The most recent action received is always the valid one. The chosen -// action may change alongside negotiation (e.g. an "ask" action can turn -// into a "move" operation), so the effects of the final action must -// always be applied in wl_data_offer.dnd_finished. -// -// Clients can trigger cursor surface changes from this point, so -// they reflect the current action. -type DataSourceActionEvent struct { - DndAction uint32 -} -type DataSourceActionHandlerFunc func(DataSourceActionEvent) - -// SetActionHandler : sets handler for DataSourceActionEvent -func (i *DataSource) SetActionHandler(f DataSourceActionHandlerFunc) { - i.actionHandler = f -} - -func (i *DataSource) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.targetHandler == nil { - return - } - var e DataSourceTargetEvent - l := 0 - mimeTypeLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.MimeType = String(data[l : l+mimeTypeLen]) - l += mimeTypeLen - - i.targetHandler(e) - case 1: - if i.sendHandler == nil { - if fd != -1 { - unix.Close(fd) - } - return - } - var e DataSourceSendEvent - l := 0 - mimeTypeLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.MimeType = String(data[l : l+mimeTypeLen]) - l += mimeTypeLen - e.Fd = fd - - i.sendHandler(e) - case 2: - if i.cancelledHandler == nil { - return - } - var e DataSourceCancelledEvent - - i.cancelledHandler(e) - case 3: - if i.dndDropPerformedHandler == nil { - return - } - var e DataSourceDndDropPerformedEvent - - i.dndDropPerformedHandler(e) - case 4: - if i.dndFinishedHandler == nil { - return - } - var e DataSourceDndFinishedEvent - - i.dndFinishedHandler(e) - case 5: - if i.actionHandler == nil { - return - } - var e DataSourceActionEvent - l := 0 - e.DndAction = Uint32(data[l : l+4]) - l += 4 - - i.actionHandler(e) - } -} - -// DataDeviceInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const DataDeviceInterfaceName = "wl_data_device" - -// DataDevice : data transfer device -// -// There is one wl_data_device per seat which can be obtained -// from the global wl_data_device_manager singleton. -// -// A wl_data_device provides access to inter-client data transfer -// mechanisms such as copy-and-paste and drag-and-drop. -type DataDevice struct { - BaseProxy - dataOfferHandler DataDeviceDataOfferHandlerFunc - enterHandler DataDeviceEnterHandlerFunc - leaveHandler DataDeviceLeaveHandlerFunc - motionHandler DataDeviceMotionHandlerFunc - dropHandler DataDeviceDropHandlerFunc - selectionHandler DataDeviceSelectionHandlerFunc -} - -// NewDataDevice : data transfer device -// -// There is one wl_data_device per seat which can be obtained -// from the global wl_data_device_manager singleton. -// -// A wl_data_device provides access to inter-client data transfer -// mechanisms such as copy-and-paste and drag-and-drop. -func NewDataDevice(ctx *Context) *DataDevice { - wlDataDevice := &DataDevice{} - ctx.Register(wlDataDevice) - return wlDataDevice -} - -// StartDrag : start drag-and-drop operation -// -// This request asks the compositor to start a drag-and-drop -// operation on behalf of the client. -// -// The source argument is the data source that provides the data -// for the eventual data transfer. If source is NULL, enter, leave -// and motion events are sent only to the client that initiated the -// drag and the client is expected to handle the data passing -// internally. If source is destroyed, the drag-and-drop session will be -// cancelled. -// -// The origin surface is the surface where the drag originates and -// the client must have an active implicit grab that matches the -// serial. -// -// The icon surface is an optional (can be NULL) surface that -// provides an icon to be moved around with the cursor. Initially, -// the top-left corner of the icon surface is placed at the cursor -// hotspot, but subsequent wl_surface.offset requests can move the -// relative position. Attach requests must be confirmed with -// wl_surface.commit as usual. The icon surface is given the role of -// a drag-and-drop icon. If the icon surface already has another role, -// it raises a protocol error. -// -// The input region is ignored for wl_surfaces with the role of a -// drag-and-drop icon. -// -// The given source may not be used in any further set_selection or -// start_drag requests. Attempting to reuse a previously-used source -// may send a used_source error. -// -// source: data source for the eventual transfer -// origin: surface where the drag originates -// icon: drag-and-drop icon surface -// serial: serial number of the implicit grab on the origin -func (i *DataDevice) StartDrag(source *DataSource, origin, icon *Surface, serial uint32) error { - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - if source == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], source.ID()) - l += 4 - } - PutUint32(_reqBuf[l:l+4], origin.ID()) - l += 4 - if icon == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], icon.ID()) - l += 4 - } - PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetSelection : copy data to the selection -// -// This request asks the compositor to set the selection -// to the data from the source on behalf of the client. -// -// To unset the selection, set the source to NULL. -// -// The given source may not be used in any further set_selection or -// start_drag requests. Attempting to reuse a previously-used source -// may send a used_source error. -// -// source: data source for the selection -// serial: serial number of the event that triggered this request -func (i *DataDevice) SetSelection(source *DataSource, serial uint32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - if source == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], source.ID()) - l += 4 - } - PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Release : destroy data device -// -// This request destroys the data device. -func (i *DataDevice) Release() error { - defer i.MarkZombie() - const opcode = 2 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type DataDeviceError uint32 - -// DataDeviceError : -const ( - // DataDeviceErrorRole : given wl_surface has another role - DataDeviceErrorRole DataDeviceError = 0 - // DataDeviceErrorUsedSource : source has already been used - DataDeviceErrorUsedSource DataDeviceError = 1 -) - -func (e DataDeviceError) Name() string { - switch e { - case DataDeviceErrorRole: - return "role" - case DataDeviceErrorUsedSource: - return "used_source" - default: - return "" - } -} - -func (e DataDeviceError) Value() string { - switch e { - case DataDeviceErrorRole: - return "0" - case DataDeviceErrorUsedSource: - return "1" - default: - return "" - } -} - -func (e DataDeviceError) String() string { - return e.Name() + "=" + e.Value() -} - -// DataDeviceDataOfferEvent : introduce a new wl_data_offer -// -// The data_offer event introduces a new wl_data_offer object, -// which will subsequently be used in either the -// data_device.enter event (for drag-and-drop) or the -// data_device.selection event (for selections). Immediately -// following the data_device.data_offer event, the new data_offer -// object will send out data_offer.offer events to describe the -// mime types it offers. -type DataDeviceDataOfferEvent struct { - Id *DataOffer -} -type DataDeviceDataOfferHandlerFunc func(DataDeviceDataOfferEvent) - -// SetDataOfferHandler : sets handler for DataDeviceDataOfferEvent -func (i *DataDevice) SetDataOfferHandler(f DataDeviceDataOfferHandlerFunc) { - i.dataOfferHandler = f -} - -// DataDeviceEnterEvent : initiate drag-and-drop session -// -// This event is sent when an active drag-and-drop pointer enters -// a surface owned by the client. The position of the pointer at -// enter time is provided by the x and y arguments, in surface-local -// coordinates. -type DataDeviceEnterEvent struct { - Serial uint32 - Surface *Surface - X float64 - Y float64 - Id *DataOffer -} -type DataDeviceEnterHandlerFunc func(DataDeviceEnterEvent) - -// SetEnterHandler : sets handler for DataDeviceEnterEvent -func (i *DataDevice) SetEnterHandler(f DataDeviceEnterHandlerFunc) { - i.enterHandler = f -} - -// DataDeviceLeaveEvent : end drag-and-drop session -// -// This event is sent when the drag-and-drop pointer leaves the -// surface and the session ends. The client must destroy the -// wl_data_offer introduced at enter time at this point. -type DataDeviceLeaveEvent struct{} -type DataDeviceLeaveHandlerFunc func(DataDeviceLeaveEvent) - -// SetLeaveHandler : sets handler for DataDeviceLeaveEvent -func (i *DataDevice) SetLeaveHandler(f DataDeviceLeaveHandlerFunc) { - i.leaveHandler = f -} - -// DataDeviceMotionEvent : drag-and-drop session motion -// -// This event is sent when the drag-and-drop pointer moves within -// the currently focused surface. The new position of the pointer -// is provided by the x and y arguments, in surface-local -// coordinates. -type DataDeviceMotionEvent struct { - Time uint32 - X float64 - Y float64 -} -type DataDeviceMotionHandlerFunc func(DataDeviceMotionEvent) - -// SetMotionHandler : sets handler for DataDeviceMotionEvent -func (i *DataDevice) SetMotionHandler(f DataDeviceMotionHandlerFunc) { - i.motionHandler = f -} - -// DataDeviceDropEvent : end drag-and-drop session successfully -// -// The event is sent when a drag-and-drop operation is ended -// because the implicit grab is removed. -// -// The drag-and-drop destination is expected to honor the last action -// received through wl_data_offer.action, if the resulting action is -// "copy" or "move", the destination can still perform -// wl_data_offer.receive requests, and is expected to end all -// transfers with a wl_data_offer.finish request. -// -// If the resulting action is "ask", the action will not be considered -// final. The drag-and-drop destination is expected to perform one last -// wl_data_offer.set_actions request, or wl_data_offer.destroy in order -// to cancel the operation. -type DataDeviceDropEvent struct{} -type DataDeviceDropHandlerFunc func(DataDeviceDropEvent) - -// SetDropHandler : sets handler for DataDeviceDropEvent -func (i *DataDevice) SetDropHandler(f DataDeviceDropHandlerFunc) { - i.dropHandler = f -} - -// DataDeviceSelectionEvent : advertise new selection -// -// The selection event is sent out to notify the client of a new -// wl_data_offer for the selection for this device. The -// data_device.data_offer and the data_offer.offer events are -// sent out immediately before this event to introduce the data -// offer object. The selection event is sent to a client -// immediately before receiving keyboard focus and when a new -// selection is set while the client has keyboard focus. The -// data_offer is valid until a new data_offer or NULL is received -// or until the client loses keyboard focus. Switching surface with -// keyboard focus within the same client doesn't mean a new selection -// will be sent. The client must destroy the previous selection -// data_offer, if any, upon receiving this event. -type DataDeviceSelectionEvent struct { - Id *DataOffer -} -type DataDeviceSelectionHandlerFunc func(DataDeviceSelectionEvent) - -// SetSelectionHandler : sets handler for DataDeviceSelectionEvent -func (i *DataDevice) SetSelectionHandler(f DataDeviceSelectionHandlerFunc) { - i.selectionHandler = f -} - -func (i *DataDevice) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.dataOfferHandler == nil { - return - } - var e DataDeviceDataOfferEvent - l := 0 - e.Id = i.Context().GetProxy(Uint32(data[l : l+4])).(*DataOffer) - l += 4 - - i.dataOfferHandler(e) - case 1: - if i.enterHandler == nil { - return - } - var e DataDeviceEnterEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface) - l += 4 - e.X = Fixed(data[l : l+4]) - l += 4 - e.Y = Fixed(data[l : l+4]) - l += 4 - e.Id = i.Context().GetProxy(Uint32(data[l : l+4])).(*DataOffer) - l += 4 - - i.enterHandler(e) - case 2: - if i.leaveHandler == nil { - return - } - var e DataDeviceLeaveEvent - - i.leaveHandler(e) - case 3: - if i.motionHandler == nil { - return - } - var e DataDeviceMotionEvent - l := 0 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.X = Fixed(data[l : l+4]) - l += 4 - e.Y = Fixed(data[l : l+4]) - l += 4 - - i.motionHandler(e) - case 4: - if i.dropHandler == nil { - return - } - var e DataDeviceDropEvent - - i.dropHandler(e) - case 5: - if i.selectionHandler == nil { - return - } - var e DataDeviceSelectionEvent - l := 0 - e.Id = i.Context().GetProxy(Uint32(data[l : l+4])).(*DataOffer) - l += 4 - - i.selectionHandler(e) - } -} - -// DataDeviceManagerInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const DataDeviceManagerInterfaceName = "wl_data_device_manager" - -// DataDeviceManager : data transfer interface -// -// The wl_data_device_manager is a singleton global object that -// provides access to inter-client data transfer mechanisms such as -// copy-and-paste and drag-and-drop. These mechanisms are tied to -// a wl_seat and this interface lets a client get a wl_data_device -// corresponding to a wl_seat. -// -// Depending on the version bound, the objects created from the bound -// wl_data_device_manager object will have different requirements for -// functioning properly. See wl_data_source.set_actions, -// wl_data_offer.accept and wl_data_offer.finish for details. -type DataDeviceManager struct { - BaseProxy -} - -// NewDataDeviceManager : data transfer interface -// -// The wl_data_device_manager is a singleton global object that -// provides access to inter-client data transfer mechanisms such as -// copy-and-paste and drag-and-drop. These mechanisms are tied to -// a wl_seat and this interface lets a client get a wl_data_device -// corresponding to a wl_seat. -// -// Depending on the version bound, the objects created from the bound -// wl_data_device_manager object will have different requirements for -// functioning properly. See wl_data_source.set_actions, -// wl_data_offer.accept and wl_data_offer.finish for details. -func NewDataDeviceManager(ctx *Context) *DataDeviceManager { - wlDataDeviceManager := &DataDeviceManager{} - ctx.Register(wlDataDeviceManager) - return wlDataDeviceManager -} - -// CreateDataSource : create a new data source -// -// Create a new data source. -func (i *DataDeviceManager) CreateDataSource() (*DataSource, error) { - id := NewDataSource(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// GetDataDevice : create a new data device -// -// Create a new data device for a given seat. -// -// seat: seat associated with the data device -func (i *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) { - id := NewDataDevice(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], seat.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -func (i *DataDeviceManager) Destroy() error { - i.MarkZombie() - return nil -} - -type DataDeviceManagerDndAction uint32 - -// DataDeviceManagerDndAction : drag and drop actions -// -// This is a bitmask of the available/preferred actions in a -// drag-and-drop operation. -// -// In the compositor, the selected action is a result of matching the -// actions offered by the source and destination sides. "action" events -// with a "none" action will be sent to both source and destination if -// there is no match. All further checks will effectively happen on -// (source actions ∩ destination actions). -// -// In addition, compositors may also pick different actions in -// reaction to key modifiers being pressed. One common design that -// is used in major toolkits (and the behavior recommended for -// compositors) is: -// -// - If no modifiers are pressed, the first match (in bit order) -// will be used. -// - Pressing Shift selects "move", if enabled in the mask. -// - Pressing Control selects "copy", if enabled in the mask. -// -// Behavior beyond that is considered implementation-dependent. -// Compositors may for example bind other modifiers (like Alt/Meta) -// or drags initiated with other buttons than BTN_LEFT to specific -// actions (e.g. "ask"). -const ( - // DataDeviceManagerDndActionNone : no action - DataDeviceManagerDndActionNone DataDeviceManagerDndAction = 0 - // DataDeviceManagerDndActionCopy : copy action - DataDeviceManagerDndActionCopy DataDeviceManagerDndAction = 1 - // DataDeviceManagerDndActionMove : move action - DataDeviceManagerDndActionMove DataDeviceManagerDndAction = 2 - // DataDeviceManagerDndActionAsk : ask action - DataDeviceManagerDndActionAsk DataDeviceManagerDndAction = 4 -) - -func (e DataDeviceManagerDndAction) Name() string { - switch e { - case DataDeviceManagerDndActionNone: - return "none" - case DataDeviceManagerDndActionCopy: - return "copy" - case DataDeviceManagerDndActionMove: - return "move" - case DataDeviceManagerDndActionAsk: - return "ask" - default: - return "" - } -} - -func (e DataDeviceManagerDndAction) Value() string { - switch e { - case DataDeviceManagerDndActionNone: - return "0" - case DataDeviceManagerDndActionCopy: - return "1" - case DataDeviceManagerDndActionMove: - return "2" - case DataDeviceManagerDndActionAsk: - return "4" - default: - return "" - } -} - -func (e DataDeviceManagerDndAction) String() string { - return e.Name() + "=" + e.Value() -} - -// ShellInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ShellInterfaceName = "wl_shell" - -// Shell : create desktop-style surfaces -// -// This interface is implemented by servers that provide -// desktop-style user interfaces. -// -// It allows clients to associate a wl_shell_surface with -// a basic surface. -// -// Note! This protocol is deprecated and not intended for production use. -// For desktop-style user interfaces, use xdg_shell. Compositors and clients -// should not implement this interface. -type Shell struct { - BaseProxy -} - -// NewShell : create desktop-style surfaces -// -// This interface is implemented by servers that provide -// desktop-style user interfaces. -// -// It allows clients to associate a wl_shell_surface with -// a basic surface. -// -// Note! This protocol is deprecated and not intended for production use. -// For desktop-style user interfaces, use xdg_shell. Compositors and clients -// should not implement this interface. -func NewShell(ctx *Context) *Shell { - wlShell := &Shell{} - ctx.Register(wlShell) - return wlShell -} - -// GetShellSurface : create a shell surface from a surface -// -// Create a shell surface for an existing surface. This gives -// the wl_surface the role of a shell surface. If the wl_surface -// already has another role, it raises a protocol error. -// -// Only one shell surface can be associated with a given surface. -// -// surface: surface to be given the shell surface role -func (i *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) { - id := NewShellSurface(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], surface.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -func (i *Shell) Destroy() error { - i.MarkZombie() - return nil -} - -type ShellError uint32 - -// ShellError : -const ( - // ShellErrorRole : given wl_surface has another role - ShellErrorRole ShellError = 0 -) - -func (e ShellError) Name() string { - switch e { - case ShellErrorRole: - return "role" - default: - return "" - } -} - -func (e ShellError) Value() string { - switch e { - case ShellErrorRole: - return "0" - default: - return "" - } -} - -func (e ShellError) String() string { - return e.Name() + "=" + e.Value() -} - -// ShellSurfaceInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const ShellSurfaceInterfaceName = "wl_shell_surface" - -// ShellSurface : desktop-style metadata interface -// -// An interface that may be implemented by a wl_surface, for -// implementations that provide a desktop-style user interface. -// -// It provides requests to treat surfaces like toplevel, fullscreen -// or popup windows, move, resize or maximize them, associate -// metadata like title and class, etc. -// -// On the server side the object is automatically destroyed when -// the related wl_surface is destroyed. On the client side, -// wl_shell_surface_destroy() must be called before destroying -// the wl_surface object. -type ShellSurface struct { - BaseProxy - pingHandler ShellSurfacePingHandlerFunc - configureHandler ShellSurfaceConfigureHandlerFunc - popupDoneHandler ShellSurfacePopupDoneHandlerFunc -} - -// NewShellSurface : desktop-style metadata interface -// -// An interface that may be implemented by a wl_surface, for -// implementations that provide a desktop-style user interface. -// -// It provides requests to treat surfaces like toplevel, fullscreen -// or popup windows, move, resize or maximize them, associate -// metadata like title and class, etc. -// -// On the server side the object is automatically destroyed when -// the related wl_surface is destroyed. On the client side, -// wl_shell_surface_destroy() must be called before destroying -// the wl_surface object. -func NewShellSurface(ctx *Context) *ShellSurface { - wlShellSurface := &ShellSurface{} - ctx.Register(wlShellSurface) - return wlShellSurface -} - -// Pong : respond to a ping event -// -// A client must respond to a ping event with a pong request or -// the client may be deemed unresponsive. -// -// serial: serial number of the ping event -func (i *ShellSurface) Pong(serial uint32) error { - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Move : start an interactive move -// -// Start a pointer-driven move of the surface. -// -// This request must be used in response to a button press event. -// The server may ignore move requests depending on the state of -// the surface (e.g. fullscreen or maximized). -// -// seat: seat whose pointer is used -// serial: serial number of the implicit grab on the pointer -func (i *ShellSurface) Move(seat *Seat, serial uint32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], seat.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Resize : start an interactive resize -// -// Start a pointer-driven resizing of the surface. -// -// This request must be used in response to a button press event. -// The server may ignore resize requests depending on the state of -// the surface (e.g. fullscreen or maximized). -// -// seat: seat whose pointer is used -// serial: serial number of the implicit grab on the pointer -// edges: which edge or corner is being dragged -func (i *ShellSurface) Resize(seat *Seat, serial, edges uint32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], seat.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(edges)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetToplevel : make the surface a toplevel surface -// -// Map the surface as a toplevel surface. -// -// A toplevel surface is not fullscreen, maximized or transient. -func (i *ShellSurface) SetToplevel() error { - const opcode = 3 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetTransient : make the surface a transient surface -// -// Map the surface relative to an existing surface. -// -// The x and y arguments specify the location of the upper left -// corner of the surface relative to the upper left corner of the -// parent surface, in surface-local coordinates. -// -// The flags argument controls details of the transient behaviour. -// -// parent: parent surface -// x: surface-local x coordinate -// y: surface-local y coordinate -// flags: transient surface behavior -func (i *ShellSurface) SetTransient(parent *Surface, x, y int32, flags uint32) error { - const opcode = 4 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], parent.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(flags)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetFullscreen : make the surface a fullscreen surface -// -// Map the surface as a fullscreen surface. -// -// If an output parameter is given then the surface will be made -// fullscreen on that output. If the client does not specify the -// output then the compositor will apply its policy - usually -// choosing the output on which the surface has the biggest surface -// area. -// -// The client may specify a method to resolve a size conflict -// between the output size and the surface size - this is provided -// through the method parameter. -// -// The framerate parameter is used only when the method is set -// to "driver", to indicate the preferred framerate. A value of 0 -// indicates that the client does not care about framerate. The -// framerate is specified in mHz, that is framerate of 60000 is 60Hz. -// -// A method of "scale" or "driver" implies a scaling operation of -// the surface, either via a direct scaling operation or a change of -// the output mode. This will override any kind of output scaling, so -// that mapping a surface with a buffer size equal to the mode can -// fill the screen independent of buffer_scale. -// -// A method of "fill" means we don't scale up the buffer, however -// any output scale is applied. This means that you may run into -// an edge case where the application maps a buffer with the same -// size of the output mode but buffer_scale 1 (thus making a -// surface larger than the output). In this case it is allowed to -// downscale the results to fit the screen. -// -// The compositor must reply to this request with a configure event -// with the dimensions for the output on which the surface will -// be made fullscreen. -// -// method: method for resolving size conflict -// framerate: framerate in mHz -// output: output on which the surface is to be fullscreen -func (i *ShellSurface) SetFullscreen(method, framerate uint32, output *Output) error { - const opcode = 5 - const _reqBufLen = 8 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(method)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(framerate)) - l += 4 - if output == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - } - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetPopup : make the surface a popup surface -// -// Map the surface as a popup. -// -// A popup surface is a transient surface with an added pointer -// grab. -// -// An existing implicit grab will be changed to owner-events mode, -// and the popup grab will continue after the implicit grab ends -// (i.e. releasing the mouse button does not cause the popup to -// be unmapped). -// -// The popup grab continues until the window is destroyed or a -// mouse button is pressed in any other client's window. A click -// in any of the client's surfaces is reported as normal, however, -// clicks in other clients' surfaces will be discarded and trigger -// the callback. -// -// The x and y arguments specify the location of the upper left -// corner of the surface relative to the upper left corner of the -// parent surface, in surface-local coordinates. -// -// seat: seat whose pointer is used -// serial: serial number of the implicit grab on the pointer -// parent: parent surface -// x: surface-local x coordinate -// y: surface-local y coordinate -// flags: transient surface behavior -func (i *ShellSurface) SetPopup(seat *Seat, serial uint32, parent *Surface, x, y int32, flags uint32) error { - const opcode = 6 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], seat.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - PutUint32(_reqBuf[l:l+4], parent.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(flags)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetMaximized : make the surface a maximized surface -// -// Map the surface as a maximized surface. -// -// If an output parameter is given then the surface will be -// maximized on that output. If the client does not specify the -// output then the compositor will apply its policy - usually -// choosing the output on which the surface has the biggest surface -// area. -// -// The compositor will reply with a configure event telling -// the expected new surface size. The operation is completed -// on the next buffer attach to this surface. -// -// A maximized surface typically fills the entire output it is -// bound to, except for desktop elements such as panels. This is -// the main difference between a maximized shell surface and a -// fullscreen shell surface. -// -// The details depend on the compositor implementation. -// -// output: output on which the surface is to be maximized -func (i *ShellSurface) SetMaximized(output *Output) error { - const opcode = 7 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - if output == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], output.ID()) - l += 4 - } - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetTitle : set surface title -// -// Set a short title for the surface. -// -// This string may be used to identify the surface in a task bar, -// window list, or other user interface elements provided by the -// compositor. -// -// The string must be encoded in UTF-8. -// -// title: surface title -func (i *ShellSurface) SetTitle(title string) error { - const opcode = 8 - titleLen := PaddedLen(len(title) + 1) - _reqBufLen := 8 + (4 + titleLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutString(_reqBuf[l:l+(4+titleLen)], title) - l += (4 + titleLen) - err := i.Context().WriteMsg(_reqBuf, nil) - return err -} - -// SetClass : set surface class -// -// Set a class for the surface. -// -// The surface class identifies the general class of applications -// to which the surface belongs. A common convention is to use the -// file name (or the full path if it is a non-standard location) of -// the application's .desktop file as the class. -// -// class: surface class -func (i *ShellSurface) SetClass(class string) error { - const opcode = 9 - classLen := PaddedLen(len(class) + 1) - _reqBufLen := 8 + (4 + classLen) - _reqBuf := make([]byte, _reqBufLen) - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutString(_reqBuf[l:l+(4+classLen)], class) - l += (4 + classLen) - err := i.Context().WriteMsg(_reqBuf, nil) - return err -} - -func (i *ShellSurface) Destroy() error { - i.MarkZombie() - return nil -} - -type ShellSurfaceResize uint32 - -// ShellSurfaceResize : edge values for resizing -// -// These values are used to indicate which edge of a surface -// is being dragged in a resize operation. The server may -// use this information to adapt its behavior, e.g. choose -// an appropriate cursor image. -const ( - // ShellSurfaceResizeNone : no edge - ShellSurfaceResizeNone ShellSurfaceResize = 0 - // ShellSurfaceResizeTop : top edge - ShellSurfaceResizeTop ShellSurfaceResize = 1 - // ShellSurfaceResizeBottom : bottom edge - ShellSurfaceResizeBottom ShellSurfaceResize = 2 - // ShellSurfaceResizeLeft : left edge - ShellSurfaceResizeLeft ShellSurfaceResize = 4 - // ShellSurfaceResizeTopLeft : top and left edges - ShellSurfaceResizeTopLeft ShellSurfaceResize = 5 - // ShellSurfaceResizeBottomLeft : bottom and left edges - ShellSurfaceResizeBottomLeft ShellSurfaceResize = 6 - // ShellSurfaceResizeRight : right edge - ShellSurfaceResizeRight ShellSurfaceResize = 8 - // ShellSurfaceResizeTopRight : top and right edges - ShellSurfaceResizeTopRight ShellSurfaceResize = 9 - // ShellSurfaceResizeBottomRight : bottom and right edges - ShellSurfaceResizeBottomRight ShellSurfaceResize = 10 -) - -func (e ShellSurfaceResize) Name() string { - switch e { - case ShellSurfaceResizeNone: - return "none" - case ShellSurfaceResizeTop: - return "top" - case ShellSurfaceResizeBottom: - return "bottom" - case ShellSurfaceResizeLeft: - return "left" - case ShellSurfaceResizeTopLeft: - return "top_left" - case ShellSurfaceResizeBottomLeft: - return "bottom_left" - case ShellSurfaceResizeRight: - return "right" - case ShellSurfaceResizeTopRight: - return "top_right" - case ShellSurfaceResizeBottomRight: - return "bottom_right" - default: - return "" - } -} - -func (e ShellSurfaceResize) Value() string { - switch e { - case ShellSurfaceResizeNone: - return "0" - case ShellSurfaceResizeTop: - return "1" - case ShellSurfaceResizeBottom: - return "2" - case ShellSurfaceResizeLeft: - return "4" - case ShellSurfaceResizeTopLeft: - return "5" - case ShellSurfaceResizeBottomLeft: - return "6" - case ShellSurfaceResizeRight: - return "8" - case ShellSurfaceResizeTopRight: - return "9" - case ShellSurfaceResizeBottomRight: - return "10" - default: - return "" - } -} - -func (e ShellSurfaceResize) String() string { - return e.Name() + "=" + e.Value() -} - -type ShellSurfaceTransient uint32 - -// ShellSurfaceTransient : details of transient behaviour -// -// These flags specify details of the expected behaviour -// of transient surfaces. Used in the set_transient request. -const ( - // ShellSurfaceTransientInactive : do not set keyboard focus - ShellSurfaceTransientInactive ShellSurfaceTransient = 0x1 -) - -func (e ShellSurfaceTransient) Name() string { - switch e { - case ShellSurfaceTransientInactive: - return "inactive" - default: - return "" - } -} - -func (e ShellSurfaceTransient) Value() string { - switch e { - case ShellSurfaceTransientInactive: - return "0x1" - default: - return "" - } -} - -func (e ShellSurfaceTransient) String() string { - return e.Name() + "=" + e.Value() -} - -type ShellSurfaceFullscreenMethod uint32 - -// ShellSurfaceFullscreenMethod : different method to set the surface fullscreen -// -// Hints to indicate to the compositor how to deal with a conflict -// between the dimensions of the surface and the dimensions of the -// output. The compositor is free to ignore this parameter. -const ( - // ShellSurfaceFullscreenMethodDefault : no preference, apply default policy - ShellSurfaceFullscreenMethodDefault ShellSurfaceFullscreenMethod = 0 - // ShellSurfaceFullscreenMethodScale : scale, preserve the surface's aspect ratio and center on output - ShellSurfaceFullscreenMethodScale ShellSurfaceFullscreenMethod = 1 - // ShellSurfaceFullscreenMethodDriver : switch output mode to the smallest mode that can fit the surface, add black borders to compensate size mismatch - ShellSurfaceFullscreenMethodDriver ShellSurfaceFullscreenMethod = 2 - // ShellSurfaceFullscreenMethodFill : no upscaling, center on output and add black borders to compensate size mismatch - ShellSurfaceFullscreenMethodFill ShellSurfaceFullscreenMethod = 3 -) - -func (e ShellSurfaceFullscreenMethod) Name() string { - switch e { - case ShellSurfaceFullscreenMethodDefault: - return "default" - case ShellSurfaceFullscreenMethodScale: - return "scale" - case ShellSurfaceFullscreenMethodDriver: - return "driver" - case ShellSurfaceFullscreenMethodFill: - return "fill" - default: - return "" - } -} - -func (e ShellSurfaceFullscreenMethod) Value() string { - switch e { - case ShellSurfaceFullscreenMethodDefault: - return "0" - case ShellSurfaceFullscreenMethodScale: - return "1" - case ShellSurfaceFullscreenMethodDriver: - return "2" - case ShellSurfaceFullscreenMethodFill: - return "3" - default: - return "" - } -} - -func (e ShellSurfaceFullscreenMethod) String() string { - return e.Name() + "=" + e.Value() -} - -// ShellSurfacePingEvent : ping client -// -// Ping a client to check if it is receiving events and sending -// requests. A client is expected to reply with a pong request. -type ShellSurfacePingEvent struct { - Serial uint32 -} -type ShellSurfacePingHandlerFunc func(ShellSurfacePingEvent) - -// SetPingHandler : sets handler for ShellSurfacePingEvent -func (i *ShellSurface) SetPingHandler(f ShellSurfacePingHandlerFunc) { - i.pingHandler = f -} - -// ShellSurfaceConfigureEvent : suggest resize -// -// The configure event asks the client to resize its surface. -// -// The size is a hint, in the sense that the client is free to -// ignore it if it doesn't resize, pick a smaller size (to -// satisfy aspect ratio or resize in steps of NxM pixels). -// -// The edges parameter provides a hint about how the surface -// was resized. The client may use this information to decide -// how to adjust its content to the new size (e.g. a scrolling -// area might adjust its content position to leave the viewable -// content unmoved). -// -// The client is free to dismiss all but the last configure -// event it received. -// -// The width and height arguments specify the size of the window -// in surface-local coordinates. -type ShellSurfaceConfigureEvent struct { - Edges uint32 - Width int32 - Height int32 -} -type ShellSurfaceConfigureHandlerFunc func(ShellSurfaceConfigureEvent) - -// SetConfigureHandler : sets handler for ShellSurfaceConfigureEvent -func (i *ShellSurface) SetConfigureHandler(f ShellSurfaceConfigureHandlerFunc) { - i.configureHandler = f -} - -// ShellSurfacePopupDoneEvent : popup interaction is done -// -// The popup_done event is sent out when a popup grab is broken, -// that is, when the user clicks a surface that doesn't belong -// to the client owning the popup surface. -type ShellSurfacePopupDoneEvent struct{} -type ShellSurfacePopupDoneHandlerFunc func(ShellSurfacePopupDoneEvent) - -// SetPopupDoneHandler : sets handler for ShellSurfacePopupDoneEvent -func (i *ShellSurface) SetPopupDoneHandler(f ShellSurfacePopupDoneHandlerFunc) { - i.popupDoneHandler = f -} - -func (i *ShellSurface) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.pingHandler == nil { - return - } - var e ShellSurfacePingEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - - i.pingHandler(e) - case 1: - if i.configureHandler == nil { - return - } - var e ShellSurfaceConfigureEvent - l := 0 - e.Edges = Uint32(data[l : l+4]) - l += 4 - e.Width = int32(Uint32(data[l : l+4])) - l += 4 - e.Height = int32(Uint32(data[l : l+4])) - l += 4 - - i.configureHandler(e) - case 2: - if i.popupDoneHandler == nil { - return - } - var e ShellSurfacePopupDoneEvent - - i.popupDoneHandler(e) - } -} - -// SurfaceInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const SurfaceInterfaceName = "wl_surface" - -// Surface : an onscreen surface -// -// A surface is a rectangular area that may be displayed on zero -// or more outputs, and shown any number of times at the compositor's -// discretion. They can present wl_buffers, receive user input, and -// define a local coordinate system. -// -// The size of a surface (and relative positions on it) is described -// in surface-local coordinates, which may differ from the buffer -// coordinates of the pixel content, in case a buffer_transform -// or a buffer_scale is used. -// -// A surface without a "role" is fairly useless: a compositor does -// not know where, when or how to present it. The role is the -// purpose of a wl_surface. Examples of roles are a cursor for a -// pointer (as set by wl_pointer.set_cursor), a drag icon -// (wl_data_device.start_drag), a sub-surface -// (wl_subcompositor.get_subsurface), and a window as defined by a -// shell protocol (e.g. wl_shell.get_shell_surface). -// -// A surface can have only one role at a time. Initially a -// wl_surface does not have a role. Once a wl_surface is given a -// role, it is set permanently for the whole lifetime of the -// wl_surface object. Giving the current role again is allowed, -// unless explicitly forbidden by the relevant interface -// specification. -// -// Surface roles are given by requests in other interfaces such as -// wl_pointer.set_cursor. The request should explicitly mention -// that this request gives a role to a wl_surface. Often, this -// request also creates a new protocol object that represents the -// role and adds additional functionality to wl_surface. When a -// client wants to destroy a wl_surface, they must destroy this role -// object before the wl_surface, otherwise a defunct_role_object error is -// sent. -// -// Destroying the role object does not remove the role from the -// wl_surface, but it may stop the wl_surface from "playing the role". -// For instance, if a wl_subsurface object is destroyed, the wl_surface -// it was created for will be unmapped and forget its position and -// z-order. It is allowed to create a wl_subsurface for the same -// wl_surface again, but it is not allowed to use the wl_surface as -// a cursor (cursor is a different role than sub-surface, and role -// switching is not allowed). -type Surface struct { - BaseProxy - enterHandler SurfaceEnterHandlerFunc - leaveHandler SurfaceLeaveHandlerFunc - preferredBufferScaleHandler SurfacePreferredBufferScaleHandlerFunc - preferredBufferTransformHandler SurfacePreferredBufferTransformHandlerFunc -} - -// NewSurface : an onscreen surface -// -// A surface is a rectangular area that may be displayed on zero -// or more outputs, and shown any number of times at the compositor's -// discretion. They can present wl_buffers, receive user input, and -// define a local coordinate system. -// -// The size of a surface (and relative positions on it) is described -// in surface-local coordinates, which may differ from the buffer -// coordinates of the pixel content, in case a buffer_transform -// or a buffer_scale is used. -// -// A surface without a "role" is fairly useless: a compositor does -// not know where, when or how to present it. The role is the -// purpose of a wl_surface. Examples of roles are a cursor for a -// pointer (as set by wl_pointer.set_cursor), a drag icon -// (wl_data_device.start_drag), a sub-surface -// (wl_subcompositor.get_subsurface), and a window as defined by a -// shell protocol (e.g. wl_shell.get_shell_surface). -// -// A surface can have only one role at a time. Initially a -// wl_surface does not have a role. Once a wl_surface is given a -// role, it is set permanently for the whole lifetime of the -// wl_surface object. Giving the current role again is allowed, -// unless explicitly forbidden by the relevant interface -// specification. -// -// Surface roles are given by requests in other interfaces such as -// wl_pointer.set_cursor. The request should explicitly mention -// that this request gives a role to a wl_surface. Often, this -// request also creates a new protocol object that represents the -// role and adds additional functionality to wl_surface. When a -// client wants to destroy a wl_surface, they must destroy this role -// object before the wl_surface, otherwise a defunct_role_object error is -// sent. -// -// Destroying the role object does not remove the role from the -// wl_surface, but it may stop the wl_surface from "playing the role". -// For instance, if a wl_subsurface object is destroyed, the wl_surface -// it was created for will be unmapped and forget its position and -// z-order. It is allowed to create a wl_subsurface for the same -// wl_surface again, but it is not allowed to use the wl_surface as -// a cursor (cursor is a different role than sub-surface, and role -// switching is not allowed). -func NewSurface(ctx *Context) *Surface { - wlSurface := &Surface{} - ctx.Register(wlSurface) - return wlSurface -} - -// Destroy : delete surface -// -// Deletes the surface and invalidates its object ID. -func (i *Surface) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Attach : set the surface contents -// -// Set a buffer as the content of this surface. -// -// The new size of the surface is calculated based on the buffer -// size transformed by the inverse buffer_transform and the -// inverse buffer_scale. This means that at commit time the supplied -// buffer size must be an integer multiple of the buffer_scale. If -// that's not the case, an invalid_size error is sent. -// -// The x and y arguments specify the location of the new pending -// buffer's upper left corner, relative to the current buffer's upper -// left corner, in surface-local coordinates. In other words, the -// x and y, combined with the new surface size define in which -// directions the surface's size changes. Setting anything other than 0 -// as x and y arguments is discouraged, and should instead be replaced -// with using the separate wl_surface.offset request. -// -// When the bound wl_surface version is 5 or higher, passing any -// non-zero x or y is a protocol violation, and will result in an -// 'invalid_offset' error being raised. The x and y arguments are ignored -// and do not change the pending state. To achieve equivalent semantics, -// use wl_surface.offset. -// -// Surface contents are double-buffered state, see wl_surface.commit. -// -// The initial surface contents are void; there is no content. -// wl_surface.attach assigns the given wl_buffer as the pending -// wl_buffer. wl_surface.commit makes the pending wl_buffer the new -// surface contents, and the size of the surface becomes the size -// calculated from the wl_buffer, as described above. After commit, -// there is no pending buffer until the next attach. -// -// Committing a pending wl_buffer allows the compositor to read the -// pixels in the wl_buffer. The compositor may access the pixels at -// any time after the wl_surface.commit request. When the compositor -// will not access the pixels anymore, it will send the -// wl_buffer.release event. Only after receiving wl_buffer.release, -// the client may reuse the wl_buffer. A wl_buffer that has been -// attached and then replaced by another attach instead of committed -// will not receive a release event, and is not used by the -// compositor. -// -// If a pending wl_buffer has been committed to more than one wl_surface, -// the delivery of wl_buffer.release events becomes undefined. A well -// behaved client should not rely on wl_buffer.release events in this -// case. Alternatively, a client could create multiple wl_buffer objects -// from the same backing storage or use a protocol extension providing -// per-commit release notifications. -// -// Destroying the wl_buffer after wl_buffer.release does not change -// the surface contents. Destroying the wl_buffer before wl_buffer.release -// is allowed as long as the underlying buffer storage isn't re-used (this -// can happen e.g. on client process termination). However, if the client -// destroys the wl_buffer before receiving the wl_buffer.release event and -// mutates the underlying buffer storage, the surface contents become -// undefined immediately. -// -// If wl_surface.attach is sent with a NULL wl_buffer, the -// following wl_surface.commit will remove the surface content. -// -// If a pending wl_buffer has been destroyed, the result is not specified. -// Many compositors are known to remove the surface content on the following -// wl_surface.commit, but this behaviour is not universal. Clients seeking to -// maximise compatibility should not destroy pending buffers and should -// ensure that they explicitly remove content from surfaces, even after -// destroying buffers. -// -// buffer: buffer of surface contents -// x: surface-local x coordinate -// y: surface-local y coordinate -func (i *Surface) Attach(buffer *Buffer, x, y int32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - if buffer == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], buffer.ID()) - l += 4 - } - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Damage : mark part of the surface damaged -// -// This request is used to describe the regions where the pending -// buffer is different from the current surface contents, and where -// the surface therefore needs to be repainted. The compositor -// ignores the parts of the damage that fall outside of the surface. -// -// Damage is double-buffered state, see wl_surface.commit. -// -// The damage rectangle is specified in surface-local coordinates, -// where x and y specify the upper left corner of the damage rectangle. -// -// The initial value for pending damage is empty: no damage. -// wl_surface.damage adds pending damage: the new pending damage -// is the union of old pending damage and the given rectangle. -// -// wl_surface.commit assigns pending damage as the current damage, -// and clears pending damage. The server will clear the current -// damage as it repaints the surface. -// -// Note! New clients should not use this request. Instead damage can be -// posted with wl_surface.damage_buffer which uses buffer coordinates -// instead of surface coordinates. -// -// x: surface-local x coordinate -// y: surface-local y coordinate -// width: width of damage rectangle -// height: height of damage rectangle -func (i *Surface) Damage(x, y, width, height int32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Frame : request a frame throttling hint -// -// Request a notification when it is a good time to start drawing a new -// frame, by creating a frame callback. This is useful for throttling -// redrawing operations, and driving animations. -// -// When a client is animating on a wl_surface, it can use the 'frame' -// request to get notified when it is a good time to draw and commit the -// next frame of animation. If the client commits an update earlier than -// that, it is likely that some updates will not make it to the display, -// and the client is wasting resources by drawing too often. -// -// The frame request will take effect on the next wl_surface.commit. -// The notification will only be posted for one frame unless -// requested again. For a wl_surface, the notifications are posted in -// the order the frame requests were committed. -// -// The server must send the notifications so that a client -// will not send excessive updates, while still allowing -// the highest possible update rate for clients that wait for the reply -// before drawing again. The server should give some time for the client -// to draw and commit after sending the frame callback events to let it -// hit the next output refresh. -// -// A server should avoid signaling the frame callbacks if the -// surface is not visible in any way, e.g. the surface is off-screen, -// or completely obscured by other opaque surfaces. -// -// The object returned by this request will be destroyed by the -// compositor after the callback is fired and as such the client must not -// attempt to use it after that point. -// -// The callback_data passed in the callback is the current time, in -// milliseconds, with an undefined base. -func (i *Surface) Frame() (*Callback, error) { - callback := NewCallback(i.Context()) - const opcode = 3 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], callback.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return callback, err -} - -// SetOpaqueRegion : set opaque region -// -// This request sets the region of the surface that contains -// opaque content. -// -// The opaque region is an optimization hint for the compositor -// that lets it optimize the redrawing of content behind opaque -// regions. Setting an opaque region is not required for correct -// behaviour, but marking transparent content as opaque will result -// in repaint artifacts. -// -// The opaque region is specified in surface-local coordinates. -// -// The compositor ignores the parts of the opaque region that fall -// outside of the surface. -// -// Opaque region is double-buffered state, see wl_surface.commit. -// -// wl_surface.set_opaque_region changes the pending opaque region. -// wl_surface.commit copies the pending region to the current region. -// Otherwise, the pending and current regions are never changed. -// -// The initial value for an opaque region is empty. Setting the pending -// opaque region has copy semantics, and the wl_region object can be -// destroyed immediately. A NULL wl_region causes the pending opaque -// region to be set to empty. -// -// region: opaque region of the surface -func (i *Surface) SetOpaqueRegion(region *Region) error { - const opcode = 4 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - if region == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], region.ID()) - l += 4 - } - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetInputRegion : set input region -// -// This request sets the region of the surface that can receive -// pointer and touch events. -// -// Input events happening outside of this region will try the next -// surface in the server surface stack. The compositor ignores the -// parts of the input region that fall outside of the surface. -// -// The input region is specified in surface-local coordinates. -// -// Input region is double-buffered state, see wl_surface.commit. -// -// wl_surface.set_input_region changes the pending input region. -// wl_surface.commit copies the pending region to the current region. -// Otherwise the pending and current regions are never changed, -// except cursor and icon surfaces are special cases, see -// wl_pointer.set_cursor and wl_data_device.start_drag. -// -// The initial value for an input region is infinite. That means the -// whole surface will accept input. Setting the pending input region -// has copy semantics, and the wl_region object can be destroyed -// immediately. A NULL wl_region causes the input region to be set -// to infinite. -// -// region: input region of the surface -func (i *Surface) SetInputRegion(region *Region) error { - const opcode = 5 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - if region == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], region.ID()) - l += 4 - } - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Commit : commit pending surface state -// -// Surface state (input, opaque, and damage regions, attached buffers, -// etc.) is double-buffered. Protocol requests modify the pending state, -// as opposed to the active state in use by the compositor. -// -// A commit request atomically creates a content update from the pending -// state, even if the pending state has not been touched. The content -// update is placed in a queue until it becomes active. After commit, the -// new pending state is as documented for each related request. -// -// When the content update is applied, the wl_buffer is applied before all -// other state. This means that all coordinates in double-buffered state -// are relative to the newly attached wl_buffers, except for -// wl_surface.attach itself. If there is no newly attached wl_buffer, the -// coordinates are relative to the previous content update. -// -// All requests that need a commit to become effective are documented -// to affect double-buffered state. -// -// Other interfaces may add further double-buffered surface state. -func (i *Surface) Commit() error { - const opcode = 6 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetBufferTransform : sets the buffer transformation -// -// This request sets the transformation that the client has already applied -// to the content of the buffer. The accepted values for the transform -// parameter are the values for wl_output.transform. -// -// The compositor applies the inverse of this transformation whenever it -// uses the buffer contents. -// -// Buffer transform is double-buffered state, see wl_surface.commit. -// -// A newly created surface has its buffer transformation set to normal. -// -// wl_surface.set_buffer_transform changes the pending buffer -// transformation. wl_surface.commit copies the pending buffer -// transformation to the current one. Otherwise, the pending and current -// values are never changed. -// -// The purpose of this request is to allow clients to render content -// according to the output transform, thus permitting the compositor to -// use certain optimizations even if the display is rotated. Using -// hardware overlays and scanning out a client buffer for fullscreen -// surfaces are examples of such optimizations. Those optimizations are -// highly dependent on the compositor implementation, so the use of this -// request should be considered on a case-by-case basis. -// -// Note that if the transform value includes 90 or 270 degree rotation, -// the width of the buffer will become the surface height and the height -// of the buffer will become the surface width. -// -// If transform is not one of the values from the -// wl_output.transform enum the invalid_transform protocol error -// is raised. -// -// transform: transform for interpreting buffer contents -func (i *Surface) SetBufferTransform(transform int32) error { - const opcode = 7 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(transform)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetBufferScale : sets the buffer scaling factor -// -// This request sets an optional scaling factor on how the compositor -// interprets the contents of the buffer attached to the window. -// -// Buffer scale is double-buffered state, see wl_surface.commit. -// -// A newly created surface has its buffer scale set to 1. -// -// wl_surface.set_buffer_scale changes the pending buffer scale. -// wl_surface.commit copies the pending buffer scale to the current one. -// Otherwise, the pending and current values are never changed. -// -// The purpose of this request is to allow clients to supply higher -// resolution buffer data for use on high resolution outputs. It is -// intended that you pick the same buffer scale as the scale of the -// output that the surface is displayed on. This means the compositor -// can avoid scaling when rendering the surface on that output. -// -// Note that if the scale is larger than 1, then you have to attach -// a buffer that is larger (by a factor of scale in each dimension) -// than the desired surface size. -// -// If scale is not greater than 0 the invalid_scale protocol error is -// raised. -// -// scale: scale for interpreting buffer contents -func (i *Surface) SetBufferScale(scale int32) error { - const opcode = 8 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(scale)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// DamageBuffer : mark part of the surface damaged using buffer coordinates -// -// This request is used to describe the regions where the pending -// buffer is different from the current surface contents, and where -// the surface therefore needs to be repainted. The compositor -// ignores the parts of the damage that fall outside of the surface. -// -// Damage is double-buffered state, see wl_surface.commit. -// -// The damage rectangle is specified in buffer coordinates, -// where x and y specify the upper left corner of the damage rectangle. -// -// The initial value for pending damage is empty: no damage. -// wl_surface.damage_buffer adds pending damage: the new pending -// damage is the union of old pending damage and the given rectangle. -// -// wl_surface.commit assigns pending damage as the current damage, -// and clears pending damage. The server will clear the current -// damage as it repaints the surface. -// -// This request differs from wl_surface.damage in only one way - it -// takes damage in buffer coordinates instead of surface-local -// coordinates. While this generally is more intuitive than surface -// coordinates, it is especially desirable when using wp_viewport -// or when a drawing library (like EGL) is unaware of buffer scale -// and buffer transform. -// -// Note: Because buffer transformation changes and damage requests may -// be interleaved in the protocol stream, it is impossible to determine -// the actual mapping between surface and buffer damage until -// wl_surface.commit time. Therefore, compositors wishing to take both -// kinds of damage into account will have to accumulate damage from the -// two requests separately and only transform from one to the other -// after receiving the wl_surface.commit. -// -// x: buffer-local x coordinate -// y: buffer-local y coordinate -// width: width of damage rectangle -// height: height of damage rectangle -func (i *Surface) DamageBuffer(x, y, width, height int32) error { - const opcode = 9 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Offset : set the surface contents offset -// -// The x and y arguments specify the location of the new pending -// buffer's upper left corner, relative to the current buffer's upper -// left corner, in surface-local coordinates. In other words, the -// x and y, combined with the new surface size define in which -// directions the surface's size changes. -// -// The exact semantics of wl_surface.offset are role-specific. Refer to -// the documentation of specific roles for more information. -// -// Surface location offset is double-buffered state, see -// wl_surface.commit. -// -// This request is semantically equivalent to and the replaces the x and y -// arguments in the wl_surface.attach request in wl_surface versions prior -// to 5. See wl_surface.attach for details. -// -// x: surface-local x coordinate -// y: surface-local y coordinate -func (i *Surface) Offset(x, y int32) error { - const opcode = 10 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type SurfaceError uint32 - -// SurfaceError : wl_surface error values -// -// These errors can be emitted in response to wl_surface requests. -const ( - // SurfaceErrorInvalidScale : buffer scale value is invalid - SurfaceErrorInvalidScale SurfaceError = 0 - // SurfaceErrorInvalidTransform : buffer transform value is invalid - SurfaceErrorInvalidTransform SurfaceError = 1 - // SurfaceErrorInvalidSize : buffer size is invalid - SurfaceErrorInvalidSize SurfaceError = 2 - // SurfaceErrorInvalidOffset : buffer offset is invalid - SurfaceErrorInvalidOffset SurfaceError = 3 - // SurfaceErrorDefunctRoleObject : surface was destroyed before its role object - SurfaceErrorDefunctRoleObject SurfaceError = 4 -) - -func (e SurfaceError) Name() string { - switch e { - case SurfaceErrorInvalidScale: - return "invalid_scale" - case SurfaceErrorInvalidTransform: - return "invalid_transform" - case SurfaceErrorInvalidSize: - return "invalid_size" - case SurfaceErrorInvalidOffset: - return "invalid_offset" - case SurfaceErrorDefunctRoleObject: - return "defunct_role_object" - default: - return "" - } -} - -func (e SurfaceError) Value() string { - switch e { - case SurfaceErrorInvalidScale: - return "0" - case SurfaceErrorInvalidTransform: - return "1" - case SurfaceErrorInvalidSize: - return "2" - case SurfaceErrorInvalidOffset: - return "3" - case SurfaceErrorDefunctRoleObject: - return "4" - default: - return "" - } -} - -func (e SurfaceError) String() string { - return e.Name() + "=" + e.Value() -} - -// SurfaceEnterEvent : surface enters an output -// -// This is emitted whenever a surface's creation, movement, or resizing -// results in some part of it being within the scanout region of an -// output. -// -// Note that a surface may be overlapping with zero or more outputs. -type SurfaceEnterEvent struct { - Output *Output -} -type SurfaceEnterHandlerFunc func(SurfaceEnterEvent) - -// SetEnterHandler : sets handler for SurfaceEnterEvent -func (i *Surface) SetEnterHandler(f SurfaceEnterHandlerFunc) { - i.enterHandler = f -} - -// SurfaceLeaveEvent : surface leaves an output -// -// This is emitted whenever a surface's creation, movement, or resizing -// results in it no longer having any part of it within the scanout region -// of an output. -// -// Clients should not use the number of outputs the surface is on for frame -// throttling purposes. The surface might be hidden even if no leave event -// has been sent, and the compositor might expect new surface content -// updates even if no enter event has been sent. The frame event should be -// used instead. -type SurfaceLeaveEvent struct { - Output *Output -} -type SurfaceLeaveHandlerFunc func(SurfaceLeaveEvent) - -// SetLeaveHandler : sets handler for SurfaceLeaveEvent -func (i *Surface) SetLeaveHandler(f SurfaceLeaveHandlerFunc) { - i.leaveHandler = f -} - -// SurfacePreferredBufferScaleEvent : preferred buffer scale for the surface -// -// This event indicates the preferred buffer scale for this surface. It is -// sent whenever the compositor's preference changes. -// -// Before receiving this event the preferred buffer scale for this surface -// is 1. -// -// It is intended that scaling aware clients use this event to scale their -// content and use wl_surface.set_buffer_scale to indicate the scale they -// have rendered with. This allows clients to supply a higher detail -// buffer. -// -// The compositor shall emit a scale value greater than 0. -type SurfacePreferredBufferScaleEvent struct { - Factor int32 -} -type SurfacePreferredBufferScaleHandlerFunc func(SurfacePreferredBufferScaleEvent) - -// SetPreferredBufferScaleHandler : sets handler for SurfacePreferredBufferScaleEvent -func (i *Surface) SetPreferredBufferScaleHandler(f SurfacePreferredBufferScaleHandlerFunc) { - i.preferredBufferScaleHandler = f -} - -// SurfacePreferredBufferTransformEvent : preferred buffer transform for the surface -// -// This event indicates the preferred buffer transform for this surface. -// It is sent whenever the compositor's preference changes. -// -// Before receiving this event the preferred buffer transform for this -// surface is normal. -// -// Applying this transformation to the surface buffer contents and using -// wl_surface.set_buffer_transform might allow the compositor to use the -// surface buffer more efficiently. -type SurfacePreferredBufferTransformEvent struct { - Transform uint32 -} -type SurfacePreferredBufferTransformHandlerFunc func(SurfacePreferredBufferTransformEvent) - -// SetPreferredBufferTransformHandler : sets handler for SurfacePreferredBufferTransformEvent -func (i *Surface) SetPreferredBufferTransformHandler(f SurfacePreferredBufferTransformHandlerFunc) { - i.preferredBufferTransformHandler = f -} - -func (i *Surface) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.enterHandler == nil { - return - } - var e SurfaceEnterEvent - l := 0 - e.Output = i.Context().GetProxy(Uint32(data[l : l+4])).(*Output) - l += 4 - - i.enterHandler(e) - case 1: - if i.leaveHandler == nil { - return - } - var e SurfaceLeaveEvent - l := 0 - e.Output = i.Context().GetProxy(Uint32(data[l : l+4])).(*Output) - l += 4 - - i.leaveHandler(e) - case 2: - if i.preferredBufferScaleHandler == nil { - return - } - var e SurfacePreferredBufferScaleEvent - l := 0 - e.Factor = int32(Uint32(data[l : l+4])) - l += 4 - - i.preferredBufferScaleHandler(e) - case 3: - if i.preferredBufferTransformHandler == nil { - return - } - var e SurfacePreferredBufferTransformEvent - l := 0 - e.Transform = Uint32(data[l : l+4]) - l += 4 - - i.preferredBufferTransformHandler(e) - } -} - -// SeatInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const SeatInterfaceName = "wl_seat" - -// Seat : group of input devices -// -// A seat is a group of keyboards, pointer and touch devices. This -// object is published as a global during start up, or when such a -// device is hot plugged. A seat typically has a pointer and -// maintains a keyboard focus and a pointer focus. -type Seat struct { - BaseProxy - capabilitiesHandler SeatCapabilitiesHandlerFunc - nameHandler SeatNameHandlerFunc -} - -// NewSeat : group of input devices -// -// A seat is a group of keyboards, pointer and touch devices. This -// object is published as a global during start up, or when such a -// device is hot plugged. A seat typically has a pointer and -// maintains a keyboard focus and a pointer focus. -func NewSeat(ctx *Context) *Seat { - wlSeat := &Seat{} - ctx.Register(wlSeat) - return wlSeat -} - -// GetPointer : return pointer object -// -// The ID provided will be initialized to the wl_pointer interface -// for this seat. -// -// This request only takes effect if the seat has the pointer -// capability, or has had the pointer capability in the past. -// It is a protocol violation to issue this request on a seat that has -// never had the pointer capability. The missing_capability error will -// be sent in this case. -func (i *Seat) GetPointer() (*Pointer, error) { - id := NewPointer(i.Context()) - const opcode = 0 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// GetKeyboard : return keyboard object -// -// The ID provided will be initialized to the wl_keyboard interface -// for this seat. -// -// This request only takes effect if the seat has the keyboard -// capability, or has had the keyboard capability in the past. -// It is a protocol violation to issue this request on a seat that has -// never had the keyboard capability. The missing_capability error will -// be sent in this case. -func (i *Seat) GetKeyboard() (*Keyboard, error) { - id := NewKeyboard(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// GetTouch : return touch object -// -// The ID provided will be initialized to the wl_touch interface -// for this seat. -// -// This request only takes effect if the seat has the touch -// capability, or has had the touch capability in the past. -// It is a protocol violation to issue this request on a seat that has -// never had the touch capability. The missing_capability error will -// be sent in this case. -func (i *Seat) GetTouch() (*Touch, error) { - id := NewTouch(i.Context()) - const opcode = 2 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -// Release : release the seat object -// -// Using this request a client can tell the server that it is not going to -// use the seat object anymore. -func (i *Seat) Release() error { - defer i.MarkZombie() - const opcode = 3 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type SeatCapability uint32 - -// SeatCapability : seat capability bitmask -// -// This is a bitmask of capabilities this seat has; if a member is -// set, then it is present on the seat. -const ( - // SeatCapabilityPointer : the seat has pointer devices - SeatCapabilityPointer SeatCapability = 1 - // SeatCapabilityKeyboard : the seat has one or more keyboards - SeatCapabilityKeyboard SeatCapability = 2 - // SeatCapabilityTouch : the seat has touch devices - SeatCapabilityTouch SeatCapability = 4 -) - -func (e SeatCapability) Name() string { - switch e { - case SeatCapabilityPointer: - return "pointer" - case SeatCapabilityKeyboard: - return "keyboard" - case SeatCapabilityTouch: - return "touch" - default: - return "" - } -} - -func (e SeatCapability) Value() string { - switch e { - case SeatCapabilityPointer: - return "1" - case SeatCapabilityKeyboard: - return "2" - case SeatCapabilityTouch: - return "4" - default: - return "" - } -} - -func (e SeatCapability) String() string { - return e.Name() + "=" + e.Value() -} - -type SeatError uint32 - -// SeatError : wl_seat error values -// -// These errors can be emitted in response to wl_seat requests. -const ( - // SeatErrorMissingCapability : get_pointer, get_keyboard or get_touch called on seat without the matching capability - SeatErrorMissingCapability SeatError = 0 -) - -func (e SeatError) Name() string { - switch e { - case SeatErrorMissingCapability: - return "missing_capability" - default: - return "" - } -} - -func (e SeatError) Value() string { - switch e { - case SeatErrorMissingCapability: - return "0" - default: - return "" - } -} - -func (e SeatError) String() string { - return e.Name() + "=" + e.Value() -} - -// SeatCapabilitiesEvent : seat capabilities changed -// -// This is sent on binding to the seat global or whenever a seat gains -// or loses the pointer, keyboard or touch capabilities. -// The argument is a capability enum containing the complete set of -// capabilities this seat has. -// -// When the pointer capability is added, a client may create a -// wl_pointer object using the wl_seat.get_pointer request. This object -// will receive pointer events until the capability is removed in the -// future. -// -// When the pointer capability is removed, a client should destroy the -// wl_pointer objects associated with the seat where the capability was -// removed, using the wl_pointer.release request. No further pointer -// events will be received on these objects. -// -// In some compositors, if a seat regains the pointer capability and a -// client has a previously obtained wl_pointer object of version 4 or -// less, that object may start sending pointer events again. This -// behavior is considered a misinterpretation of the intended behavior -// and must not be relied upon by the client. wl_pointer objects of -// version 5 or later must not send events if created before the most -// recent event notifying the client of an added pointer capability. -// -// The above behavior also applies to wl_keyboard and wl_touch with the -// keyboard and touch capabilities, respectively. -type SeatCapabilitiesEvent struct { - Capabilities uint32 -} -type SeatCapabilitiesHandlerFunc func(SeatCapabilitiesEvent) - -// SetCapabilitiesHandler : sets handler for SeatCapabilitiesEvent -func (i *Seat) SetCapabilitiesHandler(f SeatCapabilitiesHandlerFunc) { - i.capabilitiesHandler = f -} - -// SeatNameEvent : unique identifier for this seat -// -// In a multi-seat configuration the seat name can be used by clients to -// help identify which physical devices the seat represents. -// -// The seat name is a UTF-8 string with no convention defined for its -// contents. Each name is unique among all wl_seat globals. The name is -// only guaranteed to be unique for the current compositor instance. -// -// The same seat names are used for all clients. Thus, the name can be -// shared across processes to refer to a specific wl_seat global. -// -// The name event is sent after binding to the seat global, and should be sent -// before announcing capabilities. This event only sent once per seat object, -// and the name does not change over the lifetime of the wl_seat global. -// -// Compositors may re-use the same seat name if the wl_seat global is -// destroyed and re-created later. -type SeatNameEvent struct { - Name string -} -type SeatNameHandlerFunc func(SeatNameEvent) - -// SetNameHandler : sets handler for SeatNameEvent -func (i *Seat) SetNameHandler(f SeatNameHandlerFunc) { - i.nameHandler = f -} - -func (i *Seat) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.capabilitiesHandler == nil { - return - } - var e SeatCapabilitiesEvent - l := 0 - e.Capabilities = Uint32(data[l : l+4]) - l += 4 - - i.capabilitiesHandler(e) - case 1: - if i.nameHandler == nil { - return - } - var e SeatNameEvent - l := 0 - nameLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.Name = String(data[l : l+nameLen]) - l += nameLen - - i.nameHandler(e) - } -} - -// PointerInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const PointerInterfaceName = "wl_pointer" - -// Pointer : pointer input device -// -// The wl_pointer interface represents one or more input devices, -// such as mice, which control the pointer location and pointer_focus -// of a seat. -// -// The wl_pointer interface generates motion, enter and leave -// events for the surfaces that the pointer is located over, -// and button and axis events for button presses, button releases -// and scrolling. -type Pointer struct { - BaseProxy - enterHandler PointerEnterHandlerFunc - leaveHandler PointerLeaveHandlerFunc - motionHandler PointerMotionHandlerFunc - buttonHandler PointerButtonHandlerFunc - axisHandler PointerAxisHandlerFunc - frameHandler PointerFrameHandlerFunc - axisSourceHandler PointerAxisSourceHandlerFunc - axisStopHandler PointerAxisStopHandlerFunc - axisDiscreteHandler PointerAxisDiscreteHandlerFunc - axisValue120Handler PointerAxisValue120HandlerFunc - axisRelativeDirectionHandler PointerAxisRelativeDirectionHandlerFunc -} - -// NewPointer : pointer input device -// -// The wl_pointer interface represents one or more input devices, -// such as mice, which control the pointer location and pointer_focus -// of a seat. -// -// The wl_pointer interface generates motion, enter and leave -// events for the surfaces that the pointer is located over, -// and button and axis events for button presses, button releases -// and scrolling. -func NewPointer(ctx *Context) *Pointer { - wlPointer := &Pointer{} - ctx.Register(wlPointer) - return wlPointer -} - -// SetCursor : set the pointer surface -// -// Set the pointer surface, i.e., the surface that contains the -// pointer image (cursor). This request gives the surface the role -// of a cursor. If the surface already has another role, it raises -// a protocol error. -// -// The cursor actually changes only if the pointer -// focus for this device is one of the requesting client's surfaces -// or the surface parameter is the current pointer surface. If -// there was a previous surface set with this request it is -// replaced. If surface is NULL, the pointer image is hidden. -// -// The parameters hotspot_x and hotspot_y define the position of -// the pointer surface relative to the pointer location. Its -// top-left corner is always at (x, y) - (hotspot_x, hotspot_y), -// where (x, y) are the coordinates of the pointer location, in -// surface-local coordinates. -// -// On wl_surface.offset requests to the pointer surface, hotspot_x -// and hotspot_y are decremented by the x and y parameters -// passed to the request. The offset must be applied by -// wl_surface.commit as usual. -// -// The hotspot can also be updated by passing the currently set -// pointer surface to this request with new values for hotspot_x -// and hotspot_y. -// -// The input region is ignored for wl_surfaces with the role of -// a cursor. When the use as a cursor ends, the wl_surface is -// unmapped. -// -// The serial parameter must match the latest wl_pointer.enter -// serial number sent to the client. Otherwise the request will be -// ignored. -// -// serial: serial number of the enter event -// surface: pointer surface -// hotspotX: surface-local x coordinate -// hotspotY: surface-local y coordinate -func (i *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX, hotspotY int32) error { - const opcode = 0 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(serial)) - l += 4 - if surface == nil { - PutUint32(_reqBuf[l:l+4], 0) - l += 4 - } else { - PutUint32(_reqBuf[l:l+4], surface.ID()) - l += 4 - } - PutUint32(_reqBuf[l:l+4], uint32(hotspotX)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(hotspotY)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Release : release the pointer object -// -// Using this request a client can tell the server that it is not going to -// use the pointer object anymore. -// -// This request destroys the pointer proxy object, so clients must not call -// wl_pointer_destroy() after using this request. -func (i *Pointer) Release() error { - defer i.MarkZombie() - const opcode = 1 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type PointerError uint32 - -// PointerError : -const ( - // PointerErrorRole : given wl_surface has another role - PointerErrorRole PointerError = 0 -) - -func (e PointerError) Name() string { - switch e { - case PointerErrorRole: - return "role" - default: - return "" - } -} - -func (e PointerError) Value() string { - switch e { - case PointerErrorRole: - return "0" - default: - return "" - } -} - -func (e PointerError) String() string { - return e.Name() + "=" + e.Value() -} - -type PointerButtonState uint32 - -// PointerButtonState : physical button state -// -// Describes the physical state of a button that produced the button -// event. -const ( - // PointerButtonStateReleased : the button is not pressed - PointerButtonStateReleased PointerButtonState = 0 - // PointerButtonStatePressed : the button is pressed - PointerButtonStatePressed PointerButtonState = 1 -) - -func (e PointerButtonState) Name() string { - switch e { - case PointerButtonStateReleased: - return "released" - case PointerButtonStatePressed: - return "pressed" - default: - return "" - } -} - -func (e PointerButtonState) Value() string { - switch e { - case PointerButtonStateReleased: - return "0" - case PointerButtonStatePressed: - return "1" - default: - return "" - } -} - -func (e PointerButtonState) String() string { - return e.Name() + "=" + e.Value() -} - -type PointerAxis uint32 - -// PointerAxis : axis types -// -// Describes the axis types of scroll events. -const ( - // PointerAxisVerticalScroll : vertical axis - PointerAxisVerticalScroll PointerAxis = 0 - // PointerAxisHorizontalScroll : horizontal axis - PointerAxisHorizontalScroll PointerAxis = 1 -) - -func (e PointerAxis) Name() string { - switch e { - case PointerAxisVerticalScroll: - return "vertical_scroll" - case PointerAxisHorizontalScroll: - return "horizontal_scroll" - default: - return "" - } -} - -func (e PointerAxis) Value() string { - switch e { - case PointerAxisVerticalScroll: - return "0" - case PointerAxisHorizontalScroll: - return "1" - default: - return "" - } -} - -func (e PointerAxis) String() string { - return e.Name() + "=" + e.Value() -} - -type PointerAxisSource uint32 - -// PointerAxisSource : axis source types -// -// Describes the source types for axis events. This indicates to the -// client how an axis event was physically generated; a client may -// adjust the user interface accordingly. For example, scroll events -// from a "finger" source may be in a smooth coordinate space with -// kinetic scrolling whereas a "wheel" source may be in discrete steps -// of a number of lines. -// -// The "continuous" axis source is a device generating events in a -// continuous coordinate space, but using something other than a -// finger. One example for this source is button-based scrolling where -// the vertical motion of a device is converted to scroll events while -// a button is held down. -// -// The "wheel tilt" axis source indicates that the actual device is a -// wheel but the scroll event is not caused by a rotation but a -// (usually sideways) tilt of the wheel. -const ( - // PointerAxisSourceWheel : a physical wheel rotation - PointerAxisSourceWheel PointerAxisSource = 0 - // PointerAxisSourceFinger : finger on a touch surface - PointerAxisSourceFinger PointerAxisSource = 1 - // PointerAxisSourceContinuous : continuous coordinate space - PointerAxisSourceContinuous PointerAxisSource = 2 - // PointerAxisSourceWheelTilt : a physical wheel tilt - PointerAxisSourceWheelTilt PointerAxisSource = 3 -) - -func (e PointerAxisSource) Name() string { - switch e { - case PointerAxisSourceWheel: - return "wheel" - case PointerAxisSourceFinger: - return "finger" - case PointerAxisSourceContinuous: - return "continuous" - case PointerAxisSourceWheelTilt: - return "wheel_tilt" - default: - return "" - } -} - -func (e PointerAxisSource) Value() string { - switch e { - case PointerAxisSourceWheel: - return "0" - case PointerAxisSourceFinger: - return "1" - case PointerAxisSourceContinuous: - return "2" - case PointerAxisSourceWheelTilt: - return "3" - default: - return "" - } -} - -func (e PointerAxisSource) String() string { - return e.Name() + "=" + e.Value() -} - -type PointerAxisRelativeDirection uint32 - -// PointerAxisRelativeDirection : axis relative direction -// -// This specifies the direction of the physical motion that caused a -// wl_pointer.axis event, relative to the wl_pointer.axis direction. -const ( - // PointerAxisRelativeDirectionIdentical : physical motion matches axis direction - PointerAxisRelativeDirectionIdentical PointerAxisRelativeDirection = 0 - // PointerAxisRelativeDirectionInverted : physical motion is the inverse of the axis direction - PointerAxisRelativeDirectionInverted PointerAxisRelativeDirection = 1 -) - -func (e PointerAxisRelativeDirection) Name() string { - switch e { - case PointerAxisRelativeDirectionIdentical: - return "identical" - case PointerAxisRelativeDirectionInverted: - return "inverted" - default: - return "" - } -} - -func (e PointerAxisRelativeDirection) Value() string { - switch e { - case PointerAxisRelativeDirectionIdentical: - return "0" - case PointerAxisRelativeDirectionInverted: - return "1" - default: - return "" - } -} - -func (e PointerAxisRelativeDirection) String() string { - return e.Name() + "=" + e.Value() -} - -// PointerEnterEvent : enter event -// -// Notification that this seat's pointer is focused on a certain -// surface. -// -// When a seat's focus enters a surface, the pointer image -// is undefined and a client should respond to this event by setting -// an appropriate pointer image with the set_cursor request. -type PointerEnterEvent struct { - Serial uint32 - Surface *Surface - SurfaceX float64 - SurfaceY float64 -} -type PointerEnterHandlerFunc func(PointerEnterEvent) - -// SetEnterHandler : sets handler for PointerEnterEvent -func (i *Pointer) SetEnterHandler(f PointerEnterHandlerFunc) { - i.enterHandler = f -} - -// PointerLeaveEvent : leave event -// -// Notification that this seat's pointer is no longer focused on -// a certain surface. -// -// The leave notification is sent before the enter notification -// for the new focus. -type PointerLeaveEvent struct { - Serial uint32 - Surface *Surface -} -type PointerLeaveHandlerFunc func(PointerLeaveEvent) - -// SetLeaveHandler : sets handler for PointerLeaveEvent -func (i *Pointer) SetLeaveHandler(f PointerLeaveHandlerFunc) { - i.leaveHandler = f -} - -// PointerMotionEvent : pointer motion event -// -// Notification of pointer location change. The arguments -// surface_x and surface_y are the location relative to the -// focused surface. -type PointerMotionEvent struct { - Time uint32 - SurfaceX float64 - SurfaceY float64 -} -type PointerMotionHandlerFunc func(PointerMotionEvent) - -// SetMotionHandler : sets handler for PointerMotionEvent -func (i *Pointer) SetMotionHandler(f PointerMotionHandlerFunc) { - i.motionHandler = f -} - -// PointerButtonEvent : pointer button event -// -// Mouse button click and release notifications. -// -// The location of the click is given by the last motion or -// enter event. -// The time argument is a timestamp with millisecond -// granularity, with an undefined base. -// -// The button is a button code as defined in the Linux kernel's -// linux/input-event-codes.h header file, e.g. BTN_LEFT. -// -// Any 16-bit button code value is reserved for future additions to the -// kernel's event code list. All other button codes above 0xFFFF are -// currently undefined but may be used in future versions of this -// protocol. -type PointerButtonEvent struct { - Serial uint32 - Time uint32 - Button uint32 - State uint32 -} -type PointerButtonHandlerFunc func(PointerButtonEvent) - -// SetButtonHandler : sets handler for PointerButtonEvent -func (i *Pointer) SetButtonHandler(f PointerButtonHandlerFunc) { - i.buttonHandler = f -} - -// PointerAxisEvent : axis event -// -// Scroll and other axis notifications. -// -// For scroll events (vertical and horizontal scroll axes), the -// value parameter is the length of a vector along the specified -// axis in a coordinate space identical to those of motion events, -// representing a relative movement along the specified axis. -// -// For devices that support movements non-parallel to axes multiple -// axis events will be emitted. -// -// When applicable, for example for touch pads, the server can -// choose to emit scroll events where the motion vector is -// equivalent to a motion event vector. -// -// When applicable, a client can transform its content relative to the -// scroll distance. -type PointerAxisEvent struct { - Time uint32 - Axis uint32 - Value float64 -} -type PointerAxisHandlerFunc func(PointerAxisEvent) - -// SetAxisHandler : sets handler for PointerAxisEvent -func (i *Pointer) SetAxisHandler(f PointerAxisHandlerFunc) { - i.axisHandler = f -} - -// PointerFrameEvent : end of a pointer event sequence -// -// Indicates the end of a set of events that logically belong together. -// A client is expected to accumulate the data in all events within the -// frame before proceeding. -// -// All wl_pointer events before a wl_pointer.frame event belong -// logically together. For example, in a diagonal scroll motion the -// compositor will send an optional wl_pointer.axis_source event, two -// wl_pointer.axis events (horizontal and vertical) and finally a -// wl_pointer.frame event. The client may use this information to -// calculate a diagonal vector for scrolling. -// -// When multiple wl_pointer.axis events occur within the same frame, -// the motion vector is the combined motion of all events. -// When a wl_pointer.axis and a wl_pointer.axis_stop event occur within -// the same frame, this indicates that axis movement in one axis has -// stopped but continues in the other axis. -// When multiple wl_pointer.axis_stop events occur within the same -// frame, this indicates that these axes stopped in the same instance. -// -// A wl_pointer.frame event is sent for every logical event group, -// even if the group only contains a single wl_pointer event. -// Specifically, a client may get a sequence: motion, frame, button, -// frame, axis, frame, axis_stop, frame. -// -// The wl_pointer.enter and wl_pointer.leave events are logical events -// generated by the compositor and not the hardware. These events are -// also grouped by a wl_pointer.frame. When a pointer moves from one -// surface to another, a compositor should group the -// wl_pointer.leave event within the same wl_pointer.frame. -// However, a client must not rely on wl_pointer.leave and -// wl_pointer.enter being in the same wl_pointer.frame. -// Compositor-specific policies may require the wl_pointer.leave and -// wl_pointer.enter event being split across multiple wl_pointer.frame -// groups. -type PointerFrameEvent struct{} -type PointerFrameHandlerFunc func(PointerFrameEvent) - -// SetFrameHandler : sets handler for PointerFrameEvent -func (i *Pointer) SetFrameHandler(f PointerFrameHandlerFunc) { - i.frameHandler = f -} - -// PointerAxisSourceEvent : axis source event -// -// Source information for scroll and other axes. -// -// This event does not occur on its own. It is sent before a -// wl_pointer.frame event and carries the source information for -// all events within that frame. -// -// The source specifies how this event was generated. If the source is -// wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be -// sent when the user lifts the finger off the device. -// -// If the source is wl_pointer.axis_source.wheel, -// wl_pointer.axis_source.wheel_tilt or -// wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may -// or may not be sent. Whether a compositor sends an axis_stop event -// for these sources is hardware-specific and implementation-dependent; -// clients must not rely on receiving an axis_stop event for these -// scroll sources and should treat scroll sequences from these scroll -// sources as unterminated by default. -// -// This event is optional. If the source is unknown for a particular -// axis event sequence, no event is sent. -// Only one wl_pointer.axis_source event is permitted per frame. -// -// The order of wl_pointer.axis_discrete and wl_pointer.axis_source is -// not guaranteed. -type PointerAxisSourceEvent struct { - AxisSource uint32 -} -type PointerAxisSourceHandlerFunc func(PointerAxisSourceEvent) - -// SetAxisSourceHandler : sets handler for PointerAxisSourceEvent -func (i *Pointer) SetAxisSourceHandler(f PointerAxisSourceHandlerFunc) { - i.axisSourceHandler = f -} - -// PointerAxisStopEvent : axis stop event -// -// Stop notification for scroll and other axes. -// -// For some wl_pointer.axis_source types, a wl_pointer.axis_stop event -// is sent to notify a client that the axis sequence has terminated. -// This enables the client to implement kinetic scrolling. -// See the wl_pointer.axis_source documentation for information on when -// this event may be generated. -// -// Any wl_pointer.axis events with the same axis_source after this -// event should be considered as the start of a new axis motion. -// -// The timestamp is to be interpreted identical to the timestamp in the -// wl_pointer.axis event. The timestamp value may be the same as a -// preceding wl_pointer.axis event. -type PointerAxisStopEvent struct { - Time uint32 - Axis uint32 -} -type PointerAxisStopHandlerFunc func(PointerAxisStopEvent) - -// SetAxisStopHandler : sets handler for PointerAxisStopEvent -func (i *Pointer) SetAxisStopHandler(f PointerAxisStopHandlerFunc) { - i.axisStopHandler = f -} - -// PointerAxisDiscreteEvent : axis click event -// -// Discrete step information for scroll and other axes. -// -// This event carries the axis value of the wl_pointer.axis event in -// discrete steps (e.g. mouse wheel clicks). -// -// This event is deprecated with wl_pointer version 8 - this event is not -// sent to clients supporting version 8 or later. -// -// This event does not occur on its own, it is coupled with a -// wl_pointer.axis event that represents this axis value on a -// continuous scale. The protocol guarantees that each axis_discrete -// event is always followed by exactly one axis event with the same -// axis number within the same wl_pointer.frame. Note that the protocol -// allows for other events to occur between the axis_discrete and -// its coupled axis event, including other axis_discrete or axis -// events. A wl_pointer.frame must not contain more than one axis_discrete -// event per axis type. -// -// This event is optional; continuous scrolling devices -// like two-finger scrolling on touchpads do not have discrete -// steps and do not generate this event. -// -// The discrete value carries the directional information. e.g. a value -// of -2 is two steps towards the negative direction of this axis. -// -// The axis number is identical to the axis number in the associated -// axis event. -// -// The order of wl_pointer.axis_discrete and wl_pointer.axis_source is -// not guaranteed. -type PointerAxisDiscreteEvent struct { - Axis uint32 - Discrete int32 -} -type PointerAxisDiscreteHandlerFunc func(PointerAxisDiscreteEvent) - -// SetAxisDiscreteHandler : sets handler for PointerAxisDiscreteEvent -func (i *Pointer) SetAxisDiscreteHandler(f PointerAxisDiscreteHandlerFunc) { - i.axisDiscreteHandler = f -} - -// PointerAxisValue120Event : axis high-resolution scroll event -// -// Discrete high-resolution scroll information. -// -// This event carries high-resolution wheel scroll information, -// with each multiple of 120 representing one logical scroll step -// (a wheel detent). For example, an axis_value120 of 30 is one quarter of -// a logical scroll step in the positive direction, a value120 of -// -240 are two logical scroll steps in the negative direction within the -// same hardware event. -// Clients that rely on discrete scrolling should accumulate the -// value120 to multiples of 120 before processing the event. -// -// The value120 must not be zero. -// -// This event replaces the wl_pointer.axis_discrete event in clients -// supporting wl_pointer version 8 or later. -// -// Where a wl_pointer.axis_source event occurs in the same -// wl_pointer.frame, the axis source applies to this event. -// -// The order of wl_pointer.axis_value120 and wl_pointer.axis_source is -// not guaranteed. -type PointerAxisValue120Event struct { - Axis uint32 - Value120 int32 -} -type PointerAxisValue120HandlerFunc func(PointerAxisValue120Event) - -// SetAxisValue120Handler : sets handler for PointerAxisValue120Event -func (i *Pointer) SetAxisValue120Handler(f PointerAxisValue120HandlerFunc) { - i.axisValue120Handler = f -} - -// PointerAxisRelativeDirectionEvent : axis relative physical direction event -// -// Relative directional information of the entity causing the axis -// motion. -// -// For a wl_pointer.axis event, the wl_pointer.axis_relative_direction -// event specifies the movement direction of the entity causing the -// wl_pointer.axis event. For example: -// - if a user's fingers on a touchpad move down and this -// causes a wl_pointer.axis vertical_scroll down event, the physical -// direction is 'identical' -// - if a user's fingers on a touchpad move down and this causes a -// wl_pointer.axis vertical_scroll up scroll up event ('natural -// scrolling'), the physical direction is 'inverted'. -// -// A client may use this information to adjust scroll motion of -// components. Specifically, enabling natural scrolling causes the -// content to change direction compared to traditional scrolling. -// Some widgets like volume control sliders should usually match the -// physical direction regardless of whether natural scrolling is -// active. This event enables clients to match the scroll direction of -// a widget to the physical direction. -// -// This event does not occur on its own, it is coupled with a -// wl_pointer.axis event that represents this axis value. -// The protocol guarantees that each axis_relative_direction event is -// always followed by exactly one axis event with the same -// axis number within the same wl_pointer.frame. Note that the protocol -// allows for other events to occur between the axis_relative_direction -// and its coupled axis event. -// -// The axis number is identical to the axis number in the associated -// axis event. -// -// The order of wl_pointer.axis_relative_direction, -// wl_pointer.axis_discrete and wl_pointer.axis_source is not -// guaranteed. -type PointerAxisRelativeDirectionEvent struct { - Axis uint32 - Direction uint32 -} -type PointerAxisRelativeDirectionHandlerFunc func(PointerAxisRelativeDirectionEvent) - -// SetAxisRelativeDirectionHandler : sets handler for PointerAxisRelativeDirectionEvent -func (i *Pointer) SetAxisRelativeDirectionHandler(f PointerAxisRelativeDirectionHandlerFunc) { - i.axisRelativeDirectionHandler = f -} - -func (i *Pointer) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.enterHandler == nil { - return - } - var e PointerEnterEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface) - l += 4 - e.SurfaceX = Fixed(data[l : l+4]) - l += 4 - e.SurfaceY = Fixed(data[l : l+4]) - l += 4 - - i.enterHandler(e) - case 1: - if i.leaveHandler == nil { - return - } - var e PointerLeaveEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface) - l += 4 - - i.leaveHandler(e) - case 2: - if i.motionHandler == nil { - return - } - var e PointerMotionEvent - l := 0 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.SurfaceX = Fixed(data[l : l+4]) - l += 4 - e.SurfaceY = Fixed(data[l : l+4]) - l += 4 - - i.motionHandler(e) - case 3: - if i.buttonHandler == nil { - return - } - var e PointerButtonEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.Button = Uint32(data[l : l+4]) - l += 4 - e.State = Uint32(data[l : l+4]) - l += 4 - - i.buttonHandler(e) - case 4: - if i.axisHandler == nil { - return - } - var e PointerAxisEvent - l := 0 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.Axis = Uint32(data[l : l+4]) - l += 4 - e.Value = Fixed(data[l : l+4]) - l += 4 - - i.axisHandler(e) - case 5: - if i.frameHandler == nil { - return - } - var e PointerFrameEvent - - i.frameHandler(e) - case 6: - if i.axisSourceHandler == nil { - return - } - var e PointerAxisSourceEvent - l := 0 - e.AxisSource = Uint32(data[l : l+4]) - l += 4 - - i.axisSourceHandler(e) - case 7: - if i.axisStopHandler == nil { - return - } - var e PointerAxisStopEvent - l := 0 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.Axis = Uint32(data[l : l+4]) - l += 4 - - i.axisStopHandler(e) - case 8: - if i.axisDiscreteHandler == nil { - return - } - var e PointerAxisDiscreteEvent - l := 0 - e.Axis = Uint32(data[l : l+4]) - l += 4 - e.Discrete = int32(Uint32(data[l : l+4])) - l += 4 - - i.axisDiscreteHandler(e) - case 9: - if i.axisValue120Handler == nil { - return - } - var e PointerAxisValue120Event - l := 0 - e.Axis = Uint32(data[l : l+4]) - l += 4 - e.Value120 = int32(Uint32(data[l : l+4])) - l += 4 - - i.axisValue120Handler(e) - case 10: - if i.axisRelativeDirectionHandler == nil { - return - } - var e PointerAxisRelativeDirectionEvent - l := 0 - e.Axis = Uint32(data[l : l+4]) - l += 4 - e.Direction = Uint32(data[l : l+4]) - l += 4 - - i.axisRelativeDirectionHandler(e) - } -} - -// KeyboardInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const KeyboardInterfaceName = "wl_keyboard" - -// Keyboard : keyboard input device -// -// The wl_keyboard interface represents one or more keyboards -// associated with a seat. -// -// Each wl_keyboard has the following logical state: -// -// - an active surface (possibly null), -// - the keys currently logically down, -// - the active modifiers, -// - the active group. -// -// By default, the active surface is null, the keys currently logically down -// are empty, the active modifiers and the active group are 0. -type Keyboard struct { - BaseProxy - keymapHandler KeyboardKeymapHandlerFunc - enterHandler KeyboardEnterHandlerFunc - leaveHandler KeyboardLeaveHandlerFunc - keyHandler KeyboardKeyHandlerFunc - modifiersHandler KeyboardModifiersHandlerFunc - repeatInfoHandler KeyboardRepeatInfoHandlerFunc -} - -// NewKeyboard : keyboard input device -// -// The wl_keyboard interface represents one or more keyboards -// associated with a seat. -// -// Each wl_keyboard has the following logical state: -// -// - an active surface (possibly null), -// - the keys currently logically down, -// - the active modifiers, -// - the active group. -// -// By default, the active surface is null, the keys currently logically down -// are empty, the active modifiers and the active group are 0. -func NewKeyboard(ctx *Context) *Keyboard { - wlKeyboard := &Keyboard{} - ctx.Register(wlKeyboard) - return wlKeyboard -} - -// Release : release the keyboard object -func (i *Keyboard) Release() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type KeyboardKeymapFormat uint32 - -// KeyboardKeymapFormat : keyboard mapping format -// -// This specifies the format of the keymap provided to the -// client with the wl_keyboard.keymap event. -const ( - // KeyboardKeymapFormatNoKeymap : no keymap; client must understand how to interpret the raw keycode - KeyboardKeymapFormatNoKeymap KeyboardKeymapFormat = 0 - // KeyboardKeymapFormatXkbV1 : libxkbcommon compatible, null-terminated string; to determine the xkb keycode, clients must add 8 to the key event keycode - KeyboardKeymapFormatXkbV1 KeyboardKeymapFormat = 1 -) - -func (e KeyboardKeymapFormat) Name() string { - switch e { - case KeyboardKeymapFormatNoKeymap: - return "no_keymap" - case KeyboardKeymapFormatXkbV1: - return "xkb_v1" - default: - return "" - } -} - -func (e KeyboardKeymapFormat) Value() string { - switch e { - case KeyboardKeymapFormatNoKeymap: - return "0" - case KeyboardKeymapFormatXkbV1: - return "1" - default: - return "" - } -} - -func (e KeyboardKeymapFormat) String() string { - return e.Name() + "=" + e.Value() -} - -type KeyboardKeyState uint32 - -// KeyboardKeyState : physical key state -// -// Describes the physical state of a key that produced the key event. -// -// Since version 10, the key can be in a "repeated" pseudo-state which -// means the same as "pressed", but is used to signal repetition in the -// key event. -// -// The key may only enter the repeated state after entering the pressed -// state and before entering the released state. This event may be -// generated multiple times while the key is down. -const ( - // KeyboardKeyStateReleased : key is not pressed - KeyboardKeyStateReleased KeyboardKeyState = 0 - // KeyboardKeyStatePressed : key is pressed - KeyboardKeyStatePressed KeyboardKeyState = 1 - // KeyboardKeyStateRepeated : key was repeated - KeyboardKeyStateRepeated KeyboardKeyState = 2 -) - -func (e KeyboardKeyState) Name() string { - switch e { - case KeyboardKeyStateReleased: - return "released" - case KeyboardKeyStatePressed: - return "pressed" - case KeyboardKeyStateRepeated: - return "repeated" - default: - return "" - } -} - -func (e KeyboardKeyState) Value() string { - switch e { - case KeyboardKeyStateReleased: - return "0" - case KeyboardKeyStatePressed: - return "1" - case KeyboardKeyStateRepeated: - return "2" - default: - return "" - } -} - -func (e KeyboardKeyState) String() string { - return e.Name() + "=" + e.Value() -} - -// KeyboardKeymapEvent : keyboard mapping -// -// This event provides a file descriptor to the client which can be -// memory-mapped in read-only mode to provide a keyboard mapping -// description. -// -// From version 7 onwards, the fd must be mapped with MAP_PRIVATE by -// the recipient, as MAP_SHARED may fail. -type KeyboardKeymapEvent struct { - Format uint32 - Fd int - Size uint32 -} -type KeyboardKeymapHandlerFunc func(KeyboardKeymapEvent) - -// SetKeymapHandler : sets handler for KeyboardKeymapEvent -func (i *Keyboard) SetKeymapHandler(f KeyboardKeymapHandlerFunc) { - i.keymapHandler = f -} - -// KeyboardEnterEvent : enter event -// -// Notification that this seat's keyboard focus is on a certain -// surface. -// -// The compositor must send the wl_keyboard.modifiers event after this -// event. -// -// In the wl_keyboard logical state, this event sets the active surface to -// the surface argument and the keys currently logically down to the keys -// in the keys argument. The compositor must not send this event if the -// wl_keyboard already had an active surface immediately before this event. -// -// Clients should not use the list of pressed keys to emulate key-press -// events. The order of keys in the list is unspecified. -type KeyboardEnterEvent struct { - Serial uint32 - Surface *Surface - Keys []byte -} -type KeyboardEnterHandlerFunc func(KeyboardEnterEvent) - -// SetEnterHandler : sets handler for KeyboardEnterEvent -func (i *Keyboard) SetEnterHandler(f KeyboardEnterHandlerFunc) { - i.enterHandler = f -} - -// KeyboardLeaveEvent : leave event -// -// Notification that this seat's keyboard focus is no longer on -// a certain surface. -// -// The leave notification is sent before the enter notification -// for the new focus. -// -// In the wl_keyboard logical state, this event resets all values to their -// defaults. The compositor must not send this event if the active surface -// of the wl_keyboard was not equal to the surface argument immediately -// before this event. -type KeyboardLeaveEvent struct { - Serial uint32 - Surface *Surface -} -type KeyboardLeaveHandlerFunc func(KeyboardLeaveEvent) - -// SetLeaveHandler : sets handler for KeyboardLeaveEvent -func (i *Keyboard) SetLeaveHandler(f KeyboardLeaveHandlerFunc) { - i.leaveHandler = f -} - -// KeyboardKeyEvent : key event -// -// A key was pressed or released. -// The time argument is a timestamp with millisecond -// granularity, with an undefined base. -// -// The key is a platform-specific key code that can be interpreted -// by feeding it to the keyboard mapping (see the keymap event). -// -// If this event produces a change in modifiers, then the resulting -// wl_keyboard.modifiers event must be sent after this event. -// -// In the wl_keyboard logical state, this event adds the key to the keys -// currently logically down (if the state argument is pressed) or removes -// the key from the keys currently logically down (if the state argument is -// released). The compositor must not send this event if the wl_keyboard -// did not have an active surface immediately before this event. The -// compositor must not send this event if state is pressed (resp. released) -// and the key was already logically down (resp. was not logically down) -// immediately before this event. -// -// Since version 10, compositors may send key events with the "repeated" -// key state when a wl_keyboard.repeat_info event with a rate argument of -// 0 has been received. This allows the compositor to take over the -// responsibility of key repetition. -type KeyboardKeyEvent struct { - Serial uint32 - Time uint32 - Key uint32 - State uint32 -} -type KeyboardKeyHandlerFunc func(KeyboardKeyEvent) - -// SetKeyHandler : sets handler for KeyboardKeyEvent -func (i *Keyboard) SetKeyHandler(f KeyboardKeyHandlerFunc) { - i.keyHandler = f -} - -// KeyboardModifiersEvent : modifier and group state -// -// Notifies clients that the modifier and/or group state has -// changed, and it should update its local state. -// -// The compositor may send this event without a surface of the client -// having keyboard focus, for example to tie modifier information to -// pointer focus instead. If a modifier event with pressed modifiers is sent -// without a prior enter event, the client can assume the modifier state is -// valid until it receives the next wl_keyboard.modifiers event. In order to -// reset the modifier state again, the compositor can send a -// wl_keyboard.modifiers event with no pressed modifiers. -// -// In the wl_keyboard logical state, this event updates the modifiers and -// group. -type KeyboardModifiersEvent struct { - Serial uint32 - ModsDepressed uint32 - ModsLatched uint32 - ModsLocked uint32 - Group uint32 -} -type KeyboardModifiersHandlerFunc func(KeyboardModifiersEvent) - -// SetModifiersHandler : sets handler for KeyboardModifiersEvent -func (i *Keyboard) SetModifiersHandler(f KeyboardModifiersHandlerFunc) { - i.modifiersHandler = f -} - -// KeyboardRepeatInfoEvent : repeat rate and delay -// -// Informs the client about the keyboard's repeat rate and delay. -// -// This event is sent as soon as the wl_keyboard object has been created, -// and is guaranteed to be received by the client before any key press -// event. -// -// Negative values for either rate or delay are illegal. A rate of zero -// will disable any repeating (regardless of the value of delay). -// -// This event can be sent later on as well with a new value if necessary, -// so clients should continue listening for the event past the creation -// of wl_keyboard. -type KeyboardRepeatInfoEvent struct { - Rate int32 - Delay int32 -} -type KeyboardRepeatInfoHandlerFunc func(KeyboardRepeatInfoEvent) - -// SetRepeatInfoHandler : sets handler for KeyboardRepeatInfoEvent -func (i *Keyboard) SetRepeatInfoHandler(f KeyboardRepeatInfoHandlerFunc) { - i.repeatInfoHandler = f -} - -func (i *Keyboard) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.keymapHandler == nil { - if fd != -1 { - unix.Close(fd) - } - return - } - var e KeyboardKeymapEvent - l := 0 - e.Format = Uint32(data[l : l+4]) - l += 4 - e.Fd = fd - e.Size = Uint32(data[l : l+4]) - l += 4 - - i.keymapHandler(e) - case 1: - if i.enterHandler == nil { - return - } - var e KeyboardEnterEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface) - l += 4 - keysLen := int(Uint32(data[l : l+4])) - l += 4 - e.Keys = make([]byte, keysLen) - copy(e.Keys, data[l:l+keysLen]) - l += keysLen - - i.enterHandler(e) - case 2: - if i.leaveHandler == nil { - return - } - var e KeyboardLeaveEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface) - l += 4 - - i.leaveHandler(e) - case 3: - if i.keyHandler == nil { - return - } - var e KeyboardKeyEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.Key = Uint32(data[l : l+4]) - l += 4 - e.State = Uint32(data[l : l+4]) - l += 4 - - i.keyHandler(e) - case 4: - if i.modifiersHandler == nil { - return - } - var e KeyboardModifiersEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.ModsDepressed = Uint32(data[l : l+4]) - l += 4 - e.ModsLatched = Uint32(data[l : l+4]) - l += 4 - e.ModsLocked = Uint32(data[l : l+4]) - l += 4 - e.Group = Uint32(data[l : l+4]) - l += 4 - - i.modifiersHandler(e) - case 5: - if i.repeatInfoHandler == nil { - return - } - var e KeyboardRepeatInfoEvent - l := 0 - e.Rate = int32(Uint32(data[l : l+4])) - l += 4 - e.Delay = int32(Uint32(data[l : l+4])) - l += 4 - - i.repeatInfoHandler(e) - } -} - -// TouchInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const TouchInterfaceName = "wl_touch" - -// Touch : touchscreen input device -// -// The wl_touch interface represents a touchscreen -// associated with a seat. -// -// Touch interactions can consist of one or more contacts. -// For each contact, a series of events is generated, starting -// with a down event, followed by zero or more motion events, -// and ending with an up event. Events relating to the same -// contact point can be identified by the ID of the sequence. -type Touch struct { - BaseProxy - downHandler TouchDownHandlerFunc - upHandler TouchUpHandlerFunc - motionHandler TouchMotionHandlerFunc - frameHandler TouchFrameHandlerFunc - cancelHandler TouchCancelHandlerFunc - shapeHandler TouchShapeHandlerFunc - orientationHandler TouchOrientationHandlerFunc -} - -// NewTouch : touchscreen input device -// -// The wl_touch interface represents a touchscreen -// associated with a seat. -// -// Touch interactions can consist of one or more contacts. -// For each contact, a series of events is generated, starting -// with a down event, followed by zero or more motion events, -// and ending with an up event. Events relating to the same -// contact point can be identified by the ID of the sequence. -func NewTouch(ctx *Context) *Touch { - wlTouch := &Touch{} - ctx.Register(wlTouch) - return wlTouch -} - -// Release : release the touch object -func (i *Touch) Release() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// TouchDownEvent : touch down event and beginning of a touch sequence -// -// A new touch point has appeared on the surface. This touch point is -// assigned a unique ID. Future events from this touch point reference -// this ID. The ID ceases to be valid after a touch up event and may be -// reused in the future. -type TouchDownEvent struct { - Serial uint32 - Time uint32 - Surface *Surface - Id int32 - X float64 - Y float64 -} -type TouchDownHandlerFunc func(TouchDownEvent) - -// SetDownHandler : sets handler for TouchDownEvent -func (i *Touch) SetDownHandler(f TouchDownHandlerFunc) { - i.downHandler = f -} - -// TouchUpEvent : end of a touch event sequence -// -// The touch point has disappeared. No further events will be sent for -// this touch point and the touch point's ID is released and may be -// reused in a future touch down event. -type TouchUpEvent struct { - Serial uint32 - Time uint32 - Id int32 -} -type TouchUpHandlerFunc func(TouchUpEvent) - -// SetUpHandler : sets handler for TouchUpEvent -func (i *Touch) SetUpHandler(f TouchUpHandlerFunc) { - i.upHandler = f -} - -// TouchMotionEvent : update of touch point coordinates -// -// A touch point has changed coordinates. -type TouchMotionEvent struct { - Time uint32 - Id int32 - X float64 - Y float64 -} -type TouchMotionHandlerFunc func(TouchMotionEvent) - -// SetMotionHandler : sets handler for TouchMotionEvent -func (i *Touch) SetMotionHandler(f TouchMotionHandlerFunc) { - i.motionHandler = f -} - -// TouchFrameEvent : end of touch frame event -// -// Indicates the end of a set of events that logically belong together. -// A client is expected to accumulate the data in all events within the -// frame before proceeding. -// -// A wl_touch.frame terminates at least one event but otherwise no -// guarantee is provided about the set of events within a frame. A client -// must assume that any state not updated in a frame is unchanged from the -// previously known state. -type TouchFrameEvent struct{} -type TouchFrameHandlerFunc func(TouchFrameEvent) - -// SetFrameHandler : sets handler for TouchFrameEvent -func (i *Touch) SetFrameHandler(f TouchFrameHandlerFunc) { - i.frameHandler = f -} - -// TouchCancelEvent : touch session cancelled -// -// Sent if the compositor decides the touch stream is a global -// gesture. No further events are sent to the clients from that -// particular gesture. Touch cancellation applies to all touch points -// currently active on this client's surface. The client is -// responsible for finalizing the touch points, future touch points on -// this surface may reuse the touch point ID. -// -// No frame event is required after the cancel event. -type TouchCancelEvent struct{} -type TouchCancelHandlerFunc func(TouchCancelEvent) - -// SetCancelHandler : sets handler for TouchCancelEvent -func (i *Touch) SetCancelHandler(f TouchCancelHandlerFunc) { - i.cancelHandler = f -} - -// TouchShapeEvent : update shape of touch point -// -// Sent when a touchpoint has changed its shape. -// -// This event does not occur on its own. It is sent before a -// wl_touch.frame event and carries the new shape information for -// any previously reported, or new touch points of that frame. -// -// Other events describing the touch point such as wl_touch.down, -// wl_touch.motion or wl_touch.orientation may be sent within the -// same wl_touch.frame. A client should treat these events as a single -// logical touch point update. The order of wl_touch.shape, -// wl_touch.orientation and wl_touch.motion is not guaranteed. -// A wl_touch.down event is guaranteed to occur before the first -// wl_touch.shape event for this touch ID but both events may occur within -// the same wl_touch.frame. -// -// A touchpoint shape is approximated by an ellipse through the major and -// minor axis length. The major axis length describes the longer diameter -// of the ellipse, while the minor axis length describes the shorter -// diameter. Major and minor are orthogonal and both are specified in -// surface-local coordinates. The center of the ellipse is always at the -// touchpoint location as reported by wl_touch.down or wl_touch.move. -// -// This event is only sent by the compositor if the touch device supports -// shape reports. The client has to make reasonable assumptions about the -// shape if it did not receive this event. -type TouchShapeEvent struct { - Id int32 - Major float64 - Minor float64 -} -type TouchShapeHandlerFunc func(TouchShapeEvent) - -// SetShapeHandler : sets handler for TouchShapeEvent -func (i *Touch) SetShapeHandler(f TouchShapeHandlerFunc) { - i.shapeHandler = f -} - -// TouchOrientationEvent : update orientation of touch point -// -// Sent when a touchpoint has changed its orientation. -// -// This event does not occur on its own. It is sent before a -// wl_touch.frame event and carries the new shape information for -// any previously reported, or new touch points of that frame. -// -// Other events describing the touch point such as wl_touch.down, -// wl_touch.motion or wl_touch.shape may be sent within the -// same wl_touch.frame. A client should treat these events as a single -// logical touch point update. The order of wl_touch.shape, -// wl_touch.orientation and wl_touch.motion is not guaranteed. -// A wl_touch.down event is guaranteed to occur before the first -// wl_touch.orientation event for this touch ID but both events may occur -// within the same wl_touch.frame. -// -// The orientation describes the clockwise angle of a touchpoint's major -// axis to the positive surface y-axis and is normalized to the -180 to -// +180 degree range. The granularity of orientation depends on the touch -// device, some devices only support binary rotation values between 0 and -// 90 degrees. -// -// This event is only sent by the compositor if the touch device supports -// orientation reports. -type TouchOrientationEvent struct { - Id int32 - Orientation float64 -} -type TouchOrientationHandlerFunc func(TouchOrientationEvent) - -// SetOrientationHandler : sets handler for TouchOrientationEvent -func (i *Touch) SetOrientationHandler(f TouchOrientationHandlerFunc) { - i.orientationHandler = f -} - -func (i *Touch) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.downHandler == nil { - return - } - var e TouchDownEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.Surface = i.Context().GetProxy(Uint32(data[l : l+4])).(*Surface) - l += 4 - e.Id = int32(Uint32(data[l : l+4])) - l += 4 - e.X = Fixed(data[l : l+4]) - l += 4 - e.Y = Fixed(data[l : l+4]) - l += 4 - - i.downHandler(e) - case 1: - if i.upHandler == nil { - return - } - var e TouchUpEvent - l := 0 - e.Serial = Uint32(data[l : l+4]) - l += 4 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.Id = int32(Uint32(data[l : l+4])) - l += 4 - - i.upHandler(e) - case 2: - if i.motionHandler == nil { - return - } - var e TouchMotionEvent - l := 0 - e.Time = Uint32(data[l : l+4]) - l += 4 - e.Id = int32(Uint32(data[l : l+4])) - l += 4 - e.X = Fixed(data[l : l+4]) - l += 4 - e.Y = Fixed(data[l : l+4]) - l += 4 - - i.motionHandler(e) - case 3: - if i.frameHandler == nil { - return - } - var e TouchFrameEvent - - i.frameHandler(e) - case 4: - if i.cancelHandler == nil { - return - } - var e TouchCancelEvent - - i.cancelHandler(e) - case 5: - if i.shapeHandler == nil { - return - } - var e TouchShapeEvent - l := 0 - e.Id = int32(Uint32(data[l : l+4])) - l += 4 - e.Major = Fixed(data[l : l+4]) - l += 4 - e.Minor = Fixed(data[l : l+4]) - l += 4 - - i.shapeHandler(e) - case 6: - if i.orientationHandler == nil { - return - } - var e TouchOrientationEvent - l := 0 - e.Id = int32(Uint32(data[l : l+4])) - l += 4 - e.Orientation = Fixed(data[l : l+4]) - l += 4 - - i.orientationHandler(e) - } -} - -// OutputInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const OutputInterfaceName = "wl_output" - -// Output : compositor output region -// -// An output describes part of the compositor geometry. The -// compositor works in the 'compositor coordinate system' and an -// output corresponds to a rectangular area in that space that is -// actually visible. This typically corresponds to a monitor that -// displays part of the compositor space. This object is published -// as global during start up, or when a monitor is hotplugged. -type Output struct { - BaseProxy - geometryHandler OutputGeometryHandlerFunc - modeHandler OutputModeHandlerFunc - doneHandler OutputDoneHandlerFunc - scaleHandler OutputScaleHandlerFunc - nameHandler OutputNameHandlerFunc - descriptionHandler OutputDescriptionHandlerFunc -} - -// NewOutput : compositor output region -// -// An output describes part of the compositor geometry. The -// compositor works in the 'compositor coordinate system' and an -// output corresponds to a rectangular area in that space that is -// actually visible. This typically corresponds to a monitor that -// displays part of the compositor space. This object is published -// as global during start up, or when a monitor is hotplugged. -func NewOutput(ctx *Context) *Output { - wlOutput := &Output{} - ctx.Register(wlOutput) - return wlOutput -} - -// Release : release the output object -// -// Using this request a client can tell the server that it is not going to -// use the output object anymore. -func (i *Output) Release() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type OutputSubpixel uint32 - -// OutputSubpixel : subpixel geometry information -// -// This enumeration describes how the physical -// pixels on an output are laid out. -const ( - // OutputSubpixelUnknown : unknown geometry - OutputSubpixelUnknown OutputSubpixel = 0 - // OutputSubpixelNone : no geometry - OutputSubpixelNone OutputSubpixel = 1 - // OutputSubpixelHorizontalRgb : horizontal RGB - OutputSubpixelHorizontalRgb OutputSubpixel = 2 - // OutputSubpixelHorizontalBgr : horizontal BGR - OutputSubpixelHorizontalBgr OutputSubpixel = 3 - // OutputSubpixelVerticalRgb : vertical RGB - OutputSubpixelVerticalRgb OutputSubpixel = 4 - // OutputSubpixelVerticalBgr : vertical BGR - OutputSubpixelVerticalBgr OutputSubpixel = 5 -) - -func (e OutputSubpixel) Name() string { - switch e { - case OutputSubpixelUnknown: - return "unknown" - case OutputSubpixelNone: - return "none" - case OutputSubpixelHorizontalRgb: - return "horizontal_rgb" - case OutputSubpixelHorizontalBgr: - return "horizontal_bgr" - case OutputSubpixelVerticalRgb: - return "vertical_rgb" - case OutputSubpixelVerticalBgr: - return "vertical_bgr" - default: - return "" - } -} - -func (e OutputSubpixel) Value() string { - switch e { - case OutputSubpixelUnknown: - return "0" - case OutputSubpixelNone: - return "1" - case OutputSubpixelHorizontalRgb: - return "2" - case OutputSubpixelHorizontalBgr: - return "3" - case OutputSubpixelVerticalRgb: - return "4" - case OutputSubpixelVerticalBgr: - return "5" - default: - return "" - } -} - -func (e OutputSubpixel) String() string { - return e.Name() + "=" + e.Value() -} - -type OutputTransform uint32 - -// OutputTransform : transformation applied to buffer contents -// -// This describes transformations that clients and compositors apply to -// buffer contents. -// -// The flipped values correspond to an initial flip around a -// vertical axis followed by rotation. -// -// The purpose is mainly to allow clients to render accordingly and -// tell the compositor, so that for fullscreen surfaces, the -// compositor will still be able to scan out directly from client -// surfaces. -const ( - // OutputTransformNormal : no transform - OutputTransformNormal OutputTransform = 0 - // OutputTransform90 : 90 degrees counter-clockwise - OutputTransform90 OutputTransform = 1 - // OutputTransform180 : 180 degrees counter-clockwise - OutputTransform180 OutputTransform = 2 - // OutputTransform270 : 270 degrees counter-clockwise - OutputTransform270 OutputTransform = 3 - // OutputTransformFlipped : 180 degree flip around a vertical axis - OutputTransformFlipped OutputTransform = 4 - // OutputTransformFlipped90 : flip and rotate 90 degrees counter-clockwise - OutputTransformFlipped90 OutputTransform = 5 - // OutputTransformFlipped180 : flip and rotate 180 degrees counter-clockwise - OutputTransformFlipped180 OutputTransform = 6 - // OutputTransformFlipped270 : flip and rotate 270 degrees counter-clockwise - OutputTransformFlipped270 OutputTransform = 7 -) - -func (e OutputTransform) Name() string { - switch e { - case OutputTransformNormal: - return "normal" - case OutputTransform90: - return "90" - case OutputTransform180: - return "180" - case OutputTransform270: - return "270" - case OutputTransformFlipped: - return "flipped" - case OutputTransformFlipped90: - return "flipped_90" - case OutputTransformFlipped180: - return "flipped_180" - case OutputTransformFlipped270: - return "flipped_270" - default: - return "" - } -} - -func (e OutputTransform) Value() string { - switch e { - case OutputTransformNormal: - return "0" - case OutputTransform90: - return "1" - case OutputTransform180: - return "2" - case OutputTransform270: - return "3" - case OutputTransformFlipped: - return "4" - case OutputTransformFlipped90: - return "5" - case OutputTransformFlipped180: - return "6" - case OutputTransformFlipped270: - return "7" - default: - return "" - } -} - -func (e OutputTransform) String() string { - return e.Name() + "=" + e.Value() -} - -type OutputMode uint32 - -// OutputMode : mode information -// -// These flags describe properties of an output mode. -// They are used in the flags bitfield of the mode event. -const ( - // OutputModeCurrent : indicates this is the current mode - OutputModeCurrent OutputMode = 0x1 - // OutputModePreferred : indicates this is the preferred mode - OutputModePreferred OutputMode = 0x2 -) - -func (e OutputMode) Name() string { - switch e { - case OutputModeCurrent: - return "current" - case OutputModePreferred: - return "preferred" - default: - return "" - } -} - -func (e OutputMode) Value() string { - switch e { - case OutputModeCurrent: - return "0x1" - case OutputModePreferred: - return "0x2" - default: - return "" - } -} - -func (e OutputMode) String() string { - return e.Name() + "=" + e.Value() -} - -// OutputGeometryEvent : properties of the output -// -// The geometry event describes geometric properties of the output. -// The event is sent when binding to the output object and whenever -// any of the properties change. -// -// The physical size can be set to zero if it doesn't make sense for this -// output (e.g. for projectors or virtual outputs). -// -// The geometry event will be followed by a done event (starting from -// version 2). -// -// Clients should use wl_surface.preferred_buffer_transform instead of the -// transform advertised by this event to find the preferred buffer -// transform to use for a surface. -// -// Note: wl_output only advertises partial information about the output -// position and identification. Some compositors, for instance those not -// implementing a desktop-style output layout or those exposing virtual -// outputs, might fake this information. Instead of using x and y, clients -// should use xdg_output.logical_position. Instead of using make and model, -// clients should use name and description. -type OutputGeometryEvent struct { - X int32 - Y int32 - PhysicalWidth int32 - PhysicalHeight int32 - Subpixel int32 - Make string - Model string - Transform int32 -} -type OutputGeometryHandlerFunc func(OutputGeometryEvent) - -// SetGeometryHandler : sets handler for OutputGeometryEvent -func (i *Output) SetGeometryHandler(f OutputGeometryHandlerFunc) { - i.geometryHandler = f -} - -// OutputModeEvent : advertise available modes for the output -// -// The mode event describes an available mode for the output. -// -// The event is sent when binding to the output object and there -// will always be one mode, the current mode. The event is sent -// again if an output changes mode, for the mode that is now -// current. In other words, the current mode is always the last -// mode that was received with the current flag set. -// -// Non-current modes are deprecated. A compositor can decide to only -// advertise the current mode and never send other modes. Clients -// should not rely on non-current modes. -// -// The size of a mode is given in physical hardware units of -// the output device. This is not necessarily the same as -// the output size in the global compositor space. For instance, -// the output may be scaled, as described in wl_output.scale, -// or transformed, as described in wl_output.transform. Clients -// willing to retrieve the output size in the global compositor -// space should use xdg_output.logical_size instead. -// -// The vertical refresh rate can be set to zero if it doesn't make -// sense for this output (e.g. for virtual outputs). -// -// The mode event will be followed by a done event (starting from -// version 2). -// -// Clients should not use the refresh rate to schedule frames. Instead, -// they should use the wl_surface.frame event or the presentation-time -// protocol. -// -// Note: this information is not always meaningful for all outputs. Some -// compositors, such as those exposing virtual outputs, might fake the -// refresh rate or the size. -type OutputModeEvent struct { - Flags uint32 - Width int32 - Height int32 - Refresh int32 -} -type OutputModeHandlerFunc func(OutputModeEvent) - -// SetModeHandler : sets handler for OutputModeEvent -func (i *Output) SetModeHandler(f OutputModeHandlerFunc) { - i.modeHandler = f -} - -// OutputDoneEvent : sent all information about output -// -// This event is sent after all other properties have been -// sent after binding to the output object and after any -// other property changes done after that. This allows -// changes to the output properties to be seen as -// atomic, even if they happen via multiple events. -type OutputDoneEvent struct{} -type OutputDoneHandlerFunc func(OutputDoneEvent) - -// SetDoneHandler : sets handler for OutputDoneEvent -func (i *Output) SetDoneHandler(f OutputDoneHandlerFunc) { - i.doneHandler = f -} - -// OutputScaleEvent : output scaling properties -// -// This event contains scaling geometry information -// that is not in the geometry event. It may be sent after -// binding the output object or if the output scale changes -// later. The compositor will emit a non-zero, positive -// value for scale. If it is not sent, the client should -// assume a scale of 1. -// -// A scale larger than 1 means that the compositor will -// automatically scale surface buffers by this amount -// when rendering. This is used for very high resolution -// displays where applications rendering at the native -// resolution would be too small to be legible. -// -// Clients should use wl_surface.preferred_buffer_scale -// instead of this event to find the preferred buffer -// scale to use for a surface. -// -// The scale event will be followed by a done event. -type OutputScaleEvent struct { - Factor int32 -} -type OutputScaleHandlerFunc func(OutputScaleEvent) - -// SetScaleHandler : sets handler for OutputScaleEvent -func (i *Output) SetScaleHandler(f OutputScaleHandlerFunc) { - i.scaleHandler = f -} - -// OutputNameEvent : name of this output -// -// Many compositors will assign user-friendly names to their outputs, show -// them to the user, allow the user to refer to an output, etc. The client -// may wish to know this name as well to offer the user similar behaviors. -// -// The name is a UTF-8 string with no convention defined for its contents. -// Each name is unique among all wl_output globals. The name is only -// guaranteed to be unique for the compositor instance. -// -// The same output name is used for all clients for a given wl_output -// global. Thus, the name can be shared across processes to refer to a -// specific wl_output global. -// -// The name is not guaranteed to be persistent across sessions, thus cannot -// be used to reliably identify an output in e.g. configuration files. -// -// Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do -// not assume that the name is a reflection of an underlying DRM connector, -// X11 connection, etc. -// -// The name event is sent after binding the output object. This event is -// only sent once per output object, and the name does not change over the -// lifetime of the wl_output global. -// -// Compositors may re-use the same output name if the wl_output global is -// destroyed and re-created later. Compositors should avoid re-using the -// same name if possible. -// -// The name event will be followed by a done event. -type OutputNameEvent struct { - Name string -} -type OutputNameHandlerFunc func(OutputNameEvent) - -// SetNameHandler : sets handler for OutputNameEvent -func (i *Output) SetNameHandler(f OutputNameHandlerFunc) { - i.nameHandler = f -} - -// OutputDescriptionEvent : human-readable description of this output -// -// Many compositors can produce human-readable descriptions of their -// outputs. The client may wish to know this description as well, e.g. for -// output selection purposes. -// -// The description is a UTF-8 string with no convention defined for its -// contents. The description is not guaranteed to be unique among all -// wl_output globals. Examples might include 'Foocorp 11" Display' or -// 'Virtual X11 output via :1'. -// -// The description event is sent after binding the output object and -// whenever the description changes. The description is optional, and may -// not be sent at all. -// -// The description event will be followed by a done event. -type OutputDescriptionEvent struct { - Description string -} -type OutputDescriptionHandlerFunc func(OutputDescriptionEvent) - -// SetDescriptionHandler : sets handler for OutputDescriptionEvent -func (i *Output) SetDescriptionHandler(f OutputDescriptionHandlerFunc) { - i.descriptionHandler = f -} - -func (i *Output) Dispatch(opcode uint32, fd int, data []byte) { - switch opcode { - case 0: - if i.geometryHandler == nil { - return - } - var e OutputGeometryEvent - l := 0 - e.X = int32(Uint32(data[l : l+4])) - l += 4 - e.Y = int32(Uint32(data[l : l+4])) - l += 4 - e.PhysicalWidth = int32(Uint32(data[l : l+4])) - l += 4 - e.PhysicalHeight = int32(Uint32(data[l : l+4])) - l += 4 - e.Subpixel = int32(Uint32(data[l : l+4])) - l += 4 - makeLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.Make = String(data[l : l+makeLen]) - l += makeLen - modelLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.Model = String(data[l : l+modelLen]) - l += modelLen - e.Transform = int32(Uint32(data[l : l+4])) - l += 4 - - i.geometryHandler(e) - case 1: - if i.modeHandler == nil { - return - } - var e OutputModeEvent - l := 0 - e.Flags = Uint32(data[l : l+4]) - l += 4 - e.Width = int32(Uint32(data[l : l+4])) - l += 4 - e.Height = int32(Uint32(data[l : l+4])) - l += 4 - e.Refresh = int32(Uint32(data[l : l+4])) - l += 4 - - i.modeHandler(e) - case 2: - if i.doneHandler == nil { - return - } - var e OutputDoneEvent - - i.doneHandler(e) - case 3: - if i.scaleHandler == nil { - return - } - var e OutputScaleEvent - l := 0 - e.Factor = int32(Uint32(data[l : l+4])) - l += 4 - - i.scaleHandler(e) - case 4: - if i.nameHandler == nil { - return - } - var e OutputNameEvent - l := 0 - nameLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.Name = String(data[l : l+nameLen]) - l += nameLen - - i.nameHandler(e) - case 5: - if i.descriptionHandler == nil { - return - } - var e OutputDescriptionEvent - l := 0 - descriptionLen := PaddedLen(int(Uint32(data[l : l+4]))) - l += 4 - e.Description = String(data[l : l+descriptionLen]) - l += descriptionLen - - i.descriptionHandler(e) - } -} - -// RegionInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const RegionInterfaceName = "wl_region" - -// Region : region interface -// -// A region object describes an area. -// -// Region objects are used to describe the opaque and input -// regions of a surface. -type Region struct { - BaseProxy -} - -// NewRegion : region interface -// -// A region object describes an area. -// -// Region objects are used to describe the opaque and input -// regions of a surface. -func NewRegion(ctx *Context) *Region { - wlRegion := &Region{} - ctx.Register(wlRegion) - return wlRegion -} - -// Destroy : destroy region -// -// Destroy the region. This will invalidate the object ID. -func (i *Region) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Add : add rectangle to region -// -// Add the specified rectangle to the region. -// -// x: region-local x coordinate -// y: region-local y coordinate -// width: rectangle width -// height: rectangle height -func (i *Region) Add(x, y, width, height int32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// Subtract : subtract rectangle from region -// -// Subtract the specified rectangle from the region. -// -// x: region-local x coordinate -// y: region-local y coordinate -// width: rectangle width -// height: rectangle height -func (i *Region) Subtract(x, y, width, height int32) error { - const opcode = 2 - const _reqBufLen = 8 + 4 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(width)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(height)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SubcompositorInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const SubcompositorInterfaceName = "wl_subcompositor" - -// Subcompositor : sub-surface compositing -// -// The global interface exposing sub-surface compositing capabilities. -// A wl_surface, that has sub-surfaces associated, is called the -// parent surface. Sub-surfaces can be arbitrarily nested and create -// a tree of sub-surfaces. -// -// The root surface in a tree of sub-surfaces is the main -// surface. The main surface cannot be a sub-surface, because -// sub-surfaces must always have a parent. -// -// A main surface with its sub-surfaces forms a (compound) window. -// For window management purposes, this set of wl_surface objects is -// to be considered as a single window, and it should also behave as -// such. -// -// The aim of sub-surfaces is to offload some of the compositing work -// within a window from clients to the compositor. A prime example is -// a video player with decorations and video in separate wl_surface -// objects. This should allow the compositor to pass YUV video buffer -// processing to dedicated overlay hardware when possible. -type Subcompositor struct { - BaseProxy -} - -// NewSubcompositor : sub-surface compositing -// -// The global interface exposing sub-surface compositing capabilities. -// A wl_surface, that has sub-surfaces associated, is called the -// parent surface. Sub-surfaces can be arbitrarily nested and create -// a tree of sub-surfaces. -// -// The root surface in a tree of sub-surfaces is the main -// surface. The main surface cannot be a sub-surface, because -// sub-surfaces must always have a parent. -// -// A main surface with its sub-surfaces forms a (compound) window. -// For window management purposes, this set of wl_surface objects is -// to be considered as a single window, and it should also behave as -// such. -// -// The aim of sub-surfaces is to offload some of the compositing work -// within a window from clients to the compositor. A prime example is -// a video player with decorations and video in separate wl_surface -// objects. This should allow the compositor to pass YUV video buffer -// processing to dedicated overlay hardware when possible. -func NewSubcompositor(ctx *Context) *Subcompositor { - wlSubcompositor := &Subcompositor{} - ctx.Register(wlSubcompositor) - return wlSubcompositor -} - -// Destroy : unbind from the subcompositor interface -// -// Informs the server that the client will not be using this -// protocol object anymore. This does not affect any other -// objects, wl_subsurface objects included. -func (i *Subcompositor) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// GetSubsurface : give a surface the role sub-surface -// -// Create a sub-surface interface for the given surface, and -// associate it with the given parent surface. This turns a -// plain wl_surface into a sub-surface. -// -// The to-be sub-surface must not already have another role, and it -// must not have an existing wl_subsurface object. Otherwise the -// bad_surface protocol error is raised. -// -// Adding sub-surfaces to a parent is a double-buffered operation on the -// parent (see wl_surface.commit). The effect of adding a sub-surface -// becomes visible on the next time the state of the parent surface is -// applied. -// -// The parent surface must not be one of the child surface's descendants, -// and the parent must be different from the child surface, otherwise the -// bad_parent protocol error is raised. -// -// This request modifies the behaviour of wl_surface.commit request on -// the sub-surface, see the documentation on wl_subsurface interface. -// -// surface: the surface to be turned into a sub-surface -// parent: the parent surface -func (i *Subcompositor) GetSubsurface(surface, parent *Surface) (*Subsurface, error) { - id := NewSubsurface(i.Context()) - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], id.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], surface.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], parent.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return id, err -} - -type SubcompositorError uint32 - -// SubcompositorError : -const ( - // SubcompositorErrorBadSurface : the to-be sub-surface is invalid - SubcompositorErrorBadSurface SubcompositorError = 0 - // SubcompositorErrorBadParent : the to-be sub-surface parent is invalid - SubcompositorErrorBadParent SubcompositorError = 1 -) - -func (e SubcompositorError) Name() string { - switch e { - case SubcompositorErrorBadSurface: - return "bad_surface" - case SubcompositorErrorBadParent: - return "bad_parent" - default: - return "" - } -} - -func (e SubcompositorError) Value() string { - switch e { - case SubcompositorErrorBadSurface: - return "0" - case SubcompositorErrorBadParent: - return "1" - default: - return "" - } -} - -func (e SubcompositorError) String() string { - return e.Name() + "=" + e.Value() -} - -// SubsurfaceInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const SubsurfaceInterfaceName = "wl_subsurface" - -// Subsurface : sub-surface interface to a wl_surface -// -// An additional interface to a wl_surface object, which has been -// made a sub-surface. A sub-surface has one parent surface. A -// sub-surface's size and position are not limited to that of the parent. -// Particularly, a sub-surface is not automatically clipped to its -// parent's area. -// -// A sub-surface becomes mapped, when a non-NULL wl_buffer is applied -// and the parent surface is mapped. The order of which one happens -// first is irrelevant. A sub-surface is hidden if the parent becomes -// hidden, or if a NULL wl_buffer is applied. These rules apply -// recursively through the tree of surfaces. -// -// The behaviour of a wl_surface.commit request on a sub-surface -// depends on the sub-surface's mode. The possible modes are -// synchronized and desynchronized, see methods -// wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized -// mode caches the wl_surface state to be applied when the parent's -// state gets applied, and desynchronized mode applies the pending -// wl_surface state directly. A sub-surface is initially in the -// synchronized mode. -// -// Sub-surfaces also have another kind of state, which is managed by -// wl_subsurface requests, as opposed to wl_surface requests. This -// state includes the sub-surface position relative to the parent -// surface (wl_subsurface.set_position), and the stacking order of -// the parent and its sub-surfaces (wl_subsurface.place_above and -// .place_below). This state is applied when the parent surface's -// wl_surface state is applied, regardless of the sub-surface's mode. -// As the exception, set_sync and set_desync are effective immediately. -// -// The main surface can be thought to be always in desynchronized mode, -// since it does not have a parent in the sub-surfaces sense. -// -// Even if a sub-surface is in desynchronized mode, it will behave as -// in synchronized mode, if its parent surface behaves as in -// synchronized mode. This rule is applied recursively throughout the -// tree of surfaces. This means, that one can set a sub-surface into -// synchronized mode, and then assume that all its child and grand-child -// sub-surfaces are synchronized, too, without explicitly setting them. -// -// Destroying a sub-surface takes effect immediately. If you need to -// synchronize the removal of a sub-surface to the parent surface update, -// unmap the sub-surface first by attaching a NULL wl_buffer, update parent, -// and then destroy the sub-surface. -// -// If the parent wl_surface object is destroyed, the sub-surface is -// unmapped. -// -// A sub-surface never has the keyboard focus of any seat. -// -// The wl_surface.offset request is ignored: clients must use set_position -// instead to move the sub-surface. -type Subsurface struct { - BaseProxy -} - -// NewSubsurface : sub-surface interface to a wl_surface -// -// An additional interface to a wl_surface object, which has been -// made a sub-surface. A sub-surface has one parent surface. A -// sub-surface's size and position are not limited to that of the parent. -// Particularly, a sub-surface is not automatically clipped to its -// parent's area. -// -// A sub-surface becomes mapped, when a non-NULL wl_buffer is applied -// and the parent surface is mapped. The order of which one happens -// first is irrelevant. A sub-surface is hidden if the parent becomes -// hidden, or if a NULL wl_buffer is applied. These rules apply -// recursively through the tree of surfaces. -// -// The behaviour of a wl_surface.commit request on a sub-surface -// depends on the sub-surface's mode. The possible modes are -// synchronized and desynchronized, see methods -// wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized -// mode caches the wl_surface state to be applied when the parent's -// state gets applied, and desynchronized mode applies the pending -// wl_surface state directly. A sub-surface is initially in the -// synchronized mode. -// -// Sub-surfaces also have another kind of state, which is managed by -// wl_subsurface requests, as opposed to wl_surface requests. This -// state includes the sub-surface position relative to the parent -// surface (wl_subsurface.set_position), and the stacking order of -// the parent and its sub-surfaces (wl_subsurface.place_above and -// .place_below). This state is applied when the parent surface's -// wl_surface state is applied, regardless of the sub-surface's mode. -// As the exception, set_sync and set_desync are effective immediately. -// -// The main surface can be thought to be always in desynchronized mode, -// since it does not have a parent in the sub-surfaces sense. -// -// Even if a sub-surface is in desynchronized mode, it will behave as -// in synchronized mode, if its parent surface behaves as in -// synchronized mode. This rule is applied recursively throughout the -// tree of surfaces. This means, that one can set a sub-surface into -// synchronized mode, and then assume that all its child and grand-child -// sub-surfaces are synchronized, too, without explicitly setting them. -// -// Destroying a sub-surface takes effect immediately. If you need to -// synchronize the removal of a sub-surface to the parent surface update, -// unmap the sub-surface first by attaching a NULL wl_buffer, update parent, -// and then destroy the sub-surface. -// -// If the parent wl_surface object is destroyed, the sub-surface is -// unmapped. -// -// A sub-surface never has the keyboard focus of any seat. -// -// The wl_surface.offset request is ignored: clients must use set_position -// instead to move the sub-surface. -func NewSubsurface(ctx *Context) *Subsurface { - wlSubsurface := &Subsurface{} - ctx.Register(wlSubsurface) - return wlSubsurface -} - -// Destroy : remove sub-surface interface -// -// The sub-surface interface is removed from the wl_surface object -// that was turned into a sub-surface with a -// wl_subcompositor.get_subsurface request. The wl_surface's association -// to the parent is deleted. The wl_surface is unmapped immediately. -func (i *Subsurface) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetPosition : reposition the sub-surface -// -// This schedules a sub-surface position change. -// The sub-surface will be moved so that its origin (top left -// corner pixel) will be at the location x, y of the parent surface -// coordinate system. The coordinates are not restricted to the parent -// surface area. Negative values are allowed. -// -// The scheduled coordinates will take effect whenever the state of the -// parent surface is applied. -// -// If more than one set_position request is invoked by the client before -// the commit of the parent surface, the position of a new request always -// replaces the scheduled position from any previous request. -// -// The initial position is 0, 0. -// -// x: x coordinate in the parent surface -// y: y coordinate in the parent surface -func (i *Subsurface) SetPosition(x, y int32) error { - const opcode = 1 - const _reqBufLen = 8 + 4 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(x)) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(y)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// PlaceAbove : restack the sub-surface -// -// This sub-surface is taken from the stack, and put back just -// above the reference surface, changing the z-order of the sub-surfaces. -// The reference surface must be one of the sibling surfaces, or the -// parent surface. Using any other surface, including this sub-surface, -// will cause a protocol error. -// -// The z-order is double-buffered. Requests are handled in order and -// applied immediately to a pending state. The final pending state is -// copied to the active state the next time the state of the parent -// surface is applied. -// -// A new sub-surface is initially added as the top-most in the stack -// of its siblings and parent. -// -// sibling: the reference surface -func (i *Subsurface) PlaceAbove(sibling *Surface) error { - const opcode = 2 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], sibling.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// PlaceBelow : restack the sub-surface -// -// The sub-surface is placed just below the reference surface. -// See wl_subsurface.place_above. -// -// sibling: the reference surface -func (i *Subsurface) PlaceBelow(sibling *Surface) error { - const opcode = 3 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], sibling.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetSync : set sub-surface to synchronized mode -// -// Change the commit behaviour of the sub-surface to synchronized -// mode, also described as the parent dependent mode. -// -// In synchronized mode, wl_surface.commit on a sub-surface will -// accumulate the committed state in a cache, but the state will -// not be applied and hence will not change the compositor output. -// The cached state is applied to the sub-surface immediately after -// the parent surface's state is applied. This ensures atomic -// updates of the parent and all its synchronized sub-surfaces. -// Applying the cached state will invalidate the cache, so further -// parent surface commits do not (re-)apply old state. -// -// See wl_subsurface for the recursive effect of this mode. -func (i *Subsurface) SetSync() error { - const opcode = 4 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// SetDesync : set sub-surface to desynchronized mode -// -// Change the commit behaviour of the sub-surface to desynchronized -// mode, also described as independent or freely running mode. -// -// In desynchronized mode, wl_surface.commit on a sub-surface will -// apply the pending state directly, without caching, as happens -// normally with a wl_surface. Calling wl_surface.commit on the -// parent surface has no effect on the sub-surface's wl_surface -// state. This mode allows a sub-surface to be updated on its own. -// -// If cached state exists when wl_surface.commit is called in -// desynchronized mode, the pending state is added to the cached -// state, and applied as a whole. This invalidates the cache. -// -// Note: even if a sub-surface is set to desynchronized, a parent -// sub-surface may override it to behave as synchronized. For details, -// see wl_subsurface. -// -// If a surface's parent surface behaves as desynchronized, then -// the cached state is applied on set_desync. -func (i *Subsurface) SetDesync() error { - const opcode = 5 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -type SubsurfaceError uint32 - -// SubsurfaceError : -const ( - // SubsurfaceErrorBadSurface : wl_surface is not a sibling or the parent - SubsurfaceErrorBadSurface SubsurfaceError = 0 -) - -func (e SubsurfaceError) Name() string { - switch e { - case SubsurfaceErrorBadSurface: - return "bad_surface" - default: - return "" - } -} - -func (e SubsurfaceError) Value() string { - switch e { - case SubsurfaceErrorBadSurface: - return "0" - default: - return "" - } -} - -func (e SubsurfaceError) String() string { - return e.Name() + "=" + e.Value() -} - -// FixesInterfaceName is the name of the interface as it appears in the [client.Registry]. -// It can be used to match the [client.RegistryGlobalEvent.Interface] in the -// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. -const FixesInterfaceName = "wl_fixes" - -// Fixes : wayland protocol fixes -// -// This global fixes problems with other core-protocol interfaces that -// cannot be fixed in these interfaces themselves. -type Fixes struct { - BaseProxy -} - -// NewFixes : wayland protocol fixes -// -// This global fixes problems with other core-protocol interfaces that -// cannot be fixed in these interfaces themselves. -func NewFixes(ctx *Context) *Fixes { - wlFixes := &Fixes{} - ctx.Register(wlFixes) - return wlFixes -} - -// Destroy : destroys this object -func (i *Fixes) Destroy() error { - defer i.MarkZombie() - const opcode = 0 - const _reqBufLen = 8 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} - -// DestroyRegistry : destroy a wl_registry -// -// This request destroys a wl_registry object. -// -// The client should no longer use the wl_registry after making this -// request. -// -// The compositor will emit a wl_display.delete_id event with the object ID -// of the registry and will no longer emit any events on the registry. The -// client should re-use the object ID once it receives the -// wl_display.delete_id event. -// -// registry: the registry to destroy -func (i *Fixes) DestroyRegistry(registry *Registry) error { - const opcode = 1 - const _reqBufLen = 8 + 4 - var _reqBuf [_reqBufLen]byte - l := 0 - PutUint32(_reqBuf[l:4], i.ID()) - l += 4 - PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) - l += 4 - PutUint32(_reqBuf[l:l+4], registry.ID()) - l += 4 - err := i.Context().WriteMsg(_reqBuf[:], nil) - return err -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go deleted file mode 100644 index b97d1b8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/common.go +++ /dev/null @@ -1,55 +0,0 @@ -package client - -import "sync/atomic" - -type Dispatcher interface { - Dispatch(opcode uint32, fd int, data []byte) -} - -type Proxy interface { - Context() *Context - SetContext(ctx *Context) - ID() uint32 - SetID(id uint32) - IsZombie() bool - MarkZombie() -} - -type WaylandDisplay interface { - Context() *Context - GetRegistry() (*Registry, error) - Roundtrip() error - Destroy() error -} - -var _ WaylandDisplay = (*Display)(nil) - -type BaseProxy struct { - ctx *Context - id uint32 - zombie atomic.Bool -} - -func (p *BaseProxy) ID() uint32 { - return p.id -} - -func (p *BaseProxy) SetID(id uint32) { - p.id = id -} - -func (p *BaseProxy) Context() *Context { - return p.ctx -} - -func (p *BaseProxy) SetContext(ctx *Context) { - p.ctx = ctx -} - -func (p *BaseProxy) IsZombie() bool { - return p.zombie.Load() -} - -func (p *BaseProxy) MarkZombie() { - p.zombie.Store(true) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go deleted file mode 100644 index 4eee2cb..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context.go +++ /dev/null @@ -1,143 +0,0 @@ -package client - -import ( - "errors" - "fmt" - "net" - "os" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -type Context struct { - conn *net.UnixConn - objects syncmap.Map[uint32, Proxy] // map[uint32]Proxy - thread-safe concurrent map - currentID uint32 - idMu sync.Mutex // protects currentID increment -} - -func (ctx *Context) Register(p Proxy) { - ctx.idMu.Lock() - ctx.currentID++ - id := ctx.currentID - ctx.idMu.Unlock() - - p.SetID(id) - p.SetContext(ctx) - ctx.objects.Store(id, p) -} - -func (ctx *Context) RegisterWithID(p Proxy, id uint32) { - p.SetID(id) - p.SetContext(ctx) - ctx.objects.Store(id, p) -} - -func (ctx *Context) Unregister(p Proxy) { - ctx.objects.Delete(p.ID()) -} - -func (ctx *Context) DeleteID(id uint32) { - ctx.objects.Delete(id) -} - -func (ctx *Context) GetProxy(id uint32) Proxy { - if val, ok := ctx.objects.Load(id); ok { - return val - } - return nil -} - -func (ctx *Context) Close() error { - return ctx.conn.Close() -} - -func (ctx *Context) SetReadDeadline(t time.Time) error { - return ctx.conn.SetReadDeadline(t) -} - -func (ctx *Context) Fd() int { - rawConn, err := ctx.conn.SyscallConn() - if err != nil { - return -1 - } - var fd int - rawConn.Control(func(f uintptr) { - fd = int(f) - }) - return fd -} - -// Dispatch reads and processes incoming messages and calls [client.Dispatcher.Dispatch] on the -// respective wayland protocol. -// Dispatch must be called on the same goroutine as other interactions with the Context. -// If a multi goroutine approach is desired, use [Context.GetDispatch] instead. -// Dispatch blocks if there are no incoming messages. -// A Dispatch loop is usually used to handle incoming messages. -func (ctx *Context) Dispatch() error { - return ctx.GetDispatch()() -} - -var ErrDispatchSenderNotFound = errors.New("dispatch: unable to find sender") -var ErrDispatchSenderUnsupported = errors.New("dispatch: sender does not implement Dispatch method") -var ErrDispatchUnableToReadMsg = errors.New("dispatch: unable to read msg") - -// GetDispatch reads incoming messages and returns the dispatch function which calls -// [client.Dispatcher.Dispatch] on the respective wayland protocol. -// This function is now thread-safe and can be called from multiple goroutines. -// GetDispatch blocks if there are no incoming messages. -func (ctx *Context) GetDispatch() func() error { - senderID, opcode, fd, data, err := ctx.ReadMsg() // Blocks if there are no incoming messages - if err != nil { - return func() error { - return fmt.Errorf("%w: %w", ErrDispatchUnableToReadMsg, err) - } - } - - return func() error { - proxy, ok := ctx.objects.Load(senderID) - if !ok { - return nil // Proxy already deleted via delete_id, silently ignore - } - - if proxy.IsZombie() { - return nil // Zombie proxy, discard late events - } - - sender, ok := proxy.(Dispatcher) - if !ok { - return fmt.Errorf("%w (senderID=%d)", ErrDispatchSenderUnsupported, senderID) - } - - sender.Dispatch(opcode, fd, data) - return nil - } -} - -func Connect(addr string) (*Display, error) { - if addr == "" { - runtimeDir := os.Getenv("XDG_RUNTIME_DIR") - if runtimeDir == "" { - return nil, errors.New("env XDG_RUNTIME_DIR not set") - } - if addr == "" { - addr = os.Getenv("WAYLAND_DISPLAY") - } - if addr == "" { - addr = "wayland-0" - } - addr = runtimeDir + "/" + addr - } - - ctx := &Context{} - - conn, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: addr, Net: "unix"}) - if err != nil { - return nil, err - } - ctx.conn = conn - - return NewDisplay(ctx), nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go deleted file mode 100644 index 380d7da..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/context_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package client_test - -import ( - "errors" - "fmt" - "log" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -// Shows a dispatch loop that will block the goroutine. -// This approach has no risk of data races but the loop blocks the goroutine when no messages are -// received. This can be a valid approach if there are no more changes that need to be made after -// setting up and starting the loop. -// For a multi goroutine approach, use [client.Context.GetDispatch]. -func ExampleContext_Dispatch() { - display, err := client.Connect("") - if err != nil { - log.Fatalf("Error connecting to Wayland server: %v", err) - } - - registry, err := display.GetRegistry() - if err != nil { - log.Fatalf("Error getting Wayland registry: %v", err) - } - - var seat *client.Seat - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - switch e.Interface { - case client.SeatInterfaceName: - seat = client.NewSeat(display.Context()) - err := registry.Bind(e.Name, e.Interface, e.Version, seat) - if err != nil { - log.Fatalf("unable to bind %s interface: %v", client.SeatInterfaceName, err) - } - } - }) - display.Roundtrip() - display.Roundtrip() - - keyboard, err := seat.GetKeyboard() - if err != nil { - log.Printf("Error getting keyboard: %v", err) - } - log.Printf("Got keyboard: %v\n", keyboard) - - for { - err := display.Context().Dispatch() - if err != nil { - log.Printf("Dispatch error: %v\n", err) - } - } -} - -// Shows how the dispatch loop can be done in another goroutine. -// This prevents the goroutine from being blocked and allows making changes to wayland objects while -// the dispatch loop is blocking another goroutine. -func ExampleContext_GetDispatch() { - display, err := client.Connect("") - if err != nil { - log.Fatalf("Error connecting to Wayland server: %v", err) - } - - registry, err := display.GetRegistry() - if err != nil { - log.Fatalf("Error getting Wayland registry: %v", err) - } - - var seat *client.Seat - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - switch e.Interface { - case client.SeatInterfaceName: - seat = client.NewSeat(display.Context()) - err := registry.Bind(e.Name, e.Interface, e.Version, seat) - if err != nil { - log.Fatalf("unable to bind %s interface: %v", client.SeatInterfaceName, err) - } - } - }) - display.Roundtrip() - display.Roundtrip() - dispatchQueue := make(chan func() error) - - go func() { - for { - dispatchQueue <- display.Context().GetDispatch() - } - }() - - keyboard, err := seat.GetKeyboard() - if err != nil { - log.Printf("Error getting keyboard: %v", err) - } - log.Printf("Got keyboard: %v\n", keyboard) - - err = errors.Join(keyboard.Release(), seat.Release(), display.Context().Close()) - if err != nil { - fmt.Printf("Error cleaning up: %v\n", err) - } - - for { - select { - // Add other cases here to do other things - case dispatchFunc := <-dispatchQueue: - err := dispatchFunc() - if err != nil { - log.Printf("Dispatch error: %v\n", err) - } - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go deleted file mode 100644 index fa2af6f..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/display.go +++ /dev/null @@ -1,37 +0,0 @@ -package client - -import ( - "fmt" - "log" -) - -// Roundtrip blocks until all pending request are processed by the server. -// It is the implementation of [wl_display_roundtrip]. -// -// [wl_display_roundtrip]: https://wayland.freedesktop.org/docs/html/apb.html#Client-classwl__display_1ab60f38c2f80980ac84f347e932793390 -func (i *Display) Roundtrip() error { - callback, err := i.Sync() - if err != nil { - return fmt.Errorf("unable to get sync callback: %w", err) - } - defer func() { - if err2 := callback.Destroy(); err2 != nil { - log.Printf("unable to destroy callback: %v\n", err2) - } - }() - - done := false - callback.SetDoneHandler(func(_ CallbackDoneEvent) { - done = true - }) - - // Wait for callback to return - for !done { - err := i.Context().GetDispatch()() - if err != nil { - return fmt.Errorf("roundtrip: failed to dispatch: %w", err) - } - } - - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go deleted file mode 100644 index 91137f9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package client is Go port of wayland-client library -// for writing pure Go GUI software for wayland supported -// platforms. -package client - -//go:generate go run github.com/yaslama/go-wayland/cmd/go-wayland-scanner -pkg client -prefix wl -o client.go -i https://gitlab.freedesktop.org/wayland/wayland/-/raw/1.23.0/protocol/wayland.xml?ref_type=tags diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go deleted file mode 100644 index e7b5cb8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/event.go +++ /dev/null @@ -1,120 +0,0 @@ -package client - -import ( - "bytes" - "fmt" - "unsafe" - - "golang.org/x/sys/unix" - - _ "unsafe" -) - -var oobSpace = unix.CmsgSpace(4) - -func (ctx *Context) ReadMsg() (senderID uint32, opcode uint32, fd int, msg []byte, err error) { - fd = -1 - - oob := make([]byte, oobSpace) - header := make([]byte, 8) - - n, oobn, _, _, err := ctx.conn.ReadMsgUnix(header, oob) - if err != nil { - return senderID, opcode, fd, msg, err - } - if n != 8 { - return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: incorrect number of bytes read for header (n=%d)", n) - } - - if oobn > 0 { - fds, err := getFdsFromOob(oob, oobn, "header") - if err != nil { - return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err) - } - - if len(fds) > 0 { - fd = fds[0] - } - } - - senderID = Uint32(header[:4]) - opcodeAndSize := Uint32(header[4:8]) - opcode = opcodeAndSize & 0xffff - size := opcodeAndSize >> 16 - - msgSize := int(size) - 8 - if msgSize == 0 { - return senderID, opcode, fd, nil, nil - } - - msg = make([]byte, msgSize) - - if fd == -1 { - // if something was read before, then zero it out - if oobn > 0 { - oob = make([]byte, oobSpace) - } - - n, oobn, _, _, err = ctx.conn.ReadMsgUnix(msg, oob) - } else { - n, err = ctx.conn.Read(msg) - } - if err != nil { - return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err) - } - if n != msgSize { - return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: incorrect number of bytes read for msg (n=%d, msgSize=%d)", n, msgSize) - } - - if fd == -1 && oobn > 0 { - fds, err := getFdsFromOob(oob, oobn, "msg") - if err != nil { - return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err) - } - - if len(fds) > 0 { - fd = fds[0] - } - } - - return senderID, opcode, fd, msg, nil -} - -func getFdsFromOob(oob []byte, oobn int, source string) ([]int, error) { - if oobn > len(oob) { - return nil, fmt.Errorf("getFdsFromOob: incorrect number of bytes read from %s for oob (oobn=%d)", source, oobn) - } - scms, err := unix.ParseSocketControlMessage(oob) - if err != nil { - return nil, fmt.Errorf("getFdsFromOob: unable to parse control message from %s: %w", source, err) - } - - var fdsRet []int - for _, scm := range scms { - fds, err := unix.ParseUnixRights(&scm) - if err != nil { - return nil, fmt.Errorf("getFdsFromOob: unable to parse unix rights from %s: %w", source, err) - } - - fdsRet = append(fdsRet, fds...) - } - - return fdsRet, nil -} - -func Uint32(src []byte) uint32 { - _ = src[3] - return *(*uint32)(unsafe.Pointer(&src[0])) -} - -func String(src []byte) string { - idx := bytes.IndexByte(src, 0) - src = src[:idx:idx] - return *(*string)(unsafe.Pointer(&src)) -} - -func Fixed(src []byte) float64 { - _ = src[3] - fx := *(*int32)(unsafe.Pointer(&src[0])) - return fixedToFloat64(fx) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go deleted file mode 100644 index 3ca2317..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/request.go +++ /dev/null @@ -1,44 +0,0 @@ -package client - -import ( - "fmt" - "unsafe" -) - -func (ctx *Context) WriteMsg(b []byte, oob []byte) error { - n, oobn, err := ctx.conn.WriteMsgUnix(b, oob, nil) - if err != nil { - return err - } - if n != len(b) || oobn != len(oob) { - return fmt.Errorf("ctx.WriteMsg: incorrect number of bytes written (n=%d oobn=%d)", n, oobn) - } - - return nil -} - -func PutUint32(dst []byte, v uint32) { - _ = dst[3] - *(*uint32)(unsafe.Pointer(&dst[0])) = v -} - -func PutFixed(dst []byte, f float64) { - fx := fixedFromfloat64(f) - _ = dst[3] - *(*int32)(unsafe.Pointer(&dst[0])) = fx -} - -// PutString places a string in Wayland's wire format on the destination buffer. -// It first places the length of the string (plus one for the null terminator) and then the string -// followed by a null byte. -// The length of dst must be equal to, or greater than, len(v) + 5. -func PutString(dst []byte, v string) { - PutUint32(dst[:4], uint32(len(v)+1)) - copy(dst[4:], v) - dst[4+len(v)] = '\x00' // To cause panic if dst is not large enough -} - -func PutArray(dst []byte, a []byte) { - PutUint32(dst[:4], uint32(len(a))) - copy(dst[4:], a) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go deleted file mode 100644 index ba3d43a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/client/util.go +++ /dev/null @@ -1,24 +0,0 @@ -package client - -import "math" - -// From wayland/wayland-util.h - -func fixedToFloat64(f int32) float64 { - u_i := (1023+44)<<52 + (1 << 51) + int64(f) - u_d := math.Float64frombits(uint64(u_i)) - return u_d - (3 << 43) -} - -func fixedFromfloat64(d float64) int32 { - u_d := d + (3 << (51 - 8)) - u_i := int64(math.Float64bits(u_d)) - return int32(u_i) -} - -func PaddedLen(l int) int { - if (l & 0x3) != 0 { - return l + (4 - (l & 0x3)) - } - return l -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go deleted file mode 100644 index 19a203b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/go-wayland/wayland/stable/xdg-shell/xdg_shell.go +++ /dev/null @@ -1,13 +0,0 @@ -package xdg_shell - -import "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" - -type Popup struct { - client.BaseProxy -} - -func NewPopup(ctx *client.Context) *Popup { - p := &Popup{} - ctx.Register(p) - return p -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD deleted file mode 100644 index b7d28cc..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/CREDITS.MD +++ /dev/null @@ -1,5 +0,0 @@ -# Credits - -* Original socket adapter code is mostly taken from [korylprince/printer-manager-cups](https://github.com/korylprince/printer-manager-cups) -([MIT](https://github.com/korylprince/printer-manager-cups/blob/v1.0.9/LICENSE) licensed): -[conn.go](https://github.com/korylprince/printer-manager-cups/blob/v1.0.9/cups/conn.go) diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go deleted file mode 100644 index 56ababb..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter-http.go +++ /dev/null @@ -1,132 +0,0 @@ -package ipp - -import ( - "bytes" - "crypto/tls" - "fmt" - "io" - "net" - "net/http" - "strconv" - "time" -) - -type HttpAdapter struct { - host string - port int - username string - password string - useTLS bool - client *http.Client -} - -func NewHttpAdapter(host string, port int, username, password string, useTLS bool) *HttpAdapter { - httpClient := http.Client{ - Timeout: 0, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - ResponseHeaderTimeout: 90 * time.Second, - IdleConnTimeout: 120 * time.Second, - }, - } - - return &HttpAdapter{ - host: host, - port: port, - username: username, - password: password, - useTLS: useTLS, - client: &httpClient, - } -} - -func (h *HttpAdapter) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) { - payload, err := req.Encode() - if err != nil { - return nil, err - } - - size := len(payload) - var body io.Reader - if req.File != nil && req.FileSize != -1 { - size += req.FileSize - body = io.MultiReader(bytes.NewBuffer(payload), req.File) - } else { - body = bytes.NewBuffer(payload) - } - - httpReq, err := http.NewRequest("POST", url, body) - if err != nil { - return nil, err - } - - httpReq.Header.Set("Content-Length", strconv.Itoa(size)) - httpReq.Header.Set("Content-Type", ContentTypeIPP) - - if h.username != "" && h.password != "" { - httpReq.SetBasicAuth(h.username, h.password) - } - - httpResp, err := h.client.Do(httpReq) - if err != nil { - return nil, err - } - defer httpResp.Body.Close() - - if httpResp.StatusCode != 200 { - return nil, HTTPError{ - Code: httpResp.StatusCode, - } - } - - // buffer response to avoid read issues - buf := new(bytes.Buffer) - if httpResp.ContentLength > 0 { - buf.Grow(int(httpResp.ContentLength)) - } - if _, err := io.Copy(buf, httpResp.Body); err != nil { - return nil, fmt.Errorf("unable to buffer response: %w", err) - } - - ippResp, err := NewResponseDecoder(buf).Decode(additionalResponseData) - if err != nil { - return nil, err - } - - if err = ippResp.CheckForErrors(); err != nil { - return nil, fmt.Errorf("received error IPP response: %w", err) - } - - return ippResp, nil -} - -func (h *HttpAdapter) GetHttpUri(namespace string, object any) string { - proto := "http" - if h.useTLS { - proto = "https" - } - - uri := fmt.Sprintf("%s://%s:%d", proto, h.host, h.port) - - if namespace != "" { - uri = fmt.Sprintf("%s/%s", uri, namespace) - } - - if object != nil { - uri = fmt.Sprintf("%s/%v", uri, object) - } - - return uri -} - -func (h *HttpAdapter) TestConnection() error { - conn, err := net.Dial("tcp", net.JoinHostPort(h.host, fmt.Sprintf("%d", h.port))) - if err != nil { - return err - } - conn.Close() - - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go deleted file mode 100644 index 8af0520..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/adapter.go +++ /dev/null @@ -1,9 +0,0 @@ -package ipp - -import "io" - -type Adapter interface { - SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) - GetHttpUri(namespace string, object any) string - TestConnection() error -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go deleted file mode 100644 index 295f521..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/attribute.go +++ /dev/null @@ -1,528 +0,0 @@ -package ipp - -import ( - "encoding/binary" - "fmt" - "io" -) - -const ( - sizeInteger = int16(4) - sizeBoolean = int16(1) -) - -// AttributeEncoder encodes attribute to a io.Writer -type AttributeEncoder struct { - writer io.Writer -} - -// NewAttributeEncoder returns a new encoder that writes to w -func NewAttributeEncoder(w io.Writer) *AttributeEncoder { - return &AttributeEncoder{w} -} - -// Encode encodes a attribute and its value to a io.Writer -// the tag is determined by the AttributeTagMapping map -func (e *AttributeEncoder) Encode(attribute string, value any) error { - tag, ok := AttributeTagMapping[attribute] - if !ok { - return fmt.Errorf("cannot get tag of attribute %s", attribute) - } - - switch v := value.(type) { - case int: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(int32(v)); err != nil { - return err - } - case int16: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(int32(v)); err != nil { - return err - } - case int8: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(int32(v)); err != nil { - return err - } - case int32: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(v); err != nil { - return err - } - case int64: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeInteger(int32(v)); err != nil { - return err - } - case []int: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(int32(val)); err != nil { - return err - } - } - case []int16: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(int32(val)); err != nil { - return err - } - } - case []int8: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(int32(val)); err != nil { - return err - } - } - case []int32: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(val); err != nil { - return err - } - } - case []int64: - if tag != TagInteger && tag != TagEnum { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeInteger(int32(val)); err != nil { - return err - } - } - case bool: - if tag != TagBoolean { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeBoolean(v); err != nil { - return err - } - case []bool: - if tag != TagBoolean { - return fmt.Errorf("tag for attribute %s does not match with value type", attribute) - } - - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeBoolean(val); err != nil { - return err - } - } - case string: - if err := e.encodeTag(tag); err != nil { - return err - } - - if err := e.encodeString(attribute); err != nil { - return err - } - - if err := e.encodeString(v); err != nil { - return err - } - case []string: - for index, val := range v { - if err := e.encodeTag(tag); err != nil { - return err - } - - if index == 0 { - if err := e.encodeString(attribute); err != nil { - return err - } - } else { - if err := e.writeNullByte(); err != nil { - return err - } - } - - if err := e.encodeString(val); err != nil { - return err - } - } - default: - return fmt.Errorf("type %T is not supported", value) - } - - return nil -} - -func (e *AttributeEncoder) encodeString(s string) error { - if err := binary.Write(e.writer, binary.BigEndian, int16(len(s))); err != nil { - return err - } - - _, err := e.writer.Write([]byte(s)) - return err -} - -func (e *AttributeEncoder) encodeInteger(i int32) error { - if err := binary.Write(e.writer, binary.BigEndian, sizeInteger); err != nil { - return err - } - - return binary.Write(e.writer, binary.BigEndian, i) -} - -func (e *AttributeEncoder) encodeBoolean(b bool) error { - if err := binary.Write(e.writer, binary.BigEndian, sizeBoolean); err != nil { - return err - } - - return binary.Write(e.writer, binary.BigEndian, b) -} - -func (e *AttributeEncoder) encodeTag(t int8) error { - return binary.Write(e.writer, binary.BigEndian, t) -} - -func (e *AttributeEncoder) writeNullByte() error { - return binary.Write(e.writer, binary.BigEndian, int16(0)) -} - -// Attribute defines an ipp attribute -type Attribute struct { - Tag int8 - Name string - Value any -} - -// Resolution defines the resolution attribute -type Resolution struct { - Height int32 - Width int32 - Depth int8 -} - -// AttributeDecoder reads and decodes ipp from an input stream -type AttributeDecoder struct { - reader io.Reader -} - -// NewAttributeDecoder returns a new decoder that reads from r -func NewAttributeDecoder(r io.Reader) *AttributeDecoder { - return &AttributeDecoder{r} -} - -// Decode reads the next ipp attribute into a attribute struct. the type is identified by a tag passed as an argument -func (d *AttributeDecoder) Decode(tag int8) (*Attribute, error) { - attr := Attribute{Tag: tag} - - name, err := d.decodeString() - if err != nil { - return nil, err - } - attr.Name = name - - switch attr.Tag { - case TagEnum, TagInteger: - val, err := d.decodeInteger() - if err != nil { - return nil, err - } - attr.Value = val - case TagBoolean: - val, err := d.decodeBool() - if err != nil { - return nil, err - } - attr.Value = val - case TagDate: - val, err := d.decodeDate() - if err != nil { - return nil, err - } - attr.Value = val - case TagRange: - val, err := d.decodeRange() - if err != nil { - return nil, err - } - attr.Value = val - case TagResolution: - val, err := d.decodeResolution() - if err != nil { - return nil, err - } - attr.Value = val - default: - val, err := d.decodeString() - if err != nil { - return nil, err - } - attr.Value = val - } - - return &attr, nil -} - -func (d *AttributeDecoder) decodeBool() (b bool, err error) { - if _, err = d.readValueLength(); err != nil { - return - } - - if err = binary.Read(d.reader, binary.BigEndian, &b); err != nil { - return - } - - return -} - -func (d *AttributeDecoder) decodeInteger() (i int, err error) { - if _, err = d.readValueLength(); err != nil { - return - } - - var reti int32 - if err = binary.Read(d.reader, binary.BigEndian, &reti); err != nil { - return - } - - return int(reti), nil -} - -func (d *AttributeDecoder) decodeString() (string, error) { - length, err := d.readValueLength() - if err != nil { - return "", err - } - - if length == 0 { - return "", nil - } - - bs := make([]byte, length) - if _, err := d.reader.Read(bs); err != nil { - return "", nil - } - - return string(bs), nil -} - -func (d *AttributeDecoder) decodeDate() ([]int, error) { - length, err := d.readValueLength() - if err != nil { - return nil, err - } - - is := make([]int, length) - var ti int8 - - for i := int16(0); i < length; i++ { - if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil { - return nil, err - } - is[i] = int(ti) - } - - return is, nil -} - -func (d *AttributeDecoder) decodeRange() ([]int32, error) { - length, err := d.readValueLength() - if err != nil { - return nil, err - } - - // initialize range element count (c) and range slice (r) - c := length / 4 - r := make([]int32, c) - - for i := int16(0); i < c; i++ { - var ti int32 - if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil { - return nil, err - } - r[i] = ti - } - - return r, nil -} - -func (d *AttributeDecoder) decodeResolution() (res Resolution, err error) { - _, err = d.readValueLength() - if err != nil { - return - } - - if err = binary.Read(d.reader, binary.BigEndian, &res.Height); err != nil { - return - } - - if err = binary.Read(d.reader, binary.BigEndian, &res.Width); err != nil { - return - } - - if err = binary.Read(d.reader, binary.BigEndian, &res.Depth); err != nil { - return - } - - return -} - -func (d *AttributeDecoder) readValueLength() (length int16, err error) { - err = binary.Read(d.reader, binary.BigEndian, &length) - return -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go deleted file mode 100644 index 83bf41b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/constants.go +++ /dev/null @@ -1,450 +0,0 @@ -package ipp - -// ipp status codes -const ( - StatusCupsInvalid int16 = -1 - StatusOk int16 = 0x0000 - StatusOkIgnoredOrSubstituted int16 = 0x0001 - StatusOkConflicting int16 = 0x0002 - StatusOkIgnoredSubscriptions int16 = 0x0003 - StatusOkIgnoredNotifications int16 = 0x0004 - StatusOkTooManyEvents int16 = 0x0005 - StatusOkButCancelSubscription int16 = 0x0006 - StatusOkEventsComplete int16 = 0x0007 - StatusRedirectionOtherSite int16 = 0x0200 - StatusCupsSeeOther int16 = 0x0280 - StatusErrorBadRequest int16 = 0x0400 - StatusErrorForbidden int16 = 0x0401 - StatusErrorNotAuthenticated int16 = 0x0402 - StatusErrorNotAuthorized int16 = 0x0403 - StatusErrorNotPossible int16 = 0x0404 - StatusErrorTimeout int16 = 0x0405 - StatusErrorNotFound int16 = 0x0406 - StatusErrorGone int16 = 0x0407 - StatusErrorRequestEntity int16 = 0x0408 - StatusErrorRequestValue int16 = 0x0409 - StatusErrorDocumentFormatNotSupported int16 = 0x040a - StatusErrorAttributesOrValues int16 = 0x040b - StatusErrorUriScheme int16 = 0x040c - StatusErrorCharset int16 = 0x040d - StatusErrorConflicting int16 = 0x040e - StatusErrorCompressionError int16 = 0x040f - StatusErrorDocumentFormatError int16 = 0x0410 - StatusErrorDocumentAccess int16 = 0x0411 - StatusErrorAttributesNotSettable int16 = 0x0412 - StatusErrorIgnoredAllSubscriptions int16 = 0x0413 - StatusErrorTooManySubscriptions int16 = 0x0414 - StatusErrorIgnoredAllNotifications int16 = 0x0415 - StatusErrorPrintSupportFileNotFound int16 = 0x0416 - StatusErrorDocumentPassword int16 = 0x0417 - StatusErrorDocumentPermission int16 = 0x0418 - StatusErrorDocumentSecurity int16 = 0x0419 - StatusErrorDocumentUnprintable int16 = 0x041a - StatusErrorAccountInfoNeeded int16 = 0x041b - StatusErrorAccountClosed int16 = 0x041c - StatusErrorAccountLimitReached int16 = 0x041d - StatusErrorAccountAuthorizationFailed int16 = 0x041e - StatusErrorNotFetchable int16 = 0x041f - StatusErrorCupsAccountInfoNeeded int16 = 0x049C - StatusErrorCupsAccountClosed int16 = 0x049d - StatusErrorCupsAccountLimitReached int16 = 0x049e - StatusErrorCupsAccountAuthorizationFailed int16 = 0x049f - StatusErrorInternal int16 = 0x0500 - StatusErrorOperationNotSupported int16 = 0x0501 - StatusErrorServiceUnavailable int16 = 0x0502 - StatusErrorVersionNotSupported int16 = 0x0503 - StatusErrorDevice int16 = 0x0504 - StatusErrorTemporary int16 = 0x0505 - StatusErrorNotAcceptingJobs int16 = 0x0506 - StatusErrorBusy int16 = 0x0507 - StatusErrorJobCanceled int16 = 0x0508 - StatusErrorMultipleJobsNotSupported int16 = 0x0509 - StatusErrorPrinterIsDeactivated int16 = 0x050a - StatusErrorTooManyJobs int16 = 0x050b - StatusErrorTooManyDocuments int16 = 0x050c - StatusErrorCupsAuthenticationCanceled int16 = 0x1000 - StatusErrorCupsPki int16 = 0x1001 - StatusErrorCupsUpgradeRequired int16 = 0x1002 -) - -// ipp operations -const ( - OperationCupsInvalid int16 = -0x0001 - OperationCupsNone int16 = 0x0000 - OperationPrintJob int16 = 0x0002 - OperationPrintUri int16 = 0x0003 - OperationValidateJob int16 = 0x0004 - OperationCreateJob int16 = 0x0005 - OperationSendDocument int16 = 0x0006 - OperationSendUri int16 = 0x0007 - OperationCancelJob int16 = 0x0008 - OperationGetJobAttributes int16 = 0x0009 - OperationGetJobs int16 = 0x000a - OperationGetPrinterAttributes int16 = 0x000b - OperationHoldJob int16 = 0x000c - OperationReleaseJob int16 = 0x000d - OperationRestartJob int16 = 0x000e - OperationPausePrinter int16 = 0x0010 - OperationResumePrinter int16 = 0x0011 - OperationPurgeJobs int16 = 0x0012 - OperationSetPrinterAttributes int16 = 0x0013 - OperationSetJobAttributes int16 = 0x0014 - OperationGetPrinterSupportedValues int16 = 0x0015 - OperationCreatePrinterSubscriptions int16 = 0x0016 - OperationCreateJobSubscriptions int16 = 0x0017 - OperationGetSubscriptionAttributes int16 = 0x0018 - OperationGetSubscriptions int16 = 0x0019 - OperationRenewSubscription int16 = 0x001a - OperationCancelSubscription int16 = 0x001b - OperationGetNotifications int16 = 0x001c - OperationSendNotifications int16 = 0x001d - OperationGetResourceAttributes int16 = 0x001e - OperationGetResourceData int16 = 0x001f - OperationGetResources int16 = 0x0020 - OperationGetPrintSupportFiles int16 = 0x0021 - OperationEnablePrinter int16 = 0x0022 - OperationDisablePrinter int16 = 0x0023 - OperationPausePrinterAfterCurrentJob int16 = 0x0024 - OperationHoldNewJobs int16 = 0x0025 - OperationReleaseHeldNewJobs int16 = 0x0026 - OperationDeactivatePrinter int16 = 0x0027 - OperationActivatePrinter int16 = 0x0028 - OperationRestartPrinter int16 = 0x0029 - OperationShutdownPrinter int16 = 0x002a - OperationStartupPrinter int16 = 0x002b - OperationReprocessJob int16 = 0x002c - OperationCancelCurrentJob int16 = 0x002d - OperationSuspendCurrentJob int16 = 0x002e - OperationResumeJob int16 = 0x002f - OperationOperationPromoteJob int16 = 0x0030 - OperationScheduleJobAfter int16 = 0x0031 - OperationCancelDocument int16 = 0x0033 - OperationGetDocumentAttributes int16 = 0x0034 - OperationGetDocuments int16 = 0x0035 - OperationDeleteDocument int16 = 0x0036 - OperationSetDocumentAttributes int16 = 0x0037 - OperationCancelJobs int16 = 0x0038 - OperationCancelMyJobs int16 = 0x0039 - OperationResubmitJob int16 = 0x003a - OperationCloseJob int16 = 0x003b - OperationIdentifyPrinter int16 = 0x003c - OperationValidateDocument int16 = 0x003d - OperationAddDocumentImages int16 = 0x003e - OperationAcknowledgeDocument int16 = 0x003f - OperationAcknowledgeIdentifyPrinter int16 = 0x0040 - OperationAcknowledgeJob int16 = 0x0041 - OperationFetchDocument int16 = 0x0042 - OperationFetchJob int16 = 0x0043 - OperationGetOutputDeviceAttributes int16 = 0x0044 - OperationUpdateActiveJobs int16 = 0x0045 - OperationDeregisterOutputDevice int16 = 0x0046 - OperationUpdateDocumentStatus int16 = 0x0047 - OperationUpdateJobStatus int16 = 0x0048 - OperationUpdateOutputDeviceAttributes int16 = 0x0049 - OperationGetNextDocumentData int16 = 0x004a - OperationAllocatePrinterResources int16 = 0x004b - OperationCreatePrinter int16 = 0x004c - OperationDeallocatePrinterResources int16 = 0x004d - OperationDeletePrinter int16 = 0x004e - OperationGetPrinters int16 = 0x004f - OperationShutdownOnePrinter int16 = 0x0050 - OperationStartupOnePrinter int16 = 0x0051 - OperationCancelResource int16 = 0x0052 - OperationCreateResource int16 = 0x0053 - OperationInstallResource int16 = 0x0054 - OperationSendResourceData int16 = 0x0055 - OperationSetResourceAttributes int16 = 0x0056 - OperationCreateResourceSubscriptions int16 = 0x0057 - OperationCreateSystemSubscriptions int16 = 0x0058 - OperationDisableAllPrinters int16 = 0x0059 - OperationEnableAllPrinters int16 = 0x005a - OperationGetSystemAttributes int16 = 0x005b - OperationGetSystemSupportedValues int16 = 0x005c - OperationPauseAllPrinters int16 = 0x005d - OperationPauseAllPrintersAfterCurrentJob int16 = 0x005e - OperationRegisterOutputDevice int16 = 0x005f - OperationRestartSystem int16 = 0x0060 - OperationResumeAllPrinters int16 = 0x0061 - OperationSetSystemAttributes int16 = 0x0062 - OperationShutdownAllPrinter int16 = 0x0063 - OperationStartupAllPrinters int16 = 0x0064 - OperationPrivate int16 = 0x4000 - OperationCupsGetDefault int16 = 0x4001 - OperationCupsGetPrinters int16 = 0x4002 - OperationCupsAddModifyPrinter int16 = 0x4003 - OperationCupsDeletePrinter int16 = 0x4004 - OperationCupsGetClasses int16 = 0x4005 - OperationCupsAddModifyClass int16 = 0x4006 - OperationCupsDeleteClass int16 = 0x4007 - OperationCupsAcceptJobs int16 = 0x4008 - OperationCupsRejectJobs int16 = 0x4009 - OperationCupsSetDefault int16 = 0x400a - OperationCupsGetDevices int16 = 0x400b - OperationCupsGetPPDs int16 = 0x400c - OperationCupsMoveJob int16 = 0x400d - OperationCupsAuthenticateJob int16 = 0x400e - OperationCupsGetPpd int16 = 0x400f - OperationCupsGetDocument int16 = 0x4027 - OperationCupsCreateLocalPrinter int16 = 0x4028 -) - -// ipp tags -const ( - TagCupsInvalid int8 = -1 - TagZero int8 = 0x00 - TagOperation int8 = 0x01 - TagJob int8 = 0x02 - TagEnd int8 = 0x03 - TagPrinter int8 = 0x04 - TagUnsupportedGroup int8 = 0x05 - TagSubscription int8 = 0x06 - TagEventNotification int8 = 0x07 - TagResource int8 = 0x08 - TagDocument int8 = 0x09 - TagSystem int8 = 0x0a - TagUnsupportedValue int8 = 0x10 - TagDefault int8 = 0x11 - TagUnknown int8 = 0x12 - TagNoValue int8 = 0x13 - TagNotSettable int8 = 0x15 - TagDeleteAttr int8 = 0x16 - TagAdminDefine int8 = 0x17 - TagInteger int8 = 0x21 - TagBoolean int8 = 0x22 - TagEnum int8 = 0x23 - TagString int8 = 0x30 - TagDate int8 = 0x31 - TagResolution int8 = 0x32 - TagRange int8 = 0x33 - TagBeginCollection int8 = 0x34 - TagTextLang int8 = 0x35 - TagNameLang int8 = 0x36 - TagEndCollection int8 = 0x37 - TagText int8 = 0x41 - TagName int8 = 0x42 - TagReservedString int8 = 0x43 - TagKeyword int8 = 0x44 - TagUri int8 = 0x45 - TagUriScheme int8 = 0x46 - TagCharset int8 = 0x47 - TagLanguage int8 = 0x48 - TagMimeType int8 = 0x49 - TagMemberName int8 = 0x4a - TagExtension int8 = 0x7f -) - -// job states -const ( - JobStatePending int8 = 0x03 - JobStateHeld int8 = 0x04 - JobStateProcessing int8 = 0x05 - JobStateStopped int8 = 0x06 - JobStateCanceled int8 = 0x07 - JobStateAborted int8 = 0x08 - JobStateCompleted int8 = 0x09 -) - -// document states -const ( - DocumentStatePending int8 = 0x03 - DocumentStateProcessing int8 = 0x05 - DocumentStateCanceled int8 = 0x07 - DocumentStateAborted int8 = 0x08 - DocumentStateCompleted int8 = 0x08 -) - -// printer states -const ( - PrinterStateIdle int8 = 0x0003 - PrinterStateProcessing int8 = 0x0004 - PrinterStateStopped int8 = 0x0005 -) - -// job state filter -const ( - JobStateFilterNotCompleted = "not-completed" - JobStateFilterCompleted = "completed" - JobStateFilterAll = "all" -) - -// error policies -const ( - ErrorPolicyRetryJob = "retry-job" - ErrorPolicyAbortJob = "abort-job" - ErrorPolicyRetryCurrentJob = "retry-current-job" - ErrorPolicyStopPrinter = "stop-printer" -) - -// ipp defaults -const ( - CharsetLanguage = "en-US" - Charset = "utf-8" - ProtocolVersionMajor = int8(2) - ProtocolVersionMinor = int8(0) - - DefaultJobPriority = 50 -) - -// useful mime types for ipp -const ( - MimeTypePostscript = "application/postscript" - MimeTypePDF = "application/pdf" - MimeTypeOctetStream = "application/octet-stream" -) - -// ipp content types -const ( - ContentTypeIPP = "application/ipp" -) - -// known ipp attributes -const ( - AttributeCopies = "copies" - AttributeDocumentFormat = "document-format" - AttributeDocumentName = "document-name" - AttributeJobID = "job-id" - AttributeJobName = "job-name" - AttributeJobPriority = "job-priority" - AttributeJobURI = "job-uri" - AttributeLastDocument = "last-document" - AttributeMyJobs = "my-jobs" - AttributePPDName = "ppd-name" - AttributePPDMakeAndModel = "ppd-make-and-model" - AttributePrinterIsShared = "printer-is-shared" - AttributePrinterIsTemporary = "printer-is-temporary" - AttributePrinterURI = "printer-uri" - AttributePurgeJobs = "purge-jobs" - AttributeRequestedAttributes = "requested-attributes" - AttributeRequestingUserName = "requesting-user-name" - AttributeWhichJobs = "which-jobs" - AttributeFirstJobID = "first-job-id" - AttributeLimit = "limit" - AttributeStatusMessage = "status-message" - AttributeCharset = "attributes-charset" - AttributeNaturalLanguage = "attributes-natural-language" - AttributeDeviceURI = "device-uri" - AttributeHoldJobUntil = "job-hold-until" - AttributePrinterErrorPolicy = "printer-error-policy" - AttributePrinterInfo = "printer-info" - AttributePrinterLocation = "printer-location" - AttributePrinterName = "printer-name" - AttributePrinterStateReasons = "printer-state-reasons" - AttributeJobPrinterURI = "job-printer-uri" - AttributeMemberURIs = "member-uris" - AttributeDocumentNumber = "document-number" - AttributeDocumentState = "document-state" - AttributeFinishings = "finishings" - AttributeJobHoldUntil = "hold-job-until" - AttributeJobSheets = "job-sheets" - AttributeJobState = "job-state" - AttributeJobStateReason = "job-state-reason" - AttributeMedia = "media" - AttributeSides = "sides" - AttributeNumberUp = "number-up" - AttributeOrientationRequested = "orientation-requested" - AttributePrintQuality = "print-quality" - AttributePrinterIsAcceptingJobs = "printer-is-accepting-jobs" - AttributePrinterResolution = "printer-resolution" - AttributePrinterState = "printer-state" - AttributeMemberNames = "member-names" - AttributePrinterType = "printer-type" - AttributePrinterMakeAndModel = "printer-make-and-model" - AttributePrinterStateMessage = "printer-state-message" - AttributePrinterUriSupported = "printer-uri-supported" - AttributeJobMediaProgress = "job-media-progress" - AttributeJobKilobyteOctets = "job-k-octets" - AttributeNumberOfDocuments = "number-of-documents" - AttributeJobOriginatingUserName = "job-originating-user-name" - AttributeOutputOrder = "outputorder" - AttributeJobStateReasons = "job-state-reasons" - AttributeJobStateMessage = "job-state-message" - AttributeJobPrinterStateReasons = "job-printer-state-reasons" - AttributeJobPrinterStateMessage = "job-printer-state-message" - AttributeJobImpressionsCompleted = "job-impressions-completed" - AttributePrintScaling = "print-scaling" -) - -// Default attributes -var ( - DefaultClassAttributes = []string{AttributePrinterName, AttributeMemberNames} - DefaultPrinterAttributes = []string{ - AttributePrinterName, AttributePrinterType, AttributePrinterLocation, AttributePrinterInfo, - AttributePrinterMakeAndModel, AttributePrinterState, AttributePrinterStateMessage, AttributePrinterStateReasons, - AttributePrinterUriSupported, AttributeDeviceURI, AttributePrinterIsShared, - } - DefaultJobAttributes = []string{ - AttributeJobID, AttributeJobName, AttributePrinterURI, AttributeJobState, AttributeJobStateReason, - AttributeJobHoldUntil, AttributeJobMediaProgress, AttributeJobKilobyteOctets, AttributeNumberOfDocuments, AttributeCopies, - AttributeJobOriginatingUserName, - } -) - -// Attribute to tag mapping -var ( - AttributeTagMapping = map[string]int8{ - AttributeCharset: TagCharset, - AttributeNaturalLanguage: TagLanguage, - AttributeCopies: TagInteger, - AttributeDeviceURI: TagUri, - AttributeDocumentFormat: TagMimeType, - AttributeDocumentName: TagName, - AttributeDocumentNumber: TagInteger, - AttributeDocumentState: TagEnum, - AttributeFinishings: TagEnum, - AttributeJobHoldUntil: TagKeyword, - AttributeHoldJobUntil: TagKeyword, - AttributeJobID: TagInteger, - AttributeJobName: TagName, - AttributeJobPrinterURI: TagUri, - AttributeJobPriority: TagInteger, - AttributeJobSheets: TagName, - AttributeJobState: TagEnum, - AttributeJobStateReason: TagKeyword, - AttributeJobURI: TagUri, - AttributeLastDocument: TagBoolean, - AttributeMedia: TagKeyword, - AttributeSides: TagKeyword, - AttributeMemberURIs: TagUri, - AttributeMyJobs: TagBoolean, - AttributeNumberUp: TagInteger, - AttributeOrientationRequested: TagEnum, - AttributePPDName: TagName, - AttributePPDMakeAndModel: TagText, - AttributeNumberOfDocuments: TagInteger, - AttributePrintQuality: TagEnum, - AttributePrinterErrorPolicy: TagName, - AttributePrinterInfo: TagText, - AttributePrinterIsAcceptingJobs: TagBoolean, - AttributePrinterIsShared: TagBoolean, - AttributePrinterIsTemporary: TagBoolean, - AttributePrinterName: TagName, - AttributePrinterLocation: TagText, - AttributePrinterResolution: TagResolution, - AttributePrinterState: TagEnum, - AttributePrinterStateReasons: TagKeyword, - AttributePrinterURI: TagUri, - AttributePurgeJobs: TagBoolean, - AttributeRequestedAttributes: TagKeyword, - AttributeRequestingUserName: TagName, - AttributeWhichJobs: TagKeyword, - AttributeFirstJobID: TagInteger, - AttributeStatusMessage: TagText, - AttributeLimit: TagInteger, - AttributeOutputOrder: TagName, - AttributeJobStateReasons: TagString, - AttributeJobStateMessage: TagString, - AttributeJobPrinterStateReasons: TagString, - AttributeJobPrinterStateMessage: TagString, - AttributeJobImpressionsCompleted: TagInteger, - AttributePrintScaling: TagKeyword, - // IPP Subscription/Notification attributes (added for dankdots) - "notify-events": TagKeyword, - "notify-pull-method": TagKeyword, - "notify-lease-duration": TagInteger, - "notify-subscription-id": TagInteger, - "notify-subscription-ids": TagInteger, - "notify-sequence-numbers": TagInteger, - "notify-wait": TagBoolean, - "notify-recipient-uri": TagUri, - } -) diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go deleted file mode 100644 index 68d60ce..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/cups-client.go +++ /dev/null @@ -1,313 +0,0 @@ -package ipp - -import ( - "io" - "strings" -) - -// CUPSClient implements a ipp client with specific cups operations -type CUPSClient struct { - *IPPClient -} - -// NewCUPSClient creates a new cups ipp client (used HttpAdapter internally) -func NewCUPSClient(host string, port int, username, password string, useTLS bool) *CUPSClient { - ippClient := NewIPPClient(host, port, username, password, useTLS) - return &CUPSClient{ippClient} -} - -// NewCUPSClientWithAdapter creates a new cups ipp client with given Adapter -func NewCUPSClientWithAdapter(username string, adapter Adapter) *CUPSClient { - ippClient := NewIPPClientWithAdapter(username, adapter) - return &CUPSClient{ippClient} -} - -// GetDevices returns a map of device uris and printer attributes -func (c *CUPSClient) GetDevices() (map[string]Attributes, error) { - req := NewRequest(OperationCupsGetDevices, 1) - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - printerNameMap := make(map[string]Attributes) - - for _, printerAttributes := range resp.PrinterAttributes { - printerNameMap[printerAttributes[AttributeDeviceURI][0].Value.(string)] = printerAttributes - } - - return printerNameMap, nil -} - -// MoveJob moves a job to a other printer -func (c *CUPSClient) MoveJob(jobID int, destPrinter string) error { - req := NewRequest(OperationCupsMoveJob, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter) - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// MoveAllJob moves all job from a printer to a other printer -func (c *CUPSClient) MoveAllJob(srcPrinter, destPrinter string) error { - req := NewRequest(OperationCupsMoveJob, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(srcPrinter) - req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter) - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// GetPPDs returns a map of ppd names and attributes -func (c *CUPSClient) GetPPDs() (map[string]Attributes, error) { - req := NewRequest(OperationCupsGetPPDs, 1) - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - ppdNameMap := make(map[string]Attributes) - - for _, printerAttributes := range resp.PrinterAttributes { - ppdNameMap[printerAttributes[AttributePPDName][0].Value.(string)] = printerAttributes - } - - return ppdNameMap, nil -} - -// AcceptJobs lets a printer accept jobs again -func (c *CUPSClient) AcceptJobs(printer string) error { - req := NewRequest(OperationCupsAcceptJobs, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// RejectJobs does not let a printer accept jobs -func (c *CUPSClient) RejectJobs(printer string) error { - req := NewRequest(OperationCupsRejectJobs, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// AddPrinterToClass adds a printer to a class, if the class does not exists it will be created -func (c *CUPSClient) AddPrinterToClass(class, printer string) error { - attributes, err := c.GetPrinterAttributes(class, []string{AttributeMemberURIs}) - if err != nil && !IsNotExistsError(err) { - return err - } - - memberURIList := make([]string, 0) - - if !IsNotExistsError(err) { - for _, member := range attributes[AttributeMemberURIs] { - memberString := strings.Split(member.Value.(string), "/") - printerName := memberString[len(memberString)-1] - - if printerName == printer { - return nil - } - - memberURIList = append(memberURIList, member.Value.(string)) - } - } - - memberURIList = append(memberURIList, c.getPrinterUri(printer)) - - req := NewRequest(OperationCupsAddModifyClass, 1) - req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class) - req.PrinterAttributes[AttributeMemberURIs] = memberURIList - - _, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// DeletePrinterFromClass removes a printer from a class, if a class has no more printer it will be deleted -func (c *CUPSClient) DeletePrinterFromClass(class, printer string) error { - attributes, err := c.GetPrinterAttributes(class, []string{AttributeMemberURIs}) - if err != nil { - return err - } - - memberURIList := make([]string, 0) - - for _, member := range attributes[AttributeMemberURIs] { - memberString := strings.Split(member.Value.(string), "/") - printerName := memberString[len(memberString)-1] - - if printerName != printer { - memberURIList = append(memberURIList, member.Value.(string)) - } - } - - if len(memberURIList) == 0 { - return c.DeleteClass(class) - } - - req := NewRequest(OperationCupsAddModifyClass, 1) - req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class) - req.PrinterAttributes[AttributeMemberURIs] = memberURIList - - _, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// DeleteClass deletes a class -func (c *CUPSClient) DeleteClass(class string) error { - req := NewRequest(OperationCupsDeleteClass, 1) - req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// CreatePrinter creates a new printer -func (c *CUPSClient) CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy string, information, location string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(name) - req.OperationAttributes[AttributePPDName] = ppd - req.OperationAttributes[AttributePrinterIsShared] = shared - req.PrinterAttributes[AttributePrinterStateReasons] = "none" - req.PrinterAttributes[AttributeDeviceURI] = deviceURI - req.PrinterAttributes[AttributePrinterInfo] = information - req.PrinterAttributes[AttributePrinterLocation] = location - req.PrinterAttributes[AttributePrinterErrorPolicy] = errorPolicy - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterPPD sets the ppd for a printer -func (c *CUPSClient) SetPrinterPPD(printer, ppd string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.OperationAttributes[AttributePPDName] = ppd - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterDeviceURI sets the device uri for a printer -func (c *CUPSClient) SetPrinterDeviceURI(printer, deviceURI string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.PrinterAttributes[AttributeDeviceURI] = deviceURI - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterIsShared shares or unshares a printer in the network -func (c *CUPSClient) SetPrinterIsShared(printer string, shared bool) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.OperationAttributes[AttributePrinterIsShared] = shared - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterErrorPolicy sets the error policy for a printer -func (c *CUPSClient) SetPrinterErrorPolicy(printer string, errorPolicy string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.PrinterAttributes[AttributePrinterErrorPolicy] = errorPolicy - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterInformation sets general printer information -func (c *CUPSClient) SetPrinterInformation(printer, information string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.PrinterAttributes[AttributePrinterInfo] = information - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// SetPrinterLocation sets the printer location -func (c *CUPSClient) SetPrinterLocation(printer, location string) error { - req := NewRequest(OperationCupsAddModifyPrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.PrinterAttributes[AttributePrinterLocation] = location - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// DeletePrinter deletes a printer -func (c *CUPSClient) DeletePrinter(printer string) error { - req := NewRequest(OperationCupsDeletePrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// GetPrinters returns a map of printer names and attributes -func (c *CUPSClient) GetPrinters(attributes []string) (map[string]Attributes, error) { - req := NewRequest(OperationCupsGetPrinters, 1) - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultPrinterAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName) - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - printerNameMap := make(map[string]Attributes) - - for _, printerAttributes := range resp.PrinterAttributes { - printerNameMap[printerAttributes[AttributePrinterName][0].Value.(string)] = printerAttributes - } - - return printerNameMap, nil -} - -// GetClasses returns a map of class names and attributes -func (c *CUPSClient) GetClasses(attributes []string) (map[string]Attributes, error) { - req := NewRequest(OperationCupsGetClasses, 1) - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultClassAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName) - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - printerNameMap := make(map[string]Attributes) - - for _, printerAttributes := range resp.PrinterAttributes { - printerNameMap[printerAttributes[AttributePrinterName][0].Value.(string)] = printerAttributes - } - - return printerNameMap, nil -} - -// PrintTestPage prints a test page using the provided PDF data -func (c *CUPSClient) PrintTestPage(printer string, testPageData io.Reader, size int) (int, error) { - return c.PrintJob(Document{ - Document: testPageData, - Name: "Test Page", - Size: size, - MimeType: MimeTypePDF, - }, printer, map[string]any{ - AttributeJobName: "Test Page", - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go deleted file mode 100644 index e4edf5e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/error.go +++ /dev/null @@ -1,31 +0,0 @@ -package ipp - -import "fmt" - -// IsNotExistsError checks a given error whether a printer or class does not exist -func IsNotExistsError(err error) bool { - if err == nil { - return false - } - - return err.Error() == "The printer or class does not exist." -} - -// IPPError used for non ok ipp status codes -type IPPError struct { - Status int16 - Message string -} - -func (e IPPError) Error() string { - return fmt.Sprintf("ipp status: %d, message: %s", e.Status, e.Message) -} - -// HTTPError used for non 200 http codes -type HTTPError struct { - Code int -} - -func (e HTTPError) Error() string { - return fmt.Sprintf("got http code %d", e.Code) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go deleted file mode 100644 index a21dd54..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/ipp-client.go +++ /dev/null @@ -1,329 +0,0 @@ -package ipp - -import ( - "errors" - "fmt" - "io" - "os" - "path" -) - -// Document wraps an io.Reader with more information, needed for encoding -type Document struct { - Document io.Reader - Size int - Name string - MimeType string -} - -// IPPClient implements a generic ipp client -type IPPClient struct { - username string - adapter Adapter -} - -// NewIPPClient creates a new generic ipp client (used HttpAdapter internally) -func NewIPPClient(host string, port int, username, password string, useTLS bool) *IPPClient { - adapter := NewHttpAdapter(host, port, username, password, useTLS) - - return &IPPClient{ - username: username, - adapter: adapter, - } -} - -// NewIPPClientWithAdapter creates a new generic ipp client with given Adapter -func NewIPPClientWithAdapter(username string, adapter Adapter) *IPPClient { - return &IPPClient{ - username: username, - adapter: adapter, - } -} - -func (c *IPPClient) getPrinterUri(printer string) string { - return fmt.Sprintf("ipp://localhost/printers/%s", printer) -} - -func (c *IPPClient) getJobUri(jobID int) string { - return fmt.Sprintf("ipp://localhost/jobs/%d", jobID) -} - -func (c *IPPClient) getClassUri(printer string) string { - return fmt.Sprintf("ipp://localhost/classes/%s", printer) -} - -// SendRequest sends a request to a remote uri end returns the response -func (c *IPPClient) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) { - if _, ok := req.OperationAttributes[AttributeRequestingUserName]; !ok { - req.OperationAttributes[AttributeRequestingUserName] = c.username - } - - return c.adapter.SendRequest(url, req, additionalResponseData) -} - -// PrintDocuments prints one or more documents using a Create-Job operation followed by one or more Send-Document operation(s). custom job settings can be specified via the jobAttributes parameter -func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttributes map[string]any) (int, error) { - printerURI := c.getPrinterUri(printer) - - req := NewRequest(OperationCreateJob, 1) - req.OperationAttributes[AttributePrinterURI] = printerURI - req.OperationAttributes[AttributeRequestingUserName] = c.username - - // set defaults for some attributes, may get overwritten - req.OperationAttributes[AttributeJobName] = docs[0].Name - req.OperationAttributes[AttributeCopies] = 1 - req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority - - for key, value := range jobAttributes { - req.JobAttributes[key] = value - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil) - if err != nil { - return -1, err - } - - if len(resp.JobAttributes) == 0 { - return 0, errors.New("server doesn't returned a job id") - } - - jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int) - - documentCount := len(docs) - 1 - - for docID, doc := range docs { - req = NewRequest(OperationSendDocument, 2) - req.OperationAttributes[AttributePrinterURI] = printerURI - req.OperationAttributes[AttributeRequestingUserName] = c.username - req.OperationAttributes[AttributeJobID] = jobID - req.OperationAttributes[AttributeDocumentName] = doc.Name - req.OperationAttributes[AttributeDocumentFormat] = doc.MimeType - req.OperationAttributes[AttributeLastDocument] = docID == documentCount - req.File = doc.Document - req.FileSize = doc.Size - - _, err = c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil) - if err != nil { - return -1, err - } - } - - return jobID, nil -} - -// PrintJob prints a document using a Print-Job operation. custom job settings can be specified via the jobAttributes parameter -func (c *IPPClient) PrintJob(doc Document, printer string, jobAttributes map[string]any) (int, error) { - printerURI := c.getPrinterUri(printer) - - req := NewRequest(OperationPrintJob, 1) - req.OperationAttributes[AttributePrinterURI] = printerURI - req.OperationAttributes[AttributeRequestingUserName] = c.username - req.OperationAttributes[AttributeJobName] = doc.Name - req.OperationAttributes[AttributeDocumentFormat] = doc.MimeType - - // set defaults for some attributes, may get overwritten - req.OperationAttributes[AttributeCopies] = 1 - req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority - - for key, value := range jobAttributes { - req.JobAttributes[key] = value - } - - req.File = doc.Document - req.FileSize = doc.Size - - resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil) - if err != nil { - return -1, err - } - - if len(resp.JobAttributes) == 0 { - return 0, errors.New("server doesn't returned a job id") - } - - jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int) - - return jobID, nil -} - -// PrintFile prints a local file on the file system. custom job settings can be specified via the jobAttributes parameter -func (c *IPPClient) PrintFile(filePath, printer string, jobAttributes map[string]any) (int, error) { - fileStats, err := os.Stat(filePath) - if os.IsNotExist(err) { - return -1, err - } - - fileName := path.Base(filePath) - - document, err := os.Open(filePath) - if err != nil { - return 0, err - } - defer document.Close() - - jobAttributes[AttributeJobName] = fileName - - return c.PrintDocuments([]Document{ - { - Document: document, - Name: fileName, - Size: int(fileStats.Size()), - MimeType: MimeTypeOctetStream, - }, - }, printer, jobAttributes) -} - -// GetPrinterAttributes returns the requested attributes for the specified printer, if attributes is nil the default attributes will be used -func (c *IPPClient) GetPrinterAttributes(printer string, attributes []string) (Attributes, error) { - req := NewRequest(OperationGetPrinterAttributes, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.OperationAttributes[AttributeRequestingUserName] = c.username - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultPrinterAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = attributes - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil) - if err != nil { - return nil, err - } - - if len(resp.PrinterAttributes) == 0 { - return nil, errors.New("server doesn't return any printer attributes") - } - - return resp.PrinterAttributes[0], nil -} - -// ResumePrinter resumes a printer -func (c *IPPClient) ResumePrinter(printer string) error { - req := NewRequest(OperationResumePrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// PausePrinter pauses a printer -func (c *IPPClient) PausePrinter(printer string) error { - req := NewRequest(OperationPausePrinter, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// GetJobAttributes returns the requested attributes for the specified job, if attributes is nil the default job will be used -func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (Attributes, error) { - req := NewRequest(OperationGetJobAttributes, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = attributes - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("jobs", jobID), req, nil) - if err != nil { - return nil, err - } - - if len(resp.JobAttributes) == 0 { - return nil, errors.New("server doesn't return any job attributes") - } - - return resp.JobAttributes[0], nil -} - -// GetJobs returns jobs from a printer or class -func (c *IPPClient) GetJobs(printer, class string, whichJobs string, myJobs bool, firstJobId, limit int, attributes []string) (map[int]Attributes, error) { - req := NewRequest(OperationGetJobs, 1) - req.OperationAttributes[AttributeWhichJobs] = whichJobs - req.OperationAttributes[AttributeMyJobs] = myJobs - - if printer != "" { - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - } else if class != "" { - req.OperationAttributes[AttributePrinterURI] = c.getClassUri(printer) - } else { - req.OperationAttributes[AttributePrinterURI] = "ipp://localhost/" - } - - if firstJobId > 0 { - req.OperationAttributes[AttributeFirstJobID] = firstJobId - } - - if limit > 0 { - req.OperationAttributes[AttributeLimit] = limit - } - - if myJobs { - req.OperationAttributes[AttributeRequestingUserName] = c.username - } - - if attributes == nil { - req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes - } else { - req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributeJobID) - } - - resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil) - if err != nil { - return nil, err - } - - jobIDMap := make(map[int]Attributes) - - for _, jobAttributes := range resp.JobAttributes { - jobIDMap[jobAttributes[AttributeJobID][0].Value.(int)] = jobAttributes - } - - return jobIDMap, nil -} - -// CancelJob cancels a job. if purge is true, the job will also be removed -func (c *IPPClient) CancelJob(jobID int, purge bool) error { - req := NewRequest(OperationCancelJob, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - req.OperationAttributes[AttributePurgeJobs] = purge - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// CancelAllJob cancels all jobs for a specified printer. if purge is true, the jobs will also be removed -func (c *IPPClient) CancelAllJob(printer string, purge bool) error { - req := NewRequest(OperationCancelJobs, 1) - req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) - req.OperationAttributes[AttributePurgeJobs] = purge - - _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) - return err -} - -// RestartJob restarts a job -func (c *IPPClient) RestartJob(jobID int) error { - req := NewRequest(OperationRestartJob, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// HoldJobUntil holds a job -func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error { - req := NewRequest(OperationRestartJob, 1) - req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) - req.JobAttributes[AttributeHoldJobUntil] = holdUntil - - _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) - return err -} - -// TestConnection tests if a tcp connection to the remote server is possible -func (c *IPPClient) TestConnection() error { - return c.adapter.TestConnection() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go deleted file mode 100644 index 3207038..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/request.go +++ /dev/null @@ -1,299 +0,0 @@ -package ipp - -import ( - "bytes" - "encoding/binary" - "io" -) - -// Request defines a ipp request -type Request struct { - ProtocolVersionMajor int8 - ProtocolVersionMinor int8 - - Operation int16 - RequestId int32 - - OperationAttributes map[string]any - JobAttributes map[string]any - PrinterAttributes map[string]any - SubscriptionAttributes map[string]any // Added for subscription operations - - File io.Reader - FileSize int -} - -// NewRequest creates a new ipp request -func NewRequest(op int16, reqID int32) *Request { - return &Request{ - ProtocolVersionMajor: ProtocolVersionMajor, - ProtocolVersionMinor: ProtocolVersionMinor, - Operation: op, - RequestId: reqID, - OperationAttributes: make(map[string]any), - JobAttributes: make(map[string]any), - PrinterAttributes: make(map[string]any), - SubscriptionAttributes: make(map[string]any), - File: nil, - FileSize: -1, - } -} - -// Encode encodes the request to a byte slice -func (r *Request) Encode() ([]byte, error) { - buf := new(bytes.Buffer) - enc := NewAttributeEncoder(buf) - - if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMajor); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMinor); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.Operation); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.RequestId); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, TagOperation); err != nil { - return nil, err - } - - if r.OperationAttributes == nil { - r.OperationAttributes = make(map[string]any, 2) - } - - if _, found := r.OperationAttributes[AttributeCharset]; !found { - r.OperationAttributes[AttributeCharset] = Charset - } - - if _, found := r.OperationAttributes[AttributeNaturalLanguage]; !found { - r.OperationAttributes[AttributeNaturalLanguage] = CharsetLanguage - } - - if err := r.encodeOperationAttributes(enc); err != nil { - return nil, err - } - - if len(r.JobAttributes) > 0 { - if err := binary.Write(buf, binary.BigEndian, TagJob); err != nil { - return nil, err - } - for attr, value := range r.JobAttributes { - if err := enc.Encode(attr, value); err != nil { - return nil, err - } - } - } - - if len(r.PrinterAttributes) > 0 { - if err := binary.Write(buf, binary.BigEndian, TagPrinter); err != nil { - return nil, err - } - for attr, value := range r.PrinterAttributes { - if err := enc.Encode(attr, value); err != nil { - return nil, err - } - } - } - - if len(r.SubscriptionAttributes) > 0 { - if err := binary.Write(buf, binary.BigEndian, TagSubscription); err != nil { - return nil, err - } - if err := r.encodeSubscriptionAttributes(enc); err != nil { - return nil, err - } - } - - if err := binary.Write(buf, binary.BigEndian, TagEnd); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (r *Request) encodeOperationAttributes(enc *AttributeEncoder) error { - ordered := []string{ - AttributeCharset, - AttributeNaturalLanguage, - AttributePrinterURI, - AttributeJobID, - } - - for _, attr := range ordered { - if value, ok := r.OperationAttributes[attr]; ok { - delete(r.OperationAttributes, attr) - if err := enc.Encode(attr, value); err != nil { - return err - } - } - } - - for attr, value := range r.OperationAttributes { - if err := enc.Encode(attr, value); err != nil { - return err - } - } - - return nil -} - -func (r *Request) encodeSubscriptionAttributes(enc *AttributeEncoder) error { - // Encode subscription attributes in proper order - // notify-pull-method and notify-lease-duration must come before notify-events - ordered := []string{ - "notify-pull-method", - "notify-lease-duration", - "notify-events", - } - - for _, attr := range ordered { - if value, ok := r.SubscriptionAttributes[attr]; ok { - delete(r.SubscriptionAttributes, attr) - if err := enc.Encode(attr, value); err != nil { - return err - } - } - } - - // Encode any remaining subscription attributes - for attr, value := range r.SubscriptionAttributes { - if err := enc.Encode(attr, value); err != nil { - return err - } - } - - return nil -} - -// RequestDecoder reads and decodes a request from a stream -type RequestDecoder struct { - reader io.Reader -} - -// NewRequestDecoder returns a new decoder that reads from r -func NewRequestDecoder(r io.Reader) *RequestDecoder { - return &RequestDecoder{ - reader: r, - } -} - -// Decode decodes a ipp request into a request struct. additional data will be written to an io.Writer if data is not nil -func (d *RequestDecoder) Decode(data io.Writer) (*Request, error) { - req := new(Request) - - if err := binary.Read(d.reader, binary.BigEndian, &req.ProtocolVersionMajor); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &req.ProtocolVersionMinor); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &req.Operation); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &req.RequestId); err != nil { - return nil, err - } - - startByteSlice := make([]byte, 1) - - tag := TagCupsInvalid - previousAttributeName := "" - tagSet := false - - attribDecoder := NewAttributeDecoder(d.reader) - - // decode attribute buffer - for { - if _, err := d.reader.Read(startByteSlice); err != nil { - // when we read from a stream, we may get an EOF if we want to read the end tag - // all data should be read and we can ignore the error - if err == io.EOF { - break - } - return nil, err - } - - startByte := int8(startByteSlice[0]) - - // check if attributes are completed - if startByte == TagEnd { - break - } - - if startByte == TagOperation { - if req.OperationAttributes == nil { - req.OperationAttributes = make(map[string]any) - } - - tag = TagOperation - tagSet = true - - } - - if startByte == TagJob { - if req.JobAttributes == nil { - req.JobAttributes = make(map[string]any) - } - tag = TagJob - tagSet = true - } - - if startByte == TagPrinter { - if req.PrinterAttributes == nil { - req.PrinterAttributes = make(map[string]any) - } - tag = TagPrinter - tagSet = true - } - - if tagSet { - if _, err := d.reader.Read(startByteSlice); err != nil { - return nil, err - } - startByte = int8(startByteSlice[0]) - } - - attrib, err := attribDecoder.Decode(startByte) - if err != nil { - return nil, err - } - - if attrib.Name != "" { - appendAttributeToRequest(req, tag, attrib.Name, attrib.Value) - previousAttributeName = attrib.Name - } else { - appendAttributeToRequest(req, tag, previousAttributeName, attrib.Value) - } - - tagSet = false - } - - if data != nil { - if _, err := io.Copy(data, d.reader); err != nil { - return nil, err - } - } - - return req, nil -} - -func appendAttributeToRequest(req *Request, tag int8, name string, value any) { - switch tag { - case TagOperation: - req.OperationAttributes[name] = value - case TagPrinter: - req.PrinterAttributes[name] = value - case TagJob: - req.JobAttributes[name] = value - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go deleted file mode 100644 index e7cbd75..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/response.go +++ /dev/null @@ -1,383 +0,0 @@ -package ipp - -import ( - "bytes" - "encoding/binary" - "io" -) - -// Attributes is a wrapper for a set of attributes -type Attributes map[string][]Attribute - -// Response defines a ipp response -type Response struct { - ProtocolVersionMajor int8 - ProtocolVersionMinor int8 - - StatusCode int16 - RequestId int32 - - OperationAttributes Attributes - PrinterAttributes []Attributes - JobAttributes []Attributes - SubscriptionAttributes []Attributes // Added for subscription responses -} - -// CheckForErrors checks the status code and returns a error if it is not zero. it also returns the status message if provided by the server -func (r *Response) CheckForErrors() error { - if r.StatusCode != StatusOk { - err := IPPError{ - Status: r.StatusCode, - Message: "no status message returned", - } - - if len(r.OperationAttributes["status-message"]) > 0 { - err.Message = r.OperationAttributes["status-message"][0].Value.(string) - } - - return err - } - - return nil -} - -// NewResponse creates a new ipp response -func NewResponse(statusCode int16, reqID int32) *Response { - return &Response{ - ProtocolVersionMajor: ProtocolVersionMajor, - ProtocolVersionMinor: ProtocolVersionMinor, - StatusCode: statusCode, - RequestId: reqID, - OperationAttributes: make(Attributes), - PrinterAttributes: make([]Attributes, 0), - JobAttributes: make([]Attributes, 0), - } -} - -// Encode encodes the response to a byte slice -func (r *Response) Encode() ([]byte, error) { - buf := new(bytes.Buffer) - enc := NewAttributeEncoder(buf) - - if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMajor); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMinor); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.StatusCode); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, r.RequestId); err != nil { - return nil, err - } - - if err := binary.Write(buf, binary.BigEndian, TagOperation); err != nil { - return nil, err - } - - if r.OperationAttributes == nil { - r.OperationAttributes = make(Attributes, 0) - } - - if _, found := r.OperationAttributes[AttributeCharset]; !found { - r.OperationAttributes[AttributeCharset] = []Attribute{ - { - Value: Charset, - }, - } - } - - if _, found := r.OperationAttributes[AttributeNaturalLanguage]; !found { - r.OperationAttributes[AttributeNaturalLanguage] = []Attribute{ - { - Value: CharsetLanguage, - }, - } - } - - if err := r.encodeOperationAttributes(enc); err != nil { - return nil, err - } - - if len(r.PrinterAttributes) > 0 { - for _, printerAttr := range r.PrinterAttributes { - if err := binary.Write(buf, binary.BigEndian, TagPrinter); err != nil { - return nil, err - } - - for name, attr := range printerAttr { - if len(attr) == 0 { - continue - } - - values := make([]any, len(attr)) - for i, v := range attr { - values[i] = v.Value - } - - if len(values) == 1 { - if err := enc.Encode(name, values[0]); err != nil { - return nil, err - } - } else { - if err := enc.Encode(name, values); err != nil { - return nil, err - } - } - } - } - } - - if len(r.JobAttributes) > 0 { - for _, jobAttr := range r.JobAttributes { - if err := binary.Write(buf, binary.BigEndian, TagJob); err != nil { - return nil, err - } - - for name, attr := range jobAttr { - if len(attr) == 0 { - continue - } - - values := make([]any, len(attr)) - for i, v := range attr { - values[i] = v.Value - } - - if len(values) == 1 { - if err := enc.Encode(name, values[0]); err != nil { - return nil, err - } - } else { - if err := enc.Encode(name, values); err != nil { - return nil, err - } - } - } - } - } - - if err := binary.Write(buf, binary.BigEndian, TagEnd); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (r *Response) encodeOperationAttributes(enc *AttributeEncoder) error { - ordered := []string{ - AttributeCharset, - AttributeNaturalLanguage, - AttributePrinterURI, - AttributeJobID, - } - - for _, name := range ordered { - if attr, ok := r.OperationAttributes[name]; ok { - delete(r.OperationAttributes, name) - if err := encodeOperationAttribute(enc, name, attr); err != nil { - return err - } - } - } - - for name, attr := range r.OperationAttributes { - if err := encodeOperationAttribute(enc, name, attr); err != nil { - return err - } - } - - return nil -} - -func encodeOperationAttribute(enc *AttributeEncoder, name string, attr []Attribute) error { - if len(attr) == 0 { - return nil - } - - values := make([]any, len(attr)) - for i, v := range attr { - values[i] = v.Value - } - - if len(values) == 1 { - return enc.Encode(name, values[0]) - } - - return enc.Encode(name, values) -} - -// ResponseDecoder reads and decodes a response from a stream -type ResponseDecoder struct { - reader io.Reader -} - -// NewResponseDecoder returns a new decoder that reads from r -func NewResponseDecoder(r io.Reader) *ResponseDecoder { - return &ResponseDecoder{ - reader: r, - } -} - -// Decode decodes a ipp response into a response struct. additional data will be written to an io.Writer if data is not nil -func (d *ResponseDecoder) Decode(data io.Writer) (*Response, error) { - /* - 1 byte: Protocol Major Version - b - 1 byte: Protocol Minor Version - b - 2 byte: Status ID - h - 4 byte: Request ID - i - 1 byte: Operation Attribute Byte (\0x01) - N times: Attributes - 1 byte: Attribute End Byte (\0x03) - */ - - resp := new(Response) - - // wrap the reader so we have more functionality - // reader := bufio.NewReader(d.reader) - - if err := binary.Read(d.reader, binary.BigEndian, &resp.ProtocolVersionMajor); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &resp.ProtocolVersionMinor); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &resp.StatusCode); err != nil { - return nil, err - } - - if err := binary.Read(d.reader, binary.BigEndian, &resp.RequestId); err != nil { - return nil, err - } - - startByteSlice := make([]byte, 1) - - tag := TagCupsInvalid - previousAttributeName := "" - tempAttributes := make(Attributes) - tagSet := false - - attribDecoder := NewAttributeDecoder(d.reader) - - // decode attribute buffer - for { - if _, err := d.reader.Read(startByteSlice); err != nil { - // when we read from a stream, we may get an EOF if we want to read the end tag - // all data should be read and we can ignore the error - if err == io.EOF { - break - } - return nil, err - } - - startByte := int8(startByteSlice[0]) - - // check if attributes are completed - if startByte == TagEnd { - break - } - - if startByte == TagOperation { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagOperation - tagSet = true - } - - if startByte == TagJob { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagJob - tagSet = true - } - - if startByte == TagPrinter { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagPrinter - tagSet = true - } - - if startByte == TagSubscription { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagSubscription - tagSet = true - } - - if startByte == TagEventNotification { - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - tempAttributes = make(Attributes) - } - - tag = TagEventNotification - tagSet = true - } - - if tagSet { - if _, err := d.reader.Read(startByteSlice); err != nil { - return nil, err - } - startByte = int8(startByteSlice[0]) - } - - attrib, err := attribDecoder.Decode(startByte) - if err != nil { - return nil, err - } - - if attrib.Name != "" { - tempAttributes[attrib.Name] = append(tempAttributes[attrib.Name], *attrib) - previousAttributeName = attrib.Name - } else { - tempAttributes[previousAttributeName] = append(tempAttributes[previousAttributeName], *attrib) - } - - tagSet = false - } - - if len(tempAttributes) > 0 && tag != TagCupsInvalid { - appendAttributeToResponse(resp, tag, tempAttributes) - } - - if data != nil { - if _, err := io.Copy(data, d.reader); err != nil { - return nil, err - } - } - - return resp, nil -} - -func appendAttributeToResponse(resp *Response, tag int8, attr map[string][]Attribute) { - switch tag { - case TagOperation: - resp.OperationAttributes = attr - case TagPrinter: - resp.PrinterAttributes = append(resp.PrinterAttributes, attr) - case TagJob: - resp.JobAttributes = append(resp.JobAttributes, attr) - case TagSubscription, TagEventNotification: - // Both subscription and event notification attributes go to SubscriptionAttributes - resp.SubscriptionAttributes = append(resp.SubscriptionAttributes, attr) - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go deleted file mode 100644 index 62689a3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/ipp/utils.go +++ /dev/null @@ -1,28 +0,0 @@ -package ipp - -import ( - "fmt" - "os" - "path" -) - -// ParseControlFile reads and decodes a cups control file into a response -func ParseControlFile(jobID int, spoolDirectory string) (*Response, error) { - if spoolDirectory == "" { - spoolDirectory = "/var/spool/cups" - } - - controlFilePath := path.Join(spoolDirectory, fmt.Sprintf("c%d", jobID)) - - if _, err := os.Stat(controlFilePath); err != nil { - return nil, err - } - - controlFile, err := os.Open(controlFilePath) - if err != nil { - return nil, err - } - defer controlFile.Close() - - return NewResponseDecoder(controlFile).Decode(nil) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE deleted file mode 100644 index edba275..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2009 The Go Authors. -Copyright 2024 Zachary Olstein. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google LLC nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go deleted file mode 100644 index 27d9458..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/pkg/syncmap/syncmap.go +++ /dev/null @@ -1,537 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package syncmap - -import ( - "sync" - "sync/atomic" - "unsafe" -) - -// Map is like a Go map[K]V but is safe for concurrent use -// by multiple goroutines without additional locking or coordination. -// Loads, stores, and deletes run in amortized constant time. -// -// The Map type is specialized. Most code should use a plain Go map instead, -// with separate locking or coordination, for better type safety and to make it -// easier to maintain other invariants along with the map content. -// -// The Map type is optimized for two common use cases: (1) when the entry for a given -// key is only ever written once but read many times, as in caches that only grow, -// or (2) when multiple goroutines read, write, and overwrite entries for disjoint -// sets of keys. In these two cases, use of a Map may significantly reduce lock -// contention compared to a Go map paired with a separate [Mutex] or [RWMutex]. -// -// The zero Map is empty and ready for use. A Map must not be copied after first use. -// -// In the terminology of [the Go memory model], Map arranges that a write operation -// “synchronizes before” any read operation that observes the effect of the write, where -// read and write operations are defined as follows. -// [Map.Load], [Map.LoadAndDelete], [Map.LoadOrStore], and [Map.Swap] are read operations; -// [Map.Delete], [Map.LoadAndDelete], [Map.Store], and [Map.Swap] are write operations; -// [Map.LoadOrStore] is a write operation when it returns loaded set to false. -// -// [the Go memory model]: https://go.dev/ref/mem -type Map[K comparable, V any] struct { - mu sync.Mutex - - // read contains the portion of the map's contents that are safe for - // concurrent access (with or without mu held). - // - // The read field itself is always safe to load, but must only be stored with - // mu held. - // - // Entries stored in read may be updated concurrently without mu, but updating - // a previously-expunged entry requires that the entry be copied to the dirty - // map and unexpunged with mu held. - read atomic.Pointer[readOnly[K, V]] - - // dirty contains the portion of the map's contents that require mu to be - // held. To ensure that the dirty map can be promoted to the read map quickly, - // it also includes all of the non-expunged entries in the read map. - // - // Expunged entries are not stored in the dirty map. An expunged entry in the - // clean map must be unexpunged and added to the dirty map before a new value - // can be stored to it. - // - // If the dirty map is nil, the next write to the map will initialize it by - // making a shallow copy of the clean map, omitting stale entries. - dirty map[K]*entry[V] - - // misses counts the number of loads since the read map was last updated that - // needed to lock mu to determine whether the key was present. - // - // Once enough misses have occurred to cover the cost of copying the dirty - // map, the dirty map will be promoted to the read map (in the unamended - // state) and the next store to the map will make a new dirty copy. - misses int -} - -// readOnly is an immutable struct stored atomically in the Map.read field. -type readOnly[K comparable, V any] struct { - m map[K]*entry[V] - amended bool // true if the dirty map contains some key not in m. -} - -// expunged is an arbitrary pointer that marks entries which have been deleted -// from the dirty map. -// Because the same expunged pointer is used regardless of the Map's value type, -// value pointers read from the map must be compared against expunged BEFORE -// casting the pointer to *V. -var expunged = unsafe.Pointer(new(int)) - -// An entry is a slot in the map corresponding to a particular key. -type entry[V any] struct { - // p points to the value stored for the entry. - // - // If p == nil, the entry has been deleted, and either m.dirty == nil or - // m.dirty[key] is e. - // - // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry - // is missing from m.dirty. - // - // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty - // != nil, in m.dirty[key]. - // - // If p != expunged, it is always safe to cast it to (*V). - // - // An entry can be deleted by atomic replacement with nil: when m.dirty is - // next created, it will atomically replace nil with expunged and leave - // m.dirty[key] unset. - // - // An entry's associated value can be updated by atomic replacement, provided - // p != expunged. If p == expunged, an entry's associated value can be updated - // only after first setting m.dirty[key] = e so that lookups using the dirty - // map find the entry. - p unsafe.Pointer -} - -func newEntry[V any](i V) *entry[V] { - e := &entry[V]{} - atomic.StorePointer(&e.p, unsafe.Pointer(&i)) - return e -} - -func (m *Map[K, V]) loadReadOnly() readOnly[K, V] { - if p := m.read.Load(); p != nil { - return *p - } - return readOnly[K, V]{} -} - -// Load returns the value stored in the map for a key, or nil if no -// value is present. -// The ok result indicates whether value was found in the map. -func (m *Map[K, V]) Load(key K) (value V, ok bool) { - read := m.loadReadOnly() - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - // Avoid reporting a spurious miss if m.dirty got promoted while we were - // blocked on m.mu. (If further loads of the same key will not miss, it's - // not worth copying the dirty map for this key.) - read = m.loadReadOnly() - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - // Regardless of whether the entry was present, record a miss: this key - // will take the slow path until the dirty map is promoted to the read - // map. - m.missLocked() - } - m.mu.Unlock() - } - if !ok { - return value, false - } - return e.load() -} - -func (e *entry[V]) load() (value V, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expunged { - return value, false - } - return *(*V)(p), true -} - -// Store sets the value for a key. -func (m *Map[K, V]) Store(key K, value V) { - _, _ = m.Swap(key, value) -} - -// unexpungeLocked ensures that the entry is not marked as expunged. -// -// If the entry was previously expunged, it must be added to the dirty map -// before m.mu is unlocked. -func (e *entry[V]) unexpungeLocked() (wasExpunged bool) { - return atomic.CompareAndSwapPointer(&e.p, expunged, nil) -} - -// swapLocked unconditionally swaps a value into the entry. -// -// The entry must be known not to be expunged. -func (e *entry[V]) swapLocked(i *V) *V { - return (*V)(atomic.SwapPointer(&e.p, unsafe.Pointer(i))) -} - -// LoadOrStore returns the existing value for the key if present. -// Otherwise, it stores and returns the given value. -// The loaded result is true if the value was loaded, false if stored. -func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { - // Avoid locking if it's a clean hit. - read := m.loadReadOnly() - if e, ok := read.m[key]; ok { - actual, loaded, ok := e.tryLoadOrStore(value) - if ok { - return actual, loaded - } - } - - m.mu.Lock() - read = m.loadReadOnly() - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - m.dirty[key] = e - } - actual, loaded, _ = e.tryLoadOrStore(value) - } else if e, ok := m.dirty[key]; ok { - actual, loaded, _ = e.tryLoadOrStore(value) - m.missLocked() - } else { - if !read.amended { - // We're adding the first new key to the dirty map. - // Make sure it is allocated and mark the read-only map as incomplete. - m.dirtyLocked() - m.read.Store(&readOnly[K, V]{m: read.m, amended: true}) - } - m.dirty[key] = newEntry(value) - actual, loaded = value, false - } - m.mu.Unlock() - - return actual, loaded -} - -// tryLoadOrStore atomically loads or stores a value if the entry is not -// expunged. -// -// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and -// returns with ok==false. -func (e *entry[V]) tryLoadOrStore(i V) (actual V, loaded, ok bool) { - ptr := atomic.LoadPointer(&e.p) - if ptr == expunged { - return actual, false, false - } - p := (*V)(ptr) - if p != nil { - return *p, true, true - } - - // Copy the interface after the first load to make this method more amenable - // to escape analysis: if we hit the "load" path or the entry is expunged, we - // shouldn't bother heap-allocating. - ic := i - for { - if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { - return i, false, true - } - ptr = atomic.LoadPointer(&e.p) - if ptr == expunged { - return actual, false, false - } - p = (*V)(ptr) - if p != nil { - return *p, true, true - } - } -} - -// LoadAndDelete deletes the value for a key, returning the previous value if any. -// The loaded result reports whether the key was present. -func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) { - read := m.loadReadOnly() - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - read = m.loadReadOnly() - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - delete(m.dirty, key) - // Regardless of whether the entry was present, record a miss: this key - // will take the slow path until the dirty map is promoted to the read - // map. - m.missLocked() - } - m.mu.Unlock() - } - if ok { - return e.delete() - } - return value, false -} - -// Delete deletes the value for a key. -func (m *Map[K, V]) Delete(key K) { - m.LoadAndDelete(key) -} - -func (e *entry[V]) delete() (value V, ok bool) { - for { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expunged { - return value, false - } - if atomic.CompareAndSwapPointer(&e.p, p, nil) { - return *(*V)(p), true - } - } -} - -// trySwap swaps a value if the entry has not been expunged. -// -// If the entry is expunged, trySwap returns false and leaves the entry -// unchanged. -func (e *entry[V]) trySwap(i *V) (*V, bool) { - for { - p := atomic.LoadPointer(&e.p) - if p == expunged { - return nil, false - } - if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { - return (*V)(p), true - } - } -} - -// Swap swaps the value for a key and returns the previous value if any. -// The loaded result reports whether the key was present. -func (m *Map[K, V]) Swap(key K, value V) (previous V, loaded bool) { - read := m.loadReadOnly() - if e, ok := read.m[key]; ok { - if v, ok := e.trySwap(&value); ok { - if v == nil { - return previous, false - } - return *v, true - } - } - - m.mu.Lock() - read = m.loadReadOnly() - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - // The entry was previously expunged, which implies that there is a - // non-nil dirty map and this entry is not in it. - m.dirty[key] = e - } - if v := e.swapLocked(&value); v != nil { - loaded = true - previous = *v - } - } else if e, ok := m.dirty[key]; ok { - if v := e.swapLocked(&value); v != nil { - loaded = true - previous = *v - } - } else { - if !read.amended { - // We're adding the first new key to the dirty map. - // Make sure it is allocated and mark the read-only map as incomplete. - m.dirtyLocked() - m.read.Store(&readOnly[K, V]{m: read.m, amended: true}) - } - m.dirty[key] = newEntry(value) - } - m.mu.Unlock() - return previous, loaded -} - -// Range calls f sequentially for each key and value present in the map. -// If f returns false, range stops the iteration. -// -// Range does not necessarily correspond to any consistent snapshot of the Map's -// contents: no key will be visited more than once, but if the value for any key -// is stored or deleted concurrently (including by f), Range may reflect any -// mapping for that key from any point during the Range call. Range does not -// block other methods on the receiver; even f itself may call any method on m. -// -// Range may be O(N) with the number of elements in the map even if f returns -// false after a constant number of calls. -func (m *Map[K, V]) Range(f func(key K, value V) bool) { - // We need to be able to iterate over all of the keys that were already - // present at the start of the call to Range. - // If read.amended is false, then read.m satisfies that property without - // requiring us to hold m.mu for a long time. - read := m.loadReadOnly() - if read.amended { - // m.dirty contains keys not in read.m. Fortunately, Range is already O(N) - // (assuming the caller does not break out early), so a call to Range - // amortizes an entire copy of the map: we can promote the dirty copy - // immediately! - m.mu.Lock() - read = m.loadReadOnly() - if read.amended { - read = readOnly[K, V]{m: m.dirty} - copyRead := read - m.read.Store(©Read) - m.dirty = nil - m.misses = 0 - } - m.mu.Unlock() - } - - for k, e := range read.m { - v, ok := e.load() - if !ok { - continue - } - if !f(k, v) { - break - } - } -} - -// CompareAndSwap swaps the old and new values for key -// if the value stored in the map is equal to old. -// The old value must be of a comparable type. -func CompareAndSwap[K comparable, V comparable](m *Map[K, V], key K, old, new V) (swapped bool) { - read := m.loadReadOnly() - if e, ok := read.m[key]; ok { - return tryCompareAndSwap(e, old, new) - } else if !read.amended { - return false // No existing value for key. - } - - m.mu.Lock() - defer m.mu.Unlock() - read = m.loadReadOnly() - swapped = false - if e, ok := read.m[key]; ok { - swapped = tryCompareAndSwap(e, old, new) - } else if e, ok := m.dirty[key]; ok { - swapped = tryCompareAndSwap(e, old, new) - // We needed to lock mu in order to load the entry for key, - // and the operation didn't change the set of keys in the map - // (so it would be made more efficient by promoting the dirty - // map to read-only). - // Count it as a miss so that we will eventually switch to the - // more efficient steady state. - m.missLocked() - } - return swapped -} - -// CompareAndDelete deletes the entry for key if its value is equal to old. -// The old value must be of a comparable type. -// -// If there is no current value for key in the map, CompareAndDelete -// returns false (even if the old value is the zero value of V). -func CompareAndDelete[K comparable, V comparable](m *Map[K, V], key K, old V) (deleted bool) { - read := m.loadReadOnly() - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - read = m.loadReadOnly() - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - // Don't delete key from m.dirty: we still need to do the “compare” part - // of the operation. The entry will eventually be expunged when the - // dirty map is promoted to the read map. - // - // Regardless of whether the entry was present, record a miss: this key - // will take the slow path until the dirty map is promoted to the read - // map. - m.missLocked() - } - m.mu.Unlock() - } - for ok { - ptr := atomic.LoadPointer(&e.p) - if ptr == nil || ptr == expunged { - return false - } - p := (*V)(ptr) - if *p != old { - return false - } - if atomic.CompareAndSwapPointer(&e.p, ptr, nil) { - return true - } - } - return false -} - -// tryCompareAndSwap compare the entry with the given old value and swaps -// it with a new value if the entry is equal to the old value, and the entry -// has not been expunged. -// -// If the entry is expunged, tryCompareAndSwap returns false and leaves -// the entry unchanged. -func tryCompareAndSwap[V comparable](e *entry[V], old, new V) bool { - ptr := atomic.LoadPointer(&e.p) - if ptr == nil || ptr == expunged { - return false - } - p := (*V)(ptr) - if *p != old { - return false - } - - // Copy the interface after the first load to make this method more amenable - // to escape analysis: if the comparison fails from the start, we shouldn't - // bother heap-allocating an interface value to store. - nc := new - for { - if atomic.CompareAndSwapPointer(&e.p, ptr, unsafe.Pointer(&nc)) { - return true - } - ptr = atomic.LoadPointer(&e.p) - if ptr == nil || ptr == expunged { - return false - } - p = (*V)(ptr) - if *p != old { - return false - } - } -} - -func (m *Map[K, V]) missLocked() { - m.misses++ - if m.misses < len(m.dirty) { - return - } - m.read.Store(&readOnly[K, V]{m: m.dirty}) - m.dirty = nil - m.misses = 0 -} - -func (m *Map[K, V]) dirtyLocked() { - if m.dirty != nil { - return - } - - read := m.loadReadOnly() - m.dirty = make(map[K]*entry[V], len(read.m)) - for k, e := range read.m { - if !e.tryExpungeLocked() { - m.dirty[k] = e - } - } -} - -func (e *entry[V]) tryExpungeLocked() (isExpunged bool) { - p := atomic.LoadPointer(&e.p) - for p == nil { - if atomic.CompareAndSwapPointer(&e.p, nil, expunged) { - return true - } - p = atomic.LoadPointer(&e.p) - } - return p == expunged -} -- cgit v1.3