diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ef417e375..ec5ceb642 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -14,4 +14,4 @@ jobs: steps: - uses: actions/checkout@v3 - run: | - make in-docker IMAGE_TAG=4.2.1 TARGET='import test GAMESCOPE_CMD=' + make in-docker IMAGE_TAG=4.3 TARGET='import test GAMESCOPE_CMD=' diff --git a/.releaserc.yaml b/.releaserc.yaml index b87566048..5724888e0 100644 --- a/.releaserc.yaml +++ b/.releaserc.yaml @@ -43,7 +43,7 @@ plugins: # Execute commands to build the project - - "@semantic-release/exec" - shell: true - prepareCmd: "make in-docker IMAGE_TAG=4.2.2 TARGET='force-import dist'" + prepareCmd: "make in-docker IMAGE_TAG=4.3 TARGET='force-import dist'" # Publish artifacts as a GitHub release - - "@semantic-release/github" diff --git a/Makefile b/Makefile index 51c7784e2..aa70289c1 100644 --- a/Makefile +++ b/Makefile @@ -15,18 +15,16 @@ EXPORT_TEMPLATE ?= $(HOME)/.local/share/godot/export_templates/$(GODOT_REVISION) #EXPORT_TEMPLATE_URL ?= https://downloads.tuxfamily.org/godotengine/$(GODOT_VERSION)/Godot_v$(GODOT_VERSION)-$(GODOT_RELEASE)_export_templates.tpz EXPORT_TEMPLATE_URL ?= https://github.com/godotengine/godot/releases/download/$(GODOT_VERSION)-$(GODOT_RELEASE)/Godot_v$(GODOT_VERSION)-$(GODOT_RELEASE)_export_templates.tpz -ALL_ADDONS := ./addons/dbus/bin/libdbus.linux.template_$(BUILD_TYPE).x86_64.so ./addons/linuxthread/bin/liblinuxthread.linux.template_$(BUILD_TYPE).x86_64.so ./addons/pty/bin/libpty.linux.template_$(BUILD_TYPE).x86_64.so ./addons/unixsock/bin/libunixsock.linux.template_$(BUILD_TYPE).x86_64.so ./addons/xlib/bin/libxlib.linux.template_$(BUILD_TYPE).x86_64.so -ALL_ADDON_FILES := $(shell find ./addons -regex '.*\(\.cpp\|\.h\|\.hpp\)$$') +ALL_EXTENSIONS := ./addons/core/bin/libopengamepadui-core.linux.template_$(BUILD_TYPE).x86_64.so +ALL_EXTENSION_FILES := $(shell find ./extensions/ -regex '.*\(\.rs|\.toml\|\.lock\)$$') ALL_GDSCRIPT := $(shell find ./ -name '*.gd') ALL_SCENES := $(shell find ./ -name '*.tscn') ALL_RESOURCES := $(shell find ./ -regex '.*\(\.tres\|\.svg\|\.png\)$$') -PROJECT_FILES := $(ALL_ADDONS) $(ALL_GDSCRIPT) $(ALL_SCENES) $(ALL_RESOURCES) +PROJECT_FILES := $(ALL_EXTENSIONS) $(ALL_GDSCRIPT) $(ALL_SCENES) $(ALL_RESOURCES) # Docker image variables IMAGE_NAME ?= ghcr.io/shadowblip/opengamepadui-builder IMAGE_TAG ?= latest -ADDONS_IMAGE_NAME ?= ghcr.io/shadowblip/opengamepadui-addons-builder -ADDONS_IMAGE_TAG ?= latest # Remote debugging variables SSH_USER ?= deck @@ -37,6 +35,8 @@ SSH_DATA_PATH ?= /home/$(SSH_USER)/Projects # systemd-sysext variables SYSEXT_ID ?= steamos SYSEXT_VERSION_ID ?= 3.6.3 +SYSEXT_LIBIIO_VERSION ?= 0.26-1 +SYSEXT_LIBSERIALPORT_VERSION ?= 0.1.2-1 # Include any user defined settings -include settings.mk @@ -144,37 +144,40 @@ build/metadata.json: build/opengamepad-ui.x86_64 assets/crypto/keys/opengamepadu .PHONY: import import: $(IMPORT_DIR) ## Import project assets -$(IMPORT_DIR): $(ALL_ADDONS) +$(IMPORT_DIR): $(ALL_EXTENSIONS) @echo "Importing project assets. This will take some time..." command -v $(GODOT) > /dev/null 2>&1 timeout --foreground 40 $(GODOT) --headless --editor . > /dev/null 2>&1 || echo "Finished" touch $(IMPORT_DIR) .PHONY: force-import -force-import: $(ALL_ADDONS) +force-import: $(ALL_EXTENSIONS) @echo "Force importing project assets. This will take some time..." command -v $(GODOT) > /dev/null 2>&1 timeout --foreground 40 $(GODOT) --headless --editor . > /dev/null 2>&1 || echo "Finished" timeout --foreground 40 $(GODOT) --headless --editor . > /dev/null 2>&1 || echo "Finished" -.PHONY: addons -addons: $(ALL_ADDONS) ## Build GDExtension addons -$(ALL_ADDONS) &: $(ALL_ADDON_FILES) - @echo "Building native GDExtension addons..." - cd ./gdext && $(MAKE) build +.PHONY: extensions +extensions: $(ALL_EXTENSIONS) ## Build engine extensions +$(ALL_EXTENSIONS) &: $(ALL_EXTENSION_FILES) + @echo "Building engine extensions..." + cd ./extensions && $(MAKE) build .PHONY: edit edit: $(IMPORT_DIR) ## Open the project in the Godot editor $(GODOT) --editor . +.PHONY: purge +purge: clean ## Remove all build artifacts including engine extensions + rm -rf $(ROOTFS) + cd ./extensions && $(MAKE) clean + .PHONY: clean -clean: ## Remove build artifacts +clean: ## Remove Godot build artifacts rm -rf build - rm -rf $(ROOTFS) rm -rf $(CACHE_DIR) rm -rf dist rm -rf $(IMPORT_DIR) - cd ./gdext && $(MAKE) clean .PHONY: run run-force run: build/opengamepad-ui.x86_64 run-force ## Run the project in gamescope @@ -193,7 +196,7 @@ $(EXPORT_TEMPLATE): .PHONY: debug debug: $(IMPORT_DIR) ## Run the project in debug mode in gamescope - $(GAMESCOPE) -e --xwayland-count 2 -- \ + $(GAMESCOPE) -e --xwayland-count 2 --expose-wayland -- \ $(GODOT) --path $(PWD) --remote-debug tcp://127.0.0.1:6007 \ --position 320,140 res://entrypoint.tscn @@ -237,9 +240,8 @@ assets/crypto/keys/opengamepadui.pub: assets/crypto/keys/opengamepadui.key ##@ Remote Debugging .PHONY: deploy -deploy: dist-archive $(SSH_MOUNT_PATH)/.mounted ## Build, deploy, and tunnel to a remote device - cp dist/opengamepadui.tar.gz $(SSH_MOUNT_PATH) - cd $(SSH_MOUNT_PATH) #&& tar xvfz opengamepadui.tar.gz +deploy: dist-archive ## Build and deploy to a remote device + scp dist/opengamepadui.tar.gz $(SSH_USER)@$(SSH_HOST):$(SSH_DATA_PATH) ssh -t $(SSH_USER)@$(SSH_HOST) tar xvfz "$(SSH_DATA_PATH)/opengamepadui.tar.gz" @@ -274,14 +276,6 @@ tunnel: ## Create an SSH tunnel to allow remote debugging ssh $(SSH_USER)@$(SSH_HOST) -N -f -R 6007:localhost:6007 -# Mounts the remote device and creates an SSH tunnel for remote debugging -$(SSH_MOUNT_PATH)/.mounted: - mkdir -p $(SSH_MOUNT_PATH) - sshfs -o default_permissions $(SSH_USER)@$(SSH_HOST):$(SSH_DATA_PATH) $(SSH_MOUNT_PATH) - $(MAKE) tunnel - touch $(SSH_MOUNT_PATH)/.mounted - - ##@ Distribution .PHONY: rootfs @@ -293,6 +287,7 @@ rootfs: build/opengamepad-ui.x86_64 cp -r build/*.so $(ROOTFS)/usr/share/opengamepadui cp -r build/opengamepad-ui.x86_64 $(ROOTFS)/usr/share/opengamepadui cp -r build/opengamepad-ui.pck $(ROOTFS)/usr/share/opengamepadui + cp ./extensions/target/release/reaper $(ROOTFS)/usr/share/opengamepadui touch $(ROOTFS)/.gdignore @@ -393,7 +388,7 @@ $(CACHE_DIR)/inputplumber.tar.gz: $(CACHE_DIR)/libiio $(CACHE_DIR)/libserialport wget -O $@ https://github.com/ShadowBlip/InputPlumber/releases/download/$${IP_VERSION}/inputplumber.tar.gz -LIBIIO_URL ?= https://mirror.rackspace.com/archlinux/extra/os/x86_64/libiio-0.25-3-x86_64.pkg.tar.zst +LIBIIO_URL ?= https://mirror.rackspace.com/archlinux/extra/os/x86_64/libiio-$(SYSEXT_LIBIIO_VERSION)-x86_64.pkg.tar.zst $(CACHE_DIR)/libiio: rm -rf $(CACHE_DIR)/libiio* wget $(LIBIIO_URL) \ @@ -403,7 +398,7 @@ $(CACHE_DIR)/libiio: tar xvf $(CACHE_DIR)/libiio.tar -C $(CACHE_DIR)/libiio -LIBSERIALPORT_URL ?= https://mirror.rackspace.com/archlinux/extra/os/x86_64/libserialport-0.1.1-5-x86_64.pkg.tar.zst +LIBSERIALPORT_URL ?= https://mirror.rackspace.com/archlinux/extra/os/x86_64/libserialport-$(SYSEXT_LIBSERIALPORT_VERSION)-x86_64.pkg.tar.zst $(CACHE_DIR)/libserialport: rm -rf $(CACHE_DIR)/libserialport* wget $(LIBSERIALPORT_URL) \ @@ -421,14 +416,6 @@ release: ## Publish a release with semantic release # E.g. make in-docker TARGET=build .PHONY: in-docker in-docker: - @# Build addons in a seperate container for glibc compatibility - docker run --rm \ - -v $(PWD):/src \ - --workdir /src \ - -e PWD=/src \ - --user $(shell id -u):$(shell id -g) \ - $(ADDONS_IMAGE_NAME):$(ADDONS_IMAGE_TAG) \ - make addons @# Run the given make target inside Docker docker run --rm \ -v $(PWD):/src \ @@ -449,14 +436,3 @@ docker-builder: .PHONY: docker-builder-push docker-builder-push: docker-builder docker push $(IMAGE_NAME):$(IMAGE_TAG) - -.PHONY: docker-builder-addons -docker-builder-addons: - @# Pull any existing image to cache it - docker pull $(ADDONS_IMAGE_NAME):$(ADDONS_IMAGE_TAG) || echo "No remote image to pull" - @# Build the Docker image that will build the project - docker build -t $(ADDONS_IMAGE_NAME):$(ADDONS_IMAGE_TAG) -f docker/Dockerfile.addons ./docker - -.PHONY: docker-builder-addons-push -docker-builder-addons-push: docker-builder-addons - docker push $(ADDONS_IMAGE_NAME):$(ADDONS_IMAGE_TAG) diff --git a/addons/.gitignore b/addons/.gitignore index 99eef491d..140f8cf80 100644 --- a/addons/.gitignore +++ b/addons/.gitignore @@ -1,5 +1 @@ -dbus/ -linuxthread/ -pty/ -unixsock/ -xlib/ +*.so diff --git a/addons/core/assets/icons/carbon--cloud-registry.svg b/addons/core/assets/icons/carbon--cloud-registry.svg new file mode 100644 index 000000000..67dd643ec --- /dev/null +++ b/addons/core/assets/icons/carbon--cloud-registry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/core/assets/icons/carbon--cloud-registry.svg.import b/addons/core/assets/icons/carbon--cloud-registry.svg.import new file mode 100644 index 000000000..094131c98 --- /dev/null +++ b/addons/core/assets/icons/carbon--cloud-registry.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bghk66u2c0kq" +path="res://.godot/imported/carbon--cloud-registry.svg-1e050ab4a509bcd0338010b635012822.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/core/assets/icons/carbon--cloud-registry.svg" +dest_files=["res://.godot/imported/carbon--cloud-registry.svg-1e050ab4a509bcd0338010b635012822.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/core/assets/icons/clarity--process-on-vm-line.svg b/addons/core/assets/icons/clarity--process-on-vm-line.svg new file mode 100644 index 000000000..308adfad9 --- /dev/null +++ b/addons/core/assets/icons/clarity--process-on-vm-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/core/assets/icons/clarity--process-on-vm-line.svg.import b/addons/core/assets/icons/clarity--process-on-vm-line.svg.import new file mode 100644 index 000000000..11ef486b9 --- /dev/null +++ b/addons/core/assets/icons/clarity--process-on-vm-line.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b1e1er0exm6u4" +path="res://.godot/imported/clarity--process-on-vm-line.svg-283d9963c91ea54966f3276a32a96346.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/core/assets/icons/clarity--process-on-vm-line.svg" +dest_files=["res://.godot/imported/clarity--process-on-vm-line.svg-283d9963c91ea54966f3276a32a96346.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/core/assets/icons/inputplumber.svg b/addons/core/assets/icons/inputplumber.svg new file mode 100644 index 000000000..4ea3ae1e5 --- /dev/null +++ b/addons/core/assets/icons/inputplumber.svg @@ -0,0 +1,76 @@ + + diff --git a/addons/core/assets/icons/inputplumber.svg.import b/addons/core/assets/icons/inputplumber.svg.import new file mode 100644 index 000000000..b11985071 --- /dev/null +++ b/addons/core/assets/icons/inputplumber.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dbdyxgqmyeg1f" +path="res://.godot/imported/inputplumber.svg-930e8ab4c0d3c4d458c32ef96742bdaf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/core/assets/icons/inputplumber.svg" +dest_files=["res://.godot/imported/inputplumber.svg-930e8ab4c0d3c4d458c32ef96742bdaf.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/core/assets/icons/library.svg b/addons/core/assets/icons/library.svg new file mode 100644 index 000000000..a4c3cef4f --- /dev/null +++ b/addons/core/assets/icons/library.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/core/assets/icons/library.svg.import b/addons/core/assets/icons/library.svg.import new file mode 100644 index 000000000..36d12ed4b --- /dev/null +++ b/addons/core/assets/icons/library.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bn4jiyx7afl3b" +path="res://.godot/imported/library.svg-8fcfe8437951fa126b5ab9f50dba6f13.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/core/assets/icons/library.svg" +dest_files=["res://.godot/imported/library.svg-8fcfe8437951fa126b5ab9f50dba6f13.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/core/assets/icons/material-symbols-light--valve.svg b/addons/core/assets/icons/material-symbols-light--valve.svg new file mode 100644 index 000000000..8881ab392 --- /dev/null +++ b/addons/core/assets/icons/material-symbols-light--valve.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/core/assets/icons/material-symbols-light--valve.svg.import b/addons/core/assets/icons/material-symbols-light--valve.svg.import new file mode 100644 index 000000000..bf831ce91 --- /dev/null +++ b/addons/core/assets/icons/material-symbols-light--valve.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d0pv3m8ld8ess" +path="res://.godot/imported/material-symbols-light--valve.svg-c7c5e30f16c31ebbbe32f0986c258ed1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/core/assets/icons/material-symbols-light--valve.svg" +dest_files=["res://.godot/imported/material-symbols-light--valve.svg-c7c5e30f16c31ebbbe32f0986c258ed1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/core/core.gdextension b/addons/core/core.gdextension new file mode 100644 index 000000000..c47219866 --- /dev/null +++ b/addons/core/core.gdextension @@ -0,0 +1,20 @@ +[configuration] +entry_symbol = "gdext_rust_init" +compatibility_minimum = 4.3 +reloadable = false + +[libraries] +linux.debug.x86_64 = "res://addons/core/bin/libopengamepadui-core.linux.template_debug.x86_64.so" +linux.release.x86_64 = "res://addons/core/bin/libopengamepadui-core.linux.template_release.x86_64.so" + +[icons] +BluezInstance = "res://assets/editor-icons/bluetooth.svg" +GamescopeInstance = "res://assets/editor-icons/streamline--desktop-game-solid.svg" +InputPlumberInstance = "res://addons/core/assets/icons/inputplumber.svg" +NetworkManagerInstance = "res://assets/ui/icons/iconoir--network-solid.svg" +PowerStationInstance = "res://assets/editor-icons/game-icons--power-generator.svg" +Pty = "res://assets/editor-icons/devicon-plain--bash.svg" +ResourceProcessor = "res://addons/core/assets/icons/clarity--process-on-vm-line.svg" +ResourceRegistry = "res://addons/core/assets/icons/carbon--cloud-registry.svg" +UPowerInstance = "res://assets/editor-icons/material-symbols--battery-profile-sharp.svg" +Vdf = "res://addons/core/assets/icons/material-symbols-light--valve.svg" diff --git a/addons/gut/fonts/AnonymousPro-Bold.ttf.import b/addons/gut/fonts/AnonymousPro-Bold.ttf.import index a3eb4791f..de1351f60 100644 --- a/addons/gut/fonts/AnonymousPro-Bold.ttf.import +++ b/addons/gut/fonts/AnonymousPro-Bold.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/AnonymousPro-Bold.ttf-9d8fef4d357af5b52cd60af Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/AnonymousPro-BoldItalic.ttf.import b/addons/gut/fonts/AnonymousPro-BoldItalic.ttf.import index ef28dd805..bdde20728 100644 --- a/addons/gut/fonts/AnonymousPro-BoldItalic.ttf.import +++ b/addons/gut/fonts/AnonymousPro-BoldItalic.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/AnonymousPro-BoldItalic.ttf-4274bf704d3d6b9cd Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/AnonymousPro-Italic.ttf.import b/addons/gut/fonts/AnonymousPro-Italic.ttf.import index 1779af172..ce3e5b918 100644 --- a/addons/gut/fonts/AnonymousPro-Italic.ttf.import +++ b/addons/gut/fonts/AnonymousPro-Italic.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/AnonymousPro-Italic.ttf-9989590b02137b799e13d Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/AnonymousPro-Regular.ttf.import b/addons/gut/fonts/AnonymousPro-Regular.ttf.import index 1e2975b15..a567498cd 100644 --- a/addons/gut/fonts/AnonymousPro-Regular.ttf.import +++ b/addons/gut/fonts/AnonymousPro-Regular.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/AnonymousPro-Regular.ttf-856c843fd6f89964d2ca Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/CourierPrime-Bold.ttf.import b/addons/gut/fonts/CourierPrime-Bold.ttf.import index 7d60fb0a2..cb05171da 100644 --- a/addons/gut/fonts/CourierPrime-Bold.ttf.import +++ b/addons/gut/fonts/CourierPrime-Bold.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/CourierPrime-Bold.ttf-1f003c66d63ebed70964e77 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/CourierPrime-BoldItalic.ttf.import b/addons/gut/fonts/CourierPrime-BoldItalic.ttf.import index 4678c9ebb..0a9a7b770 100644 --- a/addons/gut/fonts/CourierPrime-BoldItalic.ttf.import +++ b/addons/gut/fonts/CourierPrime-BoldItalic.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/CourierPrime-BoldItalic.ttf-65ebcc61dd5e1dfa8 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/CourierPrime-Italic.ttf.import b/addons/gut/fonts/CourierPrime-Italic.ttf.import index 522e2950c..89412fc98 100644 --- a/addons/gut/fonts/CourierPrime-Italic.ttf.import +++ b/addons/gut/fonts/CourierPrime-Italic.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/CourierPrime-Italic.ttf-baa9156a73770735a0f72 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/CourierPrime-Regular.ttf.import b/addons/gut/fonts/CourierPrime-Regular.ttf.import index 38174660f..9fde40b1b 100644 --- a/addons/gut/fonts/CourierPrime-Regular.ttf.import +++ b/addons/gut/fonts/CourierPrime-Regular.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/CourierPrime-Regular.ttf-3babe7e4a7a588dfc9a8 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/LobsterTwo-Bold.ttf.import b/addons/gut/fonts/LobsterTwo-Bold.ttf.import index 7548ad04d..673d15155 100644 --- a/addons/gut/fonts/LobsterTwo-Bold.ttf.import +++ b/addons/gut/fonts/LobsterTwo-Bold.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/LobsterTwo-Bold.ttf-7c7f734103b58a32491a47881 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/LobsterTwo-BoldItalic.ttf.import b/addons/gut/fonts/LobsterTwo-BoldItalic.ttf.import index 4b609e80b..62048b0ef 100644 --- a/addons/gut/fonts/LobsterTwo-BoldItalic.ttf.import +++ b/addons/gut/fonts/LobsterTwo-BoldItalic.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/LobsterTwo-BoldItalic.ttf-227406a33e84448e6aa Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/LobsterTwo-Italic.ttf.import b/addons/gut/fonts/LobsterTwo-Italic.ttf.import index 5899b7977..d3ca27281 100644 --- a/addons/gut/fonts/LobsterTwo-Italic.ttf.import +++ b/addons/gut/fonts/LobsterTwo-Italic.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/LobsterTwo-Italic.ttf-f93abf6c25390c85ad5fb6c Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/addons/gut/fonts/LobsterTwo-Regular.ttf.import b/addons/gut/fonts/LobsterTwo-Regular.ttf.import index 45a12c8a5..9cc754218 100644 --- a/addons/gut/fonts/LobsterTwo-Regular.ttf.import +++ b/addons/gut/fonts/LobsterTwo-Regular.ttf.import @@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/LobsterTwo-Regular.ttf-f3fcfa01cd671c8da433dd Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 diff --git a/assets/editor-icons/devicon-plain--bash.svg b/assets/editor-icons/devicon-plain--bash.svg new file mode 100644 index 000000000..50a9294b3 --- /dev/null +++ b/assets/editor-icons/devicon-plain--bash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/editor-icons/devicon-plain--bash.svg.import b/assets/editor-icons/devicon-plain--bash.svg.import new file mode 100644 index 000000000..c58992fe6 --- /dev/null +++ b/assets/editor-icons/devicon-plain--bash.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://byyen2xkltwti" +path="res://.godot/imported/devicon-plain--bash.svg-6240f7f0c8c8d59a2493575f1290b4da.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/editor-icons/devicon-plain--bash.svg" +dest_files=["res://.godot/imported/devicon-plain--bash.svg-6240f7f0c8c8d59a2493575f1290b4da.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/editor-icons/game-icons--power-generator.svg b/assets/editor-icons/game-icons--power-generator.svg new file mode 100644 index 000000000..b6590b544 --- /dev/null +++ b/assets/editor-icons/game-icons--power-generator.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/editor-icons/game-icons--power-generator.svg.import b/assets/editor-icons/game-icons--power-generator.svg.import new file mode 100644 index 000000000..4e7a4ee49 --- /dev/null +++ b/assets/editor-icons/game-icons--power-generator.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://sgo62iku8hra" +path="res://.godot/imported/game-icons--power-generator.svg-c4c65834b21ee0abccaa30749630027a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/editor-icons/game-icons--power-generator.svg" +dest_files=["res://.godot/imported/game-icons--power-generator.svg-c4c65834b21ee0abccaa30749630027a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/editor-icons/inputplumber.svg b/assets/editor-icons/inputplumber.svg new file mode 100644 index 000000000..4e63ef665 --- /dev/null +++ b/assets/editor-icons/inputplumber.svg @@ -0,0 +1,50 @@ + + diff --git a/assets/editor-icons/inputplumber.svg.import b/assets/editor-icons/inputplumber.svg.import new file mode 100644 index 000000000..4f82c89c4 --- /dev/null +++ b/assets/editor-icons/inputplumber.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dhwvdnbmrvqu4" +path="res://.godot/imported/inputplumber.svg-f9a02e1ec85cdf7dc3601d7003f9de2a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/editor-icons/inputplumber.svg" +dest_files=["res://.godot/imported/inputplumber.svg-f9a02e1ec85cdf7dc3601d7003f9de2a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.063 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/editor-icons/material-symbols--battery-profile-sharp.svg b/assets/editor-icons/material-symbols--battery-profile-sharp.svg new file mode 100644 index 000000000..5dfdb58a1 --- /dev/null +++ b/assets/editor-icons/material-symbols--battery-profile-sharp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/editor-icons/material-symbols--battery-profile-sharp.svg.import b/assets/editor-icons/material-symbols--battery-profile-sharp.svg.import new file mode 100644 index 000000000..6e2b2d68b --- /dev/null +++ b/assets/editor-icons/material-symbols--battery-profile-sharp.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cl50s5usbx8nv" +path="res://.godot/imported/material-symbols--battery-profile-sharp.svg-87e7d7a90b8036cca4150ec1137bd618.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/editor-icons/material-symbols--battery-profile-sharp.svg" +dest_files=["res://.godot/imported/material-symbols--battery-profile-sharp.svg-87e7d7a90b8036cca4150ec1137bd618.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/editor-icons/solar--dialog-2-bold.svg b/assets/editor-icons/solar--dialog-2-bold.svg new file mode 100644 index 000000000..c4799ee46 --- /dev/null +++ b/assets/editor-icons/solar--dialog-2-bold.svg @@ -0,0 +1,46 @@ + + + + + + + diff --git a/assets/editor-icons/solar--dialog-2-bold.svg.import b/assets/editor-icons/solar--dialog-2-bold.svg.import new file mode 100644 index 000000000..ed2c0a288 --- /dev/null +++ b/assets/editor-icons/solar--dialog-2-bold.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dkxlstqr7f8oo" +path="res://.godot/imported/solar--dialog-2-bold.svg-da68fed2d0a8c7337ff4134355b0d904.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/editor-icons/solar--dialog-2-bold.svg" +dest_files=["res://.godot/imported/solar--dialog-2-bold.svg-da68fed2d0a8c7337ff4134355b0d904.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/editor-icons/streamline--desktop-game-solid.svg b/assets/editor-icons/streamline--desktop-game-solid.svg new file mode 100644 index 000000000..d8ebec7e1 --- /dev/null +++ b/assets/editor-icons/streamline--desktop-game-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/editor-icons/streamline--desktop-game-solid.svg.import b/assets/editor-icons/streamline--desktop-game-solid.svg.import new file mode 100644 index 000000000..b575a2729 --- /dev/null +++ b/assets/editor-icons/streamline--desktop-game-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cav2x6q8apups" +path="res://.godot/imported/streamline--desktop-game-solid.svg-ff379b75f0c49d1fc9be24f364b571c9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/editor-icons/streamline--desktop-game-solid.svg" +dest_files=["res://.godot/imported/streamline--desktop-game-solid.svg-ff379b75f0c49d1fc9be24f364b571c9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/gamepad/icon_mappings/joycon.tres b/assets/gamepad/icon_mappings/joycon.tres index 786c95ba8..fb6793f2d 100644 --- a/assets/gamepad/icon_mappings/joycon.tres +++ b/assets/gamepad/icon_mappings/joycon.tres @@ -1,36 +1,36 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=32 format=3 uid="uid://dukk7rqq5cgma"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="1_a16bb"] -[ext_resource type="Texture2D" uid="uid://dec8wg71j58h1" path="res://assets/images/gamepad/switch/switch_button_a.svg" id="1_bwo1l"] -[ext_resource type="Texture2D" uid="uid://b1qcl62witrrt" path="res://assets/images/gamepad/switch/switch_buttons.svg" id="1_ww2e7"] -[ext_resource type="Texture2D" uid="uid://c3hr6ecnhdtg2" path="res://assets/images/gamepad/switch/switch_buttons_down.svg" id="2_gavjx"] -[ext_resource type="Texture2D" uid="uid://b3v8gbuc0knsr" path="res://assets/images/gamepad/switch/switch_button_home.svg" id="2_o4j1j"] -[ext_resource type="Texture2D" uid="uid://b6wkct2qwo0ul" path="res://assets/images/gamepad/switch/switch_button_x.svg" id="3_ae6nc"] -[ext_resource type="Texture2D" uid="uid://dykfvnb0v687h" path="res://assets/images/gamepad/switch/switch_buttons_left.svg" id="3_k02em"] -[ext_resource type="Texture2D" uid="uid://bcthc11uwae6n" path="res://assets/images/gamepad/switch/switch_buttons_right.svg" id="4_w4xps"] -[ext_resource type="Texture2D" uid="uid://bo07ay1vxneo1" path="res://assets/images/gamepad/switch/switch_buttons_up.svg" id="5_e3bby"] -[ext_resource type="Texture2D" uid="uid://8o3g4dcunk87" path="res://assets/images/gamepad/switch/switch_button_b.svg" id="5_jpu3i"] -[ext_resource type="Texture2D" uid="uid://dugmdwvujsrk4" path="res://assets/images/gamepad/switch/switch_button_plus.svg" id="6_q5ufv"] -[ext_resource type="Texture2D" uid="uid://djvoe5nud1i0w" path="res://assets/images/gamepad/switch/switch_button_y.svg" id="7_e24lf"] -[ext_resource type="Texture2D" uid="uid://by4xd4qq5dspy" path="res://assets/images/gamepad/switch/switch_joycon_left_rotate.svg" id="8_5ade5"] -[ext_resource type="Texture2D" uid="uid://b57k4ko26lm6" path="res://assets/images/gamepad/switch/switch_button_l.svg" id="9_y7sje"] -[ext_resource type="Texture2D" uid="uid://bgkt5t7mmbhts" path="res://assets/images/gamepad/switch/switch_stick_l.svg" id="10_7ixoo"] -[ext_resource type="Texture2D" uid="uid://ca4fcl2jsx62k" path="res://assets/images/gamepad/switch/switch_stick_l_press.svg" id="11_sc443"] -[ext_resource type="Texture2D" uid="uid://bvnpkij77axxu" path="res://assets/images/gamepad/switch/switch_stick_l_down.svg" id="12_xl772"] -[ext_resource type="Texture2D" uid="uid://bbad7xmuveel8" path="res://assets/images/gamepad/switch/switch_stick_l_left.svg" id="13_21edv"] -[ext_resource type="Texture2D" uid="uid://lpy6n3raxq2" path="res://assets/images/gamepad/switch/switch_stick_l_right.svg" id="14_t7a81"] -[ext_resource type="Texture2D" uid="uid://btrirhorp6y57" path="res://assets/images/gamepad/switch/switch_stick_l_up.svg" id="15_c886y"] -[ext_resource type="Texture2D" uid="uid://dsa8m2vfvtx0q" path="res://assets/images/gamepad/switch/switch_button_zl.svg" id="16_xvg34"] -[ext_resource type="Texture2D" uid="uid://bu2naacuis8k8" path="res://assets/images/gamepad/switch/switch_button_r.svg" id="18_rb1n1"] -[ext_resource type="Texture2D" uid="uid://c1fas8o38r02p" path="res://assets/images/gamepad/switch/switch_stick_r.svg" id="19_royjt"] -[ext_resource type="Texture2D" uid="uid://dop088ppjtx7r" path="res://assets/images/gamepad/switch/switch_stick_r_press.svg" id="20_o8e1o"] -[ext_resource type="Texture2D" uid="uid://dqrqw3lv3kl3e" path="res://assets/images/gamepad/switch/switch_stick_r_down.svg" id="21_m6t1r"] -[ext_resource type="Texture2D" uid="uid://bcstmmysk3e1" path="res://assets/images/gamepad/switch/switch_stick_r_left.svg" id="22_4onjx"] -[ext_resource type="Texture2D" uid="uid://domi3pblhpgqy" path="res://assets/images/gamepad/switch/switch_stick_r_right.svg" id="23_rh7dk"] -[ext_resource type="Texture2D" uid="uid://dtkumw37qfis8" path="res://assets/images/gamepad/switch/switch_stick_r_up.svg" id="24_jvrdt"] -[ext_resource type="Texture2D" uid="uid://cutjyk72c0d1" path="res://assets/images/gamepad/switch/switch_button_zr.svg" id="25_pawt1"] -[ext_resource type="Texture2D" uid="uid://baeygxurw7dn1" path="res://assets/images/gamepad/switch/switch_button_sync.svg" id="26_3hoym"] -[ext_resource type="Texture2D" uid="uid://crl6hqey1xqfd" path="res://assets/images/gamepad/switch/switch_button_minus.svg" id="28_wn71f"] +[ext_resource type="Texture2D" uid="uid://dsnnac0xk0tfl" path="res://assets/images/gamepad/switch/switch_button_a.svg" id="1_bwo1l"] +[ext_resource type="Texture2D" uid="uid://bvjmsnstaoq2v" path="res://assets/images/gamepad/switch/switch_buttons.svg" id="1_ww2e7"] +[ext_resource type="Texture2D" uid="uid://cdvcck1mbmt87" path="res://assets/images/gamepad/switch/switch_buttons_down.svg" id="2_gavjx"] +[ext_resource type="Texture2D" uid="uid://dicj4tpxknqai" path="res://assets/images/gamepad/switch/switch_button_home.svg" id="2_o4j1j"] +[ext_resource type="Texture2D" uid="uid://d0nkpy8tk3f28" path="res://assets/images/gamepad/switch/switch_button_x.svg" id="3_ae6nc"] +[ext_resource type="Texture2D" uid="uid://b4j53xuo8rosw" path="res://assets/images/gamepad/switch/switch_buttons_left.svg" id="3_k02em"] +[ext_resource type="Texture2D" uid="uid://bnbk3lkeaqwfa" path="res://assets/images/gamepad/switch/switch_buttons_right.svg" id="4_w4xps"] +[ext_resource type="Texture2D" uid="uid://ducpt6w14ol4i" path="res://assets/images/gamepad/switch/switch_buttons_up.svg" id="5_e3bby"] +[ext_resource type="Texture2D" uid="uid://b21ssea6yir56" path="res://assets/images/gamepad/switch/switch_button_b.svg" id="5_jpu3i"] +[ext_resource type="Texture2D" uid="uid://rgpis0qqwyn3" path="res://assets/images/gamepad/switch/switch_button_plus.svg" id="6_q5ufv"] +[ext_resource type="Texture2D" uid="uid://ckwf0ko1ch08t" path="res://assets/images/gamepad/switch/switch_button_y.svg" id="7_e24lf"] +[ext_resource type="Texture2D" uid="uid://cda8etpu56yjn" path="res://assets/images/gamepad/switch/switch_joycon_left_rotate.svg" id="8_5ade5"] +[ext_resource type="Texture2D" uid="uid://d2ho4b7w2mdlb" path="res://assets/images/gamepad/switch/switch_button_l.svg" id="9_y7sje"] +[ext_resource type="Texture2D" uid="uid://ruhfjlhtlik3" path="res://assets/images/gamepad/switch/switch_stick_l.svg" id="10_7ixoo"] +[ext_resource type="Texture2D" uid="uid://d1xnwfo4kwgwe" path="res://assets/images/gamepad/switch/switch_stick_l_press.svg" id="11_sc443"] +[ext_resource type="Texture2D" uid="uid://ri6vpuerb2jn" path="res://assets/images/gamepad/switch/switch_stick_l_down.svg" id="12_xl772"] +[ext_resource type="Texture2D" uid="uid://dvdwtqdrgcx55" path="res://assets/images/gamepad/switch/switch_stick_l_left.svg" id="13_21edv"] +[ext_resource type="Texture2D" uid="uid://ckdjvbxmun3hk" path="res://assets/images/gamepad/switch/switch_stick_l_right.svg" id="14_t7a81"] +[ext_resource type="Texture2D" uid="uid://c54734rwsvi5y" path="res://assets/images/gamepad/switch/switch_stick_l_up.svg" id="15_c886y"] +[ext_resource type="Texture2D" uid="uid://ysw3f4s0wckl" path="res://assets/images/gamepad/switch/switch_button_zl.svg" id="16_xvg34"] +[ext_resource type="Texture2D" uid="uid://dh8gskvlf1y4n" path="res://assets/images/gamepad/switch/switch_button_r.svg" id="18_rb1n1"] +[ext_resource type="Texture2D" uid="uid://bnr0q7kupfac8" path="res://assets/images/gamepad/switch/switch_stick_r.svg" id="19_royjt"] +[ext_resource type="Texture2D" uid="uid://dalb0538ux4qy" path="res://assets/images/gamepad/switch/switch_stick_r_press.svg" id="20_o8e1o"] +[ext_resource type="Texture2D" uid="uid://c37v6pn2p4pa7" path="res://assets/images/gamepad/switch/switch_stick_r_down.svg" id="21_m6t1r"] +[ext_resource type="Texture2D" uid="uid://d0s0cx6bqiovg" path="res://assets/images/gamepad/switch/switch_stick_r_left.svg" id="22_4onjx"] +[ext_resource type="Texture2D" uid="uid://dxycp3mj54mwc" path="res://assets/images/gamepad/switch/switch_stick_r_right.svg" id="23_rh7dk"] +[ext_resource type="Texture2D" uid="uid://be6s7r6y3ndty" path="res://assets/images/gamepad/switch/switch_stick_r_up.svg" id="24_jvrdt"] +[ext_resource type="Texture2D" uid="uid://cwkd4oyqs445d" path="res://assets/images/gamepad/switch/switch_button_zr.svg" id="25_pawt1"] +[ext_resource type="Texture2D" uid="uid://brwy28sjw2pe8" path="res://assets/images/gamepad/switch/switch_button_sync.svg" id="26_3hoym"] +[ext_resource type="Texture2D" uid="uid://f3dabljd057e" path="res://assets/images/gamepad/switch/switch_button_minus.svg" id="28_wn71f"] [resource] script = ExtResource("1_a16bb") diff --git a/assets/gamepad/icon_mappings/oxp.tres b/assets/gamepad/icon_mappings/oxp.tres index 540ca3cf9..f213f9e6c 100644 --- a/assets/gamepad/icon_mappings/oxp.tres +++ b/assets/gamepad/icon_mappings/oxp.tres @@ -1,36 +1,36 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=32 format=3 uid="uid://d0xd340cjsp72"] -[ext_resource type="Texture2D" uid="uid://hmebqgl4qfmc" path="res://assets/images/gamepad/oxp/oxp-mini-a07.png" id="1_2cbux"] -[ext_resource type="Texture2D" uid="uid://cdhd2pnbunvy3" path="res://assets/images/gamepad/xbox/xbox_dpad.svg" id="2_fdy21"] -[ext_resource type="Texture2D" uid="uid://bju6gytlq2div" path="res://assets/images/gamepad/xbox/xbox_dpad_down.svg" id="3_imsyj"] -[ext_resource type="Texture2D" uid="uid://candjw8elkekp" path="res://assets/images/gamepad/xbox/xbox_dpad_left.svg" id="4_tgha8"] -[ext_resource type="Texture2D" uid="uid://dcpsb03pvu0vb" path="res://assets/images/gamepad/xbox/xbox_dpad_right.svg" id="5_m1y8h"] -[ext_resource type="Texture2D" uid="uid://c6w5exkfdkkrr" path="res://assets/images/gamepad/xbox/xbox_dpad_up.svg" id="6_rp82k"] -[ext_resource type="Texture2D" uid="uid://b3ofivkj5cwdd" path="res://assets/images/gamepad/oxp/b.png" id="7_5e6yx"] -[ext_resource type="Texture2D" uid="uid://cu0fgwxtupy5y" path="res://assets/images/gamepad/oxp/home.png" id="8_s3lvh"] -[ext_resource type="Texture2D" uid="uid://mtd8tfnbotgc" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_eiwti"] -[ext_resource type="Texture2D" uid="uid://bdqfg27h5o836" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_6wqop"] -[ext_resource type="Texture2D" uid="uid://dvv5grt45cows" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_0fasu"] -[ext_resource type="Texture2D" uid="uid://dcumk3oqkyy7g" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_5ksa6"] -[ext_resource type="Texture2D" uid="uid://dooej262t4g0x" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_g2uyl"] -[ext_resource type="Texture2D" uid="uid://coltjh07der5" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_8dpbw"] -[ext_resource type="Texture2D" uid="uid://b3xxbnyo2h20l" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_p3f62"] -[ext_resource type="Texture2D" uid="uid://u3jonqoafyg6" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="16_ly5ym"] -[ext_resource type="Texture2D" uid="uid://cfeud3gaq056l" path="res://assets/images/gamepad/oxp/y.png" id="17_io5hp"] -[ext_resource type="Texture2D" uid="uid://55kdld1mn8sh" path="res://assets/images/gamepad/oxp/dots.png" id="18_2rhb4"] -[ext_resource type="Texture2D" uid="uid://co3kg331knfu7" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="18_riyhh"] -[ext_resource type="Texture2D" uid="uid://dw15y6xiuthpe" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_u0e7h"] -[ext_resource type="Texture2D" uid="uid://de2kcqeakp3q0" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_6oyto"] -[ext_resource type="Texture2D" uid="uid://d4iq63eypbf2s" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_x8g1e"] -[ext_resource type="Texture2D" uid="uid://bus2l2lmd844f" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_kp76m"] -[ext_resource type="Texture2D" uid="uid://cokparjjmwy8c" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_lsal5"] -[ext_resource type="Texture2D" uid="uid://jn543ff1ak1c" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_5n5ek"] -[ext_resource type="Texture2D" uid="uid://da8ojjv5vttou" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="25_pdf5n"] +[ext_resource type="Texture2D" uid="uid://d224crt7h1b8l" path="res://assets/images/gamepad/oxp/oxp-mini-a07.png" id="1_2cbux"] +[ext_resource type="Texture2D" uid="uid://di87v7xnlu4hd" path="res://assets/images/gamepad/xbox/xbox_dpad.svg" id="2_fdy21"] +[ext_resource type="Texture2D" uid="uid://brqqse0hid6ki" path="res://assets/images/gamepad/xbox/xbox_dpad_down.svg" id="3_imsyj"] +[ext_resource type="Texture2D" uid="uid://caqnkbu5b0u2q" path="res://assets/images/gamepad/xbox/xbox_dpad_left.svg" id="4_tgha8"] +[ext_resource type="Texture2D" uid="uid://b5gektw3mgp1a" path="res://assets/images/gamepad/xbox/xbox_dpad_right.svg" id="5_m1y8h"] +[ext_resource type="Texture2D" uid="uid://cjwqn3wttist" path="res://assets/images/gamepad/xbox/xbox_dpad_up.svg" id="6_rp82k"] +[ext_resource type="Texture2D" uid="uid://c8cjp8mh52xh2" path="res://assets/images/gamepad/oxp/b.png" id="7_5e6yx"] +[ext_resource type="Texture2D" uid="uid://c00vtor5dsj8n" path="res://assets/images/gamepad/oxp/home.png" id="8_s3lvh"] +[ext_resource type="Texture2D" uid="uid://cwyi3if752xh6" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_eiwti"] +[ext_resource type="Texture2D" uid="uid://dqx7njocf1aok" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_6wqop"] +[ext_resource type="Texture2D" uid="uid://ckqaven4xmk16" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_0fasu"] +[ext_resource type="Texture2D" uid="uid://c1bfw2axbcg1q" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_5ksa6"] +[ext_resource type="Texture2D" uid="uid://c4iwtbyehtps2" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_g2uyl"] +[ext_resource type="Texture2D" uid="uid://d4bfok8606fmv" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_8dpbw"] +[ext_resource type="Texture2D" uid="uid://c7p0nhy4sh5wf" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_p3f62"] +[ext_resource type="Texture2D" uid="uid://cg8r4ijx7641s" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="16_ly5ym"] +[ext_resource type="Texture2D" uid="uid://dqhw0phvf3tre" path="res://assets/images/gamepad/oxp/y.png" id="17_io5hp"] +[ext_resource type="Texture2D" uid="uid://wmris721srn5" path="res://assets/images/gamepad/oxp/dots.png" id="18_2rhb4"] +[ext_resource type="Texture2D" uid="uid://ceiru5s4y3ghy" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="18_riyhh"] +[ext_resource type="Texture2D" uid="uid://bjq76f76quyry" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_u0e7h"] +[ext_resource type="Texture2D" uid="uid://5q4tjwvf7b8a" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_6oyto"] +[ext_resource type="Texture2D" uid="uid://cu3ptt3kea56h" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_x8g1e"] +[ext_resource type="Texture2D" uid="uid://s4x7rp50eub5" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_kp76m"] +[ext_resource type="Texture2D" uid="uid://kfqb1dr0735v" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_lsal5"] +[ext_resource type="Texture2D" uid="uid://o0xrox1l5sae" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_5n5ek"] +[ext_resource type="Texture2D" uid="uid://brt5gm71iv3u4" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="25_pdf5n"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="26_iuti6"] -[ext_resource type="Texture2D" uid="uid://ce732ysw0pjo1" path="res://assets/images/gamepad/xbox/xbox_button_view.svg" id="27_7206a"] -[ext_resource type="Texture2D" uid="uid://ddj7iydr5c04r" path="res://assets/images/gamepad/oxp/a.png" id="28_1faky"] -[ext_resource type="Texture2D" uid="uid://dagfma4wnmvw8" path="res://assets/images/gamepad/xbox/xbox_button_menu.svg" id="29_vuglh"] -[ext_resource type="Texture2D" uid="uid://ddma44dieeu0c" path="res://assets/images/gamepad/oxp/x.png" id="30_sjmia"] +[ext_resource type="Texture2D" uid="uid://cws272by4n8r7" path="res://assets/images/gamepad/xbox/xbox_button_view.svg" id="27_7206a"] +[ext_resource type="Texture2D" uid="uid://vmqhe01e4iit" path="res://assets/images/gamepad/oxp/a.png" id="28_1faky"] +[ext_resource type="Texture2D" uid="uid://ckk2my0j71i3r" path="res://assets/images/gamepad/xbox/xbox_button_menu.svg" id="29_vuglh"] +[ext_resource type="Texture2D" uid="uid://cgsn52xbh5g7n" path="res://assets/images/gamepad/oxp/x.png" id="30_sjmia"] [resource] script = ExtResource("26_iuti6") diff --git a/assets/gamepad/icon_mappings/ps4.tres b/assets/gamepad/icon_mappings/ps4.tres index b9ec01048..578a82a08 100644 --- a/assets/gamepad/icon_mappings/ps4.tres +++ b/assets/gamepad/icon_mappings/ps4.tres @@ -1,37 +1,37 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=33 format=3 uid="uid://d1wwvmphhodm0"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="1_6dodo"] -[ext_resource type="Texture2D" uid="uid://cgt42jlee1t3" path="res://assets/images/gamepad/playstation/playstation4_touchpad_touch.svg" id="1_dgojl"] -[ext_resource type="Texture2D" uid="uid://brf1l7pyedsg6" path="res://assets/images/gamepad/playstation/PS4_Diagram_Simple.png" id="1_xfnbr"] -[ext_resource type="Texture2D" uid="uid://dcysxvygs0t0b" path="res://assets/images/gamepad/playstation/playstation_button_color_circle.svg" id="2_boi0e"] -[ext_resource type="Texture2D" uid="uid://d1bn7vfymb8h7" path="res://assets/images/gamepad/playstation/playstation_dpad.svg" id="2_ksv3d"] -[ext_resource type="Texture2D" uid="uid://b8t8udyk7f2sy" path="res://assets/images/gamepad/playstation/playstation_dpad_down.svg" id="3_lbw7u"] -[ext_resource type="Texture2D" uid="uid://dt6pmeivafjuh" path="res://assets/images/gamepad/playstation/guide.svg" id="3_tihua"] -[ext_resource type="Texture2D" uid="uid://bucd4u3xhdkn5" path="res://assets/images/gamepad/playstation/playstation_dpad_left.svg" id="4_43xit"] -[ext_resource type="Texture2D" uid="uid://crv4tl33mg881" path="res://assets/images/gamepad/playstation/playstation_button_color_triangle.svg" id="4_t0rx5"] -[ext_resource type="Texture2D" uid="uid://nva4um5gnp46" path="res://assets/images/gamepad/playstation/playstation_dpad_right.svg" id="5_bxvhl"] -[ext_resource type="Texture2D" uid="uid://dvckjbn44kqh4" path="res://assets/images/gamepad/playstation/playstation_dpad_up.svg" id="6_01cs4"] -[ext_resource type="Texture2D" uid="uid://cvx6k287vtlda" path="res://assets/images/gamepad/playstation/playstation4_button_share.svg" id="6_eb1f1"] -[ext_resource type="Texture2D" uid="uid://d3b15ae2w8u04" path="res://assets/images/gamepad/playstation/playstation_button_color_cross.svg" id="7_1eaue"] -[ext_resource type="Texture2D" uid="uid://bbekpvxt1viwr" path="res://assets/images/gamepad/playstation/playstation4_button_options.svg" id="8_is2y1"] -[ext_resource type="Texture2D" uid="uid://ce50wyq1ctdg3" path="res://assets/images/gamepad/playstation/playstation_trigger_l1.svg" id="9_2i8h4"] -[ext_resource type="Texture2D" uid="uid://cwmll8jpe01x1" path="res://assets/images/gamepad/playstation/playstation_button_color_square.svg" id="9_41xra"] -[ext_resource type="Texture2D" uid="uid://bq64rpsb1usfh" path="res://assets/images/gamepad/playstation/playstation_stick_l.svg" id="10_sg1b0"] -[ext_resource type="Texture2D" uid="uid://bi2887g54x4ym" path="res://assets/images/gamepad/generic/gyro.png" id="10_tdin3"] -[ext_resource type="Texture2D" uid="uid://dbf2kolrjqlsk" path="res://assets/images/gamepad/playstation/playstation_stick_l_press.svg" id="11_j8ffx"] -[ext_resource type="Texture2D" uid="uid://cv1gammxiedb" path="res://assets/images/gamepad/playstation/playstation_stick_l_down.svg" id="12_vf35o"] -[ext_resource type="Texture2D" uid="uid://d22o4cxlcni3q" path="res://assets/images/gamepad/playstation/playstation_stick_l_left.svg" id="13_civhm"] -[ext_resource type="Texture2D" uid="uid://0bomms2ipyct" path="res://assets/images/gamepad/playstation/playstation_stick_l_right.svg" id="14_vpg31"] -[ext_resource type="Texture2D" uid="uid://ds60ucempaqtc" path="res://assets/images/gamepad/playstation/playstation_stick_l_up.svg" id="15_obvm1"] -[ext_resource type="Texture2D" uid="uid://d0pbaqstutkli" path="res://assets/images/gamepad/playstation/playstation_trigger_l2.svg" id="16_0u2wr"] -[ext_resource type="Texture2D" uid="uid://drbydhj23dk4v" path="res://assets/images/gamepad/playstation/playstation_trigger_r1.svg" id="18_mt27f"] -[ext_resource type="Texture2D" uid="uid://byrtduc2ys2gd" path="res://assets/images/gamepad/playstation/playstation_stick_r.svg" id="19_cv760"] -[ext_resource type="Texture2D" uid="uid://dnc8ia8fbc72f" path="res://assets/images/gamepad/playstation/playstation_stick_r_press.svg" id="20_wv7w2"] -[ext_resource type="Texture2D" uid="uid://b0hdbw4qo4nl2" path="res://assets/images/gamepad/playstation/playstation_stick_r_down.svg" id="21_64cn1"] -[ext_resource type="Texture2D" uid="uid://dedewmjipppck" path="res://assets/images/gamepad/playstation/playstation_stick_r_left.svg" id="22_6k3lc"] -[ext_resource type="Texture2D" uid="uid://d2f87ubl8ut4t" path="res://assets/images/gamepad/playstation/playstation_stick_r_right.svg" id="23_a3gna"] -[ext_resource type="Texture2D" uid="uid://cvjxqt1lsfp1t" path="res://assets/images/gamepad/playstation/playstation_stick_r_up.svg" id="24_hoi2y"] -[ext_resource type="Texture2D" uid="uid://botqou7j5tw5i" path="res://assets/images/gamepad/playstation/playstation_trigger_r2.svg" id="25_t4gxs"] +[ext_resource type="Texture2D" uid="uid://clihwqudrnv5k" path="res://assets/images/gamepad/playstation/playstation4_touchpad_touch.svg" id="1_dgojl"] +[ext_resource type="Texture2D" uid="uid://b6ebettxxbmu1" path="res://assets/images/gamepad/playstation/PS4_Diagram_Simple.png" id="1_xfnbr"] +[ext_resource type="Texture2D" uid="uid://b1wo28cyd80ov" path="res://assets/images/gamepad/playstation/playstation_button_color_circle.svg" id="2_boi0e"] +[ext_resource type="Texture2D" uid="uid://ua3iri55e4go" path="res://assets/images/gamepad/playstation/playstation_dpad.svg" id="2_ksv3d"] +[ext_resource type="Texture2D" uid="uid://byblaf3dx4by6" path="res://assets/images/gamepad/playstation/playstation_dpad_down.svg" id="3_lbw7u"] +[ext_resource type="Texture2D" uid="uid://b6qllts4hg3my" path="res://assets/images/gamepad/playstation/guide.svg" id="3_tihua"] +[ext_resource type="Texture2D" uid="uid://bmuqx6jxetdp1" path="res://assets/images/gamepad/playstation/playstation_dpad_left.svg" id="4_43xit"] +[ext_resource type="Texture2D" uid="uid://2rjhdg632n1f" path="res://assets/images/gamepad/playstation/playstation_button_color_triangle.svg" id="4_t0rx5"] +[ext_resource type="Texture2D" uid="uid://cxhc0rxkyqf2g" path="res://assets/images/gamepad/playstation/playstation_dpad_right.svg" id="5_bxvhl"] +[ext_resource type="Texture2D" uid="uid://da1vcs31c68sx" path="res://assets/images/gamepad/playstation/playstation_dpad_up.svg" id="6_01cs4"] +[ext_resource type="Texture2D" uid="uid://bb0cv3hsg3y0v" path="res://assets/images/gamepad/playstation/playstation4_button_share.svg" id="6_eb1f1"] +[ext_resource type="Texture2D" uid="uid://dwv1scwq0mlr2" path="res://assets/images/gamepad/playstation/playstation_button_color_cross.svg" id="7_1eaue"] +[ext_resource type="Texture2D" uid="uid://ci7bqueqpt20u" path="res://assets/images/gamepad/playstation/playstation4_button_options.svg" id="8_is2y1"] +[ext_resource type="Texture2D" uid="uid://bkya1ndod3v1j" path="res://assets/images/gamepad/playstation/playstation_trigger_l1.svg" id="9_2i8h4"] +[ext_resource type="Texture2D" uid="uid://brniycjajdi03" path="res://assets/images/gamepad/playstation/playstation_button_color_square.svg" id="9_41xra"] +[ext_resource type="Texture2D" uid="uid://d0hn6un6byklr" path="res://assets/images/gamepad/playstation/playstation_stick_l.svg" id="10_sg1b0"] +[ext_resource type="Texture2D" uid="uid://do8ip0e6vs5h5" path="res://assets/images/gamepad/generic/gyro.png" id="10_tdin3"] +[ext_resource type="Texture2D" uid="uid://cmijt22flcpda" path="res://assets/images/gamepad/playstation/playstation_stick_l_press.svg" id="11_j8ffx"] +[ext_resource type="Texture2D" uid="uid://od86dbeeirmp" path="res://assets/images/gamepad/playstation/playstation_stick_l_down.svg" id="12_vf35o"] +[ext_resource type="Texture2D" uid="uid://bufyl7xm4mcij" path="res://assets/images/gamepad/playstation/playstation_stick_l_left.svg" id="13_civhm"] +[ext_resource type="Texture2D" uid="uid://bj8fx1k8ectnx" path="res://assets/images/gamepad/playstation/playstation_stick_l_right.svg" id="14_vpg31"] +[ext_resource type="Texture2D" uid="uid://c8uwdhedkaonn" path="res://assets/images/gamepad/playstation/playstation_stick_l_up.svg" id="15_obvm1"] +[ext_resource type="Texture2D" uid="uid://dcnvqtiuawl0c" path="res://assets/images/gamepad/playstation/playstation_trigger_l2.svg" id="16_0u2wr"] +[ext_resource type="Texture2D" uid="uid://f2ge3igc0h6g" path="res://assets/images/gamepad/playstation/playstation_trigger_r1.svg" id="18_mt27f"] +[ext_resource type="Texture2D" uid="uid://dlh3o8lhabxmv" path="res://assets/images/gamepad/playstation/playstation_stick_r.svg" id="19_cv760"] +[ext_resource type="Texture2D" uid="uid://ctsqjuqhj0soc" path="res://assets/images/gamepad/playstation/playstation_stick_r_press.svg" id="20_wv7w2"] +[ext_resource type="Texture2D" uid="uid://do6olohpve0fm" path="res://assets/images/gamepad/playstation/playstation_stick_r_down.svg" id="21_64cn1"] +[ext_resource type="Texture2D" uid="uid://7uwvswwxu8u5" path="res://assets/images/gamepad/playstation/playstation_stick_r_left.svg" id="22_6k3lc"] +[ext_resource type="Texture2D" uid="uid://d2t2wxh6fduon" path="res://assets/images/gamepad/playstation/playstation_stick_r_right.svg" id="23_a3gna"] +[ext_resource type="Texture2D" uid="uid://csve53s1buixo" path="res://assets/images/gamepad/playstation/playstation_stick_r_up.svg" id="24_hoi2y"] +[ext_resource type="Texture2D" uid="uid://doeikq8t8xfny" path="res://assets/images/gamepad/playstation/playstation_trigger_r2.svg" id="25_t4gxs"] [resource] script = ExtResource("1_6dodo") diff --git a/assets/gamepad/icon_mappings/ps5.tres b/assets/gamepad/icon_mappings/ps5.tres index ee3ce2d20..65458bd08 100644 --- a/assets/gamepad/icon_mappings/ps5.tres +++ b/assets/gamepad/icon_mappings/ps5.tres @@ -1,27 +1,27 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=23 format=3 uid="uid://bmquv6m1c6ba1"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="1_ba5rj"] -[ext_resource type="Texture2D" uid="uid://biu5qfv6e1mq8" path="res://assets/images/gamepad/playstation/PS5_Diagram_Simple.png" id="1_brns1"] -[ext_resource type="Texture2D" uid="uid://bj0qk2s5mxkkd" path="res://assets/images/gamepad/playstation/playstation5_touchpad_touch.svg" id="1_l1iq3"] -[ext_resource type="Texture2D" uid="uid://dcysxvygs0t0b" path="res://assets/images/gamepad/playstation/playstation_button_color_circle.svg" id="2_6ey1f"] -[ext_resource type="Texture2D" uid="uid://dt6pmeivafjuh" path="res://assets/images/gamepad/playstation/guide.svg" id="3_3ltu8"] -[ext_resource type="Texture2D" uid="uid://crv4tl33mg881" path="res://assets/images/gamepad/playstation/playstation_button_color_triangle.svg" id="3_c5wlt"] -[ext_resource type="Texture2D" uid="uid://d1bn7vfymb8h7" path="res://assets/images/gamepad/playstation/playstation_dpad.svg" id="3_yh60b"] -[ext_resource type="Texture2D" uid="uid://b8t8udyk7f2sy" path="res://assets/images/gamepad/playstation/playstation_dpad_down.svg" id="4_wgvj5"] -[ext_resource type="Texture2D" uid="uid://d3b15ae2w8u04" path="res://assets/images/gamepad/playstation/playstation_button_color_cross.svg" id="5_bn8cu"] -[ext_resource type="Texture2D" uid="uid://bucd4u3xhdkn5" path="res://assets/images/gamepad/playstation/playstation_dpad_left.svg" id="5_k05x8"] -[ext_resource type="Texture2D" uid="uid://cwmll8jpe01x1" path="res://assets/images/gamepad/playstation/playstation_button_color_square.svg" id="6_ed18o"] -[ext_resource type="Texture2D" uid="uid://nva4um5gnp46" path="res://assets/images/gamepad/playstation/playstation_dpad_right.svg" id="6_vgnrb"] -[ext_resource type="Texture2D" uid="uid://y4ntbxk5qrpo" path="res://assets/images/gamepad/playstation/playstation5_button_create.svg" id="7_klhod"] -[ext_resource type="Texture2D" uid="uid://dvckjbn44kqh4" path="res://assets/images/gamepad/playstation/playstation_dpad_up.svg" id="7_paptw"] -[ext_resource type="Texture2D" uid="uid://8vgoicywpflx" path="res://assets/images/gamepad/playstation/playstation5_button_options.svg" id="9_sj432"] -[ext_resource type="Texture2D" uid="uid://bi2887g54x4ym" path="res://assets/images/gamepad/generic/gyro.png" id="10_ctm85"] -[ext_resource type="Texture2D" uid="uid://ce50wyq1ctdg3" path="res://assets/images/gamepad/playstation/playstation_trigger_l1.svg" id="10_gaqt5"] -[ext_resource type="Texture2D" uid="uid://bq64rpsb1usfh" path="res://assets/images/gamepad/playstation/playstation_stick_l.svg" id="11_mlyq1"] -[ext_resource type="Texture2D" uid="uid://d0pbaqstutkli" path="res://assets/images/gamepad/playstation/playstation_trigger_l2.svg" id="12_0m5i4"] -[ext_resource type="Texture2D" uid="uid://drbydhj23dk4v" path="res://assets/images/gamepad/playstation/playstation_trigger_r1.svg" id="14_6vcxa"] -[ext_resource type="Texture2D" uid="uid://byrtduc2ys2gd" path="res://assets/images/gamepad/playstation/playstation_stick_r.svg" id="15_chusf"] -[ext_resource type="Texture2D" uid="uid://botqou7j5tw5i" path="res://assets/images/gamepad/playstation/playstation_trigger_r2.svg" id="16_5vbcf"] +[ext_resource type="Texture2D" uid="uid://devahu3vo6pdj" path="res://assets/images/gamepad/playstation/PS5_Diagram_Simple.png" id="1_brns1"] +[ext_resource type="Texture2D" uid="uid://do6nap60p4cs6" path="res://assets/images/gamepad/playstation/playstation5_touchpad_touch.svg" id="1_l1iq3"] +[ext_resource type="Texture2D" uid="uid://b1wo28cyd80ov" path="res://assets/images/gamepad/playstation/playstation_button_color_circle.svg" id="2_6ey1f"] +[ext_resource type="Texture2D" uid="uid://b6qllts4hg3my" path="res://assets/images/gamepad/playstation/guide.svg" id="3_3ltu8"] +[ext_resource type="Texture2D" uid="uid://2rjhdg632n1f" path="res://assets/images/gamepad/playstation/playstation_button_color_triangle.svg" id="3_c5wlt"] +[ext_resource type="Texture2D" uid="uid://ua3iri55e4go" path="res://assets/images/gamepad/playstation/playstation_dpad.svg" id="3_yh60b"] +[ext_resource type="Texture2D" uid="uid://byblaf3dx4by6" path="res://assets/images/gamepad/playstation/playstation_dpad_down.svg" id="4_wgvj5"] +[ext_resource type="Texture2D" uid="uid://dwv1scwq0mlr2" path="res://assets/images/gamepad/playstation/playstation_button_color_cross.svg" id="5_bn8cu"] +[ext_resource type="Texture2D" uid="uid://bmuqx6jxetdp1" path="res://assets/images/gamepad/playstation/playstation_dpad_left.svg" id="5_k05x8"] +[ext_resource type="Texture2D" uid="uid://brniycjajdi03" path="res://assets/images/gamepad/playstation/playstation_button_color_square.svg" id="6_ed18o"] +[ext_resource type="Texture2D" uid="uid://cxhc0rxkyqf2g" path="res://assets/images/gamepad/playstation/playstation_dpad_right.svg" id="6_vgnrb"] +[ext_resource type="Texture2D" uid="uid://lsf27o58vimw" path="res://assets/images/gamepad/playstation/playstation5_button_create.svg" id="7_klhod"] +[ext_resource type="Texture2D" uid="uid://da1vcs31c68sx" path="res://assets/images/gamepad/playstation/playstation_dpad_up.svg" id="7_paptw"] +[ext_resource type="Texture2D" uid="uid://bn6oh5im6dg0t" path="res://assets/images/gamepad/playstation/playstation5_button_options.svg" id="9_sj432"] +[ext_resource type="Texture2D" uid="uid://do8ip0e6vs5h5" path="res://assets/images/gamepad/generic/gyro.png" id="10_ctm85"] +[ext_resource type="Texture2D" uid="uid://bkya1ndod3v1j" path="res://assets/images/gamepad/playstation/playstation_trigger_l1.svg" id="10_gaqt5"] +[ext_resource type="Texture2D" uid="uid://d0hn6un6byklr" path="res://assets/images/gamepad/playstation/playstation_stick_l.svg" id="11_mlyq1"] +[ext_resource type="Texture2D" uid="uid://dcnvqtiuawl0c" path="res://assets/images/gamepad/playstation/playstation_trigger_l2.svg" id="12_0m5i4"] +[ext_resource type="Texture2D" uid="uid://f2ge3igc0h6g" path="res://assets/images/gamepad/playstation/playstation_trigger_r1.svg" id="14_6vcxa"] +[ext_resource type="Texture2D" uid="uid://dlh3o8lhabxmv" path="res://assets/images/gamepad/playstation/playstation_stick_r.svg" id="15_chusf"] +[ext_resource type="Texture2D" uid="uid://doeikq8t8xfny" path="res://assets/images/gamepad/playstation/playstation_trigger_r2.svg" id="16_5vbcf"] [resource] script = ExtResource("1_ba5rj") diff --git a/assets/gamepad/icon_mappings/rog_ally.tres b/assets/gamepad/icon_mappings/rog_ally.tres index b4de5ac23..c176eba6d 100644 --- a/assets/gamepad/icon_mappings/rog_ally.tres +++ b/assets/gamepad/icon_mappings/rog_ally.tres @@ -1,39 +1,39 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=35 format=3 uid="uid://b1nd8y2yx8yq6"] -[ext_resource type="Texture2D" uid="uid://xm6mg1kykt88" path="res://assets/images/gamepad/rog-ally/diagram.png" id="1_01w5b"] -[ext_resource type="Texture2D" uid="uid://dn8i3gpeijgu1" path="res://assets/images/gamepad/xbox/xbox_dpad_round.svg" id="2_ujiwl"] -[ext_resource type="Texture2D" uid="uid://dwv3f88h7uaq7" path="res://assets/images/gamepad/xbox/xbox_dpad_round_down.svg" id="3_fu5eu"] -[ext_resource type="Texture2D" uid="uid://fmnwuu2ym0m8" path="res://assets/images/gamepad/xbox/xbox_dpad_round_left.svg" id="4_vlt4b"] -[ext_resource type="Texture2D" uid="uid://cboy1enwvdeoy" path="res://assets/images/gamepad/xbox/xbox_dpad_round_right.svg" id="5_rd21s"] -[ext_resource type="Texture2D" uid="uid://ba64eulr61afh" path="res://assets/images/gamepad/xbox/xbox_dpad_round_up.svg" id="6_e3vuo"] -[ext_resource type="Texture2D" uid="uid://bqho0a0sr6u1j" path="res://assets/images/gamepad/xbox/xbox_button_color_b.svg" id="7_5kd38"] -[ext_resource type="Texture2D" uid="uid://brctkdj250hpv" path="res://assets/images/gamepad/rog-ally/ally_button_guide.svg" id="8_3soja"] -[ext_resource type="Texture2D" uid="uid://mtd8tfnbotgc" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_ex1hq"] -[ext_resource type="Texture2D" uid="uid://bi2887g54x4ym" path="res://assets/images/gamepad/generic/gyro.png" id="9_vvq27"] -[ext_resource type="Texture2D" uid="uid://bdqfg27h5o836" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_e71eg"] -[ext_resource type="Texture2D" uid="uid://d3nga7v4ldmap" path="res://assets/images/gamepad/rog-ally/ally_button_m2.svg" id="10_t6mry"] -[ext_resource type="Texture2D" uid="uid://dvv5grt45cows" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_iyaus"] -[ext_resource type="Texture2D" uid="uid://dcumk3oqkyy7g" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_c3tu1"] -[ext_resource type="Texture2D" uid="uid://dooej262t4g0x" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_edtti"] -[ext_resource type="Texture2D" uid="uid://coltjh07der5" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_efnr7"] -[ext_resource type="Texture2D" uid="uid://b3xxbnyo2h20l" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_i2tmb"] -[ext_resource type="Texture2D" uid="uid://u3jonqoafyg6" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="16_isy1r"] -[ext_resource type="Texture2D" uid="uid://ddpudi00yulrx" path="res://assets/images/gamepad/xbox/xbox_button_color_y.svg" id="17_jr7kr"] -[ext_resource type="Texture2D" uid="uid://co3kg331knfu7" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="18_4llry"] -[ext_resource type="Texture2D" uid="uid://srjhf8dp2cpg" path="res://assets/images/gamepad/rog-ally/ally_button_quick.svg" id="18_gra38"] -[ext_resource type="Texture2D" uid="uid://dw15y6xiuthpe" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_rgqul"] -[ext_resource type="Texture2D" uid="uid://de2kcqeakp3q0" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_sg7ew"] -[ext_resource type="Texture2D" uid="uid://obqv3ikblrmx" path="res://assets/images/gamepad/rog-ally/ally_button_m1.svg" id="21_4aapa"] -[ext_resource type="Texture2D" uid="uid://d4iq63eypbf2s" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_rfair"] -[ext_resource type="Texture2D" uid="uid://bus2l2lmd844f" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_fvge1"] -[ext_resource type="Texture2D" uid="uid://cokparjjmwy8c" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_p32gu"] -[ext_resource type="Texture2D" uid="uid://jn543ff1ak1c" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_2bmwh"] -[ext_resource type="Texture2D" uid="uid://da8ojjv5vttou" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="25_1q8wd"] +[ext_resource type="Texture2D" uid="uid://c5tmyp3bdtksr" path="res://assets/images/gamepad/rog-ally/diagram.png" id="1_01w5b"] +[ext_resource type="Texture2D" uid="uid://n4t36xrl15ux" path="res://assets/images/gamepad/xbox/xbox_dpad_round.svg" id="2_ujiwl"] +[ext_resource type="Texture2D" uid="uid://bxovdah6vnpsn" path="res://assets/images/gamepad/xbox/xbox_dpad_round_down.svg" id="3_fu5eu"] +[ext_resource type="Texture2D" uid="uid://qhwj81sv67l4" path="res://assets/images/gamepad/xbox/xbox_dpad_round_left.svg" id="4_vlt4b"] +[ext_resource type="Texture2D" uid="uid://b1uo5130s6gfw" path="res://assets/images/gamepad/xbox/xbox_dpad_round_right.svg" id="5_rd21s"] +[ext_resource type="Texture2D" uid="uid://dqk82r4up56mh" path="res://assets/images/gamepad/xbox/xbox_dpad_round_up.svg" id="6_e3vuo"] +[ext_resource type="Texture2D" uid="uid://dfby3iiksln2e" path="res://assets/images/gamepad/xbox/xbox_button_color_b.svg" id="7_5kd38"] +[ext_resource type="Texture2D" uid="uid://cl1gl3j47wds5" path="res://assets/images/gamepad/rog-ally/ally_button_guide.svg" id="8_3soja"] +[ext_resource type="Texture2D" uid="uid://cwyi3if752xh6" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_ex1hq"] +[ext_resource type="Texture2D" uid="uid://do8ip0e6vs5h5" path="res://assets/images/gamepad/generic/gyro.png" id="9_vvq27"] +[ext_resource type="Texture2D" uid="uid://dqx7njocf1aok" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_e71eg"] +[ext_resource type="Texture2D" uid="uid://74k3jdksgydd" path="res://assets/images/gamepad/rog-ally/ally_button_m2.svg" id="10_t6mry"] +[ext_resource type="Texture2D" uid="uid://ckqaven4xmk16" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_iyaus"] +[ext_resource type="Texture2D" uid="uid://c1bfw2axbcg1q" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_c3tu1"] +[ext_resource type="Texture2D" uid="uid://c4iwtbyehtps2" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_edtti"] +[ext_resource type="Texture2D" uid="uid://d4bfok8606fmv" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_efnr7"] +[ext_resource type="Texture2D" uid="uid://c7p0nhy4sh5wf" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_i2tmb"] +[ext_resource type="Texture2D" uid="uid://cg8r4ijx7641s" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="16_isy1r"] +[ext_resource type="Texture2D" uid="uid://b81pjmldtt10s" path="res://assets/images/gamepad/xbox/xbox_button_color_y.svg" id="17_jr7kr"] +[ext_resource type="Texture2D" uid="uid://ceiru5s4y3ghy" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="18_4llry"] +[ext_resource type="Texture2D" uid="uid://dg7xaotlsxoix" path="res://assets/images/gamepad/rog-ally/ally_button_quick.svg" id="18_gra38"] +[ext_resource type="Texture2D" uid="uid://bjq76f76quyry" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_rgqul"] +[ext_resource type="Texture2D" uid="uid://5q4tjwvf7b8a" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_sg7ew"] +[ext_resource type="Texture2D" uid="uid://bwqadttdisdkl" path="res://assets/images/gamepad/rog-ally/ally_button_m1.svg" id="21_4aapa"] +[ext_resource type="Texture2D" uid="uid://cu3ptt3kea56h" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_rfair"] +[ext_resource type="Texture2D" uid="uid://s4x7rp50eub5" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_fvge1"] +[ext_resource type="Texture2D" uid="uid://kfqb1dr0735v" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_p32gu"] +[ext_resource type="Texture2D" uid="uid://o0xrox1l5sae" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_2bmwh"] +[ext_resource type="Texture2D" uid="uid://brt5gm71iv3u4" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="25_1q8wd"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="26_3rfes"] -[ext_resource type="Texture2D" uid="uid://dyemqkvdtk43e" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="29_10lmq"] -[ext_resource type="Texture2D" uid="uid://cu0vttshpc7ui" path="res://assets/images/gamepad/rog-ally/ally_button_view.svg" id="29_ecr88"] -[ext_resource type="Texture2D" uid="uid://csxxwf7pgahkb" path="res://assets/images/gamepad/rog-ally/ally_button_menu.svg" id="31_d6bvl"] -[ext_resource type="Texture2D" uid="uid://cuvx61nakiaoi" path="res://assets/images/gamepad/xbox/xbox_button_color_x.svg" id="31_dqvdl"] +[ext_resource type="Texture2D" uid="uid://cxvxwgt1vox0" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="29_10lmq"] +[ext_resource type="Texture2D" uid="uid://bcrthlw3xno0p" path="res://assets/images/gamepad/rog-ally/ally_button_view.svg" id="29_ecr88"] +[ext_resource type="Texture2D" uid="uid://d4gi4bucbgyhv" path="res://assets/images/gamepad/rog-ally/ally_button_menu.svg" id="31_d6bvl"] +[ext_resource type="Texture2D" uid="uid://ojrh7msglai0" path="res://assets/images/gamepad/xbox/xbox_button_color_x.svg" id="31_dqvdl"] [resource] script = ExtResource("26_3rfes") diff --git a/assets/gamepad/icon_mappings/steam.tres b/assets/gamepad/icon_mappings/steam.tres index 1afab2899..ce078b01b 100644 --- a/assets/gamepad/icon_mappings/steam.tres +++ b/assets/gamepad/icon_mappings/steam.tres @@ -1,29 +1,29 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=25 format=3 uid="uid://bgm0ymdturbr6"] -[ext_resource type="Texture2D" uid="uid://c8b8706k0iryj" path="res://assets/images/gamepad/steam/controller_steam.svg" id="1_6gggn"] +[ext_resource type="Texture2D" uid="uid://cst2i5us0vdjy" path="res://assets/images/gamepad/steam/controller_steam.svg" id="1_6gggn"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="1_vsxmp"] -[ext_resource type="Texture2D" uid="uid://dnia1n0cogcvv" path="res://assets/images/gamepad/steam/steam_dpad.svg" id="2_dr285"] -[ext_resource type="Texture2D" uid="uid://b36ese18p42qf" path="res://assets/images/gamepad/steam/steam_dpad_down.svg" id="3_614i2"] -[ext_resource type="Texture2D" uid="uid://curpocxiiytm1" path="res://assets/images/gamepad/steam/steam_dpad_left.svg" id="4_joten"] -[ext_resource type="Texture2D" uid="uid://bpws8otkr23ss" path="res://assets/images/gamepad/steam/steam_dpad_right.svg" id="5_iyjxe"] -[ext_resource type="Texture2D" uid="uid://cmlvutjr6mn8r" path="res://assets/images/gamepad/steam/steam_dpad_up.svg" id="6_khnqq"] -[ext_resource type="Texture2D" uid="uid://c435jhogs3n8o" path="res://assets/images/gamepad/steam/steam_button_color_b.svg" id="7_g8gma"] -[ext_resource type="Texture2D" uid="uid://com8m05m2uqco" path="res://assets/images/gamepad/steam/steam_button_lp.svg" id="8_6hxdr"] -[ext_resource type="Texture2D" uid="uid://b8kll7wj3mpw5" path="res://assets/images/gamepad/steamdeck/steamdeck_button_guide.svg" id="8_qwegv"] -[ext_resource type="Texture2D" uid="uid://b251los4ey64w" path="res://assets/images/gamepad/steam/steam_stick.svg" id="9_0stxd"] -[ext_resource type="Texture2D" uid="uid://01a6rkyglcse" path="res://assets/images/gamepad/steam/steam_button_color_y.svg" id="9_tbfyw"] -[ext_resource type="Texture2D" uid="uid://cer0ys0nbh0no" path="res://assets/images/gamepad/steam/steam_button_rp.svg" id="10_c0nl7"] -[ext_resource type="Texture2D" uid="uid://dipbfvbdnieqv" path="res://assets/images/gamepad/steam/steam_stick_down.svg" id="10_emyma"] -[ext_resource type="Texture2D" uid="uid://7jx3hwdcd28t" path="res://assets/images/gamepad/steam/steam_lg.svg" id="10_lb40s"] -[ext_resource type="Texture2D" uid="uid://cc3la68tqkeu7" path="res://assets/images/gamepad/steam/steam_stick_l_press.svg" id="10_uvdib"] -[ext_resource type="Texture2D" uid="uid://b7vfsacm21uvc" path="res://assets/images/gamepad/steam/steam_stick_left.svg" id="11_bq8g3"] -[ext_resource type="Texture2D" uid="uid://jfjsyxvyctbk" path="res://assets/images/gamepad/steam/steam_stick_right.svg" id="12_8u65p"] -[ext_resource type="Texture2D" uid="uid://dpgjaie64o2j7" path="res://assets/images/gamepad/steam/steam_button_back_icon.svg" id="12_gac83"] -[ext_resource type="Texture2D" uid="uid://bpkm2swtktdf0" path="res://assets/images/gamepad/steam/steam_button_color_a.svg" id="13_vysxh"] -[ext_resource type="Texture2D" uid="uid://c10buq0dndeb8" path="res://assets/images/gamepad/steam/steam_stick_up.svg" id="13_yhcf0"] -[ext_resource type="Texture2D" uid="uid://gm2fk8y4xrig" path="res://assets/images/gamepad/steam/steam_button_color_x.svg" id="14_l277p"] -[ext_resource type="Texture2D" uid="uid://bsxofaj5ietuc" path="res://assets/images/gamepad/steam/steam_rg.svg" id="19_dvgk4"] -[ext_resource type="Texture2D" uid="uid://drhbwe1m16fx7" path="res://assets/images/gamepad/steam/steam_button_start_icon.svg" id="19_mlqpj"] +[ext_resource type="Texture2D" uid="uid://dr4giue83b7ly" path="res://assets/images/gamepad/steam/steam_dpad.svg" id="2_dr285"] +[ext_resource type="Texture2D" uid="uid://hjxtuq87i2s8" path="res://assets/images/gamepad/steam/steam_dpad_down.svg" id="3_614i2"] +[ext_resource type="Texture2D" uid="uid://dlt00cmdhrufc" path="res://assets/images/gamepad/steam/steam_dpad_left.svg" id="4_joten"] +[ext_resource type="Texture2D" uid="uid://dwopaos8mykma" path="res://assets/images/gamepad/steam/steam_dpad_right.svg" id="5_iyjxe"] +[ext_resource type="Texture2D" uid="uid://u6ehkqpdr5dn" path="res://assets/images/gamepad/steam/steam_dpad_up.svg" id="6_khnqq"] +[ext_resource type="Texture2D" uid="uid://b730xukgcsh2k" path="res://assets/images/gamepad/steam/steam_button_color_b.svg" id="7_g8gma"] +[ext_resource type="Texture2D" uid="uid://mphqdrq0v6nv" path="res://assets/images/gamepad/steam/steam_button_lp.svg" id="8_6hxdr"] +[ext_resource type="Texture2D" uid="uid://7obnebcrnike" path="res://assets/images/gamepad/steamdeck/steamdeck_button_guide.svg" id="8_qwegv"] +[ext_resource type="Texture2D" uid="uid://bm0b6klr4s4me" path="res://assets/images/gamepad/steam/steam_stick.svg" id="9_0stxd"] +[ext_resource type="Texture2D" uid="uid://dl6mkuxwnau6c" path="res://assets/images/gamepad/steam/steam_button_color_y.svg" id="9_tbfyw"] +[ext_resource type="Texture2D" uid="uid://djidib24hy7sf" path="res://assets/images/gamepad/steam/steam_button_rp.svg" id="10_c0nl7"] +[ext_resource type="Texture2D" uid="uid://b38225lpoxnad" path="res://assets/images/gamepad/steam/steam_stick_down.svg" id="10_emyma"] +[ext_resource type="Texture2D" uid="uid://bxjvcfty4u5tr" path="res://assets/images/gamepad/steam/steam_lg.svg" id="10_lb40s"] +[ext_resource type="Texture2D" uid="uid://bp3y2i8nclwov" path="res://assets/images/gamepad/steam/steam_stick_l_press.svg" id="10_uvdib"] +[ext_resource type="Texture2D" uid="uid://cwe6414vhvsbm" path="res://assets/images/gamepad/steam/steam_stick_left.svg" id="11_bq8g3"] +[ext_resource type="Texture2D" uid="uid://c5c5rvhs8260" path="res://assets/images/gamepad/steam/steam_stick_right.svg" id="12_8u65p"] +[ext_resource type="Texture2D" uid="uid://cmnjax5k8juxb" path="res://assets/images/gamepad/steam/steam_button_back_icon.svg" id="12_gac83"] +[ext_resource type="Texture2D" uid="uid://1q1vr24dkc26" path="res://assets/images/gamepad/steam/steam_button_color_a.svg" id="13_vysxh"] +[ext_resource type="Texture2D" uid="uid://h0lyv05rajef" path="res://assets/images/gamepad/steam/steam_stick_up.svg" id="13_yhcf0"] +[ext_resource type="Texture2D" uid="uid://ctvb5ylheu2nm" path="res://assets/images/gamepad/steam/steam_button_color_x.svg" id="14_l277p"] +[ext_resource type="Texture2D" uid="uid://otfjb4w8k4vh" path="res://assets/images/gamepad/steam/steam_rg.svg" id="19_dvgk4"] +[ext_resource type="Texture2D" uid="uid://4lase2m3005j" path="res://assets/images/gamepad/steam/steam_button_start_icon.svg" id="19_mlqpj"] [resource] script = ExtResource("1_vsxmp") diff --git a/assets/gamepad/icon_mappings/steamdeck.tres b/assets/gamepad/icon_mappings/steamdeck.tres index fdaeeb7c9..652b94404 100644 --- a/assets/gamepad/icon_mappings/steamdeck.tres +++ b/assets/gamepad/icon_mappings/steamdeck.tres @@ -1,42 +1,42 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=38 format=3 uid="uid://dmck8es3nhbqt"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="1_xffj2"] -[ext_resource type="Texture2D" uid="uid://cvyo2q5qjpamv" path="res://assets/images/gamepad/steamdeck/diagram.png" id="1_xtoy0"] -[ext_resource type="Texture2D" uid="uid://bdijnj7q510fb" path="res://assets/images/gamepad/steamdeck/steamdeck_button_b.svg" id="2_8eg7f"] -[ext_resource type="Texture2D" uid="uid://d1vwjidwwl6g6" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad.svg" id="2_8t15o"] -[ext_resource type="Texture2D" uid="uid://ck0gwj0t1nlnb" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad_down.svg" id="3_d8lqu"] -[ext_resource type="Texture2D" uid="uid://b8kll7wj3mpw5" path="res://assets/images/gamepad/steamdeck/steamdeck_button_guide.svg" id="3_nvde6"] -[ext_resource type="Texture2D" uid="uid://2hvt2s10h4oh" path="res://assets/images/gamepad/steamdeck/steamdeck_button_l4.svg" id="4_dvvlm"] -[ext_resource type="Texture2D" uid="uid://bw3o757gon60n" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad_left.svg" id="4_vffs6"] -[ext_resource type="Texture2D" uid="uid://bsqe51ljqa1wl" path="res://assets/images/gamepad/steamdeck/steamdeck_button_l5.svg" id="5_qlfxn"] -[ext_resource type="Texture2D" uid="uid://cxe85qvlcxt8k" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad_right.svg" id="5_v1qd7"] -[ext_resource type="Texture2D" uid="uid://iuf1gwubgrkr" path="res://assets/images/gamepad/steamdeck/steamdeck_button_l1.svg" id="6_4nclm"] -[ext_resource type="Texture2D" uid="uid://d3bk0otxo2u0t" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad_up.svg" id="6_dpf6l"] -[ext_resource type="Texture2D" uid="uid://dxpamvls1wtr4" path="res://assets/images/gamepad/steamdeck/steamdeck_button_l2.svg" id="7_j67qe"] -[ext_resource type="Texture2D" uid="uid://b14twfve0lmf6" path="res://assets/images/gamepad/steamdeck/steamdeck_button_y.svg" id="8_0rfpr"] -[ext_resource type="Texture2D" uid="uid://byy323swr1naa" path="res://assets/images/gamepad/steamdeck/steamdeck_button_quickaccess.svg" id="8_owjyj"] -[ext_resource type="Texture2D" uid="uid://dmfw87xx6phhc" path="res://assets/images/gamepad/steamdeck/steamdeck_button_r4.svg" id="9_2ijj3"] -[ext_resource type="Texture2D" uid="uid://d3ocywhdiyjvk" path="res://assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all.svg" id="9_3q0xs"] -[ext_resource type="Texture2D" uid="uid://bre5c38fo7hn5" path="res://assets/images/gamepad/steamdeck/steamdeck_button_r5.svg" id="10_sihsr"] -[ext_resource type="Texture2D" uid="uid://cvm6un7wher3" path="res://assets/images/gamepad/steamdeck/steamdeck_button_r1.svg" id="11_sg6p3"] -[ext_resource type="Texture2D" uid="uid://coaaq1r1uiyx2" path="res://assets/images/gamepad/steamdeck/steamdeck_button_r2.svg" id="12_kfdop"] -[ext_resource type="Texture2D" uid="uid://c6holuruhn6gs" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l.svg" id="12_w0n16"] -[ext_resource type="Texture2D" uid="uid://dcqhk1jgepeq0" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_press.svg" id="13_f2qfl"] -[ext_resource type="Texture2D" uid="uid://2ymxt6fjxgbg" path="res://assets/images/gamepad/steamdeck/steamdeck_button_a.svg" id="14_hcyjc"] -[ext_resource type="Texture2D" uid="uid://bkuoy3851ps81" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_down.svg" id="14_nkcal"] -[ext_resource type="Texture2D" uid="uid://c3tfburs4gc2e" path="res://assets/images/gamepad/steamdeck/steamdeck_button_view.svg" id="15_1fovd"] -[ext_resource type="Texture2D" uid="uid://dfreqwma7axjm" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_left.svg" id="15_1p0bb"] -[ext_resource type="Texture2D" uid="uid://dhwuj34jww33w" path="res://assets/images/gamepad/steamdeck/steamdeck_button_options.svg" id="15_m6xmt"] -[ext_resource type="Texture2D" uid="uid://dpxu821a7oj4s" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_right.svg" id="16_tmt1u"] -[ext_resource type="Texture2D" uid="uid://dgpc6aewueu74" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_up.svg" id="17_j3c4l"] -[ext_resource type="Texture2D" uid="uid://dedkcpdkjkfgi" path="res://assets/images/gamepad/steamdeck/steamdeck_button_x.svg" id="18_ekemo"] -[ext_resource type="Texture2D" uid="uid://b5smg46hvo3en" path="res://assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all.svg" id="22_4kkvh"] -[ext_resource type="Texture2D" uid="uid://c0vodcvwmd4mq" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r.svg" id="24_4sn7m"] -[ext_resource type="Texture2D" uid="uid://bflve4wmy5l7p" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_press.svg" id="25_jya3g"] -[ext_resource type="Texture2D" uid="uid://drhc3nti5y4v2" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_down.svg" id="26_koslu"] -[ext_resource type="Texture2D" uid="uid://bf050ow2fqwl5" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_left.svg" id="27_56oh1"] -[ext_resource type="Texture2D" uid="uid://deujxqeovbjs8" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_right.svg" id="28_r81kg"] -[ext_resource type="Texture2D" uid="uid://c23ukiudnt5vx" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_up.svg" id="29_x2rgv"] +[ext_resource type="Texture2D" uid="uid://b4w5tmfci1toh" path="res://assets/images/gamepad/steamdeck/diagram.png" id="1_xtoy0"] +[ext_resource type="Texture2D" uid="uid://bi6jcsdcp4e77" path="res://assets/images/gamepad/steamdeck/steamdeck_button_b.svg" id="2_8eg7f"] +[ext_resource type="Texture2D" uid="uid://dprhshnof4mpt" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad.svg" id="2_8t15o"] +[ext_resource type="Texture2D" uid="uid://bc1anbo6va8c2" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad_down.svg" id="3_d8lqu"] +[ext_resource type="Texture2D" uid="uid://7obnebcrnike" path="res://assets/images/gamepad/steamdeck/steamdeck_button_guide.svg" id="3_nvde6"] +[ext_resource type="Texture2D" uid="uid://cru5rdx15j4ts" path="res://assets/images/gamepad/steamdeck/steamdeck_button_l4.svg" id="4_dvvlm"] +[ext_resource type="Texture2D" uid="uid://ggcsc810x7ri" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad_left.svg" id="4_vffs6"] +[ext_resource type="Texture2D" uid="uid://iby8tq0amo8b" path="res://assets/images/gamepad/steamdeck/steamdeck_button_l5.svg" id="5_qlfxn"] +[ext_resource type="Texture2D" uid="uid://tc7bgm3gcyqd" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad_right.svg" id="5_v1qd7"] +[ext_resource type="Texture2D" uid="uid://scfbg0yygqka" path="res://assets/images/gamepad/steamdeck/steamdeck_button_l1.svg" id="6_4nclm"] +[ext_resource type="Texture2D" uid="uid://dfqvij0d87gq8" path="res://assets/images/gamepad/steamdeck/steamdeck_dpad_up.svg" id="6_dpf6l"] +[ext_resource type="Texture2D" uid="uid://k5ca2xyvchl2" path="res://assets/images/gamepad/steamdeck/steamdeck_button_l2.svg" id="7_j67qe"] +[ext_resource type="Texture2D" uid="uid://d238yav03b5tk" path="res://assets/images/gamepad/steamdeck/steamdeck_button_y.svg" id="8_0rfpr"] +[ext_resource type="Texture2D" uid="uid://c8huhb14jh1ba" path="res://assets/images/gamepad/steamdeck/steamdeck_button_quickaccess.svg" id="8_owjyj"] +[ext_resource type="Texture2D" uid="uid://duirfrp1swep8" path="res://assets/images/gamepad/steamdeck/steamdeck_button_r4.svg" id="9_2ijj3"] +[ext_resource type="Texture2D" uid="uid://dvioogs1h44wj" path="res://assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all.svg" id="9_3q0xs"] +[ext_resource type="Texture2D" uid="uid://b5dvxnn2ddhar" path="res://assets/images/gamepad/steamdeck/steamdeck_button_r5.svg" id="10_sihsr"] +[ext_resource type="Texture2D" uid="uid://cdx74dhosgpi1" path="res://assets/images/gamepad/steamdeck/steamdeck_button_r1.svg" id="11_sg6p3"] +[ext_resource type="Texture2D" uid="uid://dw7hjtcfaepx" path="res://assets/images/gamepad/steamdeck/steamdeck_button_r2.svg" id="12_kfdop"] +[ext_resource type="Texture2D" uid="uid://cljgsi6rwed5y" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l.svg" id="12_w0n16"] +[ext_resource type="Texture2D" uid="uid://fk70akhspjqw" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_press.svg" id="13_f2qfl"] +[ext_resource type="Texture2D" uid="uid://glixduifqfyc" path="res://assets/images/gamepad/steamdeck/steamdeck_button_a.svg" id="14_hcyjc"] +[ext_resource type="Texture2D" uid="uid://b4t2lfdlk8v1l" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_down.svg" id="14_nkcal"] +[ext_resource type="Texture2D" uid="uid://b3g1sl3gxac3l" path="res://assets/images/gamepad/steamdeck/steamdeck_button_view.svg" id="15_1fovd"] +[ext_resource type="Texture2D" uid="uid://dgq0pu6stgp0n" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_left.svg" id="15_1p0bb"] +[ext_resource type="Texture2D" uid="uid://i37o7qma5l0m" path="res://assets/images/gamepad/steamdeck/steamdeck_button_options.svg" id="15_m6xmt"] +[ext_resource type="Texture2D" uid="uid://cg673id36yufn" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_right.svg" id="16_tmt1u"] +[ext_resource type="Texture2D" uid="uid://clerkwh0rv4qm" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_l_up.svg" id="17_j3c4l"] +[ext_resource type="Texture2D" uid="uid://du2ljto5eauh5" path="res://assets/images/gamepad/steamdeck/steamdeck_button_x.svg" id="18_ekemo"] +[ext_resource type="Texture2D" uid="uid://b006fo2wqcu8w" path="res://assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all.svg" id="22_4kkvh"] +[ext_resource type="Texture2D" uid="uid://dm1cw0o0kpajg" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r.svg" id="24_4sn7m"] +[ext_resource type="Texture2D" uid="uid://vb6n5l5ajq0t" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_press.svg" id="25_jya3g"] +[ext_resource type="Texture2D" uid="uid://dcyao1pr3jtls" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_down.svg" id="26_koslu"] +[ext_resource type="Texture2D" uid="uid://dk34rpf2xmwpp" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_left.svg" id="27_56oh1"] +[ext_resource type="Texture2D" uid="uid://jpl2xe1upygm" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_right.svg" id="28_r81kg"] +[ext_resource type="Texture2D" uid="uid://8ioc8gidakfr" path="res://assets/images/gamepad/steamdeck/steamdeck_stick_r_up.svg" id="29_x2rgv"] [resource] script = ExtResource("1_xffj2") diff --git a/assets/gamepad/icon_mappings/switch.tres b/assets/gamepad/icon_mappings/switch.tres index 0a9f5b012..116235c6e 100644 --- a/assets/gamepad/icon_mappings/switch.tres +++ b/assets/gamepad/icon_mappings/switch.tres @@ -1,36 +1,36 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=32 format=3 uid="uid://behwrymqjqtvv"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="1_s4hvo"] -[ext_resource type="Texture2D" uid="uid://jv2vbh18muy8" path="res://assets/images/gamepad/switch/switch_dpad.svg" id="1_ydeli"] -[ext_resource type="Texture2D" uid="uid://beb058tm84w5j" path="res://assets/images/gamepad/switch/switch_dpad_down.svg" id="2_tw4ge"] -[ext_resource type="Texture2D" uid="uid://dnp6tc41r8xi3" path="res://assets/images/gamepad/switch/switch_dpad_left.svg" id="3_8rr1p"] -[ext_resource type="Texture2D" uid="uid://u7ity54x4vx3" path="res://assets/images/gamepad/switch/switch_dpad_right.svg" id="4_pppsw"] -[ext_resource type="Texture2D" uid="uid://bi8viiuisfuxh" path="res://assets/images/gamepad/switch/switch_dpad_up.svg" id="5_c4oby"] -[ext_resource type="Texture2D" uid="uid://dec8wg71j58h1" path="res://assets/images/gamepad/switch/switch_button_a.svg" id="6_riih2"] -[ext_resource type="Texture2D" uid="uid://b3v8gbuc0knsr" path="res://assets/images/gamepad/switch/switch_button_home.svg" id="7_v8spc"] -[ext_resource type="Texture2D" uid="uid://by4xd4qq5dspy" path="res://assets/images/gamepad/switch/switch_joycon_left_rotate.svg" id="8_mj4ae"] -[ext_resource type="Texture2D" uid="uid://b57k4ko26lm6" path="res://assets/images/gamepad/switch/switch_button_l.svg" id="9_ryb7o"] -[ext_resource type="Texture2D" uid="uid://bgkt5t7mmbhts" path="res://assets/images/gamepad/switch/switch_stick_l.svg" id="10_jcwsc"] -[ext_resource type="Texture2D" uid="uid://ca4fcl2jsx62k" path="res://assets/images/gamepad/switch/switch_stick_l_press.svg" id="11_tcwua"] -[ext_resource type="Texture2D" uid="uid://bvnpkij77axxu" path="res://assets/images/gamepad/switch/switch_stick_l_down.svg" id="12_dmidv"] -[ext_resource type="Texture2D" uid="uid://bbad7xmuveel8" path="res://assets/images/gamepad/switch/switch_stick_l_left.svg" id="13_g56k4"] -[ext_resource type="Texture2D" uid="uid://lpy6n3raxq2" path="res://assets/images/gamepad/switch/switch_stick_l_right.svg" id="14_0ydal"] -[ext_resource type="Texture2D" uid="uid://btrirhorp6y57" path="res://assets/images/gamepad/switch/switch_stick_l_up.svg" id="15_5vsxb"] -[ext_resource type="Texture2D" uid="uid://dsa8m2vfvtx0q" path="res://assets/images/gamepad/switch/switch_button_zl.svg" id="16_4pllh"] -[ext_resource type="Texture2D" uid="uid://b6wkct2qwo0ul" path="res://assets/images/gamepad/switch/switch_button_x.svg" id="17_n3inh"] -[ext_resource type="Texture2D" uid="uid://bu2naacuis8k8" path="res://assets/images/gamepad/switch/switch_button_r.svg" id="18_45ryk"] -[ext_resource type="Texture2D" uid="uid://c1fas8o38r02p" path="res://assets/images/gamepad/switch/switch_stick_r.svg" id="19_gqh2w"] -[ext_resource type="Texture2D" uid="uid://dop088ppjtx7r" path="res://assets/images/gamepad/switch/switch_stick_r_press.svg" id="20_rugaw"] -[ext_resource type="Texture2D" uid="uid://dqrqw3lv3kl3e" path="res://assets/images/gamepad/switch/switch_stick_r_down.svg" id="21_susn6"] -[ext_resource type="Texture2D" uid="uid://bcstmmysk3e1" path="res://assets/images/gamepad/switch/switch_stick_r_left.svg" id="22_xgn3r"] -[ext_resource type="Texture2D" uid="uid://domi3pblhpgqy" path="res://assets/images/gamepad/switch/switch_stick_r_right.svg" id="23_stpgt"] -[ext_resource type="Texture2D" uid="uid://dtkumw37qfis8" path="res://assets/images/gamepad/switch/switch_stick_r_up.svg" id="24_ranay"] -[ext_resource type="Texture2D" uid="uid://cutjyk72c0d1" path="res://assets/images/gamepad/switch/switch_button_zr.svg" id="25_4bdkg"] -[ext_resource type="Texture2D" uid="uid://baeygxurw7dn1" path="res://assets/images/gamepad/switch/switch_button_sync.svg" id="26_ppthb"] -[ext_resource type="Texture2D" uid="uid://crl6hqey1xqfd" path="res://assets/images/gamepad/switch/switch_button_minus.svg" id="28_i4oi2"] -[ext_resource type="Texture2D" uid="uid://8o3g4dcunk87" path="res://assets/images/gamepad/switch/switch_button_b.svg" id="29_qxbwx"] -[ext_resource type="Texture2D" uid="uid://dugmdwvujsrk4" path="res://assets/images/gamepad/switch/switch_button_plus.svg" id="30_gmsl2"] -[ext_resource type="Texture2D" uid="uid://djvoe5nud1i0w" path="res://assets/images/gamepad/switch/switch_button_y.svg" id="31_7qfqf"] +[ext_resource type="Texture2D" uid="uid://b735i56yjhict" path="res://assets/images/gamepad/switch/switch_dpad.svg" id="1_ydeli"] +[ext_resource type="Texture2D" uid="uid://drfroynux1b3f" path="res://assets/images/gamepad/switch/switch_dpad_down.svg" id="2_tw4ge"] +[ext_resource type="Texture2D" uid="uid://e3db7ixs8tjk" path="res://assets/images/gamepad/switch/switch_dpad_left.svg" id="3_8rr1p"] +[ext_resource type="Texture2D" uid="uid://vdro02bqhoxn" path="res://assets/images/gamepad/switch/switch_dpad_right.svg" id="4_pppsw"] +[ext_resource type="Texture2D" uid="uid://hwsqjx3falhf" path="res://assets/images/gamepad/switch/switch_dpad_up.svg" id="5_c4oby"] +[ext_resource type="Texture2D" uid="uid://dsnnac0xk0tfl" path="res://assets/images/gamepad/switch/switch_button_a.svg" id="6_riih2"] +[ext_resource type="Texture2D" uid="uid://dicj4tpxknqai" path="res://assets/images/gamepad/switch/switch_button_home.svg" id="7_v8spc"] +[ext_resource type="Texture2D" uid="uid://cda8etpu56yjn" path="res://assets/images/gamepad/switch/switch_joycon_left_rotate.svg" id="8_mj4ae"] +[ext_resource type="Texture2D" uid="uid://d2ho4b7w2mdlb" path="res://assets/images/gamepad/switch/switch_button_l.svg" id="9_ryb7o"] +[ext_resource type="Texture2D" uid="uid://ruhfjlhtlik3" path="res://assets/images/gamepad/switch/switch_stick_l.svg" id="10_jcwsc"] +[ext_resource type="Texture2D" uid="uid://d1xnwfo4kwgwe" path="res://assets/images/gamepad/switch/switch_stick_l_press.svg" id="11_tcwua"] +[ext_resource type="Texture2D" uid="uid://ri6vpuerb2jn" path="res://assets/images/gamepad/switch/switch_stick_l_down.svg" id="12_dmidv"] +[ext_resource type="Texture2D" uid="uid://dvdwtqdrgcx55" path="res://assets/images/gamepad/switch/switch_stick_l_left.svg" id="13_g56k4"] +[ext_resource type="Texture2D" uid="uid://ckdjvbxmun3hk" path="res://assets/images/gamepad/switch/switch_stick_l_right.svg" id="14_0ydal"] +[ext_resource type="Texture2D" uid="uid://c54734rwsvi5y" path="res://assets/images/gamepad/switch/switch_stick_l_up.svg" id="15_5vsxb"] +[ext_resource type="Texture2D" uid="uid://ysw3f4s0wckl" path="res://assets/images/gamepad/switch/switch_button_zl.svg" id="16_4pllh"] +[ext_resource type="Texture2D" uid="uid://d0nkpy8tk3f28" path="res://assets/images/gamepad/switch/switch_button_x.svg" id="17_n3inh"] +[ext_resource type="Texture2D" uid="uid://dh8gskvlf1y4n" path="res://assets/images/gamepad/switch/switch_button_r.svg" id="18_45ryk"] +[ext_resource type="Texture2D" uid="uid://bnr0q7kupfac8" path="res://assets/images/gamepad/switch/switch_stick_r.svg" id="19_gqh2w"] +[ext_resource type="Texture2D" uid="uid://dalb0538ux4qy" path="res://assets/images/gamepad/switch/switch_stick_r_press.svg" id="20_rugaw"] +[ext_resource type="Texture2D" uid="uid://c37v6pn2p4pa7" path="res://assets/images/gamepad/switch/switch_stick_r_down.svg" id="21_susn6"] +[ext_resource type="Texture2D" uid="uid://d0s0cx6bqiovg" path="res://assets/images/gamepad/switch/switch_stick_r_left.svg" id="22_xgn3r"] +[ext_resource type="Texture2D" uid="uid://dxycp3mj54mwc" path="res://assets/images/gamepad/switch/switch_stick_r_right.svg" id="23_stpgt"] +[ext_resource type="Texture2D" uid="uid://be6s7r6y3ndty" path="res://assets/images/gamepad/switch/switch_stick_r_up.svg" id="24_ranay"] +[ext_resource type="Texture2D" uid="uid://cwkd4oyqs445d" path="res://assets/images/gamepad/switch/switch_button_zr.svg" id="25_4bdkg"] +[ext_resource type="Texture2D" uid="uid://brwy28sjw2pe8" path="res://assets/images/gamepad/switch/switch_button_sync.svg" id="26_ppthb"] +[ext_resource type="Texture2D" uid="uid://f3dabljd057e" path="res://assets/images/gamepad/switch/switch_button_minus.svg" id="28_i4oi2"] +[ext_resource type="Texture2D" uid="uid://b21ssea6yir56" path="res://assets/images/gamepad/switch/switch_button_b.svg" id="29_qxbwx"] +[ext_resource type="Texture2D" uid="uid://rgpis0qqwyn3" path="res://assets/images/gamepad/switch/switch_button_plus.svg" id="30_gmsl2"] +[ext_resource type="Texture2D" uid="uid://ckwf0ko1ch08t" path="res://assets/images/gamepad/switch/switch_button_y.svg" id="31_7qfqf"] [resource] script = ExtResource("1_s4hvo") diff --git a/assets/gamepad/icon_mappings/xbox360.tres b/assets/gamepad/icon_mappings/xbox360.tres index e99683802..20672899c 100644 --- a/assets/gamepad/icon_mappings/xbox360.tres +++ b/assets/gamepad/icon_mappings/xbox360.tres @@ -1,41 +1,41 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=31 format=3 uid="uid://bx1o03i6woyis"] -[ext_resource type="Texture2D" uid="uid://bie2sf8kjawr" path="res://assets/images/gamepad/xbox/XboxOne_Diagram.png" id="1_ivcim"] -[ext_resource type="Texture2D" uid="uid://dn8i3gpeijgu1" path="res://assets/images/gamepad/xbox/xbox_dpad_round.svg" id="2_2qico"] -[ext_resource type="Texture2D" uid="uid://bqho0a0sr6u1j" path="res://assets/images/gamepad/xbox/xbox_button_color_b.svg" id="2_scm2e"] -[ext_resource type="Texture2D" uid="uid://bcoajyq8st62e" path="res://assets/images/gamepad/xbox/xbox_guide.svg" id="3_rcstt"] -[ext_resource type="Texture2D" uid="uid://dwv3f88h7uaq7" path="res://assets/images/gamepad/xbox/xbox_dpad_round_down.svg" id="3_x7c5e"] -[ext_resource type="Texture2D" uid="uid://fmnwuu2ym0m8" path="res://assets/images/gamepad/xbox/xbox_dpad_round_left.svg" id="4_dtqoy"] -[ext_resource type="Texture2D" uid="uid://ddpudi00yulrx" path="res://assets/images/gamepad/xbox/xbox_button_color_y.svg" id="4_yf7fg"] -[ext_resource type="Texture2D" uid="uid://cboy1enwvdeoy" path="res://assets/images/gamepad/xbox/xbox_dpad_round_right.svg" id="5_l43jp"] +[ext_resource type="Texture2D" uid="uid://cibfy4ssigmqm" path="res://assets/images/gamepad/xbox/XboxOne_Diagram_Simple.png" id="1_vdrwh"] +[ext_resource type="Texture2D" uid="uid://n4t36xrl15ux" path="res://assets/images/gamepad/xbox/xbox_dpad_round.svg" id="2_2qico"] +[ext_resource type="Texture2D" uid="uid://dfby3iiksln2e" path="res://assets/images/gamepad/xbox/xbox_button_color_b.svg" id="2_scm2e"] +[ext_resource type="Texture2D" uid="uid://dq7kibukggise" path="res://assets/images/gamepad/xbox/xbox_guide.svg" id="3_rcstt"] +[ext_resource type="Texture2D" uid="uid://bxovdah6vnpsn" path="res://assets/images/gamepad/xbox/xbox_dpad_round_down.svg" id="3_x7c5e"] +[ext_resource type="Texture2D" uid="uid://qhwj81sv67l4" path="res://assets/images/gamepad/xbox/xbox_dpad_round_left.svg" id="4_dtqoy"] +[ext_resource type="Texture2D" uid="uid://b81pjmldtt10s" path="res://assets/images/gamepad/xbox/xbox_button_color_y.svg" id="4_yf7fg"] +[ext_resource type="Texture2D" uid="uid://b1uo5130s6gfw" path="res://assets/images/gamepad/xbox/xbox_dpad_round_right.svg" id="5_l43jp"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="5_qsuka"] -[ext_resource type="Texture2D" uid="uid://ba64eulr61afh" path="res://assets/images/gamepad/xbox/xbox_dpad_round_up.svg" id="6_hdxil"] -[ext_resource type="Texture2D" uid="uid://dyemqkvdtk43e" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="6_kekiu"] -[ext_resource type="Texture2D" uid="uid://cuvx61nakiaoi" path="res://assets/images/gamepad/xbox/xbox_button_color_x.svg" id="7_5gtvm"] -[ext_resource type="Texture2D" uid="uid://mtd8tfnbotgc" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_5mjlo"] -[ext_resource type="Texture2D" uid="uid://bdqfg27h5o836" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_h038p"] -[ext_resource type="Texture2D" uid="uid://dvv5grt45cows" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_87ajn"] -[ext_resource type="Texture2D" uid="uid://u3jonqoafyg6" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="12_2l3bb"] -[ext_resource type="Texture2D" uid="uid://dcumk3oqkyy7g" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_kp3w4"] -[ext_resource type="Texture2D" uid="uid://dooej262t4g0x" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_8wccm"] -[ext_resource type="Texture2D" uid="uid://co3kg331knfu7" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="14_106m0"] -[ext_resource type="Texture2D" uid="uid://coltjh07der5" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_m6oqd"] -[ext_resource type="Texture2D" uid="uid://da8ojjv5vttou" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="15_1ae1p"] -[ext_resource type="Texture2D" uid="uid://b3xxbnyo2h20l" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_wqxdi"] -[ext_resource type="Texture2D" uid="uid://worovimrqapy" path="res://assets/images/gamepad/xbox/xbox_button_back_icon.svg" id="17_ewr7o"] -[ext_resource type="Texture2D" uid="uid://lyo4d0hfj4ut" path="res://assets/images/gamepad/xbox/xbox_button_start_icon.svg" id="19_5p4eh"] -[ext_resource type="Texture2D" uid="uid://dw15y6xiuthpe" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_p4shy"] -[ext_resource type="Texture2D" uid="uid://de2kcqeakp3q0" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_4oewr"] -[ext_resource type="Texture2D" uid="uid://d4iq63eypbf2s" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_r84kb"] -[ext_resource type="Texture2D" uid="uid://bus2l2lmd844f" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_s82sf"] -[ext_resource type="Texture2D" uid="uid://cokparjjmwy8c" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_nd0cx"] -[ext_resource type="Texture2D" uid="uid://jn543ff1ak1c" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_go4xp"] +[ext_resource type="Texture2D" uid="uid://dqk82r4up56mh" path="res://assets/images/gamepad/xbox/xbox_dpad_round_up.svg" id="6_hdxil"] +[ext_resource type="Texture2D" uid="uid://cxvxwgt1vox0" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="6_kekiu"] +[ext_resource type="Texture2D" uid="uid://ojrh7msglai0" path="res://assets/images/gamepad/xbox/xbox_button_color_x.svg" id="7_5gtvm"] +[ext_resource type="Texture2D" uid="uid://cwyi3if752xh6" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_5mjlo"] +[ext_resource type="Texture2D" uid="uid://dqx7njocf1aok" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_h038p"] +[ext_resource type="Texture2D" uid="uid://ckqaven4xmk16" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_87ajn"] +[ext_resource type="Texture2D" uid="uid://cg8r4ijx7641s" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="12_2l3bb"] +[ext_resource type="Texture2D" uid="uid://c1bfw2axbcg1q" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_kp3w4"] +[ext_resource type="Texture2D" uid="uid://c4iwtbyehtps2" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_8wccm"] +[ext_resource type="Texture2D" uid="uid://ceiru5s4y3ghy" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="14_106m0"] +[ext_resource type="Texture2D" uid="uid://d4bfok8606fmv" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_m6oqd"] +[ext_resource type="Texture2D" uid="uid://brt5gm71iv3u4" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="15_1ae1p"] +[ext_resource type="Texture2D" uid="uid://c7p0nhy4sh5wf" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_wqxdi"] +[ext_resource type="Texture2D" uid="uid://cwxfxkby7fk2a" path="res://assets/images/gamepad/xbox/xbox_button_back_icon.svg" id="17_ewr7o"] +[ext_resource type="Texture2D" uid="uid://c3v2ch2hw3mhm" path="res://assets/images/gamepad/xbox/xbox_button_start_icon.svg" id="19_5p4eh"] +[ext_resource type="Texture2D" uid="uid://bjq76f76quyry" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_p4shy"] +[ext_resource type="Texture2D" uid="uid://5q4tjwvf7b8a" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_4oewr"] +[ext_resource type="Texture2D" uid="uid://cu3ptt3kea56h" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_r84kb"] +[ext_resource type="Texture2D" uid="uid://s4x7rp50eub5" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_s82sf"] +[ext_resource type="Texture2D" uid="uid://kfqb1dr0735v" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_nd0cx"] +[ext_resource type="Texture2D" uid="uid://o0xrox1l5sae" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_go4xp"] [resource] script = ExtResource("5_qsuka") name = "XBox 360" device_names = PackedStringArray("Xbox 360 Controller", "Microsoft X-Box 360 pad") -diagram = ExtResource("1_ivcim") +diagram = ExtResource("1_vdrwh") north = ExtResource("4_yf7fg") south = ExtResource("6_kekiu") east = ExtResource("2_scm2e") diff --git a/assets/gamepad/icon_mappings/xboxone.tres b/assets/gamepad/icon_mappings/xboxone.tres index 99bd09f8e..d26d14e96 100644 --- a/assets/gamepad/icon_mappings/xboxone.tres +++ b/assets/gamepad/icon_mappings/xboxone.tres @@ -1,35 +1,35 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=31 format=3 uid="uid://005wjp70lsdu"] -[ext_resource type="Texture2D" uid="uid://bie2sf8kjawr" path="res://assets/images/gamepad/xbox/XboxOne_Diagram.png" id="1_7rqq5"] +[ext_resource type="Texture2D" uid="uid://ceyt88pt5sogo" path="res://assets/images/gamepad/xbox/XboxOne_Diagram.png" id="1_7rqq5"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="1_lckpp"] -[ext_resource type="Texture2D" uid="uid://cdhd2pnbunvy3" path="res://assets/images/gamepad/xbox/xbox_dpad.svg" id="2_7tdao"] -[ext_resource type="Texture2D" uid="uid://bju6gytlq2div" path="res://assets/images/gamepad/xbox/xbox_dpad_down.svg" id="3_b76jf"] -[ext_resource type="Texture2D" uid="uid://candjw8elkekp" path="res://assets/images/gamepad/xbox/xbox_dpad_left.svg" id="4_3pall"] -[ext_resource type="Texture2D" uid="uid://dcpsb03pvu0vb" path="res://assets/images/gamepad/xbox/xbox_dpad_right.svg" id="5_g2nkq"] -[ext_resource type="Texture2D" uid="uid://c6w5exkfdkkrr" path="res://assets/images/gamepad/xbox/xbox_dpad_up.svg" id="6_tm6is"] -[ext_resource type="Texture2D" uid="uid://bqho0a0sr6u1j" path="res://assets/images/gamepad/xbox/xbox_button_color_b.svg" id="7_g13ut"] -[ext_resource type="Texture2D" uid="uid://bcoajyq8st62e" path="res://assets/images/gamepad/xbox/xbox_guide.svg" id="8_2xbuh"] -[ext_resource type="Texture2D" uid="uid://mtd8tfnbotgc" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_hg4fs"] -[ext_resource type="Texture2D" uid="uid://bdqfg27h5o836" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_6y3yw"] -[ext_resource type="Texture2D" uid="uid://dvv5grt45cows" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_u8jcq"] -[ext_resource type="Texture2D" uid="uid://dcumk3oqkyy7g" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_o47r8"] -[ext_resource type="Texture2D" uid="uid://dooej262t4g0x" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_k3o80"] -[ext_resource type="Texture2D" uid="uid://coltjh07der5" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_5mj40"] -[ext_resource type="Texture2D" uid="uid://b3xxbnyo2h20l" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_k4fks"] -[ext_resource type="Texture2D" uid="uid://u3jonqoafyg6" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="16_ld8s8"] -[ext_resource type="Texture2D" uid="uid://ddpudi00yulrx" path="res://assets/images/gamepad/xbox/xbox_button_color_y.svg" id="17_wbg38"] -[ext_resource type="Texture2D" uid="uid://co3kg331knfu7" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="18_dqiaq"] -[ext_resource type="Texture2D" uid="uid://dw15y6xiuthpe" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_tfh6l"] -[ext_resource type="Texture2D" uid="uid://de2kcqeakp3q0" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_qquah"] -[ext_resource type="Texture2D" uid="uid://d4iq63eypbf2s" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_5sudg"] -[ext_resource type="Texture2D" uid="uid://bus2l2lmd844f" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_y2dmg"] -[ext_resource type="Texture2D" uid="uid://cokparjjmwy8c" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_tnsia"] -[ext_resource type="Texture2D" uid="uid://jn543ff1ak1c" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_u3tot"] -[ext_resource type="Texture2D" uid="uid://da8ojjv5vttou" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="25_h8btq"] -[ext_resource type="Texture2D" uid="uid://ce732ysw0pjo1" path="res://assets/images/gamepad/xbox/xbox_button_view.svg" id="27_we8jk"] -[ext_resource type="Texture2D" uid="uid://dyemqkvdtk43e" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="28_e6a35"] -[ext_resource type="Texture2D" uid="uid://dagfma4wnmvw8" path="res://assets/images/gamepad/xbox/xbox_button_menu.svg" id="29_r5b3p"] -[ext_resource type="Texture2D" uid="uid://cuvx61nakiaoi" path="res://assets/images/gamepad/xbox/xbox_button_color_x.svg" id="30_2but5"] +[ext_resource type="Texture2D" uid="uid://di87v7xnlu4hd" path="res://assets/images/gamepad/xbox/xbox_dpad.svg" id="2_7tdao"] +[ext_resource type="Texture2D" uid="uid://brqqse0hid6ki" path="res://assets/images/gamepad/xbox/xbox_dpad_down.svg" id="3_b76jf"] +[ext_resource type="Texture2D" uid="uid://caqnkbu5b0u2q" path="res://assets/images/gamepad/xbox/xbox_dpad_left.svg" id="4_3pall"] +[ext_resource type="Texture2D" uid="uid://b5gektw3mgp1a" path="res://assets/images/gamepad/xbox/xbox_dpad_right.svg" id="5_g2nkq"] +[ext_resource type="Texture2D" uid="uid://cjwqn3wttist" path="res://assets/images/gamepad/xbox/xbox_dpad_up.svg" id="6_tm6is"] +[ext_resource type="Texture2D" uid="uid://dfby3iiksln2e" path="res://assets/images/gamepad/xbox/xbox_button_color_b.svg" id="7_g13ut"] +[ext_resource type="Texture2D" uid="uid://dq7kibukggise" path="res://assets/images/gamepad/xbox/xbox_guide.svg" id="8_2xbuh"] +[ext_resource type="Texture2D" uid="uid://cwyi3if752xh6" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_hg4fs"] +[ext_resource type="Texture2D" uid="uid://dqx7njocf1aok" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_6y3yw"] +[ext_resource type="Texture2D" uid="uid://ckqaven4xmk16" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_u8jcq"] +[ext_resource type="Texture2D" uid="uid://c1bfw2axbcg1q" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_o47r8"] +[ext_resource type="Texture2D" uid="uid://c4iwtbyehtps2" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_k3o80"] +[ext_resource type="Texture2D" uid="uid://d4bfok8606fmv" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_5mj40"] +[ext_resource type="Texture2D" uid="uid://c7p0nhy4sh5wf" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_k4fks"] +[ext_resource type="Texture2D" uid="uid://cg8r4ijx7641s" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="16_ld8s8"] +[ext_resource type="Texture2D" uid="uid://b81pjmldtt10s" path="res://assets/images/gamepad/xbox/xbox_button_color_y.svg" id="17_wbg38"] +[ext_resource type="Texture2D" uid="uid://ceiru5s4y3ghy" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="18_dqiaq"] +[ext_resource type="Texture2D" uid="uid://bjq76f76quyry" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_tfh6l"] +[ext_resource type="Texture2D" uid="uid://5q4tjwvf7b8a" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_qquah"] +[ext_resource type="Texture2D" uid="uid://cu3ptt3kea56h" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_5sudg"] +[ext_resource type="Texture2D" uid="uid://s4x7rp50eub5" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_y2dmg"] +[ext_resource type="Texture2D" uid="uid://kfqb1dr0735v" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_tnsia"] +[ext_resource type="Texture2D" uid="uid://o0xrox1l5sae" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_u3tot"] +[ext_resource type="Texture2D" uid="uid://brt5gm71iv3u4" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="25_h8btq"] +[ext_resource type="Texture2D" uid="uid://cws272by4n8r7" path="res://assets/images/gamepad/xbox/xbox_button_view.svg" id="27_we8jk"] +[ext_resource type="Texture2D" uid="uid://cxvxwgt1vox0" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="28_e6a35"] +[ext_resource type="Texture2D" uid="uid://ckk2my0j71i3r" path="res://assets/images/gamepad/xbox/xbox_button_menu.svg" id="29_r5b3p"] +[ext_resource type="Texture2D" uid="uid://ojrh7msglai0" path="res://assets/images/gamepad/xbox/xbox_button_color_x.svg" id="30_2but5"] [resource] script = ExtResource("1_lckpp") diff --git a/assets/gamepad/icon_mappings/xboxseries.tres b/assets/gamepad/icon_mappings/xboxseries.tres index dcae369b8..68d461b2b 100644 --- a/assets/gamepad/icon_mappings/xboxseries.tres +++ b/assets/gamepad/icon_mappings/xboxseries.tres @@ -1,36 +1,36 @@ [gd_resource type="Resource" script_class="InputIconMapping" load_steps=32 format=3 uid="uid://bpu8kbwkjlcdf"] [ext_resource type="Script" path="res://core/systems/input/input_icon_mapping.gd" id="1_b6rct"] -[ext_resource type="Texture2D" uid="uid://bie2sf8kjawr" path="res://assets/images/gamepad/xbox/XboxOne_Diagram.png" id="1_ev5wy"] -[ext_resource type="Texture2D" uid="uid://dn8i3gpeijgu1" path="res://assets/images/gamepad/xbox/xbox_dpad_round.svg" id="2_6ynjl"] -[ext_resource type="Texture2D" uid="uid://dwv3f88h7uaq7" path="res://assets/images/gamepad/xbox/xbox_dpad_round_down.svg" id="3_xm811"] -[ext_resource type="Texture2D" uid="uid://fmnwuu2ym0m8" path="res://assets/images/gamepad/xbox/xbox_dpad_round_left.svg" id="4_uybyb"] -[ext_resource type="Texture2D" uid="uid://cboy1enwvdeoy" path="res://assets/images/gamepad/xbox/xbox_dpad_round_right.svg" id="5_tp8q3"] -[ext_resource type="Texture2D" uid="uid://ba64eulr61afh" path="res://assets/images/gamepad/xbox/xbox_dpad_round_up.svg" id="6_1xdjq"] -[ext_resource type="Texture2D" uid="uid://bqho0a0sr6u1j" path="res://assets/images/gamepad/xbox/xbox_button_color_b.svg" id="7_hobo3"] -[ext_resource type="Texture2D" uid="uid://bcoajyq8st62e" path="res://assets/images/gamepad/xbox/xbox_guide.svg" id="8_auhb1"] -[ext_resource type="Texture2D" uid="uid://mtd8tfnbotgc" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_hctac"] -[ext_resource type="Texture2D" uid="uid://bdqfg27h5o836" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_xwigx"] -[ext_resource type="Texture2D" uid="uid://dvv5grt45cows" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_xcmc2"] -[ext_resource type="Texture2D" uid="uid://dcumk3oqkyy7g" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_nxi2e"] -[ext_resource type="Texture2D" uid="uid://dooej262t4g0x" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_p1oy0"] -[ext_resource type="Texture2D" uid="uid://coltjh07der5" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_8q8uu"] -[ext_resource type="Texture2D" uid="uid://b3xxbnyo2h20l" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_cswmi"] -[ext_resource type="Texture2D" uid="uid://u3jonqoafyg6" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="16_uewt6"] -[ext_resource type="Texture2D" uid="uid://ddpudi00yulrx" path="res://assets/images/gamepad/xbox/xbox_button_color_y.svg" id="17_sgpdy"] -[ext_resource type="Texture2D" uid="uid://co3kg331knfu7" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="18_1nrje"] -[ext_resource type="Texture2D" uid="uid://dw15y6xiuthpe" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_a20m1"] -[ext_resource type="Texture2D" uid="uid://de2kcqeakp3q0" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_4odka"] -[ext_resource type="Texture2D" uid="uid://d4iq63eypbf2s" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_dg0kt"] -[ext_resource type="Texture2D" uid="uid://bus2l2lmd844f" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_2ed5x"] -[ext_resource type="Texture2D" uid="uid://cokparjjmwy8c" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_vchpt"] -[ext_resource type="Texture2D" uid="uid://jn543ff1ak1c" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_3b4ji"] -[ext_resource type="Texture2D" uid="uid://da8ojjv5vttou" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="25_1f2e0"] -[ext_resource type="Texture2D" uid="uid://ce732ysw0pjo1" path="res://assets/images/gamepad/xbox/xbox_button_view.svg" id="27_yvhvb"] -[ext_resource type="Texture2D" uid="uid://dasqdy0fqn33r" path="res://assets/images/gamepad/xbox/xbox_button_share.svg" id="28_kfx4h"] -[ext_resource type="Texture2D" uid="uid://dyemqkvdtk43e" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="28_wmep3"] -[ext_resource type="Texture2D" uid="uid://dagfma4wnmvw8" path="res://assets/images/gamepad/xbox/xbox_button_menu.svg" id="30_0yia7"] -[ext_resource type="Texture2D" uid="uid://cuvx61nakiaoi" path="res://assets/images/gamepad/xbox/xbox_button_color_x.svg" id="30_ubo18"] +[ext_resource type="Texture2D" uid="uid://ceyt88pt5sogo" path="res://assets/images/gamepad/xbox/XboxOne_Diagram.png" id="1_ev5wy"] +[ext_resource type="Texture2D" uid="uid://n4t36xrl15ux" path="res://assets/images/gamepad/xbox/xbox_dpad_round.svg" id="2_6ynjl"] +[ext_resource type="Texture2D" uid="uid://bxovdah6vnpsn" path="res://assets/images/gamepad/xbox/xbox_dpad_round_down.svg" id="3_xm811"] +[ext_resource type="Texture2D" uid="uid://qhwj81sv67l4" path="res://assets/images/gamepad/xbox/xbox_dpad_round_left.svg" id="4_uybyb"] +[ext_resource type="Texture2D" uid="uid://b1uo5130s6gfw" path="res://assets/images/gamepad/xbox/xbox_dpad_round_right.svg" id="5_tp8q3"] +[ext_resource type="Texture2D" uid="uid://dqk82r4up56mh" path="res://assets/images/gamepad/xbox/xbox_dpad_round_up.svg" id="6_1xdjq"] +[ext_resource type="Texture2D" uid="uid://dfby3iiksln2e" path="res://assets/images/gamepad/xbox/xbox_button_color_b.svg" id="7_hobo3"] +[ext_resource type="Texture2D" uid="uid://dq7kibukggise" path="res://assets/images/gamepad/xbox/xbox_guide.svg" id="8_auhb1"] +[ext_resource type="Texture2D" uid="uid://cwyi3if752xh6" path="res://assets/images/gamepad/xbox/xbox_lb.svg" id="9_hctac"] +[ext_resource type="Texture2D" uid="uid://dqx7njocf1aok" path="res://assets/images/gamepad/xbox/xbox_stick_l.svg" id="10_xwigx"] +[ext_resource type="Texture2D" uid="uid://ckqaven4xmk16" path="res://assets/images/gamepad/xbox/xbox_stick_l_press.svg" id="11_xcmc2"] +[ext_resource type="Texture2D" uid="uid://c1bfw2axbcg1q" path="res://assets/images/gamepad/xbox/xbox_stick_l_down.svg" id="12_nxi2e"] +[ext_resource type="Texture2D" uid="uid://c4iwtbyehtps2" path="res://assets/images/gamepad/xbox/xbox_stick_l_left.svg" id="13_p1oy0"] +[ext_resource type="Texture2D" uid="uid://d4bfok8606fmv" path="res://assets/images/gamepad/xbox/xbox_stick_l_right.svg" id="14_8q8uu"] +[ext_resource type="Texture2D" uid="uid://c7p0nhy4sh5wf" path="res://assets/images/gamepad/xbox/xbox_stick_l_up.svg" id="15_cswmi"] +[ext_resource type="Texture2D" uid="uid://cg8r4ijx7641s" path="res://assets/images/gamepad/xbox/xbox_lt.svg" id="16_uewt6"] +[ext_resource type="Texture2D" uid="uid://b81pjmldtt10s" path="res://assets/images/gamepad/xbox/xbox_button_color_y.svg" id="17_sgpdy"] +[ext_resource type="Texture2D" uid="uid://ceiru5s4y3ghy" path="res://assets/images/gamepad/xbox/xbox_rb.svg" id="18_1nrje"] +[ext_resource type="Texture2D" uid="uid://bjq76f76quyry" path="res://assets/images/gamepad/xbox/xbox_stick_r.svg" id="19_a20m1"] +[ext_resource type="Texture2D" uid="uid://5q4tjwvf7b8a" path="res://assets/images/gamepad/xbox/xbox_stick_r_press.svg" id="20_4odka"] +[ext_resource type="Texture2D" uid="uid://cu3ptt3kea56h" path="res://assets/images/gamepad/xbox/xbox_stick_r_down.svg" id="21_dg0kt"] +[ext_resource type="Texture2D" uid="uid://s4x7rp50eub5" path="res://assets/images/gamepad/xbox/xbox_stick_r_left.svg" id="22_2ed5x"] +[ext_resource type="Texture2D" uid="uid://kfqb1dr0735v" path="res://assets/images/gamepad/xbox/xbox_stick_r_right.svg" id="23_vchpt"] +[ext_resource type="Texture2D" uid="uid://o0xrox1l5sae" path="res://assets/images/gamepad/xbox/xbox_stick_r_up.svg" id="24_3b4ji"] +[ext_resource type="Texture2D" uid="uid://brt5gm71iv3u4" path="res://assets/images/gamepad/xbox/xbox_rt.svg" id="25_1f2e0"] +[ext_resource type="Texture2D" uid="uid://cws272by4n8r7" path="res://assets/images/gamepad/xbox/xbox_button_view.svg" id="27_yvhvb"] +[ext_resource type="Texture2D" uid="uid://crda44th28ovn" path="res://assets/images/gamepad/xbox/xbox_button_share.svg" id="28_kfx4h"] +[ext_resource type="Texture2D" uid="uid://cxvxwgt1vox0" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="28_wmep3"] +[ext_resource type="Texture2D" uid="uid://ckk2my0j71i3r" path="res://assets/images/gamepad/xbox/xbox_button_menu.svg" id="30_0yia7"] +[ext_resource type="Texture2D" uid="uid://ojrh7msglai0" path="res://assets/images/gamepad/xbox/xbox_button_color_x.svg" id="30_ubo18"] [resource] script = ExtResource("1_b6rct") diff --git a/assets/gamepad/profiles/default.json b/assets/gamepad/profiles/default.json index cbed2a772..4517be37a 100644 --- a/assets/gamepad/profiles/default.json +++ b/assets/gamepad/profiles/default.json @@ -1,7 +1,7 @@ { "version": 1, "kind": "DeviceProfile", - "name": "Default", + "name": "OpenGamepadUI Default", "mapping": [ { "name": "LeftTop", @@ -52,14 +52,7 @@ }, "target_events": [ { - "gamepad": { - "button": "Guide" - } - }, - { - "gamepad": { - "button": "North" - } + "dbus": "ui_osk" } ] }, @@ -82,6 +75,19 @@ } } ] + }, + { + "name": "QuickAccess", + "source_event": { + "gamepad": { + "button": "QuickAccess" + } + }, + "target_events": [ + { + "dbus": "ui_quick" + } + ] } ] } diff --git a/assets/images/gamepad/ayaneo-2/diagram.png.import b/assets/images/gamepad/ayaneo-2/diagram.png.import index 9c835f44f..c677728e7 100644 --- a/assets/images/gamepad/ayaneo-2/diagram.png.import +++ b/assets/images/gamepad/ayaneo-2/diagram.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cutkhw5cn00t1" +uid="uid://0ks0l6lrjisb" path="res://.godot/imported/diagram.png-f50e7f3c087f705f9d78ceca7847be41.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/ayaneo-2021/diagram.png.png.import b/assets/images/gamepad/ayaneo-2021/diagram.png.png.import index 9fd83ecf9..24a4ad23e 100644 --- a/assets/images/gamepad/ayaneo-2021/diagram.png.png.import +++ b/assets/images/gamepad/ayaneo-2021/diagram.png.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bju0qdfa1agbx" +uid="uid://bycwbaq43ecps" path="res://.godot/imported/diagram.png.png-a9db9f2667b0184a3fe15017167f42a7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/ayaneo-air/diagram.png.import b/assets/images/gamepad/ayaneo-air/diagram.png.import index b16952dc1..9349fe36e 100644 --- a/assets/images/gamepad/ayaneo-air/diagram.png.import +++ b/assets/images/gamepad/ayaneo-air/diagram.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bkmukuppf8aga" +uid="uid://ol30ae8sw3j" path="res://.godot/imported/diagram.png-78a360acd3fc560b6d6e676e7bf7c1f3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/ayaneo-next/diagram.png.import b/assets/images/gamepad/ayaneo-next/diagram.png.import index 41f790c5b..a8d05221b 100644 --- a/assets/images/gamepad/ayaneo-next/diagram.png.import +++ b/assets/images/gamepad/ayaneo-next/diagram.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://by00fwcpvklck" +uid="uid://f8no2jrnw4qq" path="res://.godot/imported/diagram.png-98af58cfaf32d93ba4eb60fc0c58fa92.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/generic/generic_button_circle.svg.import b/assets/images/gamepad/generic/generic_button_circle.svg.import index 1e5d55b08..61465dfb2 100644 --- a/assets/images/gamepad/generic/generic_button_circle.svg.import +++ b/assets/images/gamepad/generic/generic_button_circle.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c5oweso4md55c" +uid="uid://ytg4and6kiyc" path="res://.godot/imported/generic_button_circle.svg-f9f0a7d2857249866da78c46a084e942.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/generic/gyro.png.import b/assets/images/gamepad/generic/gyro.png.import index 40331ea7a..3a8554280 100644 --- a/assets/images/gamepad/generic/gyro.png.import +++ b/assets/images/gamepad/generic/gyro.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bi2887g54x4ym" +uid="uid://do8ip0e6vs5h5" path="res://.godot/imported/gyro.png-8d4572ae245f576c2f8b62cdee4d47a0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/luna/Luna_Diagram_Simple.png.import b/assets/images/gamepad/luna/Luna_Diagram_Simple.png.import index a134c31c4..856690e6e 100644 --- a/assets/images/gamepad/luna/Luna_Diagram_Simple.png.import +++ b/assets/images/gamepad/luna/Luna_Diagram_Simple.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ct1wx8gqcmp2x" +uid="uid://b6o0kbjxrsmef" path="res://.godot/imported/Luna_Diagram_Simple.png-c5bfecf21132287f0b480f6e101196f0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/a.png.import b/assets/images/gamepad/oxp/a.png.import index 67acfba0d..5fba563c8 100644 --- a/assets/images/gamepad/oxp/a.png.import +++ b/assets/images/gamepad/oxp/a.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ddj7iydr5c04r" +uid="uid://vmqhe01e4iit" path="res://.godot/imported/a.png-adb7e9f43d4988d915e3c2cced5e214c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/b.png.import b/assets/images/gamepad/oxp/b.png.import index 319acdf0e..3aceb3f22 100644 --- a/assets/images/gamepad/oxp/b.png.import +++ b/assets/images/gamepad/oxp/b.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b3ofivkj5cwdd" +uid="uid://c8cjp8mh52xh2" path="res://.godot/imported/b.png-ccadb030652ec36982b4edf9756cd5c6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/dots.png.import b/assets/images/gamepad/oxp/dots.png.import index 0ffe9eaf0..90d2c591e 100644 --- a/assets/images/gamepad/oxp/dots.png.import +++ b/assets/images/gamepad/oxp/dots.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://55kdld1mn8sh" +uid="uid://wmris721srn5" path="res://.godot/imported/dots.png-aa558f692310d0b6110a9f5cd1200254.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/gyro.png.import b/assets/images/gamepad/oxp/gyro.png.import index f8483af57..60d49d3fe 100644 --- a/assets/images/gamepad/oxp/gyro.png.import +++ b/assets/images/gamepad/oxp/gyro.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bicbp0it5onk" +uid="uid://b156x3h81b827" path="res://.godot/imported/gyro.png-bd99b5512fb99777e2b958e5b4fe065d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/home.png.import b/assets/images/gamepad/oxp/home.png.import index d6bdefeef..ee68c73e0 100644 --- a/assets/images/gamepad/oxp/home.png.import +++ b/assets/images/gamepad/oxp/home.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cu0fgwxtupy5y" +uid="uid://c00vtor5dsj8n" path="res://.godot/imported/home.png-a880cdd0ca077c0f417c531781309c23.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/l_stick.png.import b/assets/images/gamepad/oxp/l_stick.png.import index 050eae854..9d4a31d98 100644 --- a/assets/images/gamepad/oxp/l_stick.png.import +++ b/assets/images/gamepad/oxp/l_stick.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://1grh7tb8l2ev" +uid="uid://do2o0rko3dl8q" path="res://.godot/imported/l_stick.png-e6268b4a4b2a52faf165716609590ea1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/l_stick_click.png.import b/assets/images/gamepad/oxp/l_stick_click.png.import index 72177a168..19ae815c0 100644 --- a/assets/images/gamepad/oxp/l_stick_click.png.import +++ b/assets/images/gamepad/oxp/l_stick_click.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cxrw6ik2qxvjv" +uid="uid://d1nbyq2klq1o3" path="res://.godot/imported/l_stick_click.png-61804b5213d32b45fe69d3f2785613aa.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/menu.png.import b/assets/images/gamepad/oxp/menu.png.import index 1ea3c3a3c..3e565fc7c 100644 --- a/assets/images/gamepad/oxp/menu.png.import +++ b/assets/images/gamepad/oxp/menu.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://8bjdmpmjwbrh" +uid="uid://dyfisopofd1ml" path="res://.godot/imported/menu.png-3f32e1e8fb1b69686806894f351a3c13.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/oxp-mini-a07.png.import b/assets/images/gamepad/oxp/oxp-mini-a07.png.import index cb4f74bf6..2614b11be 100644 --- a/assets/images/gamepad/oxp/oxp-mini-a07.png.import +++ b/assets/images/gamepad/oxp/oxp-mini-a07.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://hmebqgl4qfmc" +uid="uid://d224crt7h1b8l" path="res://.godot/imported/oxp-mini-a07.png-47d2e27d7da4015b7fb2c32e4a3ec788.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/r_stick.png.import b/assets/images/gamepad/oxp/r_stick.png.import index b45211131..5e385ad20 100644 --- a/assets/images/gamepad/oxp/r_stick.png.import +++ b/assets/images/gamepad/oxp/r_stick.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c12df6tk1yof7" +uid="uid://2efop4m33tk3" path="res://.godot/imported/r_stick.png-b0146314735f965f9130034d6d7630fc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/r_stick_click.png.import b/assets/images/gamepad/oxp/r_stick_click.png.import index 8fb0cf49e..5ce52c25c 100644 --- a/assets/images/gamepad/oxp/r_stick_click.png.import +++ b/assets/images/gamepad/oxp/r_stick_click.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://mxd2r3jqn10" +uid="uid://csfqtx1sfw3kn" path="res://.godot/imported/r_stick_click.png-557e77aa943c46c2a0d17ebf192978f7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/square.png.import b/assets/images/gamepad/oxp/square.png.import index 9f7ba1773..46ac18886 100644 --- a/assets/images/gamepad/oxp/square.png.import +++ b/assets/images/gamepad/oxp/square.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://xarlsdq5yd4l" +uid="uid://ob0fpdvy6aya" path="res://.godot/imported/square.png-d8270a92bf98e3fb174fc7fbe2426fef.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/x.png.import b/assets/images/gamepad/oxp/x.png.import index f3b3f72ba..8c748d6d2 100644 --- a/assets/images/gamepad/oxp/x.png.import +++ b/assets/images/gamepad/oxp/x.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ddma44dieeu0c" +uid="uid://cgsn52xbh5g7n" path="res://.godot/imported/x.png-ffd94bc4c243f828262a0e08def527fa.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/oxp/y.png.import b/assets/images/gamepad/oxp/y.png.import index 916ea9ed0..bae4118fc 100644 --- a/assets/images/gamepad/oxp/y.png.import +++ b/assets/images/gamepad/oxp/y.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cfeud3gaq056l" +uid="uid://dqhw0phvf3tre" path="res://.godot/imported/y.png-659c755ba143af92e8840bf905b0ba06.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/PS4_Diagram_Simple.png.import b/assets/images/gamepad/playstation/PS4_Diagram_Simple.png.import index 77d53a3c6..fed3ff5ab 100644 --- a/assets/images/gamepad/playstation/PS4_Diagram_Simple.png.import +++ b/assets/images/gamepad/playstation/PS4_Diagram_Simple.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://brf1l7pyedsg6" +uid="uid://b6ebettxxbmu1" path="res://.godot/imported/PS4_Diagram_Simple.png-9de8a8def0da5353241716fed618be6c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/PS5_Diagram_Simple.png.import b/assets/images/gamepad/playstation/PS5_Diagram_Simple.png.import index a6d4562b3..8dbb9bd35 100644 --- a/assets/images/gamepad/playstation/PS5_Diagram_Simple.png.import +++ b/assets/images/gamepad/playstation/PS5_Diagram_Simple.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://biu5qfv6e1mq8" +uid="uid://devahu3vo6pdj" path="res://.godot/imported/PS5_Diagram_Simple.png-7a455e97137ffa78f17bc447a3926f67.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/controller_playstation1.svg.import b/assets/images/gamepad/playstation/controller_playstation1.svg.import index 7e7adb191..6cfae665e 100644 --- a/assets/images/gamepad/playstation/controller_playstation1.svg.import +++ b/assets/images/gamepad/playstation/controller_playstation1.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cs6ubfgvplwfp" +uid="uid://byh1x35alqdq7" path="res://.godot/imported/controller_playstation1.svg-c3bec59d35d7f0b9cf561ebd3ac4669c.ctex" metadata={ "vram_texture": false @@ -32,6 +32,6 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=15.0 +svg/scale=1.0 editor/scale_with_editor_scale=false editor/convert_colors_with_editor_theme=false diff --git a/assets/images/gamepad/playstation/controller_playstation2.svg.import b/assets/images/gamepad/playstation/controller_playstation2.svg.import index 7709120f5..ed1c4fd09 100644 --- a/assets/images/gamepad/playstation/controller_playstation2.svg.import +++ b/assets/images/gamepad/playstation/controller_playstation2.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ix4y28aaadi5" +uid="uid://cpavuyofh1t5b" path="res://.godot/imported/controller_playstation2.svg-0a490338d7897aa8ddd715fc3576fe4d.ctex" metadata={ "vram_texture": false @@ -32,6 +32,6 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=15.0 +svg/scale=1.0 editor/scale_with_editor_scale=false editor/convert_colors_with_editor_theme=false diff --git a/assets/images/gamepad/playstation/controller_playstation3.svg.import b/assets/images/gamepad/playstation/controller_playstation3.svg.import index db6a04088..a90c8c884 100644 --- a/assets/images/gamepad/playstation/controller_playstation3.svg.import +++ b/assets/images/gamepad/playstation/controller_playstation3.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://uopdu8w1sjci" +uid="uid://gr1o4cqn6m22" path="res://.godot/imported/controller_playstation3.svg-08fa9901d1d1775542e7bcdf7c601d1c.ctex" metadata={ "vram_texture": false @@ -32,6 +32,6 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=15.0 +svg/scale=1.0 editor/scale_with_editor_scale=false editor/convert_colors_with_editor_theme=false diff --git a/assets/images/gamepad/playstation/controller_playstation4.svg.import b/assets/images/gamepad/playstation/controller_playstation4.svg.import index 22e962c79..0aeea0540 100644 --- a/assets/images/gamepad/playstation/controller_playstation4.svg.import +++ b/assets/images/gamepad/playstation/controller_playstation4.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dq281njbgcpjs" +uid="uid://kj50pj05684x" path="res://.godot/imported/controller_playstation4.svg-ea4196e59dca750d0d60766e88bc9587.ctex" metadata={ "vram_texture": false @@ -32,6 +32,6 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=15.0 +svg/scale=1.0 editor/scale_with_editor_scale=false editor/convert_colors_with_editor_theme=false diff --git a/assets/images/gamepad/playstation/controller_playstation5.svg.import b/assets/images/gamepad/playstation/controller_playstation5.svg.import index eed12a74b..dd13f386f 100644 --- a/assets/images/gamepad/playstation/controller_playstation5.svg.import +++ b/assets/images/gamepad/playstation/controller_playstation5.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://guwve61cbjpj" +uid="uid://cv4w2wqlkt341" path="res://.godot/imported/controller_playstation5.svg-cc6541a198815a9e4d2d6fe7cf5e23d6.ctex" metadata={ "vram_texture": false @@ -32,6 +32,6 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=15.0 +svg/scale=1.0 editor/scale_with_editor_scale=false editor/convert_colors_with_editor_theme=false diff --git a/assets/images/gamepad/playstation/guide.svg.import b/assets/images/gamepad/playstation/guide.svg.import index e4d2203d3..081b77ea1 100644 --- a/assets/images/gamepad/playstation/guide.svg.import +++ b/assets/images/gamepad/playstation/guide.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dt6pmeivafjuh" +uid="uid://b6qllts4hg3my" path="res://.godot/imported/guide.svg-ce9434d207a41e6efefcb450d49d0b19.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation3_button_select.svg.import b/assets/images/gamepad/playstation/playstation3_button_select.svg.import index c9257fa7e..dd5b542e5 100644 --- a/assets/images/gamepad/playstation/playstation3_button_select.svg.import +++ b/assets/images/gamepad/playstation/playstation3_button_select.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://btcpbcj64i6w3" +uid="uid://duedso6l7wcd3" path="res://.godot/imported/playstation3_button_select.svg-20e8bc106a233146c77984cfc95fdc86.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation3_button_select_outline.svg.import b/assets/images/gamepad/playstation/playstation3_button_select_outline.svg.import index ff1405fae..f669dab9a 100644 --- a/assets/images/gamepad/playstation/playstation3_button_select_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation3_button_select_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dx6m1lx0lq23s" +uid="uid://c1mptm48tj211" path="res://.godot/imported/playstation3_button_select_outline.svg-54416d6a0fcd2eb4ef6219f80fd46689.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation3_button_start.svg.import b/assets/images/gamepad/playstation/playstation3_button_start.svg.import index 39a37725c..c20f23f03 100644 --- a/assets/images/gamepad/playstation/playstation3_button_start.svg.import +++ b/assets/images/gamepad/playstation/playstation3_button_start.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c42k3k486kupj" +uid="uid://dyy835yt85d1h" path="res://.godot/imported/playstation3_button_start.svg-52212432baef095e029d7cc27bad1e4b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation3_button_start_outline.svg.import b/assets/images/gamepad/playstation/playstation3_button_start_outline.svg.import index 6329e352c..a1fd5ea57 100644 --- a/assets/images/gamepad/playstation/playstation3_button_start_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation3_button_start_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b2aavhjxo2k1c" +uid="uid://dmx0c7u4ov8uv" path="res://.godot/imported/playstation3_button_start_outline.svg-7272ebeebbac0b97d5c2a0ddac0b6bea.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation4_button_options.svg.import b/assets/images/gamepad/playstation/playstation4_button_options.svg.import index 42ea2a8e0..a57fc466a 100644 --- a/assets/images/gamepad/playstation/playstation4_button_options.svg.import +++ b/assets/images/gamepad/playstation/playstation4_button_options.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bbekpvxt1viwr" +uid="uid://ci7bqueqpt20u" path="res://.godot/imported/playstation4_button_options.svg-c9fd31a00b2d775038c55898004be7d1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation4_button_options_outline.svg.import b/assets/images/gamepad/playstation/playstation4_button_options_outline.svg.import index 6e42089f8..96161342d 100644 --- a/assets/images/gamepad/playstation/playstation4_button_options_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation4_button_options_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://625c0wogf48s" +uid="uid://bmg02xcx2rbus" path="res://.godot/imported/playstation4_button_options_outline.svg-9a7ca97c9da69171b5f8531af6131ebe.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation4_button_share.svg.import b/assets/images/gamepad/playstation/playstation4_button_share.svg.import index 5eec9550d..4cc22dd3c 100644 --- a/assets/images/gamepad/playstation/playstation4_button_share.svg.import +++ b/assets/images/gamepad/playstation/playstation4_button_share.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvx6k287vtlda" +uid="uid://bb0cv3hsg3y0v" path="res://.godot/imported/playstation4_button_share.svg-79b64357e6d64c26651cfb08560f5988.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation4_button_share_outline.svg.import b/assets/images/gamepad/playstation/playstation4_button_share_outline.svg.import index 14cd1677c..b0be3a540 100644 --- a/assets/images/gamepad/playstation/playstation4_button_share_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation4_button_share_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cqp7tf0yr3ac6" +uid="uid://b3qq5f7s37yx6" path="res://.godot/imported/playstation4_button_share_outline.svg-218d0fd990e503ace50b48ff8d8bb1fa.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation4_touchpad.svg.import b/assets/images/gamepad/playstation/playstation4_touchpad.svg.import index b0c49200e..8a59e8594 100644 --- a/assets/images/gamepad/playstation/playstation4_touchpad.svg.import +++ b/assets/images/gamepad/playstation/playstation4_touchpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b7w1bs67uogco" +uid="uid://h0mg7dksau38" path="res://.godot/imported/playstation4_touchpad.svg-0f48b732be68289ac7488f137537a381.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation4_touchpad_outline.svg.import b/assets/images/gamepad/playstation/playstation4_touchpad_outline.svg.import index dc33a3348..fcda17a27 100644 --- a/assets/images/gamepad/playstation/playstation4_touchpad_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation4_touchpad_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bxoilijemljux" +uid="uid://cwc5kohjy5k2f" path="res://.godot/imported/playstation4_touchpad_outline.svg-7648d6b6826d06b07af9aad68a2a707c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation4_touchpad_touch.svg.import b/assets/images/gamepad/playstation/playstation4_touchpad_touch.svg.import index c3af57467..f5eedf1e2 100644 --- a/assets/images/gamepad/playstation/playstation4_touchpad_touch.svg.import +++ b/assets/images/gamepad/playstation/playstation4_touchpad_touch.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cgt42jlee1t3" +uid="uid://clihwqudrnv5k" path="res://.godot/imported/playstation4_touchpad_touch.svg-3183d886f32b32c8210d5b5d1a4739d1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation4_touchpad_touch_outline.svg.import b/assets/images/gamepad/playstation/playstation4_touchpad_touch_outline.svg.import index 776316ae4..5b869e9e9 100644 --- a/assets/images/gamepad/playstation/playstation4_touchpad_touch_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation4_touchpad_touch_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://010wt5wrk1s3" +uid="uid://br3r6oqplp8xc" path="res://.godot/imported/playstation4_touchpad_touch_outline.svg-44c0823bf709e7c3981a12b76997b01c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_create.svg.import b/assets/images/gamepad/playstation/playstation5_button_create.svg.import index 85920a871..08fa2b92c 100644 --- a/assets/images/gamepad/playstation/playstation5_button_create.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_create.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://y4ntbxk5qrpo" +uid="uid://lsf27o58vimw" path="res://.godot/imported/playstation5_button_create.svg-b4c0ab4d921557e6a1f1ba33e528e069.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_create_alternative.svg.import b/assets/images/gamepad/playstation/playstation5_button_create_alternative.svg.import index a8733fa0a..4989964f6 100644 --- a/assets/images/gamepad/playstation/playstation5_button_create_alternative.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_create_alternative.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://eodm14jnxebi" +uid="uid://c5p5xci8yhfpg" path="res://.godot/imported/playstation5_button_create_alternative.svg-33fa5cd83f6e73d025864830b4c7fb91.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_create_alternative_outline.svg.import b/assets/images/gamepad/playstation/playstation5_button_create_alternative_outline.svg.import index df5f71ada..0ce7138ca 100644 --- a/assets/images/gamepad/playstation/playstation5_button_create_alternative_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_create_alternative_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ccmc1i7c51jjq" +uid="uid://c2gtebn7w8g34" path="res://.godot/imported/playstation5_button_create_alternative_outline.svg-a5c8fa8b8bf8a1d63732b1a94ec34321.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_create_outline.svg.import b/assets/images/gamepad/playstation/playstation5_button_create_outline.svg.import index 31eba448c..0cbc731cf 100644 --- a/assets/images/gamepad/playstation/playstation5_button_create_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_create_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://doh64jvkhxcyb" +uid="uid://djvtv3cof0fag" path="res://.godot/imported/playstation5_button_create_outline.svg-291a261c3cb412f706675548cb537339.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_mute.svg.import b/assets/images/gamepad/playstation/playstation5_button_mute.svg.import index 748575c5f..a5e4f93b1 100644 --- a/assets/images/gamepad/playstation/playstation5_button_mute.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_mute.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dwfcqp030lm7i" +uid="uid://doee34h2f5sui" path="res://.godot/imported/playstation5_button_mute.svg-dbf082d66e2af7204d36ccd546bdceaa.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_mute_outline.svg.import b/assets/images/gamepad/playstation/playstation5_button_mute_outline.svg.import index ef49cfcd6..7139a6532 100644 --- a/assets/images/gamepad/playstation/playstation5_button_mute_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_mute_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://hhihq8nrhjod" +uid="uid://47f8kbpm037m" path="res://.godot/imported/playstation5_button_mute_outline.svg-0918eab049a68cf2ae1591ff0d89a17d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_options.svg.import b/assets/images/gamepad/playstation/playstation5_button_options.svg.import index 68c3f5924..25e5674bb 100644 --- a/assets/images/gamepad/playstation/playstation5_button_options.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_options.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://8vgoicywpflx" +uid="uid://bn6oh5im6dg0t" path="res://.godot/imported/playstation5_button_options.svg-598e27fe351f4ae0dec9ee6463857a2d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_options_alternative.svg.import b/assets/images/gamepad/playstation/playstation5_button_options_alternative.svg.import index 01eb1b26f..778e29375 100644 --- a/assets/images/gamepad/playstation/playstation5_button_options_alternative.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_options_alternative.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dwobavrwqg52h" +uid="uid://b7tp2oavf30vb" path="res://.godot/imported/playstation5_button_options_alternative.svg-0e61ef71b5db0fda48dcbb6d8b08ff76.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_options_alternative_outline.svg.import b/assets/images/gamepad/playstation/playstation5_button_options_alternative_outline.svg.import index e6020469a..14d20c260 100644 --- a/assets/images/gamepad/playstation/playstation5_button_options_alternative_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_options_alternative_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bs8jdl7iiwrnv" +uid="uid://5hbc4imd22fh" path="res://.godot/imported/playstation5_button_options_alternative_outline.svg-c391ab346436c3e24692a3c935505d16.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_button_options_outline.svg.import b/assets/images/gamepad/playstation/playstation5_button_options_outline.svg.import index 6e1493848..a4147671d 100644 --- a/assets/images/gamepad/playstation/playstation5_button_options_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation5_button_options_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://obf61hdejues" +uid="uid://bqqqfcf7olcbd" path="res://.godot/imported/playstation5_button_options_outline.svg-e9639bf2dedfebfcc5795643ae8efeb9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_touchpad.svg.import b/assets/images/gamepad/playstation/playstation5_touchpad.svg.import index ce0ac7a52..495f42f1c 100644 --- a/assets/images/gamepad/playstation/playstation5_touchpad.svg.import +++ b/assets/images/gamepad/playstation/playstation5_touchpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://xdrolp3nr15b" +uid="uid://b268gvlnbb2d8" path="res://.godot/imported/playstation5_touchpad.svg-61e1a8482c48ae5288d6f8ebbf85309a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_touchpad_outline.svg.import b/assets/images/gamepad/playstation/playstation5_touchpad_outline.svg.import index 6024ce755..f499f0ca4 100644 --- a/assets/images/gamepad/playstation/playstation5_touchpad_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation5_touchpad_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://3j4v7uom7l8" +uid="uid://8v6fi3c7ixd0" path="res://.godot/imported/playstation5_touchpad_outline.svg-17ed64d94abbb21afe1c3abeb2afef5a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_touchpad_touch.svg.import b/assets/images/gamepad/playstation/playstation5_touchpad_touch.svg.import index 0d750db19..1eb1c0825 100644 --- a/assets/images/gamepad/playstation/playstation5_touchpad_touch.svg.import +++ b/assets/images/gamepad/playstation/playstation5_touchpad_touch.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bj0qk2s5mxkkd" +uid="uid://do6nap60p4cs6" path="res://.godot/imported/playstation5_touchpad_touch.svg-5c134d924a1c9103687f0ea27aaac82a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation5_touchpad_touch_outline.svg.import b/assets/images/gamepad/playstation/playstation5_touchpad_touch_outline.svg.import index 74ecdb64c..fba4d9940 100644 --- a/assets/images/gamepad/playstation/playstation5_touchpad_touch_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation5_touchpad_touch_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bc6dc888584pl" +uid="uid://cmsfcvp3ax5i6" path="res://.godot/imported/playstation5_touchpad_touch_outline.svg-837235ff299445b9798fac883ddba273.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_analog.svg.import b/assets/images/gamepad/playstation/playstation_button_analog.svg.import index d24d45b75..4bb508abd 100644 --- a/assets/images/gamepad/playstation/playstation_button_analog.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_analog.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://k85f16j46gc3" +uid="uid://bvx1w0km6ix13" path="res://.godot/imported/playstation_button_analog.svg-aa87cfcd0c57d013851404246a4ee4e6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_analog_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_analog_outline.svg.import index c3dca7689..0ed43e0c6 100644 --- a/assets/images/gamepad/playstation/playstation_button_analog_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_analog_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://drlke4b038a2i" +uid="uid://c0eml8c4xxg2s" path="res://.godot/imported/playstation_button_analog_outline.svg-f8be9b46e53e4afb801daf1fd39dc451.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_circle.svg.import b/assets/images/gamepad/playstation/playstation_button_circle.svg.import index 35d2904aa..9f9d0d67b 100644 --- a/assets/images/gamepad/playstation/playstation_button_circle.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_circle.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://hq2enjjkuc2x" +uid="uid://dfb1egyjgak2x" path="res://.godot/imported/playstation_button_circle.svg-58673e6f941d624c21fcc3b38bec5d6d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_circle_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_circle_outline.svg.import index 31d94f1a3..7c2390418 100644 --- a/assets/images/gamepad/playstation/playstation_button_circle_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_circle_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://gvk81uyivjtx" +uid="uid://71yrtx1tfrog" path="res://.godot/imported/playstation_button_circle_outline.svg-5139e2a4253a14eecfd6784d5d634414.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_color_circle.svg.import b/assets/images/gamepad/playstation/playstation_button_color_circle.svg.import index bdee01180..a89435409 100644 --- a/assets/images/gamepad/playstation/playstation_button_color_circle.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_color_circle.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dcysxvygs0t0b" +uid="uid://b1wo28cyd80ov" path="res://.godot/imported/playstation_button_color_circle.svg-5cfa673e7782bd07526a9ee32057bee9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_color_circle_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_color_circle_outline.svg.import index 996a5073d..cde64d3a6 100644 --- a/assets/images/gamepad/playstation/playstation_button_color_circle_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_color_circle_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bdlrfek4gpwlu" +uid="uid://kxhrmgkt4j6l" path="res://.godot/imported/playstation_button_color_circle_outline.svg-f3809e44ca1cf8e2f7720a8d02be68e7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_color_cross.svg.import b/assets/images/gamepad/playstation/playstation_button_color_cross.svg.import index ac0097347..b05d2eec8 100644 --- a/assets/images/gamepad/playstation/playstation_button_color_cross.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_color_cross.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3b15ae2w8u04" +uid="uid://dwv1scwq0mlr2" path="res://.godot/imported/playstation_button_color_cross.svg-a1810f0f7282ce4d1a185fd05ee2b1ca.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_color_cross_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_color_cross_outline.svg.import index 6565d96f9..5d1bee5eb 100644 --- a/assets/images/gamepad/playstation/playstation_button_color_cross_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_color_cross_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cxncdc1y8w3sp" +uid="uid://cidksduldvx2v" path="res://.godot/imported/playstation_button_color_cross_outline.svg-b9e78af561976a35c989f516564d45fa.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_color_square.svg.import b/assets/images/gamepad/playstation/playstation_button_color_square.svg.import index 1495fd1f2..35380fcd9 100644 --- a/assets/images/gamepad/playstation/playstation_button_color_square.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_color_square.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cwmll8jpe01x1" +uid="uid://brniycjajdi03" path="res://.godot/imported/playstation_button_color_square.svg-98c319316dd27cdf59df8db384252907.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_color_square_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_color_square_outline.svg.import index ebfa9379b..b5637ff8c 100644 --- a/assets/images/gamepad/playstation/playstation_button_color_square_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_color_square_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bq7hfbip35326" +uid="uid://576uohl166eg" path="res://.godot/imported/playstation_button_color_square_outline.svg-c3aa6104ea2be5ce6fa5c530465ce1a6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_color_triangle.svg.import b/assets/images/gamepad/playstation/playstation_button_color_triangle.svg.import index aea34ac4e..0781023c7 100644 --- a/assets/images/gamepad/playstation/playstation_button_color_triangle.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_color_triangle.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://crv4tl33mg881" +uid="uid://2rjhdg632n1f" path="res://.godot/imported/playstation_button_color_triangle.svg-173eb7e14a9eec2b2e16e6e0e4d1dfa4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_color_triangle_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_color_triangle_outline.svg.import index 77bc40a71..6ecf3d017 100644 --- a/assets/images/gamepad/playstation/playstation_button_color_triangle_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_color_triangle_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bhl7luffti3jx" +uid="uid://ch0n2tmnawppg" path="res://.godot/imported/playstation_button_color_triangle_outline.svg-42541db9911f67abcf25dcd8d26455bb.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_cross.svg.import b/assets/images/gamepad/playstation/playstation_button_cross.svg.import index dbff995b2..e99b87209 100644 --- a/assets/images/gamepad/playstation/playstation_button_cross.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_cross.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://chby0sf0s01da" +uid="uid://cwkuwr1n1edp3" path="res://.godot/imported/playstation_button_cross.svg-ef60b331443a31ca0df0f6c291bcbd26.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_cross_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_cross_outline.svg.import index f15c5a570..5acb777b6 100644 --- a/assets/images/gamepad/playstation/playstation_button_cross_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_cross_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://38mlnyg8jteh" +uid="uid://bip6t1lpm0g70" path="res://.godot/imported/playstation_button_cross_outline.svg-1c0c614f11f543eb8b021a7fc3c14665.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_l3.svg.import b/assets/images/gamepad/playstation/playstation_button_l3.svg.import index ed8131839..b70a21e58 100644 --- a/assets/images/gamepad/playstation/playstation_button_l3.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_l3.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dmii4b6k52scj" +uid="uid://davo1atqgb71c" path="res://.godot/imported/playstation_button_l3.svg-1bf8269547f4a53d084be922e7f964a0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_l3_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_l3_outline.svg.import index 89e55082f..f6762dd1d 100644 --- a/assets/images/gamepad/playstation/playstation_button_l3_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_l3_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bswoa6047q5ut" +uid="uid://crq5ldbo061bu" path="res://.godot/imported/playstation_button_l3_outline.svg-2950a518a6fb860a34ef16b24aea4742.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_r3.svg.import b/assets/images/gamepad/playstation/playstation_button_r3.svg.import index c0b9e559d..38c835962 100644 --- a/assets/images/gamepad/playstation/playstation_button_r3.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_r3.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cdaevyher8pd1" +uid="uid://bel7cl5p3pocr" path="res://.godot/imported/playstation_button_r3.svg-b7d9339b6c2c3d27ef240ec640aa2d6f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_r3_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_r3_outline.svg.import index c475fd1bb..425b1f8ce 100644 --- a/assets/images/gamepad/playstation/playstation_button_r3_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_r3_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://k8vngthu5ovm" +uid="uid://ccohtptghs0tm" path="res://.godot/imported/playstation_button_r3_outline.svg-f32dc99f4b77f5d5fa4d28bd0dea14a5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_square.svg.import b/assets/images/gamepad/playstation/playstation_button_square.svg.import index 6a5a7d68e..a23eca6e0 100644 --- a/assets/images/gamepad/playstation/playstation_button_square.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_square.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b76tygr2qcdao" +uid="uid://6so8a6c4hfk2" path="res://.godot/imported/playstation_button_square.svg-52371029f4e22483e007e6736baa04d3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_square_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_square_outline.svg.import index 527870013..4a5e015cb 100644 --- a/assets/images/gamepad/playstation/playstation_button_square_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_square_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ds3two3o2kdm6" +uid="uid://bgwp26djot28g" path="res://.godot/imported/playstation_button_square_outline.svg-d9df9280b219a7fa7cb6ed206852a3c4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_triangle.svg.import b/assets/images/gamepad/playstation/playstation_button_triangle.svg.import index 258c3eff4..9a2f0f269 100644 --- a/assets/images/gamepad/playstation/playstation_button_triangle.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_triangle.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bc0hqr1fg775q" +uid="uid://dhx2g6brifdl1" path="res://.godot/imported/playstation_button_triangle.svg-7ced16e5b24906d44b5bb4e5ad89545c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_button_triangle_outline.svg.import b/assets/images/gamepad/playstation/playstation_button_triangle_outline.svg.import index 819338f0e..b2af92eaf 100644 --- a/assets/images/gamepad/playstation/playstation_button_triangle_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_button_triangle_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dutgbovpwfag3" +uid="uid://bpagdu7f64ajc" path="res://.godot/imported/playstation_button_triangle_outline.svg-2dc559fb78b5da14027b3a08cc3f07e5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad.svg.import b/assets/images/gamepad/playstation/playstation_dpad.svg.import index d38ea9d83..fe3658682 100644 --- a/assets/images/gamepad/playstation/playstation_dpad.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d1bn7vfymb8h7" +uid="uid://ua3iri55e4go" path="res://.godot/imported/playstation_dpad.svg-86b907dbc9f02dc055128492cb189ea1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_all.svg.import b/assets/images/gamepad/playstation/playstation_dpad_all.svg.import index 80121eb65..676748069 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_all.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://iuepe8j3hcm7" +uid="uid://cpq3b5bvlki8e" path="res://.godot/imported/playstation_dpad_all.svg-926ae0a7da8a52f9ca2ecf7ff74ee2cd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_down.svg.import b/assets/images/gamepad/playstation/playstation_dpad_down.svg.import index cfe256a6d..446eb0f19 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_down.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b8t8udyk7f2sy" +uid="uid://byblaf3dx4by6" path="res://.godot/imported/playstation_dpad_down.svg-4889c43a9fb288f096ba98f364685725.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_down_outline.svg.import b/assets/images/gamepad/playstation/playstation_dpad_down_outline.svg.import index 678e21b9e..afa09387f 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_down_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bu520uchod3el" +uid="uid://1xrhh3mr2nel" path="res://.godot/imported/playstation_dpad_down_outline.svg-625aa856f9a1beb9bd1ae462ef8af1b5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_horizontal.svg.import b/assets/images/gamepad/playstation/playstation_dpad_horizontal.svg.import index a666cb478..db1f5cde9 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_horizontal.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bfc6pgyss6r64" +uid="uid://cs5bo16yewi0q" path="res://.godot/imported/playstation_dpad_horizontal.svg-91161b3369636347e4325053c8cc1db3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_horizontal_outline.svg.import b/assets/images/gamepad/playstation/playstation_dpad_horizontal_outline.svg.import index 8f2716f9b..0e44d28e0 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_horizontal_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cnmiwfmopy33d" +uid="uid://erwu6qfidj1q" path="res://.godot/imported/playstation_dpad_horizontal_outline.svg-875ab67c538e81ab39fb1359b3dcc0a0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_left.svg.import b/assets/images/gamepad/playstation/playstation_dpad_left.svg.import index 188759a5a..faf5a85b7 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_left.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bucd4u3xhdkn5" +uid="uid://bmuqx6jxetdp1" path="res://.godot/imported/playstation_dpad_left.svg-a1e233bda16263cfee2ec0e8de35360c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_left_outline.svg.import b/assets/images/gamepad/playstation/playstation_dpad_left_outline.svg.import index 2435a5888..5558fe766 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_left_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dghs30vx834ax" +uid="uid://bskdor86prw3q" path="res://.godot/imported/playstation_dpad_left_outline.svg-26d9e6764000e0dcc8c514bb647eb660.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_none.svg.import b/assets/images/gamepad/playstation/playstation_dpad_none.svg.import index a6a43dc8e..ec8d629ce 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_none.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_none.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ejy52tbaldjq" +uid="uid://44ap65odhxm" path="res://.godot/imported/playstation_dpad_none.svg-cfbd352ab93c81e86b48e7bd99c27298.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_right.svg.import b/assets/images/gamepad/playstation/playstation_dpad_right.svg.import index 7822ca26b..29ef59d7e 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_right.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://nva4um5gnp46" +uid="uid://cxhc0rxkyqf2g" path="res://.godot/imported/playstation_dpad_right.svg-135b5076d9e4d84e264d437f96ad5044.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_right_outline.svg.import b/assets/images/gamepad/playstation/playstation_dpad_right_outline.svg.import index 61df3cd5b..ba08d03c5 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_right_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ba4r6538gbgwm" +uid="uid://bb7wo613rvkoj" path="res://.godot/imported/playstation_dpad_right_outline.svg-d9713bf03043834ed1c1a64dbba5f020.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_up.svg.import b/assets/images/gamepad/playstation/playstation_dpad_up.svg.import index a03bd74df..9934db16d 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_up.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dvckjbn44kqh4" +uid="uid://da1vcs31c68sx" path="res://.godot/imported/playstation_dpad_up.svg-def95acecdef794dfc14c4de9eacd85a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_up_outline.svg.import b/assets/images/gamepad/playstation/playstation_dpad_up_outline.svg.import index b423c97c2..d63c4e86c 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_up_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cu2xh15ccdt0m" +uid="uid://c745ykeqdi8v" path="res://.godot/imported/playstation_dpad_up_outline.svg-395f31d79e66ae06c55fdcb09e1c4afc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_vertical.svg.import b/assets/images/gamepad/playstation/playstation_dpad_vertical.svg.import index 7a474ad3b..0be8ddfa3 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_vertical.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://7efn74unxey0" +uid="uid://dkl4tiwec0s3r" path="res://.godot/imported/playstation_dpad_vertical.svg-aac1e207d1fd459caa1c6a52e4f1dd7f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_dpad_vertical_outline.svg.import b/assets/images/gamepad/playstation/playstation_dpad_vertical_outline.svg.import index f4e68e085..b61c88b6a 100644 --- a/assets/images/gamepad/playstation/playstation_dpad_vertical_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_dpad_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c525mej5lvygi" +uid="uid://ddm45gl40aoa1" path="res://.godot/imported/playstation_dpad_vertical_outline.svg-590fe1643f5ddb3ee600c7cef1d7d932.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_l.svg.import b/assets/images/gamepad/playstation/playstation_stick_l.svg.import index 978cd70a4..224436e64 100644 --- a/assets/images/gamepad/playstation/playstation_stick_l.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bq64rpsb1usfh" +uid="uid://d0hn6un6byklr" path="res://.godot/imported/playstation_stick_l.svg-78771de1cc4486570e8ee7f4ba8f10a3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_l_down.svg.import b/assets/images/gamepad/playstation/playstation_stick_l_down.svg.import index c067dd00b..d2b6f359a 100644 --- a/assets/images/gamepad/playstation/playstation_stick_l_down.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_l_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cv1gammxiedb" +uid="uid://od86dbeeirmp" path="res://.godot/imported/playstation_stick_l_down.svg-0a9bcfec30b1217ec41bae800564a666.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_l_horizontal.svg.import b/assets/images/gamepad/playstation/playstation_stick_l_horizontal.svg.import index c5e5b659d..599bda249 100644 --- a/assets/images/gamepad/playstation/playstation_stick_l_horizontal.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_l_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://2vnyidlvl542" +uid="uid://c2ynlxwt4h4kp" path="res://.godot/imported/playstation_stick_l_horizontal.svg-05799650ff3c647f3da4d6f395d81859.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_l_left.svg.import b/assets/images/gamepad/playstation/playstation_stick_l_left.svg.import index 70dc9e7a7..fc5895fa7 100644 --- a/assets/images/gamepad/playstation/playstation_stick_l_left.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_l_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d22o4cxlcni3q" +uid="uid://bufyl7xm4mcij" path="res://.godot/imported/playstation_stick_l_left.svg-f5f49c466ce685378f701c1173edb9f5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_l_press.svg.import b/assets/images/gamepad/playstation/playstation_stick_l_press.svg.import index afeada187..fc764af1a 100644 --- a/assets/images/gamepad/playstation/playstation_stick_l_press.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_l_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dbf2kolrjqlsk" +uid="uid://cmijt22flcpda" path="res://.godot/imported/playstation_stick_l_press.svg-6198ab93afcc5042573cd8c40074db46.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_l_right.svg.import b/assets/images/gamepad/playstation/playstation_stick_l_right.svg.import index 21c588b1a..3fac935a7 100644 --- a/assets/images/gamepad/playstation/playstation_stick_l_right.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_l_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://0bomms2ipyct" +uid="uid://bj8fx1k8ectnx" path="res://.godot/imported/playstation_stick_l_right.svg-96fbddfa8f7e4235d145fa8b98efa2a3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_l_up.svg.import b/assets/images/gamepad/playstation/playstation_stick_l_up.svg.import index 50deaa3f6..31a0a6206 100644 --- a/assets/images/gamepad/playstation/playstation_stick_l_up.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_l_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ds60ucempaqtc" +uid="uid://c8uwdhedkaonn" path="res://.godot/imported/playstation_stick_l_up.svg-84c17c66b9e0a056bada8c06d9c6a1b0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_l_vertical.svg.import b/assets/images/gamepad/playstation/playstation_stick_l_vertical.svg.import index fad950c52..aa0245d99 100644 --- a/assets/images/gamepad/playstation/playstation_stick_l_vertical.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_l_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bapyv15bfbax0" +uid="uid://bfgooboa5sf22" path="res://.godot/imported/playstation_stick_l_vertical.svg-013d3c28e5b3783b611056738e685711.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_r.svg.import b/assets/images/gamepad/playstation/playstation_stick_r.svg.import index 62fe18c18..2118a991e 100644 --- a/assets/images/gamepad/playstation/playstation_stick_r.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://byrtduc2ys2gd" +uid="uid://dlh3o8lhabxmv" path="res://.godot/imported/playstation_stick_r.svg-21eb25318ffa23b186e9a7aad20f1a54.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_r_down.svg.import b/assets/images/gamepad/playstation/playstation_stick_r_down.svg.import index 5267a9b10..3f09a79bf 100644 --- a/assets/images/gamepad/playstation/playstation_stick_r_down.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_r_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b0hdbw4qo4nl2" +uid="uid://do6olohpve0fm" path="res://.godot/imported/playstation_stick_r_down.svg-d6136347d7764141309e1a27582c4ace.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_r_horizontal.svg.import b/assets/images/gamepad/playstation/playstation_stick_r_horizontal.svg.import index 35076143c..9d338bf42 100644 --- a/assets/images/gamepad/playstation/playstation_stick_r_horizontal.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_r_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c4ia0why4c72a" +uid="uid://bcfgvh21ubjop" path="res://.godot/imported/playstation_stick_r_horizontal.svg-98948b806f38efdb17315a06592ca911.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_r_left.svg.import b/assets/images/gamepad/playstation/playstation_stick_r_left.svg.import index 4871a2b59..fba52aec3 100644 --- a/assets/images/gamepad/playstation/playstation_stick_r_left.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_r_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dedewmjipppck" +uid="uid://7uwvswwxu8u5" path="res://.godot/imported/playstation_stick_r_left.svg-0fe661862efaf4c979ab634970041f50.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_r_press.svg.import b/assets/images/gamepad/playstation/playstation_stick_r_press.svg.import index 637bb0928..70ea624d7 100644 --- a/assets/images/gamepad/playstation/playstation_stick_r_press.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_r_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dnc8ia8fbc72f" +uid="uid://ctsqjuqhj0soc" path="res://.godot/imported/playstation_stick_r_press.svg-b2a99765532bf7c6f9d63b280174ddee.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_r_right.svg.import b/assets/images/gamepad/playstation/playstation_stick_r_right.svg.import index ffc7cd8c7..3eb06a09d 100644 --- a/assets/images/gamepad/playstation/playstation_stick_r_right.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_r_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d2f87ubl8ut4t" +uid="uid://d2t2wxh6fduon" path="res://.godot/imported/playstation_stick_r_right.svg-b70ec3d6dab7f3901ffa9e2dc6c1089d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_r_up.svg.import b/assets/images/gamepad/playstation/playstation_stick_r_up.svg.import index ff50ecbab..0502dd876 100644 --- a/assets/images/gamepad/playstation/playstation_stick_r_up.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_r_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvjxqt1lsfp1t" +uid="uid://csve53s1buixo" path="res://.godot/imported/playstation_stick_r_up.svg-33ed81f8863b64e5b1c3e2cda0bbd3c1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_r_vertical.svg.import b/assets/images/gamepad/playstation/playstation_stick_r_vertical.svg.import index c8ee96683..061532e82 100644 --- a/assets/images/gamepad/playstation/playstation_stick_r_vertical.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_r_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b7xquxe1drevm" +uid="uid://mmdrtvbfqgwp" path="res://.godot/imported/playstation_stick_r_vertical.svg-412416e1a8d54bc81ba3247ac5c343df.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_side_l.svg.import b/assets/images/gamepad/playstation/playstation_stick_side_l.svg.import index abaa27890..6c223363a 100644 --- a/assets/images/gamepad/playstation/playstation_stick_side_l.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_side_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://43gjagfbavpy" +uid="uid://dyvp38qrpane7" path="res://.godot/imported/playstation_stick_side_l.svg-537935c59a0fdcbe14b06e4047bad67c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_side_r.svg.import b/assets/images/gamepad/playstation/playstation_stick_side_r.svg.import index 8fda4683e..6def22aa1 100644 --- a/assets/images/gamepad/playstation/playstation_stick_side_r.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_side_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bl8cbv7k17etp" +uid="uid://dkotgp8doeykf" path="res://.godot/imported/playstation_stick_side_r.svg-4bf0ddcb5182bf926f23b36a21fa93bd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_top_l.svg.import b/assets/images/gamepad/playstation/playstation_stick_top_l.svg.import index ad43d9743..f017a6458 100644 --- a/assets/images/gamepad/playstation/playstation_stick_top_l.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_top_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bgpfotacv32ph" +uid="uid://dup0tccpfm6rj" path="res://.godot/imported/playstation_stick_top_l.svg-417cbe94b020bd679dfaaeedced0a5c0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_stick_top_r.svg.import b/assets/images/gamepad/playstation/playstation_stick_top_r.svg.import index de256bcff..83281db87 100644 --- a/assets/images/gamepad/playstation/playstation_stick_top_r.svg.import +++ b/assets/images/gamepad/playstation/playstation_stick_top_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bgkj3lsr5rfwp" +uid="uid://dt5hlmc48at3x" path="res://.godot/imported/playstation_stick_top_r.svg-a44a7853e0bfcb189d8b2c91e8056598.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_l1.svg.import b/assets/images/gamepad/playstation/playstation_trigger_l1.svg.import index f31ed442b..cc83fe4a1 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_l1.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_l1.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ce50wyq1ctdg3" +uid="uid://bkya1ndod3v1j" path="res://.godot/imported/playstation_trigger_l1.svg-1a7f953a755b39a459d1afbf2d7d2d9d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_l1_alternative.svg.import b/assets/images/gamepad/playstation/playstation_trigger_l1_alternative.svg.import index 010003044..be75e5c9f 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_l1_alternative.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_l1_alternative.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dxbqk8u10kwig" +uid="uid://ch70gfvca2q1m" path="res://.godot/imported/playstation_trigger_l1_alternative.svg-5cb68c8b0e5c5ff08c63409265883401.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_l1_alternative_outline.svg.import b/assets/images/gamepad/playstation/playstation_trigger_l1_alternative_outline.svg.import index 21910f811..de33bc9e3 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_l1_alternative_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_l1_alternative_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://g3e7jhclqs71" +uid="uid://dri3xldq3kdmo" path="res://.godot/imported/playstation_trigger_l1_alternative_outline.svg-97dc03ec737b5ecf31b755449e52c455.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_l1_outline.svg.import b/assets/images/gamepad/playstation/playstation_trigger_l1_outline.svg.import index f0fa38c10..acc57c6d8 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_l1_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_l1_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://460hvpu6xeu7" +uid="uid://qkv44ew2xt7w" path="res://.godot/imported/playstation_trigger_l1_outline.svg-5aaa1ea5fe4f6ae2ba2294cb5c3efb80.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_l2.svg.import b/assets/images/gamepad/playstation/playstation_trigger_l2.svg.import index d61b36430..9dee0da12 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_l2.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_l2.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d0pbaqstutkli" +uid="uid://dcnvqtiuawl0c" path="res://.godot/imported/playstation_trigger_l2.svg-2a8f6aabbb3e4f3fc1e7b9ced3f9415d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_l2_alternative.svg.import b/assets/images/gamepad/playstation/playstation_trigger_l2_alternative.svg.import index 1cc64cae6..1119973ac 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_l2_alternative.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_l2_alternative.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://2kni34s7n11k" +uid="uid://1ic45xwvjvd6" path="res://.godot/imported/playstation_trigger_l2_alternative.svg-62d4698a2b264205d33e077ac964fc88.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_l2_alternative_outline.svg.import b/assets/images/gamepad/playstation/playstation_trigger_l2_alternative_outline.svg.import index 7c5094cbc..e221c2c9c 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_l2_alternative_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_l2_alternative_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bhdhfpi3vye80" +uid="uid://d3y536qvsu1bq" path="res://.godot/imported/playstation_trigger_l2_alternative_outline.svg-83ee2242d0024a0e67467305556bfb23.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_l2_outline.svg.import b/assets/images/gamepad/playstation/playstation_trigger_l2_outline.svg.import index 9b9593426..f5a47759c 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_l2_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_l2_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dhanoxbu76h6m" +uid="uid://bupb1c0xpsta" path="res://.godot/imported/playstation_trigger_l2_outline.svg-db5c82e5fc75e2497088f296395666c3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_r1.svg.import b/assets/images/gamepad/playstation/playstation_trigger_r1.svg.import index 70c24a795..928c29943 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_r1.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_r1.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://drbydhj23dk4v" +uid="uid://f2ge3igc0h6g" path="res://.godot/imported/playstation_trigger_r1.svg-c3a2982f1ed177c78e338ff739e1bd5a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_r1_alternative.svg.import b/assets/images/gamepad/playstation/playstation_trigger_r1_alternative.svg.import index 3bf7cfde6..a59352b18 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_r1_alternative.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_r1_alternative.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cw1l4imye15u2" +uid="uid://1he8y6gxecgc" path="res://.godot/imported/playstation_trigger_r1_alternative.svg-22278c7f3a77b8ab38074e3ba6d3c4f5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_r1_alternative_outline.svg.import b/assets/images/gamepad/playstation/playstation_trigger_r1_alternative_outline.svg.import index 25b853886..850b118a6 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_r1_alternative_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_r1_alternative_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://qlitalglh3us" +uid="uid://civko3bru1bnc" path="res://.godot/imported/playstation_trigger_r1_alternative_outline.svg-44ef2e153afdababeba17288d04b4d08.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_r1_outline.svg.import b/assets/images/gamepad/playstation/playstation_trigger_r1_outline.svg.import index 064c4bf86..2a31988df 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_r1_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_r1_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d430lpfy8dws" +uid="uid://bowphl0vp8xuy" path="res://.godot/imported/playstation_trigger_r1_outline.svg-2aa389fa1fbb610b0a7fd41f0cb60322.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_r2.svg.import b/assets/images/gamepad/playstation/playstation_trigger_r2.svg.import index 5c9878a8e..df06ef4d2 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_r2.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_r2.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://botqou7j5tw5i" +uid="uid://doeikq8t8xfny" path="res://.godot/imported/playstation_trigger_r2.svg-c94d5ef5ced1efe3106d760cf3b47154.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_r2_alternative.svg.import b/assets/images/gamepad/playstation/playstation_trigger_r2_alternative.svg.import index 91faa340c..4b7dae97b 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_r2_alternative.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_r2_alternative.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bk8wdjsgrtcb8" +uid="uid://c8fiv430upxks" path="res://.godot/imported/playstation_trigger_r2_alternative.svg-e1f88b83548eeae507f33ab0760e0785.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_r2_alternative_outline.svg.import b/assets/images/gamepad/playstation/playstation_trigger_r2_alternative_outline.svg.import index 30999e791..1d5732007 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_r2_alternative_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_r2_alternative_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dg64rtw5ya867" +uid="uid://b63uyotxfiq0r" path="res://.godot/imported/playstation_trigger_r2_alternative_outline.svg-ce4b4e24e971d4554a2c1dfdc4380ab3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/playstation/playstation_trigger_r2_outline.svg.import b/assets/images/gamepad/playstation/playstation_trigger_r2_outline.svg.import index 9a91908a8..e95883fbd 100644 --- a/assets/images/gamepad/playstation/playstation_trigger_r2_outline.svg.import +++ b/assets/images/gamepad/playstation/playstation_trigger_r2_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c8m3knpevw1mj" +uid="uid://d0rkwq62acgjo" path="res://.godot/imported/playstation_trigger_r2_outline.svg-e34198810f385ed653d0099366eb3af6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/rog-ally/ally_button_guide.svg.import b/assets/images/gamepad/rog-ally/ally_button_guide.svg.import index b240eb9c9..697bd365f 100644 --- a/assets/images/gamepad/rog-ally/ally_button_guide.svg.import +++ b/assets/images/gamepad/rog-ally/ally_button_guide.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://brctkdj250hpv" +uid="uid://cl1gl3j47wds5" path="res://.godot/imported/ally_button_guide.svg-4dea71f5533fa80bed794218bac702d3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/rog-ally/ally_button_m1.svg.import b/assets/images/gamepad/rog-ally/ally_button_m1.svg.import index 2e5407b07..be52a0cf8 100644 --- a/assets/images/gamepad/rog-ally/ally_button_m1.svg.import +++ b/assets/images/gamepad/rog-ally/ally_button_m1.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://obqv3ikblrmx" +uid="uid://bwqadttdisdkl" path="res://.godot/imported/ally_button_m1.svg-2209cf215426742e59419468852c7f76.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/rog-ally/ally_button_m2.svg.import b/assets/images/gamepad/rog-ally/ally_button_m2.svg.import index e26d957c8..6cd4db3dd 100644 --- a/assets/images/gamepad/rog-ally/ally_button_m2.svg.import +++ b/assets/images/gamepad/rog-ally/ally_button_m2.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3nga7v4ldmap" +uid="uid://74k3jdksgydd" path="res://.godot/imported/ally_button_m2.svg-fb574cb511a98a7db85a8542a4eeba16.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/rog-ally/ally_button_menu.svg.import b/assets/images/gamepad/rog-ally/ally_button_menu.svg.import index d35e6c0fc..ed299fe60 100644 --- a/assets/images/gamepad/rog-ally/ally_button_menu.svg.import +++ b/assets/images/gamepad/rog-ally/ally_button_menu.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://csxxwf7pgahkb" +uid="uid://d4gi4bucbgyhv" path="res://.godot/imported/ally_button_menu.svg-5bc275dc6fe1008aaba7812362c79d13.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/rog-ally/ally_button_quick.svg.import b/assets/images/gamepad/rog-ally/ally_button_quick.svg.import index 24658736b..1f80dc2cd 100644 --- a/assets/images/gamepad/rog-ally/ally_button_quick.svg.import +++ b/assets/images/gamepad/rog-ally/ally_button_quick.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://srjhf8dp2cpg" +uid="uid://dg7xaotlsxoix" path="res://.godot/imported/ally_button_quick.svg-ff312a6b5a60c8bd3b1277ae2f6f750f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/rog-ally/ally_button_view.svg.import b/assets/images/gamepad/rog-ally/ally_button_view.svg.import index c96420504..3af054767 100644 --- a/assets/images/gamepad/rog-ally/ally_button_view.svg.import +++ b/assets/images/gamepad/rog-ally/ally_button_view.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cu0vttshpc7ui" +uid="uid://bcrthlw3xno0p" path="res://.godot/imported/ally_button_view.svg-e9edf14585505f3aeb57f7909fcfa542.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/rog-ally/diagram.png.import b/assets/images/gamepad/rog-ally/diagram.png.import index 676907366..5f3446ea7 100644 --- a/assets/images/gamepad/rog-ally/diagram.png.import +++ b/assets/images/gamepad/rog-ally/diagram.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://xm6mg1kykt88" +uid="uid://c5tmyp3bdtksr" path="res://.godot/imported/diagram.png-f99b5efd3297c6be62f3cd7c6032cbe7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/controller_steam.svg.import b/assets/images/gamepad/steam/controller_steam.svg.import index 02b08ec1f..6ea02a5a3 100644 --- a/assets/images/gamepad/steam/controller_steam.svg.import +++ b/assets/images/gamepad/steam/controller_steam.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c8b8706k0iryj" +uid="uid://cst2i5us0vdjy" path="res://.godot/imported/controller_steam.svg-62c5680d992df27a89be5b43451be65d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_a.svg.import b/assets/images/gamepad/steam/steam_button_a.svg.import index bad7917c5..5d228ffb6 100644 --- a/assets/images/gamepad/steam/steam_button_a.svg.import +++ b/assets/images/gamepad/steam/steam_button_a.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d1wgo1ahf04x2" +uid="uid://m7frkhqu2tch" path="res://.godot/imported/steam_button_a.svg-d2022bdec8581dec2a4f53c219b80d20.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_a_outline.svg.import b/assets/images/gamepad/steam/steam_button_a_outline.svg.import index f5f7b3f3d..66f7c2c44 100644 --- a/assets/images/gamepad/steam/steam_button_a_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_a_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b0d21hv13mxg8" +uid="uid://bg0xisuykiukf" path="res://.godot/imported/steam_button_a_outline.svg-a2011c3d2a870ce3b74802549bb3e376.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_b.svg.import b/assets/images/gamepad/steam/steam_button_b.svg.import index a7cbfcc86..ffce38f76 100644 --- a/assets/images/gamepad/steam/steam_button_b.svg.import +++ b/assets/images/gamepad/steam/steam_button_b.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b0unrfy7gwj1p" +uid="uid://btth5l4hrc7gt" path="res://.godot/imported/steam_button_b.svg-98263f03935b05edbd6dda5a5bba99b9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_b_outline.svg.import b/assets/images/gamepad/steam/steam_button_b_outline.svg.import index fe949974b..58ec93c93 100644 --- a/assets/images/gamepad/steam/steam_button_b_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_b_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bmqdohrxqoeom" +uid="uid://btg8nwed8br7a" path="res://.godot/imported/steam_button_b_outline.svg-e2f15b2dd4bff4950ec24bfd138bc584.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_back_icon.svg.import b/assets/images/gamepad/steam/steam_button_back_icon.svg.import index c965a03f2..4ff0735b1 100644 --- a/assets/images/gamepad/steam/steam_button_back_icon.svg.import +++ b/assets/images/gamepad/steam/steam_button_back_icon.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dpgjaie64o2j7" +uid="uid://cmnjax5k8juxb" path="res://.godot/imported/steam_button_back_icon.svg-8c7b3e3d12fbe79c9b07a90a0b16d8bd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_back_icon_outline.svg.import b/assets/images/gamepad/steam/steam_button_back_icon_outline.svg.import index aa5fa8a25..4be10ca3a 100644 --- a/assets/images/gamepad/steam/steam_button_back_icon_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_back_icon_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://hcl376g1xwbp" +uid="uid://cv6674gwc2efo" path="res://.godot/imported/steam_button_back_icon_outline.svg-55310bfad7faa509e99bef8366f3ffe6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_color_a.svg.import b/assets/images/gamepad/steam/steam_button_color_a.svg.import index 71f9f85d5..bb6049247 100644 --- a/assets/images/gamepad/steam/steam_button_color_a.svg.import +++ b/assets/images/gamepad/steam/steam_button_color_a.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bpkm2swtktdf0" +uid="uid://1q1vr24dkc26" path="res://.godot/imported/steam_button_color_a.svg-e57c6075a8ae2d4698eb9074f7a01f6c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_color_a_outline.svg.import b/assets/images/gamepad/steam/steam_button_color_a_outline.svg.import index 1bfe9f581..83fe9a247 100644 --- a/assets/images/gamepad/steam/steam_button_color_a_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_color_a_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dkrl63y6pkf64" +uid="uid://bwgr3ta8sao1i" path="res://.godot/imported/steam_button_color_a_outline.svg-f2baa759d2b97b064666c297d774b008.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_color_b.svg.import b/assets/images/gamepad/steam/steam_button_color_b.svg.import index 60d086cec..fef3ecf9b 100644 --- a/assets/images/gamepad/steam/steam_button_color_b.svg.import +++ b/assets/images/gamepad/steam/steam_button_color_b.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c435jhogs3n8o" +uid="uid://b730xukgcsh2k" path="res://.godot/imported/steam_button_color_b.svg-504c889ae1df23c8606bb98ae339f224.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_color_b_outline.svg.import b/assets/images/gamepad/steam/steam_button_color_b_outline.svg.import index 4b7ecf5c2..d4a110d9d 100644 --- a/assets/images/gamepad/steam/steam_button_color_b_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_color_b_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b611xwsk4jd8g" +uid="uid://dm3wn6k4gdhxv" path="res://.godot/imported/steam_button_color_b_outline.svg-0e1fb99cb2b144bab056a34805fcbd17.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_color_x.svg.import b/assets/images/gamepad/steam/steam_button_color_x.svg.import index f184da81b..bb000ccca 100644 --- a/assets/images/gamepad/steam/steam_button_color_x.svg.import +++ b/assets/images/gamepad/steam/steam_button_color_x.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://gm2fk8y4xrig" +uid="uid://ctvb5ylheu2nm" path="res://.godot/imported/steam_button_color_x.svg-8d6c4bbc64b2fc71f30a8373d0780939.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_color_x_outline.svg.import b/assets/images/gamepad/steam/steam_button_color_x_outline.svg.import index 9ac254522..1ea45fd46 100644 --- a/assets/images/gamepad/steam/steam_button_color_x_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_color_x_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://gr1h0bths2hq" +uid="uid://b6rc56egbak4k" path="res://.godot/imported/steam_button_color_x_outline.svg-7bff64d6fa59c13267e6b969b3ac90c2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_color_y.svg.import b/assets/images/gamepad/steam/steam_button_color_y.svg.import index cedd2d0bf..96c2bff08 100644 --- a/assets/images/gamepad/steam/steam_button_color_y.svg.import +++ b/assets/images/gamepad/steam/steam_button_color_y.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://01a6rkyglcse" +uid="uid://dl6mkuxwnau6c" path="res://.godot/imported/steam_button_color_y.svg-cdd12d662f695aa12321a0edc59476d2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_color_y_outline.svg.import b/assets/images/gamepad/steam/steam_button_color_y_outline.svg.import index 151b27909..ab65f0227 100644 --- a/assets/images/gamepad/steam/steam_button_color_y_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_color_y_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d10m4l7a4ka84" +uid="uid://6tnv6sfkkph5" path="res://.godot/imported/steam_button_color_y_outline.svg-80eca862bfbd50dc574394e812306f1e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_lp.svg.import b/assets/images/gamepad/steam/steam_button_lp.svg.import index 0e9488a3f..3fe326186 100644 --- a/assets/images/gamepad/steam/steam_button_lp.svg.import +++ b/assets/images/gamepad/steam/steam_button_lp.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://com8m05m2uqco" +uid="uid://mphqdrq0v6nv" path="res://.godot/imported/steam_button_lp.svg-f44d6df7cc16ef1c3fe7ac4513d5f5f5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_lp_outline.svg.import b/assets/images/gamepad/steam/steam_button_lp_outline.svg.import index 42b680de3..6496d9cf7 100644 --- a/assets/images/gamepad/steam/steam_button_lp_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_lp_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b3w6ihl1r6saj" +uid="uid://co68m1fx51rnp" path="res://.godot/imported/steam_button_lp_outline.svg-a9673ce26d4d1879535b7da9d7cad6e2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_rp.svg.import b/assets/images/gamepad/steam/steam_button_rp.svg.import index c68ace787..b7133e5a9 100644 --- a/assets/images/gamepad/steam/steam_button_rp.svg.import +++ b/assets/images/gamepad/steam/steam_button_rp.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cer0ys0nbh0no" +uid="uid://djidib24hy7sf" path="res://.godot/imported/steam_button_rp.svg-3048616445b39be3b96594953c16172a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_rp_outline.svg.import b/assets/images/gamepad/steam/steam_button_rp_outline.svg.import index 246a36594..bdadf715f 100644 --- a/assets/images/gamepad/steam/steam_button_rp_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_rp_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ylpv7gaw5qo5" +uid="uid://77ss42pw0tl7" path="res://.godot/imported/steam_button_rp_outline.svg-5b10b3e460186307a7f9a27db60c304b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_start_icon.svg.import b/assets/images/gamepad/steam/steam_button_start_icon.svg.import index d2409c6c6..d6e858d56 100644 --- a/assets/images/gamepad/steam/steam_button_start_icon.svg.import +++ b/assets/images/gamepad/steam/steam_button_start_icon.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://drhbwe1m16fx7" +uid="uid://4lase2m3005j" path="res://.godot/imported/steam_button_start_icon.svg-82778ba4170b4455744b14ddb4669092.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_start_icon_outline.svg.import b/assets/images/gamepad/steam/steam_button_start_icon_outline.svg.import index 87c9f9048..19a19b342 100644 --- a/assets/images/gamepad/steam/steam_button_start_icon_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_start_icon_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bximbxuv2c214" +uid="uid://brwod1hqn3pow" path="res://.godot/imported/steam_button_start_icon_outline.svg-6fd7ca17c9705b4a525b2331c792da95.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_x.svg.import b/assets/images/gamepad/steam/steam_button_x.svg.import index 170a6e218..f234ff888 100644 --- a/assets/images/gamepad/steam/steam_button_x.svg.import +++ b/assets/images/gamepad/steam/steam_button_x.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cbhyatwdtbboq" +uid="uid://d0e7vl3u4pv2i" path="res://.godot/imported/steam_button_x.svg-9cd005dab1d0b3e91c6feaa39834442d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_x_outline.svg.import b/assets/images/gamepad/steam/steam_button_x_outline.svg.import index c4b190315..c2ca841f3 100644 --- a/assets/images/gamepad/steam/steam_button_x_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_x_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dr6pj7dkcci8d" +uid="uid://d4ctfdb13smyb" path="res://.godot/imported/steam_button_x_outline.svg-1c1031f09ff40b7b336c61601c495bf6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_y.svg.import b/assets/images/gamepad/steam/steam_button_y.svg.import index 3da1f4498..b6cb17b62 100644 --- a/assets/images/gamepad/steam/steam_button_y.svg.import +++ b/assets/images/gamepad/steam/steam_button_y.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://7auey4qjw3cw" +uid="uid://btisptq5v3eeu" path="res://.godot/imported/steam_button_y.svg-685f1d4a78e54a1f823cc5b2264777f4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_button_y_outline.svg.import b/assets/images/gamepad/steam/steam_button_y_outline.svg.import index c952fc10e..4ac592370 100644 --- a/assets/images/gamepad/steam/steam_button_y_outline.svg.import +++ b/assets/images/gamepad/steam/steam_button_y_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://1bfwtcexppnp" +uid="uid://c7ymjfhrpetd1" path="res://.godot/imported/steam_button_y_outline.svg-22cdaaaf10fedddd95fdcf7723d9cbfc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad.svg.import b/assets/images/gamepad/steam/steam_dpad.svg.import index bc36aacda..185ab2d3a 100644 --- a/assets/images/gamepad/steam/steam_dpad.svg.import +++ b/assets/images/gamepad/steam/steam_dpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dnia1n0cogcvv" +uid="uid://dr4giue83b7ly" path="res://.godot/imported/steam_dpad.svg-20f150b454300110333909ef7d33c6f9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_all.svg.import b/assets/images/gamepad/steam/steam_dpad_all.svg.import index 2c2e6729b..62bc06d30 100644 --- a/assets/images/gamepad/steam/steam_dpad_all.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://tlt8jgn42cbr" +uid="uid://hskiho52kmev" path="res://.godot/imported/steam_dpad_all.svg-57fe97eec128a7e743182074184937dd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_down.svg.import b/assets/images/gamepad/steam/steam_dpad_down.svg.import index 4adf9bf91..748051af3 100644 --- a/assets/images/gamepad/steam/steam_dpad_down.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b36ese18p42qf" +uid="uid://hjxtuq87i2s8" path="res://.godot/imported/steam_dpad_down.svg-6195a0e733febce2ebcfaa2317835c84.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_down_outline.svg.import b/assets/images/gamepad/steam/steam_dpad_down_outline.svg.import index 3c45be9e7..cb0ec2854 100644 --- a/assets/images/gamepad/steam/steam_dpad_down_outline.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cwmtqdi0hbsu5" +uid="uid://hyubwe6uq6ix" path="res://.godot/imported/steam_dpad_down_outline.svg-5ecec87eb41a657ae5d3920d8fc16eb6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_horizontal.svg.import b/assets/images/gamepad/steam/steam_dpad_horizontal.svg.import index 7bda8bbc8..c5d266188 100644 --- a/assets/images/gamepad/steam/steam_dpad_horizontal.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cnwse2l7xhl26" +uid="uid://ce8plfafnl4ds" path="res://.godot/imported/steam_dpad_horizontal.svg-a3318b8ea5525e416d28b70b7d29c7fa.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_horizontal_outline.svg.import b/assets/images/gamepad/steam/steam_dpad_horizontal_outline.svg.import index c6b895f18..a697d6bdb 100644 --- a/assets/images/gamepad/steam/steam_dpad_horizontal_outline.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://i7q0vemc5kug" +uid="uid://c3sm6y4gi7jry" path="res://.godot/imported/steam_dpad_horizontal_outline.svg-3a72fde31146bb1d9eb4c0a570eeaa45.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_left.svg.import b/assets/images/gamepad/steam/steam_dpad_left.svg.import index 6a47b2ec8..b8ae9e821 100644 --- a/assets/images/gamepad/steam/steam_dpad_left.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://curpocxiiytm1" +uid="uid://dlt00cmdhrufc" path="res://.godot/imported/steam_dpad_left.svg-f02c48628a55c5667b171cdb6e10ec61.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_left_outline.svg.import b/assets/images/gamepad/steam/steam_dpad_left_outline.svg.import index 6f08ab756..2e5d422ad 100644 --- a/assets/images/gamepad/steam/steam_dpad_left_outline.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dvw6oeujbypqi" +uid="uid://dx75o4inwpu7" path="res://.godot/imported/steam_dpad_left_outline.svg-5434e37d868495d5c6c8ddc847b0b8c7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_none.svg.import b/assets/images/gamepad/steam/steam_dpad_none.svg.import index 344d19fe6..edca55095 100644 --- a/assets/images/gamepad/steam/steam_dpad_none.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_none.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://eatx11haoxew" +uid="uid://du76byhoab751" path="res://.godot/imported/steam_dpad_none.svg-2d8476e7eeef3df1bc4bf9cc9d1494b3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_right.svg.import b/assets/images/gamepad/steam/steam_dpad_right.svg.import index 84905f5d2..7a48664d0 100644 --- a/assets/images/gamepad/steam/steam_dpad_right.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bpws8otkr23ss" +uid="uid://dwopaos8mykma" path="res://.godot/imported/steam_dpad_right.svg-08180a7ed815f68bb4f114ff4ee1e3ea.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_right_outline.svg.import b/assets/images/gamepad/steam/steam_dpad_right_outline.svg.import index 15172598a..75d1ff575 100644 --- a/assets/images/gamepad/steam/steam_dpad_right_outline.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ipy1kflhf03t" +uid="uid://bxtngwdstx1sc" path="res://.godot/imported/steam_dpad_right_outline.svg-eabbc0656426185bf685e06dcca3d1d6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_up.svg.import b/assets/images/gamepad/steam/steam_dpad_up.svg.import index 3bb1a4770..0fabafa08 100644 --- a/assets/images/gamepad/steam/steam_dpad_up.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cmlvutjr6mn8r" +uid="uid://u6ehkqpdr5dn" path="res://.godot/imported/steam_dpad_up.svg-34c15774c5b8e0667e482957cc589931.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_up_outline.svg.import b/assets/images/gamepad/steam/steam_dpad_up_outline.svg.import index 21326766a..1f3f0ad77 100644 --- a/assets/images/gamepad/steam/steam_dpad_up_outline.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b5xsiltlb0era" +uid="uid://cjwbnerecy2gf" path="res://.godot/imported/steam_dpad_up_outline.svg-be5f6c67ba9c2c82ebda8b73f3b31d91.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_vertical.svg.import b/assets/images/gamepad/steam/steam_dpad_vertical.svg.import index 752a7df37..c06315f4c 100644 --- a/assets/images/gamepad/steam/steam_dpad_vertical.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cxysy67ne3dvn" +uid="uid://d2fj6mripsovk" path="res://.godot/imported/steam_dpad_vertical.svg-31b4e5461fde01da04ecedbf6d63ba9f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_dpad_vertical_outline.svg.import b/assets/images/gamepad/steam/steam_dpad_vertical_outline.svg.import index f7bae40f0..7f7ce3126 100644 --- a/assets/images/gamepad/steam/steam_dpad_vertical_outline.svg.import +++ b/assets/images/gamepad/steam/steam_dpad_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cmr0lnbcsq6bf" +uid="uid://mu3mwpq1o12c" path="res://.godot/imported/steam_dpad_vertical_outline.svg-6e1865793d97cafa03f690b211cc5a88.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_lb.svg.import b/assets/images/gamepad/steam/steam_lb.svg.import index cc3cf39ad..c6d95c1a3 100644 --- a/assets/images/gamepad/steam/steam_lb.svg.import +++ b/assets/images/gamepad/steam/steam_lb.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://o5y7nyyxy31y" +uid="uid://b501gsjclnmu1" path="res://.godot/imported/steam_lb.svg-ebdaaa7816cc3b103231afd349e36652.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_lb_outline.svg.import b/assets/images/gamepad/steam/steam_lb_outline.svg.import index d72845de9..6b01c5cb8 100644 --- a/assets/images/gamepad/steam/steam_lb_outline.svg.import +++ b/assets/images/gamepad/steam/steam_lb_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c8l4akyxxy163" +uid="uid://bgeesij5jtsp4" path="res://.godot/imported/steam_lb_outline.svg-8a2d3960a576a1b32b34fc03e78d364d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_lg.svg.import b/assets/images/gamepad/steam/steam_lg.svg.import index 0349e071c..56cdc0441 100644 --- a/assets/images/gamepad/steam/steam_lg.svg.import +++ b/assets/images/gamepad/steam/steam_lg.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://7jx3hwdcd28t" +uid="uid://bxjvcfty4u5tr" path="res://.godot/imported/steam_lg.svg-3f6e5c7e14aa2f55df576fc20a5d9ba9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_lg_outline.svg.import b/assets/images/gamepad/steam/steam_lg_outline.svg.import index 17b68d249..5a8348e0d 100644 --- a/assets/images/gamepad/steam/steam_lg_outline.svg.import +++ b/assets/images/gamepad/steam/steam_lg_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://u5xyyrbtuv4g" +uid="uid://bvn4v3wqwqs5i" path="res://.godot/imported/steam_lg_outline.svg-062c444b53b06310ba4aef86a028cfda.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_lt.svg.import b/assets/images/gamepad/steam/steam_lt.svg.import index 301645940..7d4d52a36 100644 --- a/assets/images/gamepad/steam/steam_lt.svg.import +++ b/assets/images/gamepad/steam/steam_lt.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dampk4csvkjsw" +uid="uid://bbrwngtk0wmmj" path="res://.godot/imported/steam_lt.svg-b01bfd27622635950e396e6bc9c7928b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_lt_outline.svg.import b/assets/images/gamepad/steam/steam_lt_outline.svg.import index fa6e6242a..ec10a47ad 100644 --- a/assets/images/gamepad/steam/steam_lt_outline.svg.import +++ b/assets/images/gamepad/steam/steam_lt_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://7f2dmsr0hn0d" +uid="uid://culhebtu5jfv2" path="res://.godot/imported/steam_lt_outline.svg-302e340d0c033abfd2a6d05faf49e086.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_pad.svg.import b/assets/images/gamepad/steam/steam_pad.svg.import index a428af493..588ff4fb0 100644 --- a/assets/images/gamepad/steam/steam_pad.svg.import +++ b/assets/images/gamepad/steam/steam_pad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dy5uvti4f81vs" +uid="uid://bb1iks7hcb0ld" path="res://.godot/imported/steam_pad.svg-df48b374b61757ac941ec062b7f496c0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_pad_center.svg.import b/assets/images/gamepad/steam/steam_pad_center.svg.import index b94fd236c..32a63470f 100644 --- a/assets/images/gamepad/steam/steam_pad_center.svg.import +++ b/assets/images/gamepad/steam/steam_pad_center.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cfmikv08xxrfo" +uid="uid://b240xfdmiqrgp" path="res://.godot/imported/steam_pad_center.svg-2e7a602bbe80de1f3e095c3b9c1e5e28.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_pad_down.svg.import b/assets/images/gamepad/steam/steam_pad_down.svg.import index f2d8d3052..056a816cc 100644 --- a/assets/images/gamepad/steam/steam_pad_down.svg.import +++ b/assets/images/gamepad/steam/steam_pad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cburq6bpwwckm" +uid="uid://fsgwkm8wjffx" path="res://.godot/imported/steam_pad_down.svg-e86db49bb06475315e8751f20a55a7e1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_pad_left.svg.import b/assets/images/gamepad/steam/steam_pad_left.svg.import index 2e3b6ffd3..303d2ee04 100644 --- a/assets/images/gamepad/steam/steam_pad_left.svg.import +++ b/assets/images/gamepad/steam/steam_pad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bkj2m4623hqw5" +uid="uid://qc384y4f6w8w" path="res://.godot/imported/steam_pad_left.svg-0e071ed0a5c088ebaaf46cbd900dceed.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_pad_right.svg.import b/assets/images/gamepad/steam/steam_pad_right.svg.import index 43faba3aa..6cda40ede 100644 --- a/assets/images/gamepad/steam/steam_pad_right.svg.import +++ b/assets/images/gamepad/steam/steam_pad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://yp42bh1b0erl" +uid="uid://cuu4f18c7detj" path="res://.godot/imported/steam_pad_right.svg-47fdf1c0bae4c0d252cf407f0b1f5865.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_pad_up.svg.import b/assets/images/gamepad/steam/steam_pad_up.svg.import index fa09a677b..8df253903 100644 --- a/assets/images/gamepad/steam/steam_pad_up.svg.import +++ b/assets/images/gamepad/steam/steam_pad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bfgog4n4ytv7u" +uid="uid://dp11cg0bc0kd2" path="res://.godot/imported/steam_pad_up.svg-59f45a7ba048802096ed36a7684e9f18.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_rb.svg.import b/assets/images/gamepad/steam/steam_rb.svg.import index f955d97cb..e6c79a396 100644 --- a/assets/images/gamepad/steam/steam_rb.svg.import +++ b/assets/images/gamepad/steam/steam_rb.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://xydy2h52e35y" +uid="uid://cvx88kxapa0xi" path="res://.godot/imported/steam_rb.svg-9318cab37b297594f6297f3c52e60dda.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_rb_outline.svg.import b/assets/images/gamepad/steam/steam_rb_outline.svg.import index baba1a908..e1d7af587 100644 --- a/assets/images/gamepad/steam/steam_rb_outline.svg.import +++ b/assets/images/gamepad/steam/steam_rb_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dkkfgph1iyqym" +uid="uid://cvqy5x4nlwvu7" path="res://.godot/imported/steam_rb_outline.svg-54720a810d4e0ed1cf9b532e414dcd64.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_rg.svg.import b/assets/images/gamepad/steam/steam_rg.svg.import index 269162119..0e4ddec1d 100644 --- a/assets/images/gamepad/steam/steam_rg.svg.import +++ b/assets/images/gamepad/steam/steam_rg.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bsxofaj5ietuc" +uid="uid://otfjb4w8k4vh" path="res://.godot/imported/steam_rg.svg-9d969ea629ebc8f79d977f507af8231f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_rg_outline.svg.import b/assets/images/gamepad/steam/steam_rg_outline.svg.import index a85c44b84..01a14cf6e 100644 --- a/assets/images/gamepad/steam/steam_rg_outline.svg.import +++ b/assets/images/gamepad/steam/steam_rg_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b7jrp7gupco21" +uid="uid://tyafjwljcoj5" path="res://.godot/imported/steam_rg_outline.svg-481fcdf5ceba8b3048c550792abb7468.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_rt.svg.import b/assets/images/gamepad/steam/steam_rt.svg.import index 7e8abfbc8..0755704c7 100644 --- a/assets/images/gamepad/steam/steam_rt.svg.import +++ b/assets/images/gamepad/steam/steam_rt.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dcdbjs574x67u" +uid="uid://dyq1bux1exh7u" path="res://.godot/imported/steam_rt.svg-4a43cc6d50aee24f70346df5a38e1e7d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_rt_outline.svg.import b/assets/images/gamepad/steam/steam_rt_outline.svg.import index d5043b891..ce6a68148 100644 --- a/assets/images/gamepad/steam/steam_rt_outline.svg.import +++ b/assets/images/gamepad/steam/steam_rt_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bliwxrb2col5v" +uid="uid://b4833q7cdgvdp" path="res://.godot/imported/steam_rt_outline.svg-812befdf3249ea36dd8c20073482120c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick.svg.import b/assets/images/gamepad/steam/steam_stick.svg.import index f7172ac98..e568c326f 100644 --- a/assets/images/gamepad/steam/steam_stick.svg.import +++ b/assets/images/gamepad/steam/steam_stick.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b251los4ey64w" +uid="uid://bm0b6klr4s4me" path="res://.godot/imported/steam_stick.svg-c39de154d15fe02103cb42db87a977e2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick_down.svg.import b/assets/images/gamepad/steam/steam_stick_down.svg.import index e121297aa..1204fca07 100644 --- a/assets/images/gamepad/steam/steam_stick_down.svg.import +++ b/assets/images/gamepad/steam/steam_stick_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dipbfvbdnieqv" +uid="uid://b38225lpoxnad" path="res://.godot/imported/steam_stick_down.svg-b9bdcb37ca243c9bf3a9c1344c932c97.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick_horizontal.svg.import b/assets/images/gamepad/steam/steam_stick_horizontal.svg.import index 16d85f0df..e0c9f1547 100644 --- a/assets/images/gamepad/steam/steam_stick_horizontal.svg.import +++ b/assets/images/gamepad/steam/steam_stick_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://2dlvoe02t0xj" +uid="uid://lr5cgsbsdue5" path="res://.godot/imported/steam_stick_horizontal.svg-ce74baee539630d6117416b33e77de36.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick_l_press.svg.import b/assets/images/gamepad/steam/steam_stick_l_press.svg.import index 6b230bf9a..1e9f4abea 100644 --- a/assets/images/gamepad/steam/steam_stick_l_press.svg.import +++ b/assets/images/gamepad/steam/steam_stick_l_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cc3la68tqkeu7" +uid="uid://bp3y2i8nclwov" path="res://.godot/imported/steam_stick_l_press.svg-345633f8e0fae22795871cdae118d9d5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick_left.svg.import b/assets/images/gamepad/steam/steam_stick_left.svg.import index 0c6522db6..4714d1d02 100644 --- a/assets/images/gamepad/steam/steam_stick_left.svg.import +++ b/assets/images/gamepad/steam/steam_stick_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b7vfsacm21uvc" +uid="uid://cwe6414vhvsbm" path="res://.godot/imported/steam_stick_left.svg-cba212938eec57f543e0924a399de692.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick_right.svg.import b/assets/images/gamepad/steam/steam_stick_right.svg.import index 71c017a04..b60f337a7 100644 --- a/assets/images/gamepad/steam/steam_stick_right.svg.import +++ b/assets/images/gamepad/steam/steam_stick_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://jfjsyxvyctbk" +uid="uid://c5c5rvhs8260" path="res://.godot/imported/steam_stick_right.svg-965841e5f4831da187ca9f10a6bc3a7a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick_side_l.svg.import b/assets/images/gamepad/steam/steam_stick_side_l.svg.import index 28f68b741..ada507d91 100644 --- a/assets/images/gamepad/steam/steam_stick_side_l.svg.import +++ b/assets/images/gamepad/steam/steam_stick_side_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://tp838k62c4h" +uid="uid://ccyqd0aswcuis" path="res://.godot/imported/steam_stick_side_l.svg-a9fc7d0551ff234c0ed21ddabeaa1363.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick_up.svg.import b/assets/images/gamepad/steam/steam_stick_up.svg.import index 7a8658d16..765b30719 100644 --- a/assets/images/gamepad/steam/steam_stick_up.svg.import +++ b/assets/images/gamepad/steam/steam_stick_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c10buq0dndeb8" +uid="uid://h0lyv05rajef" path="res://.godot/imported/steam_stick_up.svg-c95a45250883ac3a3cadc13f7e19f3a5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steam/steam_stick_vertical.svg.import b/assets/images/gamepad/steam/steam_stick_vertical.svg.import index 0c54e426b..683a9f7ca 100644 --- a/assets/images/gamepad/steam/steam_stick_vertical.svg.import +++ b/assets/images/gamepad/steam/steam_stick_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bqabcjwil5a60" +uid="uid://dqn8y6dtrtaj" path="res://.godot/imported/steam_stick_vertical.svg-c5c996b8de39ae68ec73252f3952df05.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/controller_steamdeck.svg.import b/assets/images/gamepad/steamdeck/controller_steamdeck.svg.import index 0406047c8..c753d65b1 100644 --- a/assets/images/gamepad/steamdeck/controller_steamdeck.svg.import +++ b/assets/images/gamepad/steamdeck/controller_steamdeck.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c8v7tlasra7pf" +uid="uid://bbkn14ei6gvyy" path="res://.godot/imported/controller_steamdeck.svg-b380028662e4389bcaad12bcd2c1457f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/diagram.png.import b/assets/images/gamepad/steamdeck/diagram.png.import index 906156d78..201984e1e 100644 --- a/assets/images/gamepad/steamdeck/diagram.png.import +++ b/assets/images/gamepad/steamdeck/diagram.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvyo2q5qjpamv" +uid="uid://b4w5tmfci1toh" path="res://.godot/imported/diagram.png-d1aa2aa0cc56822aa0de1b6eae6efe02.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_a.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_a.svg.import index 2ad8edfca..300d7279d 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_a.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_a.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://2ymxt6fjxgbg" +uid="uid://glixduifqfyc" path="res://.godot/imported/steamdeck_button_a.svg-3c96f372767139bc6a7947b6024d4c33.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_a_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_a_outline.svg.import index f31ee1d91..5844a7bbf 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_a_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_a_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://2nutdoeeqfs6" +uid="uid://buct6isrdlqfl" path="res://.godot/imported/steamdeck_button_a_outline.svg-862d22fe6a348292e059eb24fe5fd408.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_b.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_b.svg.import index 0c1d84fff..a6840a325 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_b.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_b.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bdijnj7q510fb" +uid="uid://bi6jcsdcp4e77" path="res://.godot/imported/steamdeck_button_b.svg-e4e5608d890fbee317682689863cb7da.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_b_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_b_outline.svg.import index c4fc09c2b..b5d3c0ffa 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_b_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_b_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cxnabiia426t5" +uid="uid://g8ygs37lvqqk" path="res://.godot/imported/steamdeck_button_b_outline.svg-8c421f861f790b857b05d52982c66ff2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_guide.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_guide.svg.import index eb335313d..c0a6ec975 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_guide.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_guide.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b8kll7wj3mpw5" +uid="uid://7obnebcrnike" path="res://.godot/imported/steamdeck_button_guide.svg-88570b881601d872403131bf0a893641.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_guide_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_guide_outline.svg.import index 57531a92d..0a07fa24a 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_guide_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_guide_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dt7lcs2caoj1f" +uid="uid://blkb53i8h572k" path="res://.godot/imported/steamdeck_button_guide_outline.svg-d45f44227db018837f5aecf8ae628036.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_l1.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_l1.svg.import index 3727ec339..f58f56665 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_l1.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_l1.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://iuf1gwubgrkr" +uid="uid://scfbg0yygqka" path="res://.godot/imported/steamdeck_button_l1.svg-37c5dfe953290523d7a0870f5ec4920d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_l1_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_l1_outline.svg.import index b543a2ee1..906f26b05 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_l1_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_l1_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b6s7og36abb72" +uid="uid://bkevsp88so0qo" path="res://.godot/imported/steamdeck_button_l1_outline.svg-e16d27872d60c49e588a305342949519.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_l2.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_l2.svg.import index 0dbd03bcc..09355841f 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_l2.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_l2.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dxpamvls1wtr4" +uid="uid://k5ca2xyvchl2" path="res://.godot/imported/steamdeck_button_l2.svg-aceca1274115db593d8ecb8243d34e49.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_l2_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_l2_outline.svg.import index 1f1b556e6..b5fb96a66 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_l2_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_l2_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://djq3ruhk5qvcw" +uid="uid://sroh0o3qwlhy" path="res://.godot/imported/steamdeck_button_l2_outline.svg-7a898280a759f7f328095fc1fddc1172.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_l4.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_l4.svg.import index 0b46107f7..78d2017c0 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_l4.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_l4.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://2hvt2s10h4oh" +uid="uid://cru5rdx15j4ts" path="res://.godot/imported/steamdeck_button_l4.svg-17eb0171ee9f951a043e00f5d2d799b0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_l4_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_l4_outline.svg.import index d8bae0458..09c07fc49 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_l4_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_l4_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvgsjmmmxhj27" +uid="uid://drw1e1taneo26" path="res://.godot/imported/steamdeck_button_l4_outline.svg-5b983c6aa05e41693534c6d8842a1441.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_l5.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_l5.svg.import index 70ea4caac..f1d93c1af 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_l5.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_l5.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bsqe51ljqa1wl" +uid="uid://iby8tq0amo8b" path="res://.godot/imported/steamdeck_button_l5.svg-fb46ead322627794a308ab7a4fd40f1f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_l5_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_l5_outline.svg.import index 269cc0dc5..d307d3815 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_l5_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_l5_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cdtfvv4xetm05" +uid="uid://l6866l67en71" path="res://.godot/imported/steamdeck_button_l5_outline.svg-9e67363a6a1cdeaf708bdc2260720a17.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_options.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_options.svg.import index ad0c2e3ea..1cf3fa2fe 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_options.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_options.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dhwuj34jww33w" +uid="uid://i37o7qma5l0m" path="res://.godot/imported/steamdeck_button_options.svg-fe436a11d931edc617b188532486d3f3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_options_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_options_outline.svg.import index 2a07d5f91..f0207c550 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_options_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_options_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://5lgrd7ygl855" +uid="uid://ba3th3xag5j4f" path="res://.godot/imported/steamdeck_button_options_outline.svg-d1f39f0fd37fa6a44333a87cdb873050.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_quickaccess.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_quickaccess.svg.import index 31ee89548..28333b305 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_quickaccess.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_quickaccess.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://byy323swr1naa" +uid="uid://c8huhb14jh1ba" path="res://.godot/imported/steamdeck_button_quickaccess.svg-267fcd44a36ca9cde7bdfa67733ac61d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_quickaccess_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_quickaccess_outline.svg.import index a8ba11c24..e34b8fdd6 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_quickaccess_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_quickaccess_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b684m5kw5iest" +uid="uid://bnsm37uh6a0cd" path="res://.godot/imported/steamdeck_button_quickaccess_outline.svg-fe28bfacd46a07040d742e33a4efcadd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_r1.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_r1.svg.import index fe8b95922..953ca5a4f 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_r1.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_r1.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvm6un7wher3" +uid="uid://cdx74dhosgpi1" path="res://.godot/imported/steamdeck_button_r1.svg-ea73b538a66919b66e7401df05c8d53f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_r1_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_r1_outline.svg.import index f07034861..a2c62d6cf 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_r1_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_r1_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dytmy0gt1x26k" +uid="uid://lys7vscbqlhv" path="res://.godot/imported/steamdeck_button_r1_outline.svg-cd54cc6217e365f3672d391ceb3b42a4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_r2.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_r2.svg.import index c127c7bf1..0abf2480e 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_r2.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_r2.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://coaaq1r1uiyx2" +uid="uid://dw7hjtcfaepx" path="res://.godot/imported/steamdeck_button_r2.svg-57f7f438c382adb35a10239bd22ac1d3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_r2_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_r2_outline.svg.import index 5cec3c73f..1257c3e00 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_r2_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_r2_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://nxyikcse40tf" +uid="uid://dbl6ggekqybxm" path="res://.godot/imported/steamdeck_button_r2_outline.svg-84baf69025cc9ce4619170e1f55c338b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_r4.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_r4.svg.import index cf0b4b6b9..53e8d4f17 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_r4.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_r4.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dmfw87xx6phhc" +uid="uid://duirfrp1swep8" path="res://.godot/imported/steamdeck_button_r4.svg-d12e3f86a5ac3e1f45129b859ea0a2d7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_r4_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_r4_outline.svg.import index 323d4cf95..054723e51 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_r4_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_r4_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bsv3tlxuhgena" +uid="uid://b05ajk2yqm804" path="res://.godot/imported/steamdeck_button_r4_outline.svg-ec7e1fb5a8ed988b1374be9c34228fcb.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_r5.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_r5.svg.import index 570f68e0c..b98fbfa78 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_r5.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_r5.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bre5c38fo7hn5" +uid="uid://b5dvxnn2ddhar" path="res://.godot/imported/steamdeck_button_r5.svg-1983f11c413a72b91022c926e08fc027.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_r5_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_r5_outline.svg.import index 4655262df..1ba19e797 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_r5_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_r5_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cpdr8qkxeo5ju" +uid="uid://b33wq5mcv46tp" path="res://.godot/imported/steamdeck_button_r5_outline.svg-5b3f434144f60c0bcbbba463711c0d3e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_view.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_view.svg.import index 98e2505ab..ce3dd3c79 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_view.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_view.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c3tfburs4gc2e" +uid="uid://b3g1sl3gxac3l" path="res://.godot/imported/steamdeck_button_view.svg-df5e705a2ae746d8f893dc4c49b7062f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_view_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_view_outline.svg.import index 3b6556ce5..d4f33cf8a 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_view_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_view_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b1086fd7g6tor" +uid="uid://4lvqtp435icx" path="res://.godot/imported/steamdeck_button_view_outline.svg-59748db8aa7141f9866d8e1574315527.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_x.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_x.svg.import index 27efa79fa..a563f3d9e 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_x.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_x.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dedkcpdkjkfgi" +uid="uid://du2ljto5eauh5" path="res://.godot/imported/steamdeck_button_x.svg-335cf3f8e08c9ca9a5d8df07a0ab3341.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_x_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_x_outline.svg.import index f17dc45a5..00128ae9b 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_x_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_x_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://datmi41jrwsul" +uid="uid://d358hwoxc2vq7" path="res://.godot/imported/steamdeck_button_x_outline.svg-d37a7e3f6e334568860831bf9a78e3bc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_y.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_y.svg.import index 217bfc759..4d3414b40 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_y.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_y.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b14twfve0lmf6" +uid="uid://d238yav03b5tk" path="res://.godot/imported/steamdeck_button_y.svg-491ec7b2f613d73aed5951adb98883c1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_button_y_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_button_y_outline.svg.import index 9b5eafd24..98c217db6 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_button_y_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_button_y_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dd101tqt3ta7g" +uid="uid://mgwkcxa4o7y6" path="res://.godot/imported/steamdeck_button_y_outline.svg-913defeea6af577570b1c575750854b4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad.svg.import index 6caa82bda..6720bc362 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d1vwjidwwl6g6" +uid="uid://dprhshnof4mpt" path="res://.godot/imported/steamdeck_dpad.svg-753b7ebe2856b88604fc7b17c8209d87.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_all.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_all.svg.import index f4db29955..b8c9e57e1 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_all.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dcud65iy6s5sl" +uid="uid://cph7qr17caho5" path="res://.godot/imported/steamdeck_dpad_all.svg-a4cdcb6fac9af5eaad366f8d3ddfe692.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_down.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_down.svg.import index 169331b5e..8e54826ae 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_down.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ck0gwj0t1nlnb" +uid="uid://bc1anbo6va8c2" path="res://.godot/imported/steamdeck_dpad_down.svg-ed18ec862cec822779d95a5c4bd7cea4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_down_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_down_outline.svg.import index 11ad59ab3..39a155582 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_down_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://2uwp5b8cw7gi" +uid="uid://gi2hxifsgish" path="res://.godot/imported/steamdeck_dpad_down_outline.svg-e35d85bd00e08ab760c254d9ecf8c468.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_horizontal.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_horizontal.svg.import index 002dfdddc..9983050b7 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_horizontal.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bu1c432lg87jt" +uid="uid://vm3u83b0rcwi" path="res://.godot/imported/steamdeck_dpad_horizontal.svg-5a9c4f7f6c49bece425ca169d0c1b8f7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_horizontal_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_horizontal_outline.svg.import index 682fa2017..2ee984293 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_horizontal_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://7mwu1mw50mfb" +uid="uid://drehqhi4k2cww" path="res://.godot/imported/steamdeck_dpad_horizontal_outline.svg-d3797b43452b5450c512878f738711f6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_left.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_left.svg.import index f55cfeb7e..c90762e2b 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_left.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bw3o757gon60n" +uid="uid://ggcsc810x7ri" path="res://.godot/imported/steamdeck_dpad_left.svg-39c443fbe819af802f0f3eddc3213231.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_left_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_left_outline.svg.import index 9163af2b5..40b8a5bc2 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_left_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://trep17b27in5" +uid="uid://cv41uec8jor18" path="res://.godot/imported/steamdeck_dpad_left_outline.svg-0cc5532c63959465a335c431cdd399a5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_none.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_none.svg.import index c512034ca..be6a744bd 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_none.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_none.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dt06u2aunvd6d" +uid="uid://irdbms6j8s81" path="res://.godot/imported/steamdeck_dpad_none.svg-6a4d7648c61970861acc15131f306998.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_right.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_right.svg.import index b768b15dc..1f897c465 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_right.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cxe85qvlcxt8k" +uid="uid://tc7bgm3gcyqd" path="res://.godot/imported/steamdeck_dpad_right.svg-24b9aced2fb9e18c763d60190fdc3df6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_right_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_right_outline.svg.import index 25251ee06..cb5fe4c71 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_right_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bl2oq66bq55bx" +uid="uid://c2agy2hadglv4" path="res://.godot/imported/steamdeck_dpad_right_outline.svg-7def502e7977f3b3619939ea74c91745.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_up.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_up.svg.import index fa1b1466c..76720ba97 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_up.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3bk0otxo2u0t" +uid="uid://dfqvij0d87gq8" path="res://.godot/imported/steamdeck_dpad_up.svg-5f3805a998fa474a44c8ba2a0fb5affe.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_up_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_up_outline.svg.import index ba8136eec..3af78d729 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_up_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://8vo7daip3bwa" +uid="uid://b2i2bd4ppnynk" path="res://.godot/imported/steamdeck_dpad_up_outline.svg-282a6e353faf1f6650c531c24d26f618.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_vertical.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_vertical.svg.import index 2e204ea5f..51e544c7a 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_vertical.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b5rdkewtqva3g" +uid="uid://d051eubmxe0i5" path="res://.godot/imported/steamdeck_dpad_vertical.svg-b2baad11b7e2c66f06a95a9cffcf860a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_dpad_vertical_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_dpad_vertical_outline.svg.import index ecb49087d..ae15c8436 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_dpad_vertical_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_dpad_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://gjgojrbivjk0" +uid="uid://cqylecsj4twwa" path="res://.godot/imported/steamdeck_dpad_vertical_outline.svg-4c4fc011882ecfda0ca0dfef32b18244.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_l.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_l.svg.import index 744922744..cc483ccfa 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_l.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c6holuruhn6gs" +uid="uid://cljgsi6rwed5y" path="res://.godot/imported/steamdeck_stick_l.svg-e57d296ff43284680adfdfb347d107d7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_l_down.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_l_down.svg.import index 2ace9c19b..e495232e3 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_l_down.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_l_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bkuoy3851ps81" +uid="uid://b4t2lfdlk8v1l" path="res://.godot/imported/steamdeck_stick_l_down.svg-7a0e27ed40f5448139f22f448d55a591.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_l_horizontal.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_l_horizontal.svg.import index e2e300be0..5eb19d7b5 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_l_horizontal.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_l_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bdvotsbpgqfdh" +uid="uid://2pi8ywk01h7l" path="res://.godot/imported/steamdeck_stick_l_horizontal.svg-acad8a6786e459ff92731fe6815e2f28.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_l_left.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_l_left.svg.import index 8bf9e7fe8..796f270e8 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_l_left.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_l_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dfreqwma7axjm" +uid="uid://dgq0pu6stgp0n" path="res://.godot/imported/steamdeck_stick_l_left.svg-a2746b63364a319880bd47d9af14c185.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_l_press.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_l_press.svg.import index 541cf0c54..d432e65bc 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_l_press.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_l_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dcqhk1jgepeq0" +uid="uid://fk70akhspjqw" path="res://.godot/imported/steamdeck_stick_l_press.svg-a1e9ca79e30ef553e717bec6a0ec0276.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_l_right.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_l_right.svg.import index 72f145a6e..054f8e4bf 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_l_right.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_l_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dpxu821a7oj4s" +uid="uid://cg673id36yufn" path="res://.godot/imported/steamdeck_stick_l_right.svg-e94e889b8467541c329b1f6106501ce5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_l_up.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_l_up.svg.import index 7f8cf6c62..7fb1a7a19 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_l_up.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_l_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dgpc6aewueu74" +uid="uid://clerkwh0rv4qm" path="res://.godot/imported/steamdeck_stick_l_up.svg-a9025e3fa7239eb9a249cfb7c4645379.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_l_vertical.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_l_vertical.svg.import index 1d9839dac..3a61fe3f3 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_l_vertical.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_l_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3dxqs8yjn466" +uid="uid://bk3imcmp86kgc" path="res://.godot/imported/steamdeck_stick_l_vertical.svg-463a0006ec6805b46dc4119a66ebcd2e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_r.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_r.svg.import index 19cdc61d6..f87f7c65b 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_r.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c0vodcvwmd4mq" +uid="uid://dm1cw0o0kpajg" path="res://.godot/imported/steamdeck_stick_r.svg-40fce2614f30b40f063b130b06363d2e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_r_down.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_r_down.svg.import index 8ddbb8cf4..356769f07 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_r_down.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_r_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://drhc3nti5y4v2" +uid="uid://dcyao1pr3jtls" path="res://.godot/imported/steamdeck_stick_r_down.svg-fc725e715647ca133ea27b8f22e2f9dc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_r_horizontal.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_r_horizontal.svg.import index 309490bb2..38049bed5 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_r_horizontal.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_r_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://g2l2c7s5w5g8" +uid="uid://b4ek5ijusyt1x" path="res://.godot/imported/steamdeck_stick_r_horizontal.svg-e843d1815a1b03129b431815caef58ff.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_r_left.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_r_left.svg.import index 28c2e03aa..3797d3b9c 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_r_left.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_r_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bf050ow2fqwl5" +uid="uid://dk34rpf2xmwpp" path="res://.godot/imported/steamdeck_stick_r_left.svg-74f89d5ab1621bb0ebaee0c4f6f4f9a1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_r_press.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_r_press.svg.import index f8c68426a..bd08e8fcf 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_r_press.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_r_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bflve4wmy5l7p" +uid="uid://vb6n5l5ajq0t" path="res://.godot/imported/steamdeck_stick_r_press.svg-628771ef06c6e6b796cf8d399987e99d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_r_right.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_r_right.svg.import index 96332a2cd..3dda1d769 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_r_right.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_r_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://deujxqeovbjs8" +uid="uid://jpl2xe1upygm" path="res://.godot/imported/steamdeck_stick_r_right.svg-ec7052bbe9148d012957a93bed6869ac.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_r_up.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_r_up.svg.import index 2bd2e6a45..a3858cfd5 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_r_up.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_r_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c23ukiudnt5vx" +uid="uid://8ioc8gidakfr" path="res://.godot/imported/steamdeck_stick_r_up.svg-d7c53925775ebad53f0dd406e3f9ce4f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_r_vertical.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_r_vertical.svg.import index 1d828dd5b..80b5409d2 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_r_vertical.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_r_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dgwnxs2mculkc" +uid="uid://bs274ebd2cikg" path="res://.godot/imported/steamdeck_stick_r_vertical.svg-be8bce72238e6acf6272015ba2e4d9ae.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_side_l.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_side_l.svg.import index 5cfebff32..4e94a435a 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_side_l.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_side_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://co0py5tmila8a" +uid="uid://dmjx70fjf5ysy" path="res://.godot/imported/steamdeck_stick_side_l.svg-81dbe8c04f53654c901e85c6a96eacba.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_side_r.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_side_r.svg.import index ab3255ac7..0ce8ced96 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_side_r.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_side_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://qqbosx352dwi" +uid="uid://cnat1bi7c1iy1" path="res://.godot/imported/steamdeck_stick_side_r.svg-10667782bb7cc9e65006687d639eab05.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_top_l.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_top_l.svg.import index 08bfc2828..db7efdf4c 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_top_l.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_top_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dg7sruj1cp03x" +uid="uid://byd0ivhix7vwl" path="res://.godot/imported/steamdeck_stick_top_l.svg-08c6d79693c31cf1a6246b6dcbd2b9bb.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_stick_top_r.svg.import b/assets/images/gamepad/steamdeck/steamdeck_stick_top_r.svg.import index ce7447621..2f3ff3892 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_stick_top_r.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_stick_top_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvh811d6hb5xi" +uid="uid://dlhdb57l8offw" path="res://.godot/imported/steamdeck_stick_top_r.svg-4c7f3d1d6992cd87aaef06f8e0dc62ed.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad.svg.import index 07e4e1537..53c6b8c69 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cps7dui0d3oae" +uid="uid://sycfdml1m5ex" path="res://.godot/imported/steamdeck_trackpad.svg-4bd8afdd7ac21a0997b342400e515994.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_all.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_all.svg.import index a5a95f41f..22435a434 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_all.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ce8tc0w3e3hsu" +uid="uid://c5xcaecaki0hr" path="res://.godot/imported/steamdeck_trackpad_all.svg-74d3c4f60ea641065ad9c16147dfa6fe.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_all_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_all_outline.svg.import index 1329fbac0..d9b02cb92 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_all_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_all_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvca275bt0sx0" +uid="uid://csejgvv8wqtc8" path="res://.godot/imported/steamdeck_trackpad_all_outline.svg-e2643ceada3a81c22d1f09be6475a9ff.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_down.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_down.svg.import index cbe20e069..7d560f016 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_down.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c7ly06enxm5jq" +uid="uid://bnbkfkp463ugy" path="res://.godot/imported/steamdeck_trackpad_down.svg-e5b0e2a72ecc2fad3b64337ab389461a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_down_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_down_outline.svg.import index 3d60a46a3..bbdef7e2b 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_down_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dr1jdrodtji8u" +uid="uid://dvxroqm3ri4ni" path="res://.godot/imported/steamdeck_trackpad_down_outline.svg-fb01c14e76955085f3421428d15fc027.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_horizontal.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_horizontal.svg.import index d490276b1..28659b52b 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_horizontal.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cwefuotasq164" +uid="uid://bfcyyblbhxmhl" path="res://.godot/imported/steamdeck_trackpad_horizontal.svg-59190bd913cbc5efd8d01d04d275cb2b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_horizontal_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_horizontal_outline.svg.import index bf55c4111..cf6ed002f 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_horizontal_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b5jt466sn03jo" +uid="uid://bul2avxinhktc" path="res://.godot/imported/steamdeck_trackpad_horizontal_outline.svg-4a558bfd480aac89183bac74deac9e66.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l.svg.import index 3c170d6ce..3aa4b46cb 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://fpsxow3w684w" +uid="uid://bs0jtx81eplgt" path="res://.godot/imported/steamdeck_trackpad_l.svg-0907662361c9e790d82b03f453102b0c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all.svg.import index a3d49d1a9..86d5396db 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3ocywhdiyjvk" +uid="uid://dvioogs1h44wj" path="res://.godot/imported/steamdeck_trackpad_l_all.svg-5c9d7615db66a57fe276acd76abd3aca.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all_outline.svg.import index 9fdd74bff..8997c81cb 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_all_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://tq36f51n68ku" +uid="uid://oak22878aalc" path="res://.godot/imported/steamdeck_trackpad_l_all_outline.svg-fa23b59ea3fa8b7d2fd258f711d15996.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_down.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_down.svg.import index bb5964e4e..5602f20a8 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_down.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dqgafsu3l3uj0" +uid="uid://bb5ti5dmaalrw" path="res://.godot/imported/steamdeck_trackpad_l_down.svg-fc1afbad1bc5f01ea0f6c3d3170cddc8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_down_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_down_outline.svg.import index 5f33f1894..5fe4e4b2d 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_down_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dnomrtfwget4x" +uid="uid://cekvrmhd1ot3m" path="res://.godot/imported/steamdeck_trackpad_l_down_outline.svg-f8787a9ea51ad5b764f77d08c9a4ce8e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_horizontal.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_horizontal.svg.import index 47af7dc06..29423c634 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_horizontal.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://rlhy688f6ojc" +uid="uid://dkv4ktx3l5dal" path="res://.godot/imported/steamdeck_trackpad_l_horizontal.svg-31128938b6860937b2366f53391922a8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_horizontal_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_horizontal_outline.svg.import index 889c841f9..bfb8f6981 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_horizontal_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cd4vbh0hotird" +uid="uid://bs8hat4ofxowp" path="res://.godot/imported/steamdeck_trackpad_l_horizontal_outline.svg-62fca701d78f63489fb4f3eaca573383.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_left.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_left.svg.import index acbbb0c13..59ba29bfd 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_left.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://jh64ow81otuu" +uid="uid://47w0743681wo" path="res://.godot/imported/steamdeck_trackpad_l_left.svg-c103bb7a15626f83f476aa75c1b973d7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_left_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_left_outline.svg.import index 8639dbce3..88ac15a49 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_left_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://byp0vq1liutf" +uid="uid://k0nanb3qcfi6" path="res://.godot/imported/steamdeck_trackpad_l_left_outline.svg-f7b4cda7af1d8a43cf433c1fdc3b3625.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_outline.svg.import index 409091d5e..10e078131 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3yledpl4vkjo" +uid="uid://8klud0p6amw3" path="res://.godot/imported/steamdeck_trackpad_l_outline.svg-276439bd5b946a262802792e6629b626.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_right.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_right.svg.import index e77890669..ec0582d1f 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_right.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cp37an4l8wte6" +uid="uid://c1we48r6l0yqi" path="res://.godot/imported/steamdeck_trackpad_l_right.svg-51aeddf18637cd83e587dbd7bbf2496e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_right_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_right_outline.svg.import index 35ce137af..fd9110898 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_right_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bdjf1f6ki0mmq" +uid="uid://dwidwmuandijw" path="res://.godot/imported/steamdeck_trackpad_l_right_outline.svg-b98e27a5d160a3023524ac3d1ca91276.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_up.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_up.svg.import index 075d286c6..617e730d6 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_up.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://clmhcfgwrj6gb" +uid="uid://cjqx116cvounh" path="res://.godot/imported/steamdeck_trackpad_l_up.svg-601b7f2d3d0b71266cd0064f44cfb41b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_up_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_up_outline.svg.import index 3b16284f2..bb67f32af 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_up_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cbynxgsakuer4" +uid="uid://bqnq5ev80pwtv" path="res://.godot/imported/steamdeck_trackpad_l_up_outline.svg-6bec7a04fef0bf31d167e9b6e1819bbd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_vertical.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_vertical.svg.import index ab40d0d84..92bc7bf51 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_vertical.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bj3gaowwpk7xm" +uid="uid://bynex2dpngxsl" path="res://.godot/imported/steamdeck_trackpad_l_vertical.svg-74de1926732e10e8508c87bc9cc26ee1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_vertical_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_vertical_outline.svg.import index ad532df36..09bea1a20 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_vertical_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_l_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://lyecfnrnd6b7" +uid="uid://cxu3sk0qiy004" path="res://.godot/imported/steamdeck_trackpad_l_vertical_outline.svg-10cbc8bdb739fd0856a45c34603e86c8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_left.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_left.svg.import index 77db359fc..f8e5efc7e 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_left.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://06xriwqdq5t5" +uid="uid://dtcbp5hqiltr0" path="res://.godot/imported/steamdeck_trackpad_left.svg-ee90096cfd6aeb339994399b7a75b13a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_left_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_left_outline.svg.import index 258e5186f..e2efcd055 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_left_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://mqccljqtruyj" +uid="uid://cpffkd800su26" path="res://.godot/imported/steamdeck_trackpad_left_outline.svg-3ebe2d1c8cb8cae481de1f22b4b55892.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_outline.svg.import index 68855b7fc..ab6f11271 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bu3cgxorfcnop" +uid="uid://bvs22agcyremo" path="res://.godot/imported/steamdeck_trackpad_outline.svg-d9c932ce678c6cdf03095e2dc2f8ac6f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r.svg.import index 9c25f3b9b..8df90c6a8 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://da5ds5ps218cu" +uid="uid://bwobw44drvuo" path="res://.godot/imported/steamdeck_trackpad_r.svg-7ea5bddd757dde3d7093598e2dc87821.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all.svg.import index 29d89f3c8..18b5b9510 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b5smg46hvo3en" +uid="uid://b006fo2wqcu8w" path="res://.godot/imported/steamdeck_trackpad_r_all.svg-706cbed575120d7f67f1e77300afeda4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all_outline.svg.import index 5bebbc92e..158edcef5 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_all_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://1b81jqa04fd8" +uid="uid://b47l5gbfjbaf3" path="res://.godot/imported/steamdeck_trackpad_r_all_outline.svg-157a71c4c4a8be87a50237337422c735.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_down.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_down.svg.import index 32b7dad68..64ae5dc39 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_down.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ue2g0egd4ph7" +uid="uid://du6v2pbduxbau" path="res://.godot/imported/steamdeck_trackpad_r_down.svg-005d41abe0baaefa300be710fad26b28.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_down_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_down_outline.svg.import index 93972d0bb..8fb3aaa6a 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_down_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d8tmauoecosv" +uid="uid://bnuba0jvd384a" path="res://.godot/imported/steamdeck_trackpad_r_down_outline.svg-10ef1daf7ecf5335f59f7f5cfdb0c450.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_horizontal.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_horizontal.svg.import index a8bbab84c..8b7434dee 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_horizontal.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bbsc1kwoxas20" +uid="uid://bylxo8d1ky083" path="res://.godot/imported/steamdeck_trackpad_r_horizontal.svg-bacb5d65043d055a8e762d3ea4f6691e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_horizontal_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_horizontal_outline.svg.import index c63c6d8d7..0c9978727 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_horizontal_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dj184i0dohlwq" +uid="uid://g6b6mtq6h12q" path="res://.godot/imported/steamdeck_trackpad_r_horizontal_outline.svg-74d5afb9fdb692a5985770f9820a94ac.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_left.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_left.svg.import index 473072d02..bc9afe3b3 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_left.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bvonexp8ilgdj" +uid="uid://bh0xmnvxuqvsa" path="res://.godot/imported/steamdeck_trackpad_r_left.svg-5f7013136716c28612dfd31391363091.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_left_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_left_outline.svg.import index ed0c75098..830ef2c6d 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_left_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ou5jo5c7nh1h" +uid="uid://imitg6gbgbmb" path="res://.godot/imported/steamdeck_trackpad_r_left_outline.svg-d839001e76ee62f176ad537746e5b070.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_outline.svg.import index d067c5185..5f82c1ce7 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c0ve1624rr4xp" +uid="uid://bpk2bbvq7nc16" path="res://.godot/imported/steamdeck_trackpad_r_outline.svg-a376b97064c661130d85b080c9226cc1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_right.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_right.svg.import index 56eeb81e5..565a9b52c 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_right.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cia6uq06g65yv" +uid="uid://cfrvptfw4wgal" path="res://.godot/imported/steamdeck_trackpad_r_right.svg-6f69f7ffaa9c7d08e204fda45afd632f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_right_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_right_outline.svg.import index 8c656baa2..ae3bd7d57 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_right_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d0phlopeumfb7" +uid="uid://be66sf5wpj2fs" path="res://.godot/imported/steamdeck_trackpad_r_right_outline.svg-3383aeb43bb3567a77a98f4006e8d51c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_up.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_up.svg.import index a09d2bda1..d38367476 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_up.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://t5eugo5st8l7" +uid="uid://dbof7iv20i3d1" path="res://.godot/imported/steamdeck_trackpad_r_up.svg-8620dc7c0eb7b71921167e478bd6d2c8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_up_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_up_outline.svg.import index 1b124a00b..a73ec909a 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_up_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvpfdo10uu6m4" +uid="uid://c8o73nshepu6f" path="res://.godot/imported/steamdeck_trackpad_r_up_outline.svg-851dc842ad2b2ca0cf2eff655c5dfd40.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_vertical.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_vertical.svg.import index 1c52596f8..438fc612b 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_vertical.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dnk1kwbb21xq7" +uid="uid://wv0f584p3hy3" path="res://.godot/imported/steamdeck_trackpad_r_vertical.svg-3bf40a8215e02210b3404982fc423bd3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_vertical_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_vertical_outline.svg.import index a8316dad5..dd83714f2 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_vertical_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_r_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://yru8t1ixe67j" +uid="uid://5cfmuat6okme" path="res://.godot/imported/steamdeck_trackpad_r_vertical_outline.svg-2a306bdb1e27d693a4a5eeb0f8a6fd0d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_right.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_right.svg.import index 3d7671e35..81f0f8581 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_right.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cbm4urp4onu2" +uid="uid://nsy8kr7gweas" path="res://.godot/imported/steamdeck_trackpad_right.svg-928419d42824ab4f8e5452b1cc0a52be.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_right_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_right_outline.svg.import index 4874be756..aefa9b4a0 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_right_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://3lvu2n0ius6s" +uid="uid://bsfmfl1kbt3d6" path="res://.godot/imported/steamdeck_trackpad_right_outline.svg-90513269646d5dd9fd80b8b9ff8d6d54.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_up.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_up.svg.import index 8752cba54..b96baa715 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_up.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c816h08164py6" +uid="uid://di52a0h4esc6e" path="res://.godot/imported/steamdeck_trackpad_up.svg-7115dbc5330efcd07dcd2866173bf2dc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_up_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_up_outline.svg.import index 4328fd354..f9964f10c 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_up_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://yy1fphtrtw1e" +uid="uid://imjfaos3vgf" path="res://.godot/imported/steamdeck_trackpad_up_outline.svg-bc698b1451d1d003a71b103a61a37755.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_vertical.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_vertical.svg.import index ce4841cbc..327348765 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_vertical.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4m3wd5cjm40w" +uid="uid://y332cxmoaws0" path="res://.godot/imported/steamdeck_trackpad_vertical.svg-c2e0be831bd618002d2420a69e5906bd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/steamdeck/steamdeck_trackpad_vertical_outline.svg.import b/assets/images/gamepad/steamdeck/steamdeck_trackpad_vertical_outline.svg.import index 9cfc90c36..802bc2382 100644 --- a/assets/images/gamepad/steamdeck/steamdeck_trackpad_vertical_outline.svg.import +++ b/assets/images/gamepad/steamdeck/steamdeck_trackpad_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bgb4g5i1fa23n" +uid="uid://caq1dit7xl8ha" path="res://.godot/imported/steamdeck_trackpad_vertical_outline.svg-2ddd6f3da1ed8d8260b0cd949d96cc9e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/controller_switch.svg.import b/assets/images/gamepad/switch/controller_switch.svg.import index 5b6735bc7..aa05f0749 100644 --- a/assets/images/gamepad/switch/controller_switch.svg.import +++ b/assets/images/gamepad/switch/controller_switch.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://sqnr774q3mvp" +uid="uid://cxugsmqkyjfgb" path="res://.godot/imported/controller_switch.svg-88690e3c69c3169fba3e8459c87a9d2c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/controller_switch_joycon_down.svg.import b/assets/images/gamepad/switch/controller_switch_joycon_down.svg.import index 9b82f90a9..5e6fa768b 100644 --- a/assets/images/gamepad/switch/controller_switch_joycon_down.svg.import +++ b/assets/images/gamepad/switch/controller_switch_joycon_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://gkjuhpveilje" +uid="uid://b6jmf3g838xag" path="res://.godot/imported/controller_switch_joycon_down.svg-d9b8276ac1c082c46f2382f1bbcd0a3b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/controller_switch_joycon_up.svg.import b/assets/images/gamepad/switch/controller_switch_joycon_up.svg.import index 167341853..1dc48d0fb 100644 --- a/assets/images/gamepad/switch/controller_switch_joycon_up.svg.import +++ b/assets/images/gamepad/switch/controller_switch_joycon_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cxa840aaxrl64" +uid="uid://v1fjunjxpvm4" path="res://.godot/imported/controller_switch_joycon_up.svg-fbfc4472b0e57eba29c932d1b6480f3f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/controller_switch_pro.svg.import b/assets/images/gamepad/switch/controller_switch_pro.svg.import index 464ddf996..b8c4bee50 100644 --- a/assets/images/gamepad/switch/controller_switch_pro.svg.import +++ b/assets/images/gamepad/switch/controller_switch_pro.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bcngxgq2hojum" +uid="uid://hm8ubklvtb4u" path="res://.godot/imported/controller_switch_pro.svg-e85f378a45cfeea7a4374fd18837c7ec.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_a.svg.import b/assets/images/gamepad/switch/switch_button_a.svg.import index 2fc98f0ef..6004903cb 100644 --- a/assets/images/gamepad/switch/switch_button_a.svg.import +++ b/assets/images/gamepad/switch/switch_button_a.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dec8wg71j58h1" +uid="uid://dsnnac0xk0tfl" path="res://.godot/imported/switch_button_a.svg-b7c2fc428a8939039f1db6d1de5e558b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_a_outline.svg.import b/assets/images/gamepad/switch/switch_button_a_outline.svg.import index f7fbbfce6..dcbdf91b2 100644 --- a/assets/images/gamepad/switch/switch_button_a_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_a_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ba7ddjqpe6c58" +uid="uid://d133qfc5ei021" path="res://.godot/imported/switch_button_a_outline.svg-9270fa0490025f245f1104632cf36de6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_b.svg.import b/assets/images/gamepad/switch/switch_button_b.svg.import index c7ba08375..6f2682b7f 100644 --- a/assets/images/gamepad/switch/switch_button_b.svg.import +++ b/assets/images/gamepad/switch/switch_button_b.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://8o3g4dcunk87" +uid="uid://b21ssea6yir56" path="res://.godot/imported/switch_button_b.svg-fa79e7674743feebb9be572c435c7eea.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_b_outline.svg.import b/assets/images/gamepad/switch/switch_button_b_outline.svg.import index 5f777aadf..769abf721 100644 --- a/assets/images/gamepad/switch/switch_button_b_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_b_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b3f4wxhhr68ej" +uid="uid://bebyeav5jc5vo" path="res://.godot/imported/switch_button_b_outline.svg-d4f745a5cd8a943271aae57a35922e10.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_home.svg.import b/assets/images/gamepad/switch/switch_button_home.svg.import index 5e0b15ae8..a6064eb91 100644 --- a/assets/images/gamepad/switch/switch_button_home.svg.import +++ b/assets/images/gamepad/switch/switch_button_home.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b3v8gbuc0knsr" +uid="uid://dicj4tpxknqai" path="res://.godot/imported/switch_button_home.svg-c31466b79eec07495096085bbf7f1770.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_home_outline.svg.import b/assets/images/gamepad/switch/switch_button_home_outline.svg.import index 4d00b32b5..277656d8d 100644 --- a/assets/images/gamepad/switch/switch_button_home_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_home_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://chx626dpbwwa3" +uid="uid://bwmxkbqp3i083" path="res://.godot/imported/switch_button_home_outline.svg-75d8c08d81ca6bb46efa60945414e065.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_l.svg.import b/assets/images/gamepad/switch/switch_button_l.svg.import index 0bd5bb203..18e0c4956 100644 --- a/assets/images/gamepad/switch/switch_button_l.svg.import +++ b/assets/images/gamepad/switch/switch_button_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b57k4ko26lm6" +uid="uid://d2ho4b7w2mdlb" path="res://.godot/imported/switch_button_l.svg-427a83ab83f20c13a321edf8de67a6dc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_l_outline.svg.import b/assets/images/gamepad/switch/switch_button_l_outline.svg.import index f2fe10a54..9afe36dcd 100644 --- a/assets/images/gamepad/switch/switch_button_l_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_l_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://xr3r0gaebxa3" +uid="uid://b3lpl1fc7lsfs" path="res://.godot/imported/switch_button_l_outline.svg-6c6c4b5a8f1430b1e4484ec72bdb7bff.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_minus.svg.import b/assets/images/gamepad/switch/switch_button_minus.svg.import index d0d01764a..f52fc27ac 100644 --- a/assets/images/gamepad/switch/switch_button_minus.svg.import +++ b/assets/images/gamepad/switch/switch_button_minus.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://crl6hqey1xqfd" +uid="uid://f3dabljd057e" path="res://.godot/imported/switch_button_minus.svg-71ba1bfca6601620e6dba96b0faae9e9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_minus_outline.svg.import b/assets/images/gamepad/switch/switch_button_minus_outline.svg.import index 1f2ef4fa2..2336a2811 100644 --- a/assets/images/gamepad/switch/switch_button_minus_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_minus_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://jcbnrbsvbs64" +uid="uid://xercor0xim78" path="res://.godot/imported/switch_button_minus_outline.svg-28fb1c7663dc11a8b2bcd6b23d517749.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_plus.svg.import b/assets/images/gamepad/switch/switch_button_plus.svg.import index 13060b6bd..6646c55b3 100644 --- a/assets/images/gamepad/switch/switch_button_plus.svg.import +++ b/assets/images/gamepad/switch/switch_button_plus.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dugmdwvujsrk4" +uid="uid://rgpis0qqwyn3" path="res://.godot/imported/switch_button_plus.svg-17c97b6ab5721a68d2bdaa8c61e9ec2a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_plus_outline.svg.import b/assets/images/gamepad/switch/switch_button_plus_outline.svg.import index fec41fa41..34dcdb6fa 100644 --- a/assets/images/gamepad/switch/switch_button_plus_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_plus_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b7clxv4skg0wl" +uid="uid://dg01m1prbcqj5" path="res://.godot/imported/switch_button_plus_outline.svg-4fdda2a4e04568c0ce4794e749ae7ee0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_r.svg.import b/assets/images/gamepad/switch/switch_button_r.svg.import index 10311a82d..7939ebb17 100644 --- a/assets/images/gamepad/switch/switch_button_r.svg.import +++ b/assets/images/gamepad/switch/switch_button_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bu2naacuis8k8" +uid="uid://dh8gskvlf1y4n" path="res://.godot/imported/switch_button_r.svg-0a9b6ce35f686f9403a26351249e46dd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_r_outline.svg.import b/assets/images/gamepad/switch/switch_button_r_outline.svg.import index b4e37f85a..e2c081bb0 100644 --- a/assets/images/gamepad/switch/switch_button_r_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_r_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://pwvdheaes81a" +uid="uid://d3p3bsnovkeos" path="res://.godot/imported/switch_button_r_outline.svg-d44b1c9d6654d18d83905c29bea95452.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_sl.svg.import b/assets/images/gamepad/switch/switch_button_sl.svg.import index 9b2cf09e1..95da2836b 100644 --- a/assets/images/gamepad/switch/switch_button_sl.svg.import +++ b/assets/images/gamepad/switch/switch_button_sl.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bqp0l3x30ojqa" +uid="uid://cej023qrdtqg5" path="res://.godot/imported/switch_button_sl.svg-effb40acd820d539db9ac77ed3dd57ae.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_sl_outline.svg.import b/assets/images/gamepad/switch/switch_button_sl_outline.svg.import index 89030eeb7..de59ebf5e 100644 --- a/assets/images/gamepad/switch/switch_button_sl_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_sl_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dj1gfqdc2v8c2" +uid="uid://cet8fp0burrjp" path="res://.godot/imported/switch_button_sl_outline.svg-2bfa80c9138bfa659d07a29bae74724d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_sr.svg.import b/assets/images/gamepad/switch/switch_button_sr.svg.import index b358ec4dc..8c166e47b 100644 --- a/assets/images/gamepad/switch/switch_button_sr.svg.import +++ b/assets/images/gamepad/switch/switch_button_sr.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dikclv36p7ai0" +uid="uid://d366wyqoknqtc" path="res://.godot/imported/switch_button_sr.svg-d87714c326169e4347c9ecb0db0c3030.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_sr_outline.svg.import b/assets/images/gamepad/switch/switch_button_sr_outline.svg.import index fd9f5f60b..3895faaa4 100644 --- a/assets/images/gamepad/switch/switch_button_sr_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_sr_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c0f2ufrp4fivj" +uid="uid://qf5oi850m8al" path="res://.godot/imported/switch_button_sr_outline.svg-62a0f3c5b8f7b594a05933bdc01747d8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_sync.svg.import b/assets/images/gamepad/switch/switch_button_sync.svg.import index 65bc4428a..e57c7d717 100644 --- a/assets/images/gamepad/switch/switch_button_sync.svg.import +++ b/assets/images/gamepad/switch/switch_button_sync.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://baeygxurw7dn1" +uid="uid://brwy28sjw2pe8" path="res://.godot/imported/switch_button_sync.svg-3a6d3a3eb26b161d0dec26aa8eccad58.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_sync_outline.svg.import b/assets/images/gamepad/switch/switch_button_sync_outline.svg.import index 7809983fc..1dfd1d050 100644 --- a/assets/images/gamepad/switch/switch_button_sync_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_sync_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ccwn7n7ycbw80" +uid="uid://dby6hm0625wja" path="res://.godot/imported/switch_button_sync_outline.svg-e44753a7ccc2de0a3a6dd19c147ed8b4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_x.svg.import b/assets/images/gamepad/switch/switch_button_x.svg.import index 1a46b4c8e..36535f9e5 100644 --- a/assets/images/gamepad/switch/switch_button_x.svg.import +++ b/assets/images/gamepad/switch/switch_button_x.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b6wkct2qwo0ul" +uid="uid://d0nkpy8tk3f28" path="res://.godot/imported/switch_button_x.svg-ac793655b62238b26cad4619513c2308.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_x_outline.svg.import b/assets/images/gamepad/switch/switch_button_x_outline.svg.import index ff383a3fd..9cb438f02 100644 --- a/assets/images/gamepad/switch/switch_button_x_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_x_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dwy6l0iflgddw" +uid="uid://bnag8d0gpab8s" path="res://.godot/imported/switch_button_x_outline.svg-775b19ef72714d64d717d6c05a699f6e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_y.svg.import b/assets/images/gamepad/switch/switch_button_y.svg.import index 6cece60b4..26e302b78 100644 --- a/assets/images/gamepad/switch/switch_button_y.svg.import +++ b/assets/images/gamepad/switch/switch_button_y.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://djvoe5nud1i0w" +uid="uid://ckwf0ko1ch08t" path="res://.godot/imported/switch_button_y.svg-a9757befa8d96e5aafd17afe3e36b6e3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_y_outline.svg.import b/assets/images/gamepad/switch/switch_button_y_outline.svg.import index d5bd519f8..be4c8be87 100644 --- a/assets/images/gamepad/switch/switch_button_y_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_y_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://tcuixxkr0285" +uid="uid://bqb647h8tg10v" path="res://.godot/imported/switch_button_y_outline.svg-aa05c1b6d91ae1386ec13b09f9662066.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_zl.svg.import b/assets/images/gamepad/switch/switch_button_zl.svg.import index 93dcddf24..e01c2d25b 100644 --- a/assets/images/gamepad/switch/switch_button_zl.svg.import +++ b/assets/images/gamepad/switch/switch_button_zl.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dsa8m2vfvtx0q" +uid="uid://ysw3f4s0wckl" path="res://.godot/imported/switch_button_zl.svg-4a5c362a583098d738445dedaa2972b3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_zl_outline.svg.import b/assets/images/gamepad/switch/switch_button_zl_outline.svg.import index 3a1c5d94c..6c5802827 100644 --- a/assets/images/gamepad/switch/switch_button_zl_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_zl_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://q76rrwbter2d" +uid="uid://uac20no0b2dj" path="res://.godot/imported/switch_button_zl_outline.svg-f476743e054446a59fdc9d355cfeb3a4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_zr.svg.import b/assets/images/gamepad/switch/switch_button_zr.svg.import index 968031f11..a6dd5e680 100644 --- a/assets/images/gamepad/switch/switch_button_zr.svg.import +++ b/assets/images/gamepad/switch/switch_button_zr.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cutjyk72c0d1" +uid="uid://cwkd4oyqs445d" path="res://.godot/imported/switch_button_zr.svg-9be3343c2bc958e2c1b734bd56b7a326.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_button_zr_outline.svg.import b/assets/images/gamepad/switch/switch_button_zr_outline.svg.import index d28c9d465..aa2ca082d 100644 --- a/assets/images/gamepad/switch/switch_button_zr_outline.svg.import +++ b/assets/images/gamepad/switch/switch_button_zr_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c5e4j4mufbkfj" +uid="uid://1p3ejwpd1ia4" path="res://.godot/imported/switch_button_zr_outline.svg-47403d21b19d1178a4d74d02463a90b2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons.svg.import b/assets/images/gamepad/switch/switch_buttons.svg.import index 0a4eecfdc..928b3a978 100644 --- a/assets/images/gamepad/switch/switch_buttons.svg.import +++ b/assets/images/gamepad/switch/switch_buttons.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b1qcl62witrrt" +uid="uid://bvjmsnstaoq2v" path="res://.godot/imported/switch_buttons.svg-3ed28df4fd427a3a0a7cfa0879cec9d3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_all.svg.import b/assets/images/gamepad/switch/switch_buttons_all.svg.import index c4be38648..ab08851fd 100644 --- a/assets/images/gamepad/switch/switch_buttons_all.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bsjoo254uaytt" +uid="uid://cub78cesb05r7" path="res://.godot/imported/switch_buttons_all.svg-e88654adf145effe30bbaa1654069b2c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_down.svg.import b/assets/images/gamepad/switch/switch_buttons_down.svg.import index 315e6e091..fb6665ee7 100644 --- a/assets/images/gamepad/switch/switch_buttons_down.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c3hr6ecnhdtg2" +uid="uid://cdvcck1mbmt87" path="res://.godot/imported/switch_buttons_down.svg-13fd102ec320f9f6f249aab7e628cade.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_down_outline.svg.import b/assets/images/gamepad/switch/switch_buttons_down_outline.svg.import index bfce3b080..b5c5a2adc 100644 --- a/assets/images/gamepad/switch/switch_buttons_down_outline.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bmqlk4rxwclr3" +uid="uid://q0u4lji8k55t" path="res://.godot/imported/switch_buttons_down_outline.svg-e818c6999a9fc5acdbbb4ac2bada593c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_horizontal.svg.import b/assets/images/gamepad/switch/switch_buttons_horizontal.svg.import index 76489f670..61a1df71f 100644 --- a/assets/images/gamepad/switch/switch_buttons_horizontal.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cnfktwcoxpqrs" +uid="uid://bji4c5ie7r81i" path="res://.godot/imported/switch_buttons_horizontal.svg-665a43c7d2f510df0a2590e40e8ba971.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_horizontal_outline.svg.import b/assets/images/gamepad/switch/switch_buttons_horizontal_outline.svg.import index 5e8f82b2d..15282f696 100644 --- a/assets/images/gamepad/switch/switch_buttons_horizontal_outline.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://beyhw2dnujq8p" +uid="uid://ceu3kuwhc3wtm" path="res://.godot/imported/switch_buttons_horizontal_outline.svg-f347387cf151e0066db42734c0bf6a9c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_left.svg.import b/assets/images/gamepad/switch/switch_buttons_left.svg.import index 47f8fc582..df94d542d 100644 --- a/assets/images/gamepad/switch/switch_buttons_left.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dykfvnb0v687h" +uid="uid://b4j53xuo8rosw" path="res://.godot/imported/switch_buttons_left.svg-c1b18e363ee56fbfc413d2c13c5c95bc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_left_outline.svg.import b/assets/images/gamepad/switch/switch_buttons_left_outline.svg.import index 6af83fdf5..4ae206407 100644 --- a/assets/images/gamepad/switch/switch_buttons_left_outline.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://74j5mtle3kdm" +uid="uid://rgk8j0hf5vlr" path="res://.godot/imported/switch_buttons_left_outline.svg-118459ac3b923ecca44523ae9fa70567.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_none.svg.import b/assets/images/gamepad/switch/switch_buttons_none.svg.import index c3d772836..3eedf2c2a 100644 --- a/assets/images/gamepad/switch/switch_buttons_none.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_none.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cb2gnj26b21xw" +uid="uid://dnsiav286rdds" path="res://.godot/imported/switch_buttons_none.svg-6a2811f7e8932df09cbe1a635cb9f599.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_right.svg.import b/assets/images/gamepad/switch/switch_buttons_right.svg.import index da82bff3b..36cf2075d 100644 --- a/assets/images/gamepad/switch/switch_buttons_right.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bcthc11uwae6n" +uid="uid://bnbk3lkeaqwfa" path="res://.godot/imported/switch_buttons_right.svg-77b67acfd767e572f57d2b0a612e0c6e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_right_outline.svg.import b/assets/images/gamepad/switch/switch_buttons_right_outline.svg.import index b35c39c77..0941ad0f0 100644 --- a/assets/images/gamepad/switch/switch_buttons_right_outline.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://daeysxerxps5w" +uid="uid://bpk81q4kpeaoo" path="res://.godot/imported/switch_buttons_right_outline.svg-3a02639270ce574170d60f749bb8f6b3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_up.svg.import b/assets/images/gamepad/switch/switch_buttons_up.svg.import index 0ec56706f..689170ba0 100644 --- a/assets/images/gamepad/switch/switch_buttons_up.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bo07ay1vxneo1" +uid="uid://ducpt6w14ol4i" path="res://.godot/imported/switch_buttons_up.svg-83a91b61f01d7a98db5367af6fde26bb.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_up_outline.svg.import b/assets/images/gamepad/switch/switch_buttons_up_outline.svg.import index 03f7f6a01..a2d7a6744 100644 --- a/assets/images/gamepad/switch/switch_buttons_up_outline.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://fgojvl2uagek" +uid="uid://d0xpybdv7ghc0" path="res://.godot/imported/switch_buttons_up_outline.svg-2c6a706edc84e816c816a328500bd9d4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_vertical.svg.import b/assets/images/gamepad/switch/switch_buttons_vertical.svg.import index 0af48932e..ce74a21ee 100644 --- a/assets/images/gamepad/switch/switch_buttons_vertical.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bhk3nkfw0wcf5" +uid="uid://bdaj6q5kxfki6" path="res://.godot/imported/switch_buttons_vertical.svg-116cdb0a1a7091a1ff6acf55b63c188a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_buttons_vertical_outline.svg.import b/assets/images/gamepad/switch/switch_buttons_vertical_outline.svg.import index 541c2d916..12a69482d 100644 --- a/assets/images/gamepad/switch/switch_buttons_vertical_outline.svg.import +++ b/assets/images/gamepad/switch/switch_buttons_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c68sl1ds8mwif" +uid="uid://tocbmfxy0u7m" path="res://.godot/imported/switch_buttons_vertical_outline.svg-68d6627f06f8dd0623c33edcb97f55c5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_down.svg.import b/assets/images/gamepad/switch/switch_down.svg.import index 1caadb71a..22b4b8930 100644 --- a/assets/images/gamepad/switch/switch_down.svg.import +++ b/assets/images/gamepad/switch/switch_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://fvoo46vkt8b7" +uid="uid://c3ub75gme530f" path="res://.godot/imported/switch_down.svg-61d08bf369236097d0f22cce525a5c38.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_down_outline.svg.import b/assets/images/gamepad/switch/switch_down_outline.svg.import index cf8b7aa18..6aa80defd 100644 --- a/assets/images/gamepad/switch/switch_down_outline.svg.import +++ b/assets/images/gamepad/switch/switch_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://r0ibcb2juk06" +uid="uid://cw03pa7jrwxgj" path="res://.godot/imported/switch_down_outline.svg-fbcfc8cec1d6a38af097d77261126689.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad.svg.import b/assets/images/gamepad/switch/switch_dpad.svg.import index befcfdf9e..e3c3404eb 100644 --- a/assets/images/gamepad/switch/switch_dpad.svg.import +++ b/assets/images/gamepad/switch/switch_dpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://jv2vbh18muy8" +uid="uid://b735i56yjhict" path="res://.godot/imported/switch_dpad.svg-7807cb4ee6e43bb5229c8adeea45711a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_all.svg.import b/assets/images/gamepad/switch/switch_dpad_all.svg.import index c7cbf3bb9..0a481b0c5 100644 --- a/assets/images/gamepad/switch/switch_dpad_all.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c5s007gtnials" +uid="uid://bjvnio2bp1fre" path="res://.godot/imported/switch_dpad_all.svg-c76aff9dbb1cd187d2157a2e8b58e2cb.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_down.svg.import b/assets/images/gamepad/switch/switch_dpad_down.svg.import index 724288069..563e95301 100644 --- a/assets/images/gamepad/switch/switch_dpad_down.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://beb058tm84w5j" +uid="uid://drfroynux1b3f" path="res://.godot/imported/switch_dpad_down.svg-0ef2179131eb4be2a0e4adebac4ac50c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_down_outline.svg.import b/assets/images/gamepad/switch/switch_dpad_down_outline.svg.import index 4c5fd8e8b..c99c097fd 100644 --- a/assets/images/gamepad/switch/switch_dpad_down_outline.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://47hflntjho5b" +uid="uid://h6ejds5wp0dh" path="res://.godot/imported/switch_dpad_down_outline.svg-10d37f00d3450563ee74f2f94bf36e59.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_horizontal.svg.import b/assets/images/gamepad/switch/switch_dpad_horizontal.svg.import index 1bd361249..7cee29f10 100644 --- a/assets/images/gamepad/switch/switch_dpad_horizontal.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bef43efnn06xj" +uid="uid://4ggtvi2mmku4" path="res://.godot/imported/switch_dpad_horizontal.svg-fcc9456c9b31a5069934e1a43a8c131e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_horizontal_outline.svg.import b/assets/images/gamepad/switch/switch_dpad_horizontal_outline.svg.import index aba5ad75f..842ac1ce2 100644 --- a/assets/images/gamepad/switch/switch_dpad_horizontal_outline.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dywek0kaad7v5" +uid="uid://ciny67jr2mun7" path="res://.godot/imported/switch_dpad_horizontal_outline.svg-96d5ee9475602a88b92a8801639b1fe3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_left.svg.import b/assets/images/gamepad/switch/switch_dpad_left.svg.import index 6184c2f0a..11cb5f796 100644 --- a/assets/images/gamepad/switch/switch_dpad_left.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dnp6tc41r8xi3" +uid="uid://e3db7ixs8tjk" path="res://.godot/imported/switch_dpad_left.svg-05c896c13318854a72359da288ae2bb8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_left_outline.svg.import b/assets/images/gamepad/switch/switch_dpad_left_outline.svg.import index 21c0e248d..545d6d47f 100644 --- a/assets/images/gamepad/switch/switch_dpad_left_outline.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://csa323ms64r2f" +uid="uid://b3qhsmna1ha82" path="res://.godot/imported/switch_dpad_left_outline.svg-70e11ee38114bb00d2190752646e1749.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_none.svg.import b/assets/images/gamepad/switch/switch_dpad_none.svg.import index c395314e3..b2c4f40aa 100644 --- a/assets/images/gamepad/switch/switch_dpad_none.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_none.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://24ehq8ht56tj" +uid="uid://b7p4n2y8qaedr" path="res://.godot/imported/switch_dpad_none.svg-f9665f20d992e21ae6b38f27360f2832.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_right.svg.import b/assets/images/gamepad/switch/switch_dpad_right.svg.import index 15fabb6d0..f4e9472e8 100644 --- a/assets/images/gamepad/switch/switch_dpad_right.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://u7ity54x4vx3" +uid="uid://vdro02bqhoxn" path="res://.godot/imported/switch_dpad_right.svg-4cd732b19a61281fb7fe387ab4572bf2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_right_outline.svg.import b/assets/images/gamepad/switch/switch_dpad_right_outline.svg.import index 34e9a945b..126c2af6c 100644 --- a/assets/images/gamepad/switch/switch_dpad_right_outline.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bqj684mnvtudu" +uid="uid://cpkc6kydivw6u" path="res://.godot/imported/switch_dpad_right_outline.svg-bde5b57e68848cbbebf47fbe2c7bdd9f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_up.svg.import b/assets/images/gamepad/switch/switch_dpad_up.svg.import index 5a4f3281a..34fd75ace 100644 --- a/assets/images/gamepad/switch/switch_dpad_up.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bi8viiuisfuxh" +uid="uid://hwsqjx3falhf" path="res://.godot/imported/switch_dpad_up.svg-954d4772f896299df4ab2a8a9b7f736d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_up_outline.svg.import b/assets/images/gamepad/switch/switch_dpad_up_outline.svg.import index e0313033b..7470c9bca 100644 --- a/assets/images/gamepad/switch/switch_dpad_up_outline.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://qblns8wu6gho" +uid="uid://b821cjw2446qb" path="res://.godot/imported/switch_dpad_up_outline.svg-7301585ac4bfce54f037d97a85bc5987.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_vertical.svg.import b/assets/images/gamepad/switch/switch_dpad_vertical.svg.import index 59afd28ac..145846bc9 100644 --- a/assets/images/gamepad/switch/switch_dpad_vertical.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://iafkrydlew02" +uid="uid://c8br7hyx2d3b4" path="res://.godot/imported/switch_dpad_vertical.svg-dad4d987caf73c3e0cf76970514a9013.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_dpad_vertical_outline.svg.import b/assets/images/gamepad/switch/switch_dpad_vertical_outline.svg.import index 7fde8cc7d..235a26ee5 100644 --- a/assets/images/gamepad/switch/switch_dpad_vertical_outline.svg.import +++ b/assets/images/gamepad/switch/switch_dpad_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b20xc4gi43jml" +uid="uid://8mchw4pq6eqj" path="res://.godot/imported/switch_dpad_vertical_outline.svg-275370e91928314bb10aae9037162cf8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon.svg.import b/assets/images/gamepad/switch/switch_joycon.svg.import index 6bcdae595..406c1d2e4 100644 --- a/assets/images/gamepad/switch/switch_joycon.svg.import +++ b/assets/images/gamepad/switch/switch_joycon.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://wjwxqmerud6" +uid="uid://do7ujgwkwb5vj" path="res://.godot/imported/switch_joycon.svg-99630983d8d21523e280793a0ba42727.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left.svg.import b/assets/images/gamepad/switch/switch_joycon_left.svg.import index 6d968690a..3fa700beb 100644 --- a/assets/images/gamepad/switch/switch_joycon_left.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c08u1m8o4kswm" +uid="uid://bnfjymekps4r4" path="res://.godot/imported/switch_joycon_left.svg-94f983c6b9b9f9da0a1e76f4f73cf558.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_diagonal.svg.import b/assets/images/gamepad/switch/switch_joycon_left_diagonal.svg.import index eeea8a66b..ec478a744 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_diagonal.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_diagonal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ddkwutbwswgtg" +uid="uid://be2l53k0ry6tp" path="res://.godot/imported/switch_joycon_left_diagonal.svg-21f51c8d27047fb130b970c22ee818c9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_diagonal_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_left_diagonal_outline.svg.import index 745436526..35d7251b8 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_diagonal_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_diagonal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://da2m56sqfbnt7" +uid="uid://bkxj6kxkou5gy" path="res://.godot/imported/switch_joycon_left_diagonal_outline.svg-d789357e25c9c1e6b98f3df559b01824.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_horizontal.svg.import b/assets/images/gamepad/switch/switch_joycon_left_horizontal.svg.import index 02df16a86..9adc06e63 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_horizontal.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://becf7dkmrt720" +uid="uid://c4gr80dvc4xc" path="res://.godot/imported/switch_joycon_left_horizontal.svg-706712efeeb38e67a4b0d2fc321fabf0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_horizontal_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_left_horizontal_outline.svg.import index 299430806..e88036d2f 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_horizontal_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bm4ltpk3tysjp" +uid="uid://bdgwitxguooyb" path="res://.godot/imported/switch_joycon_left_horizontal_outline.svg-2a5ba48e8c9646c039e96b549d07ed3d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_left_outline.svg.import index f7f363599..06272d0f4 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://80hrmjlfhdk7" +uid="uid://csmi83v2epni4" path="res://.godot/imported/switch_joycon_left_outline.svg-1b27cd759582719a0e01020e77e4e552.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_rotate.svg.import b/assets/images/gamepad/switch/switch_joycon_left_rotate.svg.import index 457a3e6ce..8df3ff1a1 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_rotate.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_rotate.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://by4xd4qq5dspy" +uid="uid://cda8etpu56yjn" path="res://.godot/imported/switch_joycon_left_rotate.svg-944c087dc0aefb6bda31d7f3dd5ad357.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_rotate_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_left_rotate_outline.svg.import index 8adf0d8a8..3c109db14 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_rotate_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_rotate_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d4mgd3ang8xv" +uid="uid://cef02qkdnf5w3" path="res://.godot/imported/switch_joycon_left_rotate_outline.svg-36cc63c32af2ad6da749f7600177a363.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_vertical.svg.import b/assets/images/gamepad/switch/switch_joycon_left_vertical.svg.import index 5d1b76bde..845015960 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_vertical.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bcptlybmhqpdn" +uid="uid://os8sumlnqj8n" path="res://.godot/imported/switch_joycon_left_vertical.svg-ea0d7edee10c9f6aa1194e2a5cb0b6ee.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_left_vertical_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_left_vertical_outline.svg.import index e5a0c677f..683646b20 100644 --- a/assets/images/gamepad/switch/switch_joycon_left_vertical_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_left_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cqk8lkc0njdt4" +uid="uid://fnkkivip7r3j" path="res://.godot/imported/switch_joycon_left_vertical_outline.svg-7341150781697f5fde961db943dd8d14.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_outline.svg.import index 8a463cf8c..f1e1e7470 100644 --- a/assets/images/gamepad/switch/switch_joycon_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bgms863mn70t0" +uid="uid://c0qvwfvx2n4pm" path="res://.godot/imported/switch_joycon_outline.svg-4c0db1382cdb8427d69fb63a3c203715.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right.svg.import b/assets/images/gamepad/switch/switch_joycon_right.svg.import index 785494238..00f3106fe 100644 --- a/assets/images/gamepad/switch/switch_joycon_right.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dbyiyxa814wdx" +uid="uid://71s1fkcvihco" path="res://.godot/imported/switch_joycon_right.svg-6826bf80e5c192664d7c67517db6d812.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_diagonal.svg.import b/assets/images/gamepad/switch/switch_joycon_right_diagonal.svg.import index c35ff86f0..16a29ad17 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_diagonal.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_diagonal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dt8l32jey6r56" +uid="uid://dmsy2w7mnm2c4" path="res://.godot/imported/switch_joycon_right_diagonal.svg-00a3f01eca458a52372dfef04c9eac3d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_diagonal_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_right_diagonal_outline.svg.import index cf08364c2..6c6f3ac4a 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_diagonal_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_diagonal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cu250actmithx" +uid="uid://rohtiop6glol" path="res://.godot/imported/switch_joycon_right_diagonal_outline.svg-7b81b2b30e1d1b6e045b2a75ac5e1632.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_horizontal.svg.import b/assets/images/gamepad/switch/switch_joycon_right_horizontal.svg.import index 5526a380e..4ebdfdcc2 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_horizontal.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://btxhhdt177nb1" +uid="uid://b5yxtlxfblwo6" path="res://.godot/imported/switch_joycon_right_horizontal.svg-886a5a2bd5511d4a635b73c4c8993459.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_horizontal_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_right_horizontal_outline.svg.import index 9342678bd..7c28b5a60 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_horizontal_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c7yoljyn13w7v" +uid="uid://dk7b1dgsgmkx5" path="res://.godot/imported/switch_joycon_right_horizontal_outline.svg-0f3bf20cde47b289a347a8ce9789447d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_right_outline.svg.import index cd9064df4..521fb5953 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://hy4gu3gggx0g" +uid="uid://ciehfb12nue5v" path="res://.godot/imported/switch_joycon_right_outline.svg-df7dcdee297c267ee8883cb0f3d67255.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_rotate.svg.import b/assets/images/gamepad/switch/switch_joycon_right_rotate.svg.import index dfcb20514..44f4f3ac7 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_rotate.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_rotate.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dvog60nf0kt5s" +uid="uid://rsofb7ugkkyy" path="res://.godot/imported/switch_joycon_right_rotate.svg-75b82eab6a389f044d7b7ba20fafd2b6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_rotate_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_right_rotate_outline.svg.import index d847a9a85..de0cd5036 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_rotate_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_rotate_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://6hpdc0rkrgba" +uid="uid://c2yalpuhsrbbb" path="res://.godot/imported/switch_joycon_right_rotate_outline.svg-abbe3cb88bcf15930da81ddf7558ec14.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_vertical.svg.import b/assets/images/gamepad/switch/switch_joycon_right_vertical.svg.import index d0c8b0559..b3c4a193a 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_vertical.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://rb5ecc7g4kb2" +uid="uid://c1msoefnvve7s" path="res://.godot/imported/switch_joycon_right_vertical.svg-c9d4df26e43a2e80c0a1fc0bb9c82f2c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_joycon_right_vertical_outline.svg.import b/assets/images/gamepad/switch/switch_joycon_right_vertical_outline.svg.import index 93eab4cab..95994228f 100644 --- a/assets/images/gamepad/switch/switch_joycon_right_vertical_outline.svg.import +++ b/assets/images/gamepad/switch/switch_joycon_right_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b26di7327d50c" +uid="uid://cafbe34hfdgrt" path="res://.godot/imported/switch_joycon_right_vertical_outline.svg-bcb5132be16c9ef14104546786ca9b6e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_left.svg.import b/assets/images/gamepad/switch/switch_left.svg.import index 14a123c58..fd0e5a313 100644 --- a/assets/images/gamepad/switch/switch_left.svg.import +++ b/assets/images/gamepad/switch/switch_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ck1hpi2g0iprq" +uid="uid://btfnrwxhv1hqm" path="res://.godot/imported/switch_left.svg-c7bab108015a169efe10a56ac6b51b0b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_left_outline.svg.import b/assets/images/gamepad/switch/switch_left_outline.svg.import index eee17ff2f..651c4dcc8 100644 --- a/assets/images/gamepad/switch/switch_left_outline.svg.import +++ b/assets/images/gamepad/switch/switch_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ci3kxstltykfw" +uid="uid://bdg0cg6f0ms1b" path="res://.godot/imported/switch_left_outline.svg-dff174aeb5d10c31520c2edd059fbd84.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_right.svg.import b/assets/images/gamepad/switch/switch_right.svg.import index 1761ba56b..d7eb140f0 100644 --- a/assets/images/gamepad/switch/switch_right.svg.import +++ b/assets/images/gamepad/switch/switch_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://duaf8xdncqrlo" +uid="uid://bwcjsvfcryoyv" path="res://.godot/imported/switch_right.svg-0d9b55b2f31ae028dca22757697d6a7b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_right_outline.svg.import b/assets/images/gamepad/switch/switch_right_outline.svg.import index da1677270..d3011b3b3 100644 --- a/assets/images/gamepad/switch/switch_right_outline.svg.import +++ b/assets/images/gamepad/switch/switch_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dt3gdq87voy2x" +uid="uid://nsys24svmvg5" path="res://.godot/imported/switch_right_outline.svg-f470d7fd258fc993c7b61017d1013f87.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_l.svg.import b/assets/images/gamepad/switch/switch_stick_l.svg.import index 1b164a72f..18b948043 100644 --- a/assets/images/gamepad/switch/switch_stick_l.svg.import +++ b/assets/images/gamepad/switch/switch_stick_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bgkt5t7mmbhts" +uid="uid://ruhfjlhtlik3" path="res://.godot/imported/switch_stick_l.svg-aae382f05f8e763d7ef1c2fc250541db.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_l_down.svg.import b/assets/images/gamepad/switch/switch_stick_l_down.svg.import index 562df6f15..2fe8e019e 100644 --- a/assets/images/gamepad/switch/switch_stick_l_down.svg.import +++ b/assets/images/gamepad/switch/switch_stick_l_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bvnpkij77axxu" +uid="uid://ri6vpuerb2jn" path="res://.godot/imported/switch_stick_l_down.svg-4acd6f3cbdcb3b3bf61ba920a3b60179.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_l_horizontal.svg.import b/assets/images/gamepad/switch/switch_stick_l_horizontal.svg.import index 46d6c8f0b..4523e3b4f 100644 --- a/assets/images/gamepad/switch/switch_stick_l_horizontal.svg.import +++ b/assets/images/gamepad/switch/switch_stick_l_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cisoss8hfkibx" +uid="uid://dn4ao7qj2r7iq" path="res://.godot/imported/switch_stick_l_horizontal.svg-cc9f66eb7cbea3b6415a31bd80039f78.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_l_left.svg.import b/assets/images/gamepad/switch/switch_stick_l_left.svg.import index e1b799692..021cd9821 100644 --- a/assets/images/gamepad/switch/switch_stick_l_left.svg.import +++ b/assets/images/gamepad/switch/switch_stick_l_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bbad7xmuveel8" +uid="uid://dvdwtqdrgcx55" path="res://.godot/imported/switch_stick_l_left.svg-77c801aca888a97a967cfca83860300c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_l_press.svg.import b/assets/images/gamepad/switch/switch_stick_l_press.svg.import index 389181a6f..006e2b7e2 100644 --- a/assets/images/gamepad/switch/switch_stick_l_press.svg.import +++ b/assets/images/gamepad/switch/switch_stick_l_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ca4fcl2jsx62k" +uid="uid://d1xnwfo4kwgwe" path="res://.godot/imported/switch_stick_l_press.svg-c5dcc6fd5751f66ea51a7e110553bb1c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_l_right.svg.import b/assets/images/gamepad/switch/switch_stick_l_right.svg.import index 731b2577c..94ceacd10 100644 --- a/assets/images/gamepad/switch/switch_stick_l_right.svg.import +++ b/assets/images/gamepad/switch/switch_stick_l_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://lpy6n3raxq2" +uid="uid://ckdjvbxmun3hk" path="res://.godot/imported/switch_stick_l_right.svg-de601db1d53058711973f52e5d6c6a20.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_l_up.svg.import b/assets/images/gamepad/switch/switch_stick_l_up.svg.import index 52d559c4d..447147009 100644 --- a/assets/images/gamepad/switch/switch_stick_l_up.svg.import +++ b/assets/images/gamepad/switch/switch_stick_l_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://btrirhorp6y57" +uid="uid://c54734rwsvi5y" path="res://.godot/imported/switch_stick_l_up.svg-7ec61c14ae2f7c598dd6b2f74b871769.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_l_vertical.svg.import b/assets/images/gamepad/switch/switch_stick_l_vertical.svg.import index 03b6c12fc..3fc2ad998 100644 --- a/assets/images/gamepad/switch/switch_stick_l_vertical.svg.import +++ b/assets/images/gamepad/switch/switch_stick_l_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bhos74bdthcxj" +uid="uid://wjqj6c2wcyry" path="res://.godot/imported/switch_stick_l_vertical.svg-8996d30495edbd4880713f90220989c2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_r.svg.import b/assets/images/gamepad/switch/switch_stick_r.svg.import index 32d479dec..66cdff5c0 100644 --- a/assets/images/gamepad/switch/switch_stick_r.svg.import +++ b/assets/images/gamepad/switch/switch_stick_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c1fas8o38r02p" +uid="uid://bnr0q7kupfac8" path="res://.godot/imported/switch_stick_r.svg-6e256a5f8d4a4fc03b463997f820cd35.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_r_down.svg.import b/assets/images/gamepad/switch/switch_stick_r_down.svg.import index fa5fcefda..3d47f4ab4 100644 --- a/assets/images/gamepad/switch/switch_stick_r_down.svg.import +++ b/assets/images/gamepad/switch/switch_stick_r_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dqrqw3lv3kl3e" +uid="uid://c37v6pn2p4pa7" path="res://.godot/imported/switch_stick_r_down.svg-d521a00541e87de24830d67369f603c0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_r_horizontal.svg.import b/assets/images/gamepad/switch/switch_stick_r_horizontal.svg.import index 43fb60dac..857b667f2 100644 --- a/assets/images/gamepad/switch/switch_stick_r_horizontal.svg.import +++ b/assets/images/gamepad/switch/switch_stick_r_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bp82itprpqi44" +uid="uid://kyn53m0q0o60" path="res://.godot/imported/switch_stick_r_horizontal.svg-ca8fab810667df12b86832050d3122a6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_r_left.svg.import b/assets/images/gamepad/switch/switch_stick_r_left.svg.import index 6b4aa9933..3591e459a 100644 --- a/assets/images/gamepad/switch/switch_stick_r_left.svg.import +++ b/assets/images/gamepad/switch/switch_stick_r_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bcstmmysk3e1" +uid="uid://d0s0cx6bqiovg" path="res://.godot/imported/switch_stick_r_left.svg-67ebb0066735a4763b14d2a4736f8abd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_r_press.svg.import b/assets/images/gamepad/switch/switch_stick_r_press.svg.import index f23c17068..7fa271e6e 100644 --- a/assets/images/gamepad/switch/switch_stick_r_press.svg.import +++ b/assets/images/gamepad/switch/switch_stick_r_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dop088ppjtx7r" +uid="uid://dalb0538ux4qy" path="res://.godot/imported/switch_stick_r_press.svg-a7afce4c88b1f9ebb43bed3d6f998e63.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_r_right.svg.import b/assets/images/gamepad/switch/switch_stick_r_right.svg.import index 60b335544..1672ea07b 100644 --- a/assets/images/gamepad/switch/switch_stick_r_right.svg.import +++ b/assets/images/gamepad/switch/switch_stick_r_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://domi3pblhpgqy" +uid="uid://dxycp3mj54mwc" path="res://.godot/imported/switch_stick_r_right.svg-c8a8ea34f91cb8ac27b1b8dbc35ec2dc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_r_up.svg.import b/assets/images/gamepad/switch/switch_stick_r_up.svg.import index 1d4954c7b..fbfe87ed8 100644 --- a/assets/images/gamepad/switch/switch_stick_r_up.svg.import +++ b/assets/images/gamepad/switch/switch_stick_r_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dtkumw37qfis8" +uid="uid://be6s7r6y3ndty" path="res://.godot/imported/switch_stick_r_up.svg-f63e9b2a667b9ff07adefcb25e446532.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_r_vertical.svg.import b/assets/images/gamepad/switch/switch_stick_r_vertical.svg.import index b284a777c..d07402125 100644 --- a/assets/images/gamepad/switch/switch_stick_r_vertical.svg.import +++ b/assets/images/gamepad/switch/switch_stick_r_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bk1cmphnkacdo" +uid="uid://djlj5v8c0fiho" path="res://.godot/imported/switch_stick_r_vertical.svg-daf25366dd8c5fb5b47709470d853b99.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_side_l.svg.import b/assets/images/gamepad/switch/switch_stick_side_l.svg.import index 5542be985..3d60b754d 100644 --- a/assets/images/gamepad/switch/switch_stick_side_l.svg.import +++ b/assets/images/gamepad/switch/switch_stick_side_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dpwy5hc8fo0dl" +uid="uid://dm7aknxom21r7" path="res://.godot/imported/switch_stick_side_l.svg-726e57c646f64ead20cecca407755d36.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_side_r.svg.import b/assets/images/gamepad/switch/switch_stick_side_r.svg.import index 183cb2719..9707a83cd 100644 --- a/assets/images/gamepad/switch/switch_stick_side_r.svg.import +++ b/assets/images/gamepad/switch/switch_stick_side_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cxqi3b27jc5ya" +uid="uid://uu7ejty8j7se" path="res://.godot/imported/switch_stick_side_r.svg-f5c98e6b9a5fda0ac1413b4e26c81e57.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_top_l.svg.import b/assets/images/gamepad/switch/switch_stick_top_l.svg.import index 29f302f6e..1c2430d2b 100644 --- a/assets/images/gamepad/switch/switch_stick_top_l.svg.import +++ b/assets/images/gamepad/switch/switch_stick_top_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bac30sashnbxg" +uid="uid://dp5audyyanttc" path="res://.godot/imported/switch_stick_top_l.svg-29532a2bd9b1c20e1b2e0d001196bf77.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_stick_top_r.svg.import b/assets/images/gamepad/switch/switch_stick_top_r.svg.import index 539b049ed..232f05200 100644 --- a/assets/images/gamepad/switch/switch_stick_top_r.svg.import +++ b/assets/images/gamepad/switch/switch_stick_top_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cr1spi2ofv3eh" +uid="uid://dl8a0auf0ql2d" path="res://.godot/imported/switch_stick_top_r.svg-eef0efc5598b99da72fd2941b2e3b792.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_up.svg.import b/assets/images/gamepad/switch/switch_up.svg.import index 4037f7d05..51f39b65b 100644 --- a/assets/images/gamepad/switch/switch_up.svg.import +++ b/assets/images/gamepad/switch/switch_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dx6cbkmaquu0i" +uid="uid://sboeqnr1sdod" path="res://.godot/imported/switch_up.svg-a5ba228a57134357f57102bc90fb5990.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/switch/switch_up_outline.svg.import b/assets/images/gamepad/switch/switch_up_outline.svg.import index 901a85a15..25139d299 100644 --- a/assets/images/gamepad/switch/switch_up_outline.svg.import +++ b/assets/images/gamepad/switch/switch_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://yrros5e0ffvr" +uid="uid://rl6xjmy7brub" path="res://.godot/imported/switch_up_outline.svg-9e3a14a4bc0345ded4e75b1f08cfb511.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/controller_wii_classic.svg.import b/assets/images/gamepad/wii/controller_wii_classic.svg.import index e8fc4e45e..a8a2679b4 100644 --- a/assets/images/gamepad/wii/controller_wii_classic.svg.import +++ b/assets/images/gamepad/wii/controller_wii_classic.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4suct4lxd2o5" +uid="uid://ei2kbd7bns55" path="res://.godot/imported/controller_wii_classic.svg-dc1bf5a78ba834b5eec7afb117f4137b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/controller_wii_classic_pro.svg.import b/assets/images/gamepad/wii/controller_wii_classic_pro.svg.import index 7046bdf97..12203e329 100644 --- a/assets/images/gamepad/wii/controller_wii_classic_pro.svg.import +++ b/assets/images/gamepad/wii/controller_wii_classic_pro.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://3ljlvycfy0ks" +uid="uid://bcxmqua8k8ejy" path="res://.godot/imported/controller_wii_classic_pro.svg-f8d0beec497dfc798b1f5490367a3d92.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_1.svg.import b/assets/images/gamepad/wii/wii_button_1.svg.import index a96c9ebd0..3b353053b 100644 --- a/assets/images/gamepad/wii/wii_button_1.svg.import +++ b/assets/images/gamepad/wii/wii_button_1.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dcoic7xprw3s7" +uid="uid://cjxhg5l6ubkd0" path="res://.godot/imported/wii_button_1.svg-f3971dfa4194a9225bd416449e3d61e2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_1_outline.svg.import b/assets/images/gamepad/wii/wii_button_1_outline.svg.import index 55611bfe5..12216e56a 100644 --- a/assets/images/gamepad/wii/wii_button_1_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_1_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cfeyd5ywwf1v3" +uid="uid://caylm6q24ldpn" path="res://.godot/imported/wii_button_1_outline.svg-3b5502e8a7320e252362bb7d78e426ef.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_2.svg.import b/assets/images/gamepad/wii/wii_button_2.svg.import index 747117b90..39e976403 100644 --- a/assets/images/gamepad/wii/wii_button_2.svg.import +++ b/assets/images/gamepad/wii/wii_button_2.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bkcd4by7vgbic" +uid="uid://bwnjv1hmqeenc" path="res://.godot/imported/wii_button_2.svg-b4f862ed7c7db7792341ba5bde88ad21.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_2_outline.svg.import b/assets/images/gamepad/wii/wii_button_2_outline.svg.import index fba301901..5ac55daae 100644 --- a/assets/images/gamepad/wii/wii_button_2_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_2_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ctoaykmpm6a8c" +uid="uid://cmtbbb0bgfsql" path="res://.godot/imported/wii_button_2_outline.svg-cfe121ccf8884cf6a3879b2dcd03be56.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_a.svg.import b/assets/images/gamepad/wii/wii_button_a.svg.import index 9aff7052e..83681614b 100644 --- a/assets/images/gamepad/wii/wii_button_a.svg.import +++ b/assets/images/gamepad/wii/wii_button_a.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://6x7wk7qqt0qy" +uid="uid://b7xm48eehmmnd" path="res://.godot/imported/wii_button_a.svg-5f20ec3dde54a51fe6e3ae5eaa16be13.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_a_outline.svg.import b/assets/images/gamepad/wii/wii_button_a_outline.svg.import index a0d691671..272a78e47 100644 --- a/assets/images/gamepad/wii/wii_button_a_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_a_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cxola65t655fr" +uid="uid://i5dukvf56vj8" path="res://.godot/imported/wii_button_a_outline.svg-4402ad471557eecc824df6da919008d4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_b.svg.import b/assets/images/gamepad/wii/wii_button_b.svg.import index 18f872af4..f15620f78 100644 --- a/assets/images/gamepad/wii/wii_button_b.svg.import +++ b/assets/images/gamepad/wii/wii_button_b.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dp2k3akttnjqd" +uid="uid://xfx8mk42yvv4" path="res://.godot/imported/wii_button_b.svg-634186bb96a5f08cc9fa35ceff9b49c2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_b_outline.svg.import b/assets/images/gamepad/wii/wii_button_b_outline.svg.import index 02618f7c3..957def9f6 100644 --- a/assets/images/gamepad/wii/wii_button_b_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_b_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://75u34yhw6ks7" +uid="uid://yfsqhb2372dc" path="res://.godot/imported/wii_button_b_outline.svg-d37cc5bd2dff758524e27b3f09ae962b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_c.svg.import b/assets/images/gamepad/wii/wii_button_c.svg.import index 0f30fb67f..d0974083c 100644 --- a/assets/images/gamepad/wii/wii_button_c.svg.import +++ b/assets/images/gamepad/wii/wii_button_c.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ca65lbp3d7jt3" +uid="uid://brcl5b3d7nl0x" path="res://.godot/imported/wii_button_c.svg-31c8b68f736647fd87dbbd3a71ef4a32.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_c_outline.svg.import b/assets/images/gamepad/wii/wii_button_c_outline.svg.import index f7f13329a..9163c0a12 100644 --- a/assets/images/gamepad/wii/wii_button_c_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_c_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://83w8k2mfsbgr" +uid="uid://dt6yfi01r71h2" path="res://.godot/imported/wii_button_c_outline.svg-0dfbda2c9082cff0d6216b3bfe6e5ae2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_home.svg.import b/assets/images/gamepad/wii/wii_button_home.svg.import index 7c2e7d778..638e32adb 100644 --- a/assets/images/gamepad/wii/wii_button_home.svg.import +++ b/assets/images/gamepad/wii/wii_button_home.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dsfxlgf6phk2q" +uid="uid://df82wcu8iklpg" path="res://.godot/imported/wii_button_home.svg-39a2942a06e2f4fbb61cd7379e16ea2b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_home_outline.svg.import b/assets/images/gamepad/wii/wii_button_home_outline.svg.import index adcef55fe..d48a13290 100644 --- a/assets/images/gamepad/wii/wii_button_home_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_home_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b8o78v36eggpj" +uid="uid://bvi2knm4a0kvt" path="res://.godot/imported/wii_button_home_outline.svg-69d0e8774c6ceef72cc548f66a8c12f0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_l.svg.import b/assets/images/gamepad/wii/wii_button_l.svg.import index 483da952f..d60391ce9 100644 --- a/assets/images/gamepad/wii/wii_button_l.svg.import +++ b/assets/images/gamepad/wii/wii_button_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://duu0x4agtufwg" +uid="uid://jv0f2kumegn6" path="res://.godot/imported/wii_button_l.svg-912503ea1c7e2365be31bd6f699e5066.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_l_outline.svg.import b/assets/images/gamepad/wii/wii_button_l_outline.svg.import index 779c1a924..6a884ff7f 100644 --- a/assets/images/gamepad/wii/wii_button_l_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_l_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cjc4chwhtvj3b" +uid="uid://cjs2sdqbumhsg" path="res://.godot/imported/wii_button_l_outline.svg-85b264b627126990528575d0a9dd47fe.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_minus.svg.import b/assets/images/gamepad/wii/wii_button_minus.svg.import index 8f2830c58..85f7310cb 100644 --- a/assets/images/gamepad/wii/wii_button_minus.svg.import +++ b/assets/images/gamepad/wii/wii_button_minus.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bd5oq6dvv5rhw" +uid="uid://dvuc3xpvgl7g4" path="res://.godot/imported/wii_button_minus.svg-860a76d15f06501803c4c1ef2104a643.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_minus_outline.svg.import b/assets/images/gamepad/wii/wii_button_minus_outline.svg.import index a004d888f..bfe19db8c 100644 --- a/assets/images/gamepad/wii/wii_button_minus_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_minus_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c7qxk1ailjtm6" +uid="uid://byjprmsf4j5f0" path="res://.godot/imported/wii_button_minus_outline.svg-4398c3b614db97c270ab3e7e7806e277.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_plus.svg.import b/assets/images/gamepad/wii/wii_button_plus.svg.import index 13c25ada4..0fee5cd91 100644 --- a/assets/images/gamepad/wii/wii_button_plus.svg.import +++ b/assets/images/gamepad/wii/wii_button_plus.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://f52q2dj84otl" +uid="uid://c3eujt77bspur" path="res://.godot/imported/wii_button_plus.svg-c12882fb49167f9dc00b7d14e1170589.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_plus_outline.svg.import b/assets/images/gamepad/wii/wii_button_plus_outline.svg.import index 969b1f99b..98bf13393 100644 --- a/assets/images/gamepad/wii/wii_button_plus_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_plus_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://j546dwnx2s34" +uid="uid://d0oe8dsggbu1y" path="res://.godot/imported/wii_button_plus_outline.svg-ff9294805c33c642a0171789cfea8113.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_power.svg.import b/assets/images/gamepad/wii/wii_button_power.svg.import index 277d264cd..ddaaba1a2 100644 --- a/assets/images/gamepad/wii/wii_button_power.svg.import +++ b/assets/images/gamepad/wii/wii_button_power.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bxolmk00ra2q7" +uid="uid://cks6quaniwkcy" path="res://.godot/imported/wii_button_power.svg-435396daaeb61eed63539d4af5888db8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_power_outline.svg.import b/assets/images/gamepad/wii/wii_button_power_outline.svg.import index 0e0cdffc5..219f0e6e1 100644 --- a/assets/images/gamepad/wii/wii_button_power_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_power_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://co200ljfffs2i" +uid="uid://dn7402t0c60f" path="res://.godot/imported/wii_button_power_outline.svg-1c6888491ec4c3b86e8f3a308981bc70.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_r.svg.import b/assets/images/gamepad/wii/wii_button_r.svg.import index fc981f69f..33f03bba3 100644 --- a/assets/images/gamepad/wii/wii_button_r.svg.import +++ b/assets/images/gamepad/wii/wii_button_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4rnow0ar3tqm" +uid="uid://cc7h86ctk20wd" path="res://.godot/imported/wii_button_r.svg-b505c40405df7d9ddd0d4123932661aa.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_r_outline.svg.import b/assets/images/gamepad/wii/wii_button_r_outline.svg.import index fdef564d7..dd44b5209 100644 --- a/assets/images/gamepad/wii/wii_button_r_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_r_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dnrl5lg3bji20" +uid="uid://b86y01kmbhycv" path="res://.godot/imported/wii_button_r_outline.svg-ad820ab0a92dbc3f6f1d16b91c6cd6ae.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_x.svg.import b/assets/images/gamepad/wii/wii_button_x.svg.import index 212080902..4da31657e 100644 --- a/assets/images/gamepad/wii/wii_button_x.svg.import +++ b/assets/images/gamepad/wii/wii_button_x.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3di23ekl3lmx" +uid="uid://df7ytg83oxf72" path="res://.godot/imported/wii_button_x.svg-1a063d73a38b3f77292ba7e4a1949288.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_x_outline.svg.import b/assets/images/gamepad/wii/wii_button_x_outline.svg.import index 6895f3201..46d5f0d83 100644 --- a/assets/images/gamepad/wii/wii_button_x_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_x_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://0vb1fxtd43v5" +uid="uid://5tl4y6636hy7" path="res://.godot/imported/wii_button_x_outline.svg-483dfc6b8d0398678dd56d1606a1ad38.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_y.svg.import b/assets/images/gamepad/wii/wii_button_y.svg.import index 1e367a91d..302922be3 100644 --- a/assets/images/gamepad/wii/wii_button_y.svg.import +++ b/assets/images/gamepad/wii/wii_button_y.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bpotw335akb8t" +uid="uid://cdf7rxayfascb" path="res://.godot/imported/wii_button_y.svg-7564833f4f7b6b8744e922f68565213c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_y_outline.svg.import b/assets/images/gamepad/wii/wii_button_y_outline.svg.import index e5b147968..4f61fd2d9 100644 --- a/assets/images/gamepad/wii/wii_button_y_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_y_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bssbg0unve36t" +uid="uid://dnr5lobxfg4e3" path="res://.godot/imported/wii_button_y_outline.svg-edef6503d35ae3b32b12bf7e5265a384.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_z.svg.import b/assets/images/gamepad/wii/wii_button_z.svg.import index e56bb485d..3bd5e8c36 100644 --- a/assets/images/gamepad/wii/wii_button_z.svg.import +++ b/assets/images/gamepad/wii/wii_button_z.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://1v2vwdc62uv1" +uid="uid://b66hmbh5td3xb" path="res://.godot/imported/wii_button_z.svg-3e6e537a0ff751e16d141a6af24af82b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_z_outline.svg.import b/assets/images/gamepad/wii/wii_button_z_outline.svg.import index 5a89085b2..14b52ae33 100644 --- a/assets/images/gamepad/wii/wii_button_z_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_z_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dftgua4j3vw1v" +uid="uid://bjl54ftvfhbod" path="res://.godot/imported/wii_button_z_outline.svg-f1bae1443d801015a68c25df3253182f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_zl.svg.import b/assets/images/gamepad/wii/wii_button_zl.svg.import index 61fe6f543..21eae4049 100644 --- a/assets/images/gamepad/wii/wii_button_zl.svg.import +++ b/assets/images/gamepad/wii/wii_button_zl.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c53i2connw6hq" +uid="uid://jwoi6r8v0aeu" path="res://.godot/imported/wii_button_zl.svg-33b67456438af78986d72bc8cf4fc430.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_zl_outline.svg.import b/assets/images/gamepad/wii/wii_button_zl_outline.svg.import index 026b2b31d..63cb8e04e 100644 --- a/assets/images/gamepad/wii/wii_button_zl_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_zl_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bihrnvma1guc7" +uid="uid://yqlvxk8ggchu" path="res://.godot/imported/wii_button_zl_outline.svg-db207d8a2160d4011dc7d03c89a6f9d4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_zr.svg.import b/assets/images/gamepad/wii/wii_button_zr.svg.import index d00f4bf27..80790a5a6 100644 --- a/assets/images/gamepad/wii/wii_button_zr.svg.import +++ b/assets/images/gamepad/wii/wii_button_zr.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cemhw310oy5wu" +uid="uid://bhti7edlek6mt" path="res://.godot/imported/wii_button_zr.svg-cc820b66bfad2013da9264e62c792902.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_button_zr_outline.svg.import b/assets/images/gamepad/wii/wii_button_zr_outline.svg.import index 69c75e0fe..fd0a3ccc0 100644 --- a/assets/images/gamepad/wii/wii_button_zr_outline.svg.import +++ b/assets/images/gamepad/wii/wii_button_zr_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c8c8e20m7i04p" +uid="uid://dwlupjiaoniul" path="res://.godot/imported/wii_button_zr_outline.svg-2e2f67d64bfb7f0687e2503a50e85fdc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller.svg.import b/assets/images/gamepad/wii/wii_controller.svg.import index cbbd41582..eb19f1cc7 100644 --- a/assets/images/gamepad/wii/wii_controller.svg.import +++ b/assets/images/gamepad/wii/wii_controller.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cfp8balkqg7qk" +uid="uid://b4v3acclylug2" path="res://.godot/imported/wii_controller.svg-e0f576be4e6214f56f10891c4354aa0b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_diagonal.svg.import b/assets/images/gamepad/wii/wii_controller_diagonal.svg.import index 8457429f1..1cd8e268f 100644 --- a/assets/images/gamepad/wii/wii_controller_diagonal.svg.import +++ b/assets/images/gamepad/wii/wii_controller_diagonal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c15m18opca6al" +uid="uid://b8tnhfti5v0of" path="res://.godot/imported/wii_controller_diagonal.svg-7118a665b9990f09b67cf43eff86e33f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_diagonal_outline.svg.import b/assets/images/gamepad/wii/wii_controller_diagonal_outline.svg.import index be657d510..51ec8ff57 100644 --- a/assets/images/gamepad/wii/wii_controller_diagonal_outline.svg.import +++ b/assets/images/gamepad/wii/wii_controller_diagonal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://3edtfj4ygfn3" +uid="uid://b6ueqo8iokc7" path="res://.godot/imported/wii_controller_diagonal_outline.svg-5f2593408426aecdaf47cb7a95bdb8ae.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_horizontal.svg.import b/assets/images/gamepad/wii/wii_controller_horizontal.svg.import index 87f25acb5..08fb31a4c 100644 --- a/assets/images/gamepad/wii/wii_controller_horizontal.svg.import +++ b/assets/images/gamepad/wii/wii_controller_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bs6pp8e14k4o6" +uid="uid://r8mop0r6qyb8" path="res://.godot/imported/wii_controller_horizontal.svg-0a9a3682bceb873593b8d42efc2fb9cd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_horizontal_outline.svg.import b/assets/images/gamepad/wii/wii_controller_horizontal_outline.svg.import index dbe6bb7e7..eaf060508 100644 --- a/assets/images/gamepad/wii/wii_controller_horizontal_outline.svg.import +++ b/assets/images/gamepad/wii/wii_controller_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d0ua8p1d0if3o" +uid="uid://dc8mxciok1wjp" path="res://.godot/imported/wii_controller_horizontal_outline.svg-1f407578800bce47a5f13e344d3507b1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_nunchuk.svg.import b/assets/images/gamepad/wii/wii_controller_nunchuk.svg.import index b839ecd9e..bc7eeb163 100644 --- a/assets/images/gamepad/wii/wii_controller_nunchuk.svg.import +++ b/assets/images/gamepad/wii/wii_controller_nunchuk.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://curc4goqj1g5f" +uid="uid://bm1qtwckkhj27" path="res://.godot/imported/wii_controller_nunchuk.svg-cb51f3e0118b6c7df3473dd431d72bd3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_nunchuk_wire.svg.import b/assets/images/gamepad/wii/wii_controller_nunchuk_wire.svg.import index 66aae118f..9bd8cbf85 100644 --- a/assets/images/gamepad/wii/wii_controller_nunchuk_wire.svg.import +++ b/assets/images/gamepad/wii/wii_controller_nunchuk_wire.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dtqfknuw14csy" +uid="uid://dpwe15m58b588" path="res://.godot/imported/wii_controller_nunchuk_wire.svg-351f1aa3d70f0f335ce2cc9fadfd1588.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_outline.svg.import b/assets/images/gamepad/wii/wii_controller_outline.svg.import index c00e1032d..c7f09cb53 100644 --- a/assets/images/gamepad/wii/wii_controller_outline.svg.import +++ b/assets/images/gamepad/wii/wii_controller_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bh21a5f588v8f" +uid="uid://bx3v504x4hdh8" path="res://.godot/imported/wii_controller_outline.svg-471483e247b136c434695e68c2e93481.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_rotate.svg.import b/assets/images/gamepad/wii/wii_controller_rotate.svg.import index 9823550f3..d82a07c28 100644 --- a/assets/images/gamepad/wii/wii_controller_rotate.svg.import +++ b/assets/images/gamepad/wii/wii_controller_rotate.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c14kfcscy2d7k" +uid="uid://bb1l6j3ablgv8" path="res://.godot/imported/wii_controller_rotate.svg-df11ad5a2ddbe45c2fbe75e315f9c7b5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_rotate_outline.svg.import b/assets/images/gamepad/wii/wii_controller_rotate_outline.svg.import index 349dcd14e..9d3006d4e 100644 --- a/assets/images/gamepad/wii/wii_controller_rotate_outline.svg.import +++ b/assets/images/gamepad/wii/wii_controller_rotate_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://i5l41k7eklva" +uid="uid://b6mll70inm1ht" path="res://.godot/imported/wii_controller_rotate_outline.svg-3937f3c575637192eb0d6244580122ae.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_vertical.svg.import b/assets/images/gamepad/wii/wii_controller_vertical.svg.import index a442169ca..689464333 100644 --- a/assets/images/gamepad/wii/wii_controller_vertical.svg.import +++ b/assets/images/gamepad/wii/wii_controller_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://we2qa5i8d6f8" +uid="uid://dopema7nkbl5n" path="res://.godot/imported/wii_controller_vertical.svg-ea6f5448200b26577d51d7f833240b91.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_controller_vertical_outline.svg.import b/assets/images/gamepad/wii/wii_controller_vertical_outline.svg.import index 92dd7aed0..e0179b5d0 100644 --- a/assets/images/gamepad/wii/wii_controller_vertical_outline.svg.import +++ b/assets/images/gamepad/wii/wii_controller_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cc06bdslokvu4" +uid="uid://bjf4geurelffk" path="res://.godot/imported/wii_controller_vertical_outline.svg-e07ae70b6c11cfe021f9ab4ab7881966.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad.svg.import b/assets/images/gamepad/wii/wii_dpad.svg.import index 00e4dcda6..91c9eca2b 100644 --- a/assets/images/gamepad/wii/wii_dpad.svg.import +++ b/assets/images/gamepad/wii/wii_dpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://n3xg588juoe5" +uid="uid://dh7gm6mnxis68" path="res://.godot/imported/wii_dpad.svg-4fb5b0b90bbefe77fe17c4ecae222273.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_all.svg.import b/assets/images/gamepad/wii/wii_dpad_all.svg.import index 05b27c19b..c3f50caf9 100644 --- a/assets/images/gamepad/wii/wii_dpad_all.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dj3i4yljcxef4" +uid="uid://hsh53klk8kep" path="res://.godot/imported/wii_dpad_all.svg-b572646c4291f5f7ef2a7dadda7e9c3e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_down.svg.import b/assets/images/gamepad/wii/wii_dpad_down.svg.import index 04d15cf4d..663820a75 100644 --- a/assets/images/gamepad/wii/wii_dpad_down.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://drhsjhalwcb0p" +uid="uid://14lpwxruj5f2" path="res://.godot/imported/wii_dpad_down.svg-b51b622985429f7e34ab58bad7bcab45.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_down_outline.svg.import b/assets/images/gamepad/wii/wii_dpad_down_outline.svg.import index 6e73c1288..d016e04bd 100644 --- a/assets/images/gamepad/wii/wii_dpad_down_outline.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dyepvuf3v60cr" +uid="uid://3n2slhg0yjdc" path="res://.godot/imported/wii_dpad_down_outline.svg-922080eece2ca41000f590edac255f4d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_horizontal.svg.import b/assets/images/gamepad/wii/wii_dpad_horizontal.svg.import index bad5f90c3..10f215709 100644 --- a/assets/images/gamepad/wii/wii_dpad_horizontal.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b0y1ldkgunfp8" +uid="uid://cp5tjtd0inbif" path="res://.godot/imported/wii_dpad_horizontal.svg-d52e9fa42190433f3e28d0bacb969824.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_horizontal_outline.svg.import b/assets/images/gamepad/wii/wii_dpad_horizontal_outline.svg.import index 838d7d5c2..853b48993 100644 --- a/assets/images/gamepad/wii/wii_dpad_horizontal_outline.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ddnggnmibiouq" +uid="uid://bq00llqb4m5lf" path="res://.godot/imported/wii_dpad_horizontal_outline.svg-401b2ffa62040f3b471c3b0625f2fa66.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_left.svg.import b/assets/images/gamepad/wii/wii_dpad_left.svg.import index 27b4bfa60..2e8acafe2 100644 --- a/assets/images/gamepad/wii/wii_dpad_left.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c5sxbuul3e8jt" +uid="uid://cbvdeo4q1do0u" path="res://.godot/imported/wii_dpad_left.svg-44bbfd0d9e89a5075cfb7b59e1a8b14f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_left_outline.svg.import b/assets/images/gamepad/wii/wii_dpad_left_outline.svg.import index ecaf6f73d..6ffb7aa22 100644 --- a/assets/images/gamepad/wii/wii_dpad_left_outline.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cwbie6fxsapvh" +uid="uid://dmo0nkxs3g6jd" path="res://.godot/imported/wii_dpad_left_outline.svg-05e2fbc62cc8a58436651d50e69d1d2a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_none.svg.import b/assets/images/gamepad/wii/wii_dpad_none.svg.import index 7fb800ab4..bafa2e15c 100644 --- a/assets/images/gamepad/wii/wii_dpad_none.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_none.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dpl3s1qpws0si" +uid="uid://bcggdc4awm8fe" path="res://.godot/imported/wii_dpad_none.svg-d875b89299cdd20c760f4bdab6f6fcb8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_right.svg.import b/assets/images/gamepad/wii/wii_dpad_right.svg.import index 1ae6f5ab3..5243ffdd4 100644 --- a/assets/images/gamepad/wii/wii_dpad_right.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bpyq17k74erhn" +uid="uid://de22wvyfphorq" path="res://.godot/imported/wii_dpad_right.svg-19bf50599836d669a24fa4098e5f1cbf.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_right_outline.svg.import b/assets/images/gamepad/wii/wii_dpad_right_outline.svg.import index baf895708..1c76952ab 100644 --- a/assets/images/gamepad/wii/wii_dpad_right_outline.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://r1sbcvftihte" +uid="uid://rlehfgm7tu8n" path="res://.godot/imported/wii_dpad_right_outline.svg-65667971788b78ade0accd165140264a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_up.svg.import b/assets/images/gamepad/wii/wii_dpad_up.svg.import index fb9771552..f8414526a 100644 --- a/assets/images/gamepad/wii/wii_dpad_up.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b2hwfvnmkh2vr" +uid="uid://jbjm4a7nes2" path="res://.godot/imported/wii_dpad_up.svg-3b748550a5075659ce4a751d2324cdff.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_up_outline.svg.import b/assets/images/gamepad/wii/wii_dpad_up_outline.svg.import index 1a3766d76..883c13e9d 100644 --- a/assets/images/gamepad/wii/wii_dpad_up_outline.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dwtlhkc36j2a5" +uid="uid://bb7saw75xx2ko" path="res://.godot/imported/wii_dpad_up_outline.svg-07668bdc84f56707f73eeb2a3f0a6bd1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_vertical.svg.import b/assets/images/gamepad/wii/wii_dpad_vertical.svg.import index 917ef79ff..f9d3d7ef6 100644 --- a/assets/images/gamepad/wii/wii_dpad_vertical.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ccpjxb6n8il6p" +uid="uid://dgd68irvine1k" path="res://.godot/imported/wii_dpad_vertical.svg-ef00d582883ab72593a0cc97abbccce7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_dpad_vertical_outline.svg.import b/assets/images/gamepad/wii/wii_dpad_vertical_outline.svg.import index aa2dd3e43..1713f13b8 100644 --- a/assets/images/gamepad/wii/wii_dpad_vertical_outline.svg.import +++ b/assets/images/gamepad/wii/wii_dpad_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://f0sxwhs7v57e" +uid="uid://cnvpwsclbvmc" path="res://.godot/imported/wii_dpad_vertical_outline.svg-358ead95c3668b7d6eb3816d927ac548.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick.svg.import b/assets/images/gamepad/wii/wii_stick.svg.import index 82923c8c5..59e5f94da 100644 --- a/assets/images/gamepad/wii/wii_stick.svg.import +++ b/assets/images/gamepad/wii/wii_stick.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://lrvbqjy5628j" +uid="uid://cw0vbsxgb8tu4" path="res://.godot/imported/wii_stick.svg-656b9ab24d6bb7a53bd43e36664ed282.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_down.svg.import b/assets/images/gamepad/wii/wii_stick_down.svg.import index 51fead1ee..efa1217af 100644 --- a/assets/images/gamepad/wii/wii_stick_down.svg.import +++ b/assets/images/gamepad/wii/wii_stick_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cupr1886nqj3j" +uid="uid://8rr2m346sp5f" path="res://.godot/imported/wii_stick_down.svg-c42c14a0486d97ce39a3d05183a782a8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_horizontal.svg.import b/assets/images/gamepad/wii/wii_stick_horizontal.svg.import index a549324cb..1ff9c2da4 100644 --- a/assets/images/gamepad/wii/wii_stick_horizontal.svg.import +++ b/assets/images/gamepad/wii/wii_stick_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dkdy5fucs4arc" +uid="uid://dx3f113p2raoa" path="res://.godot/imported/wii_stick_horizontal.svg-d40e99f38848a37375b43a41a43cd302.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_l.svg.import b/assets/images/gamepad/wii/wii_stick_l.svg.import index 534f6382c..d3de29d44 100644 --- a/assets/images/gamepad/wii/wii_stick_l.svg.import +++ b/assets/images/gamepad/wii/wii_stick_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bely1hxf8ye66" +uid="uid://be2oop0pkkrfr" path="res://.godot/imported/wii_stick_l.svg-3c790a5a9cd8acfd3e5ff45bd400ff1a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_l_down.svg.import b/assets/images/gamepad/wii/wii_stick_l_down.svg.import index 57802c314..7fec52a94 100644 --- a/assets/images/gamepad/wii/wii_stick_l_down.svg.import +++ b/assets/images/gamepad/wii/wii_stick_l_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ckxfscck63s5f" +uid="uid://b62hrhqt3u6aq" path="res://.godot/imported/wii_stick_l_down.svg-cafd36e791319f35361d36245121246f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_l_horizontal.svg.import b/assets/images/gamepad/wii/wii_stick_l_horizontal.svg.import index 69bebd17d..e483d1e6c 100644 --- a/assets/images/gamepad/wii/wii_stick_l_horizontal.svg.import +++ b/assets/images/gamepad/wii/wii_stick_l_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://de40hwwgj4pyg" +uid="uid://bdnh7cietk6ry" path="res://.godot/imported/wii_stick_l_horizontal.svg-d216f3a521363660a7d6eb66bba8290a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_l_left.svg.import b/assets/images/gamepad/wii/wii_stick_l_left.svg.import index c063f5fa0..2bdf89529 100644 --- a/assets/images/gamepad/wii/wii_stick_l_left.svg.import +++ b/assets/images/gamepad/wii/wii_stick_l_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cys7i0bdjrj8o" +uid="uid://cayyt75fsa6bt" path="res://.godot/imported/wii_stick_l_left.svg-fe2b5a51394d14548875f9854d1794e7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_l_right.svg.import b/assets/images/gamepad/wii/wii_stick_l_right.svg.import index 4ab1b26e5..8de6792be 100644 --- a/assets/images/gamepad/wii/wii_stick_l_right.svg.import +++ b/assets/images/gamepad/wii/wii_stick_l_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d1wpua221rg0d" +uid="uid://c6nsxqqqhlnfl" path="res://.godot/imported/wii_stick_l_right.svg-9685ed83275ba4ddc47818057a37f5d5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_l_up.svg.import b/assets/images/gamepad/wii/wii_stick_l_up.svg.import index a803a092a..7ea9a4e99 100644 --- a/assets/images/gamepad/wii/wii_stick_l_up.svg.import +++ b/assets/images/gamepad/wii/wii_stick_l_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b83a1mvgp4e1d" +uid="uid://d1xtkprhbogn6" path="res://.godot/imported/wii_stick_l_up.svg-4259065159b4e4526b5189199d47c02a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_l_vertical.svg.import b/assets/images/gamepad/wii/wii_stick_l_vertical.svg.import index 6a49b376b..708847885 100644 --- a/assets/images/gamepad/wii/wii_stick_l_vertical.svg.import +++ b/assets/images/gamepad/wii/wii_stick_l_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d4j4xhngd5k6a" +uid="uid://cw7s3xgc0dlnm" path="res://.godot/imported/wii_stick_l_vertical.svg-b1635b9fea322026ac7d800a0ed3be03.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_left.svg.import b/assets/images/gamepad/wii/wii_stick_left.svg.import index e3ce64cf1..4d276090e 100644 --- a/assets/images/gamepad/wii/wii_stick_left.svg.import +++ b/assets/images/gamepad/wii/wii_stick_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cig74ev1kcwik" +uid="uid://byqki84xmkgdj" path="res://.godot/imported/wii_stick_left.svg-efde037995799ab72512631e42091c45.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_r.svg.import b/assets/images/gamepad/wii/wii_stick_r.svg.import index 7b24cb876..08953ec57 100644 --- a/assets/images/gamepad/wii/wii_stick_r.svg.import +++ b/assets/images/gamepad/wii/wii_stick_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://1ulk2ip48xbb" +uid="uid://b66t42cwr1l0a" path="res://.godot/imported/wii_stick_r.svg-d5630162a8c3340f85877784f95078a3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_r_down.svg.import b/assets/images/gamepad/wii/wii_stick_r_down.svg.import index 728201fff..d3cd7e3ec 100644 --- a/assets/images/gamepad/wii/wii_stick_r_down.svg.import +++ b/assets/images/gamepad/wii/wii_stick_r_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://kiiav02uwggo" +uid="uid://chinkv4vury2k" path="res://.godot/imported/wii_stick_r_down.svg-39269d81a6327ae5844820c7aa51b233.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_r_horizontal.svg.import b/assets/images/gamepad/wii/wii_stick_r_horizontal.svg.import index b1354391a..bbb1bd679 100644 --- a/assets/images/gamepad/wii/wii_stick_r_horizontal.svg.import +++ b/assets/images/gamepad/wii/wii_stick_r_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dyc0gl7h8rvos" +uid="uid://cacctfx7ycs38" path="res://.godot/imported/wii_stick_r_horizontal.svg-477399d6e9fe3a34a0530cfdfd4b79bb.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_r_left.svg.import b/assets/images/gamepad/wii/wii_stick_r_left.svg.import index ae2d08551..f1debd3f4 100644 --- a/assets/images/gamepad/wii/wii_stick_r_left.svg.import +++ b/assets/images/gamepad/wii/wii_stick_r_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://1wsl7nfb0mgq" +uid="uid://b6iw7hrjmsdjb" path="res://.godot/imported/wii_stick_r_left.svg-dbdfa0e18a4325ff7b2ca5036c510440.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_r_right.svg.import b/assets/images/gamepad/wii/wii_stick_r_right.svg.import index 67eb475ad..9229a25a2 100644 --- a/assets/images/gamepad/wii/wii_stick_r_right.svg.import +++ b/assets/images/gamepad/wii/wii_stick_r_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://da6rm2k5k3qw1" +uid="uid://0b7h08ywmk7k" path="res://.godot/imported/wii_stick_r_right.svg-a24d810dd88d67d60e0260cc7ec51c6e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_r_up.svg.import b/assets/images/gamepad/wii/wii_stick_r_up.svg.import index ebd0173d5..bcab70177 100644 --- a/assets/images/gamepad/wii/wii_stick_r_up.svg.import +++ b/assets/images/gamepad/wii/wii_stick_r_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://crtooonvlnwhy" +uid="uid://bmegwrr3mn70f" path="res://.godot/imported/wii_stick_r_up.svg-1b2c48383d6bf2b549de04181e4bc73c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_r_vertical.svg.import b/assets/images/gamepad/wii/wii_stick_r_vertical.svg.import index 32c1871e8..90323756c 100644 --- a/assets/images/gamepad/wii/wii_stick_r_vertical.svg.import +++ b/assets/images/gamepad/wii/wii_stick_r_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dw6waqj1aj5mn" +uid="uid://c6hnyqorwu4wf" path="res://.godot/imported/wii_stick_r_vertical.svg-f845e8f103906a7ce358b75d77f997f1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_right.svg.import b/assets/images/gamepad/wii/wii_stick_right.svg.import index a8541ec75..9faeb0c4a 100644 --- a/assets/images/gamepad/wii/wii_stick_right.svg.import +++ b/assets/images/gamepad/wii/wii_stick_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dbnrcg1768db8" +uid="uid://d1iutll6165h2" path="res://.godot/imported/wii_stick_right.svg-41870e98be3d50788fcb3da9a9c8265e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_top_l.svg.import b/assets/images/gamepad/wii/wii_stick_top_l.svg.import index 938367b23..2e6064a55 100644 --- a/assets/images/gamepad/wii/wii_stick_top_l.svg.import +++ b/assets/images/gamepad/wii/wii_stick_top_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dkk0t8j8m05e5" +uid="uid://bguehceyrfnhv" path="res://.godot/imported/wii_stick_top_l.svg-e83c6ce225e9f5013948bea59477d964.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_top_r.svg.import b/assets/images/gamepad/wii/wii_stick_top_r.svg.import index 9455bc4d5..b63e212bd 100644 --- a/assets/images/gamepad/wii/wii_stick_top_r.svg.import +++ b/assets/images/gamepad/wii/wii_stick_top_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ynvb08ar0rgk" +uid="uid://40xbsh4571vs" path="res://.godot/imported/wii_stick_top_r.svg-8e8678fe1959755506985023a50da2c9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_up.svg.import b/assets/images/gamepad/wii/wii_stick_up.svg.import index b748330f6..2d6325672 100644 --- a/assets/images/gamepad/wii/wii_stick_up.svg.import +++ b/assets/images/gamepad/wii/wii_stick_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c6yx2n3hyyb8r" +uid="uid://ct1ktqpeouk7a" path="res://.godot/imported/wii_stick_up.svg-b77b47134f8f313e593e96dca0389ec9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wii/wii_stick_vertical.svg.import b/assets/images/gamepad/wii/wii_stick_vertical.svg.import index e0d5bd6ef..5d087004b 100644 --- a/assets/images/gamepad/wii/wii_stick_vertical.svg.import +++ b/assets/images/gamepad/wii/wii_stick_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://gorv65b1ahqs" +uid="uid://ceobf3dmi8aq3" path="res://.godot/imported/wii_stick_vertical.svg-4ba3650bc28bf58edd614f042288dcd5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/controller_wiiu_pro.svg.import b/assets/images/gamepad/wiiu/controller_wiiu_pro.svg.import index d606a41d2..4bc243ab4 100644 --- a/assets/images/gamepad/wiiu/controller_wiiu_pro.svg.import +++ b/assets/images/gamepad/wiiu/controller_wiiu_pro.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://yf77an3r1cdn" +uid="uid://dfw226f6el2uf" path="res://.godot/imported/controller_wiiu_pro.svg-78a3268c1a861555f3d383c33852f853.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_1.svg.import b/assets/images/gamepad/wiiu/wiiu_button_1.svg.import index 0e836f56a..fdbe52cfb 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_1.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_1.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://df75x0thccllb" +uid="uid://dwgioia8g50jh" path="res://.godot/imported/wiiu_button_1.svg-5ec5865e13e326b30c4152a6fa4f224e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_1_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_1_outline.svg.import index 851d96e00..5a3973e20 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_1_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_1_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bbfnaw7hh8qe5" +uid="uid://c533xywvwcydb" path="res://.godot/imported/wiiu_button_1_outline.svg-c6b36e891d1ea47056bbf438e5c0dd7d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_2.svg.import b/assets/images/gamepad/wiiu/wiiu_button_2.svg.import index a47023a42..7150bdf68 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_2.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_2.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b8wnfacjshkhq" +uid="uid://dahlvx4nsc8g3" path="res://.godot/imported/wiiu_button_2.svg-151c311b19a1945370285562791b1984.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_2_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_2_outline.svg.import index 50482632f..95f0b43d9 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_2_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_2_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dywbnqhf77i2c" +uid="uid://cmt0t6egk47ag" path="res://.godot/imported/wiiu_button_2_outline.svg-9a4d605d99d0b95c9d607db1536e9c1c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_a.svg.import b/assets/images/gamepad/wiiu/wiiu_button_a.svg.import index 3cad62af3..64d4d507f 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_a.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_a.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dip43m6hyqkk1" +uid="uid://crtwqn7qlxgpx" path="res://.godot/imported/wiiu_button_a.svg-dc5253a0d9e9654dd4846000dacad345.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_a_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_a_outline.svg.import index 3cd021914..0de584cf0 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_a_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_a_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dgogoqks4w234" +uid="uid://dbaumq746yl48" path="res://.godot/imported/wiiu_button_a_outline.svg-19838785d2158a14ed7a777f19bca626.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_b.svg.import b/assets/images/gamepad/wiiu/wiiu_button_b.svg.import index ee706ec69..1cae2a4cd 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_b.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_b.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://gy5pspnwa2gd" +uid="uid://wm54l6h6moc6" path="res://.godot/imported/wiiu_button_b.svg-9899435b3db638d531c06d554c021e61.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_b_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_b_outline.svg.import index 84cf10862..7073180ea 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_b_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_b_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c3avmb3v5li84" +uid="uid://2w0q401cvioo" path="res://.godot/imported/wiiu_button_b_outline.svg-f69d6eee07fc94b811f4eb47672ee5dc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_home.svg.import b/assets/images/gamepad/wiiu/wiiu_button_home.svg.import index 47897b7cb..81b1df2e1 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_home.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_home.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvc4p2u5xulri" +uid="uid://byt2y2jf63dp8" path="res://.godot/imported/wiiu_button_home.svg-b0c37b499db55db18c20b214d31ba2db.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_home_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_home_outline.svg.import index d1ea0bda3..2326cdb9d 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_home_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_home_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bydix7rhocxnp" +uid="uid://b2hx0mp4v5oha" path="res://.godot/imported/wiiu_button_home_outline.svg-07cd6c192e2619f405adc431f06e53f8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_l.svg.import b/assets/images/gamepad/wiiu/wiiu_button_l.svg.import index 8c22dcbfe..2618a4d9f 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_l.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://k1i0rxhfmi1y" +uid="uid://dg5d67wp1j076" path="res://.godot/imported/wiiu_button_l.svg-e8ce66b52e084e3b3bbc26e3cd6861c9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_l_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_l_outline.svg.import index c777ec220..9f74b9d80 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_l_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_l_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bup8vd3ofol7b" +uid="uid://c1lu66fawak0o" path="res://.godot/imported/wiiu_button_l_outline.svg-819d08825063c8337b2f4328a087ab0e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_minus.svg.import b/assets/images/gamepad/wiiu/wiiu_button_minus.svg.import index 07e9f89bd..fa69c6cd2 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_minus.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_minus.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dwr4yd5ayosvx" +uid="uid://bju5kflh4sjr6" path="res://.godot/imported/wiiu_button_minus.svg-0f0bac401f0eab03018c71d6235538f4.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_minus_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_minus_outline.svg.import index b48f3ede3..2a97a3ba4 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_minus_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_minus_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c8h7gh5v56cyc" +uid="uid://cenndgiblg7q0" path="res://.godot/imported/wiiu_button_minus_outline.svg-ddb7b6291194709db2583c789a2d4cb3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_plus.svg.import b/assets/images/gamepad/wiiu/wiiu_button_plus.svg.import index 464ed064a..43012f0ff 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_plus.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_plus.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bc05a5u1fyu7x" +uid="uid://b03cesqenwbl4" path="res://.godot/imported/wiiu_button_plus.svg-75058bfa797da2dee7227c18fe8a79fd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_plus_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_plus_outline.svg.import index a25a7746d..fa308faf7 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_plus_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_plus_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://qdr1vhpp5y5l" +uid="uid://dwa4pfoqdw40y" path="res://.godot/imported/wiiu_button_plus_outline.svg-46db7f53b2292e789d188b0c92795bc6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_power.svg.import b/assets/images/gamepad/wiiu/wiiu_button_power.svg.import index 0628e1c8a..e2b5f5a02 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_power.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_power.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b5884480qhqd8" +uid="uid://cy203nju4qp7f" path="res://.godot/imported/wiiu_button_power.svg-1d981fb732728e6b380a8da9f103dea3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_power_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_power_outline.svg.import index edd313116..eb798873d 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_power_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_power_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dj1yx2icni1ny" +uid="uid://btsa7lj3qxp36" path="res://.godot/imported/wiiu_button_power_outline.svg-a82860715b59e45e458cb5fe71978a74.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_r.svg.import b/assets/images/gamepad/wiiu/wiiu_button_r.svg.import index e11e2fdb7..2106b749a 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_r.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d00jbvyklnc50" +uid="uid://bs2rn6xjkn5bt" path="res://.godot/imported/wiiu_button_r.svg-8b4f9aad7545f1a8d3a135f43d1d8d14.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_r_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_r_outline.svg.import index c4f7d8202..c14b8c333 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_r_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_r_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bngnbdyvit2tv" +uid="uid://qj3b6hpwikow" path="res://.godot/imported/wiiu_button_r_outline.svg-0330e27457a4a78e7486fd719a3e8af9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_tv.svg.import b/assets/images/gamepad/wiiu/wiiu_button_tv.svg.import index f2b5c28d5..6404d08a9 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_tv.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_tv.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dw1x1emip8y30" +uid="uid://csyjs5lwjwnu6" path="res://.godot/imported/wiiu_button_tv.svg-29cace85ff8a35e7157d406635e4ec2c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_tv_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_tv_outline.svg.import index 8d5f280c8..60fc66088 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_tv_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_tv_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://gobm4m0sn4wk" +uid="uid://c5pkpa6n7ytxu" path="res://.godot/imported/wiiu_button_tv_outline.svg-dcea5d7d9107df22e89ead6ee2df8278.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_x.svg.import b/assets/images/gamepad/wiiu/wiiu_button_x.svg.import index d3510718a..aa28f51ba 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_x.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_x.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b50n645eq3huj" +uid="uid://dnfqug1or5qp6" path="res://.godot/imported/wiiu_button_x.svg-008cc99e8c0da1904a23f90cd275b25e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_x_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_x_outline.svg.import index c4a0522e3..f62fbe6d9 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_x_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_x_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bsg32vurc33ed" +uid="uid://bg83erlbwmj3k" path="res://.godot/imported/wiiu_button_x_outline.svg-a3577f4d3c26b9cf3f41d34c0f1be3dc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_y.svg.import b/assets/images/gamepad/wiiu/wiiu_button_y.svg.import index 5e78ae459..2540f4ffc 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_y.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_y.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bjgq3bmrsh3fv" +uid="uid://bxvct1cyrogov" path="res://.godot/imported/wiiu_button_y.svg-e7a51e33c6a0a7354c8fefdb71227f78.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_y_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_y_outline.svg.import index 2bb0f39c0..41f5e75a6 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_y_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_y_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4c3nb05jv11d" +uid="uid://bvbei87ttm6v3" path="res://.godot/imported/wiiu_button_y_outline.svg-36d1935f7e2502c64a5220cbdf0f6409.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_zl.svg.import b/assets/images/gamepad/wiiu/wiiu_button_zl.svg.import index 57b841c4e..14e499841 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_zl.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_zl.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bgp0kk656kp3j" +uid="uid://bq82fclhupln8" path="res://.godot/imported/wiiu_button_zl.svg-979de8055e9bc26530754eb97e160445.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_zl_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_zl_outline.svg.import index 5d39f1f17..6f6ea4dc1 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_zl_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_zl_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://chxhfi1eipp5x" +uid="uid://rgbq886q84ot" path="res://.godot/imported/wiiu_button_zl_outline.svg-3d5742a6688b848781afc44f5009c5ba.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_zr.svg.import b/assets/images/gamepad/wiiu/wiiu_button_zr.svg.import index 6cf4b7b5e..d2cea66f6 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_zr.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_zr.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://1rj547t8hqmj" +uid="uid://dqh3lmvu4sm21" path="res://.godot/imported/wiiu_button_zr.svg-cdce4ca396b5ffe162f7a0a8821c963e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_button_zr_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_button_zr_outline.svg.import index 283659f82..522525281 100644 --- a/assets/images/gamepad/wiiu/wiiu_button_zr_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_button_zr_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cgee110krbkjt" +uid="uid://b03i71uoh443h" path="res://.godot/imported/wiiu_button_zr_outline.svg-23f6e76e9ca0be26447d8863f767fa49.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_controller.svg.import b/assets/images/gamepad/wiiu/wiiu_controller.svg.import index 4d5b006a1..75b49c3bb 100644 --- a/assets/images/gamepad/wiiu/wiiu_controller.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_controller.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://byfgrcpjy2wcx" +uid="uid://dy312mcan4x4s" path="res://.godot/imported/wiiu_controller.svg-eacb2e68cb1f394d26f127163c4d6f6d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_controller_down.svg.import b/assets/images/gamepad/wiiu/wiiu_controller_down.svg.import index f97518f9d..0e4a62958 100644 --- a/assets/images/gamepad/wiiu/wiiu_controller_down.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_controller_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bhw4cndgm6eqq" +uid="uid://cghi3e64bd6em" path="res://.godot/imported/wiiu_controller_down.svg-cd64f5fb2e3704377e43adfb6c66698a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_controller_down_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_controller_down_outline.svg.import index 966d1297a..b12dfb9c1 100644 --- a/assets/images/gamepad/wiiu/wiiu_controller_down_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_controller_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://co6333a2apljn" +uid="uid://dvn1n8yle23tn" path="res://.godot/imported/wiiu_controller_down_outline.svg-d395c2c337671334dd8a7a288e185114.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_controller_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_controller_outline.svg.import index 352d7503d..472aef601 100644 --- a/assets/images/gamepad/wiiu/wiiu_controller_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_controller_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dg5xmwgdfjo7l" +uid="uid://cdk3uo4ce5vhb" path="res://.godot/imported/wiiu_controller_outline.svg-79e0d672e143dcc612e81607777178a0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_controller_up.svg.import b/assets/images/gamepad/wiiu/wiiu_controller_up.svg.import index e42024c5f..fdb2106e6 100644 --- a/assets/images/gamepad/wiiu/wiiu_controller_up.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_controller_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bcqrkeomfttgw" +uid="uid://blgxhcsc3nbdy" path="res://.godot/imported/wiiu_controller_up.svg-c2bb69fe329d0b72088ebf118c9ced4b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_controller_up_outlnie.svg.import b/assets/images/gamepad/wiiu/wiiu_controller_up_outlnie.svg.import index c663a0740..0680eefaf 100644 --- a/assets/images/gamepad/wiiu/wiiu_controller_up_outlnie.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_controller_up_outlnie.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bmkcpgjywd4gc" +uid="uid://drtd8ma0w7apy" path="res://.godot/imported/wiiu_controller_up_outlnie.svg-b3486e1be69af6b8188962dfd855dd6d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad.svg.import index 2dc8a5bba..192729116 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bcuj12tfwvvy3" +uid="uid://d4k2nl1j2h2pa" path="res://.godot/imported/wiiu_dpad.svg-a3fbd5c1c765f029dc5059aaf0572066.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_all.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_all.svg.import index 8d4525789..5b7b89cab 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_all.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://beu1j3jwadr22" +uid="uid://uk3y1pp2bq15" path="res://.godot/imported/wiiu_dpad_all.svg-57c5314bedc7962d643febfd9ff66c24.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_down.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_down.svg.import index b6ea3db02..7225648c5 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_down.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bkb50jtr4pxba" +uid="uid://d0lb1vuu1bd0m" path="res://.godot/imported/wiiu_dpad_down.svg-567c9eb58e77d044c09d43276a5dc816.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_down_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_down_outline.svg.import index 60f6f23b9..b7f85adb9 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_down_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ch1px7uro83y2" +uid="uid://bkn5fhd1iv45c" path="res://.godot/imported/wiiu_dpad_down_outline.svg-21e1e1e4bf7fa446c4fb8159673378d8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_horizontal.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_horizontal.svg.import index 348012f1e..59d002f3b 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_horizontal.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c0dc3u6pc5w55" +uid="uid://codxov8eqeayr" path="res://.godot/imported/wiiu_dpad_horizontal.svg-4d50cfd610469c38d5a15db1d705f4f6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_horizontal_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_horizontal_outline.svg.import index f5b4b8f3b..4d45273ad 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_horizontal_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cepsbve4qaklr" +uid="uid://cr6u4jphttwcy" path="res://.godot/imported/wiiu_dpad_horizontal_outline.svg-20791e2ca03ac76ceb2ff2afc0967c02.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_left.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_left.svg.import index 7018cedd1..ce09015d1 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_left.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bvh6rsmubn4d4" +uid="uid://bku4fkvbmd68y" path="res://.godot/imported/wiiu_dpad_left.svg-e6cb13df4f6ca61bd7250b45837d3bdc.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_left_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_left_outline.svg.import index 14aab6a29..8a77b9848 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_left_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://s4vb187yjudt" +uid="uid://c0iorpod5q0gl" path="res://.godot/imported/wiiu_dpad_left_outline.svg-a2146bcf2c202c767046bb70f7ea8f22.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_none.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_none.svg.import index 043d19e0f..d0a695966 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_none.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_none.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bgvhkex7wco03" +uid="uid://dp6jrl45siaku" path="res://.godot/imported/wiiu_dpad_none.svg-394322d348eae7ff5684e1f7240a295c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_right.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_right.svg.import index d49ea66ab..10a171fe9 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_right.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://djy25p3v12033" +uid="uid://d0iplpc4ni6dc" path="res://.godot/imported/wiiu_dpad_right.svg-1ff4846823a94d7624aaa35c0773b229.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_right_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_right_outline.svg.import index 13b8784bc..6ddafe32c 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_right_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://mg0kayawmdne" +uid="uid://ctco0tv7h6bec" path="res://.godot/imported/wiiu_dpad_right_outline.svg-94d66b73b22550e8d043d4e5f5b3cf97.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_up.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_up.svg.import index 0c45e1bb2..12aae8e88 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_up.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cp5ovhb3qrm0m" +uid="uid://bg8ecant07gfr" path="res://.godot/imported/wiiu_dpad_up.svg-a0f3ef05a04dec5123fb95133769d2e0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_up_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_up_outline.svg.import index 32687407c..122399341 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_up_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://be4ccsanl6w5r" +uid="uid://cxdkv8wmor1px" path="res://.godot/imported/wiiu_dpad_up_outline.svg-9ecc2cc2fdc6f18f599fc1711aed5e9d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_vertical.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_vertical.svg.import index 364326bcb..862e63bf7 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_vertical.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://sf2eaq4ddbth" +uid="uid://dnoson8ys2rb2" path="res://.godot/imported/wiiu_dpad_vertical.svg-f011156bbde3833344cb012660e44b23.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_dpad_vertical_outline.svg.import b/assets/images/gamepad/wiiu/wiiu_dpad_vertical_outline.svg.import index 5e1f2b621..7d9ec329f 100644 --- a/assets/images/gamepad/wiiu/wiiu_dpad_vertical_outline.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_dpad_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://uokqhslfubmg" +uid="uid://d2d05nbwg4p37" path="res://.godot/imported/wiiu_dpad_vertical_outline.svg-2b5c2149ee0b3094a0ea111ef06216a3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_l.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_l.svg.import index 2936f9229..e157dae94 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_l.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://e8i108stbu7v" +uid="uid://cjcrdudsu7dsx" path="res://.godot/imported/wiiu_stick_l.svg-04f1f9da4dcfed3968cda6ec1d4bcb0a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_l_down.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_l_down.svg.import index 53bcb5ca5..8a17aa3cf 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_l_down.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_l_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c0bfex67ty7e3" +uid="uid://cjq5x0mhdt8mh" path="res://.godot/imported/wiiu_stick_l_down.svg-7bcfd8625b9d89d87d0df0564cc0e29c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_l_horizontal.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_l_horizontal.svg.import index 0d8eb13ec..f7ef8f98f 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_l_horizontal.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_l_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bnnbr484ph6gi" +uid="uid://cfs0l1cpsaur5" path="res://.godot/imported/wiiu_stick_l_horizontal.svg-01e0d7483d6df2559bc91c913836b20f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_l_left.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_l_left.svg.import index 4e04bb94a..675d6ac00 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_l_left.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_l_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dcl6aqrxeh2or" +uid="uid://bjj75d7d2m3th" path="res://.godot/imported/wiiu_stick_l_left.svg-9745e48ab58ba201e4fcbdf63a62de42.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_l_right.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_l_right.svg.import index 784960a7b..f84043777 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_l_right.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_l_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bcc3s7yr1taaf" +uid="uid://cg5na4cj11g8i" path="res://.godot/imported/wiiu_stick_l_right.svg-6bc93a0e21fc7529a08da0df4f5a0915.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_l_up.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_l_up.svg.import index c19f869ab..3379d7c01 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_l_up.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_l_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cj53jm83ttpey" +uid="uid://bx1ejne0hf32e" path="res://.godot/imported/wiiu_stick_l_up.svg-02e3c39280acd6d39f036d3752c3422c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_l_vertical.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_l_vertical.svg.import index 4d73a5c44..b2a732a48 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_l_vertical.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_l_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cmcjco6h3mly5" +uid="uid://11h1m6jx4bhy" path="res://.godot/imported/wiiu_stick_l_vertical.svg-55e56d9cf079f40f0425c9c06b9fe3b1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_r.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_r.svg.import index 70972d874..cbdc8be25 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_r.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d3sxs8lvy2jpi" +uid="uid://ds66flubaekdw" path="res://.godot/imported/wiiu_stick_r.svg-d9deb19522a756574e2e4dadf04c6e34.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_r_down.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_r_down.svg.import index 777a08cea..5ae788e7d 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_r_down.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_r_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c1lyv4u6w7jhj" +uid="uid://b6d2emjvdsgjd" path="res://.godot/imported/wiiu_stick_r_down.svg-b1f01bd0fe0b941dadc4325e05513ad7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_r_horizontal.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_r_horizontal.svg.import index 0e7797025..eed3fe1ab 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_r_horizontal.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_r_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c1pynyuwsswh4" +uid="uid://cwmi2kaapq55b" path="res://.godot/imported/wiiu_stick_r_horizontal.svg-9bfda2ab53bbb34acfbc0ede27275483.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_r_left.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_r_left.svg.import index 347289321..773ac676c 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_r_left.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_r_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ddvuo0uqv2wo2" +uid="uid://tjp5ctarwubn" path="res://.godot/imported/wiiu_stick_r_left.svg-2b214a8bce9746d60ba7dfaaad59b90b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_r_right.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_r_right.svg.import index 2c6ac2aeb..2576b689d 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_r_right.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_r_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cpx4vmnilefas" +uid="uid://btsmc4eytrxe3" path="res://.godot/imported/wiiu_stick_r_right.svg-d7c14941ef1c3293766688dddbc380a2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_r_up.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_r_up.svg.import index a6d95b045..aa8e5933c 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_r_up.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_r_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://chgkwr206vyqu" +uid="uid://bsymx5elr5qxt" path="res://.godot/imported/wiiu_stick_r_up.svg-d48f29d5870c1143f41d3b5f78e3344a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_r_vertical.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_r_vertical.svg.import index ce578f277..b893fc3b6 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_r_vertical.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_r_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://djm337j48c7yy" +uid="uid://dttvnhmt43h3p" path="res://.godot/imported/wiiu_stick_r_vertical.svg-1ca5cdabbc3aaace31cd54c68c4c98d0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_top_l.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_top_l.svg.import index 5ffd0c4e8..efe5345c7 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_top_l.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_top_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cuv4fji0r5ggp" +uid="uid://byto6orxyr3ug" path="res://.godot/imported/wiiu_stick_top_l.svg-12177c0858355d1aa8c9638f8eaa4268.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/wiiu/wiiu_stick_top_r.svg.import b/assets/images/gamepad/wiiu/wiiu_stick_top_r.svg.import index 7085f5fd6..699ca803f 100644 --- a/assets/images/gamepad/wiiu/wiiu_stick_top_r.svg.import +++ b/assets/images/gamepad/wiiu/wiiu_stick_top_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c0wk5l1n6ddam" +uid="uid://sxqlbjkpontl" path="res://.godot/imported/wiiu_stick_top_r.svg-12cb9a95a85c9e5e02df52fd754f89ce.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/win4/diagram.png.import b/assets/images/gamepad/win4/diagram.png.import index 7039b8953..56d420af0 100644 --- a/assets/images/gamepad/win4/diagram.png.import +++ b/assets/images/gamepad/win4/diagram.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b57nsqeawh8pm" +uid="uid://ct7bo3dulbx0o" path="res://.godot/imported/diagram.png-03c26c29ce498ea10cb8055b51000e4c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/XboxOne_Diagram.png.import b/assets/images/gamepad/xbox/XboxOne_Diagram.png.import index 42b746a43..788c64067 100644 --- a/assets/images/gamepad/xbox/XboxOne_Diagram.png.import +++ b/assets/images/gamepad/xbox/XboxOne_Diagram.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bie2sf8kjawr" +uid="uid://ceyt88pt5sogo" path="res://.godot/imported/XboxOne_Diagram.png-528d1d386d471d3f0a4e3d004b209880.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/XboxOne_Diagram_Simple.png.import b/assets/images/gamepad/xbox/XboxOne_Diagram_Simple.png.import index 2e8470cc0..1caa94b69 100644 --- a/assets/images/gamepad/xbox/XboxOne_Diagram_Simple.png.import +++ b/assets/images/gamepad/xbox/XboxOne_Diagram_Simple.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d0jq2ge68564v" +uid="uid://cibfy4ssigmqm" path="res://.godot/imported/XboxOne_Diagram_Simple.png-ee0e86525f057d115010c33ed9921fa5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/controller_xbox360.svg.import b/assets/images/gamepad/xbox/controller_xbox360.svg.import index d4cc2ea1f..c25a1421e 100644 --- a/assets/images/gamepad/xbox/controller_xbox360.svg.import +++ b/assets/images/gamepad/xbox/controller_xbox360.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://rmvytw8m2h2r" +uid="uid://cf05gacxjxdi" path="res://.godot/imported/controller_xbox360.svg-e770ea87e1e53f27f102b9afb4da2f88.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/controller_xbox_adaptive.svg.import b/assets/images/gamepad/xbox/controller_xbox_adaptive.svg.import index b15e2f706..ed7a702e1 100644 --- a/assets/images/gamepad/xbox/controller_xbox_adaptive.svg.import +++ b/assets/images/gamepad/xbox/controller_xbox_adaptive.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cjou21a3nlwl7" +uid="uid://dt3l6lhk0mdr3" path="res://.godot/imported/controller_xbox_adaptive.svg-777c94e728519a7d798e0a7a896f11e2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/controller_xboxone.svg.import b/assets/images/gamepad/xbox/controller_xboxone.svg.import index 043a661b9..f08c01c6a 100644 --- a/assets/images/gamepad/xbox/controller_xboxone.svg.import +++ b/assets/images/gamepad/xbox/controller_xboxone.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bmlowbm4srsyr" +uid="uid://eijkgk4rk31l" path="res://.godot/imported/controller_xboxone.svg-474b0e15e22b5b156866a964b9b3221d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/controller_xboxseries.svg.import b/assets/images/gamepad/xbox/controller_xboxseries.svg.import index d0a6dbcd3..df9e58059 100644 --- a/assets/images/gamepad/xbox/controller_xboxseries.svg.import +++ b/assets/images/gamepad/xbox/controller_xboxseries.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c16tv621e0ft3" +uid="uid://o5yjthrf58su" path="res://.godot/imported/controller_xboxseries.svg-afdcdb96b3c4ece83adb6b5ba0efc1ca.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_a.svg.import b/assets/images/gamepad/xbox/xbox_button_a.svg.import index 7932d83c0..bbc8a910c 100644 --- a/assets/images/gamepad/xbox/xbox_button_a.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_a.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bdkrtl0k6qpie" +uid="uid://cl4d11t0kv11a" path="res://.godot/imported/xbox_button_a.svg-bb80ea3db87776ddc205fc0ce310ca2c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_a_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_a_outline.svg.import index 55fd3467c..bf900112a 100644 --- a/assets/images/gamepad/xbox/xbox_button_a_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_a_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bb0glmi2lyf1b" +uid="uid://daqukh8da0skq" path="res://.godot/imported/xbox_button_a_outline.svg-5c72c6c8bcb7bfe8106ed236329efc04.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_b.svg.import b/assets/images/gamepad/xbox/xbox_button_b.svg.import index 1eab330e5..a1a9ddc37 100644 --- a/assets/images/gamepad/xbox/xbox_button_b.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_b.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4fxi5irku3fp" +uid="uid://dcucdnmw0q0ov" path="res://.godot/imported/xbox_button_b.svg-c37f9ec87569772305819768f8b366c2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_b_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_b_outline.svg.import index 8fa9f5a6b..086e213fa 100644 --- a/assets/images/gamepad/xbox/xbox_button_b_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_b_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dq86lnsd771mx" +uid="uid://b1rmsrpeo4uh" path="res://.godot/imported/xbox_button_b_outline.svg-da48aefccd1b0e6ae5d9c0b00ad0c9a7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_back.svg.import b/assets/images/gamepad/xbox/xbox_button_back.svg.import index 49be7a6a2..16c551c4a 100644 --- a/assets/images/gamepad/xbox/xbox_button_back.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_back.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://wgefpgg5qcn4" +uid="uid://c6tj74yf7gsxb" path="res://.godot/imported/xbox_button_back.svg-63ec1ebb30674cb42d369ce45e17eea3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_back_icon.svg.import b/assets/images/gamepad/xbox/xbox_button_back_icon.svg.import index 62e0297a9..023e48c48 100644 --- a/assets/images/gamepad/xbox/xbox_button_back_icon.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_back_icon.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://worovimrqapy" +uid="uid://cwxfxkby7fk2a" path="res://.godot/imported/xbox_button_back_icon.svg-84cd4236d7f672c6a4c3ca81fd60e331.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_back_icon_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_back_icon_outline.svg.import index d9c5e9be7..fed8d5697 100644 --- a/assets/images/gamepad/xbox/xbox_button_back_icon_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_back_icon_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bl062vt1l8m5t" +uid="uid://bhrpqano2lehn" path="res://.godot/imported/xbox_button_back_icon_outline.svg-7f0353c398f5d24a4610a4e6df396df1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_back_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_back_outline.svg.import index 162ad080d..956c10555 100644 --- a/assets/images/gamepad/xbox/xbox_button_back_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_back_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://brjy7qkptl404" +uid="uid://ctpq56o4hvsd4" path="res://.godot/imported/xbox_button_back_outline.svg-761f914c3a1066c09c04d9250585cf26.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_color_a.svg.import b/assets/images/gamepad/xbox/xbox_button_color_a.svg.import index 9b2b45a6c..85c4ed4a1 100644 --- a/assets/images/gamepad/xbox/xbox_button_color_a.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_color_a.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dyemqkvdtk43e" +uid="uid://cxvxwgt1vox0" path="res://.godot/imported/xbox_button_color_a.svg-2f8eba8e6261ffeaa861e615a8c5091a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_color_a_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_color_a_outline.svg.import index 18bf47a82..981f26267 100644 --- a/assets/images/gamepad/xbox/xbox_button_color_a_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_color_a_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dbdplieoh63mw" +uid="uid://bp11mma26v2gb" path="res://.godot/imported/xbox_button_color_a_outline.svg-f60c5338bf5d411669a95b9a9d9e94af.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_color_b.svg.import b/assets/images/gamepad/xbox/xbox_button_color_b.svg.import index 3980eb967..c02956317 100644 --- a/assets/images/gamepad/xbox/xbox_button_color_b.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_color_b.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bqho0a0sr6u1j" +uid="uid://dfby3iiksln2e" path="res://.godot/imported/xbox_button_color_b.svg-12f75c505369b364244635a0d0ac0fcf.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_color_b_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_color_b_outline.svg.import index 26e4d6305..5395e3332 100644 --- a/assets/images/gamepad/xbox/xbox_button_color_b_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_color_b_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://jbqugaw65tjn" +uid="uid://c66tk68snyvkp" path="res://.godot/imported/xbox_button_color_b_outline.svg-bddbdac1738aa51d70c7b074d1232969.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_color_x.svg.import b/assets/images/gamepad/xbox/xbox_button_color_x.svg.import index 9a2340c75..14bed7691 100644 --- a/assets/images/gamepad/xbox/xbox_button_color_x.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_color_x.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cuvx61nakiaoi" +uid="uid://ojrh7msglai0" path="res://.godot/imported/xbox_button_color_x.svg-68cb4450be80b17c17fc0274bcac944e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_color_x_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_color_x_outline.svg.import index 15f06205f..098472d67 100644 --- a/assets/images/gamepad/xbox/xbox_button_color_x_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_color_x_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://domfr16n5e0eq" +uid="uid://cn7857u3ah7nc" path="res://.godot/imported/xbox_button_color_x_outline.svg-3a762e20b5ad4d2077b6bf65d70eaf09.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_color_y.svg.import b/assets/images/gamepad/xbox/xbox_button_color_y.svg.import index 58a415521..e2d4c0223 100644 --- a/assets/images/gamepad/xbox/xbox_button_color_y.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_color_y.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ddpudi00yulrx" +uid="uid://b81pjmldtt10s" path="res://.godot/imported/xbox_button_color_y.svg-1d248820d20c6ef050937701e498556f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_color_y_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_color_y_outline.svg.import index b17615089..38f67255b 100644 --- a/assets/images/gamepad/xbox/xbox_button_color_y_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_color_y_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cn3280t4gjd8l" +uid="uid://c2tdumulmgljk" path="res://.godot/imported/xbox_button_color_y_outline.svg-a019af67b765f1d1eb8e74c35d8adbfd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_menu.svg.import b/assets/images/gamepad/xbox/xbox_button_menu.svg.import index 715d55ccd..2ac398eff 100644 --- a/assets/images/gamepad/xbox/xbox_button_menu.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_menu.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dagfma4wnmvw8" +uid="uid://ckk2my0j71i3r" path="res://.godot/imported/xbox_button_menu.svg-b58c6fabc452de1668514256375ee4bd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_menu_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_menu_outline.svg.import index bf40510fe..216a6ecad 100644 --- a/assets/images/gamepad/xbox/xbox_button_menu_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_menu_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dnj5fsxv46yqb" +uid="uid://c7u3y2kb6qcr3" path="res://.godot/imported/xbox_button_menu_outline.svg-7efbfa23f42cbcb6fed792ad892cd118.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_share.svg.import b/assets/images/gamepad/xbox/xbox_button_share.svg.import index 17fd221bf..3ecefe258 100644 --- a/assets/images/gamepad/xbox/xbox_button_share.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_share.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dasqdy0fqn33r" +uid="uid://crda44th28ovn" path="res://.godot/imported/xbox_button_share.svg-dce1bf9ccda415346ad3deb379372594.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_share_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_share_outline.svg.import index 17171d1c6..332d3431e 100644 --- a/assets/images/gamepad/xbox/xbox_button_share_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_share_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4tqkf40tdq5f" +uid="uid://ctea5mghu44vu" path="res://.godot/imported/xbox_button_share_outline.svg-a784f1f956b9c0dae069a6bb6e0be3c8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_start.svg.import b/assets/images/gamepad/xbox/xbox_button_start.svg.import index 117491713..530fb5f79 100644 --- a/assets/images/gamepad/xbox/xbox_button_start.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_start.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dvpdtmjv4mjhh" +uid="uid://dm4aqhsm8bo7m" path="res://.godot/imported/xbox_button_start.svg-675d1c3d2310a53b97db1fa351891baa.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_start_icon.svg.import b/assets/images/gamepad/xbox/xbox_button_start_icon.svg.import index 22122db30..144a05c3d 100644 --- a/assets/images/gamepad/xbox/xbox_button_start_icon.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_start_icon.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://lyo4d0hfj4ut" +uid="uid://c3v2ch2hw3mhm" path="res://.godot/imported/xbox_button_start_icon.svg-c60ef4d394b5cc1e496eaf88984b8c91.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_start_icon_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_start_icon_outline.svg.import index b5f27bab9..3757f659e 100644 --- a/assets/images/gamepad/xbox/xbox_button_start_icon_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_start_icon_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://clgtdnaufqi7" +uid="uid://o4ylq5slkt7y" path="res://.godot/imported/xbox_button_start_icon_outline.svg-75e89531003513fa0a4a35cfa5d8746a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_start_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_start_outline.svg.import index 855b800d0..31eea8bf5 100644 --- a/assets/images/gamepad/xbox/xbox_button_start_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_start_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bc3yg3ljfduac" +uid="uid://de8qmlbhec0ux" path="res://.godot/imported/xbox_button_start_outline.svg-a105c21baddb3824554b1aa27f9dab78.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_view.svg.import b/assets/images/gamepad/xbox/xbox_button_view.svg.import index f2ce31f3a..8e1564b29 100644 --- a/assets/images/gamepad/xbox/xbox_button_view.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_view.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ce732ysw0pjo1" +uid="uid://cws272by4n8r7" path="res://.godot/imported/xbox_button_view.svg-457e98b18fe3550217eaa8b628bb7a19.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_view_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_view_outline.svg.import index c4c0d217d..d84bf245b 100644 --- a/assets/images/gamepad/xbox/xbox_button_view_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_view_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://n1ijurfdighb" +uid="uid://ciwwbm2xvghg3" path="res://.godot/imported/xbox_button_view_outline.svg-e18c011fabdce4434bd073f3366b662a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_x.svg.import b/assets/images/gamepad/xbox/xbox_button_x.svg.import index 102ac9084..5973a3f04 100644 --- a/assets/images/gamepad/xbox/xbox_button_x.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_x.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://6gch2u81rmff" +uid="uid://cjnbox2d87ta7" path="res://.godot/imported/xbox_button_x.svg-1eccbfafb5da4b6b7b000b062c9bd219.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_x_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_x_outline.svg.import index 7cd5679c9..87b962f87 100644 --- a/assets/images/gamepad/xbox/xbox_button_x_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_x_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dkyx3l8xmwnhh" +uid="uid://cwhgcx6vbtqe4" path="res://.godot/imported/xbox_button_x_outline.svg-20f267c3c0398b229f2a37aa4445e09c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_y.svg.import b/assets/images/gamepad/xbox/xbox_button_y.svg.import index 24b07d47e..aae7679e5 100644 --- a/assets/images/gamepad/xbox/xbox_button_y.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_y.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bn4hgmp4f7gr4" +uid="uid://0jg4cw548nty" path="res://.godot/imported/xbox_button_y.svg-7afd378b26ca7dff1a09678d75befd28.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_button_y_outline.svg.import b/assets/images/gamepad/xbox/xbox_button_y_outline.svg.import index 2771d01c1..1e4501131 100644 --- a/assets/images/gamepad/xbox/xbox_button_y_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_button_y_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dta0d363mjp56" +uid="uid://vf04qhyffom3" path="res://.godot/imported/xbox_button_y_outline.svg-db834979656473a43c55bffd35c230a3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad.svg.import b/assets/images/gamepad/xbox/xbox_dpad.svg.import index dc84f2979..ff122f393 100644 --- a/assets/images/gamepad/xbox/xbox_dpad.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cdhd2pnbunvy3" +uid="uid://di87v7xnlu4hd" path="res://.godot/imported/xbox_dpad.svg-ea138d2676c93dd668df2f4dd175692c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_all.svg.import b/assets/images/gamepad/xbox/xbox_dpad_all.svg.import index 9907b7b9f..2c6a14449 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_all.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d33ypa2inoshf" +uid="uid://52414c1rvldf" path="res://.godot/imported/xbox_dpad_all.svg-169a5712da050446105241c1235e6010.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_down.svg.import b/assets/images/gamepad/xbox/xbox_dpad_down.svg.import index 5080d3db8..4d87dce3c 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_down.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bju6gytlq2div" +uid="uid://brqqse0hid6ki" path="res://.godot/imported/xbox_dpad_down.svg-67715dcb6a010f6eddc14e0a5d9bdbdb.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_down_outline.svg.import b/assets/images/gamepad/xbox/xbox_dpad_down_outline.svg.import index aaae7f338..0fea2617d 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_down_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_down_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cy42kh58lpan4" +uid="uid://ct47mctn1skhr" path="res://.godot/imported/xbox_dpad_down_outline.svg-3768dd03bbda427ffbedd7ef6bebfa80.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_horizontal.svg.import b/assets/images/gamepad/xbox/xbox_dpad_horizontal.svg.import index bdb1d66bf..9519384cd 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_horizontal.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://homu12tujxbw" +uid="uid://wb6p8cij2tr5" path="res://.godot/imported/xbox_dpad_horizontal.svg-0798a6e371de7a7e91ba63014c25807c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_horizontal_outline.svg.import b/assets/images/gamepad/xbox/xbox_dpad_horizontal_outline.svg.import index ff627f2a1..dc44dab89 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_horizontal_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_horizontal_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://blpl5n5beus6c" +uid="uid://cisy02l5y4n7a" path="res://.godot/imported/xbox_dpad_horizontal_outline.svg-8afa7c864944d4c94a30e44ab371bce3.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_left.svg.import b/assets/images/gamepad/xbox/xbox_dpad_left.svg.import index 701f55819..46bbb3e2f 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_left.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://candjw8elkekp" +uid="uid://caqnkbu5b0u2q" path="res://.godot/imported/xbox_dpad_left.svg-de98007623838331d669244a5974e011.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_left_outline.svg.import b/assets/images/gamepad/xbox/xbox_dpad_left_outline.svg.import index 9420eb252..cf223d48b 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_left_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_left_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dtttrdlt8omtf" +uid="uid://cxve1m3gq2fws" path="res://.godot/imported/xbox_dpad_left_outline.svg-39483817ce2f6a7dd646f6426bfd9b87.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_none.svg.import b/assets/images/gamepad/xbox/xbox_dpad_none.svg.import index 919f00f5f..753908fd3 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_none.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_none.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cat1ov3iuseru" +uid="uid://c6fbdql365x7" path="res://.godot/imported/xbox_dpad_none.svg-448844b96111f5ead0db14d578a0a915.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_right.svg.import b/assets/images/gamepad/xbox/xbox_dpad_right.svg.import index 12bb0de23..d779485e1 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_right.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dcpsb03pvu0vb" +uid="uid://b5gektw3mgp1a" path="res://.godot/imported/xbox_dpad_right.svg-af4a893b5dc17ac86d681470cefb81e0.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_right_outline.svg.import b/assets/images/gamepad/xbox/xbox_dpad_right_outline.svg.import index 8fbe9f141..3478c445b 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_right_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_right_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dqmbqhg1oc661" +uid="uid://pkhdwfawpfcc" path="res://.godot/imported/xbox_dpad_right_outline.svg-7a8a7d1b9cda40379332c7096886ece5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_round.svg.import b/assets/images/gamepad/xbox/xbox_dpad_round.svg.import index c90ae74ed..65940f9d6 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_round.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_round.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dn8i3gpeijgu1" +uid="uid://n4t36xrl15ux" path="res://.godot/imported/xbox_dpad_round.svg-40c467bd7af68c5e122c6c75bec0135a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_round_all.svg.import b/assets/images/gamepad/xbox/xbox_dpad_round_all.svg.import index bfb57241f..264d07bcc 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_round_all.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_round_all.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bbj8ampn6hgct" +uid="uid://so0la23m3pb" path="res://.godot/imported/xbox_dpad_round_all.svg-1ed2bc5259b0b482d639b5f250f29d57.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_round_down.svg.import b/assets/images/gamepad/xbox/xbox_dpad_round_down.svg.import index 926a8d6c5..c8ad21468 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_round_down.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_round_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dwv3f88h7uaq7" +uid="uid://bxovdah6vnpsn" path="res://.godot/imported/xbox_dpad_round_down.svg-59a36379fca801af7f991132a1e8949b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_round_horizontal.svg.import b/assets/images/gamepad/xbox/xbox_dpad_round_horizontal.svg.import index 77e241248..13bc68f74 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_round_horizontal.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_round_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b6gqg2b6pcrby" +uid="uid://pdiueqpnssea" path="res://.godot/imported/xbox_dpad_round_horizontal.svg-4fde19342e5570eb0693b48a8017b211.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_round_left.svg.import b/assets/images/gamepad/xbox/xbox_dpad_round_left.svg.import index 460439dea..ab3cb173f 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_round_left.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_round_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://fmnwuu2ym0m8" +uid="uid://qhwj81sv67l4" path="res://.godot/imported/xbox_dpad_round_left.svg-502a7266adee947974e86d838ca520cb.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_round_right.svg.import b/assets/images/gamepad/xbox/xbox_dpad_round_right.svg.import index e570cad5f..bdc1abd2e 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_round_right.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_round_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cboy1enwvdeoy" +uid="uid://b1uo5130s6gfw" path="res://.godot/imported/xbox_dpad_round_right.svg-1114dc82cc3b1482e6e611adaab921c5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_round_up.svg.import b/assets/images/gamepad/xbox/xbox_dpad_round_up.svg.import index 2f94888c8..1510c3ce8 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_round_up.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_round_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ba64eulr61afh" +uid="uid://dqk82r4up56mh" path="res://.godot/imported/xbox_dpad_round_up.svg-41de4ad553b53f470c7a3fe7efaf902b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_round_vertical.svg.import b/assets/images/gamepad/xbox/xbox_dpad_round_vertical.svg.import index d363bbe77..432b4fd1f 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_round_vertical.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_round_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dmhky2baxwheu" +uid="uid://ccs5txc1px4a3" path="res://.godot/imported/xbox_dpad_round_vertical.svg-04a43b32d5655eabae549267fc17610d.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_up.svg.import b/assets/images/gamepad/xbox/xbox_dpad_up.svg.import index 9936038de..489c35f62 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_up.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c6w5exkfdkkrr" +uid="uid://cjwqn3wttist" path="res://.godot/imported/xbox_dpad_up.svg-5186fc3cc4f9679288a83d1d012cf73a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_up_outline.svg.import b/assets/images/gamepad/xbox/xbox_dpad_up_outline.svg.import index e36ca77c6..4a239a467 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_up_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_up_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bbo6fbusni1cc" +uid="uid://ch0kyfjpkcmtx" path="res://.godot/imported/xbox_dpad_up_outline.svg-ade502dd63188e5695c779711b2406b1.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_vertical.svg.import b/assets/images/gamepad/xbox/xbox_dpad_vertical.svg.import index 749b19c91..66b80589a 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_vertical.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bnndaco52g6gj" +uid="uid://ckswrlv6m834h" path="res://.godot/imported/xbox_dpad_vertical.svg-2bf59d2adeea0bf7ecbd4e9e187e5411.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_dpad_vertical_outline.svg.import b/assets/images/gamepad/xbox/xbox_dpad_vertical_outline.svg.import index c1dedcec3..09b7d3a7c 100644 --- a/assets/images/gamepad/xbox/xbox_dpad_vertical_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_dpad_vertical_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://10a2ky5156xj" +uid="uid://xsdkqw0waaax" path="res://.godot/imported/xbox_dpad_vertical_outline.svg-2afc52ca78de863b322db67e79d8f062.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_guide.svg.import b/assets/images/gamepad/xbox/xbox_guide.svg.import index 9153c52ad..35c192004 100644 --- a/assets/images/gamepad/xbox/xbox_guide.svg.import +++ b/assets/images/gamepad/xbox/xbox_guide.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bcoajyq8st62e" +uid="uid://dq7kibukggise" path="res://.godot/imported/xbox_guide.svg-7e127b7a22c3b066dc5699856d0c797f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_guide_outline.svg.import b/assets/images/gamepad/xbox/xbox_guide_outline.svg.import index 8eb54873c..382922ee4 100644 --- a/assets/images/gamepad/xbox/xbox_guide_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_guide_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://wpkboh1kyedm" +uid="uid://g73lou0pj88w" path="res://.godot/imported/xbox_guide_outline.svg-17e63c770bb3ddf95772a98313c857f7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_lb.svg.import b/assets/images/gamepad/xbox/xbox_lb.svg.import index 76b6d5e16..e445afd06 100644 --- a/assets/images/gamepad/xbox/xbox_lb.svg.import +++ b/assets/images/gamepad/xbox/xbox_lb.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://mtd8tfnbotgc" +uid="uid://cwyi3if752xh6" path="res://.godot/imported/xbox_lb.svg-398dff58e13f71ff0f7430230c58d7f2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_lb_outline.svg.import b/assets/images/gamepad/xbox/xbox_lb_outline.svg.import index 32e1665c9..a3c38d88f 100644 --- a/assets/images/gamepad/xbox/xbox_lb_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_lb_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c42xikfrkkr1v" +uid="uid://do34qfhrq5x41" path="res://.godot/imported/xbox_lb_outline.svg-2bfbaf6d828ff79113bd9e003e828e9a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_ls.svg.import b/assets/images/gamepad/xbox/xbox_ls.svg.import index 20e82a5ec..2abd31281 100644 --- a/assets/images/gamepad/xbox/xbox_ls.svg.import +++ b/assets/images/gamepad/xbox/xbox_ls.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://qs6heuf6tn1i" +uid="uid://dmybf5rjebq5j" path="res://.godot/imported/xbox_ls.svg-a14e134edf2ff040f151e9d80c01fcb2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_ls_outline.svg.import b/assets/images/gamepad/xbox/xbox_ls_outline.svg.import index 3ac2650a0..c10467f9e 100644 --- a/assets/images/gamepad/xbox/xbox_ls_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_ls_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bwcpk1ld2p5j5" +uid="uid://clhscg5hh4jat" path="res://.godot/imported/xbox_ls_outline.svg-b8a715b74389de55dadb1aa57df26ff8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_lt.svg.import b/assets/images/gamepad/xbox/xbox_lt.svg.import index 1d6a10c69..8b3993186 100644 --- a/assets/images/gamepad/xbox/xbox_lt.svg.import +++ b/assets/images/gamepad/xbox/xbox_lt.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://u3jonqoafyg6" +uid="uid://cg8r4ijx7641s" path="res://.godot/imported/xbox_lt.svg-d30dc6b47cdba60b77fe84b9e3041a43.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_lt_outline.svg.import b/assets/images/gamepad/xbox/xbox_lt_outline.svg.import index 5e1670178..df7444976 100644 --- a/assets/images/gamepad/xbox/xbox_lt_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_lt_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://66buwdk71388" +uid="uid://crs7kjqof8lgf" path="res://.godot/imported/xbox_lt_outline.svg-ed40a819eb7fc8a09708bd0424289916.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_rb.svg.import b/assets/images/gamepad/xbox/xbox_rb.svg.import index 2ce0f34d6..28772768b 100644 --- a/assets/images/gamepad/xbox/xbox_rb.svg.import +++ b/assets/images/gamepad/xbox/xbox_rb.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://co3kg331knfu7" +uid="uid://ceiru5s4y3ghy" path="res://.godot/imported/xbox_rb.svg-a88f851439045fbc24b8fdd22e99412b.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_rb_outline.svg.import b/assets/images/gamepad/xbox/xbox_rb_outline.svg.import index bac700efc..9466eedcc 100644 --- a/assets/images/gamepad/xbox/xbox_rb_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_rb_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bu5l7hyvthqer" +uid="uid://bwhh6fbprgryt" path="res://.godot/imported/xbox_rb_outline.svg-024fc9413f393d610b3e382983354e72.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_rs.svg.import b/assets/images/gamepad/xbox/xbox_rs.svg.import index a5147cf90..a930f3878 100644 --- a/assets/images/gamepad/xbox/xbox_rs.svg.import +++ b/assets/images/gamepad/xbox/xbox_rs.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bt1cwcrqvujvn" +uid="uid://bwr086x6ga5ay" path="res://.godot/imported/xbox_rs.svg-78ab1580fc2d20ab704d0879618ea576.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_rs_outline.svg.import b/assets/images/gamepad/xbox/xbox_rs_outline.svg.import index f8b88c226..c0c96c630 100644 --- a/assets/images/gamepad/xbox/xbox_rs_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_rs_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dpcxinh1uthrf" +uid="uid://bsh0ua7hu50uf" path="res://.godot/imported/xbox_rs_outline.svg-a903d4c4e479d433c86ca716b83ebbb2.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_rt.svg.import b/assets/images/gamepad/xbox/xbox_rt.svg.import index 4e4c6173c..0f4abcf65 100644 --- a/assets/images/gamepad/xbox/xbox_rt.svg.import +++ b/assets/images/gamepad/xbox/xbox_rt.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://da8ojjv5vttou" +uid="uid://brt5gm71iv3u4" path="res://.godot/imported/xbox_rt.svg-698d5c2013b4a3c8f0dcc4c446bd55f5.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_rt_outline.svg.import b/assets/images/gamepad/xbox/xbox_rt_outline.svg.import index 8f0394ad5..1a8da3f89 100644 --- a/assets/images/gamepad/xbox/xbox_rt_outline.svg.import +++ b/assets/images/gamepad/xbox/xbox_rt_outline.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cil1xigmkb71c" +uid="uid://carrmxn87xhtb" path="res://.godot/imported/xbox_rt_outline.svg-58c19f710c4a3c1ae25059e1943b2939.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_l.svg.import b/assets/images/gamepad/xbox/xbox_stick_l.svg.import index 01c3a1d97..ed1e52b76 100644 --- a/assets/images/gamepad/xbox/xbox_stick_l.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bdqfg27h5o836" +uid="uid://dqx7njocf1aok" path="res://.godot/imported/xbox_stick_l.svg-2c0ef8a05925eee7d7d471ba1671bf9e.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_l_down.svg.import b/assets/images/gamepad/xbox/xbox_stick_l_down.svg.import index c812305b9..6da04077c 100644 --- a/assets/images/gamepad/xbox/xbox_stick_l_down.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_l_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dcumk3oqkyy7g" +uid="uid://c1bfw2axbcg1q" path="res://.godot/imported/xbox_stick_l_down.svg-2871ec99e71cd38b3dbae0847ab9317a.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_l_horizontal.svg.import b/assets/images/gamepad/xbox/xbox_stick_l_horizontal.svg.import index 47b9749a8..88231ca6a 100644 --- a/assets/images/gamepad/xbox/xbox_stick_l_horizontal.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_l_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://drcwmg5hpjyo0" +uid="uid://xtwxoob18mw8" path="res://.godot/imported/xbox_stick_l_horizontal.svg-f85d175ed4d43011f3fefb5e5b29334f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_l_left.svg.import b/assets/images/gamepad/xbox/xbox_stick_l_left.svg.import index 4185bc00d..bf3255dc3 100644 --- a/assets/images/gamepad/xbox/xbox_stick_l_left.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_l_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dooej262t4g0x" +uid="uid://c4iwtbyehtps2" path="res://.godot/imported/xbox_stick_l_left.svg-670f38737f59de4a65d5eb0d47729b4c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_l_press.svg.import b/assets/images/gamepad/xbox/xbox_stick_l_press.svg.import index 300946edf..1d7f8ba5a 100644 --- a/assets/images/gamepad/xbox/xbox_stick_l_press.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_l_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dvv5grt45cows" +uid="uid://ckqaven4xmk16" path="res://.godot/imported/xbox_stick_l_press.svg-c76f558e3498373b82265363d91f1bc8.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_l_right.svg.import b/assets/images/gamepad/xbox/xbox_stick_l_right.svg.import index 23438f790..71ba56d9a 100644 --- a/assets/images/gamepad/xbox/xbox_stick_l_right.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_l_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://coltjh07der5" +uid="uid://d4bfok8606fmv" path="res://.godot/imported/xbox_stick_l_right.svg-9d5fb5ee9de7d1bf371d57141ac51d9f.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_l_up.svg.import b/assets/images/gamepad/xbox/xbox_stick_l_up.svg.import index f57a125c6..ad8db0016 100644 --- a/assets/images/gamepad/xbox/xbox_stick_l_up.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_l_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b3xxbnyo2h20l" +uid="uid://c7p0nhy4sh5wf" path="res://.godot/imported/xbox_stick_l_up.svg-b8052c88e206210677dbefa76e6f9716.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_l_vertical.svg.import b/assets/images/gamepad/xbox/xbox_stick_l_vertical.svg.import index 5079e36f0..6c91fcb44 100644 --- a/assets/images/gamepad/xbox/xbox_stick_l_vertical.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_l_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bs16gcmgm2ice" +uid="uid://5rtbypusev2a" path="res://.godot/imported/xbox_stick_l_vertical.svg-60bb43e09e4845355ebff48f8e99fca9.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_r.svg.import b/assets/images/gamepad/xbox/xbox_stick_r.svg.import index bdab31876..14fecf779 100644 --- a/assets/images/gamepad/xbox/xbox_stick_r.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dw15y6xiuthpe" +uid="uid://bjq76f76quyry" path="res://.godot/imported/xbox_stick_r.svg-b6b0a563aad875ae5baad0fef90e1b5c.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_r_down.svg.import b/assets/images/gamepad/xbox/xbox_stick_r_down.svg.import index d08318436..cbbff7e62 100644 --- a/assets/images/gamepad/xbox/xbox_stick_r_down.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_r_down.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://d4iq63eypbf2s" +uid="uid://cu3ptt3kea56h" path="res://.godot/imported/xbox_stick_r_down.svg-729218646a9ef8658f5fb344747dfcb6.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_r_horizontal.svg.import b/assets/images/gamepad/xbox/xbox_stick_r_horizontal.svg.import index e4563c035..06361aa5a 100644 --- a/assets/images/gamepad/xbox/xbox_stick_r_horizontal.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_r_horizontal.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://60v0iyqxwm65" +uid="uid://d4ihs7jvxb56h" path="res://.godot/imported/xbox_stick_r_horizontal.svg-d5c67dba97d5fd690e64fade1fc24c98.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_r_left.svg.import b/assets/images/gamepad/xbox/xbox_stick_r_left.svg.import index 70f3670d6..90c6d0f31 100644 --- a/assets/images/gamepad/xbox/xbox_stick_r_left.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_r_left.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bus2l2lmd844f" +uid="uid://s4x7rp50eub5" path="res://.godot/imported/xbox_stick_r_left.svg-75748f934fb270dcc712766fc9e05344.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_r_press.svg.import b/assets/images/gamepad/xbox/xbox_stick_r_press.svg.import index 4915364cb..ab6feeefb 100644 --- a/assets/images/gamepad/xbox/xbox_stick_r_press.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_r_press.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://de2kcqeakp3q0" +uid="uid://5q4tjwvf7b8a" path="res://.godot/imported/xbox_stick_r_press.svg-ddd86906998c0f8d808ffb0a28a37658.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_r_right.svg.import b/assets/images/gamepad/xbox/xbox_stick_r_right.svg.import index 9ce045918..709e0a843 100644 --- a/assets/images/gamepad/xbox/xbox_stick_r_right.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_r_right.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cokparjjmwy8c" +uid="uid://kfqb1dr0735v" path="res://.godot/imported/xbox_stick_r_right.svg-50e70773301ab1ce1d333d783cbffd23.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_r_up.svg.import b/assets/images/gamepad/xbox/xbox_stick_r_up.svg.import index 38d38ac32..7dbcce947 100644 --- a/assets/images/gamepad/xbox/xbox_stick_r_up.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_r_up.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://jn543ff1ak1c" +uid="uid://o0xrox1l5sae" path="res://.godot/imported/xbox_stick_r_up.svg-5e2522d5822c9d7bb7aa51e28602facf.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_r_vertical.svg.import b/assets/images/gamepad/xbox/xbox_stick_r_vertical.svg.import index bc89ef847..5f3ec986a 100644 --- a/assets/images/gamepad/xbox/xbox_stick_r_vertical.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_r_vertical.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cb87vvgyfr3cd" +uid="uid://b5xybuy7txmro" path="res://.godot/imported/xbox_stick_r_vertical.svg-1fc29f0194e1d1b5bd3ac55e4215f953.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_side_l.svg.import b/assets/images/gamepad/xbox/xbox_stick_side_l.svg.import index 8f6e48ba7..9fdbebe3d 100644 --- a/assets/images/gamepad/xbox/xbox_stick_side_l.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_side_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://pp4tpvdxcw06" +uid="uid://jyjr6jeest60" path="res://.godot/imported/xbox_stick_side_l.svg-bb0a65e7b78b9c1e45b7347398cb21ea.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_side_r.svg.import b/assets/images/gamepad/xbox/xbox_stick_side_r.svg.import index 61473ffcf..cf43855e8 100644 --- a/assets/images/gamepad/xbox/xbox_stick_side_r.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_side_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dpp5vgspiqd45" +uid="uid://ccgy64wgba2vg" path="res://.godot/imported/xbox_stick_side_r.svg-47000d6b9e86f11f14be4872c61921bd.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_top_l.svg.import b/assets/images/gamepad/xbox/xbox_stick_top_l.svg.import index 291b44ae1..941557da1 100644 --- a/assets/images/gamepad/xbox/xbox_stick_top_l.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_top_l.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b2r8gh8p7equd" +uid="uid://dqs8jabjytp1h" path="res://.godot/imported/xbox_stick_top_l.svg-116826ec220fb2927e609042115300c7.ctex" metadata={ "vram_texture": false diff --git a/assets/images/gamepad/xbox/xbox_stick_top_r.svg.import b/assets/images/gamepad/xbox/xbox_stick_top_r.svg.import index 3d4e32f29..d4c1d55ed 100644 --- a/assets/images/gamepad/xbox/xbox_stick_top_r.svg.import +++ b/assets/images/gamepad/xbox/xbox_stick_top_r.svg.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4h18p23lg15j" +uid="uid://dm552sfjigl8o" path="res://.godot/imported/xbox_stick_top_r.svg-aa85ffee187ac1c15ab213a993dde263.ctex" metadata={ "vram_texture": false diff --git a/assets/shaders/god_rays.gdshader b/assets/shaders/god_rays.gdshader new file mode 100644 index 000000000..7b35c9c4b --- /dev/null +++ b/assets/shaders/god_rays.gdshader @@ -0,0 +1,108 @@ +/* +Shader from Godot Shaders - the free shader library. +godotshaders.com/shader/god-rays + +Feel free to use, improve and change this shader according to your needs +and consider sharing the modified result on godotshaders.com. +*/ + +shader_type canvas_item; + +uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap; +uniform float angle = -0.3; +uniform float position = -0.2; +uniform float spread : hint_range(0.0, 1.0) = 0.5; +uniform float cutoff : hint_range(-1.0, 1.0) = 0.1; +uniform float falloff : hint_range(0.0, 1.0) = 0.2; +uniform float edge_fade : hint_range(0.0, 1.0) = 0.15; + +uniform float speed = 1.0; +uniform float ray1_density = 8.0; +uniform float ray2_density = 30.0; +uniform float ray2_intensity : hint_range(0.0, 1.0) = 0.3; + +uniform vec4 color : source_color = vec4(1.0, 0.9, 0.65, 0.8); + +uniform bool hdr = false; +uniform float seed = 5.0; + +// Random and noise functions from Book of Shader's chapter on Noise. +float random(vec2 _uv) { + return fract(sin(dot(_uv.xy, + vec2(12.9898, 78.233))) * + 43758.5453123); +} + +float noise (in vec2 uv) { + vec2 i = floor(uv); + vec2 f = fract(uv); + + // Four corners in 2D of a tile + float a = random(i); + float b = random(i + vec2(1.0, 0.0)); + float c = random(i + vec2(0.0, 1.0)); + float d = random(i + vec2(1.0, 1.0)); + + + // Smooth Interpolation + + // Cubic Hermine Curve. Same as SmoothStep() + vec2 u = f * f * (3.0-2.0 * f); + + // Mix 4 coorners percentages + return mix(a, b, u.x) + + (c - a)* u.y * (1.0 - u.x) + + (d - b) * u.x * u.y; +} + +mat2 rotate(float _angle){ + return mat2(vec2(cos(_angle), -sin(_angle)), + vec2(sin(_angle), cos(_angle))); +} + +vec4 screen(vec4 base, vec4 blend){ + return 1.0 - (1.0 - base) * (1.0 - blend); +} + +void fragment() +{ + + // Rotate, skew and move the UVs + vec2 transformed_uv = ( rotate(angle) * (UV - position) ) / ( (UV.y + spread) - (UV.y * spread) ); + + // Animate the ray according the the new transformed UVs + vec2 ray1 = vec2(transformed_uv.x * ray1_density + sin(TIME * 0.1 * speed) * (ray1_density * 0.2) + seed, 1.0); + vec2 ray2 = vec2(transformed_uv.x * ray2_density + sin(TIME * 0.2 * speed) * (ray1_density * 0.2) + seed, 1.0); + + // Cut off the ray's edges + float cut = step(cutoff, transformed_uv.x) * step(cutoff, 1.0 - transformed_uv.x); + ray1 *= cut; + ray2 *= cut; + + // Apply the noise pattern (i.e. create the rays) + float rays; + + if (hdr){ + // This is not really HDR, but check this to not clamp the two merged rays making + // their values go over 1.0. Can make for some nice effect + rays = noise(ray1) + (noise(ray2) * ray2_intensity); + } + else{ + rays = clamp(noise(ray1) + (noise(ray2) * ray2_intensity), 0., 1.); + } + + // Fade out edges + rays *= smoothstep(0.0, falloff, (1.0 - UV.y)); // Bottom + rays *= smoothstep(0.0 + cutoff, edge_fade + cutoff, transformed_uv.x); // Left + rays *= smoothstep(0.0 + cutoff, edge_fade + cutoff, 1.0 - transformed_uv.x); // Right + + // Color to the rays + vec3 shine = vec3(rays) * color.rgb; + + // Try different blending modes for a nicer effect. "Screen" is included in the code, + // but take a look at https://godotshaders.com/snippet/blending-modes/ for more. + // With "Screen" blend mode: + shine = screen(texture(SCREEN_TEXTURE, SCREEN_UV), vec4(color)).rgb; + + COLOR = vec4(shine, rays * color.a); +} \ No newline at end of file diff --git a/assets/shaders/highlight_shine.gdshader b/assets/shaders/highlight_shine.gdshader new file mode 100644 index 000000000..bb2922f1f --- /dev/null +++ b/assets/shaders/highlight_shine.gdshader @@ -0,0 +1,69 @@ +shader_type canvas_item; +render_mode blend_premul_alpha; + +uniform float Line_Smoothness : hint_range(0, 0.1) = 0.045; +uniform float Line_Width : hint_range(0, 0.2) = 0.09; +uniform float Brightness = 3.0; +uniform float Rotation_deg : hint_range(-90, 90) = 30; +uniform float Distortion : hint_range(1, 2) = 1.8; +uniform float Speed = 0.7; +uniform float Position : hint_range(0, 1) = 0; +uniform float Position_Min = 0.25; +uniform float Position_Max = 0.5; +uniform float Alpha : hint_range(0, 1) = 1; + +vec2 rotate_uv(vec2 uv, vec2 center, float rotation, bool use_degrees){ + float _angle = rotation; + if(use_degrees){ + _angle = rotation * (3.1415926/180.0); + } + mat2 _rotation = mat2( + vec2(cos(_angle), -sin(_angle)), + vec2(sin(_angle), cos(_angle)) + ); + vec2 _delta = uv - center; + _delta = _rotation * _delta; + return _delta + center; + } + +void fragment() { + + vec2 center_uv = UV - vec2(0.5, 0.5); + float gradient_to_edge = max(abs(center_uv.x), abs(center_uv.y)); + gradient_to_edge = gradient_to_edge * Distortion; + gradient_to_edge = 1.0 - gradient_to_edge; + vec2 rotaded_uv = rotate_uv(UV, vec2(0.5, 0.5), Rotation_deg, true); + + float remapped_position; + { + float output_range = Position_Max - Position_Min; + remapped_position = Position_Min + output_range * Position; + } + + float remapped_time = TIME * Speed + remapped_position; + remapped_time = fract(remapped_time); + { + float output_range = 2.0 - (-2.0); + remapped_time = -2.0 + output_range * remapped_time; + } + + vec2 offset_uv = vec2(rotaded_uv.xy) + vec2(remapped_time, 0.0); + float line = vec3(offset_uv, 0.0).x; + line = abs(line); + line = gradient_to_edge * line; + line = sqrt(line); + + float line_smoothness = clamp(Line_Smoothness, 0.001, 1.0); + float offset_plus = Line_Width + line_smoothness; + float offset_minus = Line_Width - line_smoothness; + + float remapped_line; + { + float input_range = offset_minus - offset_plus; + remapped_line = (line - offset_plus) / input_range; + } + remapped_line = remapped_line * Brightness; + remapped_line = min(remapped_line, Alpha); + COLOR.rgb = vec3(COLOR.xyz) * vec3(remapped_line); + COLOR.a = remapped_line; +} diff --git a/assets/state/states/quick_bar_performance.tres b/assets/state/states/quick_bar_performance.tres index 345f38ce6..811d09c6c 100644 --- a/assets/state/states/quick_bar_performance.tres +++ b/assets/state/states/quick_bar_performance.tres @@ -1,8 +1,8 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://xw3l4h1vt0oa"] +[gd_resource type="Resource" script_class="State" load_steps=2 format=3 uid="uid://b2ruoxboq5k6e"] -[ext_resource type="Script" path="res://core/systems/state/state.gd" id="1_pmemp"] +[ext_resource type="Script" path="res://core/systems/state/state.gd" id="1_3a3in"] [resource] -script = ExtResource("1_pmemp") -name = "performance" +script = ExtResource("1_3a3in") +name = "powertools" data = null diff --git a/assets/state/states/quick_bar_powertools.tres b/assets/state/states/quick_bar_powertools.tres deleted file mode 100644 index 811d09c6c..000000000 --- a/assets/state/states/quick_bar_powertools.tres +++ /dev/null @@ -1,8 +0,0 @@ -[gd_resource type="Resource" script_class="State" load_steps=2 format=3 uid="uid://b2ruoxboq5k6e"] - -[ext_resource type="Script" path="res://core/systems/state/state.gd" id="1_3a3in"] - -[resource] -script = ExtResource("1_3a3in") -name = "powertools" -data = null diff --git a/assets/ui/icons/iconoir--network-solid.svg b/assets/ui/icons/iconoir--network-solid.svg new file mode 100644 index 000000000..9105a08bc --- /dev/null +++ b/assets/ui/icons/iconoir--network-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/ui/icons/iconoir--network-solid.svg.import b/assets/ui/icons/iconoir--network-solid.svg.import new file mode 100644 index 000000000..de593f58d --- /dev/null +++ b/assets/ui/icons/iconoir--network-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cxcn8w1t87qm1" +path="res://.godot/imported/iconoir--network-solid.svg-668a1b8254e7d06c708068562df9cd81.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/ui/icons/iconoir--network-solid.svg" +dest_files=["res://.godot/imported/iconoir--network-solid.svg-668a1b8254e7d06c708068562df9cd81.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/ui/icons/material-symbols--lock.svg b/assets/ui/icons/material-symbols--lock.svg new file mode 100644 index 000000000..e32009064 --- /dev/null +++ b/assets/ui/icons/material-symbols--lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/ui/icons/material-symbols--lock.svg.import b/assets/ui/icons/material-symbols--lock.svg.import new file mode 100644 index 000000000..b5bc892e8 --- /dev/null +++ b/assets/ui/icons/material-symbols--lock.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ctnsy07oi3jks" +path="res://.godot/imported/material-symbols--lock.svg-ec8320f21f9adbbbebcfe8c41ada06a5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/ui/icons/material-symbols--lock.svg" +dest_files=["res://.godot/imported/material-symbols--lock.svg-ec8320f21f9adbbbebcfe8c41ada06a5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/ui/icons/mdi--ethernet.svg b/assets/ui/icons/mdi--ethernet.svg new file mode 100644 index 000000000..4620dd816 --- /dev/null +++ b/assets/ui/icons/mdi--ethernet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/ui/icons/mdi--ethernet.svg.import b/assets/ui/icons/mdi--ethernet.svg.import new file mode 100644 index 000000000..be57c422c --- /dev/null +++ b/assets/ui/icons/mdi--ethernet.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://caygvo6umqvln" +path="res://.godot/imported/mdi--ethernet.svg-c3832d5dc20f540b8d177a4e083b844b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/ui/icons/mdi--ethernet.svg" +dest_files=["res://.godot/imported/mdi--ethernet.svg-c3832d5dc20f540b8d177a4e083b844b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/assets/ui/icons/powertools_icon.svg b/assets/ui/icons/performance_icon.svg similarity index 100% rename from assets/ui/icons/powertools_icon.svg rename to assets/ui/icons/performance_icon.svg diff --git a/assets/ui/icons/powertools_icon.svg.import b/assets/ui/icons/performance_icon.svg.import similarity index 73% rename from assets/ui/icons/powertools_icon.svg.import rename to assets/ui/icons/performance_icon.svg.import index 360126d2b..2dd445a6f 100644 --- a/assets/ui/icons/powertools_icon.svg.import +++ b/assets/ui/icons/performance_icon.svg.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://cqy34r7oni6d4" -path="res://.godot/imported/powertools_icon.svg-652cf7833e5f111e096aaec7055e1d3a.ctex" +path="res://.godot/imported/performance_icon.svg-5082e7fb3c41a1711d81bab95097ecfa.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://assets/ui/icons/powertools_icon.svg" -dest_files=["res://.godot/imported/powertools_icon.svg-652cf7833e5f111e096aaec7055e1d3a.ctex"] +source_file="res://assets/ui/icons/performance_icon.svg" +dest_files=["res://.godot/imported/performance_icon.svg-5082e7fb3c41a1711d81bab95097ecfa.ctex"] [params] diff --git a/assets/ui/icons/tabler--network-off.svg b/assets/ui/icons/tabler--network-off.svg new file mode 100644 index 000000000..dba2e1c13 --- /dev/null +++ b/assets/ui/icons/tabler--network-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/ui/icons/tabler--network-off.svg.import b/assets/ui/icons/tabler--network-off.svg.import new file mode 100644 index 000000000..a83fce09b --- /dev/null +++ b/assets/ui/icons/tabler--network-off.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dghihbwuwab1y" +path="res://.godot/imported/tabler--network-off.svg-191244c146ebb11979e737f2add2c042.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/ui/icons/tabler--network-off.svg" +dest_files=["res://.godot/imported/tabler--network-off.svg-191244c146ebb11979e737f2add2c042.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/core/global/audio_manager.gd b/core/global/audio_manager.gd index c90969061..20be7fd0e 100644 --- a/core/global/audio_manager.gd +++ b/core/global/audio_manager.gd @@ -18,9 +18,6 @@ enum VOLUME { ## Limit the maximum volume to 200% const volume_limit := "2.0" -## Thread to run audio changes on -var thread := load("res://core/systems/threading/thread_pool.tres") - ## Current volume var current_volume := await _get_current_volume() var _muted := false @@ -29,7 +26,6 @@ var _current_output := "" func _init() -> void: - thread.start() current_volume = await _get_current_volume() _output_devices = await _get_output_devices() @@ -80,8 +76,11 @@ func set_volume(value: float, type: VOLUME = VOLUME.ABSOLUTE) -> int: suffix = "%+" var percent := value * 100 - var args := ["set-volume", "--limit", volume_limit, "@DEFAULT_AUDIO_SINK@", str(percent) + suffix] - var code := await thread.exec(OS.execute.bind("wpctl", args)) as int + var args: Array[String] = ["set-volume", "--limit", volume_limit, "@DEFAULT_AUDIO_SINK@", str(percent) + suffix] + var cmd := Command.create("wpctl", args) + cmd.timeout = 5.0 + cmd.execute() + var code := await cmd.finished as int return code @@ -90,7 +89,10 @@ func set_volume(value: float, type: VOLUME = VOLUME.ABSOLUTE) -> int: func toggle_mute() -> int: _muted = !_muted volume_mute_toggled.emit() - var code := await thread.exec(OS.execute.bind("wpctl", ["set-mute", "@DEFAULT_AUDIO_SINK@", "toggle"])) as int + var cmd := Command.create("wpctl", ["set-mute", "@DEFAULT_AUDIO_SINK@", "toggle"]) + cmd.timeout = 5.0 + cmd.execute() + var code := await cmd.finished as int return code @@ -116,7 +118,11 @@ func set_output_device(device: String) -> int: var i := devices.find(device) var device_id := ids[i] - return await thread.exec(OS.execute.bind("wpctl", ["set-default", device_id])) + var cmd := Command.create("wpctl", ["set-default", device_id]) + cmd.timeout = 5.0 + cmd.execute() + + return await cmd.finished as int ## Returns the currently set output device @@ -135,16 +141,16 @@ func get_current_volume() -> float: ## Fetch the volume for the current output device func _get_current_volume() -> float: - var output := [] - var code := await thread.exec(OS.execute.bind("wpctl", ["get-volume", "@DEFAULT_AUDIO_SINK@"], output)) as int - if code != 0: - return -1 - if output.size() == 0: + var cmd := Command.create("wpctl", ["get-volume", "@DEFAULT_AUDIO_SINK@"]) + cmd.timeout = 5.0 + cmd.execute() + var code := await cmd.finished as int + if code != OK: return -1 # Parse the output of wpctl # Example: Volume: 0.52 - var text := output[0] as String + var text := cmd.stdout var parts := text.split(" ") if parts.size() < 2: return -1 @@ -178,28 +184,26 @@ func _get_output_devices() -> PackedStringArray: # Inspects the given wirepipe object func _wpctl_inspect(id: String) -> PackedStringArray: - var out := PackedStringArray() - var output := [] - var code := await thread.exec(OS.execute.bind("wpctl", ["inspect", id], output)) as int - if code != 0: - return out - if output.size() == 0: - return out - var text := output[0] as String - return text.split("\n") + var cmd := Command.create("wpctl", ["inspect", id]) + cmd.timeout = 5.0 + cmd.execute() + var code := await cmd.finished as int + if code != OK: + return PackedStringArray() + return cmd.stdout.split("\n") # Returns an array of discovered Wirepipe object IDs func _get_wpctl_object_ids() -> PackedStringArray: var ids := PackedStringArray() - var output := [] - var code := await thread.exec(OS.execute.bind("wpctl", ["status"], output)) as int - if code != 0: - return ids - if output.size() == 0: + var cmd := Command.create("wpctl", ["status"]) + cmd.timeout = 5.0 + cmd.execute() + var code := await cmd.finished as int + if code != OK: return ids - var text := output[0] as String + var text := cmd.stdout var parts := text.split(" ") if parts.size() < 2: return ids diff --git a/core/global/battery_manager.gd b/core/global/battery_manager.gd deleted file mode 100644 index 56e8f37ad..000000000 --- a/core/global/battery_manager.gd +++ /dev/null @@ -1,91 +0,0 @@ -@icon("res://assets/ui/icons/battery-charging.svg") -extends Resource -class_name BatteryManager - - -const power_supply_dir = "/sys/class/power_supply" - -@export var icon_charging: Texture = preload("res://assets/ui/icons/battery-charging.svg") -@export var icon_full: Texture = preload("res://assets/ui/icons/battery-full.svg") -@export var icon_high: Texture = preload("res://assets/ui/icons/battery-75.svg") -@export var icon_half: Texture = preload("res://assets/ui/icons/battery-half.svg") -@export var icon_low: Texture = preload("res://assets/ui/icons/battery-low.svg") -@export var icon_empty: Texture = preload("res://assets/ui/icons/battery-empty.svg") - -enum STATUS { - NONE, - DISCHARGING, - NOT_CHARGING, - CHARGING, - FULL, -} - -var battery_path: String = find_battery_path() -var logger := Log.get_logger("BatteryManager") - -## Finds the battery path. If none is found, returns an empty string. -static func find_battery_path() -> Variant: - var power_dir: DirAccess = DirAccess.open(power_supply_dir) - var devices: PackedStringArray = power_dir.get_directories() - var battery_dir: String = "" - for folder in devices: - if folder.begins_with("BAT"): - battery_dir = folder - break - if battery_dir == "": - return "" - - return "/".join([power_supply_dir, battery_dir]) - - -## Returns the current battery capacity as a percentage. Returns -1 if no battery -## was discovered. -func get_capacity() -> int: - if battery_path == "": - return -1 - var capacity_file: String = "/".join([battery_path, "capacity"]) - var file: FileAccess = FileAccess.open(capacity_file, FileAccess.READ) - var bytes: PackedByteArray = file.get_buffer(100) - var str: String = bytes.get_string_from_ascii().strip_edges() - if not str.is_valid_int(): - return -1 - return str.to_int() - - -## Returns the status of the given battery -func get_status() -> STATUS: - if battery_path == "": - return STATUS.NONE - var status_file: String = "/".join([battery_path, "status"]) - - var output := [] - var code := OS.execute("cat", [status_file], output) - if code != OK: - return STATUS.NONE - var status := (output[0] as String).strip_edges() - - match status: - "Discharging": - return STATUS.DISCHARGING - "Not charging": - return STATUS.NOT_CHARGING - "Charging": - return STATUS.CHARGING - "Full": - return STATUS.FULL - return STATUS.NONE - - -## Returns the texture reflecting the given battery capacity -func get_capacity_texture(capacity: int, status: STATUS = STATUS.NONE) -> Texture2D: - if status > STATUS.NOT_CHARGING: - return icon_charging - if capacity >= 90: - return icon_full - if capacity >= 65: - return icon_high - if capacity >= 40: - return icon_half - if capacity >= 20: - return icon_low - return icon_empty diff --git a/core/global/battery_manager.tres b/core/global/battery_manager.tres deleted file mode 100644 index 4cec2e356..000000000 --- a/core/global/battery_manager.tres +++ /dev/null @@ -1,18 +0,0 @@ -[gd_resource type="Resource" script_class="BatteryManager" load_steps=8 format=3 uid="uid://bn5vhnowoo3dl"] - -[ext_resource type="Texture2D" uid="uid://000r1wyog2d1" path="res://assets/ui/icons/battery-charging.svg" id="1_lk1dm"] -[ext_resource type="Script" path="res://core/global/battery_manager.gd" id="2_mxapa"] -[ext_resource type="Texture2D" uid="uid://ck3r7wbbb6lj7" path="res://assets/ui/icons/battery-empty.svg" id="2_qbtqx"] -[ext_resource type="Texture2D" uid="uid://icnhb750o8ma" path="res://assets/ui/icons/battery-full.svg" id="3_v7nrv"] -[ext_resource type="Texture2D" uid="uid://c61mthg68r41g" path="res://assets/ui/icons/battery-half.svg" id="4_j7gj6"] -[ext_resource type="Texture2D" uid="uid://b3amqx05ikddx" path="res://assets/ui/icons/battery-75.svg" id="5_ick7m"] -[ext_resource type="Texture2D" uid="uid://ywcnlki2k06u" path="res://assets/ui/icons/battery-low.svg" id="6_6fi5u"] - -[resource] -script = ExtResource("2_mxapa") -icon_charging = ExtResource("1_lk1dm") -icon_full = ExtResource("3_v7nrv") -icon_high = ExtResource("5_ick7m") -icon_half = ExtResource("4_j7gj6") -icon_low = ExtResource("6_6fi5u") -icon_empty = ExtResource("2_qbtqx") diff --git a/core/global/boxart_manager.gd b/core/global/boxart_manager.gd index 0d97d33f7..361fe17fd 100644 --- a/core/global/boxart_manager.gd +++ b/core/global/boxart_manager.gd @@ -55,6 +55,9 @@ func get_boxart(item: LibraryItem, kind: BoxArtProvider.LAYOUT) -> Texture2D: func _get_boxart_sync(item: LibraryItem, kind: BoxArtProvider.LAYOUT) -> Texture2D: + if not item: + return null + if _providers.is_empty(): logger.error("No box art providers were found!") return null diff --git a/core/global/dbus_manager.gd b/core/global/dbus_manager.gd deleted file mode 100644 index 4786b3bd3..000000000 --- a/core/global/dbus_manager.gd +++ /dev/null @@ -1,311 +0,0 @@ -extends Resource -class_name DBusManager - -## DBusManager is a helper class for using DBus -## -## Use this class to interface with DBus. - -enum BUS_TYPE { - SYSTEM = dbus.DBUS_BUS_SYSTEM, - SESSION = dbus.DBUS_BUS_SESSION, - STARTER = dbus.DBUS_BUS_STARTER, -} - -const DBUS_BUS := "org.freedesktop.DBus" -const DBUS_PATH := "/org/freedesktop/DBus" - -const IFACE_DBUS := "org.freedesktop.DBus" -const IFACE_PROPERTIES := "org.freedesktop.DBus.Properties" -const IFACE_OBJECT_MANAGER := "org.freedesktop.DBus.ObjectManager" - -## Type of bus to connect to -@export var bus_type := BUS_TYPE.SYSTEM -## Shared thread to process DBus messages on -@export var thread: SharedThread = load("res://core/systems/threading/system_thread.tres") - -var logger := Log.get_logger("DBusManager") -var well_known_names := [] -var dbus := DBus.new() -var dbus_proxy := DBusProxy.new(create_proxy(DBUS_BUS, DBUS_PATH)) - - -func _init() -> void: - if dbus.connect(bus_type) != OK: - logger.warn("Unable to connect to dbus") - return - thread.add_process(_process) - thread.start() - - -## Process messages on the bus that are being watched and dispatch them. -func _process(_delta: float): - var messages: Array[DBusMessage] = [] - var msg := dbus.pop_message() - while msg != null: - logger.debug("Received DBus message on " + msg.get_sender() + " from " + msg.get_path() + ": " + str(msg.get_args())) - messages.append(msg) - msg = dbus.pop_message() - - for message in messages: - _process_message(message) - - -## Dispatch the given message to any proxy objects -func _process_message(msg: DBusMessage) -> void: - # Try looking up the well-known name of the message sender - var known_names := get_names_for_owner(msg.get_sender()) - - # Try constructing the resource path to the proxy and see if it exists - for known_name in known_names: - var res_path := "dbus://" + known_name + msg.get_path() - if not ResourceLoader.exists(res_path): - logger.debug("No proxy resource found to send message to at: " + res_path) - continue - logger.debug("Found proxy to send message signal to at: " + res_path) - var proxy := load(res_path) as Proxy - if not proxy: - logger.warn("Failed to load proxy from resource cache: " + res_path) - continue - var send_signal := func(message: DBusMessage) -> void: - proxy.message_received.emit(message) - send_signal.call_deferred(msg) - break - - -## Creates a reference to a DBus object on the given bus at the given path. -## E.g. create_proxy("org.bluez", "/org/bluez/hci0") -func create_proxy(bus: String, path: String) -> Proxy: - # Try to load the proxy if it already exists - var res_path := "dbus://" + bus + path - logger.debug("Creating proxy with resource path: " + res_path) - var proxy: Proxy - if ResourceLoader.exists(res_path): - logger.debug("Resource already exists. Returning existing instance.") - proxy = load(res_path) - return proxy - - proxy = Proxy.new(dbus, bus, path) - proxy.take_over_path(res_path) - - # Keep track of bus names so they can be referenced later - if not bus in well_known_names: - well_known_names.append(bus) - - return proxy - - -## Returns true if the given well-known name has an owner. -func bus_exists(name: String) -> bool: - return dbus.name_has_owner(name) - -# TODO: This is deprecated. Remove this and all refrences to it. -## Returns a dictionary of manages objects for the given bus and path -func get_managed_objects(bus: String, path: String) -> Array[ManagedObject]: - var obj := create_proxy(bus, path) - var result := obj.call_method(IFACE_OBJECT_MANAGER, "GetManagedObjects", [], "") - if not result: - return [] - var args := result.get_args() - if args.size() != 1: - return [] - if not args[0] is Dictionary: - return [] - - var objs_dict := args[0] as Dictionary - var objects: Array[ManagedObject] = [] - - # Convert the objects dictionary into an array of objects - for obj_path in objs_dict.keys(): - var obj_data := objs_dict[obj_path] as Dictionary - var object := ManagedObject.new(obj_path as String, obj_data) - objects.append(object) - - return objects - - -## Tries to resolve well-known names (e.g. "org.bluez") from the given owner (e.g. ":1.5"). -## This will return an array of well-known names. -func get_names_for_owner(owner: String) -> PackedStringArray: - var names := PackedStringArray() - for name in well_known_names: - var name_owner := dbus_proxy.get_name_owner(name as String) - if name_owner == owner: - names.append(name as String) - - return names - - -func _to_string() -> String: - var bus_string := "System" if bus_type == BUS_TYPE.SYSTEM else "Session" - return "".format([bus_string]) - - -class ObjectManager extends Resource: - signal interfaces_added(dbus_path: String) - signal interfaces_removed(dbus_path: String) - - var _proxy: Proxy - - func _init(proxy: Proxy) -> void: - _proxy = proxy - _proxy.message_received.connect(_on_message_received) - _proxy.thread.exec(_proxy.watch.bind(IFACE_OBJECT_MANAGER, "InterfacesAdded")) - _proxy.thread.exec(_proxy.watch.bind(IFACE_OBJECT_MANAGER, "InterfacesRemoved")) - - ## Returns a dictionary of manages objects for the given bus and path - func get_managed_objects(_bus: String, _path: String) -> Array[ManagedObject]: - var result := _proxy.call_method(IFACE_OBJECT_MANAGER, "GetManagedObjects", [], "") - if not result: - return [] - var args := result.get_args() - if args.size() != 1: - return [] - if not args[0] is Dictionary: - return [] - - var objs_dict := args[0] as Dictionary - var objects: Array[ManagedObject] = [] - - # Convert the objects dictionary into an array of objects - for obj_path in objs_dict.keys(): - var obj_data := objs_dict[obj_path] as Dictionary - var object := ManagedObject.new(obj_path as String, obj_data) - objects.append(object) - - return objects - - func _on_message_received(msg: DBusMessage) -> void: - #print("Got message: " + str(msg)) - if not msg: - return - var args := msg.get_args() - #print("Got args: " + str(args)) - if args.size() != 2: - return - #print("Got args big enough") - if msg.get_member() == "InterfacesAdded": - #print("Got InterfacesAdded") - interfaces_added.emit(args[0]) - - if msg.get_member() == "InterfacesRemoved": - #print("Got InterfacesRemoved") - interfaces_removed.emit(args[0]) - - -## A Proxy provides an interface to call methods on a DBus object. -class Proxy extends Resource: - signal message_received(msg: DBusMessage) - signal properties_changed(iface: String, props: Dictionary) - var _dbus: DBus - var bus_name: String - var path: String - var rules := PackedStringArray() - var logger := Log.get_logger("DBusProxy") - var thread: SharedThread = load("res://core/systems/threading/system_thread.tres") - - func _init(conn: DBus, bus: String, obj_path: String) -> void: - _dbus = conn - bus_name = bus - path = obj_path - message_received.connect(_on_property_changed) - thread.exec(watch.bind(IFACE_PROPERTIES, "PropertiesChanged")) - - func _on_property_changed(msg: DBusMessage) -> void: - if not msg: - return - if msg.get_member() != "PropertiesChanged": - return - var args := msg.get_args() - if args.size() < 2: - return - properties_changed.emit(args[0], args[1]) - - func _notification(what: int) -> void: - if what != NOTIFICATION_PREDELETE: - return - for rule in rules: - logger.debug("Removing watch rule: " + rule) - _dbus.remove_match(rule) - - ## Call the given method - func call_method(iface: String, method: String, args: Array = [], signature: String = "") -> DBusMessage: - logger.debug("Calling method: " + iface + "::" + method + "(" + str(args) + ")") - return _dbus.send_with_reply_and_block(bus_name, path, iface, method, args, signature) - - ## Set the given property - func set_property(iface: String, property: String, value: Variant) -> void: - logger.debug("Set property " + property + " to " + str(value) + " for interface " + iface) - call_method(IFACE_PROPERTIES, "Set", [iface, property, value], "ssv") - - ## Get the given property - func get_property(iface: String, property: String) -> Variant: - var response := call_method(IFACE_PROPERTIES, "Get", [iface, property], "ss") - if not response: - return null - var args := response.get_args() - if args.size() == 0: - return null - - return args[0] - - ## Get all properties for the given interface - func get_properties(iface: String) -> Dictionary: - var response := call_method(IFACE_PROPERTIES, "GetAll", [iface], "s") - if not response: - return {} - var args := response.get_args() - if args.size() == 0: - return {} - - return args[0] - - ## Watch the bus for particular signals - func watch(iface: String, member: String = "PropertiesChanged") -> int: - var rule := "type='signal',interface='{0}',path='{1}',member='{2}'".format( - [iface, path, member] - ) - rules.append(rule) - logger.debug("Adding watch rule: " + rule) - var err := _dbus.add_match(rule) - if err != OK: - logger.error("Unable to watch " + path) - return err - - -## A ManagedObject is a simple structure used with GetManagedObjects -class ManagedObject: - var path: String - var data: Dictionary - - func _init(obj_path: String, obj_data: Dictionary) -> void: - path = obj_path - data = obj_data - - func has_interface(name: String) -> bool: - return name in data - - func has_interface_attr(iface: String, name: String) -> bool: - if not iface in data: - return false - if not name in data[iface]: - return false - return true - - -## Proxy to manage /org/freedesktop/DBus on the org.freedesktop.DBus bus. -class DBusProxy: - var _proxy: Proxy - - func _init(proxy: Proxy) -> void: - _proxy = proxy - - ## Return the connection name (e.g. ":1.1270") from the given well-known name - func get_name_owner(name: String) -> String: - var msg := _proxy.call_method(IFACE_DBUS, "GetNameOwner", [name], "s") - if not msg: - return "" - var args := msg.get_args() - if args.size() != 1: - return "" - - return args[0] diff --git a/core/global/dbus_system.tres b/core/global/dbus_system.tres deleted file mode 100644 index 95c70f0d1..000000000 --- a/core/global/dbus_system.tres +++ /dev/null @@ -1,9 +0,0 @@ -[gd_resource type="Resource" script_class="DBusManager" load_steps=3 format=3 uid="uid://dskqww1130rb2"] - -[ext_resource type="Script" path="res://core/global/dbus_manager.gd" id="1_6xf1o"] -[ext_resource type="Resource" uid="uid://ct7pi0cx0r832" path="res://core/systems/threading/system_thread.tres" id="2_p3brt"] - -[resource] -script = ExtResource("1_6xf1o") -bus_type = 1 -thread = ExtResource("2_p3brt") diff --git a/core/global/gamescope.gd b/core/global/gamescope.gd deleted file mode 100644 index d8ae55f73..000000000 --- a/core/global/gamescope.gd +++ /dev/null @@ -1,751 +0,0 @@ -extends Resource -class_name Gamescope - -## Interact with Gamescope windows and properties -## -## The Gamescope class is responsible for interacting with Gamescope, usually -## via the means of setting gamescope-specific window properties. It can be -## used to discover Gamescope displays, list windows and their children, and set -## gamescope-specific window atoms to switch windows, set blur, limit FPS, etc. -## [br][br] -## For example, to limit the FPS, you can do the following: -## [codeblock] -## Gamescope.set_fps_limit(display, 30) -## [/codeblock] -## [br][br] -## Most of the core functionality of this class is provided by the [Xlib] -## module, which is a GDExtension that exposes Xlib methods to Godot. - -signal blur_mode_updated(from: int, to: int) -signal display_is_external_updated(from: int, to: int) -signal focused_window_updated(from: int, to: int) -signal focusable_windows_updated(from: PackedInt32Array, to: PackedInt32Array) -signal focused_app_updated(from: int, to: int) -signal focused_app_gfx_updated(from: int, to: int) -signal focusable_apps_updated(from: PackedInt32Array, to: PackedInt32Array) - -## Gamescope Blur modes -enum BLUR_MODE { - OFF = 0, ## Turns off blur of running games - COND = 1, ## Conditionally blurs running games - ALWAYS = 2, ## Turns blurring of running games on -} - -## Specifies which Gamescope xwayland server to perform an operation on. -enum XWAYLAND { - PRIMARY, ## Primary Gamescope xwayland instance - OGUI, ## Xwayland instance that OpenGamepadUI is running on - GAME, ## Xwayland instance where games run -} - -## Gamescope is hard-coded to look for STEAM_GAME=769 to determine if it is the -## overlay app. -const OVERLAY_GAME_ID := 769 - -@export var log_level := Log.LEVEL.INFO -## The primary xwayland is the primary Gamescope xwayland session that contains -## Gamescope properties on the root window. -var xwayland_primary: Xlib -## The OGUI xwayland is the xwayland instance that OGUI is running under. -var xwayland_ogui: Xlib -## The Game xwayland is the xwayland instance that games are launched under. -var xwayland_game: Xlib -## Array of all discovered xwayland instances -var xwaylands: Array[Xlib] = [] -var logger := Log.get_logger("Gamescope", log_level) - -# Gamescope properties -## Blur mode (read-only) -var blur_mode: int: - set(v): - var prev_value := blur_mode - blur_mode = v - if prev_value != v: - blur_mode_updated.emit(prev_value, v) -var baselayer_window: int -var input_counter: int -var display_is_external: int -var vrr_enabled: int -var vrr_feedback: int -var vrr_capable: int -var keyboard_focus_display: PackedInt32Array -var mouse_focus_display: PackedInt32Array -var focus_display: PackedInt32Array -var focused_window: int: - set(v): - var prev_value := focused_window - focused_window = v - if prev_value != v: - focused_window_updated.emit(prev_value, v) -var focused_app_gfx: int: - set(v): - var prev_value := focused_app_gfx - focused_app_gfx = v - if prev_value != v: - focused_app_gfx_updated.emit(prev_value, v) -var focused_app: int: - set(v): - var prev_value := focused_app - focused_app = v - if prev_value != v: - focused_app_updated.emit(prev_value, v) -var focusable_windows: PackedInt32Array: - set(v): - var prev_value := focusable_windows - focusable_windows = v - if prev_value != v: - focusable_windows_updated.emit(prev_value, v) -var focusable_apps: PackedInt32Array: - set(v): - var prev_value := focusable_apps - focusable_apps = v - if prev_value != v: - focusable_apps_updated.emit(prev_value, v) -var cursor_visible_feedback: int - - -# Connects to all gamescope xwayland instances -func _init() -> void: - # Don't initialize if run from the editor (during doc generation) - if Engine.is_editor_hint(): - logger.info("Not initializing. Ran from editor.") - return - - # Connect to the xwayland instance that OGUI is running on - var ogui_display := OS.get_environment("DISPLAY") - xwayland_ogui = Xlib.new() - if xwayland_ogui.open(ogui_display) != OK: - logger.error("Failed to open OGUI X server: " + ogui_display) - return - if _is_gamescope_xwayland(xwayland_ogui): - logger.debug("OpenGamepadUI is running in Gamescope") - xwayland_game = xwayland_ogui - if _is_gamescope_xwayland_primary(xwayland_ogui): - logger.debug("OpenGamepadUI is running on the primary Gamescope xwayland") - xwayland_primary = xwayland_ogui - xwaylands.push_front(xwayland_ogui) - - # Discover all other xwayland displays - var displays := discover_gamescope_displays() - for display in displays: - if _has_xwayland(display): - logger.debug("Already discovered xwayland: " + display) - continue - var xwayland := Xlib.new() - if xwayland.open(display) != OK: - logger.debug("Failed to open X server: " + display) - continue - if _is_gamescope_xwayland_primary(xwayland): - logger.debug("Display " + display + " is the primary gamescope instance") - xwayland_primary = xwayland - if xwayland_primary != xwayland: - xwayland_game = xwayland - xwaylands.append(xwayland) - - # If we haven't discovered any gamescope displays, set everything to use - # the OGUI xwayland instance - if not xwayland_primary: - logger.warn("OpenGamepadUI is not running in Gamescope. Unexpected behavior expected.") - xwayland_primary = xwayland_ogui - if not xwayland_game: - xwayland_game = xwayland_ogui - - logger.debug("Primary xwayland is " + xwayland_primary.get_name()) - logger.debug("OGUI xwayland is " + xwayland_ogui.get_name()) - logger.debug("Game xwayland is " + xwayland_game.get_name()) - - -## Returns all gamescope xwayland names (E.g. [":0", ":1"]) -# TODO: This seems brittle. Is there any other way we can discover Gamescope displays? -func discover_gamescope_displays() -> PackedStringArray: - logger.debug("Discovering xwaylands!") - - # X11 displays have a corresponding socket in /tmp/.X11-unix - # The sockets are named like: X0, X1, X2, etc. - var dir := DirAccess.open("/tmp/.X11-unix") - var sockets := dir.get_files() - - # Loop through each socket file and derrive the display number. - var display_names: PackedInt32Array = [] - for socket in sockets: - var suffix := (socket as String).trim_prefix("X") - if not suffix.is_valid_int(): - logger.warn("Skipping X11 socket with a weird name: " + socket) - continue - display_names.append(suffix.to_int()) - - # Check to see if the root window of these displays has gamescope-specific properties - var gamescope_displays := PackedStringArray() - for display_num in display_names: - var display := ":{0}".format([display_num]) - var xwayland := Xlib.new() - if xwayland.open(display) != OK: - logger.debug("Failed to open X server: " + display) - continue - if _is_gamescope_xwayland(xwayland): - gamescope_displays.append(display) - logger.debug("Discovered X server display: " + display) - xwayland.close() - return gamescope_displays - - -## Updates the Gamescope state. Should be called in a loop to keep the Gamescope -## state up-to-date. -func update() -> void: - blur_mode = get_blur_mode() - focused_window = get_focused_window() - focused_app = get_focused_app() - focused_app_gfx = get_focused_app_gfx() - focusable_windows = get_focusable_windows() - focusable_apps = get_focusable_apps() - baselayer_window = get_baselayer_window() - if not baselayer_window in get_focusable_windows(): - baselayer_window = -1 - remove_baselayer_window() - - -## Returns the name of the given xwayland display (e.g. ":1") -func get_display_name(display: XWAYLAND) -> String: - var xwayland := get_xwayland(display) - if not xwayland: - return "" - return xwayland.get_name() - - -## Returns true if the given X property exists on the given window. -func has_xprop(window_id: int, key: String, display: XWAYLAND) -> bool: - var xwayland := get_xwayland(display) - if not xwayland: - return false - return xwayland.has_xprop(window_id, key) - - -## Returns a list of X properties on the given window -func list_xprops(window_id: int, display: XWAYLAND) -> PackedStringArray: - var xwayland := get_xwayland(display) - if not xwayland: - return [] - return xwayland.list_xprops(window_id) - - -## Returns the name of the given window. -func get_window_name(window_id: int, display: XWAYLAND = XWAYLAND.GAME) -> String: - var xwayland := get_xwayland(display) - if not xwayland: - return "" - return xwayland.get_window_name(window_id) - - -## Returns the PID of the given window. Returns -1 if no PID was found. -func get_window_pid(window_id: int, display: XWAYLAND) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return xwayland.get_window_pid(window_id) - - -## Returns the xwayland window ID for the given process. Returns -1 if no -## window was found. -func get_window_id(pid: int, display: XWAYLAND) -> int: - var display_name := get_display_name(display) - logger.trace("Getting Window ID for {0} on display {1}".format([pid, display_name])) - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - var all_windows := get_all_windows(root_id, display) - for window_id in all_windows: - var window_pid := xwayland.get_window_pid(window_id) - if pid == window_pid: - return window_id - window_pid = xwayland.get_xprop(window_id, "_NET_WM_PID") - if pid == window_pid: - return window_id - - return -1 - - -## Returns the xwayland window ID(s) for the given process using multiple methods -## to try and discover. -func get_window_ids(pid: int, display: XWAYLAND) -> PackedInt32Array: - var window_ids := PackedInt32Array() - var display_name := get_display_name(display) - logger.trace("Getting Window ID for {0} on display {1}".format([pid, display_name])) - var xwayland := get_xwayland(display) - if not xwayland: - return window_ids - var root_id := xwayland.get_root_window_id() - var all_windows := get_all_windows(root_id, display) - for window_id in all_windows: - var window_pid := xwayland.get_window_pid(window_id) - if pid == window_pid and not window_id in window_ids: - window_ids.append(window_id) - var net_window_pid := xwayland.get_xprop(window_id, "_NET_WM_PID") - if pid == net_window_pid and not net_window_pid in window_ids: - window_ids.append(net_window_pid) - - return window_ids - - -## Returns the child window ids of the given window -func get_window_children(window_id: int, display: XWAYLAND) -> PackedInt32Array: - var xwayland := get_xwayland(display) - if not xwayland: - return PackedInt32Array() - var children := xwayland.get_window_children(window_id) - if len(children) == 0: - return PackedInt32Array() - return children - - -## Recursively returns all child windows of the given window id -func get_all_windows(window_id: int, display: XWAYLAND) -> PackedInt32Array: - var xwayland := get_xwayland(display) - if not xwayland: - return PackedInt32Array() - var children := xwayland.get_window_children(window_id) - if len(children) == 0: - return PackedInt32Array([]) - - var leaves := PackedInt32Array() - for child in children: - leaves.append(child) - leaves.append_array(get_all_windows(child, display)) - - return leaves - - -## Returns true if the window with the given window ID exists -func is_focusable_app(window_id: int, display: XWAYLAND = XWAYLAND.PRIMARY) -> bool: - var focusable := get_focusable_apps(display) - if window_id in focusable: - return true - return false - - -## Returns the root window ID of the given display -func get_root_window_id(display: XWAYLAND) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return xwayland.get_root_window_id() - - -## Returns a list of focusable app window ids -func get_focusable_apps(display: XWAYLAND = XWAYLAND.PRIMARY) -> PackedInt32Array: - var xwayland := get_xwayland(display) - if not xwayland: - return PackedInt32Array() - var root_id := xwayland.get_root_window_id() - return _get_xprop_array(xwayland, root_id, "GAMESCOPE_FOCUSABLE_APPS") - - -## Returns a list of focusable window ids -func get_focusable_windows(display: XWAYLAND = XWAYLAND.PRIMARY) -> PackedInt32Array: - var xwayland := get_xwayland(display) - if not xwayland: - return PackedInt32Array() - var root_id := xwayland.get_root_window_id() - return _get_xprop_array(xwayland, root_id, "GAMESCOPE_FOCUSABLE_WINDOWS") - - -## Returns a list of focusable window names -func get_focusable_window_names(display: XWAYLAND = XWAYLAND.PRIMARY) -> PackedStringArray: - var focusable := get_focusable_windows(display) - var results := PackedStringArray() - for window_id in focusable: - var name := get_window_name(window_id, display) - results.append(name) - return results - - -## Return the currently focused window id. -func get_focused_window(display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - var results := _get_xprop_array(xwayland, root_id, "GAMESCOPE_FOCUSED_WINDOW") - if results.size() == 0: - return 0 - return results[0] - - -## Return the currently focused app id. -func get_focused_app(display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - var results := _get_xprop_array(xwayland, root_id, "GAMESCOPE_FOCUSED_APP") - if results.size() == 0: - return 0 - return results[0] - - -## Return the currently focused gfx app id. -func get_focused_app_gfx(display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - var results := _get_xprop_array(xwayland, root_id, "GAMESCOPE_FOCUSED_APP_GFX") - if results.size() == 0: - return 0 - return results[0] - - -## Sets the given window as the main launcher app. -## Gamescope is hard-coded to look for appId 769 -func set_main_app(window_id: int, display: XWAYLAND = XWAYLAND.OGUI) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return _set_xprop(xwayland, window_id, "STEAM_GAME", OVERLAY_GAME_ID) - - -## Set the given window as the primary overlay input focus. This should be set to -## "1" whenever the overlay wants to intercept input from a game. -func set_input_focus(window_id: int, value: int, display: XWAYLAND = XWAYLAND.OGUI) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return _set_xprop(xwayland, window_id, "STEAM_INPUT_FOCUS", value) - - -## Returns whether or not the overlay window is currently focused -func is_overlay_focused(display: XWAYLAND = XWAYLAND.OGUI) -> bool: - return get_focused_app(display) == OVERLAY_GAME_ID - - -## Get the overlay status for the given window -func get_overlay(window_id: int, display: XWAYLAND = XWAYLAND.OGUI) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return _get_xprop(xwayland, window_id, "STEAM_OVERLAY") - - -## Set the given window as an overlay -func set_overlay(window_id: int, value: int, display: XWAYLAND = XWAYLAND.OGUI) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return _set_xprop(xwayland, window_id, "STEAM_OVERLAY", value) - - -## Set the given window as a notification. This should be set to "1" when some -## UI wants to be shown but not intercept input. -func set_notification(window_id: int, value: int, display: XWAYLAND = XWAYLAND.OGUI) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return _set_xprop(xwayland, window_id, "STEAM_NOTIFICATION", value) - - -## Set the given window as an external overlay -func set_external_overlay(window_id: int, value: int, display: XWAYLAND = XWAYLAND.OGUI) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return _set_xprop(xwayland, window_id, "GAMESCOPE_EXTERNAL_OVERLAY", value) - - -## Returns the currently set app ID on the given window -func get_app_id(window_id: int, display: XWAYLAND = XWAYLAND.GAME) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return _get_xprop(xwayland, window_id, "STEAM_GAME") - - -## Sets the app ID on the given window -func set_app_id(window_id: int, app_id: int, display: XWAYLAND = XWAYLAND.GAME) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - return _set_xprop(xwayland, window_id, "STEAM_GAME", app_id) - - -## Returns whether or not the given window has an app ID set -func has_app_id(window_id: int, display: XWAYLAND = XWAYLAND.GAME) -> bool: - return has_xprop(window_id, "STEAM_GAME", display) - - -## Sets the Gamescope FPS limit -func set_fps_limit(fps: int = 60, display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - logger.debug("Setting FPS to: {0}".format([fps])) - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - return _set_xprop(xwayland, root_id, "GAMESCOPE_FPS_LIMIT", fps) - - -## Returns the Gamescope FPS limit -func get_fps_limit(display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - return _get_xprop(xwayland, root_id, "GAMESCOPE_FPS_LIMIT") - - -## Returns the current Gamescope blur mode -func get_blur_mode(display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - return _get_xprop(xwayland, root_id, "GAMESCOPE_BLUR_MODE") - - -## Sets the Gamescope blur mode -func set_blur_mode(mode: BLUR_MODE = BLUR_MODE.OFF, display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - logger.debug("Setting blur mode to: {0}".format([mode])) - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - var err := _set_xprop(xwayland, root_id, "GAMESCOPE_BLUR_MODE", mode) - blur_mode = mode - return err - - -## Sets the Gamescope blur radius when blur is active -func set_blur_radius(radius: int, display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - logger.debug("Setting blur radius to: {0}".format([radius])) - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - return _set_xprop(xwayland, root_id, "GAMESCOPE_BLUR_RADIUS", radius) - - -## Configures Gamescope to allow tearing or not -func set_allow_tearing(allow: bool, display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - logger.debug("Setting tearing to: {0}".format([allow])) - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var value := 1 if allow else 0 - var root_id := xwayland.get_root_window_id() - return _set_xprop(xwayland, root_id, "GAMESCOPE_ALLOW_TEARING", value) - - -## Returns the currently set manual focus -func get_baselayer_window(display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - return _get_xprop(xwayland, root_id, "GAMESCOPECTRL_BASELAYER_WINDOW") - - -## Focuses the given window -func set_baselayer_window(window_id: int, display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - return _set_xprop(xwayland, root_id, "GAMESCOPECTRL_BASELAYER_WINDOW", window_id) - - -## Removes the baselayer property to un-focus windows -func remove_baselayer_window(display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - return _remove_xprop(xwayland, root_id, "GAMESCOPECTRL_BASELAYER_WINDOW") - - -## Request a screenshot from gamescope -func request_screenshot(display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - return _set_xprop(xwayland, root_id, "GAMESCOPECTRL_REQUEST_SCREENSHOT", 1) - - -## Sets the xwayland mode resolution on the given xwayland display -## number (default: XWAYLAND.GAME). -func set_resolution(resolution: Vector2i, allow_super: bool = false, display: XWAYLAND = XWAYLAND.GAME) -> int: - var xwayland := get_xwayland(XWAYLAND.PRIMARY) - if not xwayland: - return -1 - - var target_display := get_display_number(display) - var root_id := xwayland.get_root_window_id() - var allow_super_value := 1 if allow_super else 0 - var args := PackedInt32Array([target_display, resolution.x, resolution.y, allow_super_value]) - return _set_xprop_array(xwayland, root_id, "GAMESCOPE_XWAYLAND_MODE_CONTROL", args) - - -## Returns the currently set gamescope saturation -# Based on vibrantDeck by Scrumplex -func get_saturation(display: XWAYLAND = XWAYLAND.PRIMARY) -> float: - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - - var matrix := _get_xprop_array(xwayland, root_id, "GAMESCOPE_COLOR_MATRIX") - if matrix.size() < 2: - return -1 - - # [1065353216, 0, 0, 0, 1065353216, 0, 0, 0, 1065353216] - var matrix_arr: Array[int] = Array(matrix) - # Convert the array of longs to floats - # [1.0, 0, 0, 0, 1.0, 0, 0, 0, 1.0] - var coeffs := matrix_arr.map(_long_to_float) - var saturation := snappedf(coeffs[0] - coeffs[1], 0.01) - - return saturation - - -## Set the gamescope saturation -# Based on vibrantDeck by Scrumplex -func set_saturation(saturation: float, display: XWAYLAND = XWAYLAND.PRIMARY) -> int: - saturation = maxf(saturation, 0.0) - saturation = minf(saturation, 4.0) - logger.debug("Setting saturation to: " + str(saturation)) - - # Generate color transformation matrix - var coeffs := _saturation_to_coeffs(saturation) - - # Represent floats as integars (long) - var long_coeffs := coeffs.map(_float_to_long) - - # Get the xwayland to set the property on - var xwayland := get_xwayland(display) - if not xwayland: - return -1 - var root_id := xwayland.get_root_window_id() - - logger.debug("Setting color matrix coeffs: " + str(long_coeffs)) - return _set_xprop_array(xwayland, root_id, "GAMESCOPE_COLOR_MATRIX", long_coeffs) - - -func _saturation_to_coeffs(saturation: float) -> Array[float]: - var coeff := (1.0 - saturation) / 3.0 - - var coeffs: Array[float] = [] - coeffs.resize(9) - coeffs.fill(coeff) - coeffs[0] += saturation - coeffs[4] += saturation - coeffs[8] += saturation - - return coeffs - - -func _float_to_long(x: float) -> int: - var bytes := PackedByteArray() - bytes.resize(4) - bytes.encode_float(0, x) - return bytes.decode_u32(0) - - -func _long_to_float(x: int) -> float: - var bytes := PackedByteArray() - bytes.resize(4) - bytes.encode_u32(0, x) - return bytes.decode_float(0) - - -## Returns the display type for the given display name -func get_display_type(name: String) -> XWAYLAND: - if xwayland_primary.get_name() == name: - return XWAYLAND.PRIMARY - if xwayland_ogui.get_name() == name: - return XWAYLAND.OGUI - return XWAYLAND.GAME - - -## Returns the name of the given xwayland display -func get_display_number(display: XWAYLAND) -> int: - var name := get_display_name(display) - var clean_name := name.replace(":", "") - if clean_name.is_valid_int(): - return clean_name.to_int() - logger.error("Unable to determine display number from name: " + name) - return 0 - - -## Returns the xwayland instance for the given display type -func get_xwayland(display: XWAYLAND) -> Xlib: - if xwaylands.size() == 0: - return null - if display == XWAYLAND.PRIMARY: - return xwayland_primary - if display == XWAYLAND.OGUI: - return xwayland_ogui - return xwayland_game - - -## Returns true if Gamescope is tracking the given display -func _has_xwayland(display: String) -> bool: - for xwayland in xwaylands: - if xwayland.get_name() == display: - return true - return false - - -## Returns true if the given xwayland instance is the primary gamescope instance -func _is_gamescope_xwayland_primary(xwayland: Xlib) -> bool: - var root_id := xwayland.get_root_window_id() - if xwayland.has_xprop(root_id, "GAMESCOPE_FOCUSED_WINDOW"): - return true - return false - - -## Returns true if the given xwayland instance is a gamescope instance -func _is_gamescope_xwayland(xwayland: Xlib) -> bool: - var root_id := xwayland.get_root_window_id() - if xwayland.has_xprop(root_id, "GAMESCOPE_CURSOR_VISIBLE_FEEDBACK"): - return true - return false - - -## Sets the given X property on the given window. -## Example: -## [codeblock] -## Gamescope._set_xprop(":0", 1234, "STEAM_INPUT", 1) -## [/codeblock] -func _set_xprop(xwayland: Xlib, window_id: int, key: String, value: int) -> int: - var msg_args := [window_id, key, value, xwayland.get_name()] - logger.debug("Setting window {0} key {1} to {2} on display {3}".format(msg_args)) - return xwayland.set_xprop(window_id, key, value) - - -## Sets the given X property with the given array of values -func _set_xprop_array(xwayland: Xlib, window_id: int, key: String, values: PackedInt32Array) -> int: - var msg_args := [window_id, key, str(values), xwayland.get_name()] - logger.debug("Setting window {0} key {1} to {2} on display {3}".format(msg_args)) - # TODO: Fix set_xprop_array and use that instead of xprop - var values_str_arr := [] - for value in values: - values_str_arr.append(str(value)) - var values_str := ",".join(values_str_arr) - var cmd := "env" - var args := ["DISPLAY="+xwayland.get_name(), "xprop", "-id", str(window_id), "-f", key, "32c", "-set", key, values_str] - return OS.execute(cmd, args) - - -## Returns the value of the given X property for the given window. Returns -## [member Xlib.ERR_XPROP_NOT_FOUND] if property doesn't exist. -func _get_xprop(xwayland: Xlib, window_id: int, key: String) -> int: - return xwayland.get_xprop(window_id, key) - - -## Removes the given X property for the given window. -func _remove_xprop(xwayland: Xlib, window_id: int, key: String) -> int: - return xwayland.remove_xprop(window_id, key) - - -## Returns an array of values for the given X property for the given window. -## Returns an empty array if property was not found. -func _get_xprop_array(xwayland: Xlib, window_id: int, key: String) -> PackedInt32Array: - return xwayland.get_xprop_array(window_id, key) diff --git a/core/global/gamescope.tres b/core/global/gamescope.tres deleted file mode 100644 index f394fbb12..000000000 --- a/core/global/gamescope.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="Resource" script_class="Gamescope" load_steps=2 format=3 uid="uid://blr55dcwc05m1"] - -[ext_resource type="Script" path="res://core/global/gamescope.gd" id="1_ipl5o"] - -[resource] -script = ExtResource("1_ipl5o") -log_level = 4 diff --git a/core/global/gamescope_test.gd b/core/global/gamescope_test.gd deleted file mode 100644 index e64b41dd3..000000000 --- a/core/global/gamescope_test.gd +++ /dev/null @@ -1,15 +0,0 @@ -extends GutTest - -var gamescope := Gamescope.new() - - -func test_float_to_long() -> void: - var to_int := gamescope._float_to_long(1.3) - gut.p(to_int) - assert_eq(to_int, 1067869798, "should be converted to a long") - - -func test_long_to_float() -> void: - var to_float := gamescope._long_to_float(1067869798) - gut.p(to_float) - assert_almost_eq(to_float, 1.3, 0.01, "should be approximately 1.3") diff --git a/core/global/keyboard_instance.gd b/core/global/keyboard_instance.gd index 172ba67f0..d0f9caa9c 100644 --- a/core/global/keyboard_instance.gd +++ b/core/global/keyboard_instance.gd @@ -58,4 +58,3 @@ func set_context(ctx: KeyboardContext) -> void: context = ctx context_changed.emit(ctx) ctx.entered.emit() - diff --git a/core/global/launch_manager.gd b/core/global/launch_manager.gd index 7d65adffc..d753d43cc 100644 --- a/core/global/launch_manager.gd +++ b/core/global/launch_manager.gd @@ -12,82 +12,135 @@ class_name LaunchManager ## periodically check on launched games to see if they have exited, or are ## opening new windows that might need attention. Example: ## [codeblock] -## const LaunchManager := preload("res://core/global/launch_manager.tres") +## var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager ## ... ## # Create a LibraryLaunchItem to run something ## var item := LibraryLaunchItem.new() ## item.command = "vkcube" ## ## # Launch the app with LaunchManager -## var running_app := LaunchManager.launch(item) +## var running_app := launch_manager.launch(item) ## ## # Get a list of running apps -## var running := LaunchManager.get_running() +## var running := launch_manager.get_running() ## print(running) ## ## # Stop an app with LaunchManager -## LaunchManager.stop(running_app) +## launch_manager.stop(running_app) ## [/codeblock] +signal all_apps_stopped() signal app_launched(app: RunningApp) signal app_stopped(app: RunningApp) signal app_switched(from: RunningApp, to: RunningApp) signal recent_apps_changed() const settings_manager := preload("res://core/global/settings_manager.tres") -const NotificationManager := preload("res://core/global/notification_manager.tres") +const notification_manager := preload("res://core/global/notification_manager.tres") -var gamescope := preload("res://core/global/gamescope.tres") as Gamescope -var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber +var gamescope := preload("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance +var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance var state_machine := preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine var in_game_state := preload("res://assets/state/states/in_game.tres") as State var in_game_menu_state := preload("res://assets/state/states/in_game_menu.tres") as State var PID: int = OS.get_process_id() +var _xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) +var _xwayland_ogui := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) +var _xwayland_game := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_GAME) var _sandbox := Sandbox.get_sandbox() var _current_app: RunningApp var _pid_to_windows := {} var _running: Array[RunningApp] = [] -var _stopping: Array[RunningApp] = [] +var _running_background: Array[RunningApp] = [] var _apps_by_pid: Dictionary = {} var _apps_by_name: Dictionary = {} var _data_dir: String = ProjectSettings.get_setting("OpenGamepadUI/data/directory") var _persist_path: String = "/".join([_data_dir, "launcher.json"]) var _persist_data: Dictionary = {"version": 1} -var _ogui_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI) +var _ogui_window_id := 0 var should_manage_overlay := true var logger := Log.get_logger("LaunchManager", Log.LEVEL.INFO) +var _focused_app_id := 0 # Connect to Gamescope signals func _init() -> void: - # When window focus changes, update the current app and gamepad profile - var on_focus_changed := func(from: int, to: int): - logger.info("Window focus changed from " + str(from) + " to: " + str(to)) - var last_app := _current_app - _current_app = _detect_running_app(to) - - logger.debug("Last app: " + str(last_app) + " current_app: " + str(_current_app)) - app_switched.emit(last_app, _current_app) - - # If the app has a gamepad profile, set it - if to != _ogui_window_id and _current_app: - set_app_gamepad_profile(_current_app) + _load_persist_data() + + # Get the window ID of OpenGamepadUI + if _xwayland_ogui: + var ogui_windows := _xwayland_ogui.get_windows_for_pid(PID) + if not ogui_windows.is_empty(): + _ogui_window_id = ogui_windows[0] + + # Listen for signals from the primary Gamescope XWayland + if _xwayland_primary: + # Debug print when the focused window changes + var on_focus_changed := func(from: int, to: int): + if from == to: + return + logger.info("Window focus changed from " + str(from) + " to: " + str(to)) + self.check_running.call_deferred() + _xwayland_primary.focused_window_updated.connect(on_focus_changed) - # If we don't want LaunchManager to manage overlay (I.E. overlay mode), return false always. - if not should_manage_overlay: + # When focused app changes, update the current app and gamepad profile + var on_focused_app_changed := func(_from: int, to: int) -> void: + if _focused_app_id == to: return + logger.debug("Focused app changed from " + str(_focused_app_id) + " to " + str(to)) + _focused_app_id = to - gamescope.focused_window_updated.connect(on_focus_changed) + # If OGUI was focused, set the global gamepad profile + if to in [gamescope.OVERLAY_GAME_ID, 0, 1]: + set_gamepad_profile("") + return - # Debug print when the focused app changed - var on_focused_app_changed := func(from: int, to: int) -> void: - logger.debug("Focused app changed from " + str(from) + " to " + str(to)) - gamescope.focused_app_updated.connect(on_focused_app_changed) + # Find the running app for the given app id + var last_app := self._current_app + var detected_app: RunningApp + for app in _running: + if app.app_id == to: + detected_app = app + + # If the running app was not launched by OpenGamepadUI, then detect it. + if not detected_app: + detected_app = _detect_running_app(to) + self._current_app = detected_app + + logger.debug("Last app: " + str(last_app) + " current_app: " + str(self._current_app)) + app_switched.emit(last_app, self._current_app) + + # If the app has a gamepad profile, set it + if self._current_app: + set_app_gamepad_profile(self._current_app) + _xwayland_primary.focused_app_updated.connect(on_focused_app_changed) + + # Listen for when focusable apps change + var on_focusable_apps_changed := func(from: PackedInt64Array, to: PackedInt64Array): + if from == to: + return + logger.debug("Focusable apps changed from", from, "to", to) + self.check_running.call_deferred() + # If focusable apps has changed and the currently focused app no longer exists, + # remove the manual focus + var baselayer_app := _xwayland_primary.baselayer_app + to.append(_xwayland_primary.focused_app) + if baselayer_app > 0 and not baselayer_app in to: + _xwayland_primary.remove_baselayer_app() + _xwayland_primary.focusable_apps_updated.connect(on_focusable_apps_changed) + + # Listen for signals from the secondary Gamescope XWayland + if _xwayland_game: + # Listen for window created/destroyed events + var on_window_created := func(window_id: int): + logger.debug("Window created:", window_id) + self.check_running.call_deferred() + _xwayland_game.window_created.connect(on_window_created) # Whenever the in-game state is entered, set the gamepad profile - var on_game_state_entered := func(from: State): + var on_game_state_entered := func(_from: State): if _current_app: set_app_gamepad_profile(_current_app) @@ -95,8 +148,9 @@ func _init() -> void: if not should_manage_overlay: return - logger.debug("Enabling STEAM_OVERLAY atom") - gamescope.set_overlay(_ogui_window_id, 0) + if _xwayland_ogui: + logger.debug("Enabling STEAM_OVERLAY atom") + _xwayland_ogui.set_overlay(_ogui_window_id, 0) var on_game_state_exited := func(_to: State): # Set the gamepad profile to the global profile @@ -106,11 +160,13 @@ func _init() -> void: if not should_manage_overlay: return - logger.debug("Disabling STEAM_OVERLAY atom") - gamescope.set_overlay(_ogui_window_id, 1) + if _xwayland_ogui: + logger.debug("Disabling STEAM_OVERLAY atom") + _xwayland_ogui.set_overlay(_ogui_window_id, 1) in_game_state.state_entered.connect(on_game_state_entered) in_game_state.state_exited.connect(on_game_state_exited) + set_gamepad_profile("") # Loads persistent data like recent games launched, etc. @@ -137,9 +193,39 @@ func _save_persist_data(): file.flush() -## Launches the given command on the target xwayland display. Returns a PID -## of the launched process. +## Launches the given application and switches to the in-game state. Returns a +## [RunningApp] instance of the application. func launch(app: LibraryLaunchItem) -> RunningApp: + var running_app := _launch(app) + + # Add the running app to our list and change to the IN_GAME state + _add_running(running_app) + state_machine.set_state([in_game_state]) + _update_recent_apps(app) + + return running_app + + +## Launches the given app in the background. Returns the [RunningApp] instance. +func launch_in_background(app: LibraryLaunchItem) -> RunningApp: + # Start the application + var running_app := _launch(app) + + # Listen for app state changes + var on_app_state_changed := func(from: RunningApp.STATE, to: RunningApp.STATE): + if to != RunningApp.STATE.STOPPED: + return + logger.debug("Cleaning up pid {0}".format([running_app.pid])) + _running_background.erase(running_app) + logger.debug("Currently running background apps:", _running_background) + running_app.state_changed.connect(on_app_state_changed) + _running_background.append(running_app) + + return running_app + + +## Launches the given app +func _launch(app: LibraryLaunchItem) -> RunningApp: var cmd: String = app.command var args: PackedStringArray = app.args var env: Dictionary = app.env.duplicate() @@ -150,28 +236,30 @@ func launch(app: LibraryLaunchItem) -> RunningApp: # Override any parameters that may be in the user's config for this game var section := ".".join(["game", app.name.to_lower()]) var cmd_key := ".".join(["command", app._provider_id]) - var user_cmd = settings_manager.get_value(section, cmd_key) - if user_cmd and user_cmd is String: + var user_cmd = settings_manager.get_value(section, cmd_key, "") + if user_cmd and user_cmd is String and not (user_cmd as String).is_empty(): cmd = user_cmd var args_key := ".".join(["args", app._provider_id]) - var user_args = settings_manager.get_value(section, args_key) - if user_args and user_args is PackedStringArray: + var user_args = settings_manager.get_value(section, args_key, PackedStringArray()) + if user_args and user_args is PackedStringArray and not (user_args as PackedStringArray).is_empty(): args = user_args var cwd_key := ".".join(["cwd", app._provider_id]) - var user_cwd = settings_manager.get_value(section, cwd_key) - if user_cwd and user_cwd is String: + var user_cwd = settings_manager.get_value(section, cwd_key, "") + if user_cwd and user_cwd is String and not (user_cwd as String).is_empty(): cwd = user_cwd var env_key := ".".join(["env", app._provider_id]) - var user_env = settings_manager.get_value(section, env_key) - if user_env and user_env is Dictionary: + var user_env = settings_manager.get_value(section, env_key, {}) + if user_env and user_env is Dictionary and not (user_env as Dictionary).is_empty(): env = user_env var sandboxing_key := ".".join(["use_sandboxing", app._provider_id]) - var use_sandboxing := settings_manager.get_value(section, sandboxing_key, true) as bool + var use_sandboxing := settings_manager.get_value(section, sandboxing_key, false) as bool # Set the display environment if one was not set. if not "DISPLAY" in env: - env["DISPLAY"] = gamescope.get_display_name(Gamescope.XWAYLAND.GAME) - var display := env["DISPLAY"] as String + if _xwayland_game: + env["DISPLAY"] = _xwayland_game.name + else: + env["DISPLAY"] = "" # Set the OGUI ID environment variable env["OGUI_ID"] = app.name @@ -196,18 +284,9 @@ func launch(app: LibraryLaunchItem) -> RunningApp: logger.info("Launching game with command: {0} {1}".format([exec, str(command)])) # Launch the application process - var pid = Reaper.create_process(exec, command) - logger.info("Launched with PID: {0}".format([pid])) - - # Create a running app instance - var running_app := _make_running_app(app, pid, display) - running_app.command = command - running_app.environment = env - - # Add the running app to our list and change to the IN_GAME state - _add_running(running_app) - state_machine.set_state([in_game_state]) - _update_recent_apps(app) + var running_app := RunningApp.spawn(app, env, exec, command) + logger.info("Launched with PID: {0}".format([running_app.pid])) + return running_app @@ -254,6 +333,11 @@ func get_running() -> Array[RunningApp]: return _running.duplicate() +## Returns a list of currently running background apps +func get_running_background() -> Array[RunningApp]: + return _running_background.duplicate() + + ## Returns the running app from the given window id func get_running_from_window_id(window_id: int) -> RunningApp: for app in _running: @@ -289,9 +373,12 @@ func set_app_gamepad_profile(app: RunningApp) -> void: # Check to see if this game has any gamepad profiles. If so, set our # gamepads to use them. var section := ".".join(["game", app.launch_item.name.to_lower()]) - var profile_path = settings_manager.get_value(section, "gamepad_profile", "") + var profile_path := settings_manager.get_value(section, "gamepad_profile", "") as String var profile_gamepad := settings_manager.get_value(section, "gamepad_profile_target", "") as String - logger.debug("Using profile '" + profile_path + "' with gamepad type '" + profile_gamepad + "'") + if profile_path.is_empty(): + logger.debug("Using global gamepad profile") + else: + logger.debug("Using profile '" + profile_path + "' with gamepad type '" + profile_gamepad + "'") set_gamepad_profile(profile_path, profile_gamepad) @@ -305,26 +392,10 @@ func set_gamepad_profile(path: String, target_gamepad: String = "") -> void: # If no profile was specified, unset the gamepad profiles if path == "": # Try check to see if there is a global gamepad setting - var profile_path := settings_manager.get_value("input", "gamepad_profile", InputPlumber.DEFAULT_GLOBAL_PROFILE) as String - if not profile_path.ends_with(".json") or not FileAccess.file_exists(profile_path): - profile_path = InputPlumber.DEFAULT_GLOBAL_PROFILE - logger.info("Loading global gamepad profile: " + profile_path) - - for gamepad in input_plumber.composite_devices: - gamepad.target_modify_profile(profile_path, profile_modifier) - - # Set the target gamepad if one was specified - if not target_gamepad.is_empty(): - var target_devices := [target_gamepad, "keyboard", "mouse"] - match target_gamepad: - "xb360", "xbox-series", "xbox-elite", "gamepad": - target_devices.append("touchpad") - _: - logger.debug(target_gamepad, "needs no additional target devices.") - logger.info("Setting target devices to: ", target_devices) - gamepad.set_target_devices(target_devices) - - return + path = settings_manager.get_value("input", "gamepad_profile", InputPlumber.DEFAULT_GLOBAL_PROFILE) as String + # Verify we loaded a valid profile, or fallback. + if not path.ends_with(".json") or not FileAccess.file_exists(path): + path = InputPlumber.DEFAULT_GLOBAL_PROFILE logger.info("Loading gamepad profile: " + path) if not FileAccess.file_exists(path): @@ -338,12 +409,12 @@ func set_gamepad_profile(path: String, target_gamepad: String = "") -> void: return # TODO: Save profiles for individual controllers? - for gamepad in input_plumber.composite_devices: - gamepad.target_modify_profile(path, profile_modifier) + for gamepad in input_plumber.get_composite_devices(): + InputPlumber.load_target_modified_profile(gamepad, path, profile_modifier) # Set the target gamepad if one was specified if not target_gamepad.is_empty(): - var target_devices := [target_gamepad, "keyboard", "mouse"] + var target_devices := PackedStringArray([target_gamepad, "keyboard", "mouse"]) match target_gamepad: "xb360", "xbox-series", "xbox-elite", "gamepad": target_devices.append("touchpad") @@ -352,12 +423,12 @@ func set_gamepad_profile(path: String, target_gamepad: String = "") -> void: logger.info("Setting target devices to: ", target_devices) gamepad.set_target_devices(target_devices) - var notify := Notification.new("Using gamepad profile: " + profile.name) - NotificationManager.show(notify) + #var notify := Notification.new("Using gamepad profile: " + profile.name) + #notification_manager.show(notify) ## Sets the given running app as the current app -func set_current_app(app: RunningApp, switch_baselayer: bool = true) -> void: +func set_current_app(app: RunningApp, _switch_baselayer: bool = true) -> void: if app == null: return app.grab_focus() @@ -386,71 +457,79 @@ func _add_running(app: RunningApp): ## Called when a running app's state changes -func _on_app_state_changed(_from: RunningApp.STATE, to: RunningApp.STATE, app: RunningApp) -> void: +func _on_app_state_changed(from: RunningApp.STATE, to: RunningApp.STATE, app: RunningApp) -> void: + logger.debug("App state changed from", from, "to", to, "for app:", app) if to != RunningApp.STATE.STOPPED: return + logger.debug("Cleaning up pid {0}".format([app.pid])) _remove_running(app) + logger.debug("Currently running apps:", _running) if state_machine.has_state(in_game_state) and _running.size() == 0: logger.info("No more apps are running. Removing in-game state.") - gamescope.remove_baselayer_window() + _xwayland_primary.remove_baselayer_window() state_machine.remove_state(in_game_state) state_machine.remove_state(in_game_menu_state) + all_apps_stopped.emit() # Removes the given PID from our list of running apps func _remove_running(app: RunningApp): - logger.info("Cleaning up pid {0}".format([app.pid])) + logger.info("Removing app", app, "from running apps.") _running.erase(app) _apps_by_name.erase(app.launch_item.name) _apps_by_pid.erase(app.pid) - app_stopped.emit(app) # Checks for running apps and updates our state accordingly func check_running() -> void: # Find the root window - var root_id := gamescope.get_root_window_id(Gamescope.XWAYLAND.GAME) + if not _xwayland_game: + return + var root_id := _xwayland_game.root_window_id if root_id < 0: return - - # Update the Gamescope state - gamescope.update() - + # Update our view of running processes and what windows they have _update_pids(root_id) - + # Update the state of all running apps for app in _running: app.update() + for app in _running_background: + app.update() # Updates our mapping of PIDs to Windows. This gives us a good view of what # processes are running, and what windows they have. func _update_pids(root_id: int): + if not _xwayland_game: + return var pids := {} - var all_windows := gamescope.get_all_windows(root_id, Gamescope.XWAYLAND.GAME) + var all_windows := _xwayland_game.get_all_windows(root_id) for window in all_windows: - var pid := gamescope.get_window_pid(window, Gamescope.XWAYLAND.GAME) - if not pid in pids: - pids[pid] = [] - pids[pid].append(window) + var window_pids := _xwayland_game.get_pids_for_window(window) + for window_pid in window_pids: + if not window_pid in pids: + pids[window_pid] = [] + pids[window_pid].append(window) _pid_to_windows = pids # Below functions detect launched game from other processes # Returns the process ID func _get_pid_from_focused_window(window_id: int) -> int: - var pid := -1 - logger.debug(str(gamescope.get_xwayland(Gamescope.XWAYLAND.GAME).list_xprops(window_id))) - pid = gamescope.get_xwayland(Gamescope.XWAYLAND.GAME).get_xprop(window_id, "_NET_WM_PID") - logger.debug("PID: " + str(pid)) - return pid + if not _xwayland_game: + return -1 + var window_pids := _xwayland_game.get_pids_for_window(window_id) + if window_pids.is_empty(): + return -1 + return window_pids[0] # First method, try to get the name from the window_id using gamescope. func _get_app_name_from_window_id(window_id: int) -> String: - return gamescope.get_window_name(window_id) + return _xwayland_game.get_window_name(window_id) # Last resort, try to use the parent dir of the executable from /proc/pid/cwd to return @@ -459,22 +538,26 @@ func _get_app_name_from_proc(pid: int) -> String: var process_name: String var path := "/proc/" + str(pid) + "/cwd" var output := [] - var exit_code := OS.execute("ls", ["-l", path], output) + var _exit_code := OS.execute("ls", ["-l", path], output) var process_path: PackedStringArray = output[0].strip_edges().split("/") process_name = process_path[process_path.size()-1] return process_name -# Primary nw app ID method. Identifies the running app from steam's library.vdf +# Primary new app ID method. Identifies the running app from steam's library.vdf # and appmanifest_.acf files. func _get_name_from_steam_library() -> String: - var missing_app_id := gamescope.get_focused_app() + var missing_app_id := -1 + if _xwayland_primary: + missing_app_id = _xwayland_primary.focused_app logger.debug("Found unclaimed app id: " +str(missing_app_id)) var steam_library_path := OS.get_environment("HOME") +"/.steam/steam" var library_data := _parse_data_from_steam_file(steam_library_path + "/steamapps/libraryfolders.vdf") for library in library_data: logger.debug("Library: " + library + " : " + str(library_data[library])) + if not "apps" in library_data[library]: + continue if library_data[library]["apps"].has(str(missing_app_id)): var app_path : String = library_data[library]["path"] +"/steamapps/appmanifest_" + str(missing_app_id) + ".acf" logger.debug("Found app ID in a steam library:" + app_path) @@ -494,8 +577,14 @@ func _is_app_id_running(app_id) -> bool: # Identifies the running app from the given window_id. If none is found, # creates a new RunningApp instance. -func _detect_running_app(window_id: int) -> RunningApp: +func _detect_running_app(app_id: int) -> RunningApp: logger.debug("No known running app in focused window. Attempting to detect the running app.") + + # Get the currently focused window id + var window_id := _xwayland_primary.focused_window + if window_id == _ogui_window_id: + return null + var app_name: String # Check if this window ID is a child of an existing RunningApp @@ -531,16 +620,16 @@ func _detect_running_app(window_id: int) -> RunningApp: return null logger.debug("Found app name : " + app_name) - return _make_running_app_from_process(app_name, pid, window_id) + return _make_running_app_from_process(app_name, pid, window_id, app_id) # Creates a new RunningApp instance from a given name, PID, and window_id. Used # when an app launch is detcted that wasn't launched by an OGUI library. -func _make_running_app_from_process(name: String, pid: int, window_id: int) -> RunningApp: +func _make_running_app_from_process(name: String, pid: int, window_id: int, app_id: int) -> RunningApp: logger.debug("Creating running app from process") # Create a dummy LibraryLaunchItem to make our RunningApp. - var lauch_dict = { + var lauch_dict := { "_id": "", "_provider_id": "", "provider_app_id": "", @@ -553,8 +642,11 @@ func _make_running_app_from_process(name: String, pid: int, window_id: int) -> R } var launch_item: LibraryLaunchItem = LibraryLaunchItem.from_dict(lauch_dict) - var display:= gamescope.get_display_name(Gamescope.XWAYLAND.GAME) + var display := "" + if _xwayland_game: + display = _xwayland_game.name var running_app: RunningApp = _make_running_app(launch_item, pid, display) + running_app.app_id = app_id running_app.window_id = window_id running_app.state = RunningApp.STATE.RUNNING running_app.is_ogui_managed = false diff --git a/core/global/library_manager.gd b/core/global/library_manager.gd index 42845f05a..38975b3c1 100644 --- a/core/global/library_manager.gd +++ b/core/global/library_manager.gd @@ -198,6 +198,7 @@ func remove_library_launch_item(library_id: String, name: String) -> void: ## Loads the launch items from the given library func load_library(library_id: String) -> void: + logger.debug("Loading library from provider:", library_id) var library := get_library_by_id(library_id) var items: Array = await library.get_library_launch_items() for i in items: diff --git a/core/global/platform.gd b/core/global/platform.gd index 84af60c25..beab13ec1 100644 --- a/core/global/platform.gd +++ b/core/global/platform.gd @@ -44,6 +44,7 @@ enum PLATFORM { STEAMOS, MANJARO, ARCH_LIKE, + NIXOS, } var hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager @@ -51,7 +52,7 @@ var hardware_manager := load("res://core/systems/hardware/hardware_manager.tres" ## Detected Operating System information var os_info := _detect_os() ## The OS platform provider detected -var os: PlatformProvider +var os: OSPlatform ## The hardware platform provider detected var platform: PlatformProvider var logger := Log.get_logger("Platform", Log.LEVEL.INFO) @@ -127,6 +128,8 @@ func _init() -> void: os = load("res://core/platform/os/chimeraos.tres") if PLATFORM.MANJARO in flags: os = load("res://core/platform/os/manjaro.tres") + if PLATFORM.NIXOS in flags: + os = load("res://core/platform/os/nixos.tres") if os: for action in os.startup_actions: @@ -286,6 +289,8 @@ func _read_os() -> Array[PLATFORM]: flags.append(PLATFORM.MANJARO) if os_info.id_like == "arch": flags.append(PLATFORM.ARCH_LIKE) + if os_info.id == "nixos": + flags.append(PLATFORM.NIXOS) return flags diff --git a/core/global/plugin_loader.gd b/core/global/plugin_loader.gd index 6b244c791..2da34a3fa 100644 --- a/core/global/plugin_loader.gd +++ b/core/global/plugin_loader.gd @@ -18,7 +18,7 @@ class_name PluginLoader ## pack. const PLUGIN_STORE_URL = "https://raw.githubusercontent.com/ShadowBlip/OpenGamepadUI-plugins/main/plugins.json" -const PLUGIN_API_VERSION = "1.0.0" +const PLUGIN_API_VERSION = "1.1.0" const PLUGINS_DIR = "user://plugins" const LOADED_PLUGINS_DIR = "res://plugins" const REQUIRED_META = ["plugin.name", "plugin.version", "plugin.min-api-version", "entrypoint"] @@ -545,14 +545,34 @@ func _is_plugin_upgradable(plugin_id: String, store_db: Dictionary) -> bool: # Check if we've already found this is upgradable if plugin_id in plugins_upgradable: return false - var current_version = plugins[plugin_id]["plugin.version"] - var new_version = store_db[plugin_id]["plugin.version"] - if SemanticVersion.is_greater(new_version, current_version): - logger.info("Plugin update available: {0}.".format([plugin_id])) - plugin_upgradable.emit(plugin_id, update_type.UPDATE) - plugins_upgradable.append(plugin_id) - return true - return false + # Validate the correct data exists + if not plugin_id in plugins or not "plugin.version" in plugins[plugin_id]: + return false + if not plugin_id in store_db or not "plugin.version" in store_db[plugin_id]: + return false + var current_version := plugins[plugin_id]["plugin.version"] as String + var new_version := store_db[plugin_id]["plugin.version"] as String + + # If the plugin version in the store is not greater than the currently + # installed version, then this plugin is not upgradable. + if not SemanticVersion.is_greater(new_version, current_version): + logger.debug("Plugin", plugin_id, "with version", current_version, "is not greater than", new_version) + return false + + # Check to see if the new plugin is compatible with the OpenGamepadUI's current + # plugin API. + if not "plugin.min-api-version" in store_db[plugin_id]: + logger.error("No minimum api version specified in store for plugin:", plugin_id) + return false + var plugin_min_api_version := store_db[plugin_id]["plugin.min-api-version"] as String + if not SemanticVersion.is_feature_compatible(plugin_min_api_version, PLUGIN_API_VERSION): + logger.info("Plugin", plugin_id, "requires plugin API version", plugin_min_api_version, "but current plugin API version is", PLUGIN_API_VERSION) + return false + + logger.info("Plugin update available: {0}.".format([plugin_id])) + plugin_upgradable.emit(plugin_id, update_type.UPDATE) + plugins_upgradable.append(plugin_id) + return true # Checks if a given plugin is already in the plugin database @@ -580,6 +600,7 @@ func filter_by_tag(plugins: Dictionary, tag: String) -> Array[String]: logger.debug(plugin["plugin.id"] + " will not be loaded. " + str(tags)) return filtered_ids + # Sets the filters for the plugin list func set_plugin_filters(filters: Array[Callable]) -> void: logger.debug("Setting plugin filters.") diff --git a/core/global/settings_manager.gd b/core/global/settings_manager.gd index bd4cdcdd1..6f456b628 100644 --- a/core/global/settings_manager.gd +++ b/core/global/settings_manager.gd @@ -39,6 +39,8 @@ func get_value(section: String, key: String, default: Variant = null) -> Variant func get_library_value(item: LibraryItem, key: String, default: Variant = null) -> Variant: + if not item: + return null var section := ".".join(["game", item.name.to_lower()]) return get_value(section, key, default) diff --git a/core/platform/actions/start_inputplumber.gd b/core/platform/actions/start_inputplumber.gd index 1cb668911..ca54626b1 100644 --- a/core/platform/actions/start_inputplumber.gd +++ b/core/platform/actions/start_inputplumber.gd @@ -6,6 +6,6 @@ const INPUT_PLUMBER_PATH := "/usr/share/opengamepadui/scripts/manage_input" func execute() -> void: logger.info("Starting InputPlumber") - var cmd := CommandSync.new(INPUT_PLUMBER_PATH, ["startInputPlumber"]) - if cmd.execute() != OK: + var cmd := Command.create(INPUT_PLUMBER_PATH, ["startInputPlumber"]) + if cmd.execute_blocking() != OK: logger.warn("Failed to start InputPlumber: " + cmd.stdout) diff --git a/core/platform/actions/start_powerstation.gd b/core/platform/actions/start_powerstation.gd index 18ee2e9ac..19e05aa23 100644 --- a/core/platform/actions/start_powerstation.gd +++ b/core/platform/actions/start_powerstation.gd @@ -6,6 +6,6 @@ const POWERTOOLS_PATH := "/usr/share/opengamepadui/scripts/manage_input" func execute() -> void: logger.info("Starting PowerStation") - var cmd := CommandSync.new(POWERTOOLS_PATH, ["startPowerStation"]) - if cmd.execute() != OK: + var cmd := Command.create(POWERTOOLS_PATH, ["startPowerStation"]) + if cmd.execute_blocking() != OK: logger.warn("Failed to start PowerStation: " + cmd.stdout) diff --git a/core/platform/actions/turbo_takeover.gd b/core/platform/actions/turbo_takeover.gd index 0c9594d6d..761444dd9 100644 --- a/core/platform/actions/turbo_takeover.gd +++ b/core/platform/actions/turbo_takeover.gd @@ -3,7 +3,8 @@ class_name ActionTurboTakeover func execute() -> void: - var cmd := Command.new("/usr/share/opengamepadui/scripts/manage_input", ["turbo_takeover", "1"]) - var code := await cmd.execute() as int + var cmd := Command.create("/usr/share/opengamepadui/scripts/manage_input", ["turbo_takeover", "1"]) + cmd.execute() + var code := await cmd.finished as int if code != OK: - logger.warn("Unable to take over turbo button: " + cmd.stdout) + logger.warn("Unable to take over turbo button: " + cmd.stdout + " " + cmd.stderr) diff --git a/core/platform/handheld/handheld_platform.gd b/core/platform/handheld/handheld_platform.gd index 03dc639b4..b0740adc6 100644 --- a/core/platform/handheld/handheld_platform.gd +++ b/core/platform/handheld/handheld_platform.gd @@ -11,6 +11,6 @@ class_name HandheldPlatform @export_category("Controller Icons") ## Image of the device as a diagram to show in the gamepad configuration menus. -@export var diagram: Texture2D = load("res://addons/controller_icons/assets/xboxone/diagram_simple.png") +@export var diagram: Texture2D = load("res://assets/images/gamepad/xbox/XboxOne_Diagram_Simple.png") ## Custom icon images to use when displaying buttons/joysticks in the interface @export var icon_mappings: Array[HandheldIconMapping] diff --git a/core/platform/os/nixos.gd b/core/platform/os/nixos.gd new file mode 100644 index 000000000..f76e9b934 --- /dev/null +++ b/core/platform/os/nixos.gd @@ -0,0 +1,23 @@ +extends OSPlatform +class_name PlatformNixOS + + +func _init() -> void: + logger.set_name("PlatformNixOS") + logger.set_level(Log.LEVEL.INFO) + logger.info("Detected NixOS platform") + + +## NixOS typically cannot execute regular binaries, so downloaded binaries will +## be run with 'steam-run'. +func get_binary_compatibility_cmd(cmd: String, args: PackedStringArray) -> Array[String]: + # Hack for steam plugin running steamcmd on NixOS + var command: Array[String] = [] + if not cmd.ends_with("steamcmd.sh"): + return command + + command.push_back("steam-run") + command.push_back(cmd) + command.append_array(args) + + return command diff --git a/core/platform/os/nixos.tres b/core/platform/os/nixos.tres new file mode 100644 index 000000000..cc237b6e4 --- /dev/null +++ b/core/platform/os/nixos.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="PlatformNixOS" load_steps=3 format=3 uid="uid://byxeetkb11b3e"] + +[ext_resource type="Script" path="res://core/platform/os/nixos.gd" id="1_h88kc"] +[ext_resource type="Script" path="res://core/platform/actions/platform_action.gd" id="2_tc12v"] + +[resource] +script = ExtResource("1_h88kc") +name = "" +startup_actions = Array[ExtResource("2_tc12v")]([]) +shutdown_actions = Array[ExtResource("2_tc12v")]([]) diff --git a/core/platform/os/os_platform.gd b/core/platform/os/os_platform.gd index 7901ac126..17b66f330 100644 --- a/core/platform/os/os_platform.gd +++ b/core/platform/os/os_platform.gd @@ -4,3 +4,11 @@ class_name OSPlatform @export_category("Images") @export var logo: Texture2D ## Logo of the OS + + +## If the OS requires running regular binaries through a compatibility tool, +## this method should return the given command/args prepended with the compatibility +## tool to use. +func get_binary_compatibility_cmd(cmd: String, args: PackedStringArray) -> Array[String]: + var result: Array[String] = [] + return result diff --git a/core/systems/battery.gd b/core/systems/battery.gd deleted file mode 100644 index 70c4e7a90..000000000 --- a/core/systems/battery.gd +++ /dev/null @@ -1,87 +0,0 @@ -@icon("res://assets/icons/battery-charging.svg") -extends RefCounted -class_name Battery - -const power_supply_dir = "/sys/class/power_supply" -const icon_charging = preload("res://assets/ui/icons/battery-charging.svg") -const icon_full = preload("res://assets/ui/icons/battery-full.svg") -const icon_high = preload("res://assets/ui/icons/battery-75.svg") -const icon_half = preload("res://assets/ui/icons/battery-half.svg") -const icon_low = preload("res://assets/ui/icons/battery-low.svg") -const icon_empty = preload("res://assets/ui/icons/battery-empty.svg") - -enum STATUS { - NONE, - DISCHARGING, - NOT_CHARGING, - CHARGING, - FULL, -} - -var logger := Log.get_logger("Battery") - -# Finds the battery path. If none is found, returns null. -static func find_battery_path() -> Variant: - var power_dir: DirAccess = DirAccess.open(power_supply_dir) - var devices: PackedStringArray = power_dir.get_directories() - var battery_dir: String = "" - for folder in devices: - if folder.begins_with("BAT"): - battery_dir = folder - break - if battery_dir == "": - return "" - - return "/".join([power_supply_dir, battery_dir]) - - -# Returns the current battery capacity as a percentage -static func get_capacity(battery: String) -> int: - if battery == "": - return -1 - var capacity_file: String = "/".join([battery, "capacity"]) - var file: FileAccess = FileAccess.open(capacity_file, FileAccess.READ) - var bytes: PackedByteArray = file.get_buffer(100) - var str: String = bytes.get_string_from_ascii().strip_edges() - if not str.is_valid_int(): - return -1 - return str.to_int() - - -## Returns the status of the given battery -static func get_status(battery: String) -> STATUS: - if battery == "": - return STATUS.NONE - var status_file: String = "/".join([battery, "status"]) - - var output := [] - var code := OS.execute("cat", [status_file], output) - if code != OK: - return STATUS.NONE - var status := (output[0] as String).strip_edges() - - match status: - "Discharging": - return STATUS.DISCHARGING - "Not charging": - return STATUS.NOT_CHARGING - "Charging": - return STATUS.CHARGING - "Full": - return STATUS.FULL - return STATUS.NONE - - -## Returns the texture reflecting the given battery capacity -static func get_capacity_texture(capacity: int, status: STATUS = STATUS.NONE) -> Texture2D: - if status > STATUS.NOT_CHARGING: - return icon_charging - if capacity >= 90: - return icon_full - if capacity >= 65: - return icon_high - if capacity >= 40: - return icon_half - if capacity >= 20: - return icon_low - return icon_empty diff --git a/core/systems/bluetooth/bluetooth_manager.gd b/core/systems/bluetooth/bluetooth_manager.gd index 28cd21ae7..b4561eb58 100644 --- a/core/systems/bluetooth/bluetooth_manager.gd +++ b/core/systems/bluetooth/bluetooth_manager.gd @@ -1,188 +1,16 @@ @icon("res://assets/editor-icons/bluetooth.svg") -extends Resource +extends Node class_name BluetoothManager ## BluetoothManager interfaces with the bluetooth system ## -## This class uses [DBusManager] to communicate with the bluetooth service -## over DBus. +## This node is used to drive the bluetooth instance forward by calling its +## 'process()' method every frame to dispatch signals. -const BLUEZ_BUS := "org.bluez" -const BLUES_PREFIX := "/org/bluez" +@export var instance: BluezInstance = load("res://core/systems/bluetooth/bluetooth_manager.tres") -const IFACE_ADAPTER := "org.bluez.Adapter1" -const IFACE_DEVICE := "org.bluez.Device1" -var dbus := load("res://core/global/dbus_system.tres") as DBusManager - - -## Returns true if bluetooth can be used on this system -func supports_bluetooth() -> bool: - return dbus.bus_exists(BLUEZ_BUS) - - -## Returns the bluetooth adapter with the given name. -func get_adapter(adapter_name: String = "hci0") -> Adapter: - var adapter_path := "/".join([BLUES_PREFIX, adapter_name]) - var proxy := dbus.create_proxy(BLUEZ_BUS, adapter_path) - var adapter := Adapter.new(proxy) - - return adapter - - -## Return a list of currently discovered devices -func get_discovered_devices() -> Array[Device]: - var devices: Array[Device] = [] - var objects := dbus.get_managed_objects(BLUEZ_BUS, "/") - - # Loop through all objects on the bus - for obj in objects: - # Skip any DBus objects that aren't devices - if not obj.has_interface_attr(IFACE_DEVICE, "Address"): - continue - - # Create a bluetooth Device from this object - var proxy := dbus.create_proxy(BLUEZ_BUS, obj.path) - var device := Device.new(proxy) - - devices.append(device) - - return devices - - -## Container for a bluetooth adapter -## https://github.com/luetzel/bluez/blob/master/doc/adapter-api.txt -class Adapter: - signal updated - var _proxy: DBusManager.Proxy - var address: String: - get: - var property = _proxy.get_property(IFACE_ADAPTER, "Address") - if not property is String: - return "" - return property - var name: String: - get: - var property = _proxy.get_property(IFACE_ADAPTER, "Name") - if not property is String: - return "" - return property - var powered: bool: - set(v): - _proxy.set_property(IFACE_ADAPTER, "Powered", v) - get: - var property = _proxy.get_property(IFACE_ADAPTER, "Powered") - if not property is bool: - return false - return property - var discovering: bool: - get: - var property = _proxy.get_property(IFACE_ADAPTER, "Discovering") - if not property is bool: - return false - return property - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - func start_discovery() -> void: - _proxy.call_method(IFACE_ADAPTER, "StartDiscovery") - - func stop_discovery() -> void: - _proxy.call_method(IFACE_ADAPTER, "StopDiscovery") - - -## Container for a bluetooth device -## https://github.com/luetzel/bluez/blob/master/doc/device-api.txt -class Device: - signal connection_changed(is_connected: bool) - signal paired_changed(is_paired: bool) - signal updated - var _proxy: DBusManager.Proxy - var adapter: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Adapter") - if not property is String: - return "" - return property - var address: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Address") - if not property is String: - return "" - return property - var alias: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Alias") - if not property is String: - return "" - return property - var name: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Name") - if not property is String: - return "" - return property - var icon: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Icon") - if not property is String: - return "" - return property - var paired: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Paired") - if not property is bool: - return false - return property - var connected: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Connected") - if not property is bool: - return false - return property - var trusted: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Trusted") - if not property is bool: - return false - return property - var blocked: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Blocked") - if not property is bool: - return false - return property - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - if "Connected" in props: - connection_changed.emit(props["Connected"]) - if "Paired" in props: - paired_changed.emit(props["Paired"]) - - func connect_to() -> void: - _proxy.call_method(IFACE_DEVICE, "Connect") - - func disconnect_from() -> void: - _proxy.call_method(IFACE_DEVICE, "Disconnect") - - func connect_profile(uuid: String) -> void: - _proxy.call_method(IFACE_DEVICE, "ConnectProfile", [uuid], "s") - - func disconnect_profile(uuid: String) -> void: - _proxy.call_method(IFACE_DEVICE, "DisconnectProfile", [uuid], "s") - - func pair() -> void: - _proxy.call_method(IFACE_DEVICE, "Pair") - - func cancel_pairing() -> void: - _proxy.call_method(IFACE_DEVICE, "CancelPairing") +func _process(_delta: float) -> void: + if not instance: + return + instance.process() diff --git a/core/systems/bluetooth/bluetooth_manager.tres b/core/systems/bluetooth/bluetooth_manager.tres index 3481770fc..f7b63bfc6 100644 --- a/core/systems/bluetooth/bluetooth_manager.tres +++ b/core/systems/bluetooth/bluetooth_manager.tres @@ -1,6 +1,3 @@ -[gd_resource type="Resource" script_class="BluetoothManager" load_steps=2 format=3 uid="uid://cyra53o28hppt"] - -[ext_resource type="Script" path="res://core/systems/bluetooth/bluetooth_manager.gd" id="1_q2n5i"] +[gd_resource type="BluezInstance" format=3 uid="uid://peeajgfcx2fw"] [resource] -script = ExtResource("1_q2n5i") diff --git a/core/systems/bluetooth/bluetooth_manager_test.gd b/core/systems/bluetooth/bluetooth_manager_test.gd index 8d692916f..da46b3768 100644 --- a/core/systems/bluetooth/bluetooth_manager_test.gd +++ b/core/systems/bluetooth/bluetooth_manager_test.gd @@ -1,32 +1,33 @@ extends GutTest -var bluetooth := load("res://core/systems/bluetooth/bluetooth_manager.tres") as BluetoothManager +var bluetooth := load("res://core/systems/bluetooth/bluetooth_manager.tres") as BluezInstance func before_all() -> void: - if not bluetooth.supports_bluetooth(): + if not bluetooth.is_running(): @warning_ignore("unsafe_method_access") gut.p("Bluetooth is not supported") return func test_get_adapter() -> void: - if not bluetooth.supports_bluetooth(): + if not bluetooth.is_running(): pass_test("Bluetooth not supported, skipping") return - var adapter := bluetooth.get_adapter() - if not adapter: + var adapters := bluetooth.get_adapters() + if adapters.is_empty(): pass_test("No bluetooth adapter found, skipping") pass_test("Skipping") func test_discovery() -> void: - if not bluetooth.supports_bluetooth(): + if not bluetooth.is_running(): pass_test("Bluetooth not supported, skipping") return - var adapter := bluetooth.get_adapter() - if not adapter: + var adapters := bluetooth.get_adapters() + if adapters.is_empty(): pass_test("No bluetooth adapter found, skipping") + var adapter := adapters[0] as BluetoothAdapter adapter.start_discovery() await wait_seconds(3, "waiting for discovery") diff --git a/core/systems/debug/log_manager.gd b/core/systems/debug/log_manager.gd index 944a0935e..386bc94f9 100644 --- a/core/systems/debug/log_manager.gd +++ b/core/systems/debug/log_manager.gd @@ -88,4 +88,3 @@ func get_available_loggers() -> PackedStringArray: var logger_names := loggers_by_name.keys() mutex.unlock() return logger_names - diff --git a/core/systems/disks/disk_manager.gd b/core/systems/disks/disk_manager.gd new file mode 100644 index 000000000..281f00395 --- /dev/null +++ b/core/systems/disks/disk_manager.gd @@ -0,0 +1,11 @@ +extends Node +class_name DiskManager + + +@export var instance: UDisks2Instance = load("res://core/systems/disks/disk_manager.tres") as UDisks2Instance + + +func _process(_delta: float) -> void: + if not instance: + return + instance.process() diff --git a/core/systems/disks/disk_manager.tres b/core/systems/disks/disk_manager.tres new file mode 100644 index 000000000..2856e2ff1 --- /dev/null +++ b/core/systems/disks/disk_manager.tres @@ -0,0 +1,3 @@ +[gd_resource type="UDisks2Instance" format=3 uid="uid://dhmtkhokrb8pu"] + +[resource] diff --git a/core/systems/disks/steam_removable_media_manager.gd b/core/systems/disks/steam_removable_media_manager.gd index ffe1b525f..78f1da935 100644 --- a/core/systems/disks/steam_removable_media_manager.gd +++ b/core/systems/disks/steam_removable_media_manager.gd @@ -1,7 +1,7 @@ extends Resource class_name SteamRemovableMediaManager -var udisks2 := load("res://core/systems/disks/udisks2.tres") as UDisks2 +var udisks2 := load("res://core/systems/disks/disk_manager.tres") as UDisks2Instance var logger := Log.get_logger("SteamRemovableMediaManager", Log.LEVEL.INFO) @@ -26,7 +26,7 @@ var trim_capable: bool = false func _init() -> void: - if not udisks2.supports_disk_management(): + if not udisks2.is_running(): return # Check the required system files exist for steam_removable_media @@ -58,12 +58,6 @@ func format_drive(device: BlockDevice) -> Error: logger.error("System is not format capable") return ERR_UNAVAILABLE - # This should never hit if using our device tree, but the method is public so - # make sure. - if udisks2.block_device_has_protected_mount(device): - logger.error("Attempted to format device with protected mount. Illegal Operation.") - return ERR_UNAUTHORIZED - var drive = "/dev" + device.dbus_path.trim_prefix(BLOCK_PREFIX) logger.debug("Formatting drive:", drive) var args := ["--full", "--device", drive] @@ -83,12 +77,6 @@ func init_steam_lib(partition: PartitionDevice) -> Error: logger.error("System cannot initialize steam libraries") return ERR_UNAVAILABLE - # This should never hit if using our device tree, but the method is public so - # make sure. - if udisks2.partition_has_protected_mount(partition): - logger.error("Attempted to initialize steam library on device with protected mount. Illegal Operation.") - return ERR_UNAUTHORIZED - var drive := "/dev" + partition.dbus_path.trim_prefix(BLOCK_PREFIX) logger.debug("Intitializing partition as Steam Library: " + drive) var args := [drive] @@ -173,13 +161,11 @@ func trim_sd_card() -> Error: return ERR_SCRIPT_FAILED -func _execute_in_thread(path: String, args: Array = []) -> Array: +func _execute_in_thread(path: String, args: Array[String] = []) -> Array: block_operations = true - var thread_options := SharedThread.Option.NONE - var thread := SharedThread.new(thread_options) - thread.start() - var cmd := Command.new(path, args, thread) - var code := await cmd.execute() as int - thread.stop() + var cmd := Command.create(path, args) + if cmd.execute() != OK: + return ["", -1] + var code := await cmd.finished as int block_operations = false return [cmd.stdout, code] diff --git a/core/systems/disks/udisks2.gd b/core/systems/disks/udisks2.gd deleted file mode 100644 index 1fa780e9e..000000000 --- a/core/systems/disks/udisks2.gd +++ /dev/null @@ -1,182 +0,0 @@ -extends Resource -class_name UDisks2 - -## Manages disk discovery. -## -## The UDisks2 class is responsible for handling dbus messages to and -## from the UDisks2 daemon for disk management. - -# Paths -const UDISKS2_PATH := "/org/freedesktop/UDisks2" -const BLOCK_DEVICES_PATH := UDISKS2_PATH + "/block_devices" -const DRIVES_PATH := UDISKS2_PATH + "/drives" -const UDISKS2_MANAGER_PATH := UDISKS2_PATH + "/Manager" -# Interfaces -const UDISKS2_BUS := "org.freedesktop.UDisks2" -const IFACE_MANAGER := UDISKS2_BUS + ".Manager" -const IFACE_BLOCK := UDISKS2_BUS + ".Block" -const IFACE_FILESYSTEM := UDISKS2_BUS + ".Filesystem" -const IFACE_PARTITION := UDISKS2_BUS + ".Partition" -const IFACE_PARTITION_TABLE := UDISKS2_BUS + ".PartitionTable" -const IFACE_DRIVE := UDISKS2_BUS + ".Drive" -const IFACE_NVME_CONTROLLER := UDISKS2_BUS + ".NVMe.Controller" - -const protected_mounts = [ - "/", - "/boot", - "/boot/efi", - "/efi", - "/frzr_root", - "/frzr_root/boot", - "/home", - "/var", - "/var/cache", - "/var/log", - ] - -var dbus := load("res://core/global/dbus_system.tres") as DBusManager -var manager := Manager.new(dbus.create_proxy(UDISKS2_BUS, UDISKS2_MANAGER_PATH)) -var object_manager := dbus.ObjectManager.new(dbus.create_proxy(UDISKS2_BUS, UDISKS2_PATH)) - -signal devices_updated(devices: Array[BlockDevice]) -signal unprotected_devices_updated(unprotected_devices: Array[BlockDevice]) - -var logger := Log.get_logger("UDisks2", Log.LEVEL.INFO) - - -## Returns true if UDisks2 can be used on this system -func supports_disk_management() -> bool: - return true - return dbus.bus_exists(UDISKS2_BUS) - - -func _init() -> void: - logger.debug("Initalizing UDisks2 Dbus interface.") - object_manager.interfaces_added.connect(_drives_updated) - object_manager.interfaces_removed.connect(_drives_updated) - - -## Signals when a changes to drives are detected -func _drives_updated(iface: String) -> void: - logger.trace("Update from interface:", iface) - var devices = get_devices() - devices_updated.emit(devices) - var unprotected_devices = get_unprotected_devices(devices) - unprotected_devices_updated.emit(unprotected_devices) - - -## Returns all the current block devices detected by UDisks2 -func get_devices() -> Array[BlockDevice]: - var block_array: Array[BlockDevice] = [] - var partition_array: Array[PartitionDevice] = [] - var drive_array: Array[DriveDevice] = [] - var device_paths := dbus.get_managed_objects(UDISKS2_BUS, UDISKS2_PATH) - logger.trace("Searching for UDisks2 DBus objects.") - - # Loop through all objects on the bus - for obj in device_paths: - var object := obj as DBusManager.ManagedObject - var path := object.path - var proxy := dbus.create_proxy(UDISKS2_BUS, path) - logger.trace("Found object: " + str(object) + " with path " + path) - logger.trace("Object data: " + str(object.data)) - - if path.contains("block_devices"): - if object.has_interface(IFACE_PARTITION): - logger.trace("Found Partition Device:", path) - var device := PartitionDevice.new(proxy) - if object.has_interface(IFACE_FILESYSTEM): - logger.trace(path + " has Filesystem") - device.has_filesystem = true - partition_array.append(device) - continue - logger.trace("Found Block Device:", path) - var device := BlockDevice.new(proxy) - block_array.append(device) - - if path.contains("drives"): - var res_path := "drive://" + path - logger.trace("Found Drive Device:", path) - var device := DriveDevice.new(proxy) - device.take_over_path(res_path) - device.interface_type = _id_type(device) - drive_array.append(device) - continue - - for block in block_array: - for partition in partition_array: - if partition.partition_table == block.dbus_path: - block.partitions.append(partition) - for drive in drive_array: - if block.drive_path == drive.dbus_path: - block.drive = drive - - return block_array - - -## Returns all current devices that don't have protected mounts -func get_unprotected_devices(devices: Array[BlockDevice] = []): - var device_tree: Array[BlockDevice] - if devices == []: - logger.debug("Got empty array, grabbing devices from udisks2") - devices = get_devices() - for block in devices: - if not block_device_has_protected_mount(block): - logger.debug(block.dbus_path, "has no protected mounts.") - device_tree.append(block) - return device_tree - - -## Finds all partitions of the given block device and returns true if any of -## them have mounts in the protected_mounts list. -func block_device_has_protected_mount(device: BlockDevice) -> bool: - logger.debug("Checking block device", device.dbus_path, "for protected mounts.") - # Get all the partition dbus paths of this block device - for partition in device.partitions: - if partition_has_protected_mount(partition): - return true - return false - - -## Loops through all mount points of the given partition and returns true if any of -## them have mounts in the protected_mounts list. -func partition_has_protected_mount(device: PartitionDevice) -> bool: - logger.debug("Checking partition", device.dbus_path, "for protected mounts.") - for mount_point in device.fs_mount_points: - logger.debug("Checking mount point", mount_point, "for protected mounts.") - if mount_point in protected_mounts: - logger.debug("Found a protected mount point on drive: " + device.dbus_path) - return true - return false - - -func _id_type(device: DriveDevice) -> DriveDevice.INTERFACE_TYPE: - if device.connection_bus == "usb": - return DriveDevice.INTERFACE_TYPE.USB - elif device.connection_bus == "sdio": - return DriveDevice.INTERFACE_TYPE.SD - elif device.connection_bus == "": - if device.sort_key.contains("hotplug"): - return DriveDevice.INTERFACE_TYPE.USB - elif device.sort_key.contains("removable"): - return DriveDevice.INTERFACE_TYPE.USB - elif device.sort_key.contains("nvme"): - return DriveDevice.INTERFACE_TYPE.NVME - elif device.sort_key.contains("sd_"): - if device.rotation_rate > 0: - return DriveDevice.INTERFACE_TYPE.HDD - return DriveDevice.INTERFACE_TYPE.SSD - return DriveDevice.INTERFACE_TYPE.UNKNOWN - -# Unused. Usefull? -class Manager extends Resource: - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(_iface: String, _props: Dictionary) -> void: - updated.emit() - diff --git a/core/systems/disks/udisks2.tres b/core/systems/disks/udisks2.tres deleted file mode 100644 index 9b223797c..000000000 --- a/core/systems/disks/udisks2.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://d4k3rpacw1yn1"] - -[ext_resource type="Script" path="res://core/systems/disks/udisks2.gd" id="1_flkf8"] - -[resource] -script = ExtResource("1_flkf8") diff --git a/core/systems/disks/udisks2_block_device.gd b/core/systems/disks/udisks2_block_device.gd deleted file mode 100644 index ba34d9923..000000000 --- a/core/systems/disks/udisks2_block_device.gd +++ /dev/null @@ -1,271 +0,0 @@ -extends UDisks2 -class_name BlockDevice - -## Methods and properties of a UDisks2 Block Device dbus interface. -## -## The BlockDevice class is responsible for handling dbus messages to and -## from the UDisks2 daemon for block device management, including filesystems -## and partitions. - -signal updated - -var _proxy: DBusManager.Proxy -var dbus_path: String -var partitions: Array[PartitionDevice] - - -func _init(proxy: DBusManager.Proxy) -> void: - var dbus := load("res://core/global/dbus_system.tres") as DBusManager - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - dbus_path = _proxy.path - -func _on_properties_changed(_iface: String, _props: Dictionary) -> void: - updated.emit() - - -## Returns the human readable string equivalent of the given number of bytes. -func get_readable_size() -> String: - if block_size <= 0: - return "0 B" - var block_size_f: float = float(block_size) - var length = str(block_size).length() - var size_simple: float - - match length: - 1, 2, 3: - return str(block_size) + " B" - 4, 5, 6: - size_simple = block_size_f/1024.0 - if size_simple < 1: - size_simple *= 1000 - return str(snappedf(size_simple, 0.01)) + " B" - return str(snappedf(size_simple, 0.01)) + " KB" - 7, 8, 9: - size_simple = block_size_f/1024.0/1024.0 - if size_simple < 1: - size_simple *= 1000 - return str(snappedf(size_simple, 0.01)) + " KB" - return str(snappedf(size_simple, 0.01)) + " MB" - 10, 11, 12: - size_simple = block_size_f/1024.0/1024.0/1024.0 - if size_simple < 1: - size_simple *= 1000 - return str(snappedf(size_simple, 0.01)) + " MB" - return str(snappedf(size_simple, 0.01)) + " GB" - 13, 14, 15: - size_simple = block_size_f/1024.0/1024.0/1024.0/1024.0 - if size_simple < 1: - size_simple *= 1000 - return str(snappedf(size_simple, 0.01)) + " GB" - return str(snappedf(size_simple, 0.01)) + " TB" - 16, 17, 18: - size_simple = block_size_f/1024.0/1024.0/1024.0/1024.0/1024.0 - if size_simple < 1: - size_simple *= 1000 - return str(snappedf(size_simple, 0.01)) + " TB" - return str(snappedf(size_simple, 0.01)) + " PB" - _: - return "Undefined" - - -# Block -#func block_add_configuration_item() -> void: #(sa{sv})a{sv} - #pass - -#func block_format() -> void: #sa{sv} - #pass - -#func block_get_secret_configuration() -> void: #a{sv} -> a(sa{sv}) - #pass - -#func block_open_device() -> void: #sa{sv} -> h - #pass - -#func block_open_for_backup() -> void: #a{sv} -> h - #pass - -#func block_open_for_benchmark() -> void: #a{sv} -> h - #pass - -#func block_open_for_restore() -> void: #a{sv} -> h - #pass - -#func block_remove_configuration_item() -> void: #a{sv} -> a(sa{sv}) - #pass - -#func block_rescan() -> void: #a{sv} - #pass - -#func block_update_configuration_item() -> void: # (sa{sv})(sa{sv})a{sv} - #pass - -var block_configuration: Dictionary: #a(sa{sv}) - get: - var property = _proxy.get_property(IFACE_BLOCK, "Configuration") - if not property is Dictionary: - return {} - return property - -var block_crypto_backing_device: String: #o - get: - var property = _proxy.get_property(IFACE_BLOCK, "CryptoBackingDevice") - if not property is String: - return "" - return property - -var block_device: String: #ay - get: - var property = _proxy.get_property(IFACE_BLOCK, "Device") - if not property is String: - return "" - return property - -var block_device_number: int: #t - get: - var property = _proxy.get_property(IFACE_BLOCK, "DeviceNumber") - if not property is int: - return -1 - return property - -var drive: DriveDevice - -var drive_path: String: #o - get: - var property = _proxy.get_property(IFACE_BLOCK, "Drive") - if not property is String: - return "" - return property - -var block_hint_auto: bool: - get: - var property = _proxy.get_property(IFACE_BLOCK, "HintAuto") - if not property is bool: - return false - return property - -var block_hint_icon_name: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "HintIconName") - if not property is String: - return "" - return property - -var block_hint_ignore: bool: - get: - var property = _proxy.get_property(IFACE_BLOCK, "HintIgnore") - if not property is bool: - return false - return property - -var block_hint_name: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "HintName") - if not property is String: - return "" - return property - -var block_hint_partitionable: bool: - get: - var property = _proxy.get_property(IFACE_BLOCK, "HintPartitionable") - if not property is bool: - return false - return property - -var block_hint_symbolic_icon_name: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "HintSymbolicIconName") - if not property is String: - return "" - return property - -var block_hint_system: bool: - get: - var property = _proxy.get_property(IFACE_BLOCK, "HintSystem") - if not property is bool: - return false - return property - -var block_id: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "Id") - if not property is String: - return "" - return property - -var block_id_label: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "IdLabel") - if not property is String: - return "" - return property - -var block_id_type: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "IdType") - if not property is String: - return "" - return property - -var block_id_uuid: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "IdUUID") - if not property is String: - return "" - return property - -var block_id_usage: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "IdUsage") - if not property is String: - return "" - return property - -var block_id_version: String: #s - get: - var property = _proxy.get_property(IFACE_BLOCK, "IdVersion") - if not property is String: - return "" - return property - -var block_md_raid: String: #o - get: - var property = _proxy.get_property(IFACE_BLOCK, "MDRaid") - if not property is String: - return "" - return property - -var block_md_raid_member: String: #o - get: - var property = _proxy.get_property(IFACE_BLOCK, "MDRaidMember") - if not property is String: - return "" - return property - -var block_preferred_device: String: #ay - get: - var property = _proxy.get_property(IFACE_BLOCK, "PreferredDevice") - if not property is String: - return "" - return property - -var block_read_only: bool: - get: - var property = _proxy.get_property(IFACE_BLOCK, "ReadOnly") - if not property is bool: - return false - return property - -var block_size: int: #t - get: - var property = _proxy.get_property(IFACE_BLOCK, "Size") - if not property is int: - return -1 - return property - -var block_userspace_mount_options: PackedStringArray: #as - get: - var property = _proxy.get_property(IFACE_BLOCK, "UserspaceMountOptions") - if not property is Array: - return [] - return property diff --git a/core/systems/disks/udisks2_block_device.tres b/core/systems/disks/udisks2_block_device.tres deleted file mode 100644 index fcf724f5c..000000000 --- a/core/systems/disks/udisks2_block_device.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bqa1oyg8j861h"] - -[ext_resource type="Script" path="res://core/systems/disks/udisks2_block_device.gd" id="1_q6b6m"] - -[resource] -script = ExtResource("1_q6b6m") diff --git a/core/systems/disks/udisks2_drive_device.gd b/core/systems/disks/udisks2_drive_device.gd deleted file mode 100644 index 7a6865ca5..000000000 --- a/core/systems/disks/udisks2_drive_device.gd +++ /dev/null @@ -1,360 +0,0 @@ -extends UDisks2 -class_name DriveDevice - -## Methods and properties of a UDisks2 Drive Device dbus interface. -## -## The DriveDevice class is responsible for handling dbus messages to and -## from the UDisks2 daemon for drive device management, including drive -## controllers. - -signal updated - -var _proxy: DBusManager.Proxy -var dbus_path: String -var interface_type:= INTERFACE_TYPE.UNKNOWN - -enum INTERFACE_TYPE { - UNKNOWN, - HDD, - NVME, - SD, - SSD, - USB, -} - - -func _init(proxy: DBusManager.Proxy) -> void: - var dbus := load("res://core/global/dbus_system.tres") as DBusManager - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - dbus_path = _proxy.path - -func _on_properties_changed(_iface: String, props: Dictionary) -> void: - updated.emit() - -# Drive -#func eject() -> void: #a{sv} - #pass - -#func power_off() -> void: #a{sv} - #pass - -#func set_configuration() -> void: #a{sv}a{sv} - #pass - -var can_power_off: bool: - get: - var property = _proxy.get_property(IFACE_DRIVE, "Ejectable") - if not property is bool: - return false - return property - -var configuration: Array: #a{sv} - get: - var property = _proxy.get_property(IFACE_DRIVE, "Configuration") - if not property is Array: - return [] - return property - -var connection_bus: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "ConnectionBus") - if not property is String: - return "" - return property - -var ejectable: bool: - get: - var property = _proxy.get_property(IFACE_DRIVE, "Ejectable") - if not property is bool: - return false - return property - -var id: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "Id") - if not property is String: - return "" - return property - -var media: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "Media") - if not property is String: - return "" - return property - -var media_available: bool: - get: - var property = _proxy.get_property(IFACE_DRIVE, "MediaAvailable") - if not property is bool: - return false - return property - -var media_change_detected: bool: - get: - var property = _proxy.get_property(IFACE_DRIVE, "MediaChangeDetected") - if not property is bool: - return false - return property - -var media_compatibility: PackedStringArray: #as - get: - var property = _proxy.get_property(IFACE_DRIVE, "MediaCompatibility") - if not property is PackedStringArray: - return [] - return property - -var media_removable: bool: - get: - var property = _proxy.get_property(IFACE_DRIVE, "MediaRemovable") - if not property is bool: - return false - return property - -var model: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "Model") - if not property is String: - return "" - return property - -var optical: bool: - get: - var property = _proxy.get_property(IFACE_DRIVE, "Optical") - if not property is bool: - return false - return property - -var optical_blank: bool: - get: - var property = _proxy.get_property(IFACE_DRIVE, "OpticalBlank") - if not property is bool: - return false - return property - -var optical_num_audio_tracks: int: #u - get: - var property = _proxy.get_property(IFACE_DRIVE, "OpticalNumAudioTracks") - if not property is int: - return -1 - return property - -var optical_num_data_tracks: int: #u - get: - var property = _proxy.get_property(IFACE_DRIVE, "OpticalNumDataTracks") - if not property is int: - return -1 - return property - -var optical_num_sessions: int: #u - get: - var property = _proxy.get_property(IFACE_DRIVE, "OpticalNumSessions") - if not property is int: - return -1 - return property - -var optical_num_tracks: int: #u - get: - var property = _proxy.get_property(IFACE_DRIVE, "OpticalNumTracks") - if not property is int: - return -1 - return property - -var removable: bool: - get: - var property = _proxy.get_property(IFACE_DRIVE, "Removable") - if not property is bool: - return false - return property - -var revision: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "Revision") - if not property is String: - return "" - return property - -var rotation_rate: int: #i - get: - var property = _proxy.get_property(IFACE_DRIVE, "RotationRate") - if not property is int: - return -1 - return property - -var seat: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "Seat") - if not property is String: - return "" - return property - -var serial: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "Serial") - if not property is String: - return "" - return property - -var sibling_id: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "SiblingId") - if not property is String: - return "" - return property - -var size: int: #t - get: - var property = _proxy.get_property(IFACE_DRIVE, "Size") - if not property is int: - return -1 - return property - -var sort_key: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "SortKey") - if not property is String: - return "" - return property - -var time_detected: int: #t - get: - var property = _proxy.get_property(IFACE_DRIVE, "TimeDetected") - if not property is int: - return -1 - return property - -var time_media_detected: int: #t - get: - var property = _proxy.get_property(IFACE_DRIVE, "TimeMediaDetected") - if not property is int: - return -1 - return property - -var vendor: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "Vendor") - if not property is String: - return "" - return property - -var wwn: String: #s - get: - var property = _proxy.get_property(IFACE_DRIVE, "WWN") - if not property is String: - return "" - return property - -# NVME Controller -#func sanitize_start() -> void: #sa{sv} - #pass - -#func smart_get_attributes() -> void: #a{sv} -> a{sv} - #pass - -#func smart_selftest_abort() -> void: #a{sv} - #pass - -#func smart_selftest_start() -> void: #sa{sv} - #pass - -#func smart_update() -> void: #a{sv} - #pass - -#var controller_id: int: #q - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "ControllerID") - #if not property is int: - #return -1 - #return property #uint16 - -#var fguid: String: #s - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "FGUID") - #if not property is String: - #return "" - #return property - -#var nvme_revision: String: #s - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "NVMERevision") - #if not property is String: - #return "" - #return property - -#var sanitize_percent_remaining: int: #i - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SanitizePercentRemaining") - #if not property is int: - #return -1 - #return property - -#var sanitize_status: String: #s - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SanitizeStatus") - #if not property is String: - #return "" - #return property - -#var smart_critical_warning: PackedStringArray: #as - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SmartCriticalWarning") - #if not property is PackedStringArray: - #return [] - #return property - -#var smart_power_on_hours: int: #t - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SmartPowerOnHours") - #if not property is int: - #return -1 - #return property - -#var smart_selftest_percent_remaining: int: #i - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SmartSelftestPercentRemaining") - #if not property is int: - #return -1 - #return property - -#var smart_selftest_status: String: #s - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SmartSelftestStatus") - #if not property is String: - #return "" - #return property - -#var smart_temperature: int: #q - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SmartTemperature") - #if not property is int: - #return -1 - #return property - -#var smart_updated: int: #t - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SmartUpdated") - #if not property is int: - #return -1 - #return property - -#var state: String: #s - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "State") - #if not property is String: - #return "" - #return property - -#var subsystem_nqn: String: #ay - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "SubsystemNQN") - #if not property is String: - #return "" - #return property - -#var unallocated_capacity: int: #t - #get: - #var property = _proxy.get_property(IFACE_NVME_CONTROLLER, "unallocatedCapacity") - #if not property is int: - #return -1 - #return property diff --git a/core/systems/disks/udisks2_drive_device.tres b/core/systems/disks/udisks2_drive_device.tres deleted file mode 100644 index b69c04ef0..000000000 --- a/core/systems/disks/udisks2_drive_device.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="Resource" load_steps=2 format=3 uid="uid://biqlkubechqgc"] - -[ext_resource type="Script" path="res://core/systems/disks/udisks2_drive_device.gd" id="1_1kb7p"] - -[resource] -script = ExtResource("1_1kb7p") diff --git a/core/systems/disks/udisks2_partition_device.gd b/core/systems/disks/udisks2_partition_device.gd deleted file mode 100644 index ff31ea4a1..000000000 --- a/core/systems/disks/udisks2_partition_device.gd +++ /dev/null @@ -1,172 +0,0 @@ -extends BlockDevice -class_name PartitionDevice - -## Methods and properties of a UDisks2 Block Device's Partition and Filesystem -## dbus interface. -## -## The PartitionDevice class is responsible for handling dbus messages to and -## from the UDisks2 daemon for filesystems and partition BLock devices. - -var has_filesystem: bool = false - -# Partition -#func partition_delete() -> void: #a{sv} - #pass - -#func partition_resize_partition() -> void: #ta{sv} - #pass - -#func partition_set_flags() -> void: #ta{sv} - #pass - -#func partition_set_partition_name() -> void: #sa{sv} .SetName, set_name is a reserved Godot method - #pass - -#func partition_set_type() -> void: #sa{sv} - #pass - -#func partition_set_partition_uuid() -> void: #sa{sv} - #pass - - -var partition_flags: int: #t - get: - var property = _proxy.get_property(IFACE_PARTITION, "Flags") - if not property is int: - return -1 - return property - -var partition_is_contained: bool: - get: - var property = _proxy.get_property(IFACE_PARTITION, "IsContained") - if not property is bool: - return false - return property - -var partition_is_container: bool: - get: - var property = _proxy.get_property(IFACE_PARTITION, "IsContainer") - if not property is bool: - return false - return property - -var partition_name: String: #s - get: - var property = _proxy.get_property(IFACE_PARTITION, "Name") - if not property is String: - return "" - return property - -var partition_number: int: #u - get: - var property = _proxy.get_property(IFACE_PARTITION, "Number") - if not property is int: - return -1 - return property - -var partition_offset: int: #t - get: - var property = _proxy.get_property(IFACE_PARTITION, "Offset") - if not property is int: - return -1 - return property - -var partition_size: int: #t - get: - var property = _proxy.get_property(IFACE_PARTITION, "Size") - if not property is int: - return -1 - return property - -var partition_table: String: #o - get: - var property = _proxy.get_property(IFACE_PARTITION, "Table") - if not property is String: - return "" - return property - -var partition_type: String: #s - get: - var property = _proxy.get_property(IFACE_PARTITION, "Type") - if not property is String: - return "" - return property - -var partition_uuid: String: #s - get: - var property = _proxy.get_property(IFACE_PARTITION, "UUID") - if not property is String: - return "" - return property - -# Filesystem -#func fs_check() -> void: #a{sv} -> b - #pass - -#func fs_mount() -> void: #a{sv} -> s - #pass - -#func fs_repair() -> void: #a{sv} -> b - #pass - -#func fs_resize_filesystem() -> void: #ta{sv} - #pass - -#func fs_set_label() -> void: #sa{sv} - #pass - -#func fs_set_filesystem_uuid() -> void: #sa{sv} - #pass - -#func fs_take_ownership() -> void: #a{sv} - #pass - -#func fs_unmount() -> void: #a{sv} - #pass - -var fs_mount_points: PackedStringArray: #aay - get: - if not has_filesystem: - return [] - - var mount_points: PackedStringArray = [] - var property = _proxy.get_property(IFACE_FILESYSTEM, "MountPoints") - - if not property is Array: - return [] - - for raw_array in property: - var char_array: PackedByteArray = [] - char_array.append_array(raw_array as PackedByteArray) - var mount_point = char_array.get_string_from_ascii() - mount_points.append(mount_point as String) - - return mount_points - -var fs_size: int: #t - get: - if not has_filesystem: - return -1 - var property = _proxy.get_property(IFACE_FILESYSTEM, "Size") - if not property is int: - return -1 - return property - -var fs_symlinks: PackedStringArray: #aay - get: - if not has_filesystem: - return [] - - var symlinks: PackedStringArray = [] - var property = _proxy.get_property(IFACE_FILESYSTEM, "MountPoints") - - if not property is Array: - return [] - - for raw_array in property: - var char_array: PackedByteArray = [] - char_array.append_array(raw_array as PackedByteArray) - var symlink = char_array.get_string_from_ascii() - symlinks.append(symlink as String) - - return symlinks diff --git a/core/systems/disks/udisks2_partition_device.tres b/core/systems/disks/udisks2_partition_device.tres deleted file mode 100644 index 97950d54b..000000000 --- a/core/systems/disks/udisks2_partition_device.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="Resource" script_class="PartitionDevice" load_steps=2 format=3 uid="uid://df7pk5qc4ppt4"] - -[ext_resource type="Script" path="res://core/systems/disks/udisks2_partition_device.gd" id="1_rv37n"] - -[resource] -script = ExtResource("1_rv37n") diff --git a/core/systems/gamescope/gamescope.gd b/core/systems/gamescope/gamescope.gd new file mode 100644 index 000000000..185d62df9 --- /dev/null +++ b/core/systems/gamescope/gamescope.gd @@ -0,0 +1,26 @@ +@icon("res://assets/editor-icons/streamline--desktop-game-solid.svg") +extends Node +class_name Gamescope + +## Manages gamescope. +## +## The [Gamescope] class is responsible for loading a [GamescopeInstance] and +## calling its 'process()' method each frame. + +@export var instance: GamescopeInstance = load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance + +# Keep a reference to xwayland instances so they are not cleaned up automatically +var _xwaylands: Array[GamescopeXWayland] +var logger := Log.get_logger("Gamescope") + + +func _ready() -> void: + _xwaylands = instance.get_xwaylands() + if _xwaylands.is_empty(): + logger.warn("Gamescope not detected. Unexpected behavior expected.") + + +func _process(_delta: float) -> void: + if not instance: + return + instance.process() diff --git a/core/systems/gamescope/gamescope.tres b/core/systems/gamescope/gamescope.tres new file mode 100644 index 000000000..1e22d2c3b --- /dev/null +++ b/core/systems/gamescope/gamescope.tres @@ -0,0 +1,3 @@ +[gd_resource type="GamescopeInstance" format=3 uid="uid://chd0nc6gbfnw0"] + +[resource] diff --git a/core/systems/gamescope/gamescope_test.gd b/core/systems/gamescope/gamescope_test.gd new file mode 100644 index 000000000..96fd0b73d --- /dev/null +++ b/core/systems/gamescope/gamescope_test.gd @@ -0,0 +1,21 @@ +extends Node2D + +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance + +var PID := OS.get_process_id() + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + print(gamescope.get_xwaylands()) + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + if not xwayland: + print("XWayland not found") + return + + var ogui_window_ids := xwayland.get_windows_for_pid(PID) + if ogui_window_ids.is_empty(): + print("Unable to find window id for OGUI") + return + var ogui_window_id := ogui_window_ids[0] + xwayland.set_main_app(ogui_window_id) + print("Found window ids for OGUI: ", ogui_window_ids) diff --git a/core/systems/gamescope/gamescope_test.tscn b/core/systems/gamescope/gamescope_test.tscn new file mode 100644 index 000000000..73d96e768 --- /dev/null +++ b/core/systems/gamescope/gamescope_test.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=5 format=3 uid="uid://83606lflpyo5"] + +[ext_resource type="Script" path="res://core/systems/gamescope/gamescope_test.gd" id="1_e433b"] +[ext_resource type="Script" path="res://core/systems/gamescope/gamescope.gd" id="1_ukgov"] +[ext_resource type="GamescopeInstance" uid="uid://chd0nc6gbfnw0" path="res://core/systems/gamescope/gamescope.tres" id="2_fbm81"] +[ext_resource type="Texture2D" uid="uid://djy4rejy21s6g" path="res://icon.svg" id="4_j1wlf"] + +[node name="GamescopeTest" type="Node2D"] +script = ExtResource("1_e433b") + +[node name="Gamescope" type="Node" parent="."] +script = ExtResource("1_ukgov") +instance = ExtResource("2_fbm81") + +[node name="Icon" type="Sprite2D" parent="."] +position = Vector2(609, 401) +texture = ExtResource("4_j1wlf") diff --git a/core/systems/hardware/cpu.gd b/core/systems/hardware/cpu.gd index 259a6167a..efba6168d 100644 --- a/core/systems/hardware/cpu.gd +++ b/core/systems/hardware/cpu.gd @@ -99,8 +99,8 @@ func _get_property(prop_path: String) -> String: ## Provides info on the GPU vendor, model, and capabilities. func _get_lscpu_info() -> PackedStringArray: - var cmd := CommandSync.new("lscpu") - if cmd.execute() != OK: + var cmd := Command.create("lscpu", []) + if cmd.execute_blocking() != OK: return [] return cmd.stdout.split("\n") @@ -111,4 +111,3 @@ func _to_string() -> String: + ") Model: (" + str(model) \ + ") Core count: (" + str(core_count) \ + ")>" - diff --git a/core/systems/hardware/hardware_manager.gd b/core/systems/hardware/hardware_manager.gd index e006376e9..c24f5edd9 100644 --- a/core/systems/hardware/hardware_manager.gd +++ b/core/systems/hardware/hardware_manager.gd @@ -71,7 +71,8 @@ func get_gpu_info() -> GPUInfo: # to look into vulkaninfo as this can only gather the data # for the currently active GPU. Vulkaninfo can provide data # on all detected GPU devices. - match RenderingServer.get_video_adapter_vendor(): + gpu_info.vendor = RenderingServer.get_video_adapter_vendor() + match gpu_info.vendor: "AMD", "AuthenticAMD", 'AuthenticAMD Advanced Micro Devices, Inc.', "Advanced Micro Devices, Inc. [AMD/ATI]": gpu_info.vendor = "AMD" "Intel", "GenuineIntel", "Intel Corporation": @@ -160,11 +161,11 @@ func get_kernel_version() -> String: ## (e.g. get_gpu_card("card1")) func get_gpu_card(card_dir: String) -> DRMCardInfo: var file_prefix := "/".join(["/sys/class/drm", card_dir, "device"]) - var vendor_id := _get_card_property_from_path("/".join([file_prefix, "vendor"])) - var device_id := _get_card_property_from_path("/".join([file_prefix, "device"])) - var revision_id := _get_card_property_from_path("/".join([file_prefix, "revision"])) - var subvendor_id := _get_card_property_from_path("/".join([file_prefix, "subsystem_vendor"])) - var subdevice_id := _get_card_property_from_path("/".join([file_prefix, "subsystem_device"])) + var vendor_id := _get_card_property_from_cat("/".join([file_prefix, "vendor"])) + var device_id := _get_card_property_from_cat("/".join([file_prefix, "device"])) + var revision_id := _get_card_property_from_cat("/".join([file_prefix, "revision"])) + var subvendor_id := _get_card_property_from_cat("/".join([file_prefix, "subsystem_vendor"])) + var subdevice_id := _get_card_property_from_cat("/".join([file_prefix, "subsystem_device"])) # Try to load the card info if it already exists var res_path := "/".join(["drmcardinfo://", vendor_id, device_id, subvendor_id, subdevice_id]) @@ -272,7 +273,7 @@ func get_gpu_cards() -> Array[DRMCardInfo]: var card_info := get_gpu_card(card_name) if not card_info: continue - + found_cards.append(card_info) var vulkan_info := _get_cards_from_vulkan() @@ -378,6 +379,16 @@ func _get_card_property_from_path(path: String) -> String: return FileAccess.get_file_as_string(path).lstrip("0x").to_lower().strip_escapes() +func _get_card_property_from_cat(path: String) -> String: + var output := [] + OS.execute("cat", [path], output) + if output.is_empty(): + return "" + var value := output[0] as String + logger.debug("Got cat output:", value) + return value.lstrip("0x").to_lower().strip_escapes() + + ## Returns a an array of PackedStringArray's that each represent a sing GPU ## identified in vulkaninfo. func _get_cards_from_vulkan() ->Array[PackedStringArray]: diff --git a/core/systems/input/events/event.gd b/core/systems/input/events/event.gd index 632602be1..c996fe2c4 100644 --- a/core/systems/input/events/event.gd +++ b/core/systems/input/events/event.gd @@ -740,3 +740,266 @@ static func capability_from_keycode(scancode: int) -> String: return "Keyboard:KeyF24" _: return "" + + +## Convert the given key scancode into a target keyboard event string +static func virtual_key_from_keycode(scancode: int) -> String: + match scancode: + KEY_ESCAPE: + return "KEY_ESC" + KEY_1: + return "KEY_1" + KEY_2: + return "KEY_2" + KEY_3: + return "KEY_3" + KEY_4: + return "KEY_4" + KEY_5: + return "KEY_5" + KEY_6: + return "KEY_6" + KEY_7: + return "KEY_7" + KEY_8: + return "KEY_8" + KEY_9: + return "KEY_9" + KEY_0: + return "KEY_0" + KEY_MINUS: + return "KEY_MINUS" + KEY_EQUAL: + return "KEY_EQUAL" + KEY_BACKSPACE: + return "KEY_BACKSPACE" + KEY_TAB: + return "KEY_TAB" + KEY_Q: + return "KEY_Q" + KEY_W: + return "KEY_W" + KEY_E: + return "KEY_E" + KEY_R: + return "KEY_R" + KEY_T: + return "KEY_T" + KEY_Y: + return "KEY_Y" + KEY_U: + return "KEY_U" + KEY_I: + return "KEY_I" + KEY_O: + return "KEY_O" + KEY_P: + return "KEY_P" + KEY_BRACELEFT: + return "KEY_LEFTBRACE" + KEY_BRACERIGHT: + return "KEY_RIGHTBRACE" + KEY_ENTER: + return "KEY_ENTER" + KEY_CTRL: + return "KEY_LEFTCTRL" + KEY_A: + return "KEY_A" + KEY_S: + return "KEY_S" + KEY_D: + return "KEY_D" + KEY_F: + return "KEY_F" + KEY_G: + return "KEY_G" + KEY_H: + return "KEY_H" + KEY_J: + return "KEY_J" + KEY_K: + return "KEY_K" + KEY_L: + return "KEY_L" + KEY_SEMICOLON: + return "KEY_SEMICOLON" + KEY_APOSTROPHE: + return "KEY_APOSTROPHE" + KEY_ASCIITILDE: + return "KEY_GRAVE" + KEY_SHIFT: + return "KEY_LEFTSHIFT" + KEY_BACKSLASH: + return "KEY_BACKSLASH" + KEY_Z: + return "KEY_Z" + KEY_X: + return "KEY_X" + KEY_C: + return "KEY_C" + KEY_V: + return "KEY_V" + KEY_B: + return "KEY_B" + KEY_N: + return "KEY_N" + KEY_M: + return "KEY_M" + KEY_COMMA: + return "KEY_COMMA" + KEY_PERIOD: + return "KEY_DOT" + KEY_SLASH: + return "KEY_SLASH" + KEY_SHIFT: + return "KEY_SHIFT" + KEY_ASTERISK: + return "KEY_ASTERISK" + KEY_ALT: + return "KEY_ALT" + KEY_SPACE: + return "KEY_SPACE" + KEY_CAPSLOCK: + return "KEY_CAPSLOCK" + KEY_F1: + return "KEY_F1" + KEY_F2: + return "KEY_F2" + KEY_F3: + return "KEY_F3" + KEY_F4: + return "KEY_F4" + KEY_F5: + return "KEY_F5" + KEY_F6: + return "KEY_F6" + KEY_F7: + return "KEY_F7" + KEY_F8: + return "KEY_F8" + KEY_F9: + return "KEY_F9" + KEY_F10: + return "KEY_F10" + KEY_NUMLOCK: + return "KEY_NUMLOCK" + KEY_SCROLLLOCK: + return "KEY_SCROLLLOCK" + KEY_KP_7: + return "KEY_KP7" + KEY_KP_8: + return "KEY_KP8" + KEY_KP_9: + return "KEY_KP9" + KEY_KP_SUBTRACT: + return "KEY_KPMINUS" + KEY_KP_4: + return "KEY_KP4" + KEY_KP_5: + return "KEY_KP5" + KEY_KP_6: + return "KEY_KP6" + KEY_KP_ADD: + return "KEY_KPPLUS" + KEY_KP_1: + return "KEY_KP1" + KEY_KP_2: + return "KEY_KP2" + KEY_KP_3: + return "KEY_KP3" + KEY_KP_0: + return "KEY_KP0" + KEY_KP_PERIOD: + return "KEY_KPDOT" + KEY_F11: + return "KEY_F11" + KEY_F12: + return "KEY_F12" + KEY_JIS_KANA: + return "KEY_KATAKANAHIRAGANA" + KEY_KP_ENTER: + return "KEY_KPENTER" + KEY_CTRL: + return "KEY_CTRL" + KEY_KP_DIVIDE: + return "KEY_KPSLASH" + KEY_SYSREQ: + return "KEY_SYSREQ" + KEY_ALT: + return "KEY_ALT" + KEY_HOME: + return "KEY_HOME" + KEY_UP: + return "KEY_UP" + KEY_PAGEUP: + return "KEY_PAGEUP" + KEY_LEFT: + return "KEY_LEFT" + KEY_RIGHT: + return "KEY_RIGHT" + KEY_END: + return "KEY_END" + KEY_DOWN: + return "KEY_DOWN" + KEY_PAGEDOWN: + return "KEY_PAGEDOWN" + KEY_INSERT: + return "KEY_INSERT" + KEY_DELETE: + return "KEY_DELETE" + KEY_VOLUMEMUTE: + return "KEY_VOLUMEMUTE" + KEY_VOLUMEDOWN: + return "KEY_VOLUMEDOWN" + KEY_VOLUMEUP: + return "KEY_VOLUMEUP" + KEY_PAUSE: + return "KEY_PAUSE" + KEY_YEN: + return "KEY_YEN" + KEY_META: + return "KEY_LEFTMETA" + KEY_STOP: + return "KEY_STOP" + KEY_HELP: + return "KEY_HELP" + KEY_BACK: + return "KEY_BACK" + KEY_FORWARD: + return "KEY_FORWARD" + KEY_MEDIANEXT: + return "KEY_MEDIANEXT" + KEY_MEDIAPLAY: + return "KEY_MEDIAPLAY" + KEY_MEDIAPREVIOUS: + return "KEY_MEDIAPREVIOUS" + KEY_MEDIASTOP: + return "KEY_MEDIASTOP" + KEY_REFRESH: + return "KEY_REFRESH" + KEY_F13: + return "KEY_F13" + KEY_F14: + return "KEY_F14" + KEY_F15: + return "KEY_F15" + KEY_F16: + return "KEY_F16" + KEY_F17: + return "KEY_F17" + KEY_F18: + return "KEY_F18" + KEY_F19: + return "KEY_F19" + KEY_F20: + return "KEY_F20" + KEY_F21: + return "KEY_F21" + KEY_F22: + return "KEY_F22" + KEY_F23: + return "KEY_F23" + KEY_F24: + return "KEY_F24" + _: + return "" diff --git a/core/systems/input/input_icon_manager.gd b/core/systems/input/input_icon_manager.gd index f60d1b40e..e0cb5c177 100644 --- a/core/systems/input/input_icon_manager.gd +++ b/core/systems/input/input_icon_manager.gd @@ -20,7 +20,7 @@ enum InputType { } var in_game_state := load("res://assets/state/states/in_game.tres") as State -var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber +var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance var logger := Log.get_logger("InputIconManager", Log.LEVEL.INFO) ## Disable/Enable signaling on input type changes @@ -154,7 +154,7 @@ func _init(): in_game_state.state_exited.connect(on_in_game_exited) # Listen for InputPlumber device change events - var on_comp_device_added := func(_device: InputPlumber.CompositeDevice): + var on_comp_device_added := func(_device: CompositeDevice): _on_joy_connection_changed(true) input_plumber.composite_device_added.connect(on_comp_device_added) var on_comp_device_removed := func(_path: String): @@ -402,8 +402,6 @@ func _get_matching_event(path: String, input_type: InputType) -> Array[InputEven ## Set the last input type to the given value and emit a signal func set_last_input_type(_last_input_type: InputType): last_input_type = _last_input_type - if not self.disabled: - input_type_changed.emit(_last_input_type) ## Signal whenever a gamepad is connected/disconnected diff --git a/core/systems/input/input_icon_processor.gd b/core/systems/input/input_icon_processor.gd index 767f57f9a..2acdd3072 100644 --- a/core/systems/input/input_icon_processor.gd +++ b/core/systems/input/input_icon_processor.gd @@ -6,7 +6,7 @@ class_name InputIconProcessor const DEADZONE := 0.4 -var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber +var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance var icon_manager := load("res://core/systems/input/input_icon_manager.tres") as InputIconManager @@ -23,16 +23,21 @@ func _input(event: InputEvent) -> void: # If this is an InputPlumber event, use the name from the device if event.has_meta("dbus_path") and not event.get_meta("dbus_path", "").is_empty(): var dbus_path := event.get_meta("dbus_path") as String - var device := input_plumber.get_device(dbus_path) - device_name = device.name + var device := input_plumber.get_composite_device(dbus_path) + if device: + device_name = device.name # Otherwise, use the detected device name else: device_name = Input.get_joy_name(event.device) "InputEventJoypadMotion": if abs(event.axis_value) > DEADZONE: input_type = InputIconManager.InputType.GAMEPAD + var refresh := false if input_type != icon_manager.last_input_type: icon_manager.set_last_input_type(input_type) + refresh = true if device_name != icon_manager.last_input_device: icon_manager.last_input_device = device_name + refresh = true + if refresh: icon_manager.refresh() diff --git a/core/systems/input/input_manager.gd b/core/systems/input/input_manager.gd index 7925a935e..174b1405e 100644 --- a/core/systems/input/input_manager.gd +++ b/core/systems/input/input_manager.gd @@ -18,7 +18,7 @@ var osk := load("res://core/global/keyboard_instance.tres") as KeyboardInstance ## The audio manager to use to adjust the audio when audio input events happen. var audio_manager := load("res://core/global/audio_manager.tres") as AudioManager ## InputPlumber receives and sends DBus input events. -var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber +var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance ## LaunchManager provides context on the currently running app so we can switch profiles var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager ## The Global State Machine @@ -36,6 +36,9 @@ var popup_state := preload("res://assets/state/states/popup.tres") as State ## Map of pressed actions to prevent double inputs var actions_pressed := {} +## Number of currently pressed touches +var current_touches := 0 + ## Will show logger events with the prefix InputManager var logger := Log.get_logger("InputManager", Log.LEVEL.INFO) @@ -44,11 +47,20 @@ var logger := Log.get_logger("InputManager", Log.LEVEL.INFO) func _ready() -> void: add_to_group("InputManager") input_plumber.composite_device_added.connect(_watch_dbus_device) + input_plumber.started.connect(_init_inputplumber) + _init_inputplumber() + - for device in input_plumber.composite_devices: +func _init_inputplumber() -> void: + for device in input_plumber.get_composite_devices(): _watch_dbus_device(device) +## Returns true if the given event is an InputPlumber event +static func is_inputplumber_event(event: InputEvent) -> bool: + return event.has_meta("dbus_path") + + ## Queue a release event for the given action func action_release(dbus_path: String, action: String, strength: float = 1.0) -> void: Input.action_release(action) @@ -72,10 +84,26 @@ func _send_input(dbus_path: String, action: String, pressed: bool, strength: flo Input.parse_input_event(input_action) -## Process all unhandled input, possibly preventing the input from propagating further. -## https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#how-does-it-work +## Process all window input. Window input is processed before all _input and +## _gui_input node methods. +## @tutorial https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#how-does-it-work func _input(event: InputEvent) -> void: logger.debug("Got input event to handle: " + str(event)) + + # Keep track of the current number of touch inputs + if event is InputEventScreenTouch: + if event.is_pressed(): + self.current_touches += 1 + else: + self.current_touches -= 1 + + # Don't process Godot events if InputPlumber is running + if input_plumber.is_running() and not is_inputplumber_event(event): + if event is InputEventJoypadButton or event is InputEventJoypadMotion: + logger.debug("Skipping Godot event while InputPlumber is running:", event) + get_viewport().set_input_as_handled() + return + var dbus_path := event.get_meta("dbus_path", "") as String # Consume double inputs for controllers with DPads that have TRIGGER_HAPPY events @@ -175,7 +203,7 @@ func _guide_input(event: InputEvent) -> void: var dbus_path := event.get_meta("dbus_path", "") as String # Only act on release events if event.is_pressed(): - logger.warn("Guide pressed. Waiting for additional events.") + logger.debug("Guide pressed. Waiting for additional events.") # Set the gamepad profile to the global default so we can capture button events. # This ensures that we use the global profile and not the game's input profile for # processing guide button combos and navigating the menu. @@ -270,17 +298,19 @@ func _audio_input(event: InputEvent) -> void: return -func _watch_dbus_device(device: InputPlumber.CompositeDevice) -> void: - for target in device.dbus_targets: - logger.debug("Adding watch for " + device.name + " " + target.name) - logger.debug(str(target.get_instance_id())) - logger.debug(str(target.get_rid())) - target.input_event.connect(_on_dbus_input_event.bind(device.dbus_path)) +func _watch_dbus_device(device: CompositeDevice) -> void: + for target in device.dbus_devices: + if target.input_event.is_connected(_on_dbus_input_event.bind(device.dbus_path)): + continue + logger.debug("Adding watch for " + device.name + " " + target.dbus_path) + logger.debug(str(target.get_instance_id())) + logger.debug(str(target.get_rid())) + target.input_event.connect(_on_dbus_input_event.bind(device.dbus_path)) func _on_dbus_input_event(event: String, value: float, dbus_path: String) -> void: var pressed := value == 1.0 - logger.debug("Handling dbus input event from" + dbus_path + ": " + event + " pressed: " + str(pressed)) + logger.debug("Handling dbus input event from '" + dbus_path + "': " + event + " pressed: " + str(pressed)) var action := event match event: diff --git a/core/systems/input/input_manager.tscn b/core/systems/input/input_manager.tscn index a32585d55..000a852e8 100644 --- a/core/systems/input/input_manager.tscn +++ b/core/systems/input/input_manager.tscn @@ -1,6 +1,12 @@ -[gd_scene load_steps=2 format=3 uid="uid://n83wlhmmsu3j"] +[gd_scene load_steps=4 format=3 uid="uid://n83wlhmmsu3j"] [ext_resource type="Script" path="res://core/systems/input/input_manager.gd" id="1_3s67x"] +[ext_resource type="Script" path="res://core/systems/input/input_plumber.gd" id="2_v1kl3"] +[ext_resource type="InputPlumberInstance" uid="uid://e2bevy4j4rx2" path="res://core/systems/input/input_plumber.tres" id="3_eclcy"] [node name="InputManager" type="Node"] script = ExtResource("1_3s67x") + +[node name="InputPlumber" type="Node" parent="."] +script = ExtResource("2_v1kl3") +instance = ExtResource("3_eclcy") diff --git a/core/systems/input/input_plumber.gd b/core/systems/input/input_plumber.gd index f21c8e0ad..446a1a625 100644 --- a/core/systems/input/input_plumber.gd +++ b/core/systems/input/input_plumber.gd @@ -1,5 +1,5 @@ -@icon("res://assets/icons/game-controller.svg") -extends Resource +@icon("res://addons/core/assets/icons/inputplumber.svg") +extends Node class_name InputPlumber ## Manages routing input to and from InputPlumber. @@ -7,690 +7,118 @@ class_name InputPlumber ## The InputPlumberManager class is responsible for handling dbus messages to and ## from the InputPlumber input manager daemon. -const INPUT_PLUMBER_BUS := "org.shadowblip.InputPlumber" -const INPUT_PLUMBER_PATH := "/org/shadowblip/InputPlumber" -const INPUT_PLUMBER_PREFIX := INPUT_PLUMBER_PATH + "/devices" -const INPUT_PLUMBER_MANAGER_PATH := "/org/shadowblip/InputPlumber/Manager" -const IFACE_MANAGER := "org.shadowblip.InputManager" -const IFACE_COMPOSITE_DEVICE := "org.shadowblip.Input.CompositeDevice" -const IFACE_EVENT_DEVICE := "org.shadowblip.Input.Source.EventDevice" -const IFACE_HIDRAW_DEVICE := "org.shadowblip.Input.Source.HIDRawDevice" -const IFACE_IIO_DEVICE := "org.shadowblip.Input.Source.IIODevice" -const IFACE_DBUS_DEVICE := "org.shadowblip.Input.DBusDevice" -const IFACE_GAMEPAD_DEVICE := "org.shadowblip.Input.Gamepad" -const IFACE_KEYBOARD_DEVICE := "org.shadowblip.Input.Keyboard" -const IFACE_MOUSE_DEVICE := "org.shadowblip.Input.Mouse" - const DEFAULT_PROFILE := "res://assets/gamepad/profiles/default.json" const DEFAULT_GLOBAL_PROFILE := "user://data/gamepad/profiles/global_default.json" const PROFILES_DIR := "user://data/gamepad/profiles" -enum INTERCEPT_MODE { - NONE, - PASS, - ALL, -} - -var logger := Log.get_logger("InputPlumber", Log.LEVEL.INFO) - -var dbus := load("res://core/global/dbus_system.tres") as DBusManager -var manager := Manager.new(dbus.create_proxy(INPUT_PLUMBER_BUS, INPUT_PLUMBER_MANAGER_PATH)) -var object_manager := dbus.ObjectManager.new(dbus.create_proxy(INPUT_PLUMBER_BUS, INPUT_PLUMBER_PATH)) -var system_thread := load("res://core/systems/threading/system_thread.tres") as SharedThread - -var is_running := false -var composite_devices: Array[CompositeDevice] = [] -var composite_devices_map: Dictionary = {} -var intercept_mode_current: INTERCEPT_MODE = INTERCEPT_MODE.NONE -var intercept_triggers_current: PackedStringArray = ["Gamepad:Button:Guide"] -var intercept_target_current: String = "Gamepad:Button:Guide" +@export var instance: InputPlumberInstance = load("res://core/systems/input/input_plumber.tres") -## Emitted when InputPlumber is detected as running -signal started -## Emitted when InputPlumber is detected as stopped -signal stopped -## Emitted when a CompositeDevice is dicovered and identified as a new device -signal composite_device_added(device: CompositeDevice) -## Emitted when a CompositeDevice is dicovered over dbus but already exists in -## the local map -signal composite_device_changed(device: CompositeDevice) -## Emitted when a CompositeDevice is removed -signal composite_device_removed(dbus_path: String) +# Keep a reference to dbus devices so they are not cleaned up automatically +var _dbus_devices := {} func _init() -> void: - logger.debug("Initalizing InputPlumber. Found composite devices: " + str(composite_devices)) - object_manager.interfaces_added.connect(_on_interfaces_added) - object_manager.interfaces_removed.connect(_on_interfaces_removed) - composite_devices = get_devices() - for device in composite_devices: - composite_devices_map[device.dbus_path] = device - - # Ensure the global default config is created - var profiles_dir := ProjectSettings.globalize_path(PROFILES_DIR) - if DirAccess.make_dir_recursive_absolute(profiles_dir) != OK: - logger.error("Failed to create user profiles directory: " + profiles_dir) - return - var default_profile := FileAccess.open(DEFAULT_PROFILE, FileAccess.READ) - var default_profile_data := default_profile.get_as_text() - logger.debug("Writing default global profile to: " + DEFAULT_GLOBAL_PROFILE) - var global_default_profile := FileAccess.open(DEFAULT_GLOBAL_PROFILE, FileAccess.WRITE) - global_default_profile.store_string(default_profile_data) - - # Start a task that monitors whether or not InputPlumber is started or stopped, - # and emits signals when its running state changes. - started.connect(_on_inputplumber_started) - stopped.connect(_on_inputplumber_stopped) - var running_check := func(): - var update_running := func(): - var running := dbus.bus_exists(INPUT_PLUMBER_BUS) - if running == self.is_running: - return - self.is_running = running - if running: - started.emit() - else: - stopped.emit() - update_running.call_deferred() - system_thread.exec(running_check) - system_thread.scheduled_exec(running_check, 5000, SharedThread.ScheduledTaskType.RECURRING) - - -func _on_inputplumber_started() -> void: - logger.info("InputPlumber started") + # Ensure the default global profile exists in the user directory. + if not FileAccess.file_exists(DEFAULT_GLOBAL_PROFILE): + var file := FileAccess.open(DEFAULT_PROFILE, FileAccess.READ) + var content := file.get_as_text() + file.close() + if DirAccess.make_dir_recursive_absolute(PROFILES_DIR) != OK: + var logger := Log.get_logger("InputPlumber", Log.LEVEL.DEBUG) + logger.error("Failed to create gamepad profiles directory") + var new_file := FileAccess.open(DEFAULT_GLOBAL_PROFILE, FileAccess.WRITE) + new_file.store_string(content) + new_file.close() + + +func _ready() -> void: + # Add listeners for any new devices + var on_device_added := func(device: CompositeDevice): + var dbus_devices := device.dbus_devices + if dbus_devices.is_empty(): + return + var dbus_device := dbus_devices[0] + var dbus_path := device.dbus_path + _dbus_devices[dbus_path] = dbus_device + instance.composite_device_added.connect(on_device_added) - # Remove any Godot gamepad input maps and rely completely on InputPlumber for - # gamepad input - var actions := InputMap.get_actions() - var to_erase: Array[InputEvent] = [] - for action in actions: - var events := InputMap.action_get_events(action) - for event in events: - if event is InputEventJoypadButton or event is InputEventJoypadMotion: - logger.debug("Erasing mapping for", action, ":", event) - InputMap.action_erase_event(action, event) - + # Add listeners when devices are removed + var on_device_removed := func(dbus_path: String): + if not _dbus_devices.has(dbus_path): + return + _dbus_devices.erase(dbus_path) + instance.composite_device_removed.connect(on_device_removed) -func _on_inputplumber_stopped() -> void: - logger.warn("InputPlumber stopped") - - # Reload Godot input mappings if InputPlumber shuts down - logger.debug("Restoring input mappings from project settings") - InputMap.load_from_project_settings() + # Find all composite devices + var devices := instance.get_composite_devices() + for device in devices: + on_device_added.call(device) -func _on_interfaces_added(dbus_path: String) -> void: - logger.debug("Interfaces Added: " + str(dbus_path)) - if not "CompositeDevice" in dbus_path: +func _process(_delta: float) -> void: + if not instance: return - composite_devices = get_devices(dbus_path) - composite_devices_map.clear() - for device in composite_devices: - composite_devices_map[device.dbus_path] = device - - -func _on_interfaces_removed(dbus_path: String) -> void: - logger.debug("Interfaces Removed: " + str(dbus_path)) - if not "CompositeDevice" in dbus_path: + instance.process() + + + ## Load the given profile on the composite device, optionally specifying a profile +## modifier, which is a target device string (e.g. "deck", "ds5-edge", etc.) to +## adapt the profile for. This will update the profile with target-specific +## defaults, like mapping left/right pads to the DualSense center pad if no +## other mappings are defined. +static func load_target_modified_profile(device: CompositeDevice, path: String, profile_modifier: String = "") -> void: + var logger := Log.get_logger("InputPlumber", Log.LEVEL.DEBUG) + logger.debug("Loading Profile:", path) + if path == "" or not path.ends_with(".json") or not FileAccess.file_exists(path): + logger.error("Profile path:", path," is not a valid profile path.") + return + if profile_modifier.is_empty(): + device.load_profile_path(path) return - composite_devices = get_devices() - composite_devices_map.clear() - for device in composite_devices: - composite_devices_map[device.dbus_path] = device - if dbus_path.contains("CompositeDevice"): - composite_device_removed.emit(dbus_path) - - -## Returns true if InputPlumber can be used on this system -func supports_input_plumber() -> bool: - return dbus.bus_exists(INPUT_PLUMBER_BUS) - - -func get_objects_of(pattern: String) -> Array: - var devices: Array = [] - var device_paths := dbus.get_managed_objects(INPUT_PLUMBER_BUS, INPUT_PLUMBER_PATH) - logger.debug("Searching for " + pattern + " objects.") - # Loop through all objects on the bus - for obj in device_paths: - - var object := obj as DBusManager.ManagedObject - var path := object.path - var proxy := dbus.create_proxy(INPUT_PLUMBER_BUS, path) - #logger.debug("Found object: " + str(object) + " with path " + path) - if path.contains(pattern) and pattern == "CompositeDevice": - logger.debug("Found " + pattern + " in " + path) - var device := CompositeDevice.new(proxy) - devices.append(device) - continue - - if path.contains(pattern) and pattern == "event": - var device := EventDevice.new(proxy) - devices.append(device) - continue - - if path.contains(pattern) and pattern == "hidraw": - var device := HIDRawDevice.new(proxy) - devices.append(device) - continue - - if path.contains(pattern) and pattern == "iio": - var device := IIODevice.new(proxy) - devices.append(device) - continue - - if path.contains(pattern) and pattern == "dbus": - var device := DBusDevice.new(proxy) - devices.append(device) - continue - - if path.contains(pattern) and pattern == "gamepad": - var device := GamepadDevice.new(proxy) - devices.append(device) - continue - - if path.contains(pattern) and pattern == "keyboard": - var device := KeyboardDevice.new(proxy) - devices.append(device) - continue - - if path.contains(pattern) and pattern == "mouse": - var device := MouseDevice.new(proxy) - devices.append(device) - continue - logger.debug("Returning devices: " + str(devices)) - return devices - - -## Retrieves all CompositeDevices currently on the InputPlumber DBus interface. Will -## emit composite_device_added if the given dbus path is a new device, or -## composite_device_changed if it already existed -func get_devices(dbus_path: String = "") -> Array[CompositeDevice]: - logger.debug("Getting all composite devices.") - var new_device_list: Array[CompositeDevice] - new_device_list.assign(get_objects_of("CompositeDevice")) - var existing_devices: Array[CompositeDevice] - - # Only return new devices. Overriding devices breaks signaling. - for device in new_device_list: - var found: bool = false - for old_dev in composite_devices: - if old_dev.dbus_path == device.dbus_path: - existing_devices.append(old_dev) - found = true - if dbus_path == device.dbus_path: - composite_device_changed.emit(device) - break - - # New device found - if not found: - existing_devices.append(device) - set_intercept_mode_single(intercept_mode_current, device) - set_intercept_activation_single(intercept_triggers_current, \ - intercept_target_current, device) - composite_device_added.emit(device) - - return existing_devices - - -## Returns the [CompositeDevice] with the given DBus path -func get_device(dbus_path: String) -> CompositeDevice: - if dbus_path in composite_devices_map: - return composite_devices_map[dbus_path] - return null - - -## Sets all composite devices to the specified intercept mode. -func set_intercept_mode(mode: INTERCEPT_MODE) -> void: - logger.debug("Setting all composite devices to mode: " + str(mode)) - intercept_mode_current = mode - for d in composite_devices: - var device := d as CompositeDevice - set_intercept_mode_single(mode, device) - - -func set_intercept_mode_single(mode: INTERCEPT_MODE, device: CompositeDevice) -> void: - logger.debug("Setting composite device "+ device.dbus_path + " to mode: " + str(mode)) - match mode: - INTERCEPT_MODE.NONE: - device.intercept_mode = 0 - INTERCEPT_MODE.PASS: - device.intercept_mode = 1 - INTERCEPT_MODE.ALL: - device.intercept_mode = 2 - - -## Sets all composite devices to use the specified intercept actions. -func set_intercept_activation(triggers: PackedStringArray, target: String) -> void: - logger.debug("Setting all composite devices to intercept triggers: " + str(triggers) + " and target event: " + target) - intercept_triggers_current = triggers - intercept_target_current = target - for d in composite_devices: - var device := d as CompositeDevice - set_intercept_activation_single(triggers, target, device) - - -func set_intercept_activation_single(triggers: PackedStringArray, target: String, device: CompositeDevice) -> void: - logger.debug("Setting composite device "+ device.dbus_path + " intercept triggers: " + str(triggers) + " and target event: " + target) - device.set_intercept_activation(triggers, target) - - -class Manager extends Resource: - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - func create_composite_device(config_path: String) -> String: - var result := _proxy.call_method(IFACE_MANAGER, "CreateCompositeDevice", [config_path]) - if not result: - return "" - var args := result.get_args() - if args.size() != 1: - return "" - if not args[0] is String: - return "" - return args[0] - - -class CompositeDevice extends Resource: - signal updated - - var _proxy: DBusManager.Proxy - var dbus_targets: Array[DBusDevice] - var dbus_path: String - var logger := Log.get_logger("InputPlumber CompositeDevice", Log.LEVEL.INFO) - - func _init(proxy: DBusManager.Proxy) -> void: - var dbus := load("res://core/global/dbus_system.tres") as DBusManager - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - for path in self.dbus_devices: - var device := DBusDevice.new(dbus.create_proxy(INPUT_PLUMBER_BUS, path)) - dbus_targets.append(device) - dbus_path = _proxy.path - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - var name: String: - get: - var property = _proxy.get_property(IFACE_COMPOSITE_DEVICE, "Name") - if not property is String: - return "" - return property - - var profile_name: String: - get: - var property = _proxy.get_property(IFACE_COMPOSITE_DEVICE, "ProfileName") - if not property is String: - return "" - return property - - var intercept_mode: int: - set(v): - #print("Setting mode " + str(v) + " on " + self.dbus_path) - _proxy.set_property(IFACE_COMPOSITE_DEVICE, "InterceptMode", DBus.uint32(v)) - get: - var property = _proxy.get_property(IFACE_COMPOSITE_DEVICE, "InterceptMode") - if not property is int: - return -1 - return property - - var capabilities: PackedStringArray: - get: - var property = _proxy.get_property(IFACE_COMPOSITE_DEVICE, "Capabilities") - if not property is Array: - return [] - return property - - var target_capabilities: PackedStringArray: - get: - var property = _proxy.get_property(IFACE_COMPOSITE_DEVICE, "TargetCapabilities") - if not property is Array: - return [] - return property - - var dbus_devices: PackedStringArray: - get: - var property = _proxy.get_property(IFACE_COMPOSITE_DEVICE, "DbusDevices") - if not property is Array: - return [] - return property - - var source_device_paths: PackedStringArray: - get: - var property = _proxy.get_property(IFACE_COMPOSITE_DEVICE, "SourceDevicePaths") - if not property is Array: - return [] - return property - - var target_devices: PackedStringArray: - get: - var property = _proxy.get_property(IFACE_COMPOSITE_DEVICE, "TargetDevices") - if not property is Array: - return [] - return property - - func set_target_devices(devices: PackedStringArray) -> void: - _proxy.call_method( IFACE_COMPOSITE_DEVICE, "SetTargetDevices", [devices], "as") - - ## Load the given profile on the composite device, optionally specifying a profile - ## modifier, which is a target device string (e.g. "deck", "ds5-edge", etc.) to - ## adapt the profile for. This will update the profile with target-specific - ## defaults, like mapping left/right pads to the DualSense center pad if no - ## other mappings are defined. - func target_modify_profile(path: String, profile_modifier: String = "") -> void: - logger.debug("Loading Profile:", path) - if path == "" or not path.ends_with(".json") or not FileAccess.file_exists(path): - logger.error("Profile path:", path," is not a valid profile path.") - return - if profile_modifier.is_empty(): - load_profile_path(path) - return - - var profile := InputPlumberProfile.load(path) - - var c_pad_cap = "Touchpad:CenterPad:Motion" - var l_pad_cap = "Touchpad:LeftPad:Motion" - var r_pad_cap = "Touchpad:RightPad:Motion" - var mouse_cap = "Mouse:Motion" - - if !profile_modifier.is_empty(): - var mapped_capabilities := profile.to_json() - logger.debug("Mapped Capabilities (before):", mapped_capabilities) - match profile_modifier: - "deck": - logger.debug("Steam Deck Profile") - if c_pad_cap not in mapped_capabilities: - logger.debug("Map", c_pad_cap) - var c_pad_map := InputPlumberMapping.from_source_capability(c_pad_cap) - var r_pad_event := InputPlumberEvent.from_capability(r_pad_cap) - c_pad_map.target_events = [r_pad_event] - profile.mapping.append(c_pad_map) - - "ds5", "ds5-edge": - logger.debug("Dualsense Profile") - if l_pad_cap not in mapped_capabilities: - logger.debug("Map", l_pad_cap) - var l_pad_map := InputPlumberMapping.from_source_capability(l_pad_cap) - var c_pad_event := InputPlumberEvent.from_capability(c_pad_cap) - l_pad_map.target_events = [c_pad_event] - profile.mapping.append(l_pad_map) - if r_pad_cap not in mapped_capabilities: - logger.debug("Map", r_pad_cap) - var r_pad_map := InputPlumberMapping.from_source_capability(r_pad_cap) - var c_pad_event := InputPlumberEvent.from_capability(c_pad_cap) - r_pad_map.target_events = [c_pad_event] - profile.mapping.append(r_pad_map) - - _: - logger.debug("Target device needs no modifications:", profile_modifier) - - mapped_capabilities = profile.to_json() - logger.debug("Mapped Capabilities (after):", mapped_capabilities) - - path = path.rstrip(".json") + profile_modifier + ".json" - if profile.save(path) != OK: - logger.error("Failed to save", profile.name, "to", path) - return - load_profile_path(path) - - func load_profile_path(path: String) -> void: - # Get the absolute path if this is a resource path - var absolute_path := path - if path.begins_with("res://") or path.begins_with("user://"): - absolute_path = ProjectSettings.globalize_path(path) - _proxy.call_method(IFACE_COMPOSITE_DEVICE, "LoadProfilePath", [absolute_path], "s") - - func send_event(action: String, value: Variant) -> void: - _proxy.call_method( IFACE_COMPOSITE_DEVICE, "SendEvent", [action, value], "sv") - - func send_button_chord(actions: PackedStringArray) -> void: - _proxy.call_method( IFACE_COMPOSITE_DEVICE, "SendButtonChord", [actions], "as") - - func set_intercept_activation(triggers: PackedStringArray, target_event: String) -> void: - _proxy.call_method( IFACE_COMPOSITE_DEVICE, "SetInterceptActivation", [triggers, target_event], "ass") - - -class EventDevice extends Resource: - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - var name: String: - get: - var property = _proxy.get_property(IFACE_EVENT_DEVICE, "Name") - if not property is String: - return "" - return property - - var device_path: String: - get: - var property = _proxy.get_property(IFACE_EVENT_DEVICE, "DevicePath") - if not property is String: - return "" - return property - - var phys_path: String: - get: - var property = _proxy.get_property(IFACE_EVENT_DEVICE, "PhysPath") - if not property is String: - return "" - return property - - var sysfs_path: String: - get: - var property = _proxy.get_property(IFACE_EVENT_DEVICE, "SysfsPath") - if not property is String: - return "" - return property - - var unique_id: String: - get: - var property = _proxy.get_property(IFACE_EVENT_DEVICE, "UniqueId") - if not property is String: - return "" - return property - - var handlers: PackedStringArray: - get: - var property = _proxy.get_property(IFACE_EVENT_DEVICE, "Handlers") - if not property is Array: - return [] - return property - - -class HIDRawDevice extends Resource: - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - var interface_number: int: - get: - var property = _proxy.get_property(IFACE_HIDRAW_DEVICE, "InterfaceNumber") - if not property is int: - return -1 - return property - - var manufacturer: String: - get: - var property = _proxy.get_property(IFACE_HIDRAW_DEVICE, "Manufacturer") - if not property is String: - return "" - return property - - var path: String: - get: - var property = _proxy.get_property(IFACE_HIDRAW_DEVICE, "Path") - if not property is String: - return "" - return property - - var product: String: - get: - var property = _proxy.get_property(IFACE_HIDRAW_DEVICE, "Product") - if not property is String: - return "" - return property - - var product_id: String: - get: - var property = _proxy.get_property(IFACE_HIDRAW_DEVICE, "ProductId") - if not property is String: - return "" - return property - - var release_number: String: - get: - var property = _proxy.get_property(IFACE_HIDRAW_DEVICE, "ReleaseNumber") - if not property is String: - return "" - return property - - var serial_number: String: - get: - var property = _proxy.get_property(IFACE_HIDRAW_DEVICE, "SerialNumber") - if not property is String: - return "" - return property - - var vendor_id: String: - get: - var property = _proxy.get_property(IFACE_HIDRAW_DEVICE, "VendorId") - if not property is String: - return "" - return property - - -class IIODevice extends Resource: - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - var id: String: - get: - var property = _proxy.get_property(IFACE_IIO_DEVICE, "Id") - if not property is int: - return "" - return property - - var name: String: - get: - var property = _proxy.get_property(IFACE_IIO_DEVICE, "Name") - if not property is String: - return "" - return property - - -class KeyboardDevice extends Resource: - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - var name: String: - get: - var property = _proxy.get_property(IFACE_KEYBOARD_DEVICE, "Name") - if not property is String: - return "" - return property - - func send_key(key: String, pressed: bool) -> void: - _proxy.call_method(IFACE_KEYBOARD_DEVICE, "SendKey", [key, pressed]) - - -class MouseDevice extends Resource: - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - var name: String: - get: - var property = _proxy.get_property(IFACE_MOUSE_DEVICE, "Name") - if not property is String: - return "" - return property - - -class GamepadDevice extends Resource: - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - var name: String: - get: - var property = _proxy.get_property(IFACE_GAMEPAD_DEVICE, "Name") - if not property is String: - return "" - return property - - -class DBusDevice extends Resource: - signal input_event(type_code: String, value: float) - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - #print("Creating DBusDevice!") - _proxy = proxy - _proxy.message_received.connect(_on_message_received) - _proxy.thread.exec(_proxy.watch.bind(IFACE_DBUS_DEVICE, "InputEvent")) - - func _on_message_received(msg: DBusMessage) -> void: - if not msg: - return - if msg.get_member() != "InputEvent": - return - var args := msg.get_args() - if args.size() < 2: - return - #print("Got InputEvent " + str(args)) - #print(str(self.get_instance_id())) - #print(str(self.get_rid())) - input_event.emit(args[0], args[1]) - var name: String: - get: - var property = _proxy.get_property(IFACE_DBUS_DEVICE, "Name") - if not property is String: - return "" - return property + var profile := InputPlumberProfile.load(path) + + var c_pad_cap = "Touchpad:CenterPad:Motion" + var l_pad_cap = "Touchpad:LeftPad:Motion" + var r_pad_cap = "Touchpad:RightPad:Motion" + var mouse_cap = "Mouse:Motion" + + if !profile_modifier.is_empty(): + var mapped_capabilities := profile.to_json() + logger.debug("Mapped Capabilities (before):", mapped_capabilities) + match profile_modifier: + "deck": + logger.debug("Steam Deck Profile") + if c_pad_cap not in mapped_capabilities: + logger.debug("Map", c_pad_cap) + var c_pad_map := InputPlumberMapping.from_source_capability(c_pad_cap) + var r_pad_event := InputPlumberEvent.from_capability(r_pad_cap) + c_pad_map.target_events = [r_pad_event] + profile.mapping.append(c_pad_map) + + "ds5", "ds5-edge": + logger.debug("Dualsense Profile") + if l_pad_cap not in mapped_capabilities: + logger.debug("Map", l_pad_cap) + var l_pad_map := InputPlumberMapping.from_source_capability(l_pad_cap) + var c_pad_event := InputPlumberEvent.from_capability(c_pad_cap) + l_pad_map.target_events = [c_pad_event] + profile.mapping.append(l_pad_map) + if r_pad_cap not in mapped_capabilities: + logger.debug("Map", r_pad_cap) + var r_pad_map := InputPlumberMapping.from_source_capability(r_pad_cap) + var c_pad_event := InputPlumberEvent.from_capability(c_pad_cap) + r_pad_map.target_events = [c_pad_event] + profile.mapping.append(r_pad_map) + + _: + logger.debug("Target device needs no modifications:", profile_modifier) + + mapped_capabilities = profile.to_json() + logger.debug("Mapped Capabilities (after):", mapped_capabilities) + + path = path.rstrip(".json") + profile_modifier + ".json" + if profile.save(path) != OK: + logger.error("Failed to save", profile.name, "to", path) + return + device.load_profile_path(path) diff --git a/core/systems/input/input_plumber.tres b/core/systems/input/input_plumber.tres index 60a89d180..64f50b4ab 100644 --- a/core/systems/input/input_plumber.tres +++ b/core/systems/input/input_plumber.tres @@ -1,6 +1,3 @@ -[gd_resource type="Resource" script_class="InputPlumber" load_steps=2 format=3 uid="uid://ct1hxnffwtqvl"] - -[ext_resource type="Script" path="res://core/systems/input/input_plumber.gd" id="1_0njvc"] +[gd_resource type="InputPlumberInstance" format=3 uid="uid://e2bevy4j4rx2"] [resource] -script = ExtResource("1_0njvc") diff --git a/core/systems/input/input_plumber_profile_test.gd b/core/systems/input/input_plumber_profile_test.gd index 0d523d670..ed9c33000 100644 --- a/core/systems/input/input_plumber_profile_test.gd +++ b/core/systems/input/input_plumber_profile_test.gd @@ -3,7 +3,7 @@ extends GutTest func test_load() -> void: var profile := InputPlumberProfile.load("res://assets/gamepad/profiles/default.json") - assert_eq(profile.name, "Default") + assert_eq(profile.name, "OpenGamepadUI Default") func test_save() -> void: diff --git a/core/systems/input/input_plumber_test.gd b/core/systems/input/input_plumber_test.gd index 336420591..238dd6b09 100644 --- a/core/systems/input/input_plumber_test.gd +++ b/core/systems/input/input_plumber_test.gd @@ -1,45 +1,51 @@ -extends Node2D - -var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber - - -# Called when the node enters the scene tree for the first time. -func _ready(): - for compo in input_plumber.composite_devices: - print(compo.dbus_targets) - for dt in compo.dbus_targets: - dt.input_event.connect(_on_input_event) - - input_plumber.composite_device_added.connect(_on_dev_add) - input_plumber.composite_device_removed.connect(_on_dev_rm) - input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.ALL) - get_tree().root.mode = Window.MODE_MINIMIZED - - -# Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(delta): - pass - - -func _on_input_event(event: String, value: float) -> void: - print("Got input event " + event + " with value " + str(value)) - if event == "ui_guide": - var pressed = value == 1.0 - for device in input_plumber.composite_devices: - device.intercept_mode = 0 - device.send_event("Gamepad:Button:Guide", pressed) - device.intercept_mode = InputPlumber.INTERCEPT_MODE.ALL as int - - if event == "ui_accept": - for device in input_plumber.composite_devices: - var intercept_mode = device.intercept_mode - device.intercept_mode = 0 - device.send_button_chord(["Gamepad:Button:Guide", "Gamepad:Button:South"]) - device.intercept_mode = InputPlumber.INTERCEPT_MODE.ALL as int - - -func _on_dev_add() -> void: - print("Newbie doobie") - -func _on_dev_rm() -> void: - print("bubye") +extends GutTest + + +# Configure the given composite device to set intercept mode and print input +# events +func _watch_device(device: CompositeDevice) -> void: + gut.p(" Found device " + str(device) + " at " + device.dbus_path) + device.intercept_mode = InputPlumberInstance.INTERCEPT_MODE_ALL + assert_eq(device.intercept_mode, InputPlumberInstance.INTERCEPT_MODE_ALL) + var dbus_devices := device.dbus_devices + if dbus_devices.is_empty(): + gut.p(" No dbus devices found for device") + return + var dbus_device := dbus_devices[0] + gut.p(" Found DBus device: " + str(dbus_device) + " at " + dbus_device.dbus_path) + var on_input_event := func(event: String, value: float): + gut.p("[DBus Event] " + event + ": " + str(value)) + dbus_device.input_event.connect(on_input_event) + + +func test_inputplumber() -> void: + var inputplumber := InputPlumber.new() + inputplumber.instance = load("res://core/systems/input/input_plumber.tres") + add_child_autoqfree(inputplumber) + + if not inputplumber.instance.is_running(): + pass_test("InputPlumber is not running. Skipping tests.") + return + + # Set intercept mode + gut.p("Setting intercept mode to ALL") + inputplumber.instance.intercept_mode = InputPlumberInstance.INTERCEPT_MODE_ALL + assert_eq(inputplumber.instance.intercept_mode, InputPlumberInstance.INTERCEPT_MODE_ALL) + + # Find all composite devices + gut.p("Discovering all composite devices") + var devices := inputplumber.instance.get_composite_devices() + for device in devices: + _watch_device(device) + + # Add listeners for any new devices + inputplumber.instance.composite_device_added.connect(_watch_device) + + # Add listeners when devices are removed + var on_device_removed := func(dbus_path: String): + gut.p("Device was removed: " + dbus_path) + inputplumber.instance.composite_device_removed.connect(on_device_removed) + + await wait_seconds(30, "Waiting 30s... Press buttons to test") + + inputplumber.instance.intercept_mode = InputPlumberInstance.INTERCEPT_MODE_NONE diff --git a/core/systems/input/input_plumber_test.tscn b/core/systems/input/input_plumber_test.tscn deleted file mode 100644 index 659851bfd..000000000 --- a/core/systems/input/input_plumber_test.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://djylrf6b7nxyx"] - -[ext_resource type="Script" path="res://core/systems/input/input_plumber_test.gd" id="1_x2aky"] - -[node name="InputPlumberTest" type="Node2D"] -script = ExtResource("1_x2aky") diff --git a/core/systems/input/input_plumber_test_old.tscn b/core/systems/input/input_plumber_test_old.tscn new file mode 100644 index 000000000..88349a4ef --- /dev/null +++ b/core/systems/input/input_plumber_test_old.tscn @@ -0,0 +1,52 @@ +[gd_scene load_steps=2 format=3 uid="uid://b401m3qcqa8yd"] + +[sub_resource type="GDScript" id="GDScript_k2tux"] +script/source = "extends Node2D + +var input_plumber := load(\"res://core/systems/input/input_plumber.tres\") as InputPlumber + + +# Called when the node enters the scene tree for the first time. +func _ready(): + for compo in input_plumber.composite_devices: + print(compo.dbus_targets) + for dt in compo.dbus_targets: + dt.input_event.connect(_on_input_event) + + input_plumber.composite_device_added.connect(_on_dev_add) + input_plumber.composite_device_removed.connect(_on_dev_rm) + input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.ALL) + get_tree().root.mode = Window.MODE_MINIMIZED + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass + + +func _on_input_event(event: String, value: float) -> void: + print(\"Got input event \" + event + \" with value \" + str(value)) + if event == \"ui_guide\": + var pressed = value == 1.0 + for device in input_plumber.composite_devices: + device.intercept_mode = 0 + device.send_event(\"Gamepad:Button:Guide\", pressed) + device.intercept_mode = InputPlumber.INTERCEPT_MODE.ALL as int + + if event == \"ui_accept\": + for device in input_plumber.composite_devices: + var intercept_mode = device.intercept_mode + device.intercept_mode = 0 + device.send_button_chord([\"Gamepad:Button:Guide\", \"Gamepad:Button:South\"]) + device.intercept_mode = InputPlumber.INTERCEPT_MODE.ALL as int + + +func _on_dev_add() -> void: + print(\"Newbie doobie\") + +func _on_dev_rm() -> void: + print(\"bubye\") +" + +[node name="InputPlumberTest" type="Node2D"] +script = SubResource("GDScript_k2tux") diff --git a/core/systems/input/keyboard_opener.gd b/core/systems/input/keyboard_opener.gd new file mode 100644 index 000000000..372a5e6e2 --- /dev/null +++ b/core/systems/input/keyboard_opener.gd @@ -0,0 +1,106 @@ +@tool +@icon("res://assets/ui/icons/keyboard-rounded.svg") +extends Node +class_name KeyboardOpener + +## Node that can open the on-screen keyboard in response to a signal firing + +## Reference to the on-screen keyboard instance to open when the OSK action is +## pressed. +var osk := load("res://core/global/keyboard_instance.tres") as KeyboardInstance +## The Global State Machine +var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine +## Popup state machine to show the OSK popup. +var popup_state_machine := ( + preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine +) +var in_game_menu_state := preload("res://assets/state/states/in_game_menu.tres") as State +var main_menu_state := preload("res://assets/state/states/main_menu.tres") as State +var quick_bar_state := preload("res://assets/state/states/quick_bar_menu.tres") as State +var osk_state := preload("res://assets/state/states/osk.tres") as State +var popup_state := preload("res://assets/state/states/popup.tres") as State + +## Signal on our parent node to connect to +var on_signal: String +## Target control node to send keyboard input to. +var target: Control + +## The type of keyboard behavior. An "X11" keyboard will send keyboard events +## to a running game. A "Godot" keyboard will send text input to a control node. +@export var type: KeyboardContext.TYPE = KeyboardContext.TYPE.X11: + set(v): + type = v + notify_property_list_changed() + + +func _init() -> void: + ready.connect(_on_ready) + + +func _on_ready() -> void: + notify_property_list_changed() + # Do nothing if running in the editor + if Engine.is_editor_hint(): + return + if on_signal != "": + get_parent().connect(on_signal, _on_signal) + + +## Fires when the given signal is emitted. +func _on_signal(_arg1: Variant = null, _arg2: Variant = null, _arg3: Variant = null, _arg4: Variant = null): + var state := popup_state_machine.current_state() + if state == osk_state: + osk.close() + popup_state_machine.pop_state() + return + + if state in [main_menu_state, in_game_menu_state, quick_bar_state]: + popup_state_machine.replace_state(osk_state) + else: + popup_state_machine.push_state(osk_state) + state_machine.push_state(popup_state) + + var context := KeyboardContext.new() + context.type = type + if type == KeyboardContext.TYPE.GODOT: + context.target = target + osk.open(context) + + +# Customize editor properties that we expose. Here we dynamically look up +# the parent node's signals so we can display them in a list. +func _get_property_list(): + # By default, `on_signal` is not visible in the editor. + var property_usage := PROPERTY_USAGE_NO_EDITOR + + var parent_signals := [] + if get_parent() != null: + property_usage = PROPERTY_USAGE_DEFAULT + for sig in get_parent().get_signal_list(): + parent_signals.push_back(sig["name"]) + + # Build the exported node properties + var properties := [] + properties.append( + { + "name": "on_signal", + "type": TYPE_STRING, + "usage": property_usage, # See above assignment. + "hint": PROPERTY_HINT_ENUM, + "hint_string": ",".join(parent_signals) + } + ) + + # Only show the control node target property if "Godot" type is selected + if self.type == KeyboardContext.TYPE.GODOT: + properties.append( + { + "name": "target", + "type": TYPE_OBJECT, + "usage": property_usage, + "hint": PROPERTY_HINT_NODE_TYPE, + "hint_string": "Control", + } + ) + + return properties diff --git a/core/systems/input/overlay_mode_input_manager.gd b/core/systems/input/overlay_mode_input_manager.gd index f060c8fe3..13594c4fd 100644 --- a/core/systems/input/overlay_mode_input_manager.gd +++ b/core/systems/input/overlay_mode_input_manager.gd @@ -1,4 +1,4 @@ -@icon("res://assets/icons/navigation.svg") +@icon("res://assets/editor-icons/material-symbols-joystick.svg") extends Node class_name OverlayInputManager @@ -14,7 +14,7 @@ class_name OverlayInputManager ## The audio manager to use to adjust the audio when audio input events happen. var audio_manager := load("res://core/global/audio_manager.tres") as AudioManager ## InputPlumber receives and sends DBus input events. -var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber +var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance ## LaunchManager provides context on the currently running app so we can switch profiles var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager ## The Global State Machine @@ -39,8 +39,12 @@ var logger := Log.get_logger("InputManager(Overlay Mode)", Log.LEVEL.INFO) func _ready() -> void: add_to_group("InputManager") input_plumber.composite_device_added.connect(_watch_dbus_device) + input_plumber.started.connect(_init_inputplumber) + _init_inputplumber() - for device in input_plumber.composite_devices: + +func _init_inputplumber() -> void: + for device in input_plumber.get_composite_devices(): _watch_dbus_device(device) @@ -71,6 +75,13 @@ func _send_input(dbus_path: String, action: String, pressed: bool, strength: flo ## https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#how-does-it-work func _input(event: InputEvent) -> void: logger.debug("Got input event to handle: " + str(event)) + # Don't process Godot events if InputPlumber is running + if input_plumber.is_running() and not InputManager.is_inputplumber_event(event): + if event is InputEventJoypadButton or event is InputEventJoypadMotion: + logger.debug("Skipping Godot event while InputPlumber is running:", event) + get_viewport().set_input_as_handled() + return + var dbus_path := event.get_meta("dbus_path", "") as String # Consume double inputs for controllers with DPads that have TRIGGER_HAPPY events @@ -295,7 +306,7 @@ func _send_steam_chord(event: InputEvent) -> bool: # Steam On-Screen Keyboard elif event.is_action_pressed("ogui_osk_ov"): logger.debug("Trigger Steam OSK") - chord.append("Gamepad:Button:East") + chord.append("Gamepad:Button:North") # Steam Video-Capture elif event.is_action_pressed("ogui_vc_ov"): @@ -320,8 +331,8 @@ func _return_chord(actions: PackedStringArray) -> void: # TODO: Figure out a way to get the device who sent the event through # Input.parse_input_event so we don't do this terrible loop. This is awful. logger.debug("Return events to InputPlumber: " + str(actions)) - for device in input_plumber.composite_devices: - device.intercept_mode = InputPlumber.INTERCEPT_MODE.PASS + for device in input_plumber.get_composite_devices(): + device.intercept_mode = InputPlumberInstance.INTERCEPT_MODE_PASS device.send_button_chord(actions) @@ -347,9 +358,11 @@ func _audio_input(event: InputEvent) -> void: return -func _watch_dbus_device(device: InputPlumber.CompositeDevice) -> void: - for target in device.dbus_targets: - logger.debug("Adding watch for " + device.name + " " + target.name) +func _watch_dbus_device(device: CompositeDevice) -> void: + for target in device.dbus_devices: + if target.input_event.is_connected(_on_dbus_input_event.bind(device.dbus_path)): + continue + logger.debug("Adding watch for " + device.name + " " + target.dbus_path) logger.debug(str(target.get_instance_id())) logger.debug(str(target.get_rid())) target.input_event.connect(_on_dbus_input_event.bind(device.dbus_path)) @@ -357,8 +370,9 @@ func _watch_dbus_device(device: InputPlumber.CompositeDevice) -> void: func _on_dbus_input_event(event: String, value: float, dbus_path: String) -> void: var pressed := value == 1.0 - logger.debug("Handling dbus input event: " + event + " pressed: " + str(pressed)) - var action = event + logger.debug("Handling dbus input event from" + dbus_path + ": " + event + " pressed: " + str(pressed)) + + var action := event match event: "ui_accept": action = "ogui_south_ov" diff --git a/core/systems/input/overlay_mode_input_manager.tscn b/core/systems/input/overlay_mode_input_manager.tscn index 61b5aed19..e3973446b 100644 --- a/core/systems/input/overlay_mode_input_manager.tscn +++ b/core/systems/input/overlay_mode_input_manager.tscn @@ -1,6 +1,10 @@ -[gd_scene load_steps=2 format=3 uid="uid://bxnb8t7i08vma"] +[gd_scene load_steps=3 format=3 uid="uid://bxnb8t7i08vma"] [ext_resource type="Script" path="res://core/systems/input/overlay_mode_input_manager.gd" id="1_fvwoc"] +[ext_resource type="Script" path="res://core/systems/input/input_plumber.gd" id="2_vf8uv"] [node name="InputManager" type="Node"] script = ExtResource("1_fvwoc") + +[node name="InputPlumber" type="Node" parent="."] +script = ExtResource("2_vf8uv") diff --git a/core/systems/launcher/app_lifecycle_hook.gd b/core/systems/launcher/app_lifecycle_hook.gd new file mode 100644 index 000000000..3ea085a49 --- /dev/null +++ b/core/systems/launcher/app_lifecycle_hook.gd @@ -0,0 +1,39 @@ +extends RefCounted +class_name AppLifecycleHook + +## Base class for executing callbacks at certain points of an app's lifecycle. +## +## This class provides an interface for executing arbitrary callbacks at certain +## points of an application's lifecycle. This can allow [Library] implementations +## the ability to execute actions when apps are about to start, have started, +## or have exited. + +## The type of hook determines where in the application's lifecycle this hook +## should be executed. +enum TYPE { + ## Executes right before an app is launched + PRE_LAUNCH, + ## Executes right after an app is launched + LAUNCH, + ## Executed after app exit + EXIT, +} + +var _hook_type: TYPE + + +func _init(hook_type: TYPE) -> void: + _hook_type = hook_type + + +## Executes whenever an app from this library reaches the stage in its lifecycle +## designated by the hook type. E.g. a `PRE_LAUNCH` hook will have this method +## called whenever an app is about to launch. +func execute(item: LibraryLaunchItem) -> void: + pass + + +## Returns the hook type, which designates where in the application's lifecycle +## the hook should be executed. +func get_type() -> TYPE: + return _hook_type diff --git a/core/systems/launcher/interactive_process.gd b/core/systems/launcher/interactive_process.gd index 778c0f30b..18a8baf05 100644 --- a/core/systems/launcher/interactive_process.gd +++ b/core/systems/launcher/interactive_process.gd @@ -1,75 +1,100 @@ +@icon("res://assets/editor-icons/devicon-plain--bash.svg") extends Resource class_name InteractiveProcess +# DEPRECATED: Use the [Pty] node instead + ## Class for starting an interacting with a process through a psuedo terminal ## ## Starts an interactive session -var pty: PTY +var pty: Pty var cmd: String var args: PackedStringArray = [] var pid: int +var platform := load("res://core/global/platform.tres") as Platform +var registry := load("res://core/systems/resource/resource_registry.tres") as ResourceRegistry +var lines_mutex := Mutex.new() +var lines_buffer := PackedStringArray() var logger := Log.get_logger("InteractiveProcess", Log.LEVEL.INFO) func _init(command: String, cmd_args: PackedStringArray = []) -> void: - cmd = command - args = cmd_args + # Check to see if this OS requires running the command through a binary + # compatibility tool. + if platform and platform.os: + var compatibility_cmd := platform.os.get_binary_compatibility_cmd(command, cmd_args) + if not compatibility_cmd.is_empty(): + logger.debug("Using binary compatibility tool") + self.cmd = compatibility_cmd.pop_front() + self.args = PackedStringArray(compatibility_cmd) + return + + self.cmd = command + self.args = cmd_args ## Start the interactive process func start() -> int: - pty = PTY.new() - if pty.open() != OK: - pty = null - return ERR_CANT_CREATE + # Create a new PTY instance and start the process + self.pty = Pty.new() + self.pty.exec(self.cmd, self.args) + self.pty.line_written.connect(_on_line_written) + + # The new [Pty] is a node, so it must be added to the scene tree + self.registry.add_child(self.pty) + + return OK - pid = pty.create_process(cmd, args) - if pid < 0: - pty = null - return ERR_CANT_FORK - return OK +func _on_line_written(line: String): + logger.trace("PTY:", line) + self.lines_mutex.lock() + self.lines_buffer.append(line) + self.lines_mutex.unlock() ## Send the given input to the running process func send(input: String) -> void: if not pty: return - logger.debug("Writing input: " + input) + #if pty.write_line(input) < 0: if pty.write(input.to_utf8_buffer()) < 0: logger.debug("Unable to write to PTY") ## Read from the stdout of the running process -func read(chunk_size: int = 1024) -> String: +func read(_chunk_size: int = 1024) -> String: if not pty: logger.debug("Unable to read from closed PTY") return "" # Keep reading from the process until the buffer is empty - var output := "" - var buffer := pty.read(chunk_size) - while buffer.size() != 0: - output += buffer.get_string_from_utf8() - buffer = pty.read(chunk_size) + self.lines_mutex.lock() + var output := "\n".join(self.lines_buffer) + self.lines_buffer = PackedStringArray() + self.lines_mutex.unlock() return output ## Stop the given process func stop() -> void: - logger.debug("Stopping pid: " + str(pid)) - OS.kill(pid) - pty = null + logger.debug("Stopping pid: " + str(self.pid)) + self.pty.kill() + self.registry.remove_child(self.pty) + self.pty = null ## Returns whether or not the interactive process is still running func is_running() -> bool: - return OS.is_process_running(pid) + if not pty: + return false + return pty.running -func output_to_log_file(log_file: FileAccess, chunk_size: int = 1024) -> int: +# TODO: Fixme +func output_to_log_file(log_file: FileAccess, _chunk_size: int = 1024) -> int: if not log_file: logger.warn("Unable to log output. Log file has not been opened.") return ERR_FILE_CANT_OPEN @@ -79,10 +104,6 @@ func output_to_log_file(log_file: FileAccess, chunk_size: int = 1024) -> int: return ERR_DOES_NOT_EXIST # Keep reading from the process until the buffer is empty - var buffer := pty.read(chunk_size) - while buffer.size() != 0: - log_file.store_buffer(buffer) - buffer = pty.read(chunk_size) log_file.flush() return OK diff --git a/core/systems/launcher/launch_manager.gd b/core/systems/launcher/launch_manager.gd deleted file mode 100644 index b7e36fa9e..000000000 --- a/core/systems/launcher/launch_manager.gd +++ /dev/null @@ -1,15 +0,0 @@ -@icon("res://assets/editor-icons/ph-rocket-launch-fill.svg") -extends Node - -@export var launch_manager := preload("res://core/global/launch_manager.tres") as LaunchManager - -@onready var overlay_display = OS.get_environment("DISPLAY") - - -func _init() -> void: - launch_manager._load_persist_data() - - -# TODO: Replace this with dbus signaling. This is super shitty. -func _process(delta) -> void: - launch_manager.check_running() diff --git a/core/systems/launcher/launch_manager.tscn b/core/systems/launcher/launch_manager.tscn deleted file mode 100644 index 134a43c7a..000000000 --- a/core/systems/launcher/launch_manager.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://cbboox5bujlx1"] - -[ext_resource type="Script" path="res://core/systems/launcher/launch_manager.gd" id="1_h73xp"] - -[node name="LaunchManager" type="Node"] -script = ExtResource("1_h73xp") diff --git a/core/systems/launcher/launcher.gd b/core/systems/launcher/launcher.gd index 38971ffa5..11271ebe9 100644 --- a/core/systems/launcher/launcher.gd +++ b/core/systems/launcher/launcher.gd @@ -1,54 +1,20 @@ -@icon("res://assets/icons/loader.svg") +@icon("res://assets/editor-icons/ph-rocket-launch-fill.svg") extends Node class_name Launcher -const LaunchManager := preload("res://core/global/launch_manager.tres") -var LibraryManager := preload("res://core/global/library_manager.tres") -const NotificationManager := preload("res://core/global/notification_manager.tres") +@export var launch_manager := preload("res://core/global/launch_manager.tres") as LaunchManager -var state_machine := ( - preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine -) -var in_game_state := preload("res://assets/state/states/in_game.tres") as State -var installing := {} +@onready var overlay_display = OS.get_environment("DISPLAY") -# The library item to launch -@export var launch_item: LibraryLaunchItem -# Signal on our parent to connect to -@export var signal_name: String = "button_up" -@onready var parent: Node = get_parent() +func _init() -> void: + launch_manager._load_persist_data() func _ready() -> void: - parent.connect(signal_name, _on_launch) - - -func _on_launch(): - # Resume if the game is running already - if LaunchManager.is_running(launch_item.name): - state_machine.set_state([in_game_state]) - return - - # If the app isn't installed, install it. - if not launch_item.installed: - if launch_item.name in installing and installing[launch_item.name]: - return - var notify := Notification.new("Installing " + launch_item.name) - NotificationManager.show(notify) - LibraryManager.install(launch_item) - LibraryManager.item_installed.connect(_on_installed) - installing[launch_item.name] = true - return - - # Launch the game using launch manager - LaunchManager.launch(launch_item) - - -func _on_installed(item: LibraryLaunchItem, success: bool) -> void: - var verb := "failed" - if success: - verb = "completed" - var notify := Notification.new("Install " + verb + " for " + item.name) - NotificationManager.show(notify) - installing[launch_item.name] = false + var timer := Timer.new() + timer.autostart = true + timer.one_shot = false + timer.wait_time = 1 + timer.timeout.connect(launch_manager.check_running) + add_child(timer) diff --git a/core/systems/launcher/launcher.tscn b/core/systems/launcher/launcher.tscn index 2cac63ce8..e69de29bb 100644 --- a/core/systems/launcher/launcher.tscn +++ b/core/systems/launcher/launcher.tscn @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bargrxdqbu3gl"] - -[ext_resource type="Script" path="res://core/systems/launcher/launcher.gd" id="1_cd3hc"] - -[node name="Launcher" type="Node"] -script = ExtResource("1_cd3hc") diff --git a/core/systems/launcher/reaper.gd b/core/systems/launcher/reaper.gd index fb05d0d53..597a92b2e 100644 --- a/core/systems/launcher/reaper.gd +++ b/core/systems/launcher/reaper.gd @@ -11,9 +11,54 @@ enum SIG { ## Spawn a process with PR_SET_CHILD_SUBREAPER set so child processes will ## reparent themselves to OpenGamepadUI. Returns the PID of the spawned process. -static func create_process(cmd: String, args: PackedStringArray) -> int: - #return OS.create_process(cmd, args) - return LinuxThread.subreaper_create_process(cmd, args) +static func create_process(cmd: String, args: PackedStringArray, app_id: int = -1) -> int: + var logger := Log.get_logger("Reaper") + logger.debug("Got command to execute:", cmd, args) + var reaper_cmd := get_reaper_command() + logger.debug("Got reaper command:", reaper_cmd) + if reaper_cmd.is_empty(): + logger.warn("'reaper' binary not found, launching without reaper") + logger.info("Executing OS command:", cmd, args) + return OS.create_process(cmd, args) + + # Build the arguments for reaper. + var reaper_args := PackedStringArray() + if app_id >= 0: + reaper_args.append("SteamLaunch") + reaper_args.append("AppId={0}".format([app_id])) + reaper_args.append("--") + reaper_args.append(cmd) + reaper_args.append_array(args) + logger.info("Executing REAPER command:", reaper_cmd, reaper_args) + return OS.create_process(reaper_cmd, reaper_args) + +## Discovers the 'reaper' binary to execute commands with PR_SET_CHILD_SUBREAPER. +static func get_reaper_command() -> String: + var logger := Log.get_logger("Reaper") + var home := OS.get_environment("HOME") + var search_paths := [ + "./extensions/target/release", + "/usr/share/opengamepadui", + "{0}/.local/share/opengamepadui".format([home]), + "/run/current-system/sw/share/opengamepadui" + ] + + for path in search_paths: + logger.debug("Checking for reaper in path:", path) + # Check if the path exists, its the responsible thing to do. + if not DirAccess.dir_exists_absolute(path): + logger.debug("Path does not exists!") + continue + logger.debug("Path does exists!") + var directory := DirAccess.open(path) + if not directory: + logger.warn("Failed to open path:", path) + continue + if directory.file_exists("reaper"): + var reaper_path := "{0}/reaper".format([path]) + return reaper_path + + return "" # Kills the given PID and all its descendants @@ -26,6 +71,11 @@ static func reap(pid: int, sig: SIG = SIG.TERM) -> void: pids.reverse() var logger := Log.get_logger("Reaper") for p in pids: + # Don't kill the actual reaper PID + if p == pid: + logger.debug("Skipping killing reaper pid:", pid) + continue + # Kill PGID var cmd := "kill" var args := [sig_arg, "--", "-{0}".format([p])] @@ -72,9 +122,7 @@ static func get_pid_group(pid: int) -> int: static func get_pid_state(pid: int) -> String: var status: Dictionary = get_pid_status(pid) if not "State" in status: - var logger := Log.get_logger("Reaper") - logger.warn("Unable to check state of PID " + str(pid)) - return "" + return "D (dead)" return status["State"] @@ -104,7 +152,7 @@ static func get_pid_status(pid: int) -> Dictionary: var status_file: FileAccess = FileAccess.open(status_path, FileAccess.READ) if not status_file: var logger := Log.get_logger("Reaper") - logger.error("Unable to check status for pid: {0}".format([pid])) + logger.debug("Unable to check status for pid: {0}".format([pid])) return {} # Parse the status output @@ -172,7 +220,7 @@ static func is_gamescope_pid(pid: int) -> bool: var status: Dictionary = get_pid_status(pid) if not "Name" in status: var logger := Log.get_logger("Reaper") - logger.error("No name was found in pid status!") + logger.debug("No name was found in pid status!") return false if status["Name"] == "gamescope-wl": return true diff --git a/core/systems/launcher/running_app.gd b/core/systems/launcher/running_app.gd index 145feffff..974a1ea14 100644 --- a/core/systems/launcher/running_app.gd +++ b/core/systems/launcher/running_app.gd @@ -5,13 +5,15 @@ class_name RunningApp ## ## RunningApp contains details and methods around running applications -const Gamescope := preload("res://core/global/gamescope.tres") +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance ## Emitted when all child processes of the app are no longer running signal app_killed ## Emitted when the given app is gracefully stopped signal app_stopped +## Emitted when the app type is detected +signal app_type_detected ## Emitted when the window id of the given app has changed signal window_id_changed ## Emitted whenever the windows change for the app @@ -36,6 +38,12 @@ enum STATE { STOPPED, ## App is no longer running } +enum APP_TYPE { + UNKNOWN, + X11, + WAYLAND, +} + ## The LibraryLaunchItem associated with the running application var launch_item: LibraryLaunchItem ## The PID of the launched application @@ -79,6 +87,8 @@ var app_id: int: set(v): app_id = v app_id_changed.emit() +## The type of app +var app_type: APP_TYPE = APP_TYPE.UNKNOWN ## Whether or not the app is currently focused var focused: bool = false: set(v): @@ -111,10 +121,105 @@ func _init(item: LibraryLaunchItem, process_id: int, dsp: String) -> void: display = dsp +## Run the given command and return it as a [RunningApp] +static func spawn(app: LibraryLaunchItem, env: Dictionary, cmd: String, args: PackedStringArray) -> RunningApp: + # Generate an app id for the running application based on its name + var app_id := app.get_app_id() + + # Launch the application process + var pid := Reaper.create_process(cmd, args, app_id) + var display := env["DISPLAY"] as String + var command := PackedStringArray([cmd]) + command.append_array(args) + + # Create a running app instance + var running_app := RunningApp.new(app, pid, display) + running_app.command = command + running_app.environment = env + running_app.app_id = app_id + + return running_app + + +# TODO: Only call this on window creation/deletion ## Updates the running app and fires signals func update() -> void: + match self.app_type: + APP_TYPE.UNKNOWN: + self.app_type = discover_app_type() + if self.app_type != APP_TYPE.UNKNOWN: + app_type_detected.emit() + self.update() + APP_TYPE.X11: + update_xwayland_app() + APP_TYPE.WAYLAND: + update_wayland_app() + + +## Tries to discover if the launched app is an X11 or Wayland application +func discover_app_type() -> APP_TYPE: + # Update all the windows + self.window_ids = get_all_window_ids() + + # Check to see if running app's app_id exists in focusable_apps + var xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) + var focused_app := xwayland_primary.focused_app + var focusable_apps := xwayland_primary.focusable_apps + if (self.app_id == focused_app or self.app_id in focusable_apps) and self.window_ids.is_empty(): + logger.debug("Discovered app type: Wayland") + return APP_TYPE.WAYLAND + + if self.app_id in focusable_apps and not self.window_ids.is_empty(): + logger.debug("Discovered app type: X11") + return APP_TYPE.X11 + + return APP_TYPE.UNKNOWN + + +func update_wayland_app() -> void: + # Check if the app, or any of its children, are still running + var running := is_running() + if not running: + self.not_running_count += 1 + + # Update the running app's state + if not_running_count > 3: + state = STATE.STOPPED + app_killed.emit() + elif state == STATE.STARTED: + state = STATE.RUNNING + #grab_focus() # How can we grab wayland window focus? + + # Update the focus state of the app + focused = is_focused() + + var state_str := { + STATE.STARTED: "started", + STATE.RUNNING: "running", + STATE.MISSING_WINDOW: "no window", + STATE.STOPPING: "stopping", + STATE.STOPPED: "stopped" + } + logger.trace(launch_item.name + " current state: " + state_str[state]) + + # If this was launched by Steam, try and detect if the game closed + # so we can kill Steam gracefully + if is_steam_app() and state == STATE.STOPPED and is_ogui_managed: + logger.trace(launch_item.name + " is a Steam game and has no valid window ID. It may have closed.") + # Don't try closing Steam immediately. Wait a few more ticks before attempting + # to close Steam. + if steam_close_tries < 4: + steam_close_tries += 1 + return + var steam_pid := find_steam() + if steam_pid > 0: + logger.info("Trying to stop steam with pid: " + str(steam_pid)) + OS.execute("kill", ["-15", str(steam_pid)]) + + +func update_xwayland_app() -> void: # Update all windows related to the app's PID - window_ids = get_all_window_ids() + self.window_ids = get_all_window_ids() # Ensure that all windows related to the app have an app ID set _ensure_app_id() @@ -127,6 +232,7 @@ func update() -> void: if id > 0 and window_id != id: logger.trace("Setting window ID " + str(id) + " for " + launch_item.name) window_id = id + has_valid_window = true else: has_valid_window = true @@ -187,33 +293,47 @@ func suspend(enable: bool) -> void: func get_window_title(win_id: int) -> String: if not win_id in window_ids: return "" - return Gamescope.get_window_name(win_id) + var xwayland := gamescope.get_xwayland_by_name(display) + if not xwayland: + return "" + var title := xwayland.get_window_name(win_id) + return title ## Attempt to discover the window ID from the PID of the given application func get_window_id_from_pid() -> int: - var display_type := Gamescope.get_display_type(display) - return Gamescope.get_window_id(pid, display_type) + var xwayland := gamescope.get_xwayland_by_name(display) + if not xwayland: + return -1 + var windows := xwayland.get_windows_for_pid(pid) + if windows.is_empty(): + return -1 + return windows[0] ## Attempt to discover all window IDs from the PID of the given application and ## the PIDs of all processes in the same process group. func get_all_window_ids() -> PackedInt32Array: var app_name := launch_item.name - var display_type := Gamescope.get_display_type(display) var window_ids := PackedInt32Array() var pids := get_child_pids() + var xwayland := gamescope.get_xwayland_by_name(display) pids.append(pid) logger.trace(app_name + " found related PIDs: " + str(pids)) - for process_id in pids: - var windows := Gamescope.get_window_ids(process_id, display_type) - for window in windows: - if window < 0: - continue - if window in window_ids: - continue - window_ids.append(window) + # Loop through all windows and check if the window belongs to one of our + # processes + var all_windows := xwayland.get_all_windows(xwayland.root_window_id) + for window_id in all_windows: + if xwayland.has_app_id(window_id) and xwayland.get_app_id(window_id) == self.app_id: + window_ids.append(window_id) + continue + var window_pids := xwayland.get_pids_for_window(window_id) + for window_pid in window_pids: + if window_pid in pids: + #logger.trace("Found window for pid", window_pid, ":", window_id) + window_ids.append(window_id) + logger.trace(app_name + " found related window IDs: " + str(window_ids)) return window_ids @@ -222,25 +342,7 @@ func get_all_window_ids() -> PackedInt32Array: ## Returns true if the app's PID is running or any decendents with the same ## process group. func is_running() -> bool: - # If the app is still running, great! - if OS.is_process_running(pid): - return true - - # If that failed, check if reaper can get the status. - logger.trace("Reaper pid State: " + Reaper.get_pid_state(pid)) - if Reaper.get_pid_state(pid) in ["R (running)", "S (sleeping)"]: - return true - - logger.trace("Original process not running. Checking child PID's...") - # If it's not running, let's check to make sure it's REALLY not running - # and hasn't re-parented itself - var children := get_child_pids() - if children.size() > 0: - var pids := Array(children) - logger.trace("{0} is not running, but lives on in {1}".format([pid, ",".join(pids)])) - return true - logger.trace("Process " + str(pid) + " has died and no child PID's could be found.") - return false + return OS.is_process_running(pid) ## Return a list of child PIDs. When launching apps with [Reaper], PR_SET_CHILD_SUBREAPER @@ -280,22 +382,28 @@ func get_child_pids() -> PackedInt32Array: ## Returns whether or not the app can be switched to/focused func can_focus() -> bool: - return window_id > 0 + return self.app_id > 0 ## Return true if the currently running app is focused func is_focused() -> bool: if not can_focus(): return false - var focused_window := Gamescope.get_focused_window() - return window_id == focused_window or focused_window in window_ids + var xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) + if not xwayland_primary: + return false + var focused_app := xwayland_primary.focused_app + return self.app_id == focused_app ## Focuses to the app's window func grab_focus() -> void: if not can_focus(): return - Gamescope.set_baselayer_window(window_id) + var xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) + if not xwayland_primary: + return + xwayland_primary.baselayer_app = self.app_id focused = true @@ -303,17 +411,24 @@ func grab_focus() -> void: ## to switch to the window func switch_window(win_id: int, focus: bool = true) -> int: # Error if the window does not belong to the running app + # TODO: Look into how window switching can work with Wayland windows if not win_id in window_ids: return ERR_DOES_NOT_EXIST - + + # Get the primary XWayland instance + var xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) + if not xwayland_primary: + return ERR_UNAVAILABLE + # Check if this app is a focusable window. - if not win_id in Gamescope.get_focusable_windows(): + if not win_id in xwayland_primary.focusable_windows: return ERR_UNAVAILABLE # Update the window ID and optionally grab focus window_id = win_id if focus: grab_focus() + xwayland_primary.baselayer_window = win_id return OK @@ -332,32 +447,48 @@ func _ensure_app_id() -> void: if is_steam_app() or not is_ogui_managed: return + # Get the xwayland instance this app is running on + var xwayland := gamescope.get_xwayland_by_name(display) + if not xwayland: + return + # Get all windows associated with the running app var possible_windows := window_ids.duplicate() # Try setting the app ID on each possible Window. If they are valid windows, # gamescope will make these windows available as focusable windows. - var app_name := launch_item.name for window in possible_windows: - var display_type := Gamescope.get_display_type(display) - if Gamescope.has_app_id(window, display_type): + if xwayland.has_app_id(window): continue - Gamescope.set_app_id(window, window, display_type) + xwayland.set_app_id(window, self.app_id) ## Returns whether or not the window id of the running app needs to be discovered func needs_window_id() -> bool: + var xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) + if not xwayland_primary: + return false + if window_id <= 0: logger.trace(launch_item.name + " has a bad window ID: " + str(window_id)) return true - var focusable_windows := Gamescope.get_focusable_windows() + # If this is a Steam app and the current window id is a Steam window, don't + # consider this a valid window id. TODO: We might not need this since we also + # check this in _discover_window_id() + if is_steam_app() and is_steam_window(window_id): + return true + var focusable_windows := xwayland_primary.focusable_windows if not window_id in focusable_windows: logger.trace(str(window_id) + " is not in the list of focusable windows") return true + var xwayland := gamescope.get_xwayland_by_name(display) + if not xwayland: + return false + # Check if the current window ID exists in the list of open windows - var root_window := Gamescope.get_root_window_id(Gamescope.XWAYLAND.GAME) - var all_windows := Gamescope.get_all_windows(root_window, Gamescope.XWAYLAND.GAME) + var root_window := xwayland.root_window_id + var all_windows := xwayland.get_all_windows(root_window) if not window_id in all_windows: logger.trace(str(window_id) + " is not in the list of all windows") return true @@ -365,12 +496,11 @@ func needs_window_id() -> bool: # If this is a Steam app, the only acceptable window will have its STEAM_GAME # property set. if is_steam_app(): - var display_type := Gamescope.get_display_type(display) var steam_app_id := get_meta("steam_app_id") as int - if not Gamescope.has_app_id(window_id, display_type): + if not xwayland.has_app_id(window_id): logger.trace(str(window_id) + " does not have an app ID already set by Steam") return true - if Gamescope.get_app_id(window_id) != steam_app_id: + if xwayland.get_app_id(window_id) != steam_app_id: logger.trace(str(window_id) + " has an app ID but it does not match " + str(steam_app_id)) return true @@ -392,11 +522,21 @@ func _discover_window_id() -> int: # Get all windows associated with the running app var possible_windows := window_ids.duplicate() - + + # Get the primary XWayland instance + var xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) + if not xwayland_primary: + return -1 + # Look for the app window in the list of focusable windows - var focusable := Gamescope.get_focusable_windows() + var focusable := xwayland_primary.focusable_windows for window in possible_windows: if window in focusable: + # If this is a steam app, don't consider any Steam windows as valid + # discovered window ids for this app. + if is_steam_app() and is_steam_window(window): + logger.debug("Window", window, "is a Steam window") + continue return window return -1 @@ -418,6 +558,25 @@ func is_steam_app() -> bool: return false +## Returns true if the given window id is detected as a Steam window +func is_steam_window(window_id: int) -> bool: + var xwayland := gamescope.get_xwayland_by_name(display) + if not xwayland: + return false + + var window_pids := xwayland.get_pids_for_window(window_id) + for window_pid in window_pids: + var pid_info := Reaper.get_pid_status(window_pid) + if not "Name" in pid_info: + continue + var process_name := pid_info["Name"] as String + if process_name in ["steam", "steamwebhelper"]: + return true + print(process_name) + + return false + + ## Finds the steam process so it can be killed when a game closes func find_steam() -> int: var child_pids := get_child_pids() @@ -431,3 +590,9 @@ func find_steam() -> int: return child_pid return -1 + + +func _to_string() -> String: + if not self.launch_item: + return "" + return "".format([self.launch_item.name, self.app_id]) diff --git a/core/systems/library/library.gd b/core/systems/library/library.gd index 718bc7088..2e8c9c02d 100644 --- a/core/systems/library/library.gd +++ b/core/systems/library/library.gd @@ -94,5 +94,13 @@ func has_update(item: LibraryLaunchItem) -> bool: return false +## This method should be overridden if the library requires executing callbacks +## at certain points in an app's lifecycle, such as when an app is starting or +## stopping. +func get_app_lifecycle_hooks() -> Array[AppLifecycleHook]: + var hooks: Array[AppLifecycleHook] + return hooks + + func _exit_tree() -> void: LibraryManager.unregister_library(self) diff --git a/core/systems/library/library_desktop.gd b/core/systems/library/library_desktop.gd index d98235c88..a9b551c69 100644 --- a/core/systems/library/library_desktop.gd +++ b/core/systems/library/library_desktop.gd @@ -80,7 +80,8 @@ func _apply_quirks(launch_item: LibraryLaunchItem) -> void: # If the desktop shortcut is for Steam, add the '-silent' argument so it # doesn't launch into the Steam interface - var args := PackedStringArray(["-gamepadui", "-steamos3", "-steampal", "-steamdeck", "-silent"]) + #var args := PackedStringArray(["-gamepadui", "-steamos3", "-steampal", "-steamdeck", "-silent"]) + var args := PackedStringArray(["-silent"]) args.append_array(launch_item.args) launch_item.args = args diff --git a/core/systems/library/library_launch_item.gd b/core/systems/library/library_launch_item.gd index d3cbadcc6..fc4949c2d 100644 --- a/core/systems/library/library_launch_item.gd +++ b/core/systems/library/library_launch_item.gd @@ -65,3 +65,24 @@ static func from_dict(d: Dictionary) -> LibraryLaunchItem: if "metadata" in d: item.metadata = d["metadata"] return item + + +## Returns a numerical app ID associated with the launch item +func get_app_id() -> int: + # If this launch item launches a Steam game, use the Steam app id instead + for arg in self.args: + if not arg.contains("steam://rungameid/"): + continue + var parts := arg.split("/", false) + if parts.is_empty(): + continue + var id := parts[-1] as String + if not id.is_valid_int(): + continue + return int(id) + + # In all other cases, use the hash of the app name for its app id + var app_id := hash(self.name) + const MAX_APP_ID := 800483647 + + return app_id % MAX_APP_ID diff --git a/core/systems/library/library_refresher.gd b/core/systems/library/library_refresher.gd new file mode 100644 index 000000000..711dc2eaf --- /dev/null +++ b/core/systems/library/library_refresher.gd @@ -0,0 +1,55 @@ +@icon("res://assets/editor-icons/library.svg") +@tool +extends Node +class_name LibraryRefresher + +## Refreshes the library when the given parent signal is fired + +var library_manager := load("res://core/global/library_manager.tres") as LibraryManager + +## Signal on our parent node to connect to +var on_signal: String + + +func _init() -> void: + ready.connect(_on_ready) + + +func _on_ready() -> void: + notify_property_list_changed() + # Do nothing if running in the editor + if Engine.is_editor_hint(): + return + if on_signal != "": + get_parent().connect(on_signal, _on_signal) + + +## Fires when the given signal is emitted. +func _on_signal(): + library_manager.reload_library() + + +# Customize editor properties that we expose. Here we dynamically look up +# the parent node's signals so we can display them in a list. +func _get_property_list(): + # By default, `on_signal` is not visible in the editor. + var property_usage := PROPERTY_USAGE_NO_EDITOR + + var parent_signals := [] + if get_parent() != null: + property_usage = PROPERTY_USAGE_DEFAULT + for sig in get_parent().get_signal_list(): + parent_signals.push_back(sig["name"]) + + var properties := [] + properties.append( + { + "name": "on_signal", + "type": TYPE_STRING, + "usage": property_usage, # See above assignment. + "hint": PROPERTY_HINT_ENUM, + "hint_string": ",".join(parent_signals) + } + ) + + return properties diff --git a/core/systems/library/library_refresher.tscn b/core/systems/library/library_refresher.tscn new file mode 100644 index 000000000..c3514f640 --- /dev/null +++ b/core/systems/library/library_refresher.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://buwdabfwboyn0"] + +[ext_resource type="Script" path="res://core/systems/library/library_refresher.gd" id="1_ttjxh"] + +[node name="LibraryRefresher" type="Node"] +script = ExtResource("1_ttjxh") diff --git a/core/systems/network/network_manager.gd b/core/systems/network/network_manager.gd index 3b7dc0f64..6dab85bce 100644 --- a/core/systems/network/network_manager.gd +++ b/core/systems/network/network_manager.gd @@ -1,106 +1,20 @@ -extends RefCounted +@icon("res://assets/ui/icons/iconoir--network-solid.svg") +extends Node class_name NetworkManager -## Manage and interact with the system network settings +## Manages NetworkManager. ## -## Allows network management through nmcli -## Reference: https://developer-old.gnome.org/NetworkManager/stable/nmcli.html +## The [NetworkManager] class is responsible for loading a [NetworkManagerInstance] and +## calling its 'process()' method each frame. const bar_0 := preload("res://assets/ui/icons/wifi-none.svg") const bar_1 := preload("res://assets/ui/icons/wifi-low.svg") const bar_2 := preload("res://assets/ui/icons/wifi-medium.svg") const bar_3 := preload("res://assets/ui/icons/wifi-high.svg") +const no_network := preload("res://assets/ui/icons/tabler--network-off.svg") +const ethernet := preload("res://assets/ui/icons/mdi--ethernet.svg") -const common_args := ["--terse", "--color", "no"] - - -## Wireless Access Point -class WifiAP: - var in_use: bool - var bssid: String - var ssid: String - var mode: String - var channel: int - var rate: String - var strength: int - var security: String - - -## Network device -class NetworkDevice: - var device: String - var type: String - var state: String - var connection: String - - -## Returns true if the system has network controls we support -static func supports_network() -> bool: - var code := OS.execute("which", ["nmcli"]) - return code == 0 - - -## Returns a list of network devices -#$nmcli --terse --color no device -#enp5s0:ethernet:connected:Wired connection 1 -#wlp4s0:wifi:connected:Chonenberg -#lo:loopback:connected (externally):lo -#p2p-dev-wlp4s0:wifi-p2p:disconnected: -static func get_devices() -> Array[NetworkDevice]: - var result: Array[NetworkDevice] = [] - var output := _run_nmcli(["device"]) - for line in output: - var device := NetworkDevice.new() - device.device = line[0] - device.type = line[1] - device.state = line[2] - device.connection = line[3] - result.append(device) - - return result - - -## Returns a list of available wifi access points -#$ nmcli --terse --color no dev wifi -# :AA\:BB\:CC\:83\:82\:FF:Chronenberg 5GHz:Infra:120:405 Mbit/s:94:▂▄▆█:WPA2 -#*:AA\:BB\:CC\:83\:82\:FB:Chonenberg:Infra:11:195 Mbit/s:83:▂▄▆█:WPA2 -static func get_access_points() -> Array[WifiAP]: - var result: Array[WifiAP] = [] - var output := _run_nmcli(["dev", "wifi"]) - for line in output: - var ap := WifiAP.new() - ap.in_use = line[0] == "*" - ap.bssid = line[1] - ap.ssid = line[2] - ap.mode = line[3] - ap.channel = line[4].to_int() - ap.rate = line[5] - ap.strength = line[6].to_int() - ap.security = line[8] - result.append(ap) - - return result - - -## Returns the currently connected access point -static func get_current_access_point() -> WifiAP: - var access_points := get_access_points() - for ap in access_points: - if ap.in_use: - return ap - return null - - -## Connect to the given wifi access point -static func connect_access_point(ssid: String, password: String = "") -> int: - var args := ["dev", "wifi", "connect", ssid] - if password != "": - args.append_array(["password", password]) - var output := [] - var code := OS.execute("nmcli", args, output) - if code != OK: - push_warning("Unable to connect to ", ssid, ": ", output[0]) - return code +@export var instance: NetworkManagerInstance = load("res://core/systems/network/network_manager.tres") as NetworkManagerInstance ## Returns the texture reflecting the given wifi strength @@ -114,34 +28,7 @@ static func get_strength_texture(strength: int) -> Texture2D: return bar_0 -# Run nmcli with the given arguments. Returns the parsed output. -static func _run_nmcli(args: PackedStringArray) -> Array[PackedStringArray]: - var output := [] - var cmd_args := common_args.duplicate() - cmd_args.append_array(args) - var code := OS.execute("nmcli", cmd_args, output) - if code != OK: - return [] - - return _parse_nmcli(output[0]) - - -# Parses the terse output of nmcli, which is separated by ':' -#$ nmcli --terse --color no dev wifi -# :AA\:BB\:CC\:83\:82\:FF:Chronenberg 5GHz:Infra:120:405 Mbit/s:94:▂▄▆█:WPA2 -#*:AA\:BB\:CC\:83\:82\:FB:Chonenberg:Infra:11:195 Mbit/s:83:▂▄▆█:WPA2 -static func _parse_nmcli(output: String) -> Array[PackedStringArray]: - var parsed: Array[PackedStringArray] = [] - var lines := output.split("\n") - for line in lines: - var parsed_line := PackedStringArray() - if line == "": - continue - line = line.replace("\\:", "%COLON%") - var columns := line.split(":") - for column in columns: - column = column.replace("%COLON%", ":") - parsed_line.append(column) - parsed.append(parsed_line) - - return parsed +func _process(_delta: float) -> void: + if not instance: + return + instance.process() diff --git a/core/systems/network/network_manager.tres b/core/systems/network/network_manager.tres new file mode 100644 index 000000000..319aa7c1f --- /dev/null +++ b/core/systems/network/network_manager.tres @@ -0,0 +1,3 @@ +[gd_resource type="NetworkManagerInstance" format=3 uid="uid://cmwavccnjwpr0"] + +[resource] diff --git a/core/systems/network/network_manager_test.gd b/core/systems/network/network_manager_test.gd index ccb0ab893..14c0d44ac 100644 --- a/core/systems/network/network_manager_test.gd +++ b/core/systems/network/network_manager_test.gd @@ -1,14 +1,95 @@ extends GutTest +var network_manager := load("res://core/systems/network/network_manager.tres") as NetworkManagerInstance -func test_get_access_points() -> void: - if not NetworkManager.supports_network(): +const SSID := "SomeSSID" +const PSK := "SomePassword" + + +func before_all() -> void: + if not network_manager.is_running(): + @warning_ignore("unsafe_method_access") + gut.p("Networking is not supported") + return + + +func test_connect() -> void: + if not network_manager.is_running(): pass_test("Networking not supported, skipping") return + var devices := network_manager.get_devices() + if devices.is_empty(): + pass_test("No network adapters found, skipping") + return + var wireless_devices := devices.filter(func(device: NetworkDevice): return device.wireless != null) + if wireless_devices.is_empty(): + pass_test("No wireless adapters found, skipping") + return + gut.p("Found wireless devices: " + str(wireless_devices)) + var device := wireless_devices[0] as NetworkDevice + var access_point: NetworkAccessPoint + for ap in device.wireless.access_points: + if ap.ssid != SSID: + continue + access_point = ap + break + + if not access_point: + pass_test("No valid wireless networks found, skipping") + return + + var connection := access_point.connect(device, PSK) + + for i in range(50): + var state := connection.state + gut.p("Device state: " + str(device.state)) + gut.p("Connection state: " + str(state)) + match device.state: + device.NM_DEVICE_STATE_CONFIG: + gut.p("Connecting...") + device.NM_DEVICE_STATE_NEED_AUTH: + gut.p("Authentication required") + break + device.NM_DEVICE_STATE_ACTIVATED: + gut.p("Successfully connected!") + break + device.NM_DEVICE_STATE_IP_CONFIG: + gut.p("Aquiring IP address...") + device.NM_DEVICE_STATE_DEACTIVATING: + gut.p("Deactivation connection...") + + await wait_seconds(1, "waiting for connection") + + pass_test("Skipping") - for ap in NetworkManager.get_access_points(): - gut.p("Found AP: " + str(ap.ssid)) - for device in NetworkManager.get_devices(): - gut.p("Found device: " + device.device + " " + device.type + " " + device.state) +func test_get_devices() -> void: + if not network_manager.is_running(): + pass_test("Networking not supported, skipping") + return + var devices := network_manager.get_devices() + if devices.is_empty(): + pass_test("No network adapters found, skipping") + for device in devices: + @warning_ignore("unsafe_method_access") + gut.p("discovered device: " + device.dbus_path) + + if device.ip4_config: + gut.p(" found address: " + str(device.ip4_config.addresses)) + + if device.device_type == device.NM_DEVICE_TYPE_ETHERNET: + assert_null(device.wireless) + @warning_ignore("unsafe_method_access") + gut.p(" discovered ethernet device") + if device.device_type == device.NM_DEVICE_TYPE_WIFI: + assert_not_null(device.wireless) + @warning_ignore("unsafe_method_access") + gut.p(" discovered wireless device: " + device.wireless.dbus_path) + if device.wireless.active_access_point: + gut.p(" active access point: " + device.wireless.active_access_point.ssid) + for ap in device.wireless.access_points: + gut.p(" discovered access point: " + ap.ssid) + gut.p(" strength: " + str(ap.strength)) + gut.p(" freq: " + str(ap.frequency)) + gut.p(" max bitrate: " + str(ap.max_bitrate)) pass_test("Skipping") diff --git a/core/systems/network/unix_socket_client.gd b/core/systems/network/unix_socket_client.gd deleted file mode 100644 index 19ac3e480..000000000 --- a/core/systems/network/unix_socket_client.gd +++ /dev/null @@ -1,59 +0,0 @@ -@icon("res://assets/editor-icons/socket-bold.svg") -extends Node -class_name UnixSocketClient - -signal connected -signal data -signal disconnected -signal error - -var _is_open: bool = false -var _stream := StreamPeerUnix.new() -var _logger := Log.get_logger("UnixSocketClient") - - -func _ready() -> void: - _is_open = _stream.is_open() - - -func _process(_delta: float) -> void: - var new_status := _stream.is_open() - if new_status != _is_open: - var prev_status := _is_open - _is_open = new_status - if prev_status and not new_status: - _logger.info("Disconnected from socket.") - disconnected.emit() - if new_status: - _logger.info("Connected to socket.") - connected.emit() - - if _is_open: - var available_bytes: int = _stream.get_available_bytes() - if available_bytes > 0: - _logger.debug("available bytes: " + str(available_bytes)) - var parts := _stream.get_partial_data(available_bytes) - # Check for read error. - if parts[0] != OK: - _logger.error("Error getting data from stream: " + str(parts[0])) - error.emit() - else: - data.emit(parts[1]) - - -func open(path: String) -> void: - # Reset status so we can tell if it changes to error again. - _is_open = false - if _stream.open(path) != OK: - error.emit() - - -func send(data: PackedByteArray) -> bool: - if not _is_open: - _logger.error("Error: Stream is not currently connected.") - return false - var err: int = _stream.put_data(data) - if err != OK: - _logger.error("Error writing to stream: " + str(err)) - return false - return true diff --git a/core/systems/performance/performance_manager.gd b/core/systems/performance/performance_manager.gd index b7166f76b..590e4af72 100644 --- a/core/systems/performance/performance_manager.gd +++ b/core/systems/performance/performance_manager.gd @@ -11,7 +11,7 @@ signal profile_loaded(profile: PerformanceProfile) signal profile_saved(profile: PerformanceProfile, path: String) const USER_PROFILES := "user://data/performance/profiles" -const DOCKED_STATES := [PowerManager.DEVICE_STATE.CHARGING, PowerManager.DEVICE_STATE.FULLY_CHARGED, PowerManager.DEVICE_STATE.PENDING_CHARGE] +const DOCKED_STATES := [UPowerDevice.STATE_CHARGING, UPowerDevice.STATE_FULLY_CHARGED, UPowerDevice.STATE_PENDING_CHARGE] ## Performance profiles are separated into these states, so users can have different ## performance depending on whether or not they are plugged in to an external power @@ -22,11 +22,12 @@ enum PROFILE_STATE { } var _hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager -var _power_manager := load("res://core/systems/power/power_manager.tres") as PowerManager +var _power_manager := load("res://core/systems/power/power_manager.tres") as UPowerInstance var _settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager -var _power_station := load("res://core/systems/performance/power_station.tres") as PowerStation +var _power_station := load("res://core/systems/performance/power_station.tres") as PowerStationInstance var _launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager +var display_device := _power_manager.get_display_device() var current_profile: PerformanceProfile var current_profile_state: PROFILE_STATE # docked or undocked var logger := Log.get_logger("PerformanceManager", Log.LEVEL.INFO) @@ -36,18 +37,17 @@ func _init() -> void: # Listen for signals when the current app switches so we can update the profile # accordingly. _launch_manager.app_switched.connect(_on_app_switched) + _launch_manager.all_apps_stopped.connect(_on_all_apps_stopped) # Connect to battery state changes to switch between "docked" and "undocked" # performance profiles. - var batteries := _power_manager.get_devices_by_type(PowerManager.DEVICE_TYPE.BATTERY) - if batteries.size() > 1: - logger.warn("You somehow have more than one battery. We don't know what to do with that.") - if batteries.size() > 0: - var battery := batteries[0] - battery.updated.connect(_on_battery_updated.bind(battery)) - + if display_device: + display_device.updated.connect(_on_battery_updated.bind(display_device)) + + # TODO: Listen for signals if PowerStation starts/stops + # Do nothing if PowerStation is not detected - if not _power_station.supports_power_station(): + if not _power_station.is_running(): return # Load and apply the default profile @@ -109,22 +109,25 @@ func create_profile(library_item: LibraryLaunchItem = null) -> PerformanceProfil profile.cpu_smt_enabled = _power_station.cpu.smt_enabled # Detect all GPU cards - var cards: Array[PowerStation.GPUCard] = [] + var cards: Array[GpuCard] = [] if _power_station.gpu: cards = _power_station.gpu.get_cards() # Configure GPU settings # TODO: Support multiple GPUs? for card in cards: - if card.class_type != "integrated": + if card.class != "integrated": continue - - profile.tdp_current = card.tdp - profile.tdp_boost_current = card.boost + if _hardware_manager.gpu: + profile.tdp_current = round(_hardware_manager.gpu.tdp_max) + profile.tdp_boost_current = round(_hardware_manager.gpu.max_boost) + else: + profile.tdp_current = card.tdp + profile.tdp_boost_current = card.boost profile.gpu_freq_min_current = card.clock_value_mhz_min profile.gpu_freq_max_current = card.clock_value_mhz_max profile.gpu_manual_enabled = card.manual_clock - #profile.gpu_power_profile = card.power_profile # TODO: Fix this + profile.gpu_power_profile = card.power_profile profile.gpu_temp_current = card.thermal_throttle_limit_c logger.debug("Created performance profile: " + profile.name) @@ -152,7 +155,7 @@ func load_or_create_profile(profile_path: String, library_item: LibraryLaunchIte if profile: logger.debug("Found profile at: " + profile_path) return profile - + # If the profile does not exist, create one with the currently applied # performance settings. logger.debug("No profile found. Creating one.") @@ -161,60 +164,58 @@ func load_or_create_profile(profile_path: String, library_item: LibraryLaunchIte ## Applies the given performance profile to the system func apply_profile(profile: PerformanceProfile) -> void: - if not _power_station.supports_power_station(): + if not _power_station.is_running(): logger.info("Unable to apply performance profile. PowerStation not detected.") return - - logger.info("Applying performance profile: " + profile.name) - # Apply CPU settings from the given profile - if _power_station.cpu: - logger.debug("Applying CPU performance settings from profile") - if _power_station.cpu.boost_enabled != profile.cpu_boost_enabled: - _power_station.cpu.boost_enabled = profile.cpu_boost_enabled - if _power_station.cpu.smt_enabled != profile.cpu_smt_enabled: - _power_station.cpu.smt_enabled = profile.cpu_smt_enabled - if profile.cpu_core_count_current > 0 and _power_station.cpu.cores_enabled != profile.cpu_core_count_current: - _power_station.cpu.cores_enabled = profile.cpu_core_count_current + logger.info("Applying performance profile: " + profile.name) # Detect all GPU cards - var cards: Array[PowerStation.GPUCard] = [] + var cards: Array[GpuCard] = [] if _power_station.gpu: cards = _power_station.gpu.get_cards() # Configure GPU settings # TODO: Support mutliple GPUs? for card in cards: - if card.class_type != "integrated": + if card.class != "integrated": continue logger.debug("Applying GPU performance settings from profile") + if card.power_profile != profile.gpu_power_profile: + logger.debug("Applying Power Profile: " + profile.gpu_power_profile) + card.power_profile = profile.gpu_power_profile if card.manual_clock != profile.gpu_manual_enabled: + logger.debug("Applying Manual Clock Enabled: " + str(profile.gpu_manual_enabled)) card.manual_clock = profile.gpu_manual_enabled - if profile.tdp_current > 0 and card.tdp != profile.tdp_current: - logger.debug("Applying TDP: " + str(profile.tdp_current)) - card.tdp = profile.tdp_current - if profile.tdp_boost_current > 0 and card.boost != profile.tdp_boost_current: - logger.debug("Applying TDP Boost: " + str(profile.tdp_boost_current)) - card.boost = profile.tdp_boost_current if profile.gpu_freq_min_current > 0 and card.clock_value_mhz_min != profile.gpu_freq_min_current: logger.debug("Applying Clock Freq Min: " + str(profile.gpu_freq_min_current)) card.clock_value_mhz_min = profile.gpu_freq_min_current if profile.gpu_freq_max_current > 0 and card.clock_value_mhz_max != profile.gpu_freq_max_current: logger.debug("Applying Clock Freq Max: " + str(profile.gpu_freq_max_current)) card.clock_value_mhz_max = profile.gpu_freq_max_current - if profile.gpu_power_profile >= 0: - var power_profile := "max-performance" - if profile.gpu_power_profile == 0: - power_profile = "max-performance" - if profile.gpu_power_profile == 1: - power_profile = "power-saving" - if card.power_profile != power_profile: - logger.debug("Applying Power Profile: " + power_profile) - card.power_profile = power_profile if profile.gpu_temp_current > 0 and card.thermal_throttle_limit_c != profile.gpu_temp_current: logger.debug("Applying Thermal Throttle Limit: " + str(profile.gpu_temp_current)) card.thermal_throttle_limit_c = profile.gpu_temp_current + # Only apply GPU TDP settings from the given profile if we're in a mode that supports it + if profile.advanced_mode or "max-performance" in get_power_profiles_available(): + if profile.tdp_current > 0 and card.tdp != profile.tdp_current: + logger.debug("Applying TDP: " + str(profile.tdp_current)) + card.tdp = profile.tdp_current + if profile.tdp_boost_current > 0 and card.boost != profile.tdp_boost_current: + logger.debug("Applying TDP Boost: " + str(profile.tdp_boost_current)) + card.boost = profile.tdp_boost_current + + # Apply CPU settings from the given profile + if _power_station.cpu: + logger.debug("Applying CPU performance settings from profile") + if _power_station.cpu.boost_enabled != profile.cpu_boost_enabled: + _power_station.cpu.boost_enabled = profile.cpu_boost_enabled + if _power_station.cpu.smt_enabled != profile.cpu_smt_enabled: + _power_station.cpu.smt_enabled = profile.cpu_smt_enabled + if profile.cpu_core_count_current > 0 and _power_station.cpu.cores_enabled != profile.cpu_core_count_current: + _power_station.cpu.cores_enabled = profile.cpu_core_count_current + logger.info("Applied Performance Profile: " + profile.name) profile_applied.emit(profile) @@ -242,26 +243,22 @@ func apply_and_save_profile(profile: PerformanceProfile) -> void: ## Returns the current profile state. I.e. whether or not the "docked" or "undocked" ## performance profiles should be used. func get_profile_state() -> PROFILE_STATE: - var batteries := _power_manager.get_devices_by_type(PowerManager.DEVICE_TYPE.BATTERY) - if batteries.size() > 1: - logger.warn("You somehow have more than one battery. We don't know what to do with that.") - if batteries.size() > 0: - var battery := batteries[0] - return get_profile_state_from_battery(battery) + if display_device: + return get_profile_state_from_battery(display_device) return PROFILE_STATE.DOCKED ## Returns the current profile state. I.e. whether or not the "docked" or "undocked" ## performance profiles should be used. -func get_profile_state_from_battery(battery: PowerManager.Device) -> PROFILE_STATE: +func get_profile_state_from_battery(battery: UPowerDevice) -> PROFILE_STATE: if battery.state not in DOCKED_STATES: return PROFILE_STATE.UNDOCKED return PROFILE_STATE.DOCKED ## Called whenever a battery is updated -func _on_battery_updated(battery: PowerManager.Device) -> void: +func _on_battery_updated(battery: UPowerDevice) -> void: # Get the current profile state to see if we need to load the docked or # undocked profile. var profile_state := get_profile_state_from_battery(battery) @@ -287,6 +284,23 @@ func _on_battery_updated(battery: PowerManager.Device) -> void: apply_profile(profile) +## Sets the default performance profile when all running apps have stopped +func _on_all_apps_stopped() -> void: + logger.debug("Detected all apps stopped") + + # Get the current profile state to see if we need to load the docked or + # undocked profile. + var profile_state := get_profile_state() + + # Load the performance profile based on the running game + var profile_path := get_profile_filename(profile_state) + profile_path = "/".join([USER_PROFILES, profile_path]) + var profile := load_or_create_profile(profile_path) + current_profile = profile + profile_loaded.emit(profile) + apply_profile(profile) + + ## Called whenever an app is switched. E.g. when a game is launched func _on_app_switched(_from: RunningApp, to: RunningApp) -> void: logger.debug("Detected app switch") @@ -304,3 +318,18 @@ func _on_app_switched(_from: RunningApp, to: RunningApp) -> void: current_profile = profile profile_loaded.emit(profile) apply_profile(profile) + + +# Get the currently available power profiles +func get_power_profiles_available() -> PackedStringArray: + # Detect all GPU cards + var cards: Array[GpuCard] = [] + if _power_station.gpu: + cards = _power_station.gpu.get_cards() + + for card in cards: + if card.class != "integrated": + continue + + return card.power_profiles_available + return [] diff --git a/core/systems/performance/performance_profile.gd b/core/systems/performance/performance_profile.gd index 9fe0d4448..76e80c796 100644 --- a/core/systems/performance/performance_profile.gd +++ b/core/systems/performance/performance_profile.gd @@ -10,11 +10,12 @@ class_name PerformanceProfile @export var gpu_freq_max_current: float @export var gpu_freq_min_current: float @export var gpu_manual_enabled: bool -@export var gpu_power_profile: int +@export var gpu_power_profile: String @export var gpu_temp_current: float @export var tdp_boost_current: float @export var tdp_current: float @export var thermal_profile: int +@export var advanced_mode: bool = false func _to_string() -> String: @@ -25,8 +26,9 @@ func _to_string() -> String: + "gpu_freq_max_current: " + str(gpu_freq_max_current) + ", " \ + "gpu_freq_min_current: " + str(gpu_freq_min_current) + ", " \ + "gpu_manual_enabled: " + str(gpu_manual_enabled) + ", " \ - + "gpu_power_profile: " + str(gpu_power_profile) + ", " \ + + "gpu_power_profile: " + gpu_power_profile + ", " \ + "gpu_temp_current: " + str(gpu_temp_current) + ", " \ + "tdp_boost_current: " + str(tdp_boost_current) + ", " \ + "tdp_current: " + str(tdp_current) + ", " \ - + "thermal_profile: " + str(thermal_profile) + ">" + + "thermal_profile: " + str(thermal_profile) + ", " \ + + "advanced_mode:" + str(advanced_mode) + ">" diff --git a/core/systems/performance/power_station.gd b/core/systems/performance/power_station.gd index 2c9127791..e714a78b9 100644 --- a/core/systems/performance/power_station.gd +++ b/core/systems/performance/power_station.gd @@ -1,4 +1,5 @@ -extends Resource +@icon("res://assets/editor-icons/game-icons--power-generator.svg") +extends Node class_name PowerStation ## Proxy interface to PowerStation over DBus @@ -6,404 +7,20 @@ class_name PowerStation ## Provides wrapper classes and methods for interacting with PowerStation over ## DBus to control CPU and GPU performance. -const POWERSTATION_BUS := "org.shadowblip.PowerStation" -const PERFORMANCE_PATH := "/org/shadowblip/Performance" -const CPU_PATH := "/org/shadowblip/Performance/CPU" -const GPU_PATH := "/org/shadowblip/Performance/GPU" -const IFACE_CPU := "org.shadowblip.CPU" -const IFACE_CPU_CORE := "org.shadowblip.CPU.Core" -const IFACE_GPU := "org.shadowblip.GPU" -const IFACE_GPU_CARD := "org.shadowblip.GPU.Card" -const IFACE_GPU_TDP := "org.shadowblip.GPU.Card.TDP" -const IFACE_GPU_CONNECTOR := "org.shadowblip.GPU.Card.Connector" +@export var instance: PowerStationInstance = load("res://core/systems/performance/power_station.tres") as PowerStationInstance +# Keep a reference to instances so they are not cleaned up automatically +var _cpu: Cpu +var _gpu: Gpu var logger := Log.get_logger("PowerStation") -var dbus := load("res://core/global/dbus_system.tres") as DBusManager -var cpu := CPUBus.new(dbus.create_proxy(POWERSTATION_BUS, CPU_PATH)) -var gpu := GPUBus.new(dbus.create_proxy(POWERSTATION_BUS, GPU_PATH)) -## Returns true if PowerStation can be used on this system -func supports_power_station() -> bool: - return dbus.bus_exists(POWERSTATION_BUS) +func _ready() -> void: + _cpu = instance.get_cpu() + _gpu = instance.get_gpu() -## CPUBus provides a DBus connection to the CPU bus for CPU controls -class CPUBus extends Resource: - signal properties_changed - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - ## Returns a list of DBus object paths to every detected core - func enumerate_cores() -> PackedStringArray: - var result := _proxy.call_method(IFACE_CPU, "EnumerateCores") - if not result: - return [] - var args := result.get_args() - if args.size() != 1: - return [] - if not args[0] is Array: - return [] - return args[0] - - ## Returns true if the CPU has the given feature - func has_feature(feature: String) -> bool: - var result := _proxy.call_method(IFACE_CPU, "HasFeature", [feature], "s") - if not result: - return false - var args := result.get_args() - if args.size() != 1: - return false - if not args[0] is bool: - return false - return args[0] - - var boost_enabled: bool: - set(v): - _proxy.set_property(IFACE_CPU, "BoostEnabled", v) - get: - var property = _proxy.get_property(IFACE_CPU, "BoostEnabled") - if not property is bool: - return false - return property - - var cores_count: int: - get: - var property = _proxy.get_property(IFACE_CPU, "CoresCount") - if not property is int: - return -1 - return property - - var cores_enabled: int: - set(v): - _proxy.set_property(IFACE_CPU, "CoresEnabled", DBus.uint32(v)) - get: - var property = _proxy.get_property(IFACE_CPU, "CoresEnabled") - if not property is int: - return -1 - return property - - var features: PackedStringArray: - get: - var property = _proxy.get_property(IFACE_CPU, "Features") - if not property is Array: - return [] - return property - - var smt_enabled: bool: - set(v): - _proxy.set_property(IFACE_CPU, "SmtEnabled", v) - get: - var property = _proxy.get_property(IFACE_CPU, "SmtEnabled") - if not property is bool: - return false - return property - - -## Provides an interface to enumerate all detected GPU cards -class GPUBus extends Resource: - signal properties_changed - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - ## Returns a list of DBus object paths to every detected GPU card - func enumerate_cards() -> PackedStringArray: - var result := _proxy.call_method(IFACE_GPU, "EnumerateCards") - if not result: - return [] - var args := result.get_args() - if args.size() != 1: - return [] - if not args[0] is Array: - return [] - return args[0] - - ## Returns a list of all GPUCard objects - func get_cards() -> Array[GPUCard]: - var dbus := load("res://core/global/dbus_system.tres") as DBusManager - var cards: Array[GPUCard] = [] - var paths := self.enumerate_cards() - for path in paths: - var card = GPUCard.new(dbus.create_proxy(POWERSTATION_BUS, path)) - cards.append(card) - - return cards - - -## GPUCard provides a DBus connection to the GPU for GPU control -class GPUCard extends Resource: - signal properties_changed - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - ## Returns true if the card supports TDP control - func supports_tdp() -> bool: - return self.tdp > 1 - - ## Returns a list of DBus object paths to every detected GPU connector - func enumerate_connectors() -> PackedStringArray: - var result := _proxy.call_method(IFACE_GPU_CARD, "EnumerateConnectors") - if not result: - return [] - var args := result.get_args() - if args.size() != 1: - return [] - if not args[0] is Array: - return [] - return args[0] - - ## Returns a list of all GPUConnector objects - func get_connectors() -> Array[GPUConnector]: - var dbus := load("res://core/global/dbus_system.tres") as DBusManager - var connectors: Array[GPUConnector] = [] - var paths := self.enumerate_connectors() - for path in paths: - var card = GPUConnector.new(dbus.create_proxy(POWERSTATION_BUS, path)) - connectors.append(card) - - return connectors - - var class_type: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "Class") - if not property is String: - return "" - return property - - var class_id: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "ClassId") - if not property is String: - return "" - return property - - var clock_limit_mhz_max: float: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "ClockLimitMhzMax") - if not property is float: - return -1 - return property - - var clock_limit_mhz_min: float: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "ClockLimitMhzMin") - if not property is float: - return -1 - return property - - var clock_value_mhz_max: float: - set(v): - _proxy.set_property(IFACE_GPU_CARD, "ClockValueMhzMax", float(v)) - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "ClockValueMhzMax") - if not property is float: - return -1 - return property - - var clock_value_mhz_min: float: - set(v): - _proxy.set_property(IFACE_GPU_CARD, "ClockValueMhzMin", float(v)) - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "ClockValueMhzMin") - if not property is float: - return -1 - return property - - var device: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "Device") - if not property is String: - return "" - return property - - var device_id: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "DeviceId") - if not property is String: - return "" - return property - - var manual_clock: bool: - set(v): - _proxy.set_property(IFACE_GPU_CARD, "ManualClock", v) - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "ManualClock") - if not property is bool: - return false - return property - - var name: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "Name") - if not property is String: - return "" - return property - - var path: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "Path") - if not property is String: - return "" - return property - - var revision_id: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "RevisionId") - if not property is String: - return "" - return property - - var subdevice: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "Subdevice") - if not property is String: - return "" - return property - - var subdevice_id: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "SubdeviceId") - if not property is String: - return "" - return property - - var subvendor_id: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "SubvendorId") - if not property is String: - return "" - return property - - var vendor: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "Vendor") - if not property is String: - return "" - return property - - var vendor_id: String: - get: - var property = _proxy.get_property(IFACE_GPU_CARD, "VendorId") - if not property is String: - return "" - return property - - var boost: float: - set(v): - _proxy.set_property(IFACE_GPU_TDP, "Boost", float(v)) - get: - var property = _proxy.get_property(IFACE_GPU_TDP, "Boost") - if not property is float: - return -1 - return property - - var power_profile: String: - set(v): - _proxy.set_property(IFACE_GPU_TDP, "PowerProfile", v) - get: - var property = _proxy.get_property(IFACE_GPU_TDP, "PowerProfile") - if not property is String: - return "" - return property - - var tdp: float: - set(v): - _proxy.set_property(IFACE_GPU_TDP, "TDP", float(v)) - get: - var property = _proxy.get_property(IFACE_GPU_TDP, "TDP") - if not property is float: - return -1 - return property - - var thermal_throttle_limit_c: float: - set(v): - _proxy.set_property(IFACE_GPU_TDP, "ThermalThrottleLimitC", float(v)) - get: - var property = _proxy.get_property(IFACE_GPU_TDP, "ThermalThrottleLimitC") - if not property is float: - return -1 - return property - - -## GPUConnector provides a DBus connection to a GPU connector -class GPUConnector extends Resource: - signal properties_changed - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - var dpms: bool: - get: - var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "DPMS") - if not property is bool: - return false - return property - - var enabled: bool: - get: - var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Enabled") - if not property is bool: - return false - return property - - var id: int: - get: - var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Id") - if not property is int: - return -1 - return property - - var modes: PackedStringArray: - get: - var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Modes") - if not property is Array: - return [] - return property - - var name: String: - get: - var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Name") - if not property is String: - return "" - return property - - var path: String: - get: - var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Path") - if not property is String: - return "" - return property - - var status: String: - get: - var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Status") - if not property is String: - return "" - return property +func _process(_delta: float) -> void: + if not instance: + return + instance.process() diff --git a/core/systems/performance/power_station.tres b/core/systems/performance/power_station.tres index 20b696118..14a601808 100644 --- a/core/systems/performance/power_station.tres +++ b/core/systems/performance/power_station.tres @@ -1,6 +1,3 @@ -[gd_resource type="Resource" script_class="PowerStation" load_steps=2 format=3 uid="uid://cdyeux8mdf6l6"] - -[ext_resource type="Script" path="res://core/systems/performance/power_station.gd" id="1_bdrpq"] +[gd_resource type="PowerStationInstance" format=3 uid="uid://c2mmrnh3rcs58"] [resource] -script = ExtResource("1_bdrpq") diff --git a/core/systems/performance/power_station_test.gd b/core/systems/performance/power_station_test.gd index d20f159e3..1444dcac5 100644 --- a/core/systems/performance/power_station_test.gd +++ b/core/systems/performance/power_station_test.gd @@ -1,39 +1,43 @@ extends GutTest -var power_station := load("res://core/systems/performance/power_station.tres") as PowerStation - func test_cpu() -> void: - if not power_station.supports_power_station(): + var powerstation := PowerStation.new() + powerstation.instance = load("res://core/systems/performance/power_station.tres") as PowerStationInstance + add_child_autoqfree(powerstation) + + if not powerstation.instance.is_running(): pass_test("PowerStation is not running, skipping") return - var cpu := power_station.cpu + var cpu := powerstation.instance.get_cpu() assert_not_null(cpu, "should return CPU instance") # Test getting total number of cpu cores - var num_cores = cpu.cores_count + var num_cores := cpu.cores_count + gut.p("Total CPU cores: " + str(num_cores)) assert_ne(num_cores, -1, "should have returned total core count") - # Test that CPU cores get set - cpu.cores_enabled = num_cores - 1 - assert_eq(cpu.cores_enabled, num_cores - 1, "should have disabled cores") - - # Set the cores back - cpu.cores_enabled = num_cores - assert_eq(cpu.cores_enabled, num_cores, "should have re-enabled all cores") - - # Test enumerating cores - var enumerated := cpu.enumerate_cores() - assert_gt(enumerated.size(), 0, "should return at least 1 cpu core path") - # Test getting features - var features = cpu.features + var features := cpu.features + gut.p("Found CPU features: " + str(features)) assert_gt(features.size(), 1, "should return CPU features") if features.size() > 1: var feature := features[0] as String assert_true(cpu.has_feature(feature), "should have CPU feature") assert_false(cpu.has_feature("IdontXsist!"), "should not have CPU feature") + # Test that CPU cores get set + gut.p("Disabling one CPU core") + cpu.cores_enabled = num_cores - 1 + gut.p("Cores enabled: " + str(cpu.cores_enabled)) + assert_eq(cpu.cores_enabled, num_cores - 1, "should have disabled cores") + + # Set the cores back + gut.p("Re-enabling CPU core") + cpu.cores_enabled = num_cores + gut.p("Cores enabled: " + str(cpu.cores_enabled)) + assert_eq(cpu.cores_enabled, num_cores, "should have re-enabled all cores") + # Test setting SMT cpu.smt_enabled = false assert_false(cpu.smt_enabled, "should have disabled SMT") @@ -44,24 +48,32 @@ func test_cpu() -> void: # Test setting boost cpu.boost_enabled = false - assert_false(cpu.boost_enabled, "should have disabled boost") + #assert_false(cpu.boost_enabled, "should have disabled boost") await wait_frames(1, "wait for change") cpu.boost_enabled = true - assert_true(cpu.boost_enabled, "should have enabled boost") + #assert_true(cpu.boost_enabled, "should have enabled boost") await wait_frames(1, "wait for change") + # Test enumerating cores + var cores := cpu.get_cores() + assert_gt(cores.size(), 0, "should return at least 1 cpu core") + for core in cores: + gut.p("CPU Core: " + str(core.number)) + gut.p(" ID: " + str(core.core_id)) + gut.p(" Online: " + str(core.online)) + func test_gpu() -> void: - if not power_station.supports_power_station(): + var powerstation := PowerStation.new() + powerstation.instance = load("res://core/systems/performance/power_station.tres") as PowerStationInstance + add_child_autoqfree(powerstation) + + if not powerstation.instance.is_running(): pass_test("PowerStation is not running, skipping") return - var gpu := power_station.gpu + var gpu := powerstation.instance.get_gpu() assert_not_null(gpu, "should return GPU instance") - # Test enumerating cards - var card_paths := gpu.enumerate_cards() - #assert_gt(cards.size(), 0, "should return at least 1 gpu") - # Test all GPU card methods var cards := gpu.get_cards() for card in cards: diff --git a/core/systems/power/power_manager.gd b/core/systems/power/power_manager.gd index 371ff69ae..72fccbe19 100644 --- a/core/systems/power/power_manager.gd +++ b/core/systems/power/power_manager.gd @@ -1,352 +1,27 @@ -extends Resource +@icon("res://assets/editor-icons/material-symbols--battery-profile-sharp.svg") +extends Node class_name PowerManager -const POWER_BUS := "org.freedesktop.UPower" -const UPOWER_PATH := "/org/freedesktop/UPower" -const POWER_PREFIX := "/org/freedesktop/UPower/devices" -const IFACE_UPOWER := "org.freedesktop.UPower" -const IFACE_DEVICE := "org.freedesktop.UPower.Device" +## Manages power settings. +## +## The [PowerManager] class is responsible for loading a [UPowerInstance] and +## calling its 'process()' method each frame. -enum DEVICE_TYPE { - UNKNOWN, - LINE_POWER, - BATTERY, - UPS, - MONITOR, - MOUSE, - KEYBOARD, - PDA, - PHONE, -} +@export var instance: UPowerInstance = load("res://core/systems/power/power_manager.tres") as UPowerInstance -enum DEVICE_STATE { - UNKNOWN, - CHARGING, - DISCHARGING, - EMPTY, - FULLY_CHARGED, - PENDING_CHARGE, - PENDING_DISCHARGE, -} +# Keep a reference to device instances so they are not cleaned up automatically +var _devices: Array[UPowerDevice] +var logger := Log.get_logger("PowerManager") -enum DEVICE_WARNING_LEVEL { - UNKNOWN, - NONE, - DISCHARGING, - LOW, - CRITICAL, - ACTION, -} -enum DEVICE_BATTERY_LEVEL { - UNKNOWN, - NONE, - LOW, - CRITICAL, - NORMAL, - HIGH, - FULL,Z -} +func _ready() -> void: + var display_device := instance.get_display_device() + _devices.push_back(display_device) + if _devices.is_empty(): + logger.warn("UPower not detected.") -enum DEVICE_TECHNOLOGY { - UNKNOWN, - LITHIUM_ION, - LITHIUM_POLYMER, - LITHIUM_IRON_PHOSPHATE, - LEAD_ACID, - NICKLE_CADMIUM, - NICKLE_METAL_HYDRIDE, -} -var dbus := load("res://core/global/dbus_system.tres") as DBusManager -var upower := UPower.new(dbus.create_proxy(POWER_BUS, UPOWER_PATH)) - - -func get_devices() -> Array[Device]: - var devices: Array[Device] = [] - var device_paths := upower.enumerate_devices() - - # Loop through all objects on the bus - for path in device_paths: - # Create a power Device from this object - var proxy := dbus.create_proxy(POWER_BUS, path) - var device := Device.new(proxy) - - devices.append(device) - - return devices - - -func get_devices_by_type(type: DEVICE_TYPE) -> Array[Device]: - var all_devices := get_devices() - var type_devices : Array[Device] - for device in all_devices: - if device.type == type: - type_devices.append(device) - return type_devices - - -func get_device(device_name: String) -> Device: - var device_path := "/".join([POWER_PREFIX, device_name]) - var proxy := dbus.create_proxy(POWER_BUS, device_path) - var device := Device.new(proxy) - - return device - - -## Returns true if bluetooth can be used on this system -func supports_power() -> bool: - return dbus.bus_exists(POWER_BUS) - - -class UPower extends Resource: - signal updated - var _proxy: DBusManager.Proxy - var on_battery: bool: - get: - var property = _proxy.get_property(IFACE_UPOWER, "OnBattery") - if not property is bool: - return false - return property - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - func enumerate_devices() -> Array: - var result := _proxy.call_method(IFACE_UPOWER, "EnumerateDevices") - var args := result.get_args() - if args.size() != 1: - return [] - if not args[0] is Array: - return [] - return args[0] - - -class Device extends Resource: - signal porperties_changed - signal updated - var _proxy: DBusManager.Proxy - - func _init(proxy: DBusManager.Proxy) -> void: - _proxy = proxy - _proxy.properties_changed.connect(_on_properties_changed) - - func _on_properties_changed(iface: String, props: Dictionary) -> void: - updated.emit() - - func refresh() -> void: - _proxy.call_method(IFACE_DEVICE, "Refresh") - - var native_path: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "NativePath") - if not property is String: - return "" - return property - - var vendor: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Vendor") - if not property is String: - return "" - return property - - var model: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Model") - if not property is String: - return "" - return property - - var serial: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Serial") - if not property is String: - return "" - return property - - var update_time: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "UpdateTime") - if not property is int: - return 0 - return property - - var type: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Type") - if not property is int: - return 0 - return property - - var power_supply: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "PowerSupply") - if not property is bool: - return false - return property - - var has_history: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "HasHistory") - if not property is bool: - return false - return property - - var has_statistics: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "HasStatisics") - if not property is bool: - return false - return property - - var online: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Online") - if not property is bool: - return false - return property - - var energy: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Energy") - if not property is float: - return 0.0 - return property - - var energy_empty: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "EnergyEmpty") - if not property is float: - return 0.0 - return property - - var energy_full: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "EnergyFull") - if not property is float: - return 0.0 - return property - - var energy_full_design: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "EnergyFullDesign") - if not property is float: - return 0.0 - return property - - var energy_rate: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "EnergyRate") - if not property is float: - return 0.0 - return property - - var voltage: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Voltage") - if not property is float: - return 0.0 - return property - - var charge_cycles: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "ChargeCycles") - if not property is int: - return 0 - return property - - var luminosity: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Luminosity") - if not property is float: - return 0.0 - return property - - var time_to_empty: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "TimeToEmpty") - if not property is int: - return 0 - return property - - var time_to_full: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "TimeToFull") - if not property is int: - return 0 - return property - - var percentage: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Percentage") - if not property is float: - return 0.0 - return property - - var temperature: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Temperature") - if not property is float: - return 0.0 - return property - - var is_present: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "IsPresent") - if not property is bool: - return false - return property - - var state: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "State") - if not property is int: - return 0 - return property - - var is_rechargable: bool: - get: - var property = _proxy.get_property(IFACE_DEVICE, "IsRechargeable") - if not property is bool: - return false - return property - - var capacity: float: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Capacity") - if not property is float: - return 0.0 - return property - - var technology: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "Technology") - if not property is int: - return 0 - return property - - var warning_level: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "WarningLevel") - if not property is int: - return 0 - return property - - var battery_level: int: - get: - var property = _proxy.get_property(IFACE_DEVICE, "BatteryLevel") - if not property is int: - return 0 - return property - - var icon_name: String: - get: - var property = _proxy.get_property(IFACE_DEVICE, "IconName") - if not property is String: - return "" - return property +func _process(_delta: float) -> void: + if not instance: + return + instance.process() diff --git a/core/systems/power/power_manager.tres b/core/systems/power/power_manager.tres index a13360eaa..79b53674a 100644 --- a/core/systems/power/power_manager.tres +++ b/core/systems/power/power_manager.tres @@ -1,6 +1,3 @@ -[gd_resource type="Resource" script_class="PowerManager" load_steps=2 format=3 uid="uid://dxa4156di7yk2"] - -[ext_resource type="Script" path="res://core/systems/power/power_manager.gd" id="1_57v16"] +[gd_resource type="UPowerInstance" format=3 uid="uid://pgiv8yxro3n8"] [resource] -script = ExtResource("1_57v16") diff --git a/core/systems/power/power_manager_test.gd b/core/systems/power/power_manager_test.gd new file mode 100644 index 000000000..76ef3a8c1 --- /dev/null +++ b/core/systems/power/power_manager_test.gd @@ -0,0 +1,20 @@ +extends GutTest + + +func test_upower() -> void: + var power_manager := PowerManager.new() + power_manager.instance = load("res://core/systems/power/power_manager.tres") + add_child_autoqfree(power_manager) + + if not power_manager.instance.is_running(): + gut.p("InputPlumber is not running. Skipping tests.") + return + + # Get the display device + var display_device := power_manager.instance.get_display_device() + assert_eq(display_device.dbus_path, "/org/freedesktop/UPower/devices/DisplayDevice") + gut.p("DBus Path: " + str(display_device.dbus_path)) + gut.p("Battery level: " + str(display_device.battery_level)) + gut.p("Icon name: " + str(display_device.icon_name)) + gut.p("Percentage: " + str(display_device.percentage)) + gut.p("State: " + str(display_device.state)) diff --git a/core/systems/power/power_saver.gd b/core/systems/power/power_saver.gd index a3a5a91b8..ba6dbdc26 100644 --- a/core/systems/power/power_saver.gd +++ b/core/systems/power/power_saver.gd @@ -6,7 +6,7 @@ class_name PowerSaver var display := load("res://core/global/display_manager.tres") as DisplayManager var settings := load("res://core/global/settings_manager.tres") as SettingsManager -var power_manager := load("res://core/systems/power/power_manager.tres") as PowerManager +var power_manager := load("res://core/systems/power/power_manager.tres") as UPowerInstance const MINUTE := 60 @@ -27,14 +27,13 @@ var dimmed := false var prev_brightness := {} var supports_brightness := display.supports_brightness() var has_battery := false -var batteries : Array[PowerManager.Device] +var display_device := power_manager.get_display_device() var logger := Log.get_logger("PowerSaver") func _ready() -> void: - batteries = power_manager.get_devices_by_type(PowerManager.DEVICE_TYPE.BATTERY) - if batteries.size() > 0: - has_battery = true + if display_device: + has_battery = display_device.is_present if dim_screen_enabled and supports_brightness: dim_timer.timeout.connect(_on_dim_timer_timeout) @@ -46,9 +45,9 @@ func _ready() -> void: func _on_dim_timer_timeout() -> void: # If dimming is disabled when charging, check the battery state - if has_battery and not dim_when_charging: - var status: int = batteries[0].state - if status in [PowerManager.DEVICE_STATE.CHARGING, PowerManager.DEVICE_STATE.FULLY_CHARGED]: + if has_battery and display_device and not dim_when_charging: + var status := display_device.state + if status in [UPowerDevice.STATE_CHARGING, UPowerDevice.STATE_FULLY_CHARGED]: logger.debug("Not dimming because battery is charging") return if not has_battery and not dim_when_charging: @@ -71,9 +70,9 @@ func _on_dim_timer_timeout() -> void: func _on_suspend_timer_timeout() -> void: # If suspend is disabled when charging, check the battery state - if has_battery and not suspend_when_charging: - var status: int = batteries[0].state - if status in [PowerManager.DEVICE_STATE.CHARGING, PowerManager.DEVICE_STATE.FULLY_CHARGED]: + if has_battery and display_device and not suspend_when_charging: + var status := display_device.state + if status in [UPowerDevice.STATE_CHARGING, UPowerDevice.STATE_FULLY_CHARGED]: logger.debug("Not suspending because battery is charging") return if not has_battery and not suspend_when_charging: diff --git a/core/systems/resource/resource_registry.tres b/core/systems/resource/resource_registry.tres new file mode 100644 index 000000000..4c457a768 --- /dev/null +++ b/core/systems/resource/resource_registry.tres @@ -0,0 +1,3 @@ +[gd_resource type="ResourceRegistry" format=3 uid="uid://bsr58xihnpn1j"] + +[resource] diff --git a/core/systems/sandbox/sandbox_bubblewrap.gd b/core/systems/sandbox/sandbox_bubblewrap.gd index 96ab5677d..e42a28a3d 100644 --- a/core/systems/sandbox/sandbox_bubblewrap.gd +++ b/core/systems/sandbox/sandbox_bubblewrap.gd @@ -27,15 +27,16 @@ func is_available() -> bool: # Applies additional blacklists depending on if we're launching a steam app func _apply_quirks(app: LibraryLaunchItem) -> PackedStringArray: var args := PackedStringArray() - # Only block device access if this is a Steam app - if app.command != "steam": - return args - - # Block any hidraw devices - var devices := DirAccess.get_files_at("/dev") - for dev in devices: - if not dev.begins_with("hidraw"): - continue - var path := "/".join(["/dev", dev]) - args.append_array(["--bind", "/dev/null", path]) return args +# # Only block device access if this is a Steam app +# if app.command != "steam": +# return args +# +# # Block any hidraw devices +# var devices := DirAccess.get_files_at("/dev") +# for dev in devices: +# if not dev.begins_with("hidraw"): +# continue +# var path := "/".join(["/dev", dev]) +# args.append_array(["--bind", "/dev/null", path]) +# return args diff --git a/core/systems/state/state.gd b/core/systems/state/state.gd index e76304ccc..bb3f323a5 100644 --- a/core/systems/state/state.gd +++ b/core/systems/state/state.gd @@ -36,4 +36,3 @@ func _to_string() -> String: if not name.is_empty(): return "".format({"name": name}) return "".format({"rid": get_rid()}) - diff --git a/core/systems/testing/pre_run.gd b/core/systems/testing/pre_run.gd index b78c3f844..3732566c6 100644 --- a/core/systems/testing/pre_run.gd +++ b/core/systems/testing/pre_run.gd @@ -1,11 +1,19 @@ extends GutHookScript -var PID: int = OS.get_process_id() -var gamescope := load("res://core/global/gamescope.tres") as Gamescope -var window_id = gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI) +var PID: int +var gamescope: GamescopeInstance +var window_id: int func run() -> void: + PID = OS.get_process_id() + gamescope = load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + if not xwayland: + return + var window_ids := xwayland.get_windows_for_pid(PID) + if not window_ids.is_empty(): + window_id = window_ids[0] if window_id < 0: return diff --git a/core/systems/threading/command.gd b/core/systems/threading/command.gd deleted file mode 100644 index af3ea375e..000000000 --- a/core/systems/threading/command.gd +++ /dev/null @@ -1,73 +0,0 @@ -extends RefCounted -class_name Command - -## Execute an OS command in a thread -## -## The [Command] class allows you to asyncronously execute a command in a thread -## that does not block the main thread. Optionally a [SharedThread] can be -## passed if you do not wish for the command to execute in the [ThreadPool].[br] -## Example: -## [codeblock] -## var cmd := Command.new("cat", ["/etc/issue"]) -## if cmd.execute() != OK: -## print("Command failed with exit code: ", cmd.code) -## -## print(cmd.stdout) -## [/codeblock] - -## The [ThreadPool] to execute the command in -var thread_pool := load("res://core/systems/threading/thread_pool.tres") as ThreadPool -## Optional [SharedThread] to execute the command in -var shared_thread: SharedThread -## Path to the command to execute -var cmd: String -## Array of arguments to pass to the command -var args := PackedStringArray() -## The command output after execution -var stdout: String -## The exit code of the command after execution -var code := 0 - - -func _init(command: String = "", arguments: PackedStringArray = [], thread: SharedThread = null) -> void: - cmd = command - args = arguments - if thread: - shared_thread = thread - - -## Execute the command in a thread and return the command's exit code. -func execute() -> int: - var ret: int - if shared_thread: - ret = await _shared_thread_exec() - else: - ret = await _thread_pool_exec() - - return ret - - -func _shared_thread_exec() -> int: - shared_thread.start() - - var output := [] - var ret := await shared_thread.exec(OS.execute.bind(cmd, args, output)) as int - code = ret - stdout = output[0] - - return ret - - -func _thread_pool_exec() -> int: - thread_pool.start() - - var output := [] - var ret := await thread_pool.exec(OS.execute.bind(cmd, args, output)) as int - code = ret - stdout = output[0] - - return ret - - -func _to_string() -> String: - return "Command<{0} {1}>".format([cmd, " ".join(args)]) diff --git a/core/systems/threading/command_sync.gd b/core/systems/threading/command_sync.gd deleted file mode 100644 index 4a522cd84..000000000 --- a/core/systems/threading/command_sync.gd +++ /dev/null @@ -1,35 +0,0 @@ -extends RefCounted -class_name CommandSync - -## Execute a blocking OS command -## -## The [CommandSync] class allows you to syncronously execute a command that -## will block the executing thread. - -## Path to the command to execute -var cmd: String -## Array of arguments to pass to the command -var args := PackedStringArray() -## The command output after execution -var stdout: String -## The exit code of the command after execution -var code := 0 - - -func _init(command: String = "", arguments: PackedStringArray = []) -> void: - cmd = command - args = arguments - - -## Execute the command in a thread and return the command's exit code. -func execute() -> int: - var output := [] - var ret := OS.execute(cmd, args, output) - code = ret - stdout = output[0] - - return ret - - -func _to_string() -> String: - return "CommandSync<{0} {1}>".format([cmd, " ".join(args)]) diff --git a/core/systems/threading/command_test.gd b/core/systems/threading/command_test.gd new file mode 100644 index 000000000..88634a4e0 --- /dev/null +++ b/core/systems/threading/command_test.gd @@ -0,0 +1,60 @@ +extends GutTest + + +func test_command(): + # Ensure the [ResourceProcessor] node is added to the scene tree + var resource_processor := ResourceProcessor.new() + resource_processor.registry = load("res://core/systems/resource/resource_registry.tres") as ResourceRegistry + add_child_autoqfree(resource_processor) + + var command := Command.create("ls", ["/", "/idontexistforrealz"]) + gut.p("Executing command: " + command.command + " " + str(command.args)) + if command.execute() != OK: + gut.p(" Failed to start executing command") + var code := await command.finished as int + gut.p(" Command exited with code: " + str(code)) + gut.p(command.stdout) + gut.p(command.stderr) + assert_false(command.stdout.is_empty(), "should return output") + assert_eq(command.stderr, "ls: cannot access '/idontexistforrealz': No such file or directory\n", "should produce error output") + + command = Command.create("echo", ["-n", "one", "two", "three"]) + gut.p("Executing command: " + command.command + " " + str(command.args)) + if command.execute() != OK: + gut.p(" Failed to start executing command") + code = await command.finished as int + gut.p(" Command exited with code: " + str(code)) + gut.p(command.stdout) + assert_eq(code, OK, "should return zero") + assert_eq(command.stdout, "one two three", "should have correct output") + + command = Command.create("Id0NtEx1st", []) + gut.p("Executing command: " + command.command + " " + str(command.args)) + if command.execute() != OK: + gut.p(" Failed to start executing command") + code = await command.finished as int + assert_ne(code, OK, "should fail to run") + + +func test_command_cancel(): + # Ensure the [ResourceProcessor] node is added to the scene tree + var resource_processor := ResourceProcessor.new() + resource_processor.registry = load("res://core/systems/resource/resource_registry.tres") as ResourceRegistry + add_child_autoqfree(resource_processor) + + var command := Command.create("sleep", ["5"]) + command.timeout = 1.0 + gut.p("Executing command: " + command.command + " " + str(command.args)) + if command.execute() != OK: + gut.p(" Failed to start executing command") + var code := await command.finished as int + gut.p(" Command exited with code: " + str(code)) + assert_eq(code, command.EXIT_CODE_CANCEL, "should exit with code 130, indicating cancellation") + + +func test_command_sync(): + var command := Command.create("sleep", ["1"]) + gut.p("Executing command: " + command.command + " " + str(command.args)) + var code := command.execute_blocking() + gut.p(" Command exited with code: " + str(code)) + assert_eq(code, 0, "should exit ok") diff --git a/core/systems/threading/linuxthread_test.gd b/core/systems/threading/linuxthread_test.gd index 9e1f19600..97396e3f2 100644 --- a/core/systems/threading/linuxthread_test.gd +++ b/core/systems/threading/linuxthread_test.gd @@ -2,9 +2,9 @@ extends GutTest func test_subreaper_create_process() -> void: - var pid := LinuxThread.subreaper_create_process("sleep", ["1"]) + var pid := SubReaper.create_process("sleep", ["1"]) gut.p("Got reaper PID: " + str(pid)) - + assert_true(OS.is_process_running(pid), "reaper process should be running") await wait_seconds(2) assert_false(OS.is_process_running(pid), "reaper process should have exited") diff --git a/core/systems/threading/pty_test.gd b/core/systems/threading/pty_test.gd new file mode 100644 index 000000000..6386fa7d8 --- /dev/null +++ b/core/systems/threading/pty_test.gd @@ -0,0 +1,24 @@ +extends GutTest + + +func test_pty() -> void: + var pty := Pty.new() + add_child_autoqfree(pty) + + # Listen for output from the command + var on_line_written := func(line: String): + gut.p("Line: " + line) + if line.contains("Type something:"): + pty.write_line("Hello World") + pty.line_written.connect(on_line_written) + + # Execute the command in the PTY + var result := pty.exec("bash", PackedStringArray(["-c", "read -p 'Type something: ' foo; echo 'You typed:' $foo"])) + assert_eq(result, OK) + + # Wait for the command to exit + var exit_code = await pty.finished + + gut.p("Command finished with exit code: " + str(exit_code)) + + await wait_seconds(5, "Waiting") diff --git a/core/systems/threading/shared_thread.gd b/core/systems/threading/shared_thread.gd index ffd820f1a..416021d94 100644 --- a/core/systems/threading/shared_thread.gd +++ b/core/systems/threading/shared_thread.gd @@ -94,17 +94,18 @@ func set_priority(value: int) -> int: mutex.lock() var thread_id := tid mutex.unlock() - if LinuxThread.get_tid() != thread_id: - logger.debug("Set thread priority was called from another thread") - return await exec(set_priority.bind(value)) + # TODO: fixme + #if LinuxThread.get_tid() != thread_id: + # logger.debug("Set thread priority was called from another thread") + # return await exec(set_priority.bind(value)) # Set the thread priority if this function was called from the SharedThread - var err := LinuxThread.set_thread_priority(value) - if err == OK: - niceness = value - logger.info("Set thread niceness on {0} ({1}) to: {2}".format([name, thread_id, value])) + #var err := LinuxThread.set_thread_priority(value) + #if err == OK: + # niceness = value + # logger.info("Set thread niceness on {0} ({1}) to: {2}".format([name, thread_id, value])) - return err + return OK ## Add the given [NodeThread] to the list of nodes to process. This should @@ -204,9 +205,9 @@ func remove_process(method: Callable) -> void: func _run() -> void: # Update the thread ID mutex.lock() - tid = LinuxThread.get_tid() + #tid = LinuxThread.get_tid() mutex.unlock() - logger.info("Started thread with thread ID: " + str(LinuxThread.get_tid())) + #logger.info("Started thread with thread ID: " + str(LinuxThread.get_tid())) # If the nice value isn't default, reassign the thread priority if niceness != 0: diff --git a/core/systems/threading/thread_pool.gd b/core/systems/threading/thread_pool.gd index 7fa774cb3..0fe400f01 100644 --- a/core/systems/threading/thread_pool.gd +++ b/core/systems/threading/thread_pool.gd @@ -25,6 +25,7 @@ var logger := Log.get_logger("ThreadPool", Log.LEVEL.INFO) ## A queued task to run in the thread pool class Task extends RefCounted: + var task_name: String var method: Callable var ret: Variant @@ -75,10 +76,14 @@ func is_running() -> bool: ## method and awaits it to be called during the process loop. You should await ## this method if your method returns something. ## E.g. [code]var result = await thread_pool.exec(myfund.bind("myarg"))[/code] -func exec(method: Callable) -> Variant: +func exec(method: Callable, name: String = "") -> Variant: if size == 0: return method.call() var task := Task.new() + if name.is_empty(): + task.task_name = str(method) + else: + task.task_name = name task.method = method mutex.lock() queue.append(task) @@ -108,7 +113,7 @@ func _process(id: int) -> void: var task := queue.pop_front() as Task mutex.unlock() - logger.debug("Processing task in thread " + str(id)) + logger.debug("Processing task", task.task_name, "in thread " + str(id)) _async_call(task) diff --git a/core/systems/utility/vdf_test.gd b/core/systems/utility/vdf_test.gd new file mode 100644 index 000000000..89e665907 --- /dev/null +++ b/core/systems/utility/vdf_test.gd @@ -0,0 +1,80 @@ +extends GutTest + +const LOGIN_USERS_VDF: String = """"users" +{ + "12345678901234567" + { + "AccountName" "ACCOUNT_NAME" + "MostRecent" "1" + "RememberPassword" "1" + "PersonaName" "PERSONA_NAME" + "Timestamp" "1234567890" + } +} +""" + +const LOGIN_USERS_DICT: Dictionary = { + "users": { + "12345678901234567": { + "AccountName": "ACCOUNT_NAME", + "MostRecent": "1", + "PersonaName": "PERSONA_NAME", + "RememberPassword": "1", + "Timestamp": "1234567890" + } + } +} + +const LOCAL_VDF: String = """"MachineUserConfigStore" +{ + "Software" + { + "valve" + { + "Steam" + { + "ConnectCache" + { + "1111a2222" "someverylongtext" + } + } + } + } +} +""" + +const LOCAL_DICT: Dictionary = { + "MachineUserConfigStore": { + "Software": { + "valve": { + "Steam": { + "ConnectCache": { + "1111a2222": "someverylongtext" + } + } + } + } + } +} + +const vdf_content: Array[String] = [LOGIN_USERS_VDF, LOCAL_VDF] +const dict_content: Array[Dictionary] = [LOGIN_USERS_DICT, LOCAL_DICT] + +func test_parse() -> void: + var i := 0 + for vdf_data in vdf_content: + var vdf := Vdf.new() + var err := vdf.parse(vdf_data) + assert_eq(err, OK, "should successfully parse") + gut.p("Got result: " + str(vdf.data)) + assert_eq(vdf.data, dict_content[i], "should deserialize the correct values") + i += 1 + + +func test_stringify() -> void: + var i := 0 + for dict_data in dict_content: + var text := Vdf.stringify(dict_data) + gut.p("Got serialized result:\n" + text) + assert_false(text.is_empty(), "should serialize into text") + i += 1 diff --git a/core/ui/card_ui/card_ui.gd b/core/ui/card_ui/card_ui.gd index 44327f30a..057c7f16d 100644 --- a/core/ui/card_ui/card_ui.gd +++ b/core/ui/card_ui/card_ui.gd @@ -1,10 +1,11 @@ extends Control var platform := load("res://core/global/platform.tres") as Platform -var gamescope := load("res://core/global/gamescope.tres") as Gamescope +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance var library_manager := load("res://core/global/library_manager.tres") as LibraryManager var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager -var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber +var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance +var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager var state_machine := ( preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine @@ -20,7 +21,10 @@ var osk_state := preload("res://assets/state/states/osk.tres") as State var power_state := preload("res://assets/state/states/power_menu.tres") as State var PID: int = OS.get_process_id() -var overlay_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI) +var _xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) +var _xwayland_ogui := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) +var _xwayland_game := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_GAME) +var overlay_window_id := 0 @onready var panel := $%Panel as Panel @onready var ui_container := $%MenuContent as MarginContainer @@ -33,6 +37,12 @@ var overlay_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI) var logger = Log.get_logger("Main", Log.LEVEL.INFO) func _init() -> void: + # Discover the window id of OpenGamepadUI + if _xwayland_ogui: + var ogui_windows := _xwayland_ogui.get_windows_for_pid(PID) + if not ogui_windows.is_empty(): + overlay_window_id = ogui_windows[0] + # Tell gamescope that we're an overlay if overlay_window_id <= 0: logger.error("Unable to detect Window ID. Overlay is not going to work!") @@ -47,12 +57,34 @@ func _setup(window_id: int) -> void: return # Pretend to be Steam # Gamescope is hard-coded to look for appId 769 - if gamescope.set_main_app(window_id) != OK: + if _xwayland_primary.set_main_app(window_id) != OK: logger.error("Unable to set STEAM_GAME atom!") # Sets ourselves to the input focus - if gamescope.set_input_focus(window_id, 1) != OK: + if _xwayland_primary.set_input_focus(window_id, 1) != OK: logger.error("Unable to set STEAM_INPUT_FOCUS atom!") + # Override reserved app ids for any newly created windows + # Listen for window created/destroyed events + var on_window_created := func(window_id: int): + logger.debug("Window created:", window_id) + var try := 0 + while try < 10: + if _xwayland_game.has_app_id(window_id): + break + try += 1 + await get_tree().create_timer(0.2).timeout # wait a beat + var app_id := _xwayland_game.get_app_id(window_id) + if app_id == GamescopeInstance.OVERLAY_GAME_ID: + # Find the current running app and use that app id + var running := launch_manager.get_running() + running.reverse() + for app in running: + _xwayland_game.set_app_id(window_id, app.app_id) + return + logger.warn("Unable to find a running app to tie Steam to") + _xwayland_game.set_app_id(window_id, 7769) + _xwayland_game.window_created.connect(on_window_created) + # Called when the node enters the scene tree for the first time. # gamescope --xwayland-count 2 -- build/opengamepad-ui.x86_64 @@ -111,19 +143,24 @@ func _ready() -> void: get_viewport().gui_focus_changed.connect(_on_focus_changed) library_manager.reload_library() - # Set the initial intercept mode - input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.ALL) - var on_device_changed := func(device: InputPlumber.CompositeDevice): - var intercept_mode : InputPlumber.INTERCEPT_MODE = input_plumber.intercept_mode_current + # Setup inputplumber to receive guide presses. + input_plumber.set_intercept_mode(InputPlumberInstance.INTERCEPT_MODE_ALL) + input_plumber.set_intercept_activation(PackedStringArray(["Gamepad:Button:Guide"]), "Gamepad:Button:Guide") + + # Sets the intercept mode and intercept activation keys to what overlay_mode expects. + var on_device_changed := func(device: CompositeDevice): + var intercept_mode := input_plumber.intercept_mode logger.debug("Setting intercept mode to: " + str(intercept_mode)) - input_plumber.set_intercept_mode_single(intercept_mode, device) - input_plumber.composite_device_changed.connect(on_device_changed) + device.intercept_mode = intercept_mode + device.set_intercept_activation(PackedStringArray(["Gamepad:Button:Guide"]), "Gamepad:Button:Guide") + input_plumber.composite_device_added.connect(on_device_changed) # Set the theme if one was set var theme_path := settings_manager.get_value("general", "theme", "res://assets/themes/card_ui-dracula.tres") as String logger.debug("Setting theme to: " + theme_path) var loaded_theme = load(theme_path) if loaded_theme != null: + @warning_ignore("unsafe_call_argument") set_theme(loaded_theme) else: logger.debug("Unable to load theme") @@ -137,13 +174,13 @@ func _on_focus_changed(control: Control) -> void: ## Invoked when the in-game state was entered func _on_game_state_entered(_from: State) -> void: # Pass all gamepad input to the game - input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.PASS) + input_plumber.set_intercept_mode(InputPlumberInstance.INTERCEPT_MODE_PASS) # Turn off gamescope blur effect - _set_blur(gamescope.BLUR_MODE.OFF) + _set_blur(GamescopeXWayland.BLUR_MODE_OFF) # Set gamescope input focus to off so the user can interact with the game - if gamescope.set_input_focus(overlay_window_id, 0) != OK: + if _xwayland_ogui and _xwayland_ogui.set_input_focus(overlay_window_id, 0) != OK: logger.error("Unable to set STEAM_INPUT_FOCUS atom!") # Ensure panel is invisible @@ -160,7 +197,7 @@ func _on_game_state_entered(_from: State) -> void: ## Invoked when the in-game state is exited func _on_game_state_exited(to: State) -> void: # Intercept all gamepad input when not in-game - input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.ALL) + input_plumber.set_intercept_mode(InputPlumberInstance.INTERCEPT_MODE_ALL) # Revert back to the default gamepad profile #gamepad_manager.set_gamepads_profile(null) @@ -171,16 +208,18 @@ func _on_game_state_exited(to: State) -> void: if current_popup == osk_state: return - if gamescope.set_input_focus(overlay_window_id, 1) != OK: - logger.error("Unable to set STEAM_INPUT_FOCUS atom!") + if _xwayland_primary: + if _xwayland_primary.set_input_focus(overlay_window_id, 1) != OK: + logger.error("Unable to set STEAM_INPUT_FOCUS atom!") # If the in-game state still exists in the stack, set the blur state. if state_machine.has_state(in_game_state): panel.visible = false # Only blur if the focused GFX app is set - var should_blur := settings_manager.get_value("display", "enable_overlay_blur", true) as bool - if should_blur and gamescope.get_focused_app_gfx() != Gamescope.OVERLAY_GAME_ID: - _set_blur(gamescope.BLUR_MODE.ALWAYS) + if _xwayland_primary: + var should_blur := settings_manager.get_value("display", "enable_overlay_blur", false) as bool + if should_blur and _xwayland_primary.get_focused_app_gfx() != gamescope.OVERLAY_GAME_ID: + _set_blur(GamescopeXWayland.BLUR_MODE_ALWAYS) else: _on_game_state_removed() @@ -195,7 +234,7 @@ func _on_game_state_exited(to: State) -> void: ## Invoked when the in-game state is removed func _on_game_state_removed() -> void: # Turn off gamescope blur - _set_blur(gamescope.BLUR_MODE.OFF) + _set_blur(GamescopeXWayland.BLUR_MODE_OFF) # Un-hide the background panel panel.visible = true @@ -208,12 +247,14 @@ func _on_game_state_removed() -> void: # Sets the blur mode in gamescope -func _set_blur(mode: Gamescope.BLUR_MODE) -> void: +func _set_blur(mode: int) -> void: + if not _xwayland_primary: + return # Sometimes setting this may fail when Steam closes. Retry several times. for try in range(10): - if gamescope.set_blur_mode(mode) != OK: - logger.warn("Unable to set blur mode atom!") - var current := gamescope.get_blur_mode() + if _xwayland_primary: + _xwayland_primary.set_blur_mode(mode) + var current := _xwayland_primary.get_blur_mode() if mode == current: break logger.warn("Retrying in " + str(try) + "ms") @@ -255,7 +296,7 @@ func _input(event: InputEvent) -> void: if not power_timer.is_stopped(): logger.info("Received suspend signal") for connection in power_timer.timeout.get_connections(): - power_timer.timeout.disconnect(connection["callable"]) + power_timer.timeout.disconnect(connection["callable"] as Callable) power_timer.stop() var output: Array = [] if OS.execute("systemctl", ["suspend"], output) != OK: @@ -263,12 +304,12 @@ func _input(event: InputEvent) -> void: # Removes specified child elements from the given Node. -func _remove_children(remove_list: PackedStringArray, parent:Node) -> void: +func _remove_children(remove_list: PackedStringArray, parent: Node) -> void: var child_count := parent.get_child_count() - var to_remove_list := [] + var to_remove_list: Array[Node] for child_idx in child_count: - var child = parent.get_child(child_idx) + var child := parent.get_child(child_idx) logger.trace("Checking if " + child.name + " in remove list...") if child.name in remove_list: logger.trace(child.name + " queued for removal!") diff --git a/core/ui/card_ui/card_ui.tscn b/core/ui/card_ui/card_ui.tscn index 80c61b45a..28e61d095 100644 --- a/core/ui/card_ui/card_ui.tscn +++ b/core/ui/card_ui/card_ui.tscn @@ -1,33 +1,42 @@ -[gd_scene load_steps=38 format=3 uid="uid://fhriwlhm0lcj"] +[gd_scene load_steps=47 format=3 uid="uid://fhriwlhm0lcj"] [ext_resource type="PackedScene" uid="uid://n83wlhmmsu3j" path="res://core/systems/input/input_manager.tscn" id="1_34t85"] [ext_resource type="Script" path="res://core/ui/card_ui/card_ui.gd" id="1_f8851"] [ext_resource type="PackedScene" uid="uid://dlegwm7jqfe2i" path="res://core/systems/boxart/boxart_local.tscn" id="2_600i0"] [ext_resource type="PackedScene" uid="uid://ch6qw6obetalo" path="res://core/systems/library/library_desktop.tscn" id="3_68bes"] [ext_resource type="Script" path="res://core/systems/input/input_icon_processor.gd" id="3_y116l"] -[ext_resource type="PackedScene" uid="uid://cbboox5bujlx1" path="res://core/systems/launcher/launch_manager.tscn" id="4_tgw75"] +[ext_resource type="Script" path="res://core/systems/gamescope/gamescope.gd" id="4_ksi1t"] [ext_resource type="PackedScene" uid="uid://uam46dtvo2yh" path="res://core/systems/plugin/plugin_manager.tscn" id="5_dv70s"] +[ext_resource type="GamescopeInstance" uid="uid://chd0nc6gbfnw0" path="res://core/systems/gamescope/gamescope.tres" id="5_wmkau"] [ext_resource type="PackedScene" uid="uid://o70x5igrlq30" path="res://core/ui/card_ui/home/cardui_home.tscn" id="6_12lhu"] [ext_resource type="PackedScene" uid="uid://dnq5j20fbcrwx" path="res://core/systems/power/power_saver.tscn" id="8_hyc1j"] [ext_resource type="PackedScene" uid="uid://bcdk1lj6enq3l" path="res://core/ui/card_ui/launch/game_launch_menu.tscn" id="9_m34in"] [ext_resource type="PackedScene" uid="uid://d2jiecrd5sw4s" path="res://core/ui/card_ui/settings/settings_menu.tscn" id="10_1ruvi"] [ext_resource type="PackedScene" uid="uid://d4bmkauhrlhq0" path="res://core/ui/card_ui/navigation/search_bar_menu.tscn" id="10_7bl6b"] [ext_resource type="PackedScene" uid="uid://58qlqqbh58im" path="res://core/ui/card_ui/launch/game_settings.tscn" id="10_7cj6o"] +[ext_resource type="Script" path="res://core/systems/power/power_manager.gd" id="10_opsp8"] [ext_resource type="PackedScene" uid="uid://uqkwpeq7f1o" path="res://core/ui/card_ui/library/library_menu.tscn" id="10_uqodp"] +[ext_resource type="UPowerInstance" uid="uid://pgiv8yxro3n8" path="res://core/systems/power/power_manager.tres" id="11_nk5v7"] [ext_resource type="PackedScene" uid="uid://by0i08fw1fwty" path="res://core/ui/card_ui/navigation/top_button_menu.tscn" id="11_x7ns7"] [ext_resource type="PackedScene" uid="uid://cd2p3lu01aric" path="res://core/ui/card_ui/navigation/context_bar_menu.tscn" id="12_bstc8"] [ext_resource type="PackedScene" uid="uid://jfacx7uys32r" path="res://core/ui/card_ui/main-menu/main_menu.tscn" id="13_46tck"] [ext_resource type="PackedScene" uid="uid://cwarv58ju0sow" path="res://core/ui/card_ui/gamepad/gamepad_settings.tscn" id="13_n64ve"] +[ext_resource type="Script" path="res://core/systems/performance/power_station.gd" id="13_tag7s"] +[ext_resource type="PowerStationInstance" uid="uid://c2mmrnh3rcs58" path="res://core/systems/performance/power_station.tres" id="14_a5vk3"] [ext_resource type="PackedScene" uid="uid://b30stcxjwk3od" path="res://core/ui/card_ui/ootbe/first_boot_menu.tscn" id="14_dpfu1"] +[ext_resource type="Script" path="res://core/systems/launcher/launcher.gd" id="14_fs00k"] [ext_resource type="PackedScene" uid="uid://hroo3ll4inrb" path="res://core/ui/card_ui/quick_bar/quick_bar_menu.tscn" id="14_lsaok"] +[ext_resource type="Script" path="res://core/systems/network/network_manager.gd" id="14_rqu7s"] +[ext_resource type="NetworkManagerInstance" uid="uid://cmwavccnjwpr0" path="res://core/systems/network/network_manager.tres" id="15_k47ua"] [ext_resource type="PackedScene" uid="uid://dj1fooc3gh13l" path="res://core/ui/card_ui/help/help_menu.tscn" id="15_m1wp2"] +[ext_resource type="ResourceRegistry" uid="uid://bsr58xihnpn1j" path="res://core/systems/resource/resource_registry.tres" id="15_ne50o"] [ext_resource type="PackedScene" uid="uid://cu4l0d1joc37w" path="res://core/ui/card_ui/navigation/in-game_notification.tscn" id="16_bcudw"] [ext_resource type="PackedScene" uid="uid://vf4sij64f82b" path="res://core/ui/common/osk/on_screen_keyboard.tscn" id="18_462u5"] [ext_resource type="PackedScene" uid="uid://doft5r1y37j1" path="res://core/ui/components/volume_indicator.tscn" id="18_g4maw"] [ext_resource type="PackedScene" uid="uid://bbcd5tclmp2ux" path="res://core/ui/card_ui/navigation/library_loading_notification.tscn" id="20_7tnsn"] [ext_resource type="PackedScene" uid="uid://lfm3ps837gy5" path="res://core/ui/card_ui/power/power_menu.tscn" id="20_noim3"] [ext_resource type="VideoStream" path="res://assets/videos/boot.ogv" id="21_s3peg"] -[ext_resource type="PackedScene" path="res://core/ui/common/game/game_loading.tscn" id="22_morxu"] +[ext_resource type="PackedScene" uid="uid://b1kist0rarpcy" path="res://core/ui/common/game/game_loading.tscn" id="22_morxu"] [ext_resource type="PackedScene" uid="uid://ccwn4n40imfg8" path="res://core/ui/components/containers/overlay_container.tscn" id="23_h8glb"] [ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="27_jq430"] [ext_resource type="PackedScene" uid="uid://ekhjpmat02f8" path="res://core/systems/effects/slide_effect.tscn" id="28_ex7w4"] @@ -84,26 +93,48 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_f8851") -[node name="InputManager" parent="." instance=ExtResource("1_34t85")] - [node name="InputIconProcessor" type="Node" parent="."] script = ExtResource("3_y116l") +[node name="Gamescope" type="Node" parent="."] +script = ExtResource("4_ksi1t") +instance = ExtResource("5_wmkau") + [node name="BoxArtLocal" parent="." instance=ExtResource("2_600i0")] [node name="DesktopLibrary" parent="." instance=ExtResource("3_68bes")] -[node name="LaunchManager" parent="." instance=ExtResource("4_tgw75")] - [node name="PluginManager" parent="." instance=ExtResource("5_dv70s")] +[node name="PowerManager" type="Node" parent="."] +script = ExtResource("10_opsp8") +instance = ExtResource("11_nk5v7") + +[node name="Launcher" type="Node" parent="."] +script = ExtResource("14_fs00k") + [node name="PowerSaver" parent="." instance=ExtResource("8_hyc1j")] +[node name="PowerStation" type="Node" parent="."] +script = ExtResource("13_tag7s") +instance = ExtResource("14_a5vk3") + +[node name="NetworkManager" type="Node" parent="."] +script = ExtResource("14_rqu7s") +instance = ExtResource("15_k47ua") + +[node name="ResourceProcessor" type="ResourceProcessor" parent="."] +registry = ExtResource("15_ne50o") + [node name="PowerTimer" type="Timer" parent="."] unique_name_in_owner = true wait_time = 1.5 one_shot = true +[node name="GameLoading" parent="." instance=ExtResource("22_morxu")] +visible = false +layout_mode = 1 + [node name="Panel" type="Panel" parent="."] unique_name_in_owner = true layout_mode = 1 @@ -219,10 +250,6 @@ layout_mode = 2 visible = false layout_mode = 2 -[node name="GameLoading" parent="." instance=ExtResource("22_morxu")] -visible = false -layout_mode = 1 - [node name="AlwaysVisibleContent" type="MarginContainer" parent="."] editor_description = "Anything in this container will always be visible, even during in-game states. " @@ -339,4 +366,6 @@ grow_vertical = 2 stream = ExtResource("21_s3peg") autoplay = true +[node name="InputManager" parent="." instance=ExtResource("1_34t85")] + [editable path="AlwaysVisibleContent/OnScreenKeyboard"] diff --git a/core/ui/card_ui/gamepad/gamepad_mapper.gd b/core/ui/card_ui/gamepad/gamepad_mapper.gd index 1d0522375..2e5585d66 100644 --- a/core/ui/card_ui/gamepad/gamepad_mapper.gd +++ b/core/ui/card_ui/gamepad/gamepad_mapper.gd @@ -72,7 +72,7 @@ func _ready() -> void: func _on_state_entered(_from: State) -> void: var texture := change_input_state.get_meta("texture") as Texture2D var mapping := change_input_state.get_meta("mappings") as InputPlumberMapping - var gamepad := change_input_state.get_meta("gamepad") as InputPlumber.CompositeDevice + var gamepad := change_input_state.get_meta("gamepad") as CompositeDevice var gamepad_type := change_input_state.get_meta("gamepad_type") as String var gamepad_icons_type := change_input_state.get_meta("gamepad_type_icon_map") as String diff --git a/core/ui/card_ui/gamepad/gamepad_mapper.tscn b/core/ui/card_ui/gamepad/gamepad_mapper.tscn index a208cd533..de5bb95e5 100644 --- a/core/ui/card_ui/gamepad/gamepad_mapper.tscn +++ b/core/ui/card_ui/gamepad/gamepad_mapper.tscn @@ -8,7 +8,7 @@ [ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="4_scwwv"] [ext_resource type="Resource" uid="uid://46cu324n427u" path="res://assets/state/states/gamepad_change_input.tres" id="4_sv30y"] [ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="5_i6uqd"] -[ext_resource type="Texture2D" uid="uid://dyemqkvdtk43e" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="8_5jaa6"] +[ext_resource type="Texture2D" uid="uid://cxvxwgt1vox0" path="res://assets/images/gamepad/xbox/xbox_button_color_a.svg" id="8_5jaa6"] [ext_resource type="PackedScene" uid="uid://cgmb4kr2ec4ha" path="res://core/ui/components/tabs_header.tscn" id="8_okgql"] [ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="9_an8os"] [ext_resource type="Resource" uid="uid://b58i2ue2qwe6f" path="res://core/ui/card_ui/gamepad/gamepad_mapper_tabs_state.tres" id="10_gr678"] @@ -16,7 +16,7 @@ [ext_resource type="PackedScene" uid="uid://cr83fmlociwko" path="res://core/ui/components/card_icon_button.tscn" id="11_taofv"] [ext_resource type="PackedScene" uid="uid://vf4sij64f82b" path="res://core/ui/common/osk/on_screen_keyboard.tscn" id="12_30f1c"] [ext_resource type="Texture2D" uid="uid://dj1ohb74chydb" path="res://assets/ui/icons/round-delete-forever.svg" id="12_akkxd"] -[ext_resource type="Texture2D" uid="uid://bie2sf8kjawr" path="res://assets/images/gamepad/xbox/XboxOne_Diagram.png" id="13_hvmil"] +[ext_resource type="Texture2D" uid="uid://ceyt88pt5sogo" path="res://assets/images/gamepad/xbox/XboxOne_Diagram.png" id="13_hvmil"] [ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="16_bkk3w"] [ext_resource type="Texture2D" uid="uid://05mc8os4g5ax" path="res://assets/images/mouse/diagram.svg" id="18_lkxxd"] [ext_resource type="Script" path="res://core/global/keyboard_instance.gd" id="18_u0i73"] @@ -114,10 +114,12 @@ unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 theme_override_styles/panel = SubResource("StyleBoxEmpty_t62r6") +current_tab = 0 tabs_visible = false [node name="Gamepad" type="MarginContainer" parent="VBoxContainer/TabContainer"] layout_mode = 2 +metadata/_tab_index = 0 [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/TabContainer/Gamepad"] layout_mode = 2 @@ -219,6 +221,7 @@ text = "Touch Pads" [node name="Keyboard" type="MarginContainer" parent="VBoxContainer/TabContainer"] visible = false layout_mode = 2 +metadata/_tab_index = 1 [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/TabContainer/Keyboard"] layout_mode = 2 @@ -248,6 +251,7 @@ size_flags_vertical = 3 [node name="Mouse" type="MarginContainer" parent="VBoxContainer/TabContainer"] visible = false layout_mode = 2 +metadata/_tab_index = 2 [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/TabContainer/Mouse"] layout_mode = 2 @@ -305,6 +309,7 @@ text = "Buttons" [node name="System" type="MarginContainer" parent="VBoxContainer/TabContainer"] visible = false layout_mode = 2 +metadata/_tab_index = 3 [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/TabContainer/System"] layout_mode = 2 diff --git a/core/ui/card_ui/gamepad/gamepad_settings.gd b/core/ui/card_ui/gamepad/gamepad_settings.gd index d5dbdab00..bed2b5c04 100644 --- a/core/ui/card_ui/gamepad/gamepad_settings.gd +++ b/core/ui/card_ui/gamepad/gamepad_settings.gd @@ -12,13 +12,13 @@ var notification_manager := load("res://core/global/notification_manager.tres") var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager var global_state_machine := load("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine var state_machine := load("res://assets/state/state_machines/gamepad_settings_state_machine.tres") as StateMachine -var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumber +var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance var input_icons := load("res://core/systems/input/input_icon_manager.tres") as InputIconManager var button_scene := load("res://core/ui/components/card_mapping_button.tscn") as PackedScene var container_scene := load("res://core/ui/components/card_mapping_button_group.tscn") as PackedScene var expandable_scene := load("res://core/ui/card_ui/quick_bar/qb_card.tscn") as PackedScene -var gamepad: InputPlumber.CompositeDevice +var gamepad: CompositeDevice var profile: InputPlumberProfile var profile_gamepad: String var library_item: LibraryItem @@ -63,8 +63,8 @@ func _ready() -> void: self.gamepad_type_selected = item_selected if self.profile: var gamepad_type := self.get_selected_target_gamepad() - profile_gamepad = InputPlumberProfile.get_target_device_string(gamepad_type) - logger.debug("Setting gamepad to " + profile_gamepad) + self.profile_gamepad = InputPlumberProfile.get_target_device_string(gamepad_type) + logger.debug("Setting gamepad to " + self.profile_gamepad) else: logger.debug("No profile, unable to set gamepad type.") self._update_mapping_elements() @@ -72,9 +72,9 @@ func _ready() -> void: # Load the default profile var profile_path = settings_manager.get_value("input", "gamepad_profile", "") - profile_gamepad = settings_manager.get_value("input", "gamepad_profile_target", "") - for gamepad in input_plumber.composite_devices: - _set_gamepad_profile(gamepad, profile_path) + self.profile_gamepad = settings_manager.get_value("input", "gamepad_profile_target", "") + for composite_device in input_plumber.get_composite_devices(): + _set_gamepad_profile(composite_device, profile_path) # Grab focus when the mapper exits var on_state_changed := func(_from: State, to: State): @@ -85,7 +85,7 @@ func _ready() -> void: # Ensure new devices are set to the correct profile when added input_plumber.composite_device_added.connect(_set_gamepad_profile) - input_plumber.composite_device_changed.connect(_set_gamepad_profile) + #input_plumber.composite_device_changed.connect(_set_gamepad_profile) #TODO: fixme? ## Called when the gamepad settings state is entered @@ -94,7 +94,7 @@ func _on_state_entered(_from: State) -> void: in_game_panel.visible = global_state_machine.has_state(in_game_state) # Ensure that InputPlumber is running - if not input_plumber.supports_input_plumber(): + if not input_plumber.is_running(): not_available.visible = true main_container.visible = false $ServiceNotAvailableContainer/Label.text = "InputPlumber service not available" @@ -103,7 +103,7 @@ func _on_state_entered(_from: State) -> void: main_container.visible = true # Read from the state to determine which gamepad is being configured - gamepad = null + self.gamepad = null if !gamepad_state.has_meta("dbus_path"): logger.error("No gamepad was set to configure!") # Make menu empty, unable to find gamepad to configure @@ -114,58 +114,50 @@ func _on_state_entered(_from: State) -> void: var dbus_path := gamepad_state.get_meta("dbus_path") as String # Find the composite device to configure - for device: InputPlumber.CompositeDevice in input_plumber.composite_devices: + for device: CompositeDevice in input_plumber.get_composite_devices(): if device.dbus_path == dbus_path: - gamepad = device + self.gamepad = device break - if gamepad == null: + + if self.gamepad == null: logger.error("Unable to find CompositeDevice with path: " + dbus_path) not_available.visible = true main_container.visible = false $ServiceNotAvailableContainer/Label.text = "No gamepad to configure" return - logger.debug("Configuring gamepad '" + gamepad.name + "': " + dbus_path) + logger.debug("Configuring gamepad '" + self.gamepad.name + "': " + dbus_path) # Set the gamepad name label - gamepad_label.text = gamepad.name + gamepad_label.text = self.gamepad.name # Populate the menu with the source inputs for the given gamepad - populate_mappings_for(gamepad) + populate_mappings_for(self.gamepad) # Set the library item, if one exists - library_item = null - profile = null + self.library_item = null + self.profile = null if gamepad_state.has_meta("item"): - library_item = gamepad_state.get_meta("item") as LibraryItem + self.library_item = gamepad_state.get_meta("item") as LibraryItem # If no library item was set, but there's a running app, try to see if # there is a library item for it instead. - if not library_item: - library_item = launch_manager.get_current_app_library_item() + if not self.library_item: + self.library_item = launch_manager.get_current_app_library_item() # If no library item was set with the state, then configure the OGUI profile - if not library_item: - profile_label.text = "Global" - @warning_ignore("confusable_local_declaration") - var profile_path := settings_manager.get_value("input", "gamepad_profile", InputPlumber.DEFAULT_GLOBAL_PROFILE) as String - var profile_target_gamepad := settings_manager.get_value("input", "gamepad_profile_target", "") as String - profile = _load_profile(profile_path) - profile_gamepad = profile_target_gamepad - _update_mapping_elements() - return - - # Set the profile text to the game name - profile_label.text = library_item.name - + var profile_path: String + if not self.library_item: + self.profile_label.text = "Global" + profile_path = settings_manager.get_value("input", "gamepad_profile", InputPlumber.DEFAULT_GLOBAL_PROFILE) as String + else: + self.profile_label.text = self.library_item.name + profile_path = settings_manager.get_library_value(self.library_item, "gamepad_profile", "") as String + self.profile_gamepad = settings_manager.get_library_value(self.library_item, "gamepad_profile_target", "") as String - # Check to see if the given game has a gamepad profile - var profile_path := settings_manager.get_library_value(library_item, "gamepad_profile", "") as String - var profile_target_gamepad := settings_manager.get_library_value(library_item, "gamepad_profile_target", "") as String - profile = _load_profile(profile_path) - profile_gamepad = profile_target_gamepad + self.profile = _load_profile(profile_path) _update_mapping_elements() - + # Clear focus mapping_focus_group.current_focus = null @@ -196,7 +188,7 @@ func _on_state_exited(_to: State) -> void: ## Populates the button mappings for the given gamepad -func populate_mappings_for(gamepad: InputPlumber.CompositeDevice) -> void: +func populate_mappings_for(gamepad: CompositeDevice) -> void: var gamepad_name := gamepad.name var capabilities := gamepad.capabilities @@ -402,8 +394,8 @@ func _update_mapping_elements() -> void: profile_label.text = profile.name # Update the dropdown based on the profile's target gamepad type - if not profile_gamepad.is_empty(): - var target_device := InputPlumberProfile.get_target_device(profile_gamepad) + if not self.profile_gamepad.is_empty(): + var target_device := InputPlumberProfile.get_target_device(self.profile_gamepad) var gamepad_text := self.get_target_gamepad_text(target_device) var i := 0 var idx := 0 @@ -595,7 +587,7 @@ func get_target_gamepad_text(gamepad_type: InputPlumberProfile.TargetDevice) -> # Set the given profile for the given composte device. -func _set_gamepad_profile(gamepad: InputPlumber.CompositeDevice, profile_path: String = "") -> void: +func _set_gamepad_profile(gamepad: CompositeDevice, profile_path: String = "") -> void: if profile_path == "": if gamepad_state.has_meta("item"): library_item = gamepad_state.get_meta("item") as LibraryItem @@ -612,16 +604,16 @@ func _set_gamepad_profile(gamepad: InputPlumber.CompositeDevice, profile_path: S profile_path = settings_manager.get_library_value(library_item, "gamepad_profile", "") logger.debug("Setting " + gamepad.name + " to profile: " + profile_path) - gamepad.target_modify_profile(profile_path, profile_gamepad) + InputPlumber.load_target_modified_profile(gamepad, profile_path, self.profile_gamepad) # Set the target gamepad if one was specified - if not profile_gamepad.is_empty(): - var target_devices := [profile_gamepad, "keyboard", "mouse"] - match profile_gamepad: + if not self.profile_gamepad.is_empty(): + var target_devices := PackedStringArray([self.profile_gamepad, "keyboard", "mouse"]) + match self.profile_gamepad: "xb360", "xbox-series", "xbox-elite", "gamepad": target_devices.append("touchpad") _: - logger.debug(profile_gamepad, "needs no additional target devices.") + logger.debug(self.profile_gamepad, "needs no additional target devices.") logger.debug("Setting target devices to: ", target_devices) gamepad.set_target_devices(target_devices) @@ -645,10 +637,11 @@ func _save_profile() -> void: return # Update the game settings to use this global profile + self.logger.info("Saving gamepad profile at", path, "and gamepad target", self.profile_gamepad) settings_manager.set_value("input", "gamepad_profile", path) - settings_manager.set_value("input", "gamepad_profile_target", profile_gamepad) + settings_manager.set_value("input", "gamepad_profile_target", self.profile_gamepad) - for gamepad in input_plumber.composite_devices: + for gamepad in input_plumber.get_composite_devices(): _set_gamepad_profile(gamepad, path) logger.debug("Saved global gamepad profile to: " + path) @@ -668,7 +661,7 @@ func _save_profile() -> void: # Update the game settings to use this gamepad profile var section := "game.{0}".format([library_item.name.to_lower()]) settings_manager.set_value(section, "gamepad_profile", path) - settings_manager.set_value(section, "gamepad_profile_target", profile_gamepad) + settings_manager.set_value(section, "gamepad_profile_target", self.profile_gamepad) logger.debug("Saved gamepad profile to: " + path) notify.text = "Gamepad profile saved" notification_manager.show(notify) diff --git a/core/ui/card_ui/home/cardui_home.gd b/core/ui/card_ui/home/cardui_home.gd index 41ba28098..1e4b668a4 100644 --- a/core/ui/card_ui/home/cardui_home.gd +++ b/core/ui/card_ui/home/cardui_home.gd @@ -201,6 +201,15 @@ func _on_card_focused(item: LibraryItem, card: Control) -> void: player.play("fade_in") banner.texture = await BoxArtManager.get_boxart_or_placeholder(item, BoxArtProvider.LAYOUT.BANNER) library_banner.visible = false + + # Don't scroll to the card if mouse or touch is being used + var input_manager := get_tree().get_first_node_in_group("InputManager") + if input_manager: + if (input_manager as InputManager).current_touches > 0: + return + if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): + return + _scroll_to(card) diff --git a/core/ui/card_ui/home/cardui_home.tscn b/core/ui/card_ui/home/cardui_home.tscn index eea547d86..7c2624ced 100644 --- a/core/ui/card_ui/home/cardui_home.tscn +++ b/core/ui/card_ui/home/cardui_home.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=18 format=3 uid="uid://o70x5igrlq30"] +[gd_scene load_steps=20 format=3 uid="uid://o70x5igrlq30"] [ext_resource type="Script" path="res://core/ui/card_ui/home/cardui_home.gd" id="1_a4a3j"] [ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_d1wwx"] @@ -9,6 +9,7 @@ [ext_resource type="Resource" uid="uid://e7bbebwf7guj" path="res://assets/state/states/main_menu.tres" id="5_gbh30"] [ext_resource type="Texture2D" uid="uid://d1mksukdkqorr" path="res://assets/images/placeholder-grid-banner.png" id="10_mmfgs"] [ext_resource type="PackedScene" uid="uid://rosd00fxjrs8" path="res://core/ui/components/library_banner.tscn" id="11_16gcd"] +[ext_resource type="Shader" path="res://assets/shaders/god_rays.gdshader" id="11_r5mwe"] [ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="12_h5dxg"] [ext_resource type="PackedScene" uid="uid://bkhrcemal7uxo" path="res://core/ui/components/card.tscn" id="12_m30ge"] [ext_resource type="PackedScene" uid="uid://crsu0vpicq0vh" path="res://core/ui/components/library_deck.tscn" id="13_rxwf5"] @@ -16,6 +17,22 @@ [ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="14_wdgux"] [ext_resource type="Resource" uid="uid://boq501bigx8kl" path="res://assets/state/states/library.tres" id="16_6odlo"] +[sub_resource type="ShaderMaterial" id="ShaderMaterial_bjg8e"] +shader = ExtResource("11_r5mwe") +shader_parameter/angle = 0.06 +shader_parameter/position = -0.1 +shader_parameter/spread = 0.674 +shader_parameter/cutoff = 0.1 +shader_parameter/falloff = 0.896 +shader_parameter/edge_fade = 0.15 +shader_parameter/speed = 6.995 +shader_parameter/ray1_density = 8.0 +shader_parameter/ray2_density = 30.0 +shader_parameter/ray2_intensity = 0.184 +shader_parameter/color = Color(0.888, 0.885, 1, 0.173) +shader_parameter/hdr = true +shader_parameter/seed = 1.505 + [sub_resource type="Animation" id="Animation_638a6"] resource_name = "fade_in" length = 0.5 @@ -88,6 +105,15 @@ unique_name_in_owner = true visible = false layout_mode = 1 +[node name="GodRaysShader" type="ColorRect" parent="VBoxContainer/BannerTexture"] +material = SubResource("ShaderMaterial_bjg8e") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + [node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"] layout_mode = 2 size_flags_vertical = 3 diff --git a/core/ui/card_ui/launch/game_launch_menu.tscn b/core/ui/card_ui/launch/game_launch_menu.tscn index 430a0b745..1f201075b 100644 --- a/core/ui/card_ui/launch/game_launch_menu.tscn +++ b/core/ui/card_ui/launch/game_launch_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=20 format=3 uid="uid://bcdk1lj6enq3l"] +[gd_scene load_steps=22 format=3 uid="uid://bcdk1lj6enq3l"] [ext_resource type="Script" path="res://core/ui/card_ui/launch/game_launch_menu.gd" id="1_u3ehs"] [ext_resource type="Texture2D" uid="uid://d1mksukdkqorr" path="res://assets/images/placeholder-grid-banner.png" id="2_oae7b"] @@ -9,6 +9,7 @@ [ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_4ja2v"] [ext_resource type="Texture2D" uid="uid://bec6fhiswf6ve" path="res://assets/ui/icons/gamepad-bold.svg" id="5_ewj68"] [ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="7_ch3qu"] +[ext_resource type="Shader" path="res://assets/shaders/god_rays.gdshader" id="10_l1dl8"] [ext_resource type="Texture2D" uid="uid://d2ipfga47yjju" path="res://assets/images/empty-grid-logo.png" id="12_01uv6"] [ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="14_3h1st"] [ext_resource type="PackedScene" uid="uid://cr83fmlociwko" path="res://core/ui/components/card_icon_button.tscn" id="15_f3ktw"] @@ -37,6 +38,22 @@ _data = { "fade_in": SubResource("Animation_ou6f5") } +[sub_resource type="ShaderMaterial" id="ShaderMaterial_f0qty"] +shader = ExtResource("10_l1dl8") +shader_parameter/angle = 0.06 +shader_parameter/position = -0.1 +shader_parameter/spread = 0.674 +shader_parameter/cutoff = 0.1 +shader_parameter/falloff = 0.896 +shader_parameter/edge_fade = 0.15 +shader_parameter/speed = 6.995 +shader_parameter/ray1_density = 8.0 +shader_parameter/ray2_density = 30.0 +shader_parameter/ray2_intensity = 0.184 +shader_parameter/color = Color(0.888, 0.885, 1, 0.173) +shader_parameter/hdr = true +shader_parameter/seed = 1.505 + [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7fb8y"] bg_color = Color(1, 0.47451, 0.776471, 1) corner_radius_top_left = 5 @@ -109,6 +126,15 @@ libraries = { "": SubResource("AnimationLibrary_yfglc") } +[node name="GodRaysShader" type="ColorRect" parent="VBoxContainer/BannerTexture"] +material = SubResource("ShaderMaterial_f0qty") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + [node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"] layout_mode = 2 size_flags_vertical = 3 diff --git a/core/ui/card_ui/library/library_menu.gd b/core/ui/card_ui/library/library_menu.gd index b032739c1..03b553425 100644 --- a/core/ui/card_ui/library/library_menu.gd +++ b/core/ui/card_ui/library/library_menu.gd @@ -244,8 +244,17 @@ func _on_uninstalled(req: InstallManager.Request) -> void: # Called when a library card is focused func _on_focus_updated(card: Control, tab: int) -> void: + # Update the currently selected card _current_selection[tab] = card + # Don't scroll to the card if mouse or touch is being used + var input_manager := get_tree().get_first_node_in_group("InputManager") + if input_manager: + if (input_manager as InputManager).current_touches > 0: + return + if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): + return + # Get the scroll container for this card var scroll_container := tab_container.get_child(tab) as ScrollContainer if not scroll_container: diff --git a/core/ui/card_ui/library/library_menu.tscn b/core/ui/card_ui/library/library_menu.tscn index e035c4bc8..f070b56cb 100644 --- a/core/ui/card_ui/library/library_menu.tscn +++ b/core/ui/card_ui/library/library_menu.tscn @@ -52,11 +52,13 @@ grow_horizontal = 2 grow_vertical = 2 size_flags_vertical = 3 theme_override_styles/panel = SubResource("StyleBoxFlat_xn2qp") +current_tab = 0 tabs_visible = false [node name="InstalledTab" type="ScrollContainer" parent="TabContainer"] layout_mode = 2 horizontal_scroll_mode = 0 +metadata/_tab_index = 0 [node name="ScrollerJoystick" parent="TabContainer/InstalledTab" instance=ExtResource("11_27umd")] @@ -113,6 +115,7 @@ layout_mode = 2 visible = false layout_mode = 2 horizontal_scroll_mode = 0 +metadata/_tab_index = 1 [node name="ScrollerJoystick" parent="TabContainer/AllGamesTab" instance=ExtResource("11_27umd")] diff --git a/core/ui/card_ui/navigation/context_bar_menu.gd b/core/ui/card_ui/navigation/context_bar_menu.gd index 91689c9dc..e072a0aa6 100644 --- a/core/ui/card_ui/navigation/context_bar_menu.gd +++ b/core/ui/card_ui/navigation/context_bar_menu.gd @@ -1,12 +1,8 @@ extends PanelContainer -const thread := preload("res://core/systems/threading/thread_pool.tres") - var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine -var battery_capacity := -1 var logger := Log.get_logger("ContextBar") -@onready var battery: String = Battery.find_battery_path() @onready var time_label: Label = $%TimeLabel diff --git a/core/ui/card_ui/navigation/in-game_notification.gd b/core/ui/card_ui/navigation/in-game_notification.gd index d53ab2a23..6b615fcd1 100644 --- a/core/ui/card_ui/navigation/in-game_notification.gd +++ b/core/ui/card_ui/navigation/in-game_notification.gd @@ -4,10 +4,10 @@ signal notification_received signal notification_finished var PID: int = OS.get_process_id() -var gamescope := load("res://core/global/gamescope.tres") as Gamescope +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager var default_icon := preload("res://icon.svg") -var overlay_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI) +var overlay_window_id: int @onready var panel := $%PanelContainer as PanelContainer @onready var texture := $%TextureRect as TextureRect @@ -18,6 +18,11 @@ var overlay_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI) # Called when the node enters the scene tree for the first time. func _ready() -> void: + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + if xwayland: + var overlay_window_ids := xwayland.get_windows_for_pid(PID) + if not overlay_window_ids.is_empty(): + overlay_window_id = overlay_window_ids[0] panel.visible = false # Subscribe to any notifications notification_manager.notification_queued.connect(_on_notification_queued) @@ -25,7 +30,9 @@ func _ready() -> void: # Continue showing any other queued messages var on_finished := func(): - gamescope.set_notification(overlay_window_id, 0) + var xwland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + if xwland: + xwland.set_notification(overlay_window_id, 0) _on_notification_queued(null) effect.slide_out_finished.connect(on_finished) @@ -38,7 +45,9 @@ func _on_notification_queued(_notify: Notification) -> void: var notify := notification_manager.next() if not notify: return - gamescope.set_notification(overlay_window_id, 1) + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + if xwayland: + xwayland.set_notification(overlay_window_id, 1) show_toast(notify.text, notify.icon, notify.timeout) diff --git a/core/ui/card_ui/navigation/running_game_card.gd b/core/ui/card_ui/navigation/running_game_card.gd index e5302252e..18860ae57 100644 --- a/core/ui/card_ui/navigation/running_game_card.gd +++ b/core/ui/card_ui/navigation/running_game_card.gd @@ -10,8 +10,7 @@ signal toggled_off signal finished_growing signal finished_shrinking -const Gamescope := preload("res://core/global/gamescope.tres") - +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager var boxart_manager := load("res://core/global/boxart_manager.tres") as BoxArtManager var library_manager := load("res://core/global/library_manager.tres") as LibraryManager @@ -20,6 +19,7 @@ var menu_state_machine := load("res://assets/state/state_machines/menu_state_mac var popup_state_machine := load("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine var in_game_state := load("res://assets/state/states/in_game.tres") as State var button_scene := load("res://core/ui/components/card_button.tscn") as PackedScene +var gamepad_state := load("res://assets/state/states/gamepad_settings.tres") as State @export_category("Card") @export var is_toggled := false: @@ -31,14 +31,15 @@ var button_scene := load("res://core/ui/components/card_button.tscn") as PackedS toggled_off.emit() toggled.emit(is_toggled) -@onready var content_container := $%ContentContainer -@onready var game_logo := $%GameLogo -@onready var game_label := $%GameLabel +@onready var content_container := $%ContentContainer as Container +@onready var game_logo := $%GameLogo as TextureRect +@onready var game_label := $%GameLabel as Label @onready var resume_button := $%ResumeButton as CardButton @onready var pause_button := $%PauseButton as CardButton @onready var exit_button := $%ExitButton as CardButton @onready var highlight_rect := $%HighlightTextureRect @onready var focus_group := $%FocusGroup as FocusGroup +@onready var gamepad_button := $%GamepadButton var tween: Tween var running_app: RunningApp @@ -55,6 +56,7 @@ func _ready() -> void: focus_exited.connect(_on_unfocus) button_up.connect(_on_button_up) theme_changed.connect(_on_theme_changed) + gamepad_button.button_down.connect(_on_gampad_button_pressed) # Find the parent theme and update if required var effective_theme := ThemeUtils.get_effective_theme(self) @@ -100,6 +102,9 @@ func set_running_app(app: RunningApp): return running_app = app var item := library_manager.get_app_by_name(app.launch_item.name) + if not item: + logger.warn("App", app.app_id, "has no library item.") + return game_logo.visible = false game_label.visible = false var logo := await boxart_manager.get_boxart(item, BoxArtProvider.LAYOUT.LOGO) as Texture2D @@ -112,7 +117,9 @@ func set_running_app(app: RunningApp): # Connect to app signals to allow switching between app windows var on_windows_changed := func(_from: PackedInt32Array, to: PackedInt32Array): - var focusable_windows := Gamescope.get_focusable_windows() + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) + var xwayland_game := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_GAME) + var focusable_windows := xwayland.get_focusable_windows() # Add a button to switch to a given window for window_id in to: # A button already exists for this window @@ -120,9 +127,9 @@ func set_running_app(app: RunningApp): continue if not window_id in focusable_windows: continue - var window_name := app.get_window_title(window_id) + var window_name := xwayland_game.get_window_name(window_id) if window_name == "": - continue + window_name = "Window (" + str(window_id) + ")" var button := button_scene.instantiate() as CardButton button.text = window_name button.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART @@ -207,3 +214,12 @@ func _input(event: InputEvent) -> void: #logger.debug("Consuming input event '{action}' for node {n}".format({"action": action, "n": str(self)})) get_viewport().set_input_as_handled() self.grab_focus() + + +## Ensure that the library item meta data is always set before opening the gamepad +## settings menu +func _on_gampad_button_pressed() -> void: + var library_item: LibraryItem = null + if running_app and running_app.launch_item: + library_item = LibraryItem.new_from_launch_item(running_app.launch_item) + gamepad_state.set_meta("item", library_item) diff --git a/core/ui/card_ui/navigation/running_game_card.tscn b/core/ui/card_ui/navigation/running_game_card.tscn index 279c8c8a2..e16a6254b 100644 --- a/core/ui/card_ui/navigation/running_game_card.tscn +++ b/core/ui/card_ui/navigation/running_game_card.tscn @@ -17,7 +17,7 @@ [ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="15_p4kr0"] [ext_resource type="Resource" uid="uid://cv3vduo0ojk1u" path="res://assets/state/states/menu.tres" id="16_vmedb"] -[sub_resource type="Image" id="Image_2yqt6"] +[sub_resource type="Image" id="Image_8fib7"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -27,7 +27,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_osglk"] -image = SubResource("Image_2yqt6") +image = SubResource("Image_8fib7") [node name="RunningGameCard" type="PanelContainer"] anchors_preset = 10 @@ -160,6 +160,7 @@ layout_mode = 2 text = "Pause" [node name="GamepadButton" parent="MarginContainer/VBoxContainer/ContentContainer" instance=ExtResource("8_ixs6g")] +unique_name_in_owner = true layout_mode = 2 text = "Gamepad" diff --git a/core/ui/card_ui/navigation/system_container.gd b/core/ui/card_ui/navigation/system_container.gd index 58e642688..b71b27710 100644 --- a/core/ui/card_ui/navigation/system_container.gd +++ b/core/ui/card_ui/navigation/system_container.gd @@ -1,29 +1,59 @@ extends HBoxContainer -const thread := preload("res://core/systems/threading/thread_pool.tres") +var network_manager := load("res://core/systems/network/network_manager.tres") as NetworkManagerInstance @onready var network_icon := $%NetworkIcon as TextureRect +var current_access_point: NetworkAccessPoint + # Called when the node enters the scene tree for the first time. func _ready() -> void: - thread.start() - - # Create a timer to check wifi signal strength and battery percent - var wifi_timer := Timer.new() - wifi_timer.timeout.connect(_on_wifi_update) - wifi_timer.wait_time = 60 - wifi_timer.autostart = true - add_child(wifi_timer) - _on_wifi_update() - - -# Updates the wifi signal strength on timer timeout -func _on_wifi_update() -> void: - var get_ap := func() -> NetworkManager.WifiAP: - return NetworkManager.get_current_access_point() - var current_ap := await thread.exec(get_ap) as NetworkManager.WifiAP - var strength := 0 - if current_ap: - strength = current_ap.strength + # Set the visibility of the network icon depending on if networking is available + network_manager.started.connect(_on_networking_available) + network_manager.stopped.connect(_on_networking_available) + _on_networking_available() + + # Listen for active connection changes to determine what icon to display + network_manager.primary_connection_changed.connect(_on_primary_change) + _on_primary_change(network_manager.primary_connection) + + +func _on_networking_available() -> void: + network_icon.visible = network_manager.is_running() + + +func _on_primary_change(conn: NetworkActiveConnection) -> void: + if current_access_point: + current_access_point.strength_changed.disconnect(_on_wifi_strength_changed) + current_access_point = null + if not conn: + # No active connection + network_icon.texture = NetworkManager.no_network + return + + var devices := conn.devices + if devices.is_empty(): + # No devices associated with connection + network_icon.texture = NetworkManager.no_network + return + + # Set the icon based on the type of device + var device := devices[0] + if not device.wireless: + network_icon.texture = NetworkManager.ethernet + return + + # Set the texture based on wifi state + current_access_point = device.wireless.active_access_point + if not current_access_point: + network_icon.texture = NetworkManager.bar_0 + return + network_icon.texture = NetworkManager.get_strength_texture(current_access_point.strength) + + # Update the wifi strength + current_access_point.strength_changed.connect(_on_wifi_strength_changed) + + +func _on_wifi_strength_changed(strength: int) -> void: network_icon.texture = NetworkManager.get_strength_texture(strength) diff --git a/core/ui/card_ui/ootbe/language_select.gd b/core/ui/card_ui/ootbe/language_select.gd index 259eb9fc5..fead50c3c 100644 --- a/core/ui/card_ui/ootbe/language_select.gd +++ b/core/ui/card_ui/ootbe/language_select.gd @@ -1,6 +1,9 @@ extends MarginContainer -var state_updater_scene := load("res://core/systems/state/state_updater.tscn") as PackedScene +var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager +var network_manager := load("res://core/systems/network/network_manager.tres") as NetworkManagerInstance +var state_machine := load("res://assets/state/state_machines/first_boot_state_machine.tres") as StateMachine +var no_networking_state := load("res://assets/state/states/first_boot_finished.tres") as State var button_scene := load("res://core/ui/components/card_button.tscn") as PackedScene @onready var button_container := %VBoxContainer @@ -14,19 +17,29 @@ func _ready() -> void: # Create a button for each locale for locale in TranslationServer.get_loaded_locales(): var language := locale.split("_")[0] - var language_name := TranslationServer.get_language_name(language) - - # Configure a state updater to change menu states when the button is pressed - var state_updater := state_updater_scene.instantiate() as StateUpdater - state_updater.state_machine = load("res://assets/state/state_machines/first_boot_state_machine.tres") - state_updater.state = load("res://assets/state/states/first_boot_network.tres") - state_updater.action = state_updater.ACTION.PUSH - state_updater.on_signal = "button_up" + var language_name := tr(TranslationServer.get_language_name(language)) # Create the button and set the language name var button := button_scene.instantiate() as CardButton + button.button_up.connect(_on_button_up.bind(locale)) button.text = language_name - button.add_child(state_updater) # Add the button to the scene button_container.add_child(button) + + +func _on_button_up(locale: String) -> void: + # Set and save locale + TranslationServer.set_locale(locale) + settings_manager.set_value("general", "locale", locale) + + # Determine the next state to go to + if not network_manager.is_running(): + state_machine.push_state(no_networking_state) + return + var next_state: State + if network_manager.state >= network_manager.NM_STATE_CONNECTED_GLOBAL: + next_state = load("res://assets/state/states/first_boot_plugin_select.tres") as State + else: + next_state = load("res://assets/state/states/first_boot_network.tres") as State + state_machine.push_state(next_state) diff --git a/core/ui/card_ui/ootbe/network_select.gd b/core/ui/card_ui/ootbe/network_select.gd index 8742261a3..c87c6bb88 100644 --- a/core/ui/card_ui/ootbe/network_select.gd +++ b/core/ui/card_ui/ootbe/network_select.gd @@ -1,14 +1,14 @@ extends MarginContainer +var network_manager := load("res://core/systems/network/network_manager.tres") as NetworkManagerInstance var state_machine := load("res://assets/state/state_machines/first_boot_state_machine.tres") as StateMachine -var next_state := load("res://assets/state/states/first_boot_plugin_select.tres") as State -var network_state := load("res://assets/state/states/first_boot_network.tres") as State - +var no_networking_state := load("res://assets/state/states/first_boot_finished.tres") as State var network_state_machine := load("res://assets/state/state_machines/first_boot_network_state_machine.tres") as StateMachine var password_popup_state := load("res://assets/state/states/first_boot_network_password.tres") as State var logger := Log.get_logger("NetworkSelect") +@onready var next_button := $%NextButton as CardButton @onready var wifi_tree := $%WifiNetworkTree as WifiNetworkTree @onready var wifi_pass_box := $%WifiPasswordTextInput @onready var wifi_pass_button := $%WifiPasswordButton @@ -16,17 +16,21 @@ var logger := Log.get_logger("NetworkSelect") # Called when the node enters the scene tree for the first time. func _ready() -> void: - network_state.state_entered.connect(_on_state_entered) + next_button.button_up.connect(_on_next_button) wifi_tree.challenge_required.connect(_on_challenge_required) -# When this menu is shown, check if networking is supported. If not, -# proceed to the next OOBE menu. -func _on_state_entered(_from: State) -> void: - if NetworkManager.supports_network(): +func _on_next_button() -> void: + # Determine the next state to go to + if not network_manager.is_running(): + state_machine.push_state(no_networking_state) return - logger.info("Networking is not supported. Skipping.") - state_machine.replace_state(next_state) + var next_state: State + if network_manager.state >= network_manager.NM_STATE_CONNECTED_GLOBAL: + next_state = load("res://assets/state/states/first_boot_plugin_select.tres") as State + else: + next_state = load("res://assets/state/states/first_boot_finished.tres") as State + state_machine.push_state(next_state) # If a wifi password is needed, this method will be called. The callback diff --git a/core/ui/card_ui/ootbe/network_select.tscn b/core/ui/card_ui/ootbe/network_select.tscn index 94ea7062c..9649e6e8b 100644 --- a/core/ui/card_ui/ootbe/network_select.tscn +++ b/core/ui/card_ui/ootbe/network_select.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=22 format=3 uid="uid://b64v61ut4e1qv"] +[gd_scene load_steps=21 format=3 uid="uid://b64v61ut4e1qv"] [ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="1_isj2i"] [ext_resource type="Script" path="res://core/ui/card_ui/ootbe/network_select.gd" id="1_lbpas"] @@ -16,7 +16,6 @@ [ext_resource type="Theme" uid="uid://bko0q7gp1hwjp" path="res://assets/themes/dracula.tres" id="10_oxokr"] [ext_resource type="PackedScene" uid="uid://btg3dylh0tqoi" path="res://core/systems/effects/smooth_scroll_effect.tscn" id="10_umeai"] [ext_resource type="PackedScene" uid="uid://d1rjdfxxrdccf" path="res://core/ui/components/text_input.tscn" id="11_hldpx"] -[ext_resource type="Resource" uid="uid://bpi21xakop5wy" path="res://assets/state/states/first_boot_plugin_select.tres" id="14_bvbb6"] [ext_resource type="Resource" uid="uid://b5e8kjinft7rs" path="res://assets/state/state_machines/first_boot_network_state_machine.tres" id="16_ckd3v"] [ext_resource type="Resource" uid="uid://bm3fu76wxxmnt" path="res://assets/state/states/first_boot_network_password.tres" id="17_mmuaj"] [ext_resource type="Script" path="res://core/systems/state/state_watcher.gd" id="17_umvp2"] @@ -60,11 +59,11 @@ fade_out_signal = "state_exited" on_signal = "state_entered" [node name="FocusGroupSetter" parent="StateWatcher/FadeEffect" node_paths=PackedStringArray("target") instance=ExtResource("4_y44we")] -target = NodePath("../../../HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/FocusGroup") +target = NodePath("../../../HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/NetworkSelectFocusGroup") on_signal = "effect_finished" [node name="FocusGroupSetter" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_y44we")] -target = NodePath("../../HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/FocusGroup") +target = NodePath("../../HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/NetworkSelectFocusGroup") on_signal = "state_refreshed" [node name="HBoxContainer" type="HBoxContainer" parent="."] @@ -103,7 +102,7 @@ theme_override_constants/margin_bottom = 80 layout_mode = 2 theme_override_constants/separation = 40 -[node name="FocusGroup" parent="HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("6_c8yx4")] +[node name="NetworkSelectFocusGroup" parent="HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("6_c8yx4")] current_focus = NodePath("../WifiNetworkTree") [node name="Spacer" type="Control" parent="HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer"] @@ -115,17 +114,13 @@ layout_mode = 2 size_flags_vertical = 3 [node name="NextButton" parent="HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" instance=ExtResource("3_nucyf")] +unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 8 size_flags_vertical = 8 text = "Next" click_focuses = false -[node name="StateUpdater" parent="HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/NextButton" instance=ExtResource("9_d1uu0")] -state_machine = ExtResource("2_0fb0p") -state = ExtResource("14_bvbb6") -on_signal = "button_up" - [node name="PopupContainer" type="CenterContainer" parent="."] unique_name_in_owner = true visible = false @@ -145,14 +140,20 @@ on_signal = "input_released" script = ExtResource("17_umvp2") state = ExtResource("17_mmuaj") -[node name="FocusOnPopupOpen" parent="PopupContainer/StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_y44we")] -target = NodePath("../../PanelContainer/MarginContainer/VBoxContainer/FocusGroup") -on_signal = "state_entered" - [node name="FocusOnPopupClose" parent="PopupContainer/StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_y44we")] -target = NodePath("../../../HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/FocusGroup") +target = NodePath("../../../HBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/NetworkSelectFocusGroup") on_signal = "state_exited" +[node name="FadeEffect" parent="PopupContainer/StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("7_2d2el")] +target = NodePath("../..") +on_signal = "state_entered" +fade_out_signal = "state_exited" +on_signal = "state_entered" + +[node name="FocusOnPopupOpen" parent="PopupContainer/StateWatcher/FadeEffect" node_paths=PackedStringArray("target") instance=ExtResource("4_y44we")] +target = NodePath("../../../PanelContainer/MarginContainer/VBoxContainer/PasswordFocusGroup") +on_signal = "effect_finished" + [node name="PanelContainer" type="PanelContainer" parent="PopupContainer"] layout_mode = 2 theme = ExtResource("10_oxokr") @@ -169,7 +170,7 @@ theme_override_constants/margin_bottom = 20 layout_mode = 2 theme_override_constants/separation = 10 -[node name="FocusGroup" parent="PopupContainer/PanelContainer/MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("6_c8yx4")] +[node name="PasswordFocusGroup" parent="PopupContainer/PanelContainer/MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("6_c8yx4")] current_focus = NodePath("../WifiPasswordTextInput") [node name="WifiPasswordTextInput" parent="PopupContainer/PanelContainer/MarginContainer/VBoxContainer" instance=ExtResource("11_hldpx")] diff --git a/core/ui/card_ui/ootbe/plugin_select.gd b/core/ui/card_ui/ootbe/plugin_select.gd new file mode 100644 index 000000000..89f019dfd --- /dev/null +++ b/core/ui/card_ui/ootbe/plugin_select.gd @@ -0,0 +1,28 @@ +extends MarginContainer + +var network_manager := load("res://core/systems/network/network_manager.tres") as NetworkManagerInstance +var state_machine := load("res://assets/state/state_machines/first_boot_state_machine.tres") as StateMachine +var next_state := load("res://assets/state/states/first_boot_plugin_select.tres") as State +var no_networking_state := load("res://assets/state/states/first_boot_finished.tres") as State +var plugin_store_state := load("res://assets/state/states/settings_plugin_store.tres") as State +var plugin_select_state := load("res://assets/state/states/first_boot_plugin_select.tres") as State + + +var logger := Log.get_logger("PluginSelect") + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + plugin_select_state.state_entered.connect(_on_state_entered) + + +# When this menu is shown, check if networking is supported. If not, +# proceed to the next OOBE menu. +func _on_state_entered(_from: State) -> void: + if not network_manager.is_running(): + logger.info("Networking is not supported. Skipping.") + state_machine.replace_state(no_networking_state) + return + if network_manager.state < network_manager.NM_STATE_CONNECTED_GLOBAL: + logger.info("Network is not available. Skipping.") + state_machine.replace_state(no_networking_state) + return diff --git a/core/ui/card_ui/ootbe/plugin_select.tscn b/core/ui/card_ui/ootbe/plugin_select.tscn index 904281072..145c634a7 100644 --- a/core/ui/card_ui/ootbe/plugin_select.tscn +++ b/core/ui/card_ui/ootbe/plugin_select.tscn @@ -1,13 +1,15 @@ -[gd_scene load_steps=12 format=3 uid="uid://cq5do4hbuevrd"] +[gd_scene load_steps=14 format=3 uid="uid://cq5do4hbuevrd"] -[ext_resource type="PackedScene" uid="uid://orey8uxm7v6v" path="res://core/systems/state/visibility_manager.tscn" id="1_glflu"] +[ext_resource type="Script" path="res://core/ui/card_ui/ootbe/plugin_select.gd" id="1_v2m4r"] [ext_resource type="Resource" uid="uid://bj8aa7rky3u36" path="res://assets/state/state_machines/first_boot_state_machine.tres" id="2_8r7jx"] +[ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_lfjkx"] [ext_resource type="Resource" uid="uid://bpi21xakop5wy" path="res://assets/state/states/first_boot_plugin_select.tres" id="3_r2fie"] [ext_resource type="PackedScene" uid="uid://uljtdvmuol3l" path="res://core/systems/input/focus_group_setter.tscn" id="4_byts3"] -[ext_resource type="PackedScene" uid="uid://bo077a5mwi7xl" path="res://core/ui/components/transition_fade_in.tscn" id="5_6lpmx"] +[ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_svc26"] [ext_resource type="PackedScene" uid="uid://cf5bdr4sh2irb" path="res://core/ui/card_ui/settings/plugin_store_menu.tscn" id="6_cox3h"] [ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="6_mn0iu"] [ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="7_4pfh5"] +[ext_resource type="PackedScene" uid="uid://bw8113ocotx2r" path="res://core/systems/effects/fade_effect.tscn" id="7_6yapm"] [ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="8_7mmqh"] [ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="9_di8uy"] [ext_resource type="Resource" uid="uid://j1csnsnb672v" path="res://assets/state/states/first_boot_plugin_setup.tres" id="11_83y3o"] @@ -20,16 +22,34 @@ grow_horizontal = 2 grow_vertical = 2 theme_override_constants/margin_left = 100 theme_override_constants/margin_right = 20 +script = ExtResource("1_v2m4r") -[node name="VisibilityManager" parent="." instance=ExtResource("1_glflu")] +[node name="InputWatcher" parent="." instance=ExtResource("2_lfjkx")] +stop_propagation = true +process_input_mode = 1 +action = "ogui_east" + +[node name="StateUpdater" parent="InputWatcher" instance=ExtResource("8_7mmqh")] state_machine = ExtResource("2_8r7jx") +action = 2 +on_signal = "input_released" + +[node name="StateWatcher" parent="." instance=ExtResource("5_svc26")] state = ExtResource("3_r2fie") -[node name="FocusGroupSetter" parent="VisibilityManager" node_paths=PackedStringArray("target") instance=ExtResource("4_byts3")] -target = NodePath("../../HBoxContainer/MarginContainer/VBoxContainer/PluginStoreMenu/MarginContainer/HFlowContainer/FocusGroup") -on_signal = "entered" +[node name="FadeEffect" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("7_6yapm")] +target = NodePath("../..") +on_signal = "state_entered" +fade_out_signal = "state_exited" +on_signal = "state_entered" -[node name="TransitionFadeIn" parent="VisibilityManager" instance=ExtResource("5_6lpmx")] +[node name="FocusGroupSetter" parent="StateWatcher/FadeEffect" node_paths=PackedStringArray("target") instance=ExtResource("4_byts3")] +target = NodePath("../../../HBoxContainer/MarginContainer/VBoxContainer/FocusGroup") +on_signal = "effect_finished" + +[node name="FocusGroupSetter" parent="StateWatcher" node_paths=PackedStringArray("target") instance=ExtResource("4_byts3")] +target = NodePath("../../HBoxContainer/MarginContainer/VBoxContainer/FocusGroup") +on_signal = "state_refreshed" [node name="HBoxContainer" type="HBoxContainer" parent="."] layout_mode = 2 @@ -69,14 +89,11 @@ layout_mode = 2 size_flags_vertical = 3 [node name="InputWatcher" parent="HBoxContainer/MarginContainer/VBoxContainer/PluginStoreMenu" index="0"] +stop_propagation = false process_input_mode = 1 -[node name="StateUpdater" parent="HBoxContainer/MarginContainer/VBoxContainer/PluginStoreMenu/InputWatcher" index="0"] -state_machine = ExtResource("2_8r7jx") - -[node name="FocusGroup" parent="HBoxContainer/MarginContainer/VBoxContainer/PluginStoreMenu/MarginContainer/HFlowContainer" index="0" node_paths=PackedStringArray("focus_neighbor_bottom", "focus_neighbor_top")] +[node name="FocusGroup" parent="HBoxContainer/MarginContainer/VBoxContainer/PluginStoreMenu/MarginContainer/HFlowContainer" index="0" node_paths=PackedStringArray("focus_neighbor_bottom")] focus_neighbor_bottom = NodePath("../../../../FocusGroup") -focus_neighbor_top = NodePath("../../../../FocusGroup") [node name="NextButton" parent="HBoxContainer/MarginContainer/VBoxContainer" instance=ExtResource("7_4pfh5")] layout_mode = 2 diff --git a/core/ui/card_ui/power/power_menu.gd b/core/ui/card_ui/power/power_menu.gd index 8e62e9542..7d51a6407 100644 --- a/core/ui/card_ui/power/power_menu.gd +++ b/core/ui/card_ui/power/power_menu.gd @@ -12,7 +12,6 @@ var logger := Log.get_logger("PowerMenu") @onready var shutdown_button := $%ShutdownButton @onready var exit_button := $%ExitButton @onready var cancel_button := $%CancelButton -@onready var blur := $BlurRect # Called when the node enters the scene tree for the first time. @@ -23,30 +22,11 @@ func _ready() -> void: reboot_button.button_down.connect(_on_systemctl_cmd.bind("reboot")) exit_button.button_down.connect(_on_exit) cancel_button.button_up.connect(_on_cancel) - - # Set the blur background shader parameters - blur.material.set_shader_parameter("blur_amount", 1.587) - blur.material.set_shader_parameter("mix_amount", 0.402) - blur.material.set_shader_parameter("color_over", Color(0, 0, 0, 1)) func _on_state_entered(_from: State) -> void: if focus_group: focus_group.grab_focus() - - # TODO: Fix this - # HACK to prevent giant pink texture from flashing for a second on first run - # in OpenGL renderer - if blur.has_meta("first_run"): - return - blur.set_meta("first_run", true) - - # Move the blur rect out of view for a few frames - blur.position = Vector2(get_tree().get_root().size) - - # Create a timer and wait for a bit before moving the rect back - await get_tree().create_timer(0.2).timeout - blur.position = Vector2.ZERO func _on_systemctl_cmd(command: String) -> void: diff --git a/core/ui/card_ui/power/power_menu.tscn b/core/ui/card_ui/power/power_menu.tscn index b67c488e2..a27c2e1b8 100644 --- a/core/ui/card_ui/power/power_menu.tscn +++ b/core/ui/card_ui/power/power_menu.tscn @@ -1,8 +1,8 @@ -[gd_scene load_steps=17 format=3 uid="uid://lfm3ps837gy5"] +[gd_scene load_steps=15 format=3 uid="uid://lfm3ps837gy5"] [ext_resource type="Script" path="res://core/ui/card_ui/power/power_menu.gd" id="2_0vc46"] +[ext_resource type="Shader" path="res://assets/shaders/simple_blur.gdshader" id="2_hkifr"] [ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="3_1whtm"] -[ext_resource type="Shader" path="res://assets/shaders/simple_blur.gdshader" id="3_oe8pc"] [ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="4_4rhfl"] [ext_resource type="Resource" uid="uid://cadriyl38ny5y" path="res://assets/state/state_machines/popup_state_machine.tres" id="5_kxixy"] [ext_resource type="Resource" uid="uid://bw0mtk7sso8m2" path="res://assets/state/states/power_menu.tres" id="5_u1rly"] @@ -13,17 +13,11 @@ [ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="10_y3iyf"] [ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="12_2pwjc"] -[sub_resource type="ShaderMaterial" id="ShaderMaterial_bxm1k"] -shader = ExtResource("3_oe8pc") -shader_parameter/blur_amount = 1.587 -shader_parameter/mix_amount = 0.402 -shader_parameter/color_over = null - -[sub_resource type="Gradient" id="Gradient_bc3c0"] -colors = PackedColorArray(0, 0, 0, 1, 0, 0, 0, 1) - -[sub_resource type="GradientTexture1D" id="GradientTexture1D_nu0jx"] -gradient = SubResource("Gradient_bc3c0") +[sub_resource type="ShaderMaterial" id="ShaderMaterial_v0ws8"] +shader = ExtResource("2_hkifr") +shader_parameter/blur_amount = 1.578 +shader_parameter/mix_amount = null +shader_parameter/color_over = Color(0, 0, 0, 0) [sub_resource type="Resource" id="Resource_fafkw"] script = ExtResource("10_6ywv6") @@ -38,16 +32,27 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("2_0vc46") -[node name="BlurRect" type="TextureRect" parent="."] -modulate = Color(1, 1, 1, 0.501961) -material = SubResource("ShaderMaterial_bxm1k") +[node name="BackBufferCopy" type="BackBufferCopy" parent="."] +copy_mode = 2 + +[node name="ColorRect" type="ColorRect" parent="."] +material = SubResource("ShaderMaterial_v0ws8") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) + +[node name="ColorRect2" type="ColorRect" parent="."] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -texture = SubResource("GradientTexture1D_nu0jx") +color = Color(0, 0, 0, 0.666667) [node name="InputWatcher" parent="." instance=ExtResource("3_1whtm")] stop_propagation = true diff --git a/core/ui/card_ui/quick_bar/performance_card.tscn b/core/ui/card_ui/quick_bar/performance_card.tscn index 2e3c31f40..10c2e8a44 100644 --- a/core/ui/card_ui/quick_bar/performance_card.tscn +++ b/core/ui/card_ui/quick_bar/performance_card.tscn @@ -1,28 +1,14 @@ -[gd_scene load_steps=5 format=3 uid="uid://dycb7m0oj13ly"] +[gd_scene load_steps=3 format=3 uid="uid://dycb7m0oj13ly"] [ext_resource type="PackedScene" uid="uid://b5xnora73yd8x" path="res://core/ui/card_ui/quick_bar/qb_card.tscn" id="1_77cql"] -[ext_resource type="PackedScene" uid="uid://b7piua3snox4i" path="res://core/ui/common/quick_bar/performance_menu.tscn" id="2_k3j2r"] - -[sub_resource type="Image" id="Image_asnwl"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_adlkc"] -image = SubResource("Image_asnwl") +[ext_resource type="PackedScene" uid="uid://dv3dt0j3jketh" path="res://core/ui/common/quick_bar/performance_menu.tscn" id="3_e67l3"] [node name="PerformanceCard" instance=ExtResource("1_77cql")] title = "Performance" -[node name="HighlightTexture" parent="." index="4"] -texture = SubResource("ImageTexture_adlkc") - [node name="SectionLabel" parent="MarginContainer/CardVBoxContainer" index="0"] text = "Performance" -[node name="PerformanceMenu" parent="MarginContainer/CardVBoxContainer/ContentContainer" index="0" instance=ExtResource("2_k3j2r")] +[node name="Performance" parent="MarginContainer/CardVBoxContainer/ContentContainer" index="0" instance=ExtResource("3_e67l3")] layout_mode = 2 +size_flags_vertical = 0 diff --git a/core/ui/card_ui/quick_bar/power_tools_card.tscn b/core/ui/card_ui/quick_bar/power_tools_card.tscn deleted file mode 100644 index 165465ad4..000000000 --- a/core/ui/card_ui/quick_bar/power_tools_card.tscn +++ /dev/null @@ -1,29 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://v751ima8r8vg"] - -[ext_resource type="PackedScene" uid="uid://b5xnora73yd8x" path="res://core/ui/card_ui/quick_bar/qb_card.tscn" id="1_6lv34"] -[ext_resource type="PackedScene" uid="uid://dv3dt0j3jketh" path="res://core/ui/common/quick_bar/powertools_menu.tscn" id="2_votl1"] - -[sub_resource type="Image" id="Image_n6cge"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_ca0vc"] -image = SubResource("Image_n6cge") - -[node name="PowerToolsCard" instance=ExtResource("1_6lv34")] -title = "Power Tools" - -[node name="HighlightTexture" parent="PanelContainer" index="0"] -texture = SubResource("ImageTexture_ca0vc") - -[node name="SectionLabel" parent="MarginContainer/CardVBoxContainer" index="0"] -text = "Power Tools" - -[node name="PowerTools" parent="MarginContainer/CardVBoxContainer/ContentContainer" index="0" instance=ExtResource("2_votl1")] -layout_mode = 2 -size_flags_vertical = 0 diff --git a/core/ui/card_ui/quick_bar/quick_bar_menu.gd b/core/ui/card_ui/quick_bar/quick_bar_menu.gd index 26d94012e..be7452d46 100644 --- a/core/ui/card_ui/quick_bar/quick_bar_menu.gd +++ b/core/ui/card_ui/quick_bar/quick_bar_menu.gd @@ -9,6 +9,7 @@ const qb_card_scene := preload("res://core/ui/card_ui/quick_bar/qb_card.tscn") var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager var quick_bar_menu_state := preload("res://assets/state/states/quick_bar_menu.tres") as State var qb_focus := preload("res://core/ui/card_ui/quick_bar/quick_bar_menu_focus.tres") as FocusStack +var gamepad_state := load("res://assets/state/states/gamepad_settings.tres") as State @onready var viewport: VBoxContainer = $%Viewport @onready var focus_group := $%FocusGroup as FocusGroup @@ -16,6 +17,7 @@ var qb_focus := preload("res://core/ui/card_ui/quick_bar/quick_bar_menu_focus.tr @onready var game_label := $%GameNameLabel @onready var notify_button := $%NotifyButton @onready var notify_card := $%NotificationsCard +@onready var gamepad_button := $%GamepadButton # Called when the node enters the scene tree for the first time. @@ -23,6 +25,9 @@ func _ready() -> void: quick_bar_menu_state.state_entered.connect(_on_state_entered) quick_bar_menu_state.state_exited.connect(_on_state_exited) launch_manager.app_switched.connect(_on_app_switched) + launch_manager.app_stopped.connect(_on_app_stopped) + launch_manager.app_launched.connect(_on_app_focus_changed) + gamepad_button.button_down.connect(_on_gampad_button_pressed) # Handle when the notifications button is pressed var on_notify_pressed := func(): @@ -45,6 +50,10 @@ func _on_state_exited(_to: State) -> void: func _on_app_switched(_from: RunningApp, to: RunningApp) -> void: + _on_app_focus_changed(to) + + +func _on_app_focus_changed(to: RunningApp) -> void: if to == null: playing_container.visible = false return @@ -54,6 +63,11 @@ func _on_app_switched(_from: RunningApp, to: RunningApp) -> void: # TODO: Implement fetching game icon and setting it +func _on_app_stopped(app: RunningApp) -> void: + if launch_manager.get_running().is_empty(): + playing_container.visible = false + + # Adds the given Control menu to the quick bar. A focus node can be given which will # be the first node to focus func add_child_menu(qb_item: Control, icon: Texture2D, focus_node: Control = null, title: String = ""): @@ -84,3 +98,10 @@ func add_child_menu(qb_item: Control, icon: Texture2D, focus_node: Control = nul focus_parent.add_child(focus_group) viewport.add_child(qb_card) + + +## Ensure that the library item meta data is always set before opening the gamepad +## settings menu +func _on_gampad_button_pressed() -> void: + var library_item := launch_manager.get_current_app_library_item() + gamepad_state.set_meta("item", library_item) diff --git a/core/ui/card_ui/quick_bar/quick_bar_menu.tscn b/core/ui/card_ui/quick_bar/quick_bar_menu.tscn index c89442234..4978e0a00 100644 --- a/core/ui/card_ui/quick_bar/quick_bar_menu.tscn +++ b/core/ui/card_ui/quick_bar/quick_bar_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=29 format=3 uid="uid://hroo3ll4inrb"] +[gd_scene load_steps=30 format=3 uid="uid://hroo3ll4inrb"] [ext_resource type="Script" path="res://core/ui/card_ui/quick_bar/quick_bar_menu.gd" id="1_56jo7"] [ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_6rvrx"] @@ -13,8 +13,10 @@ [ext_resource type="Texture2D" uid="uid://bjscvn2us6tal" path="res://assets/ui/icons/bell.svg" id="10_4yppf"] [ext_resource type="Texture2D" uid="uid://dq32os2qn6atc" path="res://assets/ui/icons/help.svg" id="11_a0ma3"] [ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="12_ldp5y"] +[ext_resource type="Texture2D" uid="uid://p3lsljjgjnv7" path="res://assets/ui/icons/keyboard-rounded.svg" id="13_625jj"] [ext_resource type="Resource" uid="uid://d3gp85f35oiw6" path="res://assets/state/states/settings.tres" id="14_didkb"] [ext_resource type="Resource" uid="uid://db5gbdl3xgwlq" path="res://assets/state/states/help_menu.tres" id="14_gr3i0"] +[ext_resource type="Script" path="res://core/systems/input/keyboard_opener.gd" id="14_vjul7"] [ext_resource type="Texture2D" uid="uid://c8pq5h4uim4pj" path="res://assets/ui/icons/game-controller.svg" id="15_0l0p5"] [ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="15_ip4q6"] [ext_resource type="Texture2D" uid="uid://djy4rejy21s6g" path="res://icon.svg" id="16_5eydp"] @@ -27,7 +29,6 @@ [ext_resource type="PackedScene" uid="uid://bjy50kdrebgre" path="res://core/ui/card_ui/quick_bar/notifications_card.tscn" id="19_pppbi"] [ext_resource type="PackedScene" uid="uid://dxaeufuk7ump2" path="res://core/ui/card_ui/quick_bar/quick_settings_card.tscn" id="20_17ks0"] [ext_resource type="PackedScene" uid="uid://dycb7m0oj13ly" path="res://core/ui/card_ui/quick_bar/performance_card.tscn" id="21_uw510"] -[ext_resource type="PackedScene" uid="uid://v751ima8r8vg" path="res://core/ui/card_ui/quick_bar/power_tools_card.tscn" id="22_dtanu"] [node name="QuickBarMenu" type="Control" groups=["quick-bar"]] z_index = 20 @@ -109,6 +110,16 @@ custom_minimum_size = Vector2(26, 26) layout_mode = 2 texture = ExtResource("10_4yppf") +[node name="KeyboardButton" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer" instance=ExtResource("9_6qs1m")] +unique_name_in_owner = true +custom_minimum_size = Vector2(26, 26) +layout_mode = 2 +texture = ExtResource("13_625jj") + +[node name="KeyboardOpener" type="Node" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/KeyboardButton"] +script = ExtResource("14_vjul7") +on_signal = "button_up" + [node name="HelpButton" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer" instance=ExtResource("9_6qs1m")] custom_minimum_size = Vector2(26, 26) layout_mode = 2 @@ -120,6 +131,7 @@ state = ExtResource("14_gr3i0") on_signal = "button_up" [node name="GamepadButton" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer" instance=ExtResource("9_6qs1m")] +unique_name_in_owner = true custom_minimum_size = Vector2(26, 26) layout_mode = 2 texture = ExtResource("15_0l0p5") @@ -223,6 +235,3 @@ layout_mode = 2 [node name="PerformanceCard" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/Viewport" instance=ExtResource("21_uw510")] layout_mode = 2 - -[node name="PowerToolsCard" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ScrollContainer/Viewport" instance=ExtResource("22_dtanu")] -layout_mode = 2 diff --git a/core/ui/card_ui/settings/bluetooth_settings_menu.gd b/core/ui/card_ui/settings/bluetooth_settings_menu.gd index 0355c31b4..d4a6d829c 100644 --- a/core/ui/card_ui/settings/bluetooth_settings_menu.gd +++ b/core/ui/card_ui/settings/bluetooth_settings_menu.gd @@ -1,12 +1,16 @@ extends Control -var thread_pool := load("res://core/systems/threading/thread_pool.tres") as ThreadPool -var bluetooth := load("res://core/systems/bluetooth/bluetooth_manager.tres") as BluetoothManager -var adapter := bluetooth.get_adapter() # TODO: allow choosing adapter +## Reference to the bluetooth service instance +var bluetooth := load("res://core/systems/bluetooth/bluetooth_manager.tres") as BluezInstance +## List of discovered bluetooth adapters +var adapters := bluetooth.get_adapters() # TODO: allow choosing adapter +## Currently used bluetooth adapter +var adapter: BluetoothAdapter +## Map of DBus path of bluetooth device to its tree item. +## E.g. {"/org/bluez/dev_XX_XX_XX_XX_XX": } var tree_items := {} var logger := Log.get_logger("BluetoothMenu") -@onready var timer := $%DiscoverTimer as Timer @onready var enabled_toggle := $%EnableToggle as Toggle @onready var discover_toggle := $%DiscoverToggle as Toggle @onready var tree := $%Tree as Tree @@ -15,18 +19,7 @@ var logger := Log.get_logger("BluetoothMenu") # Called when the node enters the scene tree for the first time. func _ready() -> void: - # Set the initial toggled states - enabled_toggle.button_pressed = adapter.powered - discover_toggle.button_pressed = adapter.discovering - - # Connect signals - visibility_changed.connect(_on_visibility_changed) - enabled_toggle.toggled.connect(_on_enable) - discover_toggle.toggled.connect(_on_discover) - timer.timeout.connect(_on_timer_timeout) - tree.item_activated.connect(_on_item_activated) - - # Configure the tree + # Configure the tree with a root node and column titles tree.create_item() tree.set_column_title_alignment(0, HORIZONTAL_ALIGNMENT_CENTER) tree.set_column_title(1, "Name") @@ -40,56 +33,115 @@ func _ready() -> void: # tree.set_column_title(5, "Signal Strength") # tree.set_column_title_alignment(5, HORIZONTAL_ALIGNMENT_CENTER) + # Configure menu when bluetooth starts up or stops + bluetooth.started.connect(_on_bluetooth_started) + bluetooth.stopped.connect(_on_bluetooth_stopped) + + # Configure the initial menu state + if _supports_bluetooth(): + _on_bluetooth_started() + else: + _on_bluetooth_stopped() + + # Connect signals + visibility_changed.connect(_on_visibility_changed) + enabled_toggle.toggled.connect(_on_enable) + discover_toggle.toggled.connect(_on_discover) + tree.item_activated.connect(_on_item_activated) + + +## Returns whether or not the bluez service is running and bluetooth adapters are found +func _supports_bluetooth() -> bool: + if not bluetooth.is_running(): + return false + if bluetooth.get_adapters().size() == 0: + return false + return true + + +## Invoked whenever the bluetooth service has started +func _on_bluetooth_started() -> void: + logger.info("Bluetooth started") + adapters = bluetooth.get_adapters() + if not adapters.is_empty(): + adapter = adapters[0] as BluetoothAdapter + + # Listen for devices being added/removed + bluetooth.device_added.connect(_on_device_added) + bluetooth.device_removed.connect(_on_device_removed) + + # Set the initial toggled states + if adapter: + enabled_toggle.button_pressed = adapter.powered + discover_toggle.button_pressed = adapter.discovering + else: + enabled_toggle.button_pressed = false + discover_toggle.button_pressed = false + + # Perform initial device discovery + var devices := bluetooth.get_discovered_devices() + for device in devices: + _on_device_added(device) + + _on_visibility_changed() + + +## Invoked whenever the bluetooth service has stopped +func _on_bluetooth_stopped() -> void: + logger.info("Bluetooth stopped") + adapters.clear() + adapter = null + var root := tree.get_root() + for dbus_path in tree_items.keys(): + var item := tree_items[dbus_path] as TreeItem + root.remove_child(item) + tree_items.clear() + + # Disconnect signals + if bluetooth.device_added.is_connected(_on_device_added): + bluetooth.device_added.disconnect(_on_device_added) + if bluetooth.device_removed.is_connected(_on_device_removed): + bluetooth.device_removed.disconnect(_on_device_removed) + + _on_visibility_changed() + ## Invoked when the menu becomes visible func _on_visibility_changed(): if not is_visible_in_tree(): # Disable discovery if not visible - if discover_toggle.button_pressed: + if adapter and discover_toggle.button_pressed: adapter.stop_discovery() return # Show/hide parts of the menu if bluetooth is available - var supports_bluetooth := bluetooth.supports_bluetooth() + var supports_bluetooth := _supports_bluetooth() container_unavail.visible = not supports_bluetooth container_avail.visible = supports_bluetooth if not supports_bluetooth: return - _on_timer_timeout() - enabled_toggle.button_pressed = adapter.powered - discover_toggle.button_pressed = adapter.discovering + if adapter: + enabled_toggle.button_pressed = adapter.powered + discover_toggle.button_pressed = adapter.discovering ## Invoked when the enabled toggle is toggled func _on_enable(toggled: bool) -> void: + if not adapter: + return adapter.powered = toggled ## Invoked when the discover toggle is toggled func _on_discover(toggled: bool) -> void: - if toggled: - timer.start() - thread_pool.exec(adapter.start_discovery) + if not adapter: + logger.warn("No bluetooth adapter found to start discovery") return - timer.stop() - thread_pool.exec(adapter.stop_discovery) - - -## Invoked when the user selects a bluetooth device from the tree view -func _on_item_activated() -> void: - var selected := tree.get_selected() - var device := selected.get_metadata(0) as BluetoothManager.Device - - # Disconnect if already connected - if device.connected: - selected.set_text(4, "Disconnecting") - thread_pool.exec(device.disconnect_from) + if toggled: + adapter.start_discovery() return - - # Try connecting to the device - selected.set_text(4, "Connecting") - thread_pool.exec(device.connect_to) + adapter.stop_discovery() ## Invoked when the discovery timer times out @@ -102,8 +154,6 @@ func _on_timer_timeout() -> void: for device in discovered: var device_name := device.name var address := device.address - var paired := device.paired - var connected := device.connected addresses.append(address) logger.debug("Discovered device: " + address) @@ -133,8 +183,44 @@ func _on_timer_timeout() -> void: tree_items.erase(address) +func _on_device_added(device: BluetoothDevice) -> void: + var device_name := device.name + var address := device.address + var dbus_path := device.dbus_path + logger.debug("Discovered device:", address) + + # Skip this device if it was already discovered + if dbus_path in tree_items: + return + + # Skip this device if it has no name + if device_name == "": + return + + # Create a tree item for this device + var root := tree.get_root() + var item := root.create_child() + item.set_metadata(0, device) + _on_device_updated(item) + device.updated.connect(_on_device_updated.bind(item)) + + # Add the item + tree_items[dbus_path] = item + + +## Invoked whenever a bluetooth device has been removed +func _on_device_removed(dbus_path: String) -> void: + if not dbus_path in tree_items: + return + var item := tree_items[dbus_path] as TreeItem + var root := tree.get_root() + root.remove_child(item) + tree_items.erase(dbus_path) + + +## Invoked whenever a bluetooth device has property changes func _on_device_updated(item: TreeItem) -> void: - var device := item.get_metadata(0) as BluetoothManager.Device + var device := item.get_metadata(0) as BluetoothDevice var device_name := device.name var address := device.address var paired := device.paired @@ -162,3 +248,19 @@ func _on_device_updated(item: TreeItem) -> void: item.set_icon(0, texture) item.set_icon_max_width(0, 24) item.set_text_alignment(0, HORIZONTAL_ALIGNMENT_CENTER) + + +## Invoked when the user selects a bluetooth device from the tree view +func _on_item_activated() -> void: + var selected := tree.get_selected() + var device := selected.get_metadata(0) as BluetoothDevice + + # Disconnect if already connected + if device.connected: + selected.set_text(4, "Disconnecting") + device.disconnect_from() + return + + # Try connecting to the device + selected.set_text(4, "Connecting") + device.connect_to() diff --git a/core/ui/card_ui/settings/bluetooth_settings_menu.tscn b/core/ui/card_ui/settings/bluetooth_settings_menu.tscn index 92d5e7c4c..a53eb216d 100644 --- a/core/ui/card_ui/settings/bluetooth_settings_menu.tscn +++ b/core/ui/card_ui/settings/bluetooth_settings_menu.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=10 format=3 uid="uid://6ygxdkvh1hib"] +[gd_scene load_steps=12 format=3 uid="uid://6ygxdkvh1hib"] [ext_resource type="Script" path="res://core/ui/card_ui/settings/bluetooth_settings_menu.gd" id="1_7sjpd"] [ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_36ef6"] [ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_p7et2"] +[ext_resource type="Script" path="res://core/systems/bluetooth/bluetooth_manager.gd" id="2_yoi1n"] [ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_2trn0"] +[ext_resource type="BluezInstance" uid="uid://peeajgfcx2fw" path="res://core/systems/bluetooth/bluetooth_manager.tres" id="3_aju5k"] [ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="4_isnhb"] [ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_l428g"] [ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_ivwga"] @@ -19,6 +21,10 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_7sjpd") +[node name="BluetoothManager" type="Node" parent="."] +script = ExtResource("2_yoi1n") +instance = ExtResource("3_aju5k") + [node name="InputWatcher" parent="." instance=ExtResource("2_p7et2")] stop_propagation = true action = "ogui_east" @@ -36,9 +42,6 @@ script = ExtResource("7_klxjh") target = NodePath("../../MarginContainer/ScrollContainer/AvailableContainer/VBoxContainer/FocusGroup") on_signal = "state_entered" -[node name="DiscoverTimer" type="Timer" parent="."] -unique_name_in_owner = true - [node name="MarginContainer" type="MarginContainer" parent="."] layout_mode = 1 anchors_preset = 15 diff --git a/core/ui/card_ui/settings/disks_settings_menu.gd b/core/ui/card_ui/settings/disks_settings_menu.gd index 3fe3439c2..a10e03005 100644 --- a/core/ui/card_ui/settings/disks_settings_menu.gd +++ b/core/ui/card_ui/settings/disks_settings_menu.gd @@ -1,6 +1,6 @@ extends ScrollContainer -var udisks2 := load("res://core/systems/disks/udisks2.tres") as UDisks2 +var udisks2 := load("res://core/systems/disks/disk_manager.tres") as UDisks2Instance var steam_disks := load("res://core/systems/disks/steam_removable_media_manager.tres") as SteamRemovableMediaManager const drive_card_scene: PackedScene = preload("res://core/ui/components/drive_card.tscn") @@ -11,10 +11,12 @@ var logger := Log.get_logger("DisksMenu", Log.LEVEL.INFO) @onready var focus_group: FocusGroup = $%FocusGroup @onready var no_drive_label: Label = $%NoDisksLabel + # Called when the node enters the scene tree for the first time. func _ready(): - if not udisks2.supports_disk_management(): + if not udisks2.is_running(): return + udisks2.unprotected_devices_updated.connect(_on_drives_updated) _on_drives_updated(udisks2.get_unprotected_devices()) @@ -49,7 +51,7 @@ func _on_drives_updated(devices: Array[BlockDevice]) -> void: continue # Create Drive Card - var drive_card := drive_card_scene.instantiate() + var drive_card := drive_card_scene.instantiate() as DriveCard container.add_child(drive_card) drive_card.setup(drive) drive_card.visible = true diff --git a/core/ui/card_ui/settings/disks_settings_menu.tscn b/core/ui/card_ui/settings/disks_settings_menu.tscn index babb7c5b8..36c6adcff 100644 --- a/core/ui/card_ui/settings/disks_settings_menu.tscn +++ b/core/ui/card_ui/settings/disks_settings_menu.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=11 format=3 uid="uid://clbapexwfoqtu"] +[gd_scene load_steps=13 format=3 uid="uid://clbapexwfoqtu"] [ext_resource type="Script" path="res://core/ui/card_ui/settings/disks_settings_menu.gd" id="1_ct0k3"] +[ext_resource type="Script" path="res://core/systems/disks/disk_manager.gd" id="2_2gbtg"] [ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_6shyw"] [ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_8gros"] [ext_resource type="PackedScene" uid="uid://b76dvfuouhlwd" path="res://core/systems/state/state_updater.tscn" id="3_lwwgd"] +[ext_resource type="UDisks2Instance" uid="uid://dhmtkhokrb8pu" path="res://core/systems/disks/disk_manager.tres" id="3_v1ku5"] [ext_resource type="PackedScene" uid="uid://caeaxm6st4a4u" path="res://core/ui/components/drive_card.tscn" id="4_brwek"] [ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_vkilo"] [ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_hkv7e"] @@ -19,6 +21,10 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_ct0k3") +[node name="DiskManager" type="Node" parent="."] +script = ExtResource("2_2gbtg") +instance = ExtResource("3_v1ku5") + [node name="InputWatcher" parent="." instance=ExtResource("2_6shyw")] stop_propagation = true action = "ogui_east" diff --git a/core/ui/card_ui/settings/general_settings_menu.gd b/core/ui/card_ui/settings/general_settings_menu.gd index a115f57e6..abb0773db 100644 --- a/core/ui/card_ui/settings/general_settings_menu.gd +++ b/core/ui/card_ui/settings/general_settings_menu.gd @@ -114,7 +114,7 @@ func _ready() -> void: var i := 0 for locale in TranslationServer.get_loaded_locales(): var language := locale.split("_")[0] - var language_name := TranslationServer.get_language_name(language) + var language_name := tr(TranslationServer.get_language_name(language)) lang_dropdown.add_item(language_name) lang_dropdown.option_button.set_item_metadata(i, locale) if locale == current_locale: @@ -123,6 +123,9 @@ func _ready() -> void: # Update the locale when it is changed var on_language_change := func(idx: int) -> void: + if idx < 0: + logger.warn("Invalid language index selected:", idx) + return var locale := lang_dropdown.option_button.get_item_metadata(idx) as String var language_name := lang_dropdown.option_button.get_item_text(idx) logger.info("Setting language to: " + locale) diff --git a/core/ui/card_ui/settings/general_settings_menu.tscn b/core/ui/card_ui/settings/general_settings_menu.tscn index 83869bf03..30a07eeef 100644 --- a/core/ui/card_ui/settings/general_settings_menu.tscn +++ b/core/ui/card_ui/settings/general_settings_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=23 format=3 uid="uid://dsgrw1grwef4m"] +[gd_scene load_steps=24 format=3 uid="uid://dsgrw1grwef4m"] [ext_resource type="Script" path="res://core/ui/card_ui/settings/general_settings_menu.gd" id="1_if7xt"] [ext_resource type="PackedScene" uid="uid://c6fg6uvng0ovi" path="res://core/systems/input/input_watcher.tscn" id="2_b5gbi"] @@ -19,6 +19,7 @@ [ext_resource type="PackedScene" uid="uid://cmjjyqx1pl23d" path="res://core/ui/components/text.tscn" id="9_sq2rc"] [ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="10_06mt3"] [ext_resource type="Theme" uid="uid://ehplgpp70vxa" path="res://assets/themes/card_ui-dracula.tres" id="10_b386h"] +[ext_resource type="PackedScene" uid="uid://bk5ld0h1jgd2t" path="res://core/ui/components/card_button_setting.tscn" id="12_3b8j5"] [ext_resource type="Theme" uid="uid://ddqvfbno2i1n4" path="res://assets/themes/card_ui-darksoul.tres" id="12_m1lrx"] [ext_resource type="Theme" uid="uid://de64j20kxm1k1" path="res://assets/themes/card_ui-water-vapor.tres" id="13_2j54j"] [ext_resource type="Theme" uid="uid://cw7auu2ayqnp8" path="res://assets/themes/card_ui-mountain.tres" id="14_wt0wj"] @@ -93,20 +94,19 @@ text = "Automatic Updates" separator_visible = false description = "Automatically download and apply updates in the background when they are available" -[node name="CheckUpdateButton" parent="MarginContainer/VBoxContainer/VBoxContainer" instance=ExtResource("7_qenel")] +[node name="CheckUpdateButton" parent="MarginContainer/VBoxContainer/VBoxContainer" instance=ExtResource("12_3b8j5")] unique_name_in_owner = true layout_mode = 2 -size_flags_horizontal = 4 -text = "Check for Updates" -uppercase = false +text = "Check for updates" +button_text = "Check for updates" +disabled = false -[node name="UpdateButton" parent="MarginContainer/VBoxContainer/VBoxContainer" instance=ExtResource("7_qenel")] +[node name="UpdateButton" parent="MarginContainer/VBoxContainer/VBoxContainer" instance=ExtResource("12_3b8j5")] unique_name_in_owner = true -custom_minimum_size = Vector2(158, 0) layout_mode = 2 -size_flags_horizontal = 4 -text = "Install Update" -uppercase = false +text = "Install Updates" +button_text = "Update" +disabled = false [node name="HSeparator2" type="HSeparator" parent="MarginContainer/VBoxContainer/VBoxContainer"] layout_mode = 2 diff --git a/core/ui/card_ui/settings/library_settings_menu.gd b/core/ui/card_ui/settings/library_settings_menu.gd index f33f84cd2..f51e2036e 100644 --- a/core/ui/card_ui/settings/library_settings_menu.gd +++ b/core/ui/card_ui/settings/library_settings_menu.gd @@ -2,7 +2,7 @@ extends ScrollContainer var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager var library_manager := load("res://core/global/library_manager.tres") as LibraryManager -var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine +var state_machine := load("res://assets/state/state_machines/menu_state_machine.tres") as StateMachine var settings_state := load("res://assets/state/states/settings.tres") as State var game_settings_state := preload("res://assets/state/states/game_settings.tres") as State var button_scene := load("res://core/ui/components/card_button.tscn") as PackedScene @@ -27,6 +27,8 @@ func _on_state_entered(_from: State) -> void: for child in container.get_children(): if not child is CardButton: continue + if child.name == "RefreshButton": + continue container.remove_child(child) child.queue_free() diff --git a/core/ui/card_ui/settings/library_settings_menu.tscn b/core/ui/card_ui/settings/library_settings_menu.tscn index 39d3547d2..abb33b7f5 100644 --- a/core/ui/card_ui/settings/library_settings_menu.tscn +++ b/core/ui/card_ui/settings/library_settings_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=3 uid="uid://drbp6ec8646v3"] +[gd_scene load_steps=13 format=3 uid="uid://drbp6ec8646v3"] [ext_resource type="Script" path="res://core/ui/card_ui/settings/library_settings_menu.gd" id="1_0w1vu"] [ext_resource type="PackedScene" uid="uid://cemxrvvjgm4g" path="res://core/ui/components/slider.tscn" id="1_obgkx"] @@ -10,6 +10,8 @@ [ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_cd7pj"] [ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="5_sh522"] [ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_5nxbl"] +[ext_resource type="PackedScene" uid="uid://bk5ld0h1jgd2t" path="res://core/ui/components/card_button_setting.tscn" id="9_3ae07"] +[ext_resource type="Script" path="res://core/systems/library/library_refresher.gd" id="10_xjg34"] [node name="LibrarySettingsMenu" type="ScrollContainer"] anchors_preset = 15 @@ -53,6 +55,21 @@ layout_mode = 2 [node name="FocusGroup" parent="MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("5_sh522")] current_focus = NodePath("../MaxRecentAppsSlider") +[node name="GeneralLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("3_x5lfl")] +layout_mode = 2 +text = "General" + +[node name="RefreshLibraryButton" parent="MarginContainer/VBoxContainer" instance=ExtResource("9_3ae07")] +layout_mode = 2 +text = "Refresh Library" +description = "Trigger reloading your library" +button_text = "Refresh" +disabled = false + +[node name="LibraryRefresher" type="Node" parent="MarginContainer/VBoxContainer/RefreshLibraryButton"] +script = ExtResource("10_xjg34") +on_signal = "button_up" + [node name="HomeLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("3_x5lfl")] layout_mode = 2 text = "Home" diff --git a/core/ui/card_ui/settings/settings_menu.tscn b/core/ui/card_ui/settings/settings_menu.tscn index b6a2ff313..c6302ced3 100644 --- a/core/ui/card_ui/settings/settings_menu.tscn +++ b/core/ui/card_ui/settings/settings_menu.tscn @@ -522,51 +522,64 @@ theme_override_constants/margin_bottom = 5 layout_mode = 2 size_flags_horizontal = 3 theme_override_styles/panel = SubResource("StyleBoxEmpty_a60ci") +current_tab = 0 tabs_visible = false [node name="GeneralSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("29_8hwjo")] layout_mode = 2 +metadata/_tab_index = 0 [node name="LibrarySettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("34_lrrl0")] visible = false layout_mode = 2 +metadata/_tab_index = 1 [node name="DisplaySettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("30_22tgj")] visible = false layout_mode = 2 +metadata/_tab_index = 2 [node name="NetworkSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("31_35x2h")] visible = false layout_mode = 2 +metadata/_tab_index = 3 [node name="BluetoothSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("33_a7xb2")] visible = false layout_mode = 2 +metadata/_tab_index = 4 [node name="AudioSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("32_dqi6v")] visible = false layout_mode = 2 +metadata/_tab_index = 5 [node name="DisksMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("40_lyyu6")] visible = false layout_mode = 2 +metadata/_tab_index = 6 [node name="PluginSettingsMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("35_dooq8")] visible = false layout_mode = 2 +metadata/_tab_index = 7 [node name="PluginStoreMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("35_0u4bv")] visible = false layout_mode = 2 +metadata/_tab_index = 8 [node name="ProcessesMenu" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("36_hnokx")] visible = false layout_mode = 2 +metadata/_tab_index = 9 [node name="LoggingSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("37_iwtxk")] visible = false layout_mode = 2 +metadata/_tab_index = 10 [node name="GeneralControllerSettings" parent="MarginContainer/HBoxContainer/ContentContainer/VBoxContainer/ContentContainer/MarginContainer/TabContainer" instance=ExtResource("40_uqw2r")] visible = false layout_mode = 2 +metadata/_tab_index = 11 diff --git a/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd b/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd index c83c7c437..8b9133881 100644 --- a/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd +++ b/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd @@ -1,10 +1,13 @@ extends Control -var platform := preload("res://core/global/platform.tres") as Platform -var gamescope := preload("res://core/global/gamescope.tres") as Gamescope -var launch_manager := preload("res://core/global/launch_manager.tres") as LaunchManager -var settings_manager := preload("res://core/global/settings_manager.tres") as SettingsManager -var input_plumber := preload("res://core/systems/input/input_plumber.tres") as InputPlumber +# Managers +var platform := load("res://core/global/platform.tres") as Platform +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance +var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager +var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager +var input_plumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance + +# State var state_machine := ( preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine ) @@ -12,36 +15,65 @@ var menu_state_machine := preload("res://assets/state/state_machines/menu_state_ var popup_state_machine := preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine var menu_state := preload("res://assets/state/states/menu.tres") as State var popup_state := preload("res://assets/state/states/popup.tres") as State -var quick_bar_state = preload("res://assets/state/states/quick_bar_menu.tres") as State -var settings_state = preload("res://assets/state/states/settings.tres") as State -var gamepad_state = preload("res://assets/state/states/gamepad_settings.tres") as State -var base_state = preload("res://assets/state/states/in_game.tres") as State +var quick_bar_state := preload("res://assets/state/states/quick_bar_menu.tres") as State +var settings_state := preload("res://assets/state/states/settings.tres") as State +var gamepad_state := preload("res://assets/state/states/gamepad_settings.tres") as State +var base_state := preload("res://assets/state/states/in_game.tres") as State +# Xwayland var managed_states: Array[State] = [quick_bar_state, settings_state, gamepad_state] +var xwayland_primary := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) +var xwayland_ogui := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) +var overlay_window_id := 0 +var set_steam_overlay_focus := false + +# Process var PID: int = OS.get_process_id() -var args := OS.get_cmdline_user_args() +var launch_args := OS.get_cmdline_user_args() var cmdargs := OS.get_cmdline_args() -var display := Gamescope.XWAYLAND.OGUI -var overlay_window_id := gamescope.get_window_id(PID, display) var underlay_log: FileAccess var underlay_process: int var underlay_window_id: int +# UI References @onready var quick_bar_menu := $%QuickBarMenu @onready var settings_menu := $%SettingsMenu +# Constants + +const remove_list: PackedStringArray = [ + "KeyboardButton", + "NotifyButton", + "HelpButton", + "VolumeSlider", + "BrightnessSlider", + "PerGameToggle", + "MangoAppSlider", + "FramerateLimitSlider", + "RefreshRateSlider" + ] + +const settings_remove_list: PackedStringArray = [ + "LibraryButton", + "NetworkButton", + "BluetoothButton", + "AudioButton" + ] + +# Logger var logger := Log.get_logger("Main", Log.LEVEL.INFO) ## Sets up overlay mode. func _init(): # Discover the OpenGamepadUI window ID + if xwayland_ogui: + var ogui_windows := xwayland_ogui.get_windows_for_pid(PID) + if not ogui_windows.is_empty(): + overlay_window_id = ogui_windows[0] if overlay_window_id <= 0: logger.error("Unable to detect Window ID. Overlay is not going to work!") logger.info("Found primary window id: {0}".format([overlay_window_id])) - # Back button wont close windows without this. OverlayInputManager prevents poping the last state. - state_machine.push_state(base_state) - # Ensure LaunchManager doesn't override our custom overlay management l launch_manager.should_manage_overlay = false @@ -58,9 +90,9 @@ func _init(): func _ready() -> void: # Workaround old versions that don't pass launch args via update pack # TODO: Parse the parent PID's CLI args and use those instead. - if "--skip-update-pack" in cmdargs and args.size() == 0: + if "--skip-update-pack" in cmdargs and launch_args.size() == 0: logger.warn("Launched via update pack without arguments! Falling back to default.") - args = ["steam", "-gamepadui", "-steamos3", "-steampal", "-steamdeck"] + launch_args = PackedStringArray(["steam", "-gamepadui", "-steamos3", "-steampal", "-steamdeck"]) # Configure the locale logger.debug("Setup Locale") @@ -83,35 +115,33 @@ func _ready() -> void: # Set up the session logger.debug("Setup Overlay Mode") - _setup_overlay_mode(args) + _setup_overlay_mode(launch_args) # Set the theme if one was set - logger.debug("Setup Theme") - var theme_path := settings_manager.get_value("general", "theme", "") as String - if theme_path == "": - logger.debug("No theme set. Using default theme.") - - var current_theme = get_theme() - if theme_path != "" && current_theme.resource_path != theme_path: - logger.debug("Setting theme to: " + theme_path) - var loaded_theme = load(theme_path) - if loaded_theme != null: - # TODO: This is a workaround, themes aren't properly set the first time. - call_deferred("set_theme", loaded_theme) - call_deferred("set_theme", current_theme) - call_deferred("set_theme", loaded_theme) - else: - logger.debug("Unable to load theme") + var theme_path := settings_manager.get_value("general", "theme", "res://assets/themes/card_ui-dracula.tres") as String + if theme_path.is_empty(): + logger.error("Failed to load theme from settings manager.") + return + logger.debug("Setting theme to: " + theme_path) + var loaded_theme = load(theme_path) + if loaded_theme != null: + @warning_ignore("unsafe_call_argument") + set_theme(loaded_theme) + else: + logger.debug("Unable to load theme") ## Finds needed PID's and global vars, Starts the user defined program as an ## underlay process. -func _setup_overlay_mode(args: Array) -> void: +func _setup_overlay_mode(args: PackedStringArray) -> void: # Always push the base state if we end up with an empty stack. var on_states_emptied := func(): state_machine.push_state.call_deferred(base_state) state_machine.emptied.connect(on_states_emptied) + # Back button wont close windows without this. OverlayInputManager prevents poping the last state. + state_machine.push_state(base_state) + # Whenever the menu state is refreshed, refresh the menu state machine to # re-grab focus. var on_menu_refreshed := func(): @@ -143,42 +173,43 @@ func _setup_overlay_mode(args: Array) -> void: base_state.state_exited.connect(_on_base_state_exited) # Don't crash if we're not launching another program. - if args == []: + if args.is_empty(): logger.warn("overlay mode started with no launcher arguments.") return if "steam" in args: + logger.info("Starting Steam with args:", args) _start_steam_process(args) else: + logger.info("Starting underlay process with args:", args) var log_path := OS.get_environment("HOME") + "/.underlay-stdout.log" _start_underlay_process(args, log_path) # Remove unneeded/conflicting elements from default menues - var remove_list: PackedStringArray = ["PerformanceCard", "NotifyButton", "HelpButton", "VolumeSlider", "BrightnessSlider", "PerGameToggle"] + _remove_children(remove_list, quick_bar_menu) - var settings_remove_list: PackedStringArray = ["LibraryButton", "NetworkButton", "BluetoothButton", "AudioButton"] _remove_children(settings_remove_list, settings_menu) # Setup inputplumber to receive guide presses. - input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.PASS) - input_plumber.set_intercept_activation(["Gamepad:Button:Guide", "Gamepad:Button:East"], "Gamepad:Button:QuickAccess2") + input_plumber.set_intercept_mode(InputPlumberInstance.INTERCEPT_MODE_PASS) + input_plumber.set_intercept_activation(PackedStringArray(["Gamepad:Button:Guide", "Gamepad:Button:East"]), "Gamepad:Button:QuickAccess2") # Sets the intercept mode and intercept activation keys to what overlay_mode expects. - var on_device_changed := func(device: InputPlumber.CompositeDevice): - var intercept_mode : InputPlumber.INTERCEPT_MODE = input_plumber.intercept_mode_current + var on_device_changed := func(device: CompositeDevice): + var intercept_mode := input_plumber.intercept_mode logger.debug("Setting intercept mode to: " + str(intercept_mode)) - input_plumber.set_intercept_mode_single(intercept_mode, device) - input_plumber.set_intercept_activation_single(["Gamepad:Button:Guide", "Gamepad:Button:East"], "Gamepad:Button:QuickAccess2", device) - input_plumber.composite_device_changed.connect(on_device_changed) + device.intercept_mode = intercept_mode + device.set_intercept_activation(PackedStringArray(["Gamepad:Button:Guide", "Gamepad:Button:East"]), "Gamepad:Button:QuickAccess2") + input_plumber.composite_device_added.connect(on_device_changed) # Removes specified child elements from the given Node. func _remove_children(remove_list: PackedStringArray, parent:Node) -> void: var child_count := parent.get_child_count() - var to_remove_list := [] + var to_remove_list: Array[Node] = [] for child_idx in child_count: - var child = parent.get_child(child_idx) + var child := parent.get_child(child_idx) logger.trace("Checking if " + child.name + " in remove list...") if child.name in remove_list: logger.trace(child.name + " queued for removal!") @@ -195,10 +226,11 @@ func _remove_children(remove_list: PackedStringArray, parent:Node) -> void: logger.trace("Removing " + child.name) child.queue_free() + ## Starts Steam as an underlay process -func _start_steam_process(args: Array) -> void: +func _start_steam_process(args: PackedStringArray) -> void: logger.debug("Starting steam: " + str(args)) - var underlay_log_path = OS.get_environment("HOME") + "/.steam-stdout.log" + var underlay_log_path := OS.get_environment("HOME") + "/.steam-stdout.log" _start_underlay_process(args, underlay_log_path) _find_underlay_window_id() @@ -213,35 +245,36 @@ func _start_steam_process(args: Array) -> void: ## Called to start the specified underlay process and redirects logging to a ## seperate log file. -func _start_underlay_process(args: Array, log_path: String) -> void: +func _start_underlay_process(args: PackedStringArray, _log_path: String) -> void: logger.debug("Starting underlay process: " + str(args)) - # Set up loggining in the new thread. - args.append("2>&1") - args.append(log_path) + ## TODO: Fix this so it works + ## Set up logging in the new thread. + #args.append("2>&1") + #args.append(log_path) # Setup logging - underlay_log = FileAccess.open(log_path, FileAccess.WRITE) - var error := underlay_log.get_open_error() - if error != OK: - logger.warn("Got error opening log file.") - else: - logger.info("Started logging underlay process at " + log_path) - var command: String = "bash" - underlay_process = Reaper.create_process(command, ["-c", " ".join(args)]) + #underlay_log = FileAccess.open(log_path, FileAccess.WRITE) + #var error := FileAccess.get_open_error() + #if error != OK: + #logger.warn("Got error opening log file.") + #else: + #logger.info("Started logging underlay process at " + log_path) + var command: String = args[0] + args.remove_at(0) + underlay_process = Reaper.create_process(command, args) ## Called to identify the xwayland window ID of the underlay process. func _find_underlay_window_id() -> void: # Find Steam in the display tree - var root_win_id := gamescope.get_root_window_id(display) - var all_windows := gamescope.get_all_windows(root_win_id, display) + var root_win_id := xwayland_ogui.root_window_id + var all_windows := xwayland_ogui.get_all_windows(root_win_id) for window in all_windows: if window == overlay_window_id: continue - if gamescope.has_xprop(window, "STEAM_NOTIFICATION", display): + if xwayland_ogui.has_notification(window): underlay_window_id = window logger.info("Found steam! " + str(underlay_window_id)) - gamescope.focused_app_updated.connect(_on_app_focus_changed) break # If we didn't find the window_id, set up a tiemr to loop back and try again. @@ -256,49 +289,45 @@ func _find_underlay_window_id() -> void: ## Called when the base state is entered. -func _on_base_state_entered(from: State) -> void: +func _on_base_state_entered(_from: State) -> void: + logger.debug("Setting Underlay proccess window as overlay") + # Manage input focus - input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.PASS) - if gamescope.set_input_focus(overlay_window_id, 0) != OK: + input_plumber.set_intercept_mode(InputPlumberInstance.INTERCEPT_MODE_PASS) + if xwayland_ogui.set_input_focus(overlay_window_id, 0) != OK: + logger.error("Unable to set STEAM_INPUT_FOCUS atom!") + if xwayland_ogui.set_input_focus(underlay_window_id, 1) != OK: logger.error("Unable to set STEAM_INPUT_FOCUS atom!") # Manage overlay - gamescope.set_overlay(overlay_window_id, 0, display) - gamescope.set_overlay(underlay_window_id, 1, display) + xwayland_ogui.set_overlay(overlay_window_id, 0) + if self.set_steam_overlay_focus: + xwayland_ogui.set_overlay(underlay_window_id, 1) ## Called when a the base state is exited. -func _on_base_state_exited(to: State) -> void: +func _on_base_state_exited(_to: State) -> void: + logger.debug("Setting OpenGamepadUI window as overlay") + # Manage input focus - input_plumber.set_intercept_mode(InputPlumber.INTERCEPT_MODE.ALL) - if gamescope.set_input_focus(overlay_window_id, 1) != OK: + input_plumber.set_intercept_mode(InputPlumberInstance.INTERCEPT_MODE_ALL) + if xwayland_ogui.set_input_focus(overlay_window_id, 1) != OK: + logger.error("Unable to set STEAM_INPUT_FOCUS atom!") + if xwayland_ogui.set_input_focus(underlay_window_id, 0) != OK: logger.error("Unable to set STEAM_INPUT_FOCUS atom!") # Manage overlay - gamescope.set_overlay(overlay_window_id, 1, display) - gamescope.set_overlay(underlay_window_id, 0, display) + self.set_steam_overlay_focus = xwayland_ogui.get_overlay(underlay_window_id) == 1 + xwayland_ogui.set_overlay(overlay_window_id, 1) + if self.set_steam_overlay_focus: + xwayland_ogui.set_overlay(underlay_window_id, 0) ## Verifies steam is still running by checking for the steam overlay, closes otherwise. func _check_exit() -> void: if Reaper.get_pid_state(underlay_process) in ["R (running)", "S (sleeping)"]: return - if gamescope.has_xprop(underlay_window_id, "STEAM_OVERLAY", display): + if xwayland_ogui.has_overlay(underlay_window_id): return logger.debug("Steam closed. Shutting down.") get_tree().quit() - - -func _on_app_focus_changed(_from: int, to: int) -> void: - # On focus to the steam overlay, ensure the default profile is used. - logger.warn("Changed window focus to app ID:", to) - if to in [Gamescope.OVERLAY_GAME_ID, 0]: - launch_manager.set_gamepad_profile("") - return - - # On focus back to the game, ensure the game profile is set - var _current_app := launch_manager.get_current_app() - if not _current_app: - logger.error("Unable to set gamepad profile. Current app is NULL and we aren't focused") - return - launch_manager.set_app_gamepad_profile(_current_app) diff --git a/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.tscn b/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.tscn index b09a8af15..2a4ae00b2 100644 --- a/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.tscn +++ b/core/ui/card_ui_overlay_mode/card_ui_overlay_mode.tscn @@ -1,13 +1,21 @@ -[gd_scene load_steps=9 format=3 uid="uid://b13lnfkjbafkj"] +[gd_scene load_steps=17 format=3 uid="uid://b13lnfkjbafkj"] [ext_resource type="Theme" uid="uid://ehplgpp70vxa" path="res://assets/themes/card_ui-dracula.tres" id="1_0qmlq"] [ext_resource type="Script" path="res://core/ui/card_ui_overlay_mode/card_ui_overlay_mode.gd" id="2_3ptao"] [ext_resource type="PackedScene" uid="uid://bxnb8t7i08vma" path="res://core/systems/input/overlay_mode_input_manager.tscn" id="3_klhmb"] -[ext_resource type="PackedScene" uid="uid://cbboox5bujlx1" path="res://core/systems/launcher/launch_manager.tscn" id="4_b1ok4"] +[ext_resource type="Script" path="res://core/systems/input/input_icon_processor.gd" id="4_6rltg"] +[ext_resource type="Script" path="res://core/systems/launcher/launcher.gd" id="4_o0rva"] [ext_resource type="PackedScene" uid="uid://d2jiecrd5sw4s" path="res://core/ui/card_ui/settings/settings_menu.tscn" id="5_4sdt1"] +[ext_resource type="Script" path="res://core/systems/gamescope/gamescope.gd" id="5_sgddx"] +[ext_resource type="GamescopeInstance" uid="uid://chd0nc6gbfnw0" path="res://core/systems/gamescope/gamescope.tres" id="6_jj3bv"] [ext_resource type="PackedScene" uid="uid://cwarv58ju0sow" path="res://core/ui/card_ui/gamepad/gamepad_settings.tscn" id="6_oddte"] [ext_resource type="PackedScene" uid="uid://hroo3ll4inrb" path="res://core/ui/card_ui/quick_bar/quick_bar_menu.tscn" id="7_e54f8"] +[ext_resource type="Script" path="res://core/systems/plugin/plugin_manager.gd" id="7_qqh0e"] +[ext_resource type="Script" path="res://core/systems/performance/power_station.gd" id="8_kxe4b"] [ext_resource type="PackedScene" uid="uid://eqqk1uve143x" path="res://core/ui/components/dialog.tscn" id="8_otm5f"] +[ext_resource type="ResourceRegistry" uid="uid://bsr58xihnpn1j" path="res://core/systems/resource/resource_registry.tres" id="9_4si0w"] +[ext_resource type="PowerStationInstance" uid="uid://c2mmrnh3rcs58" path="res://core/systems/performance/power_station.tres" id="9_nw6s5"] +[ext_resource type="Script" path="res://core/systems/network/network_manager.gd" id="10_atypa"] [node name="CardUIOverlayMode" type="Control" groups=["main"]] layout_mode = 3 @@ -19,9 +27,28 @@ grow_vertical = 2 theme = ExtResource("1_0qmlq") script = ExtResource("2_3ptao") -[node name="InputManager" parent="." instance=ExtResource("3_klhmb")] +[node name="InputIconProcessor" type="Node" parent="."] +script = ExtResource("4_6rltg") + +[node name="Gamescope" type="Node" parent="."] +script = ExtResource("5_sgddx") +instance = ExtResource("6_jj3bv") + +[node name="Launcher" type="Node" parent="."] +script = ExtResource("4_o0rva") + +[node name="PluginManager" type="Node" parent="."] +script = ExtResource("7_qqh0e") -[node name="LaunchManager" parent="." instance=ExtResource("4_b1ok4")] +[node name="PowerStation" type="Node" parent="."] +script = ExtResource("8_kxe4b") +instance = ExtResource("9_nw6s5") + +[node name="NetworkManager" type="Node" parent="."] +script = ExtResource("10_atypa") + +[node name="ResourceProcessor" type="ResourceProcessor" parent="."] +registry = ExtResource("9_4si0w") [node name="MenuContent" type="MarginContainer" parent="."] unique_name_in_owner = true @@ -67,3 +94,5 @@ anchor_right = 0.5 anchor_bottom = 0.5 grow_horizontal = 2 grow_vertical = 2 + +[node name="InputManager" parent="." instance=ExtResource("3_klhmb")] diff --git a/core/ui/common/debug/processes_menu.gd b/core/ui/common/debug/processes_menu.gd index 77a56ea99..7683cb8ab 100644 --- a/core/ui/common/debug/processes_menu.gd +++ b/core/ui/common/debug/processes_menu.gd @@ -1,19 +1,26 @@ extends Control -const Gamescope := preload("res://core/global/gamescope.tres") +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance -signal displays_updated +## Currently selected PID +var _selected_pid := -1 +## Map of gamescope displays to XWayland instance +## E.g. {":0": , ":1": } +var _xwaylands := {} -var selected_pid := -1 - -@onready var displays := Gamescope.discover_gamescope_displays() -@onready var refresh_timer := $RefreshTimer +@onready var refresh_timer := $RefreshTimer as Timer @onready var pid_inspector := $%PIDInspector as Tree @onready var kill_button := $%KillButton as Button # Called when the node enters the scene tree for the first time. func _ready() -> void: + # Discover Gamescope displays + var xwaylands := gamescope.get_xwaylands() + for xwayland in xwaylands: + var xwayland_name := xwayland.name + _xwaylands[xwayland_name] = xwayland + # Pause refresh when the inspector is not visible var on_visible_changed := func(): if visible: @@ -21,7 +28,6 @@ func _ready() -> void: return refresh_timer.stop() visibility_changed.connect(on_visible_changed) - refresh_timer.timeout.connect(_update_displays) refresh_timer.timeout.connect(_update_pid_tree) # Configure the PID inspector @@ -41,34 +47,26 @@ func _on_pid_selected() -> void: var selected := pid_inspector.get_selected() var metadata = selected.get_metadata(0) if not metadata: - selected_pid = -1 + _selected_pid = -1 kill_button.disabled = true return var pid := metadata as int - selected_pid = pid + _selected_pid = pid kill_button.disabled = false # Triggers when the kill button is pressed func _on_kill_pressed() -> void: - if selected_pid < 1: + if _selected_pid < 1: return - Reaper.reap(selected_pid) - OS.kill(selected_pid) + Reaper.reap(_selected_pid) + OS.kill(_selected_pid) kill_button.disabled = true -# Looks for gamescope displays at refresh interval -func _update_displays() -> void: - if len(displays) != 0: - return - displays = Gamescope.discover_gamescope_displays() - displays_updated.emit() - - # Update the PID tree component func _update_pid_tree() -> void: - if len(displays) == 0: + if _xwaylands.size() == 0: return # Get the PID tree component and get the root element @@ -76,10 +74,9 @@ func _update_pid_tree() -> void: var root := tree.get_root() # Loop through all displays and find any processes with windows - for display in displays: - + for xwayland_name: String in _xwaylands.keys(): # Create a display root element if it does not exist - var display_str := "Display %s" % display + var display_str := "Display %s" % xwayland_name var display_root: TreeItem for child in root.get_children(): var text := child.get_text(0) @@ -90,26 +87,30 @@ func _update_pid_tree() -> void: display_root.set_text(0, display_str) # Update the tree for the given display - _update_tree_for_display(display, tree, display_root) + _update_tree_for_display(xwayland_name, tree, display_root) # Updates the given tree for the given display func _update_tree_for_display(display_name: String, tree: Tree, root: TreeItem) -> void: - var display := Gamescope.get_display_type(display_name) + if not display_name in _xwaylands: + return + var xwayland := _xwaylands[display_name] as GamescopeXWayland # Get all windows for the given Gamescope display - var windows_root := Gamescope.get_root_window_id(display) - var windows_all := Gamescope.get_all_windows(windows_root, display) + var windows_root := xwayland.root_window_id + var windows_all := xwayland.get_all_windows(windows_root) var pids := {} for window in windows_all: - var pid := Gamescope.get_window_pid(window, display) - if not pid in pids: - pids[pid] = [] - pids[pid].append(window) + var window_pids := xwayland.get_windows_for_pid(window) + for pid in window_pids: + if not pid in pids: + pids[pid] = [] + @warning_ignore("unsafe_method_access") + pids[pid].append(window) # Create a list of tree nodes to add or remove - var needs_creation := [] - var needs_removal := [] + var needs_creation: Array[int] = [] + var needs_removal: Array[TreeItem] = [] # Get a list of all the current children in the tree so we can reconcile # it with what processes are currently running @@ -140,9 +141,8 @@ func _update_tree_for_display(display_name: String, tree: Tree, root: TreeItem) pid_child.set_metadata(0, pid) # Create a tree node for each window associated with the PID - for window in pids[pid]: + for window: int in pids[pid]: var window_child := tree.create_item(pid_child) - var window_name := Gamescope.get_window_name(window, display) + var window_name := xwayland.get_window_name(window) window_child.set_text(0, "Window {0} ({1})".format([window, window_name])) window_child.set_metadata(0, pid) - diff --git a/core/ui/common/game/game_loading.gd b/core/ui/common/game/game_loading.gd index 5a0e09791..0865c0369 100644 --- a/core/ui/common/game/game_loading.gd +++ b/core/ui/common/game/game_loading.gd @@ -1,17 +1,33 @@ extends Control var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager - +var state_machine := ( + preload("res://assets/state/state_machines/global_state_machine.tres") as StateMachine +) +var menu_state := preload("res://assets/state/states/menu.tres") as State +var popup_state := preload("res://assets/state/states/popup.tres") as State +var in_game_state := preload("res://assets/state/states/in_game.tres") as State +var game_launching := false # Called when the node enters the scene tree for the first time. func _ready() -> void: - var on_app_launched := func(app: RunningApp): - visible = true - app.window_id_changed.connect(_on_window_created, CONNECT_ONE_SHOT) - app.app_killed.connect(_on_window_created, CONNECT_ONE_SHOT) - launch_manager.app_launched.connect(on_app_launched) + launch_manager.app_launched.connect(_on_app_launched) + + var on_state_changed := func(_from: State, to: State): + if not game_launching: + visible = false + return + visible = to in [in_game_state, popup_state] + state_machine.state_changed.connect(on_state_changed) + + +func _on_app_launched(app: RunningApp): + game_launching = true + app.app_type_detected.connect(_on_window_created, CONNECT_ONE_SHOT) + app.app_killed.connect(_on_window_created, CONNECT_ONE_SHOT) func _on_window_created() -> void: + game_launching = false visible = false diff --git a/core/ui/common/game/game_loading.tscn b/core/ui/common/game/game_loading.tscn index 9016bc1d0..9eff4db55 100644 --- a/core/ui/common/game/game_loading.tscn +++ b/core/ui/common/game/game_loading.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=3 format=3] +[gd_scene load_steps=3 format=3 uid="uid://b1kist0rarpcy"] [ext_resource type="Script" path="res://core/ui/common/game/game_loading.gd" id="1_1ucmh"] [ext_resource type="PackedScene" uid="uid://2tdbi1v6qb6h" path="res://core/ui/components/loading02.tscn" id="1_jduoa"] diff --git a/core/ui/common/osk/on_screen_keyboard.gd b/core/ui/common/osk/on_screen_keyboard.gd index cfb549089..80ece6941 100644 --- a/core/ui/common/osk/on_screen_keyboard.gd +++ b/core/ui/common/osk/on_screen_keyboard.gd @@ -8,13 +8,16 @@ signal mode_shifted(mode: MODE_SHIFT) signal opened signal closed -const Gamescope := preload("res://core/global/gamescope.tres") const key_scene := preload("res://core/ui/components/button.tscn") +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance +var inputplumber := load("res://core/systems/input/input_plumber.tres") as InputPlumberInstance +var state_machine := load("res://assets/state/state_machines/global_state_machine.tres") as StateMachine ## State machine to use to switch menu states in response to input events. var popup_state_machine := ( preload("res://assets/state/state_machines/popup_state_machine.tres") as StateMachine ) +var in_game_state := load("res://assets/state/states/in_game.tres") as State var osk_state := preload("res://assets/state/states/osk.tres") as State var input_icons := load("res://core/systems/input/input_icon_manager.tres") as InputIconManager @@ -28,6 +31,7 @@ enum MODE_SHIFT { @export var layout: KeyboardLayout @export var instance: KeyboardInstance = preload("res://core/global/keyboard_instance.tres") var _mode_shift: MODE_SHIFT = MODE_SHIFT.OFF +var _last_input_focus: int var logger := Log.get_logger("OSK") @onready var rows_container := $%KeyboardRowsContainer @@ -193,12 +197,33 @@ func open() -> void: timer.timeout.connect(on_timeout, CONNECT_ONE_SHOT) timer.start() + # If the keyboard is configured to send input to the game, set gamescope accordinly + if instance.context.type == instance.context.TYPE.X11: + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + var pid := OS.get_process_id() + var ogui_windows := xwayland.get_windows_for_pid(pid) + if not ogui_windows.is_empty(): + var overlay_window_id := ogui_windows[0] + xwayland.set_input_focus(overlay_window_id, 2) + opened.emit() # Closes the OSK func close() -> void: popup_state_machine.remove_state(osk_state) + + # If the keyboard is configured to send input to the game, set gamescope accordinly + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + var pid := OS.get_process_id() + var ogui_windows := xwayland.get_windows_for_pid(pid) + if not ogui_windows.is_empty(): + var overlay_window_id := ogui_windows[0] + if state_machine.current_state() == in_game_state: + xwayland.set_input_focus(overlay_window_id, 0) + else: + xwayland.set_input_focus(overlay_window_id, 1) + closed.emit() @@ -279,8 +304,6 @@ func _on_key_pressed(key: KeyboardKeyConfig) -> void: # Handle sending key presses to an xwayland instance func _handle_x11(key: KeyboardKeyConfig) -> void: - var xwayland := Gamescope.get_xwayland(Gamescope.XWAYLAND.GAME) - # Check for shift or capslock inputs if key.input.keycode in [KEY_SHIFT, KEY_CAPSLOCK]: _handle_native_action(key) @@ -291,15 +314,32 @@ func _handle_x11(key: KeyboardKeyConfig) -> void: if _mode_shift > MODE_SHIFT.OFF and key.mode_shift_input: event = key.mode_shift_input + # Convert the keycode into an InputPlumber virtual keyboard event + var virtual_key := InputPlumberEvent.virtual_key_from_keycode(event.keycode) + + # Find an InputPlumber keyboard to send the event + var virtual_keyboard: KeyboardDevice + for device in inputplumber.get_composite_devices(): + var target_devices := device.get_target_devices() + for target_device in target_devices: + if target_device is KeyboardDevice: + virtual_keyboard = target_device + break + + # Check to see if a keyboard was found + if not virtual_keyboard: + logger.error("Unable to find virtual InputPlumber keyboard to send input") + return + # Send a shift keypress if mode shifted if _mode_shift > MODE_SHIFT.OFF: - xwayland.send_key(KEY_SHIFT, true) + virtual_keyboard.send_key("KEY_LEFTSHIFT", true) - xwayland.send_key(event.keycode, true) - xwayland.send_key(event.keycode, false) + virtual_keyboard.send_key(virtual_key, true) + virtual_keyboard.send_key(virtual_key, false) if _mode_shift > MODE_SHIFT.OFF: - xwayland.send_key(KEY_SHIFT, false) + virtual_keyboard.send_key("KEY_LEFTSHIFT", false) # Reset the modeshift if this was a one shot if _mode_shift == MODE_SHIFT.ONE_SHOT: diff --git a/core/ui/common/quick_bar/performance_menu.gd b/core/ui/common/quick_bar/performance_menu.gd index f05304784..211b324ed 100644 --- a/core/ui/common/quick_bar/performance_menu.gd +++ b/core/ui/common/quick_bar/performance_menu.gd @@ -1,13 +1,305 @@ -extends Control +extends VBoxContainer -var logger := Log.get_logger("PerformanceMenu", Log.LEVEL.INFO) +var _hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager +var _platform := load("res://core/global/platform.tres") as Platform +var _performance_manager := load("res://core/systems/performance/performance_manager.tres") as PerformanceManager +var _power_station := load("res://core/systems/performance/power_station.tres") as PowerStationInstance +var _profiles_available: PackedStringArray -@onready var mangoapp_slider := $%MangoAppSlider +@onready var focus_group := $%FocusGroup as FocusGroup +@onready var cpu_boost_button := $CPUBoostButton as Toggle +@onready var cpu_cores_slider := $CPUCoresSlider as ValueSlider +@onready var gpu_freq_enable := $GPUFreqButton as Toggle +@onready var gpu_freq_max_slider := $GPUFreqMaxSlider as ValueSlider +@onready var gpu_freq_min_slider := $GPUFreqMinSlider as ValueSlider +@onready var gpu_temp_slider := $GPUTempSlider as ValueSlider +@onready var power_profile_dropdown := $PowerProfileDropdown as Dropdown +@onready var tdp_boost_slider := $TDPBoostSlider as ValueSlider +@onready var tdp_slider := $TDPSlider as ValueSlider +@onready var smt_button := $SMTButton as Toggle +@onready var cpu_label := $CPUSectionLabel as Control +@onready var gpu_label := $GPUSectionLabel as Control +@onready var wait_label := $WaitLabel as Control +@onready var service_timer := $ServiceTimer as Timer +@onready var apply_timer := $ApplyTimer as Timer +@onready var mangoapp_slider := $%MangoAppSlider as ValueSlider +@onready var mode_toggle := $%ModeToggle as Toggle + +var _power_station_running := false +var _profile_loading := false +var _current_profile: PerformanceProfile +var logger := Log.get_logger("Performance", Log.LEVEL.INFO) # Called when the node enters the scene tree for the first time. +# Finds default values and current settings of the hardware. func _ready() -> void: + # Setup dropdowns + var i := 0 + _get_available_profiles() + power_profile_dropdown.clear() + for profile in _profiles_available: + power_profile_dropdown.add_item(profile, i) + i += 1 + + # Configure the interface + _on_profile_loaded(_performance_manager.current_profile) + + _performance_manager.profile_loaded.connect(_on_profile_loaded) mangoapp_slider.value_changed.connect(_on_mangoapp_changed) + mode_toggle.toggled.connect(_on_mode_toggled) + + service_timer.timeout.connect(_on_service_timer_timeout) + apply_timer.timeout.connect(_on_apply_timer_timeout) + + # Re-start the apply timer when changes happen + var on_changed := func() -> void: + if _profile_loading: + return + apply_timer.start() + cpu_boost_button.pressed.connect(on_changed) + smt_button.pressed.connect(on_changed) + gpu_freq_enable.pressed.connect(on_changed) + mode_toggle.pressed.connect(on_changed) + + # Set the total number of available cores if the SMT button is pressed + var on_smt_pressed := func() -> void: + if not _hardware_manager.cpu: + return + var cpu := _hardware_manager.cpu + if smt_button.button_pressed: + cpu_cores_slider.max_value = cpu.core_count + else: + var cores := cpu.core_count / 2 + if cpu_cores_slider.value > cores: + cpu_cores_slider.value = cores + cpu_cores_slider.max_value = cores + smt_button.pressed.connect(on_smt_pressed) + + # Restart the timer when any slider changes happen + var on_slider_changed := func(_value) -> void: + if _profile_loading: + return + apply_timer.start() + cpu_cores_slider.value_changed.connect(on_slider_changed) + tdp_slider.value_changed.connect(on_slider_changed) + tdp_boost_slider.value_changed.connect(on_slider_changed) + gpu_freq_min_slider.value_changed.connect(on_slider_changed) + gpu_freq_max_slider.value_changed.connect(on_slider_changed) + gpu_temp_slider.value_changed.connect(on_slider_changed) + + # Configure GPU frequency timers so the minimum value can never go higher + # than the maximum value slider and the maximum value can never go lower + # than the minimum value slider. + var on_gpu_freq_changed := func(_value: float, kind: String) -> void: + if kind == "min" and gpu_freq_min_slider.value > gpu_freq_max_slider.value: + gpu_freq_max_slider.value = gpu_freq_min_slider.value + return + if kind == "max" and gpu_freq_max_slider.value < gpu_freq_min_slider.value: + gpu_freq_min_slider.value = gpu_freq_max_slider.value + return + gpu_freq_min_slider.value_changed.connect(on_gpu_freq_changed.bind("min")) + gpu_freq_max_slider.value_changed.connect(on_gpu_freq_changed.bind("max")) + + # Also restart the apply timer when dropdown changes happen + var on_dropdown_changed := func(index) -> void: + if _profile_loading: + return + + var new_profile: PerformanceProfile + if _profiles_available[index] == "power-saving": + new_profile = _create_powersaving_profile() + elif _profiles_available[index] == "max-performance": + new_profile = _create_performance_profile(false) + + if new_profile: + _on_profile_loaded(new_profile) + _performance_manager.apply_and_save_profile(_current_profile) + else: + apply_timer.start() + power_profile_dropdown.item_selected.connect(on_dropdown_changed) + + # Toggle visibility when the GPU freq manual toggle is on + var on_manual_freq := func() -> void: + # Immediately apply manual GPU frequency so we can read the min/max + # values for the sliders + var card := _get_integrated_card() + if not card: + logger.warn("No integrated GPU to set manual frequency on!") + return + card.manual_clock = gpu_freq_enable.button_pressed + + # Update the slider values with the current values + gpu_freq_min_slider.visible = gpu_freq_enable.button_pressed + gpu_freq_min_slider.min_value = round(card.clock_limit_mhz_min) + gpu_freq_min_slider.max_value = round(card.clock_limit_mhz_max) + gpu_freq_min_slider.value = round(card.clock_value_mhz_min) + gpu_freq_max_slider.visible = gpu_freq_enable.button_pressed + gpu_freq_max_slider.min_value = round(card.clock_limit_mhz_min) + gpu_freq_max_slider.max_value = round(card.clock_limit_mhz_max) + gpu_freq_max_slider.value = round(card.clock_value_mhz_max) + gpu_freq_enable.pressed.connect(on_manual_freq) + + +# Triggers when the apply timer times out. The apply timer will start/restart +# whenever the user makes a change to any item. When the timer runs out, it will +# call this to apply the current profile. +func _on_apply_timer_timeout() -> void: + if not _current_profile: + logger.debug("No loaded profile to apply") + return + logger.debug("Applying and saving profile") + + # Update the profile based on the currently set values + var power_profile := _profiles_available[power_profile_dropdown.selected] + _current_profile.gpu_power_profile = power_profile + _current_profile.cpu_boost_enabled = cpu_boost_button.button_pressed + _current_profile.cpu_smt_enabled = smt_button.button_pressed + _current_profile.cpu_core_count_current = int(cpu_cores_slider.value) + _current_profile.tdp_current = tdp_slider.value + _current_profile.tdp_boost_current = tdp_boost_slider.value + _current_profile.gpu_manual_enabled = gpu_freq_enable.button_pressed + _current_profile.gpu_freq_min_current = gpu_freq_min_slider.value + _current_profile.gpu_freq_max_current = gpu_freq_max_slider.value + _current_profile.gpu_temp_current = gpu_temp_slider.value + _current_profile.advanced_mode = mode_toggle.button_pressed + + _performance_manager.apply_and_save_profile(_current_profile) + + +# Triggers every timeout to monitor the PowerStation DBus +func _on_service_timer_timeout() -> void: + var bus_running := _power_station.is_running() + if bus_running == _power_station_running: + return + + # If the state of powerstation changes, update the interface accordingly + _power_station_running = bus_running + _setup_interface() + + +## Called when a performance profile is loaded +func _on_profile_loaded(profile: PerformanceProfile) -> void: + if not _power_station.is_running(): + logger.info("Unable to load performance profile. PowerStation not detected.") + return + var core_count := 1 + if _hardware_manager.cpu: + core_count = _hardware_manager.cpu.core_count + + logger.debug("Updating UI with loaded performance profile") + # Keep track of the currently loaded profile + _current_profile = profile + + # Update UI components based on the loaded profile + _profile_loading = true + var idx := _profiles_available.find(profile.gpu_power_profile) + if idx > -1: + power_profile_dropdown.select(idx) + _setup_interface() + + cpu_boost_button.button_pressed = profile.cpu_boost_enabled + smt_button.button_pressed = profile.cpu_smt_enabled + if smt_button.button_pressed: + cpu_cores_slider.max_value = core_count + else: + var cores := core_count / 2 + if cpu_cores_slider.value > cores: + cpu_cores_slider.value = cores + cpu_cores_slider.max_value = cores + cpu_cores_slider.value = round(profile.cpu_core_count_current) + + # Update GPU UI components + tdp_slider.value = round(profile.tdp_current) + tdp_boost_slider.value = round(profile.tdp_boost_current) + gpu_freq_enable.button_pressed = profile.gpu_manual_enabled + gpu_freq_enable.pressed.emit() + gpu_freq_min_slider.value = round(profile.gpu_freq_min_current) + gpu_freq_max_slider.value = round(profile.gpu_freq_max_current) + gpu_temp_slider.value = round(profile.gpu_temp_current) + + mode_toggle.button_pressed = profile.advanced_mode + + _profile_loading = false + + +# Configure the min/max values and visibility based on detected performance +# features. +func _setup_interface() -> void: + # If powerstation is not running, hide everything + if not _power_station.is_running(): + wait_label.visible = true + for node in get_children(): + if node == wait_label: + continue + if node == Control: + (node as Control).visible = false + return + + # Configure visibility for all components + wait_label.visible = false + + var is_advanced := false + if _current_profile: + is_advanced = _current_profile.advanced_mode + if mode_toggle.button_pressed != is_advanced: + mode_toggle.button_pressed = is_advanced + focus_group.current_focus = mode_toggle + + # Configure CPU components + if _power_station.cpu: + var cpu := _power_station.cpu + cpu_label.visible = is_advanced + cpu_boost_button.visible = cpu.has_feature("cpb") and is_advanced + smt_button.visible = cpu.has_feature("ht") and is_advanced + if cpu.smt_enabled: + cpu_cores_slider.max_value = cpu.cores_count + else: + cpu_cores_slider.max_value = cpu.cores_count / 2 + cpu_cores_slider.visible = is_advanced + + # Configure GPU components + if not _power_station.gpu: + return + var card := _get_integrated_card() + + # Configure based on integrated graphics card + if not card: + return + + gpu_label.visible = is_advanced + + tdp_slider.visible = is_advanced + tdp_slider.min_value = round(_hardware_manager.gpu.tdp_min) + tdp_slider.max_value = round(_hardware_manager.gpu.tdp_max) + + tdp_boost_slider.visible = is_advanced + tdp_boost_slider.max_value = round(_hardware_manager.gpu.max_boost) + + gpu_freq_enable.visible = is_advanced + + power_profile_dropdown.visible = not is_advanced + + gpu_freq_min_slider.visible = card.manual_clock and is_advanced + gpu_freq_min_slider.min_value = round(card.clock_limit_mhz_min) + gpu_freq_min_slider.max_value = round(card.clock_limit_mhz_max) + + gpu_freq_max_slider.visible = card.manual_clock and is_advanced + gpu_freq_max_slider.min_value = round(card.clock_limit_mhz_min) + gpu_freq_max_slider.max_value = round(card.clock_limit_mhz_max) + + gpu_temp_slider.visible = is_advanced + + +## Returns the primary integrated GPU instance +func _get_integrated_card() -> GpuCard: + var card: GpuCard + var cards := _power_station.gpu.get_cards() + for c in cards: + if c.class != "integrated": + continue + card = c + return card # Set the mangoapp config on slider change @@ -27,3 +319,87 @@ func _on_mangoapp_changed(value: float) -> void: if value >= 4: MangoApp.set_config(MangoApp.CONFIG_INSANE) return + + +func _create_performance_profile(is_advanced: bool) -> PerformanceProfile: + var new_profile := PerformanceProfile.new() + + # CPU Settings + new_profile.cpu_boost_enabled = true + new_profile.cpu_core_count_current = cpu_cores_slider.max_value + new_profile.cpu_smt_enabled = true + + # GPU Settings + var profiles := _performance_manager.get_power_profiles_available() as PackedStringArray + if profiles.is_empty(): + logger.error("No _platform profiles available. Unable to assume sane performance defaults.") + return null + if "custom" in profiles: + profiles.remove_at(profiles.find("custom")) + if not "max-performance" in profiles and not "performance" in profiles: + logger.error("Performance profile not found. Unable to assume sane performance defaults.") + return null + var profile_idx := profiles.find("max-performance") + if profile_idx == -1: + profile_idx = profiles.find("performance") + if profile_idx == -1: + logger.error("Performance profile not found. Unable to assume sane performance defaults.") + return null + new_profile.gpu_power_profile = profiles[profile_idx] + new_profile.gpu_freq_max_current = gpu_freq_max_slider.max_value + new_profile.gpu_freq_min_current = gpu_freq_min_slider.min_value + new_profile.gpu_manual_enabled = false + new_profile.gpu_temp_current = 95 + new_profile.tdp_boost_current = tdp_boost_slider.max_value + new_profile.tdp_current = tdp_slider.max_value + new_profile.advanced_mode = is_advanced + return new_profile + + +func _create_powersaving_profile() -> PerformanceProfile: + var new_profile := PerformanceProfile.new() + + # CPU Settings + new_profile.cpu_boost_enabled = true + new_profile.cpu_core_count_current = cpu_cores_slider.max_value + new_profile.cpu_smt_enabled = true + + # GPU Settings + _get_available_profiles() + + if _profiles_available.is_empty(): + logger.error("No _platform profiles available. Unable to assume sane performance defaults.") + return null + if not "power-saving" in _profiles_available: + logger.error("Power Saving profile not found. Unable to assume sane performance defaults.") + return null + var profile_idx := _profiles_available.find("power-saving") + if profile_idx == -1: + logger.error("Performance profile not found. Unable to assume sane performance defaults.") + return null + + new_profile.gpu_power_profile = _profiles_available[profile_idx] + new_profile.gpu_freq_max_current = gpu_freq_max_slider.max_value + new_profile.gpu_freq_min_current = gpu_freq_min_slider.min_value + new_profile.gpu_manual_enabled = false + new_profile.gpu_temp_current = 95 + var tdp_boost_mid = ((tdp_boost_slider.max_value - tdp_boost_slider.min_value) / 2) + tdp_boost_slider.min_value + new_profile.tdp_boost_current = tdp_boost_mid + var tdp_mid = ((tdp_slider.max_value - tdp_slider.min_value) / 2) + tdp_slider.min_value + new_profile.tdp_current = tdp_mid + new_profile.advanced_mode = false + return new_profile + + +# Get +func _get_available_profiles() -> void: + _profiles_available = _performance_manager.get_power_profiles_available() + if "custom" in _profiles_available: + var idx = _profiles_available.find("custom") + _profiles_available.remove_at(idx) + + +# Adjust the available options based on the mode toggle +func _on_mode_toggled(pressed: bool) -> void: + var new_profile := _create_performance_profile(pressed) + _on_profile_loaded(new_profile) diff --git a/core/ui/common/quick_bar/performance_menu.tscn b/core/ui/common/quick_bar/performance_menu.tscn index cfa5e66da..dfb503614 100644 --- a/core/ui/common/quick_bar/performance_menu.tscn +++ b/core/ui/common/quick_bar/performance_menu.tscn @@ -1,46 +1,184 @@ -[gd_scene load_steps=5 format=3 uid="uid://b7piua3snox4i"] +[gd_scene load_steps=9 format=3 uid="uid://dv3dt0j3jketh"] [ext_resource type="Script" path="res://core/ui/common/quick_bar/performance_menu.gd" id="1_r31yj"] [ext_resource type="PackedScene" uid="uid://cemxrvvjgm4g" path="res://core/ui/components/slider.tscn" id="1_yptsc"] [ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_my16i"] [ext_resource type="Resource" uid="uid://dpc1o781f43ef" path="res://core/ui/card_ui/quick_bar/quick_bar_menu_focus.tres" id="3_hsr7n"] +[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="5_hil7p"] +[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="6_ycrnf"] +[ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="7_j02ci"] +[ext_resource type="PackedScene" uid="uid://xei5afwefxud" path="res://core/ui/components/dropdown.tscn" id="8_j2331"] -[node name="PerformanceMenu" type="Control"] -layout_mode = 3 -anchors_preset = 15 +[node name="Performance" type="VBoxContainer"] +anchors_preset = 10 anchor_right = 1.0 -anchor_bottom = 1.0 +offset_bottom = 28.0 grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 script = ExtResource("1_r31yj") -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="FocusGroup" parent="VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("2_my16i")] -current_focus = NodePath("../MangoAppSlider") +[node name="FocusGroup" parent="." instance=ExtResource("2_my16i")] +unique_name_in_owner = true focus_stack = ExtResource("3_hsr7n") -[node name="MangoAppSlider" parent="VBoxContainer" instance=ExtResource("1_yptsc")] +[node name="ApplyTimer" type="Timer" parent="."] +wait_time = 1.5 +one_shot = true + +[node name="ServiceTimer" type="Timer" parent="."] +wait_time = 5.0 +autostart = true + +[node name="MangoAppSlider" parent="." instance=ExtResource("1_yptsc")] unique_name_in_owner = true layout_mode = 2 text = "Performance Overlay" max_value = 4.0 tick_count = 5 -[node name="FramerateLimitSlider" parent="VBoxContainer" instance=ExtResource("1_yptsc")] +[node name="FramerateLimitSlider" parent="." instance=ExtResource("1_yptsc")] visible = false layout_mode = 2 text = "FPS Limit" -[node name="RefreshRateSlider" parent="VBoxContainer" instance=ExtResource("1_yptsc")] +[node name="RefreshRateSlider" parent="." instance=ExtResource("1_yptsc")] visible = false layout_mode = 2 text = "Refresh Rate" + +[node name="ModeToggle" parent="." instance=ExtResource("7_j02ci")] +unique_name_in_owner = true +layout_mode = 2 +text = "Advanced Mode" +button_pressed = true + +[node name="WaitLabel" parent="." instance=ExtResource("5_hil7p")] +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +text = "Waiting for PowerStation service..." +horizontal_alignment = 1 +vertical_alignment = 1 +autowrap_mode = 3 + +[node name="CPUSectionLabel" parent="." instance=ExtResource("6_ycrnf")] +visible = false +layout_mode = 2 +text = "CPU Settings" + +[node name="CPUBoostButton" parent="." instance=ExtResource("7_j02ci")] +visible = false +layout_mode = 2 +focus_neighbor_left = NodePath("../GPUTempSlider") +focus_neighbor_top = NodePath("../GPUTempSlider") +focus_neighbor_right = NodePath("../SMTButton") +focus_neighbor_bottom = NodePath("../SMTButton") +focus_next = NodePath("../SMTButton") +focus_previous = NodePath("../GPUTempSlider") +text = "CPU Boost" +separator_visible = false + +[node name="SMTButton" parent="." instance=ExtResource("7_j02ci")] +visible = false +layout_mode = 2 +focus_neighbor_left = NodePath("../CPUBoostButton") +focus_neighbor_top = NodePath("../CPUBoostButton") +focus_neighbor_right = NodePath("../CPUCoresSlider") +focus_neighbor_bottom = NodePath("../CPUCoresSlider") +focus_next = NodePath("../CPUCoresSlider") +focus_previous = NodePath("../CPUBoostButton") +text = "SMT Enabled" +separator_visible = false + +[node name="CPUCoresSlider" parent="." instance=ExtResource("1_yptsc")] +visible = false +layout_mode = 2 +focus_neighbor_left = NodePath("../SMTButton") +focus_neighbor_top = NodePath("../SMTButton") +focus_neighbor_right = NodePath("../TDPSlider") +focus_neighbor_bottom = NodePath("../TDPSlider") +focus_next = NodePath("../TDPSlider") +focus_previous = NodePath("../SMTButton") +text = "CPU Cores" +value = 1.0 +max_value = 1.0 +min_value = 1.0 + +[node name="GPUSectionLabel" parent="." instance=ExtResource("6_ycrnf")] +visible = false +layout_mode = 2 +text = "GPU Settings" + +[node name="TDPSlider" parent="." instance=ExtResource("1_yptsc")] +visible = false +layout_mode = 2 +focus_neighbor_left = NodePath("../CPUCoresSlider") +focus_neighbor_top = NodePath("../CPUCoresSlider") +focus_neighbor_right = NodePath("../TDPBoostSlider") +focus_neighbor_bottom = NodePath("../TDPBoostSlider") +focus_next = NodePath("../TDPBoostSlider") +focus_previous = NodePath("../CPUCoresSlider") +text = "TDP" +max_value = 0.0 + +[node name="TDPBoostSlider" parent="." instance=ExtResource("1_yptsc")] +visible = false +layout_mode = 2 +focus_neighbor_left = NodePath("../TDPSlider") +focus_neighbor_top = NodePath("../TDPSlider") +focus_previous = NodePath("../TDPSlider") +text = "TDP Boost" +max_value = 0.0 + +[node name="GPUFreqButton" parent="." instance=ExtResource("7_j02ci")] +visible = false +layout_mode = 2 +focus_neighbor_left = NodePath("../TDPBoostSlider") +focus_neighbor_top = NodePath("../TDPBoostSlider") +focus_neighbor_right = NodePath("../GPUFreqMinSlider") +focus_neighbor_bottom = NodePath("../GPUFreqMinSlider") +focus_next = NodePath("../GPUFreqMinSlider") +focus_previous = NodePath("../TDPBoostSlider") +text = "Manual Freq" +separator_visible = false + +[node name="GPUFreqMinSlider" parent="." instance=ExtResource("1_yptsc")] +visible = false +layout_mode = 2 +focus_neighbor_right = NodePath("../GPUFreqMaxSlider") +focus_neighbor_bottom = NodePath("../GPUFreqMaxSlider") +focus_next = NodePath("../GPUFreqMaxSlider") +text = "Min Freq" +max_value = 0.0 +step = 100.0 + +[node name="GPUFreqMaxSlider" parent="." instance=ExtResource("1_yptsc")] +visible = false +layout_mode = 2 +focus_neighbor_left = NodePath("../GPUFreqMinSlider") +focus_neighbor_top = NodePath("../GPUFreqMinSlider") +focus_neighbor_right = NodePath("../GPUTempSlider") +focus_neighbor_bottom = NodePath("../GPUTempSlider") +focus_next = NodePath("../GPUTempSlider") +focus_previous = NodePath("../GPUFreqMinSlider") +text = "Max Freq" +max_value = 0.0 +step = 100.0 + +[node name="GPUTempSlider" parent="." instance=ExtResource("1_yptsc")] +visible = false +layout_mode = 2 +focus_neighbor_left = NodePath("../GPUFreqMaxSlider") +focus_neighbor_top = NodePath("../GPUFreqMaxSlider") +focus_neighbor_right = NodePath("../CPUBoostButton") +focus_neighbor_bottom = NodePath("../CPUBoostButton") +focus_next = NodePath("../CPUBoostButton") +focus_previous = NodePath("../GPUFreqMaxSlider") +text = "GPU Temp Limit" +value = 70.0 +max_value = 105.0 +min_value = 70.0 + +[node name="PowerProfileDropdown" parent="." instance=ExtResource("8_j2331")] +visible = false +layout_mode = 2 +title = "Power Profile" +description = "" diff --git a/core/ui/common/quick_bar/powertools_menu.gd b/core/ui/common/quick_bar/powertools_menu.gd deleted file mode 100644 index 222a585fa..000000000 --- a/core/ui/common/quick_bar/powertools_menu.gd +++ /dev/null @@ -1,273 +0,0 @@ -extends VBoxContainer - -var hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager -var platform := load("res://core/global/platform.tres") as Platform -var performance_manager := load("res://core/systems/performance/performance_manager.tres") as PerformanceManager -var power_station := load("res://core/systems/performance/power_station.tres") as PowerStation - -@onready var cpu_boost_button := $CPUBoostButton as Toggle -@onready var cpu_cores_slider := $CPUCoresSlider as ValueSlider -@onready var gpu_freq_enable := $GPUFreqButton as Toggle -@onready var gpu_freq_max_slider := $GPUFreqMaxSlider as ValueSlider -@onready var gpu_freq_min_slider := $GPUFreqMinSlider as ValueSlider -@onready var gpu_temp_slider := $GPUTempSlider as ValueSlider -@onready var power_profile_dropdown := $PowerProfileDropdown as Dropdown -@onready var tdp_boost_slider := $TDPBoostSlider as ValueSlider -@onready var tdp_slider := $TDPSlider as ValueSlider -@onready var thermal_profile_dropdown := $ThermalProfileDropdown as Dropdown -@onready var smt_button := $SMTButton as Toggle -@onready var cpu_label := $CPUSectionLabel as Control -@onready var gpu_label := $GPUSectionLabel as Control -@onready var wait_label := $WaitLabel as Control -@onready var service_timer := $ServiceTimer as Timer -@onready var apply_timer := $ApplyTimer as Timer - -var power_station_running := false -var profile_loading := false -var current_profile: PerformanceProfile -var logger := Log.get_logger("PowerTools", Log.LEVEL.INFO) - - -# Called when the node enters the scene tree for the first time. -# Finds default values and current settings of the hardware. -func _ready() -> void: - # Listen for signals from performance manager - performance_manager.profile_loaded.connect(_on_profile_loaded) - - # Configure a timer that will monitor the PowerStation DBus service - service_timer.timeout.connect(_on_service_timer_timeout) - # Configure a timer that will apply and save the performance profile - apply_timer.timeout.connect(_on_apply_timer_timeout) - - # Configure the interface - _setup_interface() - - # Re-start the apply timer when changes happen - var on_changed := func() -> void: - if profile_loading: - return - apply_timer.start() - cpu_boost_button.pressed.connect(on_changed) - smt_button.pressed.connect(on_changed) - gpu_freq_enable.pressed.connect(on_changed) - - # Set the total number of available cores if the SMT button is pressed - var on_smt_pressed := func() -> void: - if not hardware_manager.cpu: - return - var cpu := hardware_manager.cpu - if smt_button.button_pressed: - cpu_cores_slider.max_value = cpu.core_count - else: - var cores := cpu.core_count / 2 - if cpu_cores_slider.value > cores: - cpu_cores_slider.value = cores - cpu_cores_slider.max_value = cores - smt_button.pressed.connect(on_smt_pressed) - - # Restart the timer when any slider changes happen - var on_slider_changed := func(_value) -> void: - if profile_loading: - return - apply_timer.start() - cpu_cores_slider.value_changed.connect(on_slider_changed) - tdp_slider.value_changed.connect(on_slider_changed) - tdp_boost_slider.value_changed.connect(on_slider_changed) - gpu_freq_min_slider.value_changed.connect(on_slider_changed) - gpu_freq_max_slider.value_changed.connect(on_slider_changed) - gpu_temp_slider.value_changed.connect(on_slider_changed) - - # Configure GPU frequency timers so the minimum value can never go higher - # than the maximum value slider and the maximum value can never go lower - # than the minimum value slider. - var on_gpu_freq_changed := func(_value: float, kind: String) -> void: - if kind == "min" and gpu_freq_min_slider.value > gpu_freq_max_slider.value: - gpu_freq_max_slider.value = gpu_freq_min_slider.value - return - if kind == "max" and gpu_freq_max_slider.value < gpu_freq_min_slider.value: - gpu_freq_min_slider.value = gpu_freq_max_slider.value - return - gpu_freq_min_slider.value_changed.connect(on_gpu_freq_changed.bind("min")) - gpu_freq_max_slider.value_changed.connect(on_gpu_freq_changed.bind("max")) - - # Also restart the apply timer when dropdown changes happen - var on_dropdown_changed := func(_index) -> void: - if profile_loading: - return - apply_timer.start() - power_profile_dropdown.item_selected.connect(on_dropdown_changed) - thermal_profile_dropdown.item_selected.connect(on_dropdown_changed) - - # Toggle visibility when the GPU freq manual toggle is on - var on_manual_freq := func() -> void: - # Immediately apply manual GPU frequency so we can read the min/max - # values for the sliders - var card := _get_integrated_card() - if not card: - logger.warn("No integrated GPU to set manual frequency on!") - return - card.manual_clock = gpu_freq_enable.button_pressed - - # Update the slider values with the current values - gpu_freq_min_slider.visible = gpu_freq_enable.button_pressed - gpu_freq_min_slider.min_value = card.clock_limit_mhz_min - gpu_freq_min_slider.max_value = card.clock_limit_mhz_max - gpu_freq_min_slider.value = card.clock_value_mhz_min - gpu_freq_max_slider.visible = gpu_freq_enable.button_pressed - gpu_freq_max_slider.min_value = card.clock_limit_mhz_min - gpu_freq_max_slider.max_value = card.clock_limit_mhz_max - gpu_freq_max_slider.value = card.clock_value_mhz_max - - gpu_freq_enable.pressed.connect(on_manual_freq) - - # Setup dropdowns - power_profile_dropdown.clear() - power_profile_dropdown.add_item("Max Performance", 0) - power_profile_dropdown.add_item("Power Saving", 1) - thermal_profile_dropdown.clear() - thermal_profile_dropdown.add_item("Balanced", 0) - thermal_profile_dropdown.add_item("Performance", 1) - thermal_profile_dropdown.add_item("Silent", 2) - - # Set the initial values - _on_profile_loaded(performance_manager.current_profile) - - -# Triggers when the apply timer times out. The apply timer will start/restart -# whenever the user makes a change to any item. When the timer runs out, it will -# call this to apply the current profile. -func _on_apply_timer_timeout() -> void: - if not current_profile: - logger.debug("No loaded profile to apply") - return - logger.debug("Applying and saving profile") - - # Update the profile based on the currently set values - current_profile.cpu_boost_enabled = cpu_boost_button.button_pressed - current_profile.cpu_smt_enabled = smt_button.button_pressed - current_profile.cpu_core_count_current = int(cpu_cores_slider.value) - current_profile.tdp_current = tdp_slider.value - current_profile.tdp_boost_current = tdp_boost_slider.value - current_profile.gpu_manual_enabled = gpu_freq_enable.button_pressed - current_profile.gpu_freq_min_current = gpu_freq_min_slider.value - current_profile.gpu_freq_max_current = gpu_freq_max_slider.value - current_profile.gpu_temp_current = gpu_temp_slider.value - current_profile.gpu_power_profile = power_profile_dropdown.selected - - performance_manager.apply_and_save_profile(current_profile) - - -# Triggers every timeout to monitor the PowerStation DBus -func _on_service_timer_timeout() -> void: - var bus_running := power_station.supports_power_station() - if bus_running == power_station_running: - return - - # If the state of powerstation changes, update the interface accordingly - power_station_running = bus_running - _setup_interface() - - -## Called when a performance profile is loaded -func _on_profile_loaded(profile: PerformanceProfile) -> void: - if not power_station.supports_power_station(): - logger.info("Unable to load performance profile. PowerStation not detected.") - return - var core_count := 1 - if hardware_manager.cpu: - core_count = hardware_manager.cpu.core_count - - logger.debug("Updating UI with loaded performance profile") - # Keep track of the currently loaded profile - current_profile = profile - - # Update CPU UI components based on the loaded profile - profile_loading = true - cpu_boost_button.button_pressed = profile.cpu_boost_enabled - smt_button.button_pressed = profile.cpu_smt_enabled - if smt_button.button_pressed: - cpu_cores_slider.max_value = core_count - else: - var cores := core_count / 2 - if cpu_cores_slider.value > cores: - cpu_cores_slider.value = cores - cpu_cores_slider.max_value = cores - cpu_cores_slider.value = round(profile.cpu_core_count_current) - - # Update GPU UI components - tdp_slider.value = round(profile.tdp_current) - tdp_boost_slider.value = round(profile.tdp_boost_current) - gpu_freq_enable.button_pressed = profile.gpu_manual_enabled - gpu_freq_enable.pressed.emit() - gpu_freq_min_slider.value = round(profile.gpu_freq_min_current) - gpu_freq_max_slider.value = round(profile.gpu_freq_max_current) - gpu_temp_slider.value = round(profile.gpu_temp_current) - - power_profile_dropdown.select(profile.gpu_power_profile) - thermal_profile_dropdown.select(profile.thermal_profile) - - profile_loading = false - - -# Configure the min/max values and visibility based on detected performance -# features. -func _setup_interface() -> void: - # If powerstation is not running, disable everything - if not power_station.supports_power_station(): - wait_label.visible = true - for node in get_children(): - if node == wait_label: - continue - if node == Control: - (node as Control).visible = false - return - - # Configure visibility for all components - wait_label.visible = false - - # Configure CPU components - if power_station.cpu: - var cpu := power_station.cpu - cpu_label.visible = true - cpu_boost_button.visible = cpu.has_feature("cpb") - smt_button.visible = cpu.has_feature("ht") - if cpu.smt_enabled: - cpu_cores_slider.max_value = cpu.cores_count - else: - cpu_cores_slider.max_value = cpu.cores_count / 2 - cpu_cores_slider.visible = true - - # Configure GPU components - if power_station.gpu: - var card := _get_integrated_card() - - # Configure based on integrated graphics card - if card: - gpu_label.visible = true - tdp_slider.visible = true - tdp_slider.min_value = hardware_manager.gpu.tdp_min - tdp_slider.max_value = hardware_manager.gpu.tdp_max - tdp_boost_slider.visible = true - tdp_boost_slider.max_value = hardware_manager.gpu.max_boost - gpu_freq_enable.visible = true - power_profile_dropdown.visible = true - if card.clock_limit_mhz_min > 0 and card.clock_limit_mhz_max > 0: - gpu_freq_min_slider.visible = card.manual_clock - gpu_freq_min_slider.min_value = card.clock_limit_mhz_min - gpu_freq_min_slider.max_value = card.clock_limit_mhz_max - gpu_freq_max_slider.visible = card.manual_clock - gpu_freq_max_slider.min_value = card.clock_limit_mhz_min - gpu_freq_max_slider.max_value = card.clock_limit_mhz_max - if card.thermal_throttle_limit_c > 0: - gpu_temp_slider.visible = true - - -## Returns the primary integrated GPU instance -func _get_integrated_card() -> PowerStation.GPUCard: - var card: PowerStation.GPUCard - var cards := power_station.gpu.get_cards() - for c in cards: - if c.class_type != "integrated": - continue - card = c - return card diff --git a/core/ui/common/quick_bar/powertools_menu.tscn b/core/ui/common/quick_bar/powertools_menu.tscn deleted file mode 100644 index ee0cd9d52..000000000 --- a/core/ui/common/quick_bar/powertools_menu.tscn +++ /dev/null @@ -1,166 +0,0 @@ -[gd_scene load_steps=9 format=3 uid="uid://dv3dt0j3jketh"] - -[ext_resource type="Script" path="res://core/ui/common/quick_bar/powertools_menu.gd" id="1_qncpn"] -[ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_h0jgv"] -[ext_resource type="Resource" uid="uid://dpc1o781f43ef" path="res://core/ui/card_ui/quick_bar/quick_bar_menu_focus.tres" id="3_0iyf7"] -[ext_resource type="PackedScene" uid="uid://dithv38oqgy58" path="res://core/ui/components/section_label.tscn" id="4_1pfjc"] -[ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="5_xig3u"] -[ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="5_yr563"] -[ext_resource type="PackedScene" uid="uid://cemxrvvjgm4g" path="res://core/ui/components/slider.tscn" id="6_7aip6"] -[ext_resource type="PackedScene" uid="uid://xei5afwefxud" path="res://core/ui/components/dropdown.tscn" id="7_0kvsa"] - -[node name="PowerTools" type="VBoxContainer"] -anchors_preset = 10 -anchor_right = 1.0 -offset_bottom = 28.0 -grow_horizontal = 2 -script = ExtResource("1_qncpn") - -[node name="FocusGroup" parent="." instance=ExtResource("2_h0jgv")] -focus_stack = ExtResource("3_0iyf7") - -[node name="ApplyTimer" type="Timer" parent="."] -wait_time = 1.5 -one_shot = true - -[node name="ServiceTimer" type="Timer" parent="."] -wait_time = 5.0 -autostart = true - -[node name="WaitLabel" parent="." instance=ExtResource("5_yr563")] -custom_minimum_size = Vector2(100, 0) -layout_mode = 2 -text = "Waiting for PowerStation service..." -horizontal_alignment = 1 -vertical_alignment = 1 -autowrap_mode = 3 - -[node name="CPUSectionLabel" parent="." instance=ExtResource("4_1pfjc")] -visible = false -layout_mode = 2 -text = "CPU Settings" - -[node name="CPUBoostButton" parent="." instance=ExtResource("5_xig3u")] -visible = false -layout_mode = 2 -focus_neighbor_left = NodePath("../GPUTempSlider") -focus_neighbor_top = NodePath("../GPUTempSlider") -focus_neighbor_right = NodePath("../SMTButton") -focus_neighbor_bottom = NodePath("../SMTButton") -focus_next = NodePath("../SMTButton") -focus_previous = NodePath("../GPUTempSlider") -text = "CPU Boost" -separator_visible = false - -[node name="SMTButton" parent="." instance=ExtResource("5_xig3u")] -visible = false -layout_mode = 2 -focus_neighbor_left = NodePath("../CPUBoostButton") -focus_neighbor_top = NodePath("../CPUBoostButton") -focus_neighbor_right = NodePath("../CPUCoresSlider") -focus_neighbor_bottom = NodePath("../CPUCoresSlider") -focus_next = NodePath("../CPUCoresSlider") -focus_previous = NodePath("../CPUBoostButton") -text = "SMT Enabled" -separator_visible = false - -[node name="CPUCoresSlider" parent="." instance=ExtResource("6_7aip6")] -visible = false -layout_mode = 2 -focus_neighbor_left = NodePath("../SMTButton") -focus_neighbor_top = NodePath("../SMTButton") -focus_neighbor_right = NodePath("../TDPSlider") -focus_neighbor_bottom = NodePath("../TDPSlider") -focus_next = NodePath("../TDPSlider") -focus_previous = NodePath("../SMTButton") -text = "CPU Cores" -value = 1.0 -max_value = 1.0 -min_value = 1.0 - -[node name="GPUSectionLabel" parent="." instance=ExtResource("4_1pfjc")] -visible = false -layout_mode = 2 -text = "GPU Settings" - -[node name="TDPSlider" parent="." instance=ExtResource("6_7aip6")] -visible = false -layout_mode = 2 -focus_neighbor_left = NodePath("../CPUCoresSlider") -focus_neighbor_top = NodePath("../CPUCoresSlider") -focus_neighbor_right = NodePath("../TDPBoostSlider") -focus_neighbor_bottom = NodePath("../TDPBoostSlider") -focus_next = NodePath("../TDPBoostSlider") -focus_previous = NodePath("../CPUCoresSlider") -text = "TDP" -max_value = 0.0 - -[node name="TDPBoostSlider" parent="." instance=ExtResource("6_7aip6")] -visible = false -layout_mode = 2 -focus_neighbor_left = NodePath("../TDPSlider") -focus_neighbor_top = NodePath("../TDPSlider") -focus_previous = NodePath("../TDPSlider") -text = "TDP Boost" -max_value = 0.0 - -[node name="GPUFreqButton" parent="." instance=ExtResource("5_xig3u")] -visible = false -layout_mode = 2 -focus_neighbor_left = NodePath("../TDPBoostSlider") -focus_neighbor_top = NodePath("../TDPBoostSlider") -focus_neighbor_right = NodePath("../GPUFreqMinSlider") -focus_neighbor_bottom = NodePath("../GPUFreqMinSlider") -focus_next = NodePath("../GPUFreqMinSlider") -focus_previous = NodePath("../TDPBoostSlider") -text = "Manual Freq" -separator_visible = false - -[node name="GPUFreqMinSlider" parent="." instance=ExtResource("6_7aip6")] -visible = false -layout_mode = 2 -focus_neighbor_right = NodePath("../GPUFreqMaxSlider") -focus_neighbor_bottom = NodePath("../GPUFreqMaxSlider") -focus_next = NodePath("../GPUFreqMaxSlider") -text = "Min Freq" -max_value = 0.0 -step = 100.0 - -[node name="GPUFreqMaxSlider" parent="." instance=ExtResource("6_7aip6")] -visible = false -layout_mode = 2 -focus_neighbor_left = NodePath("../GPUFreqMinSlider") -focus_neighbor_top = NodePath("../GPUFreqMinSlider") -focus_neighbor_right = NodePath("../GPUTempSlider") -focus_neighbor_bottom = NodePath("../GPUTempSlider") -focus_next = NodePath("../GPUTempSlider") -focus_previous = NodePath("../GPUFreqMinSlider") -text = "Max Freq" -max_value = 0.0 -step = 100.0 - -[node name="GPUTempSlider" parent="." instance=ExtResource("6_7aip6")] -visible = false -layout_mode = 2 -focus_neighbor_left = NodePath("../GPUFreqMaxSlider") -focus_neighbor_top = NodePath("../GPUFreqMaxSlider") -focus_neighbor_right = NodePath("../CPUBoostButton") -focus_neighbor_bottom = NodePath("../CPUBoostButton") -focus_next = NodePath("../CPUBoostButton") -focus_previous = NodePath("../GPUFreqMaxSlider") -text = "GPU Temp Limit" -value = 70.0 -max_value = 105.0 -min_value = 70.0 - -[node name="PowerProfileDropdown" parent="." instance=ExtResource("7_0kvsa")] -visible = false -layout_mode = 2 -title = "Power Profile" -description = "" - -[node name="ThermalProfileDropdown" parent="." instance=ExtResource("7_0kvsa")] -visible = false -layout_mode = 2 -title = "Thermal Throttle Profile" -description = "" diff --git a/core/ui/common/quick_bar/quick_settings_menu.gd b/core/ui/common/quick_bar/quick_settings_menu.gd index 277207f0a..423d5453d 100644 --- a/core/ui/common/quick_bar/quick_settings_menu.gd +++ b/core/ui/common/quick_bar/quick_settings_menu.gd @@ -1,9 +1,9 @@ extends Control -var Gamescope := preload("res://core/global/gamescope.tres") as Gamescope var AudioManager := preload("res://core/global/audio_manager.tres") as AudioManager var DisplayManager := preload("res://core/global/display_manager.tres") as DisplayManager +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance var backlights := DisplayManager.get_backlight_paths() var logger := Log.get_logger("QuickSettings", Log.LEVEL.INFO) @@ -48,7 +48,10 @@ func _on_brightness_slider_changed(value: float) -> void: DisplayManager.set_brightness(percent) +# TODO: Deprecate func _on_saturation_changed(value: float) -> void: - var code := Gamescope.set_saturation(value / 100.0) - if code != OK: - logger.warn("Unable to set saturation. Code: " + str(code)) + pass + #var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_PRIMARY) + #var code := xwayland.set_saturation(value / 100.0) + #if code != OK: + # logger.warn("Unable to set saturation. Code: " + str(code)) diff --git a/core/ui/common/settings/display_settings_menu.gd b/core/ui/common/settings/display_settings_menu.gd index 09f45a3b0..f7561947a 100644 --- a/core/ui/common/settings/display_settings_menu.gd +++ b/core/ui/common/settings/display_settings_menu.gd @@ -12,7 +12,7 @@ func _ready() -> void: get_window().content_scale_factor = display_scale scale_slider.value_changed.connect(_on_scale_changed) - var blur_enabled := settings_manager.get_value("display", "enable_overlay_blur", true) as bool + var blur_enabled := settings_manager.get_value("display", "enable_overlay_blur", false) as bool blur_toggle.button_pressed = blur_enabled blur_toggle.toggled.connect(_on_blur_toggled) diff --git a/core/ui/common/settings/network_settings_menu.gd b/core/ui/common/settings/network_settings_menu.gd index 5ce877f3f..1788bd40d 100644 --- a/core/ui/common/settings/network_settings_menu.gd +++ b/core/ui/common/settings/network_settings_menu.gd @@ -1,31 +1,51 @@ extends Control -const thread := preload("res://core/systems/threading/thread_pool.tres") -const bar_0 := preload("res://assets/ui/icons/wifi-none.svg") -const bar_1 := preload("res://assets/ui/icons/wifi-low.svg") -const bar_2 := preload("res://assets/ui/icons/wifi-medium.svg") -const bar_3 := preload("res://assets/ui/icons/wifi-high.svg") +var network_manager := load("res://core/systems/network/network_manager.tres") as NetworkManagerInstance var connecting := false @onready var no_net_label := $%NoNetworkLabel -@onready var wifi_tree := $%WifiNetworkTree as Tree -@onready var refresh_button := $%RefreshButton as Button +@onready var wifi_tree := $%WifiNetworkTree as WifiNetworkTree +@onready var wireless_toggle := $%WirelessEnableToggle as Toggle +@onready var wifi_label := $%WifiLabel @onready var password_button := $%WifiPasswordButton @onready var password_input := $%WifiPasswordTextInput @onready var password_popup := $%PopupContainer +@onready var ip_text := $%IPAddressText as SelectableText +@onready var mask_text := $%SubnetText as SelectableText +@onready var gateway_text := $%GatewayText as SelectableText # Called when the node enters the scene tree for the first time. func _ready() -> void: - if not NetworkManager.supports_network(): - no_net_label.visible = true - return - - thread.start() + # Display label if no networking is available + var on_networking_available := func(): + no_net_label.visible = not network_manager.is_running() + network_manager.started.connect(on_networking_available) + network_manager.stopped.connect(on_networking_available) + on_networking_available.call() + + # Connect the wireless enable toggle + var wireless_enabled := network_manager.wireless_enabled + wireless_toggle.button_pressed = wireless_enabled + wifi_label.visible = wireless_enabled + wifi_tree.visible = wireless_enabled + var on_wireless_toggled := func(enabled: bool): + network_manager.wireless_enabled = enabled + wifi_label.visible = enabled + wifi_tree.visible = enabled + if enabled: + await get_tree().create_timer(4.0).timeout + wifi_tree.refresh_networks() + wireless_toggle.toggled.connect(on_wireless_toggled) + + # Fill out the connection details + var connection := network_manager.primary_connection + _on_connection_changed(connection) + network_manager.primary_connection_changed.connect(_on_connection_changed) + + # Connect visibility and challenge signals visibility_changed.connect(_on_visible_changed) - refresh_button.pressed.connect(_refresh_networks) - wifi_tree.item_activated.connect(_on_wifi_selected) - wifi_tree.create_item() + wifi_tree.challenge_required.connect(_on_wifi_challenge) # Configure the OSK with the password input box var password_context := password_input.keyboard_context as KeyboardContext @@ -35,118 +55,61 @@ func _ready() -> void: password_context.close_on_submit = true -func _on_visible_changed() -> void: - _refresh_networks() - password_popup.visible = false - - -func _on_wifi_selected() -> void: - if connecting: +func _on_connection_changed(connection: NetworkActiveConnection) -> void: + await get_tree().create_timer(3.0).timeout + if not connection: + ip_text.text = "0.0.0.0" + mask_text.text = "0" + gateway_text.text = "0.0.0.0" + return + var devices := connection.devices + if devices.is_empty(): + ip_text.text = "0.0.0.0" + mask_text.text = "0" + gateway_text.text = "0.0.0.0" return - connecting = true - refresh_button.disabled = true - - var item := wifi_tree.get_selected() - var ssid := item.get_text(1) - var connect_ap := func() -> int: - return NetworkManager.connect_access_point(ssid) - item.set_text(4, "Connecting") - var code := await thread.exec(connect_ap) as int - - connecting = false - refresh_button.disabled = false - if code == OK: - _refresh_networks() + var device := devices[0] + var ip := device.ip4_config + if not ip: + ip_text.text = "0.0.0.0" + mask_text.text = "0" + gateway_text.text = "0.0.0.0" return + var gateway := ip.gateway + if not gateway.is_empty(): + gateway_text.text = gateway + var addresses := ip.addresses + if addresses.is_empty(): + ip_text.text = "0.0.0.0" + mask_text.text = "0" + gateway_text.text = "0.0.0.0" + return + var info := addresses[0] + if "prefix" in info: + mask_text.text = str(info["prefix"]) + if "address" in info: + ip_text.text = info["address"] - # If we fail to connect, open the wifi challenge - _on_wifi_challenge(item, ssid) + +func _on_visible_changed() -> void: + _on_connection_changed(network_manager.primary_connection) + wifi_tree.refresh_networks() + password_popup.visible = false -func _on_wifi_challenge(item: TreeItem, ssid: String) -> void: +func _on_wifi_challenge(callback: Callable) -> void: password_popup.visible = true password_input.grab_focus.call_deferred() var on_pass_submit := func(): connecting = true - refresh_button.disabled = true password_popup.visible = false - refresh_button.grab_focus.call_deferred() + wifi_tree.grab_focus.call_deferred() var password := password_input.text as String - - var connect_ap := func() -> int: - return NetworkManager.connect_access_point(ssid, password) - item.set_text(4, "Connecting") - var code := await thread.exec(connect_ap) as int - - connecting = false - refresh_button.disabled = false - if code == OK: - _refresh_networks() - return - - item.set_text(4, "Failed to connect") - + callback.call(password) password_button.pressed.connect(on_pass_submit, CONNECT_ONE_SHOT) -func _refresh_networks() -> void: - # Disable the refresh button while refreshing - refresh_button.disabled = true - - # Fetch all the available access points from NetworkManager - var tree := wifi_tree as Tree - var root := tree.get_root() - var access_points: Array[NetworkManager.WifiAP] - var get_aps := func() -> Array[NetworkManager.WifiAP]: - return NetworkManager.get_access_points() - access_points = await thread.exec(get_aps) - - # Create an array of BSSIDs from our access points - var bssids := [] - for ap in access_points: - bssids.append(ap.bssid) - - # Look at the current tree items to see if any need to be removed - var tree_bssids := {} - for item in root.get_children(): - var bssid := item.get_metadata(0) as String - if bssid in bssids: - tree_bssids[bssid] = item - continue - root.remove_child(item) - - # Look at the current APs to see if any tree items need to be created - # or updated - for ap in access_points: - var item: TreeItem - if ap.bssid in tree_bssids: - item = tree_bssids[ap.bssid] - else: - item = tree.create_item(root) - item.set_metadata(0, ap.bssid) - item.set_icon(0, NetworkManager.get_strength_texture(ap.strength)) - item.set_text(1, ap.ssid) - item.set_text(2, ap.security) - item.set_text(3, ap.rate) - if ap.in_use: - item.set_text(4, "Connected") - else: - item.set_text(4, "") - - refresh_button.disabled = false - - -func _get_strength_texture(strength: int) -> Texture2D: - if strength >= 80: - return bar_3 - if strength >= 60: - return bar_2 - if strength >= 40: - return bar_1 - return bar_0 - - # Intercept back input when the password dialog is open func _input(event: InputEvent) -> void: if not visible: diff --git a/core/ui/common/settings/network_settings_menu.tscn b/core/ui/common/settings/network_settings_menu.tscn index 8ea0ed6de..b37bfbe58 100644 --- a/core/ui/common/settings/network_settings_menu.tscn +++ b/core/ui/common/settings/network_settings_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=15 format=3 uid="uid://cpss2bhdwm8t7"] +[gd_scene load_steps=17 format=3 uid="uid://cpss2bhdwm8t7"] [ext_resource type="Script" path="res://core/ui/common/settings/network_settings_menu.gd" id="1_2qdps"] [ext_resource type="PackedScene" uid="uid://8m20p2s0v5gb" path="res://core/systems/input/focus_group.tscn" id="2_8jmkq"] @@ -8,12 +8,14 @@ [ext_resource type="PackedScene" uid="uid://d0u3rsa5qpj57" path="res://core/ui/components/subsection_label.tscn" id="4_t7br0"] [ext_resource type="Resource" uid="uid://iqrotrmq62i6" path="res://assets/state/state_machines/settings_state_machine.tres" id="4_tf04q"] [ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="5_jobhc"] -[ext_resource type="PackedScene" uid="uid://df5o1o2dsik84" path="res://core/ui/components/button.tscn" id="5_round"] -[ext_resource type="Theme" uid="uid://bko0q7gp1hwjp" path="res://assets/themes/dracula.tres" id="6_i7u1a"] [ext_resource type="Resource" uid="uid://2efht48q7i6v" path="res://assets/state/states/settings_network.tres" id="6_ke40f"] [ext_resource type="Script" path="res://core/systems/input/focus_group_setter.gd" id="7_gnxgo"] [ext_resource type="PackedScene" uid="uid://d1rjdfxxrdccf" path="res://core/ui/components/text_input.tscn" id="7_qx5i7"] [ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="9_v6vet"] +[ext_resource type="PackedScene" uid="uid://d1qb7euwlu7bh" path="res://core/ui/components/toggle.tscn" id="11_y8h23"] +[ext_resource type="Script" path="res://core/ui/components/wifi_network_tree.gd" id="12_flshi"] +[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="13_p7bew"] +[ext_resource type="PackedScene" uid="uid://cmjjyqx1pl23d" path="res://core/ui/components/text.tscn" id="13_wnqd6"] [node name="NetworkSettings" type="ScrollContainer"] anchors_preset = 15 @@ -54,39 +56,39 @@ layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 -[node name="FocusGroup" parent="MarginContainer/VBoxContainer" node_paths=PackedStringArray("current_focus") instance=ExtResource("2_8jmkq")] -current_focus = NodePath("../RefreshButton") +[node name="FocusGroup" parent="MarginContainer/VBoxContainer" instance=ExtResource("2_8jmkq")] [node name="NoNetworkLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("4_t7br0")] unique_name_in_owner = true visible = false layout_mode = 2 -text = "Network dependencies not found" +text = "Networking not available" horizontal_alignment = 1 -[node name="DisplayLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("3_ddyy1")] +[node name="WirelessDisplayLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("3_ddyy1")] layout_mode = 2 text = "Wireless Network" [node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"] layout_mode = 2 -[node name="WifiLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("4_t7br0")] +[node name="WirelessEnableToggle" parent="MarginContainer/VBoxContainer" instance=ExtResource("11_y8h23")] +unique_name_in_owner = true layout_mode = 2 -text = "Visible Networks" +text = "Enable Wireless" +separator_visible = false +button_pressed = true -[node name="RefreshButton" parent="MarginContainer/VBoxContainer" instance=ExtResource("5_round")] +[node name="WifiLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("4_t7br0")] unique_name_in_owner = true layout_mode = 2 -text = "Refresh Networks" +text = "Visible Networks" [node name="WifiNetworkTree" type="Tree" parent="MarginContainer/VBoxContainer"] unique_name_in_owner = true custom_minimum_size = Vector2(0, 300) layout_mode = 2 -columns = 5 -hide_root = true -select_mode = 1 +script = ExtResource("12_flshi") [node name="Spacer" type="Control" parent="MarginContainer/VBoxContainer"] custom_minimum_size = Vector2(0, 20) @@ -101,15 +103,42 @@ text = "Wired Network" visible = false layout_mode = 2 +[node name="DetailsDisplayLabel" parent="MarginContainer/VBoxContainer" instance=ExtResource("3_ddyy1")] +layout_mode = 2 +text = "Connection Details" + +[node name="HSeparator3" type="HSeparator" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="IPAddressText" parent="MarginContainer/VBoxContainer" instance=ExtResource("13_wnqd6")] +unique_name_in_owner = true +layout_mode = 2 +title = "IP Address" +description = "" +text = "0.0.0.0" + +[node name="SubnetText" parent="MarginContainer/VBoxContainer" instance=ExtResource("13_wnqd6")] +unique_name_in_owner = true +layout_mode = 2 +title = "Subnet Prefix" +description = "" +text = "0" + +[node name="GatewayText" parent="MarginContainer/VBoxContainer" instance=ExtResource("13_wnqd6")] +unique_name_in_owner = true +layout_mode = 2 +title = "Gateway" +description = "" +text = "0.0.0.0" + [node name="PopupContainer" type="CenterContainer" parent="MarginContainer"] unique_name_in_owner = true visible = false layout_mode = 2 [node name="PanelContainer" type="PanelContainer" parent="MarginContainer/PopupContainer"] -custom_minimum_size = Vector2(300, 200) +custom_minimum_size = Vector2(300, 0) layout_mode = 2 -theme = ExtResource("6_i7u1a") [node name="MarginContainer" type="MarginContainer" parent="MarginContainer/PopupContainer/PanelContainer"] layout_mode = 2 @@ -120,6 +149,7 @@ theme_override_constants/margin_bottom = 20 [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/PopupContainer/PanelContainer/MarginContainer"] layout_mode = 2 +theme_override_constants/separation = 10 [node name="WifiPasswordTextInput" parent="MarginContainer/PopupContainer/PanelContainer/MarginContainer/VBoxContainer" instance=ExtResource("7_qx5i7")] unique_name_in_owner = true @@ -129,7 +159,7 @@ title = "Password" description = "Enter the password for this network" secret = true -[node name="WifiPasswordButton" parent="MarginContainer/PopupContainer/PanelContainer/MarginContainer/VBoxContainer" instance=ExtResource("5_round")] +[node name="WifiPasswordButton" parent="MarginContainer/PopupContainer/PanelContainer/MarginContainer/VBoxContainer" instance=ExtResource("13_p7bew")] unique_name_in_owner = true layout_mode = 2 text = "Submit" diff --git a/core/ui/components/battery_container.gd b/core/ui/components/battery_container.gd index 463bb8d06..b9d3063a1 100644 --- a/core/ui/components/battery_container.gd +++ b/core/ui/components/battery_container.gd @@ -7,8 +7,10 @@ const icon_half = preload("res://assets/ui/icons/battery-half.svg") const icon_low = preload("res://assets/ui/icons/battery-low.svg") const icon_empty = preload("res://assets/ui/icons/battery-empty.svg") -var power_manager := load("res://core/systems/power/power_manager.tres") as PowerManager -var batteries : Array[PowerManager.Device] +const no_battery_names := ["battery-missing-symbolic"] + +var power_manager := load("res://core/systems/power/power_manager.tres") as UPowerInstance +var display_device := power_manager.get_display_device() var logger := Log.get_logger("BatteryContainer", Log.LEVEL.INFO) @@ -17,20 +19,21 @@ var logger := Log.get_logger("BatteryContainer", Log.LEVEL.INFO) func _ready(): - batteries = power_manager.get_devices_by_type(PowerManager.DEVICE_TYPE.BATTERY) - if batteries.size() > 1: - logger.warn("You somehow have more than one battery. We don't know what to do with that.") - if batteries.size() == 0: + if not display_device: + logger.debug("No battery detected. nothing to do.") + visible = false + return + + if display_device.icon_name in no_battery_names: logger.debug("No battery detected. nothing to do.") visible = false return - var battery := batteries[0] - _on_update_device(battery) - battery.updated.connect(_on_update_device.bind(battery)) + _on_update_device(display_device) + display_device.updated.connect(_on_update_device.bind(display_device)) -func _on_update_device(item: PowerManager.Device): +func _on_update_device(item: UPowerDevice): var capacity := item.percentage var state := item.state battery_icon.texture = get_capacity_texture(capacity, state) @@ -42,8 +45,8 @@ func _on_update_device(item: PowerManager.Device): ## Returns the texture reflecting the given battery capacity -static func get_capacity_texture(capacity: int, state: PowerManager.DEVICE_STATE) -> Texture2D: - if state in [PowerManager.DEVICE_STATE.CHARGING, PowerManager.DEVICE_STATE.FULLY_CHARGED]: +static func get_capacity_texture(capacity: int, state: int) -> Texture2D: + if state in [UPowerDevice.STATE_CHARGING, UPowerDevice.STATE_FULLY_CHARGED]: return icon_charging if capacity >= 90: return icon_full diff --git a/core/ui/components/card.gd b/core/ui/components/card.gd index dd5f27ebd..e2243c003 100644 --- a/core/ui/components/card.gd +++ b/core/ui/components/card.gd @@ -35,12 +35,16 @@ signal unhighlighted progress.value = v var library_item: LibraryItem +var tapped_count := 0 var logger := Log.get_logger("GameCard") @onready var texture := $%TextureRect @onready var name_container := $%NameMargin @onready var name_label := $%NameLabel @onready var progress := $%ProgressBar as ProgressBar +@onready var tap_timer := $%TapTimer as Timer +@onready var shine_rect := $%ShineShader as ColorRect +@onready var god_rays_rect := $%GodRaysShader as ColorRect # Called when the node enters the scene tree for the first time. @@ -53,7 +57,12 @@ func _ready() -> void: focus_exited.connect(_on_unfocus) texture.mouse_entered.connect(_on_focus) texture.mouse_exited.connect(_on_unfocus) - + + # Setup a timer callback to clear number of taps on the card + var on_timeout := func(): + tapped_count = 0 + tap_timer.timeout.connect(on_timeout) + var parent := get_parent() if parent and parent is Container: parent.queue_sort() @@ -91,16 +100,76 @@ func set_library_item(item: LibraryItem, free_on_remove: bool = true) -> void: func _on_focus() -> void: highlighted.emit() + _shader_godrays_fade_in() + _shader_shine_fade_in() + + +func _shader_godrays_fade_in() -> void: + var tween := create_tween() + var shader := god_rays_rect.material as ShaderMaterial + var final_color := Color(0.961, 0.937, 1.0, 0.2) + var start_color := Color(0.961, 0.937, 1.0, 0.0) + shader.set_shader_parameter("color", start_color) + god_rays_rect.visible = true + var on_tween := func(value: Color): + shader.set_shader_parameter("color", value) + tween.tween_method(on_tween, start_color, final_color, 0.5) + + +func _shader_shine_fade_in() -> void: + var tween := create_tween() + var shader := shine_rect.material as ShaderMaterial + var final_alpha := 0.02 + var start_alpha := 0.0 + shader.set_shader_parameter("Alpha", start_alpha) + shine_rect.visible = true + var on_tween := func(value: float): + shader.set_shader_parameter("Alpha", value) + tween.tween_method(on_tween, start_alpha, final_alpha, 0.8) func _on_unfocus() -> void: unhighlighted.emit() + _shader_godrays_fade_out() + _shader_shine_fade_out() + + +func _shader_godrays_fade_out() -> void: + var tween := create_tween() + var shader := god_rays_rect.material as ShaderMaterial + var start_color := Color(0.961, 0.937, 1.0, 0.2) + var final_color := Color(0.961, 0.937, 1.0, 0.0) + shader.set_shader_parameter("color", start_color) + var on_tween := func(value: Color): + shader.set_shader_parameter("color", value) + tween.tween_method(on_tween, start_color, final_color, 0.5) + tween.tween_property(god_rays_rect, "visible", false, 0.0) + + +func _shader_shine_fade_out() -> void: + var tween := create_tween() + var shader := shine_rect.material as ShaderMaterial + var start_alpha := 0.02 + var final_alpha := 0.0 + shader.set_shader_parameter("Alpha", start_alpha) + var on_tween := func(value: float): + shader.set_shader_parameter("Alpha", value) + tween.tween_method(on_tween, start_alpha, final_alpha, 0.1) + tween.tween_property(shine_rect, "visible", false, 0.0) func _gui_input(event: InputEvent) -> void: if event is InputEventMouseButton: if event.is_pressed() and event.double_click: button_up.emit() + if event is InputEventScreenTouch: + if event.is_pressed(): + tapped_count += 1 + if tapped_count > 1: + tapped_count = 0 + button_up.emit() + else: + tap_timer.start() if event.is_action("ui_accept"): if event.is_pressed(): button_down.emit() diff --git a/core/ui/components/card.tscn b/core/ui/components/card.tscn index 1117e1468..eac5f657b 100644 --- a/core/ui/components/card.tscn +++ b/core/ui/components/card.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=9 format=3 uid="uid://bkhrcemal7uxo"] +[gd_scene load_steps=13 format=3 uid="uid://bkhrcemal7uxo"] [ext_resource type="Script" path="res://core/ui/components/card.gd" id="1_aiin0"] [ext_resource type="Texture2D" uid="uid://blc0qyvt1qhci" path="res://assets/images/placeholder-grid-portrait.png" id="2_2hlyj"] [ext_resource type="PackedScene" uid="uid://c5sfkhrfbao71" path="res://core/systems/effects/play_audio_effect.tscn" id="2_701dj"] [ext_resource type="PackedScene" uid="uid://bui0u88oe8jr" path="res://core/systems/effects/raise_effect.tscn" id="3_gyi80"] +[ext_resource type="Shader" path="res://assets/shaders/highlight_shine.gdshader" id="5_0n631"] +[ext_resource type="Shader" path="res://assets/shaders/god_rays.gdshader" id="7_8n7dp"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ygsnb"] corner_radius_top_left = 26 @@ -21,6 +23,37 @@ corner_radius_top_right = 5 corner_radius_bottom_right = 5 corner_radius_bottom_left = 5 +[sub_resource type="ShaderMaterial" id="ShaderMaterial_1bdvx"] +resource_local_to_scene = true +shader = ExtResource("5_0n631") +shader_parameter/Line_Smoothness = 0.045 +shader_parameter/Line_Width = 0.075 +shader_parameter/Brightness = 0.81 +shader_parameter/Rotation_deg = 35.0 +shader_parameter/Distortion = 1.67 +shader_parameter/Speed = 0.31 +shader_parameter/Position = 0.0 +shader_parameter/Position_Min = 0.25 +shader_parameter/Position_Max = 0.5 +shader_parameter/Alpha = 0.02 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_1nde2"] +resource_local_to_scene = true +shader = ExtResource("7_8n7dp") +shader_parameter/angle = 0.0 +shader_parameter/position = 0.0 +shader_parameter/spread = 1.0 +shader_parameter/cutoff = -1.0 +shader_parameter/falloff = 1.0 +shader_parameter/edge_fade = 0.904 +shader_parameter/speed = 20.0 +shader_parameter/ray1_density = 8.0 +shader_parameter/ray2_density = 30.0 +shader_parameter/ray2_intensity = 0.3 +shader_parameter/color = Color(0.960784, 0.937255, 1, 0.117647) +shader_parameter/hdr = false +shader_parameter/seed = 5.96 + [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_st1t5"] resource_local_to_scene = true bg_color = Color(0, 0, 0, 0) @@ -63,8 +96,9 @@ on_signal = "highlighted" [node name="PanelContainer" type="PanelContainer" parent="."] clip_children = 1 layout_mode = 0 -offset_right = 40.0 -offset_bottom = 40.0 +offset_right = 200.0 +offset_bottom = 300.0 +mouse_filter = 1 theme_override_styles/panel = SubResource("StyleBoxFlat_ygsnb") [node name="TextureRect" type="TextureRect" parent="PanelContainer"] @@ -119,6 +153,20 @@ theme_override_styles/fill = SubResource("StyleBoxFlat_up8ek") value = 50.0 rounded = true +[node name="ShineShader" type="ColorRect" parent="PanelContainer"] +unique_name_in_owner = true +visible = false +material = SubResource("ShaderMaterial_1bdvx") +layout_mode = 2 +mouse_filter = 2 + +[node name="GodRaysShader" type="ColorRect" parent="PanelContainer"] +unique_name_in_owner = true +visible = false +material = SubResource("ShaderMaterial_1nde2") +layout_mode = 2 +mouse_filter = 2 + [node name="MarginContainer" type="MarginContainer" parent="."] show_behind_parent = true custom_minimum_size = Vector2(200, 300) @@ -136,3 +184,8 @@ show_behind_parent = true layout_mode = 2 mouse_filter = 2 theme_override_styles/panel = SubResource("StyleBoxFlat_st1t5") + +[node name="TapTimer" type="Timer" parent="."] +unique_name_in_owner = true +wait_time = 0.5 +one_shot = true diff --git a/core/ui/components/card_button_setting.gd b/core/ui/components/card_button_setting.gd new file mode 100644 index 000000000..8b09e42e9 --- /dev/null +++ b/core/ui/components/card_button_setting.gd @@ -0,0 +1,89 @@ +@tool +extends BoxContainer +class_name CardButtonSetting + +signal pressed +signal button_up +signal player_button_up(metaname: String, dbus_path: String) +signal button_down +signal player_button_down(metaname: String, dbus_path: String) + +@export_category("Label Settings") +@export var text: String = "Setting" +@export var separator_visible: bool = false +@export var show_label := true: + set(v): + show_label = v + if label: + label.visible = v + notify_property_list_changed() +@export var description: String = "": + set(v): + description = v + if description_label: + description_label.text = v + description_label.visible = v != "" + notify_property_list_changed() + +@export_category("Button Settings") +@export var button_text := "Button": + set(v): + button_text = v + if card_button: + card_button.text = v + notify_property_list_changed() + +@export var disabled := false: + set(v): + disabled = v + if card_button: + card_button.disabled = v + notify_property_list_changed() + +@onready var label := $%Label as Label +@onready var description_label := $%DescriptionLabel as Label +@onready var card_button := $%CardButton as CardButton +@onready var hsep := $%HSeparator as HSeparator +@onready var panel := $%PanelContainer as PanelContainer + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + label.text = text + description_label.text = description + description_label.visible = description != "" + hsep.visible = separator_visible + + if Engine.is_editor_hint(): + return + + # Update colors on focus + focus_entered.connect(_on_focus.bind(true)) + focus_exited.connect(_on_focus.bind(false)) + theme_changed.connect(_on_theme_changed) + + # Connect button signals + card_button.text = button_text + card_button.pressed.connect(self.emit_signal.bind("pressed")) + card_button.button_up.connect(self.emit_signal.bind("button_up")) + card_button.button_down.connect(self.emit_signal.bind("button_down")) + var on_player_button := func(metaname: String, dbus_path: String, sig: String): + self.emit_signal(sig, metaname, dbus_path) + card_button.player_button_up.connect(on_player_button.bind("player_button_up")) + card_button.player_button_down.connect(on_player_button.bind("player_button_up")) + + # Find the parent theme and update if required + var effective_theme := ThemeUtils.get_effective_theme(self) + if effective_theme: + _on_theme_changed() + + +func _on_theme_changed() -> void: + # Get the style from the set theme so it can be set on the panel container + var normal_stylebox := get_theme_stylebox("panel", "SelectableText").duplicate() + panel.add_theme_stylebox_override("panel", normal_stylebox) + + +func _on_focus(focused: bool) -> void: + if focused: + card_button.grab_focus() diff --git a/core/ui/components/card_button_setting.tscn b/core/ui/components/card_button_setting.tscn new file mode 100644 index 000000000..f1fa344f8 --- /dev/null +++ b/core/ui/components/card_button_setting.tscn @@ -0,0 +1,63 @@ +[gd_scene load_steps=5 format=3 uid="uid://bk5ld0h1jgd2t"] + +[ext_resource type="LabelSettings" uid="uid://bdx3xgfcaqhse" path="res://assets/label/title_label.tres" id="1_bah65"] +[ext_resource type="Script" path="res://core/ui/components/card_button_setting.gd" id="1_vyqam"] +[ext_resource type="LabelSettings" uid="uid://bv56n31s84bfn" path="res://assets/label/subheading_label.tres" id="2_m742p"] +[ext_resource type="PackedScene" uid="uid://c71ayw7pcw6u6" path="res://core/ui/components/card_button.tscn" id="3_kep31"] + +[node name="CardButtonSetting" type="BoxContainer"] +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 47.0 +grow_horizontal = 2 +size_flags_horizontal = 3 +focus_mode = 2 +mouse_filter = 0 +vertical = true +script = ExtResource("1_vyqam") +button_text = null +disabled = null + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/PanelContainer/MarginContainer"] +layout_mode = 2 + +[node name="ToggleContainer" type="HBoxContainer" parent="VBoxContainer/PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/PanelContainer/MarginContainer/VBoxContainer/ToggleContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Setting" +label_settings = ExtResource("1_bah65") + +[node name="CardButton" parent="VBoxContainer/PanelContainer/MarginContainer/VBoxContainer/ToggleContainer" instance=ExtResource("3_kep31")] +unique_name_in_owner = true +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 + +[node name="DescriptionLabel" type="Label" parent="VBoxContainer/PanelContainer/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +label_settings = ExtResource("2_m742p") +autowrap_mode = 3 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 diff --git a/core/ui/components/dialog.gd b/core/ui/components/dialog.gd index 55f499dfb..56bc4caf4 100644 --- a/core/ui/components/dialog.gd +++ b/core/ui/components/dialog.gd @@ -1,4 +1,4 @@ -@icon("res://assets/editor-icons/dialog-2-bold.svg") +@icon("res://assets/editor-icons/solar--dialog-2-bold.svg") @tool extends Control class_name Dialog diff --git a/core/ui/components/drive_card.gd b/core/ui/components/drive_card.gd index 12cce13f7..50a9792db 100644 --- a/core/ui/components/drive_card.gd +++ b/core/ui/components/drive_card.gd @@ -11,7 +11,7 @@ const sd_icon = preload("res://assets/icons/interface-sd.svg") const ssd_icon = preload("res://assets/icons/interface-ssd.svg") const usb_icon = preload("res://assets/icons/interface-usb.svg") -var device: BlockDevice +var block_device: BlockDevice var device_path: String var highlight_tween: Tween @@ -39,8 +39,8 @@ var log_level:= Log.LEVEL.INFO ## Performs _ready fucntionality with the given BlockDevice func setup(device: BlockDevice) -> void: # Setup UDisks2 information - self.device = device - self.device_path = "/dev" + self.device.dbus_path.trim_prefix(steam_disks.BLOCK_PREFIX) + self.block_device = device + self.device_path = "/dev" + self.block_device.dbus_path.trim_prefix(steam_disks.BLOCK_PREFIX) logger = Log.get_logger("DriveCard|"+self.device_path, log_level) logger.debug("Setup Drive Card") @@ -86,7 +86,7 @@ func _srm_format_drive() -> void: _clear_partitions() _on_format_started() var note := Notification.new("Format Complete: " + self.device_path) - if await steam_disks.format_drive(device) != OK: + if await steam_disks.format_drive(block_device) != OK: note.text = "Failed to format " + self.device_path _on_format_complete(note) @@ -132,39 +132,49 @@ func _srm_init_drive(partition: PartitionDevice) -> void: init_partition.emit(partition) -func _clear_partitions() -> void: - # Clear the current grid of items - var keep_nodes := [partitions_focus_group] - for child in partitions_container.get_children(): - if child in keep_nodes: - continue - partitions_container.remove_child(child) - child.queue_free() - func _set_icon() -> void: - if not device or not device.drive: - logger.warn("Unable to detect drive to set icon for:", device) + if not block_device: + logger.warn("Unable to detect drive to set icon for something?") drive_icon.texture = hdd_icon return - match device.drive.interface_type: - DriveDevice.INTERFACE_TYPE.HDD: + var drive := block_device.get_drive() as DriveDevice + if not drive: + logger.warn("Unable to detect drive to set icon for:", block_device) + drive_icon.texture = hdd_icon + return + + match drive.interface_type(): + DriveDevice.INTERFACE_TYPE_HDD: drive_icon.texture = hdd_icon - DriveDevice.INTERFACE_TYPE.NVME: + DriveDevice.INTERFACE_TYPE_NVME: drive_icon.texture = nvme_icon - DriveDevice.INTERFACE_TYPE.SD: + DriveDevice.INTERFACE_TYPE_SD: drive_icon.texture = sd_icon - DriveDevice.INTERFACE_TYPE.SSD: + DriveDevice.INTERFACE_TYPE_SSD: drive_icon.texture = ssd_icon - DriveDevice.INTERFACE_TYPE.USB: + DriveDevice.INTERFACE_TYPE_USB: drive_icon.texture = usb_icon + DriveDevice.INTERFACE_TYPE_UNKNOWN: + # TODO: Assign a unique texture for this + drive_icon.texture = hdd_icon + + +func _clear_partitions() -> void: + # Clear the current grid of items + var keep_nodes := [partitions_focus_group] + for child in partitions_container.get_children(): + if child in keep_nodes: + continue + partitions_container.remove_child(child) + child.queue_free() ## Populates the partition grid with an item for every PartitionDevice on this BlockDevice func _populate_partitions() -> void: _clear_partitions() var last_focus: FocusGroup - for partition in self.device.partitions: + for partition in self.block_device.get_partitions(): # Ignore loop devices if partition.partition_name.contains("/dev/loop"): diff --git a/core/ui/components/input_icon.gd b/core/ui/components/input_icon.gd index 099ba1d36..1a6d56d1a 100644 --- a/core/ui/components/input_icon.gd +++ b/core/ui/components/input_icon.gd @@ -31,15 +31,15 @@ var internal_children: Array[Node] = [] self.textures = input_icons.parse_path(path, force_mapping, force_type - 1) else: self.textures = input_icons.parse_path(path, force_mapping) - + # If no textures are found, become invisible self.visible = !self.textures.is_empty() - + # Remove old children for child in internal_children.duplicate(): _remove_internal_child(child) internal_children.clear() - + # Add new children var i := 0 for texture in self.textures: @@ -123,8 +123,7 @@ func _on_input_type_changed(input_type: InputIconManager.InputType): (show_only == 2 and input_type == InputIconManager.InputType.GAMEPAD): self.visible = true self.path = path - var width := self.max_width - self.max_width = width + self.max_width = max_width else: self.visible = false diff --git a/core/ui/components/input_texture_rect.gd b/core/ui/components/input_texture_rect.gd deleted file mode 100644 index c7e48c87d..000000000 --- a/core/ui/components/input_texture_rect.gd +++ /dev/null @@ -1,53 +0,0 @@ -@tool -extends TextureRect -class_name InputTextureRect - - -@export var path : String = "": - set(_path): - path = _path - if is_inside_tree(): - if force_type > 0: - texture = ControllerIcons.parse_path(path, force_type - 1) - else: - texture = ControllerIcons.parse_path(path) - -@export_enum("Both", "Keyboard/Mouse", "Controller") var show_only : int = 0: - set(_show_only): - show_only = _show_only - _on_input_type_changed(ControllerIcons._last_input_type) - -@export_enum("None", "Keyboard/Mouse", "Controller") var force_type : int = 0: - set(_force_type): - force_type = _force_type - _on_input_type_changed(ControllerIcons._last_input_type) - -@export var max_width : int = 40: - set(_max_width): - max_width = _max_width - if is_inside_tree(): - if max_width < 0: - expand_mode = TextureRect.EXPAND_KEEP_SIZE - else: - expand_mode = TextureRect.EXPAND_IGNORE_SIZE - custom_minimum_size.x = max_width - if texture: - custom_minimum_size.y = texture.get_height() * max_width / texture.get_width() - else: - custom_minimum_size.y = custom_minimum_size.x - - -func _ready(): - ControllerIcons.input_type_changed.connect(_on_input_type_changed) - self.path = path - self.max_width = max_width - - -func _on_input_type_changed(input_type): - if show_only == 0 or \ - (show_only == 1 and input_type == ControllerIcons.InputType.KEYBOARD_MOUSE) or \ - (show_only == 2 and input_type == ControllerIcons.InputType.CONTROLLER): - visible = true - self.path = path - else: - visible = false diff --git a/core/ui/components/loading02.gd b/core/ui/components/loading02.gd index 8d0ce9ebb..c93369ddf 100644 --- a/core/ui/components/loading02.gd +++ b/core/ui/components/loading02.gd @@ -7,15 +7,19 @@ extends Control # Called when the node enters the scene tree for the first time. func _ready() -> void: - if visible: + if is_visible_in_tree(): + set_process(true) animation_player.play("play") + else: + set_process(false) + animation_player.stop() visibility_changed.connect(_on_visibility_changed) func _on_visibility_changed() -> void: if not animation_player: return - if visible: + if is_visible_in_tree(): set_process(true) animation_player.play("play") return @@ -26,5 +30,9 @@ func _on_visibility_changed() -> void: # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(_delta: float) -> void: var sprite_scale := 1 / (sprite.texture.get_height() / size.y) - sprite.offset = size / 2 / sprite_scale - sprite.scale = Vector2(sprite_scale, sprite_scale) + var new_offset := size / 2 / sprite_scale + if new_offset != sprite.offset: + sprite.offset = new_offset + var new_scale := Vector2(sprite_scale, sprite_scale) + if new_scale != sprite.scale: + sprite.scale = new_scale diff --git a/core/ui/components/loading02.tscn b/core/ui/components/loading02.tscn index 4f1019b86..995fcecc3 100644 --- a/core/ui/components/loading02.tscn +++ b/core/ui/components/loading02.tscn @@ -45,7 +45,7 @@ script = ExtResource("1_1u2cn") texture = ExtResource("1_jaecb") offset = Vector2(128, 128) hframes = 43 -frame = 20 +frame = 31 [node name="AnimationPlayer" type="AnimationPlayer" parent="Sprite2D"] root_node = NodePath("../..") diff --git a/core/ui/components/partition_card.gd b/core/ui/components/partition_card.gd index 81e885cea..91cbf7ec8 100644 --- a/core/ui/components/partition_card.gd +++ b/core/ui/components/partition_card.gd @@ -4,8 +4,8 @@ class_name PartitionCard var steam_disks := load("res://core/systems/disks/steam_removable_media_manager.tres") as SteamRemovableMediaManager var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager -var device: PartitionDevice -var device_path: String +var partition_device: PartitionDevice +var partition_device_path: String signal init_partition(device: PartitionDevice) @@ -20,14 +20,14 @@ var log_level:= Log.LEVEL.INFO ## Performs _ready fucntionality with the given PartitionDevice -func setup(partition_device: PartitionDevice) -> void: +func setup(device: PartitionDevice) -> void: # Setup UDisks2 information - self.device = partition_device - self.device_path = "/dev" + self.device.dbus_path.trim_prefix(steam_disks.BLOCK_PREFIX) - logger = Log.get_logger("PartitionCard|"+self.device_path, log_level) - logger.debug("Setup card for:", self.device.dbus_path) - name_label.text = self.device_path - filesystem_label.text = self.device.block_id_type + self.partition_device = device + self.partition_device_path = "/dev" + self.partition_device.dbus_path.trim_prefix(steam_disks.BLOCK_PREFIX) + logger = Log.get_logger("PartitionCard|"+self.partition_device_path, log_level) + logger.debug("Setup card for:", self.partition_device.dbus_path) + name_label.text = self.partition_device_path + filesystem_label.text = self.partition_device.get_filesystem_type() size_label.text = partition_device.get_readable_size() _populate_mounts() @@ -39,7 +39,7 @@ func setup(partition_device: PartitionDevice) -> void: func _srm_init_drive() -> void: var dialog := get_tree().get_first_node_in_group("dialog") as Dialog - var msg := "INFO: Adding " + device_path + " as a steam path will NOT cause data loss. " + \ + var msg := "INFO: Adding " + self.partition_device_path + " as a steam path will NOT cause data loss. " + \ "The drive will be made available to Steam for installing games. Do you wish to continue?" dialog.open(init_button, msg, "Cancel", "Continue") var cancel := await dialog.choice_selected as bool @@ -50,16 +50,16 @@ func _srm_init_drive() -> void: logger.debug("Init operation blocked.") return - logger.debug("Init Partition", device_path) + logger.debug("Init Partition", self.partition_device_path) _clear_mounts() - var note := Notification.new("Started Initializing Steam Library on " + self.device_path) + var note := Notification.new("Started Initializing Steam Library on " + self.partition_device_path) notification_manager.show(note) init_button.text = "Adding to Steam..." - note.text = "Initializing Complete: " + self.device_path - if await steam_disks.init_steam_lib(device) != OK: - note.text = "Failed to add drive to steam: " + self.device_path + note.text = "Initializing Complete: " + self.partition_device_path + if await steam_disks.init_steam_lib(self.partition_device) != OK: + note.text = "Failed to add drive to steam: " + self.partition_device_path - logger.debug("Init Complete", device_path) + logger.debug("Init Complete", self.partition_device_path) init_button.text = "Add to Steam" notification_manager.show(note) _populate_mounts() @@ -75,8 +75,12 @@ func _clear_mounts() -> void: ## Populates the mount grid with an item for every mount point on this PartitionDevice func _populate_mounts() -> void: _clear_mounts() - logger.debug("Mounts:", str(self.device.fs_mount_points)) - for mount in self.device.fs_mount_points: + var filesystem_device = self.partition_device.get_filesystem() + if not filesystem_device: + return + var mounts: PackedStringArray = filesystem_device.get_mounts() + logger.debug("Mounts:", str(mounts)) + for mount in mounts: var fs_label := Label.new() fs_label.text = mount mounts_container.add_child(fs_label) diff --git a/core/ui/components/plugin_store_card.gd b/core/ui/components/plugin_store_card.gd index 991eab306..31798ed91 100644 --- a/core/ui/components/plugin_store_card.gd +++ b/core/ui/components/plugin_store_card.gd @@ -120,13 +120,16 @@ func _on_update_button() -> void: func _on_unfocus() -> void: # Emit a signal if a non-child node grabs focus - var focus_owner := get_viewport().gui_get_focus_owner() + var viewport := get_viewport() + if not viewport: + return + var focus_owner := viewport.gui_get_focus_owner() if not self.is_ancestor_of(focus_owner): nonchild_focused.emit() return # If a child has focus, listen for focus changes until a non-child has focus - get_viewport().gui_focus_changed.connect(_on_focus_change) + viewport.gui_focus_changed.connect(_on_focus_change) func _on_focus_change(focused: Control) -> void: diff --git a/core/ui/components/progress_dialog.gd b/core/ui/components/progress_dialog.gd index 4a023374b..d4c92c61e 100644 --- a/core/ui/components/progress_dialog.gd +++ b/core/ui/components/progress_dialog.gd @@ -1,4 +1,4 @@ -@icon("res://assets/editor-icons/dialog-2-bold.svg") +@icon("res://assets/editor-icons/solar--dialog-2-bold.svg") @tool extends Control class_name ProgressDialog diff --git a/core/ui/components/toggle.tscn b/core/ui/components/toggle.tscn index 4c68c13f3..55a8fd1f3 100644 --- a/core/ui/components/toggle.tscn +++ b/core/ui/components/toggle.tscn @@ -58,5 +58,4 @@ autowrap_mode = 3 [node name="HSeparator" type="HSeparator" parent="VBoxContainer"] unique_name_in_owner = true -visible = false layout_mode = 2 diff --git a/core/ui/components/volume_indicator.gd b/core/ui/components/volume_indicator.gd index e021fdb6c..c0c396c9d 100644 --- a/core/ui/components/volume_indicator.gd +++ b/core/ui/components/volume_indicator.gd @@ -2,8 +2,8 @@ extends Control var audio_manager := load("res://core/global/audio_manager.tres") as AudioManager var PID: int = OS.get_process_id() -var gamescope := load("res://core/global/gamescope.tres") as Gamescope -var overlay_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI) +var gamescope := load("res://core/systems/gamescope/gamescope.tres") as GamescopeInstance +var overlay_window_id: int @onready var timer := $%Timer as Timer @onready var level_indicator := $%LevelIndicator @@ -11,6 +11,11 @@ var overlay_window_id := gamescope.get_window_id(PID, gamescope.XWAYLAND.OGUI) # Called when the node enters the scene tree for the first time. func _ready() -> void: + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + if xwayland: + var window_ids := xwayland.get_windows_for_pid(PID) + if not window_ids.is_empty(): + overlay_window_id = window_ids[0] audio_manager.volume_changed.connect(_on_volume_changed) timer.timeout.connect(_on_timeout) visible = false @@ -20,9 +25,13 @@ func _on_volume_changed(value: float) -> void: visible = true level_indicator.value = value * 100 timer.start() - gamescope.set_notification(overlay_window_id, 1) + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + if xwayland: + xwayland.set_notification(overlay_window_id, 1) func _on_timeout() -> void: visible = false - gamescope.set_notification(overlay_window_id, 0) + var xwayland := gamescope.get_xwayland(gamescope.XWAYLAND_TYPE_OGUI) + if xwayland: + xwayland.set_notification(overlay_window_id, 0) diff --git a/core/ui/components/wifi_network_tree.gd b/core/ui/components/wifi_network_tree.gd index 1dcfc6ed3..a3a70d484 100644 --- a/core/ui/components/wifi_network_tree.gd +++ b/core/ui/components/wifi_network_tree.gd @@ -1,7 +1,7 @@ extends Tree class_name WifiNetworkTree -const thread := preload("res://core/systems/threading/thread_pool.tres") +var network_manager := load("res://core/systems/network/network_manager.tres") as NetworkManagerInstance ## Emitted when a password is needed to connect. The given callable should be ## invoked by a listener with the password. E.g. callback.call(password) @@ -15,11 +15,17 @@ var logger := Log.get_logger("WifiNetworkTree") # Called when the node enters the scene tree for the first time. func _ready() -> void: - if not NetworkManager.supports_network(): - logger.warn("Network management not supported") - return + hide_root = true + columns = 5 + select_mode = SELECT_ROW + scroll_horizontal_enabled = false + if not network_manager.is_running(): + logger.warn("NetworkManager is not running") + + # Adjust column sizes + set_column_expand(0, false) + set_column_expand(2, false) - thread.start() visibility_changed.connect(refresh_networks) item_activated.connect(_on_wifi_selected) create_item() @@ -28,7 +34,11 @@ func _ready() -> void: func _on_wifi_selected() -> void: var item := get_selected() - var ssid := item.get_text(1) + var ssid := item.get_metadata(0) as String + var has_security := item.get_metadata(2) as bool + if has_security: + challenge_required.emit(network_connect.bind(ssid)) + return network_connect("", ssid) @@ -40,40 +50,100 @@ func network_connect(password: String, ssid: String) -> void: logger.info("Connecting to SSID: " + ssid) connecting = true + # Find the wireless device + var devices := network_manager.get_devices() + var wifi_devices := devices.filter(func(device: NetworkDevice): return device.wireless != null) + if wifi_devices.is_empty(): + logger.warn("No wifi devices found") + connecting = false + return + var device := wifi_devices[0] as NetworkDevice + + # Find the access point to connect to + var access_points := device.wireless.access_points + var valid_access_points := access_points.filter(func(ap: NetworkAccessPoint): return ap.ssid == ssid) + if valid_access_points.is_empty(): + logger.warn("Unable to find access point with SSID:", ssid) + connecting = false + return + var access_point := valid_access_points[0] as NetworkAccessPoint + + # Try to connect to the access point + access_point.connect(device, password) + + # Update the tree item's status var item := get_selected() - var connect_ap := func() -> int: - return NetworkManager.connect_access_point(ssid) item.set_text(4, "Connecting") - var code := await thread.exec(connect_ap) as int - connecting = false - if code == OK: - logger.info("Successfully connected to " + ssid) - refresh_networks() - return + # Wait for the connection to be established + for i in range(60): + logger.info("Device state: " + str(device.state)) + match device.state: + device.NM_DEVICE_STATE_ACTIVATED: + logger.info("Successfully connected to", ssid) + item.set_text(4, "Connected") + connecting = false + refresh_networks() + return + device.NM_DEVICE_STATE_NEED_AUTH: + logger.info("Authentication required") + item.set_text(4, "Authentication required") + connecting = false + challenge_required.emit(network_connect.bind(ssid)) + return + device.NM_DEVICE_STATE_CONFIG: + logger.info("Connecting...") + item.set_text(4, "Connecting") + device.NM_DEVICE_STATE_IP_CONFIG: + logger.info("Acquiring IP address...") + item.set_text(4, "Acquiring IP address") + device.NM_DEVICE_STATE_DEACTIVATING: + logger.info("Deactivation connection...") + item.set_text(4, "Deactivating connection") + device.NM_DEVICE_STATE_DISCONNECTED: + logger.info("Disconnected") + item.set_text(4, "Disconnected") + connecting = false + return + device.NM_DEVICE_STATE_FAILED: + logger.info("Failed to connect") + item.set_text(4, "Failed to connect") + connecting = false + return + # Use a timer to wait between tries + await get_tree().create_timer(0.5).timeout - # If we fail to connect, open a wifi challenge - logger.info("Wifi challenge required") - item.set_text(4, "Unable to connect") - challenge_required.emit(network_connect.bind(ssid)) + connecting = false + logger.info("Timed out waiting to connect") + item.set_text(4, "Failed to connect") ## Refreshes the available wifi networks func refresh_networks() -> void: logger.info("Refreshing wifi networks") refresh_started.emit() - # Fetch all the available access points from NetworkManager + + # Find the wireless device + var devices := network_manager.get_devices() + var wifi_devices := devices.filter(func(device: NetworkDevice): return device.wireless != null) + if wifi_devices.is_empty(): + logger.warn("No wifi devices found") + return + var device := wifi_devices[0] as NetworkDevice + + # Request a scan + device.wireless.request_scan() + await get_tree().create_timer(0.5).timeout + + # Fetch all the available access points var tree := self as Tree var root := tree.get_root() - var access_points: Array[NetworkManager.WifiAP] - var get_aps := func() -> Array[NetworkManager.WifiAP]: - return NetworkManager.get_access_points() - access_points = await thread.exec(get_aps) + var access_points := device.wireless.access_points # Create an array of BSSIDs from our access points var bssids := [] for ap in access_points: - bssids.append(ap.bssid) + bssids.append(ap.ssid) # Look at the current tree items to see if any need to be removed var tree_bssids := {} @@ -84,20 +154,36 @@ func refresh_networks() -> void: continue root.remove_child(item) + var active_ap := device.wireless.active_access_point + # Look at the current APs to see if any tree items need to be created # or updated for ap in access_points: + await get_tree().create_timer(0.1).timeout # help spread out sync data fetching var item: TreeItem - if ap.bssid in tree_bssids: - item = tree_bssids[ap.bssid] + if ap.ssid in tree_bssids: + item = tree_bssids[ap.ssid] else: item = tree.create_item(root) - item.set_metadata(0, ap.bssid) + + # Determine the security used for the access point + var has_security := ap.flags > ap.NM_802_11_AP_SEC_NONE + var security_texture: Texture2D + if has_security: + security_texture = load("res://assets/ui/icons/material-symbols--lock.svg") + + var bitrate = (ap.max_bitrate / 1024.0) + bitrate = int(round(bitrate)) + + item.set_metadata(0, ap.ssid) item.set_icon(0, NetworkManager.get_strength_texture(ap.strength)) item.set_text(1, ap.ssid) - item.set_text(2, ap.security) - item.set_text(3, ap.rate) - if ap.in_use: + item.set_icon(2, security_texture) + item.set_icon_max_width(2, 16) + item.set_expand_right(2, false) + item.set_metadata(2, has_security) + item.set_text(3, str(bitrate) + " Mb/s") + if active_ap and active_ap.ssid == ap.ssid: item.set_text(4, "Connected") else: item.set_text(4, "") diff --git a/docker/Dockerfile b/docker/Dockerfile index 7cdb05b89..d416a8500 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,45 +2,27 @@ FROM archlinux:base-devel LABEL org.opencontainers.image.title="OpenGamepadUI Builder" LABEL org.opencontainers.image.description="Build container for OpenGamepadUI" LABEL org.opencontainers.image.source="https://github.com/ShadowBlip/OpenGamepadUI" -LABEL org.opencontainers.image.version="4.2.2" +LABEL org.opencontainers.image.version="4.3" RUN sed -i '/ParallelDownloads/s/^#//g' /etc/pacman.conf # Install dependencies RUN echo -e "[multilib]\nInclude = /etc/pacman.d/mirrorlist\n" >> /etc/pacman.conf RUN pacman --noconfirm -Syyu && pacman -S --needed --noconfirm \ - scons \ pkgconf \ - gcc \ - gcc-libs \ - libxcursor \ - libxinerama \ - libxi \ - libxrandr \ - mesa \ - glu \ - libglvnd \ - alsa-lib \ make \ - cmake \ unzip \ wget \ git \ - libevdev \ - libxau \ - libxcb \ - libxdmcp \ - libxext \ - libxres \ - libxtst \ squashfs-tools \ rpm-tools \ + rust \ fontconfig \ zip \ jq # Download and install the appropriate version of Godot -ARG GODOT_VERSION=4.2.2 +ARG GODOT_VERSION=4.3 ARG GODOT_RELEASE=stable RUN mkdir /tmp/godot && \ wget -q https://github.com/godotengine/godot-builds/releases/download/${GODOT_VERSION}-${GODOT_RELEASE}/Godot_v${GODOT_VERSION}-${GODOT_RELEASE}_linux.x86_64.zip -O /tmp/godot/godot.zip && \ diff --git a/docker/Dockerfile.addons b/docker/Dockerfile.addons deleted file mode 100644 index 70348ae89..000000000 --- a/docker/Dockerfile.addons +++ /dev/null @@ -1,33 +0,0 @@ -FROM ubuntu:22.04 - -# Install GDExtension dependencies -ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update && apt-get install -y \ - build-essential \ - python3-pip \ - pkg-config \ - libx11-dev \ - libxcursor-dev \ - libxinerama-dev \ - libgl1-mesa-dev \ - libglu-dev \ - libasound2-dev \ - libpulse-dev \ - libudev-dev \ - libxi-dev \ - libxrandr-dev \ - binutils \ - git - -# Install scons from pypi -RUN pip3 install scons - -# Install DBus dependencies -RUN apt-get install -y \ - libdbus-1-dev \ - libdbus-c++-dev - -# Install Xlib dependencies -RUN apt-get install -y \ - libxres-dev \ - libxtst-dev diff --git a/entrypoint.gd b/entrypoint.gd index aab3f90d3..b244c3d43 100644 --- a/entrypoint.gd +++ b/entrypoint.gd @@ -9,6 +9,10 @@ var args := OS.get_cmdline_args() var child_pid := -1 var logger := Log.get_logger("Entrypoint") +# We don't need this here, except we do. For some reason it won't load properly +# after the entrypoint anymore and I don't care why. Don't touch this. +var settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager +var input_icons := load("res://core/systems/input/input_icon_manager.tres") as InputIconManager # Called when the node enters the scene tree for the first time. func _ready() -> void: diff --git a/gdext/.gdignore b/extensions/.gdignore similarity index 100% rename from gdext/.gdignore rename to extensions/.gdignore diff --git a/extensions/.gitignore b/extensions/.gitignore new file mode 100644 index 000000000..54b02da46 --- /dev/null +++ b/extensions/.gitignore @@ -0,0 +1,2 @@ +/target +*.so diff --git a/extensions/Cargo.lock b/extensions/Cargo.lock new file mode 100644 index 000000000..db4ee3af2 --- /dev/null +++ b/extensions/Cargo.lock @@ -0,0 +1,1934 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "borsh" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "byte-unit" +version = "5.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" +dependencies = [ + "rust_decimal", + "serde", + "utf8-width", +] + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gamescope-x11-client" +version = "0.1.0" +source = "git+https://github.com/ShadowBlip/gamescope-x11-client?branch=main#9526a6e0d684d9530e8705f7f1a53efa401350f3" +dependencies = [ + "log", + "strum", + "strum_macros", + "x11rb", +] + +[[package]] +name = "gdextension-api" +version = "0.2.1" +source = "git+https://github.com/godot-rust/godot4-prebuilt?branch=releases#53fa4a856d93ac01d87deb8f57c6851179dfacec" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gensym" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "uuid", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glam" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" + +[[package]] +name = "godot" +version = "0.2.0" +source = "git+https://github.com/godot-rust/gdext?branch=master#c6b8b0e12414a69eb29ec512a313b4f5817e37de" +dependencies = [ + "godot-core", + "godot-macros", +] + +[[package]] +name = "godot-bindings" +version = "0.2.0" +source = "git+https://github.com/godot-rust/gdext?branch=master#c6b8b0e12414a69eb29ec512a313b4f5817e37de" +dependencies = [ + "gdextension-api", +] + +[[package]] +name = "godot-cell" +version = "0.2.0" +source = "git+https://github.com/godot-rust/gdext?branch=master#c6b8b0e12414a69eb29ec512a313b4f5817e37de" + +[[package]] +name = "godot-codegen" +version = "0.2.0" +source = "git+https://github.com/godot-rust/gdext?branch=master#c6b8b0e12414a69eb29ec512a313b4f5817e37de" +dependencies = [ + "godot-bindings", + "heck 0.5.0", + "nanoserde", + "proc-macro2", + "quote", + "regex", +] + +[[package]] +name = "godot-core" +version = "0.2.0" +source = "git+https://github.com/godot-rust/gdext?branch=master#c6b8b0e12414a69eb29ec512a313b4f5817e37de" +dependencies = [ + "glam", + "godot-bindings", + "godot-cell", + "godot-codegen", + "godot-ffi", +] + +[[package]] +name = "godot-ffi" +version = "0.2.0" +source = "git+https://github.com/godot-rust/gdext?branch=master#c6b8b0e12414a69eb29ec512a313b4f5817e37de" +dependencies = [ + "gensym", + "godot-bindings", + "godot-codegen", + "libc", + "paste", +] + +[[package]] +name = "godot-macros" +version = "0.2.0" +source = "git+https://github.com/godot-rust/gdext?branch=master#c6b8b0e12414a69eb29ec512a313b4f5817e37de" +dependencies = [ + "godot-bindings", + "markdown", + "proc-macro2", + "quote", + "venial", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags", + "futures-core", + "inotify-sys", + "libc", + "tokio", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "keyvalues-parser" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4c8354918309196302015ac9cae43362f1a13d0d5c5539a33b4c2fd2cd6d25" +dependencies = [ + "pest", + "pest_derive", + "thiserror", +] + +[[package]] +name = "libc" +version = "0.2.167" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "markdown" +version = "1.0.0-alpha.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6491e6c702bf7e3b24e769d800746d5f2c06a6c6a2db7992612e0f429029e81" +dependencies = [ + "unicode-id", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "nanoserde" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de9cf844ab1e25a0353525bd74cb889843a6215fa4a0d156fd446f4857a1b99" +dependencies = [ + "nanoserde-derive", +] + +[[package]] +name = "nanoserde-derive" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e943b2c21337b7e3ec6678500687cdc741b7639ad457f234693352075c082204" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opengamepadui-core" +version = "0.1.0" +dependencies = [ + "byte-unit", + "futures-util", + "gamescope-x11-client", + "godot", + "inotify", + "keyvalues-parser", + "log", + "nix", + "once_cell", + "tokio", + "zbus", + "zvariant", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pest" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "pest_meta" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "reaper" +version = "0.1.0" +dependencies = [ + "nix", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.90", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-id" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", +] + +[[package]] +name = "venial" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6816bc32f30bf8dd1b3adb04de8406c7bf187d2f923bd9e4c0b99365d012613f" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.90", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.90", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] diff --git a/extensions/Cargo.toml b/extensions/Cargo.toml new file mode 100644 index 000000000..2d2cbba14 --- /dev/null +++ b/extensions/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["core", "reaper"] +resolver = "2" diff --git a/extensions/Makefile b/extensions/Makefile new file mode 100644 index 000000000..6f355153f --- /dev/null +++ b/extensions/Makefile @@ -0,0 +1,50 @@ +PREFIX ?= addons +EXT_NAME := $(shell grep 'name =' core/Cargo.toml | head -n 1 | cut -d'"' -f2) +LIB_NAME := $(shell grep 'name =' core/Cargo.toml | head -n 1 | cut -d'"' -f2 | sed 's/-/_/g') +ALL_RS := $(shell find ./ -name '*.rs') +ADDON_PATH := ../addons/core +RELEASE_TARGET := $(ADDON_PATH)/bin/lib$(EXT_NAME).linux.template_release.x86_64.so +DEBUG_TARGET := $(ADDON_PATH)/bin/lib$(EXT_NAME).linux.template_debug.x86_64.so + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + + +.PHONY: build +build: $(RELEASE_TARGET) $(DEBUG_TARGET) ## Build release and debug binaries + + +.PHONY: clean +clean: ## Clean build artifacts + rm $(RELEASE_TARGET) $(DEBUG_TARGET) + rm -rf target + + +.PHONY: release +release: $(RELEASE_TARGET) ## Build release binary +$(RELEASE_TARGET): $(ALL_RS) + cargo build --release + mkdir -p $(@D) + cp target/release/lib$(LIB_NAME).so $@ + + +.PHONY: debug +debug: $(DEBUG_TARGET) ## Build binary with debug symbols +$(DEBUG_TARGET): $(ALL_RS) + cargo build + mkdir -p $(@D) + cp target/debug/lib$(LIB_NAME).so $@ diff --git a/extensions/core/.gitignore b/extensions/core/.gitignore new file mode 100644 index 000000000..54b02da46 --- /dev/null +++ b/extensions/core/.gitignore @@ -0,0 +1,2 @@ +/target +*.so diff --git a/extensions/core/Cargo.toml b/extensions/core/Cargo.toml new file mode 100644 index 000000000..b8123ac73 --- /dev/null +++ b/extensions/core/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "opengamepadui-core" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] # Compile this crate to a dynamic C library. + +[dependencies] +futures-util = "0.3.30" +godot = { git = "https://github.com/godot-rust/gdext", branch = "master", features = [ + "experimental-threads", + "register-docs", +] } +nix = { version = "0.29.0", features = ["term", "process"] } +once_cell = "1.20.1" +tokio = { version = "1.39.3", features = ["full"] } +zbus = "4.4.0" +zvariant = "4.2.0" +gamescope-x11-client = { git = "https://github.com/ShadowBlip/gamescope-x11-client", branch = "main" } +inotify = "0.11.0" +byte-unit = "5.1.4" +log = "0.4.22" +keyvalues-parser = "0.2.0" diff --git a/extensions/core/src/bluetooth.rs b/extensions/core/src/bluetooth.rs new file mode 100644 index 000000000..5447862b1 --- /dev/null +++ b/extensions/core/src/bluetooth.rs @@ -0,0 +1 @@ +pub mod bluez; diff --git a/extensions/core/src/bluetooth/bluez.rs b/extensions/core/src/bluetooth/bluez.rs new file mode 100644 index 000000000..9cab950d3 --- /dev/null +++ b/extensions/core/src/bluetooth/bluez.rs @@ -0,0 +1,386 @@ +pub mod adapter; +pub mod device; + +use std::{ + collections::HashMap, + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, + time::Duration, +}; + +use adapter::BluetoothAdapter; +use device::BluetoothDevice; +use futures_util::stream::StreamExt; +use godot::{classes::Engine, obj::WithBaseField, prelude::*}; +use zbus::fdo::ObjectManagerProxy; +use zbus::{fdo::ManagedObjects, names::BusName}; + +use crate::{dbus::RunError, get_dbus_system, get_dbus_system_blocking, RUNTIME}; + +pub const BLUEZ_BUS: &str = "org.bluez"; +const BLUEZ_MANAGER_PATH: &str = "/"; + +/// Supported Bluez DBus objects +#[derive(Debug)] +enum ObjectType { + Unknown, + Adapter, + Device, +} + +impl ObjectType { + /// Returns the object type from the list of implemented interfaces + fn from_ifaces(ifaces: Vec) -> Self { + if ifaces.contains(&"org.bluez.Device1".to_string()) { + Self::Device + } else if ifaces.contains(&"org.bluez.Adapter1".to_string()) { + Self::Adapter + } else { + Self::Unknown + } + } +} + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Started, + Stopped, + ObjectAdded { path: String, ifaces: Vec }, + ObjectRemoved { path: String, ifaces: Vec }, +} + +#[derive(GodotClass)] +#[class(base=Resource)] +pub struct BluezInstance { + base: Base, + rx: Receiver, + conn: Option, + adapters: HashMap>, + devices: HashMap>, +} + +#[godot_api] +impl BluezInstance { + /// Emitted when Bluez is detected as running + #[signal] + fn started(); + + /// Emitted when Bluez is detected as stopped + #[signal] + fn stopped(); + + /// Emitted when a new bluetooth adapter is discovered + #[signal] + fn adapter_added(device: Gd); + + /// Emitted when a bluetooth adapter is removed + #[signal] + fn adapter_removed(path: GString); + + /// Emitted when a new bluetooth device is discovered + #[signal] + fn device_added(device: Gd); + + /// Emitted when a bluetooth device is removed + #[signal] + fn device_removed(path: GString); + + /// Returns true if the Bluez service is currently running + #[func] + fn is_running(&self) -> bool { + let Some(conn) = self.conn.as_ref() else { + return false; + }; + let bus = BusName::from_static_str(BLUEZ_BUS).unwrap(); + let dbus = zbus::blocking::fdo::DBusProxy::new(conn).ok(); + let Some(dbus) = dbus else { + return false; + }; + dbus.name_has_owner(bus.clone()).unwrap_or_default() + } + + /// Get managed objects + fn get_managed_objects(&self) -> Result { + let Some(conn) = self.conn.as_ref() else { + return Err(zbus::fdo::Error::Disconnected( + "No DBus connection found".into(), + )); + }; + + let bus = BusName::from_static_str(BLUEZ_BUS).unwrap(); + let object_manager = zbus::blocking::fdo::ObjectManagerProxy::builder(conn) + .destination(bus) + .ok() + .and_then(|builder| builder.path(BLUEZ_MANAGER_PATH).ok()) + .and_then(|builder| builder.build().ok()); + let Some(object_manager) = object_manager else { + return Ok(ManagedObjects::new()); + }; + + object_manager.get_managed_objects() + } + + /// Return a list of currently discovered bluetooth adapters + #[func] + fn get_adapters(&self) -> Array> { + let mut adapters = array![]; + for adapter in self.adapters.values() { + adapters.push(adapter); + } + + adapters + } + + /// Return a list of currently discovered devices + #[func] + fn get_discovered_devices(&self) -> Array> { + let mut devices = array![]; + for device in self.devices.values() { + devices.push(device); + } + + devices + } + + /// Process Bluez signals and emit them as Godot signals. This method + /// should be called every frame in the "_process" loop of a node. + #[func] + fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + + // Process signals on child objects + for adapter in self.adapters.values_mut() { + adapter.bind_mut().process(); + } + for device in self.devices.values_mut() { + device.bind_mut().process(); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Started => { + self.base_mut().emit_signal("started", &[]); + } + Signal::Stopped => { + self.base_mut().emit_signal("stopped", &[]); + } + Signal::ObjectAdded { path, ifaces } => { + let obj_type = ObjectType::from_ifaces(ifaces); + match obj_type { + ObjectType::Unknown => (), + ObjectType::Adapter => { + let adapter = BluetoothAdapter::new(path.as_str()); + self.adapters.insert(path, adapter.clone()); + self.base_mut() + .emit_signal("adapter_added", &[adapter.to_variant()]); + } + ObjectType::Device => { + let device = BluetoothDevice::new(path.as_str()); + self.devices.insert(path, device.clone()); + self.base_mut() + .emit_signal("device_added", &[device.to_variant()]); + } + } + } + Signal::ObjectRemoved { path, ifaces } => { + let obj_type = ObjectType::from_ifaces(ifaces); + match obj_type { + ObjectType::Unknown => (), + ObjectType::Adapter => { + self.adapters.remove(&path); + self.base_mut() + .emit_signal("adapter_removed", &[path.to_variant()]); + } + ObjectType::Device => { + self.devices.remove(&path); + self.base_mut() + .emit_signal("device_removed", &[path.to_variant()]); + } + } + } + } + } +} + +#[godot_api] +impl IResource for BluezInstance { + /// Called upon object initialization in the engine + fn init(base: Base) -> Self { + log::debug!("Initializing Bluez instance"); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + let conn = get_dbus_system_blocking().ok(); + + // Don't run in the editor + let engine = Engine::singleton(); + if engine.is_editor_hint() { + return Self { + base, + rx, + conn, + adapters: Default::default(), + devices: Default::default(), + }; + } + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx).await { + log::error!("Failed to run Bluez task: ${e:?}"); + } + }); + + // Create a new Bluez instance + let mut instance = Self { + base, + rx, + conn, + adapters: HashMap::new(), + devices: HashMap::new(), + }; + + // Perform initial object discovery + let mut adapters = HashMap::new(); + let mut devices = HashMap::new(); + let objects = instance.get_managed_objects().unwrap_or_default(); + for (path, ifaces) in objects.into_iter() { + let path = path.to_string(); + let ifaces: Vec = ifaces.into_keys().map(|v| v.to_string()).collect(); + let obj_type = ObjectType::from_ifaces(ifaces); + + match obj_type { + ObjectType::Unknown => (), + ObjectType::Adapter => { + let adapter = BluetoothAdapter::new(path.as_str()); + adapters.insert(path, adapter); + } + ObjectType::Device => { + let device = BluetoothDevice::new(path.as_str()); + devices.insert(path, device); + } + } + } + + // Update the discovered objects + instance.adapters = adapters; + instance.devices = devices; + + instance + } +} + +/// Runs Bluez tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning Bluez tasks"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Spawn a task to listen for Bluez start/stop + let dbus_conn = conn.clone(); + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + let bus = BusName::from_static_str(BLUEZ_BUS).unwrap(); + let mut is_running = { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + return; + }; + dbus.name_has_owner(bus.clone()).await.unwrap_or_default() + }; + + loop { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + break; + }; + let running = dbus.name_has_owner(bus.clone()).await.unwrap_or_default(); + if running != is_running { + let signal = if running { + Signal::Started + } else { + Signal::Stopped + }; + if signals_tx.send(signal).is_err() { + break; + } + } + is_running = running; + tokio::time::sleep(Duration::from_secs(5)).await; + } + }); + + // Get a proxy instance to ObjectManager + let bus = BusName::from_static_str(BLUEZ_BUS).unwrap(); + let object_manager: ObjectManagerProxy = ObjectManagerProxy::builder(&conn) + .destination(bus)? + .path(BLUEZ_MANAGER_PATH)? + .build() + .await?; + + // Spawn a task to listen for objects added + let mut ifaces_added = object_manager.receive_interfaces_added().await?; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + while let Some(signal) = ifaces_added.next().await { + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + + let path = args.object_path.to_string(); + let ifaces = args + .interfaces_and_properties + .keys() + .map(|v| v.to_string()) + .collect(); + let signal = Signal::ObjectAdded { path, ifaces }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + // Spawn a task to listen for objects removed + let mut ifaces_removed = object_manager.receive_interfaces_removed().await?; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + while let Some(signal) = ifaces_removed.next().await { + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + + let path = args.object_path.to_string(); + let ifaces = args.interfaces.iter().map(|v| v.to_string()).collect(); + let signal = Signal::ObjectRemoved { path, ifaces }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/bluetooth/bluez/adapter.rs b/extensions/core/src/bluetooth/bluez/adapter.rs new file mode 100644 index 000000000..87babcedc --- /dev/null +++ b/extensions/core/src/bluetooth/bluez/adapter.rs @@ -0,0 +1,570 @@ +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use futures_util::StreamExt; +use godot::obj::WithBaseField; +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; +use zvariant::ObjectPath; + +use crate::dbus::bluez::adapter1::{Adapter1Proxy, Adapter1ProxyBlocking}; +use crate::dbus::RunError; +use crate::{get_dbus_system, get_dbus_system_blocking, RUNTIME}; + +use super::device::BluetoothDevice; +use super::BLUEZ_BUS; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Discoverable { value: bool }, + Discovering { value: bool }, + Pairable { value: bool }, + Powered { value: bool }, + PowerState { value: String }, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct BluetoothAdapter { + base: Base, + rx: Receiver, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + #[allow(dead_code)] + #[var(get = get_address)] + address: GString, + #[allow(dead_code)] + #[var(get = get_address_type)] + address_type: GString, + #[allow(dead_code)] + #[var(get = get_alias, set = set_alias)] + alias: GString, + #[allow(dead_code)] + #[var(get = get_class)] + class: u32, + #[allow(dead_code)] + #[var(get = get_discoverable, set = set_discoverable)] + discoverable: bool, + #[allow(dead_code)] + #[var(get = get_discoverable_timeout, set = set_discoverable_timeout)] + discoverable_timeout: u32, + #[allow(dead_code)] + #[var(get = get_discovering)] + discovering: bool, + #[allow(dead_code)] + #[var(get = get_experimental_features)] + experimental_features: PackedStringArray, + #[allow(dead_code)] + #[var(get = get_manufacturer)] + manufacturer: u16, + #[allow(dead_code)] + #[var(get = get_modalias)] + modalias: GString, + #[allow(dead_code)] + #[var(get = get_name)] + name: GString, + #[allow(dead_code)] + #[var(get = get_pairable, set = set_pairable)] + pairable: bool, + #[allow(dead_code)] + #[var(get = get_pairable_timeout, set = set_pairable_timeout)] + pairable_timeout: u32, + #[allow(dead_code)] + #[var(get = get_power_state)] + power_state: GString, + #[allow(dead_code)] + #[var(get = get_powered, set = set_powered)] + powered: bool, + #[allow(dead_code)] + #[var(get = get_roles)] + roles: PackedStringArray, + #[allow(dead_code)] + #[var(get = get_uuids)] + uuids: PackedStringArray, + #[allow(dead_code)] + #[var(get = get_version)] + version: u8, +} + +#[godot_api] +impl BluetoothAdapter { + #[signal] + fn discoverable_changed(value: bool); + + #[signal] + fn discovering_changed(value: bool); + + #[signal] + fn pairable_changed(value: bool); + + #[signal] + fn powered_changed(value: bool); + + #[signal] + fn power_state_changed(value: GString); + + /// Create a new [BluetoothAdapter] with the given DBus path + pub fn from_path(path: GString) -> Gd { + // Create a channel to communicate with the signals task + log::debug!("BluetoothAdapter created with path: {path}"); + let (tx, rx) = channel(); + let dbus_path = path.clone().into(); + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx, dbus_path).await { + log::error!("Failed to run DBusDevice task: ${e:?}"); + } + }); + + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + rx, + conn, + dbus_path: path, + address: Default::default(), + address_type: Default::default(), + alias: Default::default(), + class: Default::default(), + discoverable: Default::default(), + discoverable_timeout: Default::default(), + discovering: Default::default(), + experimental_features: Default::default(), + manufacturer: Default::default(), + modalias: Default::default(), + name: Default::default(), + pairable: Default::default(), + pairable_timeout: Default::default(), + power_state: Default::default(), + powered: Default::default(), + roles: Default::default(), + uuids: Default::default(), + version: Default::default(), + } + }) + } + + /// Return a proxy instance to the adapter + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + Adapter1ProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [BluetoothAdapter] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{BLUEZ_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = BluetoothAdapter::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = BluetoothAdapter::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Returns the DBus path to the [BluetoothAdapter] + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + #[func] + pub fn get_address(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.address().unwrap_or_default().into() + } + + #[func] + pub fn get_address_type(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.address_type().unwrap_or_default().into() + } + + #[func] + pub fn get_alias(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.alias().unwrap_or_default().into() + } + + #[func] + pub fn set_alias(&self, value: GString) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy + .set_alias(value.to_string().as_str()) + .unwrap_or_default() + } + + #[func] + pub fn get_class(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.class().unwrap_or_default() + } + + #[func] + pub fn get_discoverable(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.discoverable().unwrap_or_default() + } + + #[func] + pub fn set_discoverable(&self, value: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_discoverable(value).unwrap_or_default() + } + + #[func] + pub fn get_discoverable_timeout(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.discoverable_timeout().unwrap_or_default() + } + + #[func] + pub fn set_discoverable_timeout(&self, value: u32) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_discoverable_timeout(value).unwrap_or_default() + } + + #[func] + pub fn get_discovering(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.discovering().unwrap_or_default() + } + + #[func] + pub fn get_experimental_features(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let values: Vec = proxy + .experimental_features() + .unwrap_or_default() + .into_iter() + .map(|v| v.to_godot()) + .collect(); + values.into() + } + + #[func] + pub fn get_manufacturer(&self) -> u16 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.manufacturer().unwrap_or_default() + } + + #[func] + pub fn get_modalias(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.modalias().unwrap_or_default().into() + } + + #[func] + pub fn get_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.name().unwrap_or_default().into() + } + + #[func] + pub fn get_pairable(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.pairable().unwrap_or_default() + } + + #[func] + pub fn set_pairable(&self, value: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_pairable(value).unwrap_or_default() + } + + #[func] + pub fn get_pairable_timeout(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.pairable_timeout().unwrap_or_default() + } + + #[func] + pub fn set_pairable_timeout(&self, value: u32) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_pairable_timeout(value).unwrap_or_default() + } + + #[func] + pub fn get_power_state(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.power_state().unwrap_or_default().into() + } + + #[func] + pub fn get_powered(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.powered().unwrap_or_default() + } + + #[func] + pub fn set_powered(&self, value: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_powered(value).unwrap_or_default() + } + + #[func] + pub fn get_roles(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let values: Vec = proxy + .roles() + .unwrap_or_default() + .into_iter() + .map(|v| v.to_godot()) + .collect(); + values.into() + } + + #[func] + pub fn get_uuids(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let values: Vec = proxy + .uuids() + .unwrap_or_default() + .into_iter() + .map(|v| v.to_godot()) + .collect(); + values.into() + } + + #[func] + pub fn get_version(&self) -> u8 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.version().unwrap_or_default() + } + + #[func] + pub fn get_discovery_filters(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let filters: Vec = proxy + .get_discovery_filters() + .unwrap_or_default() + .into_iter() + .map(|v| v.to_godot()) + .collect(); + filters.into() + } + + #[func] + pub fn remove_device(&self, device: Gd) { + let Some(proxy) = self.get_proxy() else { + return; + }; + let path = device.bind().get_dbus_path().to_string(); + let path = ObjectPath::try_from(path).unwrap_or_default(); + proxy.remove_device(&path).unwrap_or_default() + } + + #[func] + pub fn start_discovery(&self) { + let Some(proxy) = self.get_proxy() else { + return; + }; + proxy.start_discovery().unwrap_or_default() + } + + #[func] + pub fn stop_discovery(&self) { + let Some(proxy) = self.get_proxy() else { + return; + }; + proxy.stop_discovery().unwrap_or_default() + } + + /// Dispatches signals + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Discoverable { value } => { + self.base_mut() + .emit_signal("discoverable_changed", &[value.to_variant()]); + } + Signal::Discovering { value } => { + self.base_mut() + .emit_signal("discovering_changed", &[value.to_variant()]); + } + Signal::Pairable { value } => { + self.base_mut() + .emit_signal("pairable_changed", &[value.to_variant()]); + } + Signal::Powered { value } => { + self.base_mut() + .emit_signal("powered_changed", &[value.to_variant()]); + } + Signal::PowerState { value } => { + self.base_mut() + .emit_signal("power_state_changed", &[value.to_variant()]); + } + } + } +} + +impl Drop for BluetoothAdapter { + fn drop(&mut self) { + log::trace!("BluetoothAdapter '{}' is being destroyed!", self.dbus_path); + } +} + +/// Run the signals task +async fn run(tx: Sender, path: String) -> Result<(), RunError> { + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + let proxy = Adapter1Proxy::builder(&conn).path(path)?.build().await?; + + let signals_tx = tx.clone(); + let mut events = proxy.receive_discoverable_changed().await; + RUNTIME.spawn(async move { + while let Some(event) = events.next().await { + let value = event.get().await.unwrap_or_default(); + let signal = Signal::Discoverable { value }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_discovering_changed().await; + RUNTIME.spawn(async move { + while let Some(event) = events.next().await { + let value = event.get().await.unwrap_or_default(); + let signal = Signal::Discovering { value }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_pairable_changed().await; + RUNTIME.spawn(async move { + while let Some(event) = events.next().await { + let value = event.get().await.unwrap_or_default(); + let signal = Signal::Pairable { value }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_powered_changed().await; + RUNTIME.spawn(async move { + while let Some(event) = events.next().await { + let value = event.get().await.unwrap_or_default(); + let signal = Signal::Powered { value }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_power_state_changed().await; + RUNTIME.spawn(async move { + while let Some(event) = events.next().await { + let value = event.get().await.unwrap_or_default(); + let signal = Signal::PowerState { value }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/bluetooth/bluez/device.rs b/extensions/core/src/bluetooth/bluez/device.rs new file mode 100644 index 000000000..f317588ad --- /dev/null +++ b/extensions/core/src/bluetooth/bluez/device.rs @@ -0,0 +1,529 @@ +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use futures_util::StreamExt; +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::bluez::device1::{Device1Proxy, Device1ProxyBlocking}; +use crate::dbus::RunError; +use crate::{get_dbus_system, get_dbus_system_blocking, RUNTIME}; + +use super::BLUEZ_BUS; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Updated, + ConnectedChanged { value: bool }, + PairedChanged { value: bool }, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct BluetoothDevice { + base: Base, + rx: Receiver, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + #[allow(dead_code)] + #[var(get = get_adapter)] + adapter: GString, + #[allow(dead_code)] + #[var(get = get_address)] + address: GString, + #[allow(dead_code)] + #[var(get = get_address_type)] + address_type: GString, + #[allow(dead_code)] + #[var(get = get_alias, set = set_alias)] + alias: GString, + #[allow(dead_code)] + #[var(get = get_appearance)] + appearance: u16, + #[allow(dead_code)] + #[var(get = get_blocked, set = set_blocked)] + blocked: bool, + #[allow(dead_code)] + #[var(get = get_bonded)] + bonded: bool, + #[allow(dead_code)] + #[var(get = get_class)] + class: u32, + #[allow(dead_code)] + #[var(get = get_connected)] + connected: bool, + #[allow(dead_code)] + #[var(get = get_icon)] + icon: GString, + #[allow(dead_code)] + #[var(get = get_legacy_pairing)] + legacy_pairing: bool, + #[allow(dead_code)] + #[var(get = get_modalias)] + modalias: GString, + #[allow(dead_code)] + #[var(get = get_name)] + name: GString, + #[allow(dead_code)] + #[var(get = get_paired)] + paired: bool, + #[allow(dead_code)] + #[var(get = get_rssi)] + rssi: i16, + #[allow(dead_code)] + #[var(get = get_services_resolved)] + services_resolved: bool, + #[allow(dead_code)] + #[var(get = get_trusted, set = set_trusted)] + trusted: bool, + #[allow(dead_code)] + #[var(get = get_tx_power)] + tx_power: i16, + #[allow(dead_code)] + #[var(get = get_uuids)] + uuids: PackedStringArray, + #[allow(dead_code)] + #[var(get = get_wake_allowed, set = set_wake_allowed)] + wake_allowed: bool, +} + +#[godot_api] +impl BluetoothDevice { + #[signal] + fn updated(); + + #[signal] + fn connected_changed(value: bool); + + #[signal] + fn paired_changed(value: bool); + + /// Create a new [BluetoothDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + // Create a channel to communicate with the signals task + log::debug!("BluetoothDevice created with path: {path}"); + let (tx, rx) = channel(); + let dbus_path = path.clone().into(); + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx, dbus_path).await { + log::error!("Failed to run BluetoothDevice task: ${e:?}"); + } + }); + + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + rx, + conn, + dbus_path: path, + adapter: Default::default(), + address: Default::default(), + address_type: Default::default(), + alias: Default::default(), + appearance: Default::default(), + blocked: Default::default(), + bonded: Default::default(), + class: Default::default(), + connected: Default::default(), + icon: Default::default(), + legacy_pairing: Default::default(), + modalias: Default::default(), + name: Default::default(), + paired: Default::default(), + rssi: Default::default(), + services_resolved: Default::default(), + trusted: Default::default(), + tx_power: Default::default(), + uuids: Default::default(), + wake_allowed: Default::default(), + } + }) + } + + /// Return a proxy instance to the device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + Device1ProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [BluetoothDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{BLUEZ_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = BluetoothDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = BluetoothDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + #[func] + pub fn cancel_pairing(&self) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.cancel_pairing().unwrap_or_default() + } + + #[func] + pub fn connect_to(&self) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.connect().unwrap_or_default() + } + + #[func] + pub fn connect_to_profile(&self, uuid: GString) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let uuid = uuid.to_string(); + proxy.connect_profile(uuid.as_str()).unwrap_or_default() + } + + #[func] + pub fn disconnect_from(&self) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.disconnect().unwrap_or_default() + } + + #[func] + pub fn disconnect_from_profile(&self, uuid: GString) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let uuid = uuid.to_string(); + proxy.disconnect_profile(uuid.as_str()).unwrap_or_default() + } + + #[func] + pub fn pair(&self) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.pair().unwrap_or_default() + } + + #[func] + pub fn get_wake_allowed(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.wake_allowed().unwrap_or_default() + } + + #[func] + pub fn set_wake_allowed(&self, allowed: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_wake_allowed(allowed).unwrap_or_default() + } + + #[func] + pub fn get_uuids(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let values: Vec = proxy + .uuids() + .unwrap_or_default() + .into_iter() + .map(|v| v.to_godot()) + .collect(); + values.into() + } + + #[func] + pub fn get_tx_power(&self) -> i16 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.tx_power().unwrap_or_default() + } + + #[func] + pub fn get_trusted(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.trusted().unwrap_or_default() + } + + #[func] + pub fn set_trusted(&self, value: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_trusted(value).unwrap_or_default() + } + + #[func] + pub fn get_services_resolved(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.services_resolved().unwrap_or_default() + } + + #[func] + pub fn get_rssi(&self) -> i16 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.rssi().unwrap_or_default() + } + + #[func] + pub fn get_paired(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.paired().unwrap_or_default() + } + + #[func] + pub fn get_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.name().unwrap_or_default().into() + } + + #[func] + pub fn get_modalias(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.modalias().unwrap_or_default().into() + } + + #[func] + pub fn get_legacy_pairing(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.legacy_pairing().unwrap_or_default() + } + + #[func] + pub fn get_icon(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.icon().unwrap_or_default().into() + } + + #[func] + pub fn get_connected(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.connected().unwrap_or_default() + } + + #[func] + pub fn get_class(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.class().unwrap_or_default() + } + + #[func] + pub fn get_bonded(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.bonded().unwrap_or_default() + } + + #[func] + pub fn get_blocked(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.blocked().unwrap_or_default() + } + + #[func] + pub fn set_blocked(&self, value: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_blocked(value).unwrap_or_default() + } + + #[func] + pub fn get_appearance(&self) -> u16 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.appearance().unwrap_or_default() + } + + #[func] + pub fn get_alias(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.alias().unwrap_or_default().into() + } + + #[func] + pub fn set_alias(&self, value: GString) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy + .set_alias(value.to_string().as_str()) + .unwrap_or_default() + } + + #[func] + pub fn get_address_type(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.address_type().unwrap_or_default().into() + } + + #[func] + pub fn get_address(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.address().unwrap_or_default().into() + } + + #[func] + pub fn get_adapter(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.adapter().unwrap_or_default().to_string().into() + } + + /// Dispatches signals + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + log::trace!("Got signal: {signal:?}"); + match signal { + Signal::Updated => { + self.base_mut().emit_signal("updated", &[]); + } + Signal::ConnectedChanged { value } => { + self.base_mut() + .emit_signal("connected_changed", &[value.to_variant()]); + } + Signal::PairedChanged { value } => { + self.base_mut() + .emit_signal("paired_changed", &[value.to_variant()]); + } + } + } +} + +impl Drop for BluetoothDevice { + fn drop(&mut self) { + log::trace!("BluetoothDevice '{}' is being destroyed!", self.dbus_path); + } +} + +/// Run the signals task +async fn run(tx: Sender, path: String) -> Result<(), RunError> { + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + let proxy = Device1Proxy::builder(&conn).path(path)?.build().await?; + + let signals_tx = tx.clone(); + let mut events = proxy.receive_connected_changed().await; + RUNTIME.spawn(async move { + while let Some(event) = events.next().await { + let value = event.get().await.unwrap_or_default(); + let signal = Signal::ConnectedChanged { value }; + if signals_tx.send(signal).is_err() { + break; + } + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_paired_changed().await; + RUNTIME.spawn(async move { + while let Some(event) = events.next().await { + let value = event.get().await.unwrap_or_default(); + let signal = Signal::PairedChanged { value }; + if signals_tx.send(signal).is_err() { + break; + } + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/dbus.rs b/extensions/core/src/dbus.rs new file mode 100644 index 000000000..900750538 --- /dev/null +++ b/extensions/core/src/dbus.rs @@ -0,0 +1,164 @@ +use godot::prelude::*; +use zvariant::NoneValue; + +pub mod bluez; +pub mod inputplumber; +pub mod networkmanager; +pub mod powerstation; +pub mod udisks2; +pub mod upower; + +/// Possible DBus runtime errors +#[derive(Debug)] +pub enum RunError { + Zbus(zbus::Error), + ZbusFdo(zbus::fdo::Error), +} + +impl From for RunError { + fn from(value: zbus::Error) -> Self { + RunError::Zbus(value) + } +} + +impl From for RunError { + fn from(value: zbus::fdo::Error) -> Self { + RunError::ZbusFdo(value) + } +} + +/// Interface for converting DBus types -> Godot types +pub trait GodotVariant { + fn as_godot_variant(&self) -> Option; +} + +impl GodotVariant for zvariant::OwnedValue { + /// Convert the DBus variant type into a Godot variant type + fn as_godot_variant(&self) -> Option { + let value = zvariant::Value::try_from(self).ok()?; + value.as_godot_variant() + } +} + +impl<'a> GodotVariant for zvariant::Value<'a> { + /// Convert the DBus variant type into a Godot variant type + fn as_godot_variant(&self) -> Option { + match self { + zvariant::Value::U8(value) => Some(value.to_variant()), + zvariant::Value::Bool(value) => Some(value.to_variant()), + zvariant::Value::I16(value) => Some(value.to_variant()), + zvariant::Value::U16(value) => Some(value.to_variant()), + zvariant::Value::I32(value) => Some(value.to_variant()), + zvariant::Value::U32(value) => Some(value.to_variant()), + zvariant::Value::I64(value) => Some(value.to_variant()), + zvariant::Value::U64(value) => Some(value.to_variant()), + zvariant::Value::F64(value) => Some(value.to_variant()), + zvariant::Value::Str(value) => Some(value.to_string().to_variant()), + zvariant::Value::Signature(_) => None, + zvariant::Value::ObjectPath(value) => Some(value.to_string().to_variant()), + zvariant::Value::Value(_) => None, + zvariant::Value::Array(value) => { + let mut arr = array![]; + for item in value.iter() { + let Some(variant) = item.as_godot_variant() else { + continue; + }; + arr.push(&variant); + } + + Some(arr.to_variant()) + } + zvariant::Value::Dict(value) => { + let mut dict = Dictionary::new(); + for (key, val) in value.iter() { + let Some(key) = key.as_godot_variant() else { + continue; + }; + let Some(val) = val.as_godot_variant() else { + continue; + }; + dict.set(key, val); + } + + Some(dict.to_variant()) + } + zvariant::Value::Structure(_) => None, + zvariant::Value::Fd(_) => None, + } + } +} + +/// Interface for converting Godot types -> DBus types +pub trait DBusVariant { + fn as_zvariant(&self) -> Option; +} + +impl DBusVariant for Variant { + /// Convert the Godot variant type into a DBus variant type + fn as_zvariant(&self) -> Option { + match self.get_type() { + VariantType::NIL => { + let value = zvariant::Optional::<&str>::null_value(); + Some(zvariant::Value::new(value)) + } + VariantType::BOOL => { + let value: bool = self.to(); + Some(zvariant::Value::new(value)) + } + VariantType::INT => { + let value: i64 = self.to(); + Some(zvariant::Value::new(value)) + } + VariantType::FLOAT => { + let value: f64 = self.to(); + Some(zvariant::Value::new(value)) + } + VariantType::STRING => { + let value: GString = self.to(); + let value: String = value.into(); + Some(zvariant::Value::new(value)) + } + VariantType::VECTOR2 => None, + VariantType::VECTOR2I => None, + VariantType::RECT2 => None, + VariantType::RECT2I => None, + VariantType::VECTOR3 => None, + VariantType::VECTOR3I => None, + VariantType::TRANSFORM2D => None, + VariantType::VECTOR4 => None, + VariantType::VECTOR4I => None, + VariantType::PLANE => None, + VariantType::QUATERNION => None, + VariantType::AABB => None, + VariantType::BASIS => None, + VariantType::TRANSFORM3D => None, + VariantType::PROJECTION => None, + VariantType::COLOR => None, + VariantType::STRING_NAME => None, + VariantType::NODE_PATH => None, + VariantType::RID => { + let value: i64 = self.to(); + Some(zvariant::Value::new(value)) + } + VariantType::OBJECT => None, + VariantType::CALLABLE => None, + VariantType::SIGNAL => None, + VariantType::DICTIONARY => None, + VariantType::ARRAY => None, + VariantType::PACKED_BYTE_ARRAY => None, + VariantType::PACKED_INT32_ARRAY => None, + VariantType::PACKED_INT64_ARRAY => None, + VariantType::PACKED_FLOAT32_ARRAY => None, + VariantType::PACKED_FLOAT64_ARRAY => None, + VariantType::PACKED_STRING_ARRAY => None, + VariantType::PACKED_VECTOR2_ARRAY => None, + VariantType::PACKED_VECTOR3_ARRAY => None, + VariantType::PACKED_COLOR_ARRAY => None, + VariantType::PACKED_VECTOR4_ARRAY => None, + VariantType::MAX => None, + + // Unsupported conversion + _ => None, + } + } +} diff --git a/extensions/core/src/dbus/bluez.rs b/extensions/core/src/dbus/bluez.rs new file mode 100644 index 000000000..bce2e071b --- /dev/null +++ b/extensions/core/src/dbus/bluez.rs @@ -0,0 +1,8 @@ +pub mod adapter1; +pub mod battery_provider_manager1; +pub mod device1; +pub mod gatt_manager1; +pub mod leadvertising_manager1; +pub mod media1; +pub mod media_control1; +pub mod network_server1; diff --git a/extensions/core/src/dbus/bluez/adapter1.rs b/extensions/core/src/dbus/bluez/adapter1.rs new file mode 100644 index 000000000..79e76a192 --- /dev/null +++ b/extensions/core/src/dbus/bluez/adapter1.rs @@ -0,0 +1,129 @@ +//! # D-Bus interface proxy for: `org.bluez.Adapter1` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/bluez/hci0' from service 'org.bluez' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.bluez.Adapter1", + default_service = "org.bluez", + default_path = "/org/bluez/hci0" +)] +trait Adapter1 { + /// GetDiscoveryFilters method + fn get_discovery_filters(&self) -> zbus::Result>; + + /// RemoveDevice method + fn remove_device(&self, device: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// SetDiscoveryFilter method + fn set_discovery_filter( + &self, + properties: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// StartDiscovery method + fn start_discovery(&self) -> zbus::Result<()>; + + /// StopDiscovery method + fn stop_discovery(&self) -> zbus::Result<()>; + + /// Address property + #[zbus(property)] + fn address(&self) -> zbus::Result; + + /// AddressType property + #[zbus(property)] + fn address_type(&self) -> zbus::Result; + + /// Alias property + #[zbus(property)] + fn alias(&self) -> zbus::Result; + #[zbus(property)] + fn set_alias(&self, value: &str) -> zbus::Result<()>; + + /// Class property + #[zbus(property)] + fn class(&self) -> zbus::Result; + + /// Discoverable property + #[zbus(property)] + fn discoverable(&self) -> zbus::Result; + #[zbus(property)] + fn set_discoverable(&self, value: bool) -> zbus::Result<()>; + + /// DiscoverableTimeout property + #[zbus(property)] + fn discoverable_timeout(&self) -> zbus::Result; + #[zbus(property)] + fn set_discoverable_timeout(&self, value: u32) -> zbus::Result<()>; + + /// Discovering property + #[zbus(property)] + fn discovering(&self) -> zbus::Result; + + /// ExperimentalFeatures property + #[zbus(property)] + fn experimental_features(&self) -> zbus::Result>; + + /// Manufacturer property + #[zbus(property)] + fn manufacturer(&self) -> zbus::Result; + + /// Modalias property + #[zbus(property)] + fn modalias(&self) -> zbus::Result; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// Pairable property + #[zbus(property)] + fn pairable(&self) -> zbus::Result; + #[zbus(property)] + fn set_pairable(&self, value: bool) -> zbus::Result<()>; + + /// PairableTimeout property + #[zbus(property)] + fn pairable_timeout(&self) -> zbus::Result; + #[zbus(property)] + fn set_pairable_timeout(&self, value: u32) -> zbus::Result<()>; + + /// PowerState property + #[zbus(property)] + fn power_state(&self) -> zbus::Result; + + /// Powered property + #[zbus(property)] + fn powered(&self) -> zbus::Result; + #[zbus(property)] + fn set_powered(&self, value: bool) -> zbus::Result<()>; + + /// Roles property + #[zbus(property)] + fn roles(&self) -> zbus::Result>; + + /// UUIDs property + #[zbus(property, name = "UUIDs")] + fn uuids(&self) -> zbus::Result>; + + /// Version property + #[zbus(property)] + fn version(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/bluez/battery_provider_manager1.rs b/extensions/core/src/dbus/bluez/battery_provider_manager1.rs new file mode 100644 index 000000000..c47ac8f73 --- /dev/null +++ b/extensions/core/src/dbus/bluez/battery_provider_manager1.rs @@ -0,0 +1,39 @@ +//! # D-Bus interface proxy for: `org.bluez.BatteryProviderManager1` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/bluez/hci0' from service 'org.bluez' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.bluez.BatteryProviderManager1", + default_service = "org.bluez", + default_path = "/org/bluez/hci0" +)] +trait BatteryProviderManager1 { + /// RegisterBatteryProvider method + fn register_battery_provider( + &self, + provider: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result<()>; + + /// UnregisterBatteryProvider method + fn unregister_battery_provider( + &self, + provider: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result<()>; +} diff --git a/extensions/core/src/dbus/bluez/device1.rs b/extensions/core/src/dbus/bluez/device1.rs new file mode 100644 index 000000000..1006f9c64 --- /dev/null +++ b/extensions/core/src/dbus/bluez/device1.rs @@ -0,0 +1,154 @@ +//! # D-Bus interface proxy for: `org.bluez.Device1` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/bluez/hci0/dev_XX...' from service 'org.bluez' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy(interface = "org.bluez.Device1", default_service = "org.bluez")] +trait Device1 { + /// CancelPairing method + fn cancel_pairing(&self) -> zbus::Result<()>; + + /// Connect method + fn connect(&self) -> zbus::Result<()>; + + /// ConnectProfile method + #[allow(non_snake_case)] + fn connect_profile(&self, UUID: &str) -> zbus::Result<()>; + + /// Disconnect method + fn disconnect(&self) -> zbus::Result<()>; + + /// DisconnectProfile method + #[allow(non_snake_case)] + fn disconnect_profile(&self, UUID: &str) -> zbus::Result<()>; + + /// Pair method + fn pair(&self) -> zbus::Result<()>; + + /// Adapter property + #[zbus(property)] + fn adapter(&self) -> zbus::Result; + + /// Address property + #[zbus(property)] + fn address(&self) -> zbus::Result; + + /// AddressType property + #[zbus(property)] + fn address_type(&self) -> zbus::Result; + + /// Alias property + #[zbus(property)] + fn alias(&self) -> zbus::Result; + #[zbus(property)] + fn set_alias(&self, value: &str) -> zbus::Result<()>; + + /// Appearance property + #[zbus(property)] + fn appearance(&self) -> zbus::Result; + + /// Blocked property + #[zbus(property)] + fn blocked(&self) -> zbus::Result; + #[zbus(property)] + fn set_blocked(&self, value: bool) -> zbus::Result<()>; + + /// Bonded property + #[zbus(property)] + fn bonded(&self) -> zbus::Result; + + /// Class property + #[zbus(property)] + fn class(&self) -> zbus::Result; + + /// Connected property + #[zbus(property)] + fn connected(&self) -> zbus::Result; + + /// Icon property + #[zbus(property)] + fn icon(&self) -> zbus::Result; + + /// LegacyPairing property + #[zbus(property)] + fn legacy_pairing(&self) -> zbus::Result; + + /// ManufacturerData property + #[zbus(property)] + fn manufacturer_data( + &self, + ) -> zbus::Result>; + + /// Modalias property + #[zbus(property)] + fn modalias(&self) -> zbus::Result; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// Paired property + #[zbus(property)] + fn paired(&self) -> zbus::Result; + + /// RSSI property + #[zbus(property, name = "RSSI")] + fn rssi(&self) -> zbus::Result; + + /// ServiceData property + #[zbus(property)] + fn service_data( + &self, + ) -> zbus::Result>; + + /// ServicesResolved property + #[zbus(property)] + fn services_resolved(&self) -> zbus::Result; + + /// Sets property + #[zbus(property)] + fn sets( + &self, + ) -> zbus::Result< + std::collections::HashMap< + zbus::zvariant::OwnedObjectPath, + std::collections::HashMap, + >, + >; + + /// Trusted property + #[zbus(property)] + fn trusted(&self) -> zbus::Result; + #[zbus(property)] + fn set_trusted(&self, value: bool) -> zbus::Result<()>; + + /// TxPower property + #[zbus(property)] + fn tx_power(&self) -> zbus::Result; + + /// UUIDs property + #[zbus(property, name = "UUIDs")] + fn uuids(&self) -> zbus::Result>; + + /// WakeAllowed property + #[zbus(property)] + fn wake_allowed(&self) -> zbus::Result; + #[zbus(property)] + fn set_wake_allowed(&self, value: bool) -> zbus::Result<()>; +} diff --git a/extensions/core/src/dbus/bluez/gatt_manager1.rs b/extensions/core/src/dbus/bluez/gatt_manager1.rs new file mode 100644 index 000000000..bf0153d70 --- /dev/null +++ b/extensions/core/src/dbus/bluez/gatt_manager1.rs @@ -0,0 +1,40 @@ +//! # D-Bus interface proxy for: `org.bluez.GattManager1` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/bluez/hci0' from service 'org.bluez' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.bluez.GattManager1", + default_service = "org.bluez", + default_path = "/org/bluez/hci0" +)] +trait GattManager1 { + /// RegisterApplication method + fn register_application( + &self, + application: &zbus::zvariant::ObjectPath<'_>, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// UnregisterApplication method + fn unregister_application( + &self, + application: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result<()>; +} diff --git a/extensions/core/src/dbus/bluez/leadvertising_manager1.rs b/extensions/core/src/dbus/bluez/leadvertising_manager1.rs new file mode 100644 index 000000000..da3ddbb64 --- /dev/null +++ b/extensions/core/src/dbus/bluez/leadvertising_manager1.rs @@ -0,0 +1,56 @@ +//! # D-Bus interface proxy for: `org.bluez.LEAdvertisingManager1` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/bluez/hci0' from service 'org.bluez' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.bluez.LEAdvertisingManager1", + default_service = "org.bluez", + default_path = "/org/bluez/hci0" +)] +trait LEAdvertisingManager1 { + /// RegisterAdvertisement method + fn register_advertisement( + &self, + advertisement: &zbus::zvariant::ObjectPath<'_>, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// UnregisterAdvertisement method + fn unregister_advertisement( + &self, + service: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result<()>; + + /// ActiveInstances property + #[zbus(property)] + fn active_instances(&self) -> zbus::Result; + + /// SupportedIncludes property + #[zbus(property)] + fn supported_includes(&self) -> zbus::Result>; + + /// SupportedInstances property + #[zbus(property)] + fn supported_instances(&self) -> zbus::Result; + + /// SupportedSecondaryChannels property + #[zbus(property)] + fn supported_secondary_channels(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/bluez/media1.rs b/extensions/core/src/dbus/bluez/media1.rs new file mode 100644 index 000000000..ba0456865 --- /dev/null +++ b/extensions/core/src/dbus/bluez/media1.rs @@ -0,0 +1,64 @@ +//! # D-Bus interface proxy for: `org.bluez.Media1` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/bluez/hci0' from service 'org.bluez' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.bluez.Media1", + default_service = "org.bluez", + default_path = "/org/bluez/hci0" +)] +trait Media1 { + /// RegisterApplication method + fn register_application( + &self, + application: &zbus::zvariant::ObjectPath<'_>, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// RegisterEndpoint method + fn register_endpoint( + &self, + endpoint: &zbus::zvariant::ObjectPath<'_>, + properties: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// RegisterPlayer method + fn register_player( + &self, + player: &zbus::zvariant::ObjectPath<'_>, + properties: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// UnregisterApplication method + fn unregister_application( + &self, + application: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result<()>; + + /// UnregisterEndpoint method + fn unregister_endpoint(&self, endpoint: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// UnregisterPlayer method + fn unregister_player(&self, player: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// SupportedUUIDs property + #[zbus(property, name = "SupportedUUIDs")] + fn supported_uuids(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/bluez/media_control1.rs b/extensions/core/src/dbus/bluez/media_control1.rs new file mode 100644 index 000000000..77583bff7 --- /dev/null +++ b/extensions/core/src/dbus/bluez/media_control1.rs @@ -0,0 +1,58 @@ +//! # D-Bus interface proxy for: `org.bluez.MediaControl1` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/bluez/hci0/dev_XX...' from service 'org.bluez' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy(interface = "org.bluez.MediaControl1", default_service = "org.bluez")] +trait MediaControl1 { + /// FastForward method + fn fast_forward(&self) -> zbus::Result<()>; + + /// Next method + fn next(&self) -> zbus::Result<()>; + + /// Pause method + fn pause(&self) -> zbus::Result<()>; + + /// Play method + fn play(&self) -> zbus::Result<()>; + + /// Previous method + fn previous(&self) -> zbus::Result<()>; + + /// Rewind method + fn rewind(&self) -> zbus::Result<()>; + + /// Stop method + fn stop(&self) -> zbus::Result<()>; + + /// VolumeDown method + fn volume_down(&self) -> zbus::Result<()>; + + /// VolumeUp method + fn volume_up(&self) -> zbus::Result<()>; + + /// Connected property + #[zbus(property)] + fn connected(&self) -> zbus::Result; + + /// Player property + #[zbus(property)] + fn player(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/bluez/network_server1.rs b/extensions/core/src/dbus/bluez/network_server1.rs new file mode 100644 index 000000000..22a9f13ab --- /dev/null +++ b/extensions/core/src/dbus/bluez/network_server1.rs @@ -0,0 +1,33 @@ +//! # D-Bus interface proxy for: `org.bluez.NetworkServer1` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/bluez/hci0' from service 'org.bluez' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.bluez.NetworkServer1", + default_service = "org.bluez", + default_path = "/org/bluez/hci0" +)] +trait NetworkServer1 { + /// Register method + fn register(&self, uuid: &str, bridge: &str) -> zbus::Result<()>; + + /// Unregister method + fn unregister(&self, uuid: &str) -> zbus::Result<()>; +} diff --git a/extensions/core/src/dbus/inputplumber.rs b/extensions/core/src/dbus/inputplumber.rs new file mode 100644 index 000000000..e722fce81 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber.rs @@ -0,0 +1,6 @@ +pub mod composite_device; +pub mod dbus_device; +pub mod event_device; +pub mod input_manager; +pub mod keyboard; +pub mod mouse; diff --git a/extensions/core/src/dbus/inputplumber/composite_device.rs b/extensions/core/src/dbus/inputplumber/composite_device.rs new file mode 100644 index 000000000..bfb9ea4b8 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/composite_device.rs @@ -0,0 +1,87 @@ +//! # D-Bus interface proxy for: `org.shadowblip.Input.CompositeDevice` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/CompositeDevice0' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.Input.CompositeDevice", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/CompositeDevice0" +)] +trait CompositeDevice { + /// LoadProfileFromYaml method + fn load_profile_from_yaml(&self, profile: &str) -> zbus::Result<()>; + + /// LoadProfilePath method + fn load_profile_path(&self, path: &str) -> zbus::Result<()>; + + /// SendButtonChord method + fn send_button_chord(&self, events: &[&str]) -> zbus::Result<()>; + + /// SendEvent method + fn send_event(&self, event: &str, value: &zbus::zvariant::Value<'_>) -> zbus::Result<()>; + + /// SetInterceptActivation method + fn set_intercept_activation( + &self, + activation_events: &[&str], + target_event: &str, + ) -> zbus::Result<()>; + + /// SetTargetDevices method + fn set_target_devices(&self, target_device_types: &[&str]) -> zbus::Result<()>; + + /// Stop method + fn stop(&self) -> zbus::Result<()>; + + /// Capabilities property + #[zbus(property)] + fn capabilities(&self) -> zbus::Result>; + + /// DbusDevices property + #[zbus(property)] + fn dbus_devices(&self) -> zbus::Result>; + + /// InterceptMode property + #[zbus(property)] + fn intercept_mode(&self) -> zbus::Result; + #[zbus(property)] + fn set_intercept_mode(&self, value: u32) -> zbus::Result<()>; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// ProfileName property + #[zbus(property)] + fn profile_name(&self) -> zbus::Result; + + /// SourceDevicePaths property + #[zbus(property)] + fn source_device_paths(&self) -> zbus::Result>; + + /// TargetCapabilities property + #[zbus(property)] + fn target_capabilities(&self) -> zbus::Result>; + + /// TargetDevices property + #[zbus(property)] + fn target_devices(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/inputplumber/dbus_device.rs b/extensions/core/src/dbus/inputplumber/dbus_device.rs new file mode 100644 index 000000000..56f054508 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/dbus_device.rs @@ -0,0 +1,48 @@ +//! # D-Bus interface proxy for: `org.shadowblip.Input.DBusDevice` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/devices/target/dbus0' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.Input.DBusDevice", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/devices/target/dbus0" +)] +trait DBusDevice { + /// InputEvent signal + #[zbus(signal)] + fn input_event(&self, event: &str, value: f64) -> zbus::Result<()>; + + /// TouchEvent signal + #[zbus(signal)] + fn touch_event( + &self, + event: &str, + index: u32, + is_touching: bool, + pressure: f64, + x: f64, + y: f64, + ) -> zbus::Result<()>; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/inputplumber/event_device.rs b/extensions/core/src/dbus/inputplumber/event_device.rs new file mode 100644 index 000000000..5b435da77 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/event_device.rs @@ -0,0 +1,64 @@ +//! # D-Bus interface proxy for: `org.shadowblip.Input.Source.EventDevice` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/devices/source/event9' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.Input.Source.EventDevice", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/devices/source/event9" +)] +trait EventDevice { + /// DevicePath property + #[zbus(property)] + fn device_path(&self) -> zbus::Result; + + /// IdBustype property + #[zbus(property)] + fn id_bustype(&self) -> zbus::Result; + + /// IdProduct property + #[zbus(property)] + fn id_product(&self) -> zbus::Result; + + /// IdVendor property + #[zbus(property)] + fn id_vendor(&self) -> zbus::Result; + + /// IdVersion property + #[zbus(property)] + fn id_version(&self) -> zbus::Result; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// PhysPath property + #[zbus(property)] + fn phys_path(&self) -> zbus::Result; + + /// SysfsPath property + #[zbus(property)] + fn sysfs_path(&self) -> zbus::Result; + + /// UniqueId property + #[zbus(property)] + fn unique_id(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/inputplumber/gamepad.rs b/extensions/core/src/dbus/inputplumber/gamepad.rs new file mode 100644 index 000000000..207174b19 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/gamepad.rs @@ -0,0 +1,32 @@ +//! # D-Bus interface proxy for: `org.shadowblip.Input.Gamepad` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/devices/target/gamepad0' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.Input.Gamepad", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/devices/target/gamepad0" +)] +trait Gamepad { + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/inputplumber/hidraw_device.rs b/extensions/core/src/dbus/inputplumber/hidraw_device.rs new file mode 100644 index 000000000..f0a50da52 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/hidraw_device.rs @@ -0,0 +1,64 @@ +//! # D-Bus interface proxy for: `org.shadowblip.Input.Source.HIDRawDevice` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/devices/source/hidraw0' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.Input.Source.HIDRawDevice", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/devices/source/hidraw0" +)] +trait HIDRawDevice { + /// DevPath property + #[zbus(property)] + fn dev_path(&self) -> zbus::Result; + + /// IdProduct property + #[zbus(property)] + fn id_product(&self) -> zbus::Result; + + /// IdVendor property + #[zbus(property)] + fn id_vendor(&self) -> zbus::Result; + + /// InterfaceNumber property + #[zbus(property)] + fn interface_number(&self) -> zbus::Result; + + /// Manufacturer property + #[zbus(property)] + fn manufacturer(&self) -> zbus::Result; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// Product property + #[zbus(property)] + fn product(&self) -> zbus::Result; + + /// SerialNumber property + #[zbus(property)] + fn serial_number(&self) -> zbus::Result; + + /// SysfsPath property + #[zbus(property)] + fn sysfs_path(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/inputplumber/iioimudevice.rs b/extensions/core/src/dbus/inputplumber/iioimudevice.rs new file mode 100644 index 000000000..7dbfdfdf9 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/iioimudevice.rs @@ -0,0 +1,76 @@ +//! # D-Bus interface proxy for: `org.shadowblip.Input.Source.IIOIMUDevice` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/devices/source/iio_device0' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.Input.Source.IIOIMUDevice", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/devices/source/iio_device0" +)] +trait IIOIMUDevice { + /// AccelSampleRate property + #[zbus(property)] + fn accel_sample_rate(&self) -> zbus::Result; + #[zbus(property)] + fn set_accel_sample_rate(&self, value: f64) -> zbus::Result<()>; + + /// AccelSampleRatesAvail property + #[zbus(property)] + fn accel_sample_rates_avail(&self) -> zbus::Result>; + + /// AccelScale property + #[zbus(property)] + fn accel_scale(&self) -> zbus::Result; + #[zbus(property)] + fn set_accel_scale(&self, value: f64) -> zbus::Result<()>; + + /// AccelScalesAvail property + #[zbus(property)] + fn accel_scales_avail(&self) -> zbus::Result>; + + /// AngvelSampleRate property + #[zbus(property)] + fn angvel_sample_rate(&self) -> zbus::Result; + #[zbus(property)] + fn set_angvel_sample_rate(&self, value: f64) -> zbus::Result<()>; + + /// AngvelSampleRatesAvail property + #[zbus(property)] + fn angvel_sample_rates_avail(&self) -> zbus::Result>; + + /// AngvelScale property + #[zbus(property)] + fn angvel_scale(&self) -> zbus::Result; + #[zbus(property)] + fn set_angvel_scale(&self, value: f64) -> zbus::Result<()>; + + /// AngvelScalesAvail property + #[zbus(property)] + fn angvel_scales_avail(&self) -> zbus::Result>; + + /// Id property + #[zbus(property)] + fn id(&self) -> zbus::Result; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/inputplumber/input_manager.rs b/extensions/core/src/dbus/inputplumber/input_manager.rs new file mode 100644 index 000000000..49b1c3837 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/input_manager.rs @@ -0,0 +1,52 @@ +//! # D-Bus interface proxy for: `org.shadowblip.InputManager` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/Manager' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.InputManager", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/Manager" +)] +trait InputManager { + /// AttachTargetDevice method + fn attach_target_device(&self, target_path: &str, composite_path: &str) -> zbus::Result<()>; + + /// CreateCompositeDevice method + fn create_composite_device(&self, config_path: &str) -> zbus::Result; + + /// CreateTargetDevice method + fn create_target_device(&self, kind: &str) -> zbus::Result; + + /// StopTargetDevice method + fn stop_target_device(&self, path: &str) -> zbus::Result<()>; + + /// InterceptMode property + #[zbus(property)] + fn intercept_mode(&self) -> zbus::Result; + + /// SupportedTargetDeviceIds property + #[zbus(property)] + fn supported_target_device_ids(&self) -> zbus::Result>; + + /// SupportedTargetDevices property + #[zbus(property)] + fn supported_target_devices(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/inputplumber/keyboard.rs b/extensions/core/src/dbus/inputplumber/keyboard.rs new file mode 100644 index 000000000..3da27d524 --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/keyboard.rs @@ -0,0 +1,35 @@ +//! # D-Bus interface proxy for: `org.shadowblip.Input.Keyboard` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/devices/target/keyboard0' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.Input.Keyboard", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/devices/target/keyboard0" +)] +trait Keyboard { + /// SendKey method + fn send_key(&self, key: &str, value: bool) -> zbus::Result<()>; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/inputplumber/mouse.rs b/extensions/core/src/dbus/inputplumber/mouse.rs new file mode 100644 index 000000000..8ffe1b2dd --- /dev/null +++ b/extensions/core/src/dbus/inputplumber/mouse.rs @@ -0,0 +1,35 @@ +//! # D-Bus interface proxy for: `org.shadowblip.Input.Mouse` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/InputPlumber/devices/target/mouse0' from service 'org.shadowblip.InputPlumber' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.Input.Mouse", + default_service = "org.shadowblip.InputPlumber", + default_path = "/org/shadowblip/InputPlumber/devices/target/mouse0" +)] +trait Mouse { + /// MoveCursor method + fn move_cursor(&self, x: i32, y: i32) -> zbus::Result<()>; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager.rs b/extensions/core/src/dbus/networkmanager.rs new file mode 100644 index 000000000..f45e3ff28 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager.rs @@ -0,0 +1,17 @@ +pub mod access_point; +pub mod active; +pub mod agent_manager; +pub mod bridge; +pub mod connection; +pub mod device; +pub mod dhcp4config; +pub mod dns_manager; +pub mod ip4config; +pub mod ip6config; +pub mod loopback; +pub mod network_manager; +pub mod settings; +pub mod statistics; +pub mod wifi_p2p; +pub mod wired; +pub mod wireless; diff --git a/extensions/core/src/dbus/networkmanager/access_point.rs b/extensions/core/src/dbus/networkmanager/access_point.rs new file mode 100644 index 000000000..ec64db903 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/access_point.rs @@ -0,0 +1,71 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.AccessPoint` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/AccessPoint/891' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.AccessPoint", + default_service = "org.freedesktop.NetworkManager" +)] +trait AccessPoint { + /// Bandwidth property + #[zbus(property)] + fn bandwidth(&self) -> zbus::Result; + + /// Flags property + #[zbus(property)] + fn flags(&self) -> zbus::Result; + + /// Frequency property + #[zbus(property)] + fn frequency(&self) -> zbus::Result; + + /// HwAddress property + #[zbus(property)] + fn hw_address(&self) -> zbus::Result; + + /// LastSeen property + #[zbus(property)] + fn last_seen(&self) -> zbus::Result; + + /// MaxBitrate property + #[zbus(property)] + fn max_bitrate(&self) -> zbus::Result; + + /// Mode property + #[zbus(property)] + fn mode(&self) -> zbus::Result; + + /// RsnFlags property + #[zbus(property)] + fn rsn_flags(&self) -> zbus::Result; + + /// Ssid property + #[zbus(property)] + fn ssid(&self) -> zbus::Result>; + + /// Strength property + #[zbus(property)] + fn strength(&self) -> zbus::Result; + + /// WpaFlags property + #[zbus(property)] + fn wpa_flags(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/active.rs b/extensions/core/src/dbus/networkmanager/active.rs new file mode 100644 index 000000000..1b5900428 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/active.rs @@ -0,0 +1,99 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Connection.Active` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/ActiveConnection/3' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Connection.Active", + default_service = "org.freedesktop.NetworkManager" +)] +trait Active { + /// StateChanged signal + //#[zbus(signal)] + //fn state_changed(&self, state: u32, reason: u32) -> zbus::Result<()>; + + /// Connection property + #[zbus(property)] + fn connection(&self) -> zbus::Result; + + /// Controller property + #[zbus(property)] + fn controller(&self) -> zbus::Result; + + /// Default property + #[zbus(property)] + fn default(&self) -> zbus::Result; + + /// Default6 property + #[zbus(property)] + fn default6(&self) -> zbus::Result; + + /// Devices property + #[zbus(property)] + fn devices(&self) -> zbus::Result>; + + /// Dhcp4Config property + #[zbus(property)] + fn dhcp4_config(&self) -> zbus::Result; + + /// Dhcp6Config property + #[zbus(property)] + fn dhcp6_config(&self) -> zbus::Result; + + /// Id property + #[zbus(property)] + fn id(&self) -> zbus::Result; + + /// Ip4Config property + #[zbus(property)] + fn ip4_config(&self) -> zbus::Result; + + /// Ip6Config property + #[zbus(property)] + fn ip6_config(&self) -> zbus::Result; + + /// Master property + #[zbus(property)] + fn master(&self) -> zbus::Result; + + /// SpecificObject property + #[zbus(property)] + fn specific_object(&self) -> zbus::Result; + + /// State property + #[zbus(property)] + fn state(&self) -> zbus::Result; + + /// StateFlags property + #[zbus(property)] + fn state_flags(&self) -> zbus::Result; + + /// Type property + #[zbus(property)] + fn type_(&self) -> zbus::Result; + + /// Uuid property + #[zbus(property)] + fn uuid(&self) -> zbus::Result; + + /// Vpn property + #[zbus(property)] + fn vpn(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/agent_manager.rs b/extensions/core/src/dbus/networkmanager/agent_manager.rs new file mode 100644 index 000000000..8a0c281d6 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/agent_manager.rs @@ -0,0 +1,37 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.AgentManager` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/AgentManager' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.AgentManager", + default_service = "org.freedesktop.NetworkManager", + default_path = "/org/freedesktop/NetworkManager/AgentManager" +)] +trait AgentManager { + /// Register method + fn register(&self, identifier: &str) -> zbus::Result<()>; + + /// RegisterWithCapabilities method + fn register_with_capabilities(&self, identifier: &str, capabilities: u32) -> zbus::Result<()>; + + /// Unregister method + fn unregister(&self) -> zbus::Result<()>; +} diff --git a/extensions/core/src/dbus/networkmanager/bridge.rs b/extensions/core/src/dbus/networkmanager/bridge.rs new file mode 100644 index 000000000..5608afa39 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/bridge.rs @@ -0,0 +1,39 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Device.Bridge` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Devices/5' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Device.Bridge", + default_service = "org.freedesktop.NetworkManager" +)] +trait Bridge { + /// Carrier property + #[zbus(property)] + fn carrier(&self) -> zbus::Result; + + /// HwAddress property + #[zbus(property)] + fn hw_address(&self) -> zbus::Result; + + /// Slaves property + #[zbus(property)] + fn slaves(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/networkmanager/connection.rs b/extensions/core/src/dbus/networkmanager/connection.rs new file mode 100644 index 000000000..ddbdee10c --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/connection.rs @@ -0,0 +1,110 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Settings.Connection` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Settings/5' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Settings.Connection", + default_service = "org.freedesktop.NetworkManager" +)] +trait Connection { + /// ClearSecrets method + fn clear_secrets(&self) -> zbus::Result<()>; + + /// Delete method + fn delete(&self) -> zbus::Result<()>; + + /// GetSecrets method + fn get_secrets( + &self, + setting_name: &str, + ) -> zbus::Result< + std::collections::HashMap< + String, + std::collections::HashMap, + >, + >; + + /// GetSettings method + fn get_settings( + &self, + ) -> zbus::Result< + std::collections::HashMap< + String, + std::collections::HashMap, + >, + >; + + /// Save method + fn save(&self) -> zbus::Result<()>; + + /// Update method + fn update( + &self, + properties: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + ) -> zbus::Result<()>; + + /// Update2 method + fn update2( + &self, + settings: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + flags: u32, + args: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result>; + + /// UpdateUnsaved method + fn update_unsaved( + &self, + properties: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + ) -> zbus::Result<()>; + + /// Removed signal + #[zbus(signal)] + fn removed(&self) -> zbus::Result<()>; + + /// Updated signal + #[zbus(signal)] + fn updated(&self) -> zbus::Result<()>; + + /// Filename property + #[zbus(property)] + fn filename(&self) -> zbus::Result; + + /// Flags property + #[zbus(property)] + fn flags(&self) -> zbus::Result; + + /// Unsaved property + #[zbus(property)] + fn unsaved(&self) -> zbus::Result; + + /// VersionId property + #[zbus(property)] + fn version_id(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/device.rs b/extensions/core/src/dbus/networkmanager/device.rs new file mode 100644 index 000000000..a2f6d84ad --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/device.rs @@ -0,0 +1,195 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Device` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Devices/5' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Device", + default_service = "org.freedesktop.NetworkManager" +)] +trait Device { + /// Delete method + fn delete(&self) -> zbus::Result<()>; + + /// Disconnect method + fn disconnect(&self) -> zbus::Result<()>; + + /// GetAppliedConnection method + #[allow(clippy::type_complexity)] + fn get_applied_connection( + &self, + flags: u32, + ) -> zbus::Result<( + std::collections::HashMap< + String, + std::collections::HashMap, + >, + u64, + )>; + + /// Reapply method + fn reapply( + &self, + connection: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + version_id: u64, + flags: u32, + ) -> zbus::Result<()>; + + /// StateChanged signal + //#[zbus(signal)] + //fn state_changed(&self, new_state: u32, old_state: u32, reason: u32) -> zbus::Result<()>; + + /// ActiveConnection property + #[zbus(property)] + fn active_connection(&self) -> zbus::Result; + + /// Autoconnect property + #[zbus(property)] + fn autoconnect(&self) -> zbus::Result; + #[zbus(property)] + fn set_autoconnect(&self, value: bool) -> zbus::Result<()>; + + /// AvailableConnections property + #[zbus(property)] + fn available_connections(&self) -> zbus::Result>; + + /// Capabilities property + #[zbus(property)] + fn capabilities(&self) -> zbus::Result; + + /// DeviceType property + #[zbus(property)] + fn device_type(&self) -> zbus::Result; + + /// Dhcp4Config property + #[zbus(property)] + fn dhcp4_config(&self) -> zbus::Result; + + /// Dhcp6Config property + #[zbus(property)] + fn dhcp6_config(&self) -> zbus::Result; + + /// Driver property + #[zbus(property)] + fn driver(&self) -> zbus::Result; + + /// DriverVersion property + #[zbus(property)] + fn driver_version(&self) -> zbus::Result; + + /// FirmwareMissing property + #[zbus(property)] + fn firmware_missing(&self) -> zbus::Result; + + /// FirmwareVersion property + #[zbus(property)] + fn firmware_version(&self) -> zbus::Result; + + /// HwAddress property + #[zbus(property)] + fn hw_address(&self) -> zbus::Result; + + /// Interface property + #[zbus(property)] + fn interface(&self) -> zbus::Result; + + /// InterfaceFlags property + #[zbus(property)] + fn interface_flags(&self) -> zbus::Result; + + /// Ip4Address property + #[zbus(property)] + fn ip4_address(&self) -> zbus::Result; + + /// Ip4Config property + #[zbus(property)] + fn ip4_config(&self) -> zbus::Result; + + /// Ip4Connectivity property + #[zbus(property)] + fn ip4_connectivity(&self) -> zbus::Result; + + /// Ip6Config property + #[zbus(property)] + fn ip6_config(&self) -> zbus::Result; + + /// Ip6Connectivity property + #[zbus(property)] + fn ip6_connectivity(&self) -> zbus::Result; + + /// IpInterface property + #[zbus(property)] + fn ip_interface(&self) -> zbus::Result; + + /// LldpNeighbors property + #[zbus(property)] + fn lldp_neighbors( + &self, + ) -> zbus::Result>>; + + /// Managed property + #[zbus(property)] + fn managed(&self) -> zbus::Result; + #[zbus(property)] + fn set_managed(&self, value: bool) -> zbus::Result<()>; + + /// Metered property + #[zbus(property)] + fn metered(&self) -> zbus::Result; + + /// Mtu property + #[zbus(property)] + fn mtu(&self) -> zbus::Result; + + /// NmPluginMissing property + #[zbus(property)] + fn nm_plugin_missing(&self) -> zbus::Result; + + /// Path property + #[zbus(property)] + fn path(&self) -> zbus::Result; + + /// PhysicalPortId property + #[zbus(property)] + fn physical_port_id(&self) -> zbus::Result; + + /// Ports property + #[zbus(property)] + fn ports(&self) -> zbus::Result>; + + /// Real property + #[zbus(property)] + fn real(&self) -> zbus::Result; + + /// State property + #[zbus(property)] + fn state(&self) -> zbus::Result; + + /// StateReason property + #[zbus(property)] + fn state_reason(&self) -> zbus::Result<(u32, u32)>; + + /// Udi property + #[zbus(property)] + fn udi(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/dhcp4config.rs b/extensions/core/src/dbus/networkmanager/dhcp4config.rs new file mode 100644 index 000000000..d1186bc1c --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/dhcp4config.rs @@ -0,0 +1,33 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.DHCP4Config` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/DHCP4Config/2' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.DHCP4Config", + default_service = "org.freedesktop.NetworkManager" +)] +trait DHCP4Config { + /// Options property + #[zbus(property)] + fn options( + &self, + ) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/networkmanager/dns_manager.rs b/extensions/core/src/dbus/networkmanager/dns_manager.rs new file mode 100644 index 000000000..d3d32a0df --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/dns_manager.rs @@ -0,0 +1,42 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.DnsManager` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/DnsManager' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.DnsManager", + default_service = "org.freedesktop.NetworkManager", + default_path = "/org/freedesktop/NetworkManager/DnsManager" +)] +trait DnsManager { + /// Configuration property + #[zbus(property)] + fn configuration( + &self, + ) -> zbus::Result>>; + + /// Mode property + #[zbus(property)] + fn mode(&self) -> zbus::Result; + + /// RcManager property + #[zbus(property)] + fn rc_manager(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/ip4config.rs b/extensions/core/src/dbus/networkmanager/ip4config.rs new file mode 100644 index 000000000..ea9fef85e --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/ip4config.rs @@ -0,0 +1,85 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.IP4Config` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/IP4Config/1' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.IP4Config", + default_service = "org.freedesktop.NetworkManager" +)] +trait IP4Config { + /// AddressData property + #[zbus(property)] + fn address_data( + &self, + ) -> zbus::Result>>; + + /// Addresses property + #[zbus(property)] + fn addresses(&self) -> zbus::Result>>; + + /// DnsOptions property + #[zbus(property)] + fn dns_options(&self) -> zbus::Result>; + + /// DnsPriority property + #[zbus(property)] + fn dns_priority(&self) -> zbus::Result; + + /// Domains property + #[zbus(property)] + fn domains(&self) -> zbus::Result>; + + /// Gateway property + #[zbus(property)] + fn gateway(&self) -> zbus::Result; + + /// NameserverData property + #[zbus(property)] + fn nameserver_data( + &self, + ) -> zbus::Result>>; + + /// Nameservers property + #[zbus(property)] + fn nameservers(&self) -> zbus::Result>; + + /// RouteData property + #[zbus(property)] + fn route_data( + &self, + ) -> zbus::Result>>; + + /// Routes property + #[zbus(property)] + fn routes(&self) -> zbus::Result>>; + + /// Searches property + #[zbus(property)] + fn searches(&self) -> zbus::Result>; + + /// WinsServerData property + #[zbus(property)] + fn wins_server_data(&self) -> zbus::Result>; + + /// WinsServers property + #[zbus(property)] + fn wins_servers(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/networkmanager/ip6config.rs b/extensions/core/src/dbus/networkmanager/ip6config.rs new file mode 100644 index 000000000..701976e4c --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/ip6config.rs @@ -0,0 +1,73 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.IP6Config` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/IP6Config/3' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.IP6Config", + default_service = "org.freedesktop.NetworkManager" +)] +trait IP6Config { + /// AddressData property + #[zbus(property)] + fn address_data( + &self, + ) -> zbus::Result>>; + + /// Addresses property + #[allow(clippy::type_complexity)] + #[zbus(property)] + fn addresses(&self) -> zbus::Result, u32, Vec)>>; + + /// DnsOptions property + #[zbus(property)] + fn dns_options(&self) -> zbus::Result>; + + /// DnsPriority property + #[zbus(property)] + fn dns_priority(&self) -> zbus::Result; + + /// Domains property + #[zbus(property)] + fn domains(&self) -> zbus::Result>; + + /// Gateway property + #[zbus(property)] + fn gateway(&self) -> zbus::Result; + + /// Nameservers property + #[zbus(property)] + fn nameservers(&self) -> zbus::Result>>; + + /// RouteData property + #[zbus(property)] + fn route_data( + &self, + ) -> zbus::Result>>; + + /// Routes property + #[allow(clippy::type_complexity)] + #[zbus(property)] + fn routes(&self) -> zbus::Result, u32, Vec, u32)>>; + + /// Searches property + #[zbus(property)] + fn searches(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/networkmanager/loopback.rs b/extensions/core/src/dbus/networkmanager/loopback.rs new file mode 100644 index 000000000..f979a31a0 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/loopback.rs @@ -0,0 +1,27 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Device.Loopback` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Devices/1' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Device.Loopback", + default_service = "org.freedesktop.NetworkManager" +)] +trait Loopback {} diff --git a/extensions/core/src/dbus/networkmanager/network_manager.rs b/extensions/core/src/dbus/networkmanager/network_manager.rs new file mode 100644 index 000000000..27c05c7d8 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/network_manager.rs @@ -0,0 +1,266 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager", + default_service = "org.freedesktop.NetworkManager", + default_path = "/org/freedesktop/NetworkManager" +)] +trait NetworkManager { + /// ActivateConnection method + fn activate_connection( + &self, + connection: &zbus::zvariant::ObjectPath<'_>, + device: &zbus::zvariant::ObjectPath<'_>, + specific_object: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result; + + /// AddAndActivateConnection method + fn add_and_activate_connection( + &self, + connection: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + device: &zbus::zvariant::ObjectPath<'_>, + specific_object: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result<( + zbus::zvariant::OwnedObjectPath, + zbus::zvariant::OwnedObjectPath, + )>; + + /// AddAndActivateConnection2 method + #[allow(clippy::too_many_arguments)] + fn add_and_activate_connection2( + &self, + connection: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + device: &zbus::zvariant::ObjectPath<'_>, + specific_object: &zbus::zvariant::ObjectPath<'_>, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<( + zbus::zvariant::OwnedObjectPath, + zbus::zvariant::OwnedObjectPath, + std::collections::HashMap, + )>; + + /// CheckConnectivity method + fn check_connectivity(&self) -> zbus::Result; + + /// CheckpointAdjustRollbackTimeout method + fn checkpoint_adjust_rollback_timeout( + &self, + checkpoint: &zbus::zvariant::ObjectPath<'_>, + add_timeout: u32, + ) -> zbus::Result<()>; + + /// CheckpointCreate method + fn checkpoint_create( + &self, + devices: &[&zbus::zvariant::ObjectPath<'_>], + rollback_timeout: u32, + flags: u32, + ) -> zbus::Result; + + /// CheckpointDestroy method + fn checkpoint_destroy(&self, checkpoint: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// CheckpointRollback method + fn checkpoint_rollback( + &self, + checkpoint: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result>; + + /// DeactivateConnection method + fn deactivate_connection( + &self, + active_connection: &zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result<()>; + + /// Enable method + fn enable(&self, enable: bool) -> zbus::Result<()>; + + /// GetAllDevices method + fn get_all_devices(&self) -> zbus::Result>; + + /// GetDeviceByIpIface method + fn get_device_by_ip_iface(&self, iface: &str) -> zbus::Result; + + /// GetDevices method + fn get_devices(&self) -> zbus::Result>; + + /// GetLogging method + fn get_logging(&self) -> zbus::Result<(String, String)>; + + /// GetPermissions method + fn get_permissions(&self) -> zbus::Result>; + + /// Reload method + fn reload(&self, flags: u32) -> zbus::Result<()>; + + /// SetLogging method + fn set_logging(&self, level: &str, domains: &str) -> zbus::Result<()>; + + /// Sleep method + fn sleep(&self, sleep: bool) -> zbus::Result<()>; + + /// state method + //#[zbus(name = "state")] + //fn state(&self) -> zbus::Result; + + /// CheckPermissions signal + #[zbus(signal)] + fn check_permissions(&self) -> zbus::Result<()>; + + /// DeviceAdded signal + #[zbus(signal)] + fn device_added(&self, device_path: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// DeviceRemoved signal + #[zbus(signal)] + fn device_removed(&self, device_path: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// StateChanged signal + //#[zbus(signal)] + //fn state_changed(&self, state: u32) -> zbus::Result<()>; + + /// ActivatingConnection property + #[zbus(property)] + fn activating_connection(&self) -> zbus::Result; + + /// ActiveConnections property + #[zbus(property)] + fn active_connections(&self) -> zbus::Result>; + + /// AllDevices property + #[zbus(property)] + fn all_devices(&self) -> zbus::Result>; + + /// Capabilities property + #[zbus(property)] + fn capabilities(&self) -> zbus::Result>; + + /// Checkpoints property + #[zbus(property)] + fn checkpoints(&self) -> zbus::Result>; + + /// Connectivity property + #[zbus(property)] + fn connectivity(&self) -> zbus::Result; + + /// ConnectivityCheckAvailable property + #[zbus(property)] + fn connectivity_check_available(&self) -> zbus::Result; + + /// ConnectivityCheckEnabled property + #[zbus(property)] + fn connectivity_check_enabled(&self) -> zbus::Result; + #[zbus(property)] + fn set_connectivity_check_enabled(&self, value: bool) -> zbus::Result<()>; + + /// ConnectivityCheckUri property + #[zbus(property)] + fn connectivity_check_uri(&self) -> zbus::Result; + + /// Devices property + #[zbus(property)] + fn devices(&self) -> zbus::Result>; + + /// GlobalDnsConfiguration property + #[zbus(property)] + fn global_dns_configuration( + &self, + ) -> zbus::Result>; + //#[zbus(property)] + //fn set_global_dns_configuration( + // &self, + // value: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + //) -> zbus::Result<()>; + + /// Metered property + #[zbus(property)] + fn metered(&self) -> zbus::Result; + + /// NetworkingEnabled property + #[zbus(property)] + fn networking_enabled(&self) -> zbus::Result; + + /// PrimaryConnection property + #[zbus(property)] + fn primary_connection(&self) -> zbus::Result; + + /// PrimaryConnectionType property + #[zbus(property)] + fn primary_connection_type(&self) -> zbus::Result; + + /// RadioFlags property + #[zbus(property)] + fn radio_flags(&self) -> zbus::Result; + + /// Startup property + #[zbus(property)] + fn startup(&self) -> zbus::Result; + + /// State property + #[zbus(property)] + fn state(&self) -> zbus::Result; + + /// Version property + #[zbus(property)] + fn version(&self) -> zbus::Result; + + /// VersionInfo property + #[zbus(property)] + fn version_info(&self) -> zbus::Result>; + + /// WimaxEnabled property + #[zbus(property)] + fn wimax_enabled(&self) -> zbus::Result; + #[zbus(property)] + fn set_wimax_enabled(&self, value: bool) -> zbus::Result<()>; + + /// WimaxHardwareEnabled property + #[zbus(property)] + fn wimax_hardware_enabled(&self) -> zbus::Result; + + /// WirelessEnabled property + #[zbus(property)] + fn wireless_enabled(&self) -> zbus::Result; + #[zbus(property)] + fn set_wireless_enabled(&self, value: bool) -> zbus::Result<()>; + + /// WirelessHardwareEnabled property + #[zbus(property)] + fn wireless_hardware_enabled(&self) -> zbus::Result; + + /// WwanEnabled property + #[zbus(property)] + fn wwan_enabled(&self) -> zbus::Result; + #[zbus(property)] + fn set_wwan_enabled(&self, value: bool) -> zbus::Result<()>; + + /// WwanHardwareEnabled property + #[zbus(property)] + fn wwan_hardware_enabled(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/settings.rs b/extensions/core/src/dbus/networkmanager/settings.rs new file mode 100644 index 000000000..04309c08e --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/settings.rs @@ -0,0 +1,95 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Settings` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Settings' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Settings", + default_service = "org.freedesktop.NetworkManager", + default_path = "/org/freedesktop/NetworkManager/Settings" +)] +trait Settings { + /// AddConnection method + fn add_connection( + &self, + connection: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + ) -> zbus::Result; + + /// AddConnection2 method + fn add_connection2( + &self, + settings: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + flags: u32, + args: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<( + zbus::zvariant::OwnedObjectPath, + std::collections::HashMap, + )>; + + /// AddConnectionUnsaved method + fn add_connection_unsaved( + &self, + connection: std::collections::HashMap< + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + >, + ) -> zbus::Result; + + /// GetConnectionByUuid method + fn get_connection_by_uuid(&self, uuid: &str) -> zbus::Result; + + /// ListConnections method + fn list_connections(&self) -> zbus::Result>; + + /// LoadConnections method + fn load_connections(&self, filenames: &[&str]) -> zbus::Result<(bool, Vec)>; + + /// ReloadConnections method + fn reload_connections(&self) -> zbus::Result; + + /// SaveHostname method + fn save_hostname(&self, hostname: &str) -> zbus::Result<()>; + + /// ConnectionRemoved signal + #[zbus(signal)] + fn connection_removed(&self, connection: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// NewConnection signal + #[zbus(signal)] + fn new_connection(&self, connection: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// CanModify property + #[zbus(property)] + fn can_modify(&self) -> zbus::Result; + + /// Connections property + #[zbus(property)] + fn connections(&self) -> zbus::Result>; + + /// Hostname property + #[zbus(property)] + fn hostname(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/statistics.rs b/extensions/core/src/dbus/networkmanager/statistics.rs new file mode 100644 index 000000000..35b0a89b2 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/statistics.rs @@ -0,0 +1,41 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Device.Statistics` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Devices/5' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Device.Statistics", + default_service = "org.freedesktop.NetworkManager" +)] +trait Statistics { + /// RefreshRateMs property + #[zbus(property)] + fn refresh_rate_ms(&self) -> zbus::Result; + #[zbus(property)] + fn set_refresh_rate_ms(&self, value: u32) -> zbus::Result<()>; + + /// RxBytes property + #[zbus(property)] + fn rx_bytes(&self) -> zbus::Result; + + /// TxBytes property + #[zbus(property)] + fn tx_bytes(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/wifi_p2p.rs b/extensions/core/src/dbus/networkmanager/wifi_p2p.rs new file mode 100644 index 000000000..136bd51be --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/wifi_p2p.rs @@ -0,0 +1,52 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Device.WifiP2P` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Devices/4' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Device.WifiP2P", + default_service = "org.freedesktop.NetworkManager" +)] +trait WifiP2P { + /// StartFind method + fn start_find( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// StopFind method + fn stop_find(&self) -> zbus::Result<()>; + + /// PeerAdded signal + #[zbus(signal)] + fn peer_added(&self, peer: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// PeerRemoved signal + #[zbus(signal)] + fn peer_removed(&self, peer: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// HwAddress property + #[zbus(property)] + fn hw_address(&self) -> zbus::Result; + + /// Peers property + #[zbus(property)] + fn peers(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/networkmanager/wired.rs b/extensions/core/src/dbus/networkmanager/wired.rs new file mode 100644 index 000000000..badd8da62 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/wired.rs @@ -0,0 +1,47 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Device.Wired` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Devices/2' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Device.Wired", + default_service = "org.freedesktop.NetworkManager" +)] +trait Wired { + /// Carrier property + #[zbus(property)] + fn carrier(&self) -> zbus::Result; + + /// HwAddress property + #[zbus(property)] + fn hw_address(&self) -> zbus::Result; + + /// PermHwAddress property + #[zbus(property)] + fn perm_hw_address(&self) -> zbus::Result; + + /// S390Subchannels property + #[zbus(property, name = "S390Subchannels")] + fn s390subchannels(&self) -> zbus::Result>; + + /// Speed property + #[zbus(property)] + fn speed(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/networkmanager/wireless.rs b/extensions/core/src/dbus/networkmanager/wireless.rs new file mode 100644 index 000000000..4993a11a5 --- /dev/null +++ b/extensions/core/src/dbus/networkmanager/wireless.rs @@ -0,0 +1,82 @@ +//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.Device.Wireless` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/NetworkManager/Devices/3' from service 'org.freedesktop.NetworkManager' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.NetworkManager.Device.Wireless", + default_service = "org.freedesktop.NetworkManager" +)] +trait Wireless { + /// GetAccessPoints method + fn get_access_points(&self) -> zbus::Result>; + + /// GetAllAccessPoints method + fn get_all_access_points(&self) -> zbus::Result>; + + /// RequestScan method + fn request_scan( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// AccessPointAdded signal + #[zbus(signal)] + fn access_point_added(&self, access_point: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// AccessPointRemoved signal + #[zbus(signal)] + fn access_point_removed( + &self, + access_point: zbus::zvariant::ObjectPath<'_>, + ) -> zbus::Result<()>; + + /// AccessPoints property + #[zbus(property)] + fn access_points(&self) -> zbus::Result>; + + /// ActiveAccessPoint property + #[zbus(property)] + fn active_access_point(&self) -> zbus::Result; + + /// Bitrate property + #[zbus(property)] + fn bitrate(&self) -> zbus::Result; + + /// HwAddress property + #[zbus(property)] + fn hw_address(&self) -> zbus::Result; + + /// LastScan property + #[zbus(property)] + fn last_scan(&self) -> zbus::Result; + + /// Mode property + #[zbus(property)] + fn mode(&self) -> zbus::Result; + + /// PermHwAddress property + #[zbus(property)] + fn perm_hw_address(&self) -> zbus::Result; + + /// WirelessCapabilities property + #[zbus(property)] + fn wireless_capabilities(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/powerstation.rs b/extensions/core/src/dbus/powerstation.rs new file mode 100644 index 000000000..3ae387c03 --- /dev/null +++ b/extensions/core/src/dbus/powerstation.rs @@ -0,0 +1,6 @@ +pub mod card; +pub mod connector; +pub mod core; +pub mod cpu; +pub mod gpu; +pub mod tdp; diff --git a/extensions/core/src/dbus/powerstation/card.rs b/extensions/core/src/dbus/powerstation/card.rs new file mode 100644 index 000000000..0634b4d50 --- /dev/null +++ b/extensions/core/src/dbus/powerstation/card.rs @@ -0,0 +1,105 @@ +//! # D-Bus interface proxy for: `org.shadowblip.GPU.Card` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/Performance/GPU/card0' from service 'org.shadowblip.PowerStation' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.GPU.Card", + default_service = "org.shadowblip.PowerStation", + default_path = "/org/shadowblip/Performance/GPU/card0" +)] +trait Card { + /// EnumerateConnectors method + fn enumerate_connectors(&self) -> zbus::Result>; + + /// Class property + #[zbus(property)] + fn class(&self) -> zbus::Result; + + /// ClassId property + #[zbus(property)] + fn class_id(&self) -> zbus::Result; + + /// ClockLimitMhzMax property + #[zbus(property)] + fn clock_limit_mhz_max(&self) -> zbus::Result; + + /// ClockLimitMhzMin property + #[zbus(property)] + fn clock_limit_mhz_min(&self) -> zbus::Result; + + /// ClockValueMhzMax property + #[zbus(property)] + fn clock_value_mhz_max(&self) -> zbus::Result; + #[zbus(property)] + fn set_clock_value_mhz_max(&self, value: f64) -> zbus::Result<()>; + + /// ClockValueMhzMin property + #[zbus(property)] + fn clock_value_mhz_min(&self) -> zbus::Result; + #[zbus(property)] + fn set_clock_value_mhz_min(&self, value: f64) -> zbus::Result<()>; + + /// Device property + #[zbus(property)] + fn device(&self) -> zbus::Result; + + /// DeviceId property + #[zbus(property)] + fn device_id(&self) -> zbus::Result; + + /// ManualClock property + #[zbus(property)] + fn manual_clock(&self) -> zbus::Result; + #[zbus(property)] + fn set_manual_clock(&self, value: bool) -> zbus::Result<()>; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// Path property + #[zbus(property)] + fn path(&self) -> zbus::Result; + + /// RevisionId property + #[zbus(property)] + fn revision_id(&self) -> zbus::Result; + + /// Subdevice property + #[zbus(property)] + fn subdevice(&self) -> zbus::Result; + + /// SubdeviceId property + #[zbus(property)] + fn subdevice_id(&self) -> zbus::Result; + + /// SubvendorId property + #[zbus(property)] + fn subvendor_id(&self) -> zbus::Result; + + /// Vendor property + #[zbus(property)] + fn vendor(&self) -> zbus::Result; + + /// VendorId property + #[zbus(property)] + fn vendor_id(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/powerstation/connector.rs b/extensions/core/src/dbus/powerstation/connector.rs new file mode 100644 index 000000000..aea3599a2 --- /dev/null +++ b/extensions/core/src/dbus/powerstation/connector.rs @@ -0,0 +1,56 @@ +//! # D-Bus interface proxy for: `org.shadowblip.GPU.Card.Connector` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/Performance/GPU/card0/DP/1' from service 'org.shadowblip.PowerStation' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.GPU.Card.Connector", + default_service = "org.shadowblip.PowerStation", + default_path = "/org/shadowblip/Performance/GPU/card0/DP/1" +)] +trait Connector { + /// DPMS property + #[zbus(property, name = "DPMS")] + fn dpms(&self) -> zbus::Result; + + /// Enabled property + #[zbus(property)] + fn enabled(&self) -> zbus::Result; + + /// Id property + #[zbus(property)] + fn id(&self) -> zbus::Result; + + /// Modes property + #[zbus(property)] + fn modes(&self) -> zbus::Result>; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// Path property + #[zbus(property)] + fn path(&self) -> zbus::Result; + + /// Status property + #[zbus(property)] + fn status(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/powerstation/core.rs b/extensions/core/src/dbus/powerstation/core.rs new file mode 100644 index 000000000..3790bdb69 --- /dev/null +++ b/extensions/core/src/dbus/powerstation/core.rs @@ -0,0 +1,42 @@ +//! # D-Bus interface proxy for: `org.shadowblip.CPU.Core` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/Performance/CPU/Core0' from service 'org.shadowblip.PowerStation' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.CPU.Core", + default_service = "org.shadowblip.PowerStation", + default_path = "/org/shadowblip/Performance/CPU/Core0" +)] +trait Core { + /// CoreId property + #[zbus(property)] + fn core_id(&self) -> zbus::Result; + + /// Number property + #[zbus(property)] + fn number(&self) -> zbus::Result; + + /// Online property + #[zbus(property)] + fn online(&self) -> zbus::Result; + #[zbus(property)] + fn set_online(&self, value: bool) -> zbus::Result<()>; +} diff --git a/extensions/core/src/dbus/powerstation/cpu.rs b/extensions/core/src/dbus/powerstation/cpu.rs new file mode 100644 index 000000000..b4f9d1179 --- /dev/null +++ b/extensions/core/src/dbus/powerstation/cpu.rs @@ -0,0 +1,60 @@ +//! # D-Bus interface proxy for: `org.shadowblip.CPU` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/Performance/CPU' from service 'org.shadowblip.PowerStation' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.CPU", + default_service = "org.shadowblip.PowerStation", + default_path = "/org/shadowblip/Performance/CPU" +)] +trait CPU { + /// EnumerateCores method + fn enumerate_cores(&self) -> zbus::Result>; + + /// HasFeature method + fn has_feature(&self, flag: &str) -> zbus::Result; + + /// BoostEnabled property + #[zbus(property)] + fn boost_enabled(&self) -> zbus::Result; + #[zbus(property)] + fn set_boost_enabled(&self, value: bool) -> zbus::Result<()>; + + /// CoresCount property + #[zbus(property)] + fn cores_count(&self) -> zbus::Result; + + /// CoresEnabled property + #[zbus(property)] + fn cores_enabled(&self) -> zbus::Result; + #[zbus(property)] + fn set_cores_enabled(&self, value: u32) -> zbus::Result<()>; + + /// Features property + #[zbus(property)] + fn features(&self) -> zbus::Result>; + + /// SmtEnabled property + #[zbus(property)] + fn smt_enabled(&self) -> zbus::Result; + #[zbus(property)] + fn set_smt_enabled(&self, value: bool) -> zbus::Result<()>; +} diff --git a/extensions/core/src/dbus/powerstation/gpu.rs b/extensions/core/src/dbus/powerstation/gpu.rs new file mode 100644 index 000000000..f85c00cc3 --- /dev/null +++ b/extensions/core/src/dbus/powerstation/gpu.rs @@ -0,0 +1,31 @@ +//! # D-Bus interface proxy for: `org.shadowblip.GPU` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/Performance/GPU' from service 'org.shadowblip.PowerStation' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.GPU", + default_service = "org.shadowblip.PowerStation", + default_path = "/org/shadowblip/Performance/GPU" +)] +trait GPU { + /// EnumerateCards method + fn enumerate_cards(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/powerstation/tdp.rs b/extensions/core/src/dbus/powerstation/tdp.rs new file mode 100644 index 000000000..14cf87578 --- /dev/null +++ b/extensions/core/src/dbus/powerstation/tdp.rs @@ -0,0 +1,54 @@ +//! # D-Bus interface proxy for: `org.shadowblip.GPU.Card.TDP` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/shadowblip/Performance/GPU/card0' from service 'org.shadowblip.PowerStation' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PeerProxy`] +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.shadowblip.GPU.Card.TDP", + default_service = "org.shadowblip.PowerStation", + default_path = "/org/shadowblip/Performance/GPU/card0" +)] +trait TDP { + /// Boost property + #[zbus(property)] + fn boost(&self) -> zbus::Result; + #[zbus(property)] + fn set_boost(&self, value: f64) -> zbus::Result<()>; + + /// PowerProfile property + #[zbus(property)] + fn power_profiles_available(&self) -> zbus::Result>; + #[zbus(property)] + fn power_profile(&self) -> zbus::Result; + #[zbus(property)] + fn set_power_profile(&self, value: &str) -> zbus::Result<()>; + + /// TDP property + #[zbus(property, name = "TDP")] + fn tdp(&self) -> zbus::Result; + #[zbus(property, name = "TDP")] + fn set_tdp(&self, value: f64) -> zbus::Result<()>; + + /// ThermalThrottleLimitC property + #[zbus(property)] + fn thermal_throttle_limit_c(&self) -> zbus::Result; + #[zbus(property)] + fn set_thermal_throttle_limit_c(&self, value: f64) -> zbus::Result<()>; +} diff --git a/extensions/core/src/dbus/udisks2.rs b/extensions/core/src/dbus/udisks2.rs new file mode 100644 index 000000000..937623550 --- /dev/null +++ b/extensions/core/src/dbus/udisks2.rs @@ -0,0 +1,9 @@ +pub mod block; +pub mod controller; +pub mod drive; +pub mod filesystem; +pub mod manager; +pub mod namespace; +pub mod nvme; +pub mod partition; +pub mod partition_table; diff --git a/extensions/core/src/dbus/udisks2/block.rs b/extensions/core/src/dbus/udisks2/block.rs new file mode 100644 index 000000000..903ace65b --- /dev/null +++ b/extensions/core/src/dbus/udisks2/block.rs @@ -0,0 +1,218 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.Block` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/block_devices/nvme0n1p2' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.Block", + default_service = "org.freedesktop.UDisks2", + default_path = "/org/freedesktop/UDisks2/block_devices/nvme0n1p2" +)] +trait Block { + /// AddConfigurationItem method + fn add_configuration_item( + &self, + item: &( + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ), + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// Format method + fn format( + &self, + type_: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// GetSecretConfiguration method + fn get_secret_configuration( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result< + Vec<( + String, + std::collections::HashMap, + )>, + >; + + /// OpenDevice method + fn open_device( + &self, + mode: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// OpenForBackup method + fn open_for_backup( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// OpenForBenchmark method + fn open_for_benchmark( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// OpenForRestore method + fn open_for_restore( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// RemoveConfigurationItem method + fn remove_configuration_item( + &self, + item: &( + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ), + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// Rescan method + fn rescan( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// UpdateConfigurationItem method + fn update_configuration_item( + &self, + old_item: &( + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ), + new_item: &( + &str, + std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ), + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// Configuration property + #[zbus(property)] + fn configuration( + &self, + ) -> zbus::Result< + Vec<( + String, + std::collections::HashMap, + )>, + >; + + /// CryptoBackingDevice property + #[zbus(property)] + fn crypto_backing_device(&self) -> zbus::Result; + + /// Device property + #[zbus(property)] + fn device(&self) -> zbus::Result>; + + /// DeviceNumber property + #[zbus(property)] + fn device_number(&self) -> zbus::Result; + + /// Drive property + #[zbus(property)] + fn drive(&self) -> zbus::Result; + + /// HintAuto property + #[zbus(property)] + fn hint_auto(&self) -> zbus::Result; + + /// HintIconName property + #[zbus(property)] + fn hint_icon_name(&self) -> zbus::Result; + + /// HintIgnore property + #[zbus(property)] + fn hint_ignore(&self) -> zbus::Result; + + /// HintName property + #[zbus(property)] + fn hint_name(&self) -> zbus::Result; + + /// HintPartitionable property + #[zbus(property)] + fn hint_partitionable(&self) -> zbus::Result; + + /// HintSymbolicIconName property + #[zbus(property)] + fn hint_symbolic_icon_name(&self) -> zbus::Result; + + /// HintSystem property + #[zbus(property)] + fn hint_system(&self) -> zbus::Result; + + /// Id property + #[zbus(property)] + fn id(&self) -> zbus::Result; + + /// IdLabel property + #[zbus(property)] + fn id_label(&self) -> zbus::Result; + + /// IdType property + #[zbus(property)] + fn id_type(&self) -> zbus::Result; + + /// IdUUID property + #[zbus(property, name = "IdUUID")] + fn id_uuid(&self) -> zbus::Result; + + /// IdUsage property + #[zbus(property)] + fn id_usage(&self) -> zbus::Result; + + /// IdVersion property + #[zbus(property)] + fn id_version(&self) -> zbus::Result; + + /// MDRaid property + #[zbus(property, name = "MDRaid")] + fn mdraid(&self) -> zbus::Result; + + /// MDRaidMember property + #[zbus(property, name = "MDRaidMember")] + fn mdraid_member(&self) -> zbus::Result; + + /// PreferredDevice property + #[zbus(property)] + fn preferred_device(&self) -> zbus::Result>; + + /// ReadOnly property + #[zbus(property)] + fn read_only(&self) -> zbus::Result; + + /// Size property + #[zbus(property)] + fn size(&self) -> zbus::Result; + + /// Symlinks property + #[zbus(property)] + fn symlinks(&self) -> zbus::Result>>; + + /// UserspaceMountOptions property + #[zbus(property)] + fn userspace_mount_options(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/udisks2/controller.rs b/extensions/core/src/dbus/udisks2/controller.rs new file mode 100644 index 000000000..a5f3e155f --- /dev/null +++ b/extensions/core/src/dbus/udisks2/controller.rs @@ -0,0 +1,115 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.NVMe.Controller` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/drives/xxxx' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.NVMe.Controller", + default_service = "org.freedesktop.UDisks2" +)] +trait Controller { + /// SanitizeStart method + fn sanitize_start( + &self, + action: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SmartGetAttributes method + fn smart_get_attributes( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result>; + + /// SmartSelftestAbort method + fn smart_selftest_abort( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SmartSelftestStart method + fn smart_selftest_start( + &self, + type_: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SmartUpdate method + fn smart_update( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// ControllerID property + #[zbus(property, name = "ControllerID")] + fn controller_id(&self) -> zbus::Result; + + /// FGUID property + #[zbus(property, name = "FGUID")] + fn fguid(&self) -> zbus::Result; + + /// NVMeRevision property + #[zbus(property, name = "NVMeRevision")] + fn nvme_revision(&self) -> zbus::Result; + + /// SanitizePercentRemaining property + #[zbus(property)] + fn sanitize_percent_remaining(&self) -> zbus::Result; + + /// SanitizeStatus property + #[zbus(property)] + fn sanitize_status(&self) -> zbus::Result; + + /// SmartCriticalWarning property + #[zbus(property)] + fn smart_critical_warning(&self) -> zbus::Result>; + + /// SmartPowerOnHours property + #[zbus(property)] + fn smart_power_on_hours(&self) -> zbus::Result; + + /// SmartSelftestPercentRemaining property + #[zbus(property)] + fn smart_selftest_percent_remaining(&self) -> zbus::Result; + + /// SmartSelftestStatus property + #[zbus(property)] + fn smart_selftest_status(&self) -> zbus::Result; + + /// SmartTemperature property + #[zbus(property)] + fn smart_temperature(&self) -> zbus::Result; + + /// SmartUpdated property + #[zbus(property)] + fn smart_updated(&self) -> zbus::Result; + + /// State property + #[zbus(property)] + fn state(&self) -> zbus::Result; + + /// SubsystemNQN property + #[zbus(property, name = "SubsystemNQN")] + fn subsystem_nqn(&self) -> zbus::Result>; + + /// UnallocatedCapacity property + #[zbus(property)] + fn unallocated_capacity(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/udisks2/drive.rs b/extensions/core/src/dbus/udisks2/drive.rs new file mode 100644 index 000000000..5ef6232c1 --- /dev/null +++ b/extensions/core/src/dbus/udisks2/drive.rs @@ -0,0 +1,164 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.Drive` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/drives/xxxx' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.Drive", + default_service = "org.freedesktop.UDisks2" +)] +trait Drive { + /// Eject method + fn eject( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// PowerOff method + fn power_off( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SetConfiguration method + fn set_configuration( + &self, + value: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// CanPowerOff property + #[zbus(property)] + fn can_power_off(&self) -> zbus::Result; + + /// Configuration property + #[zbus(property)] + fn configuration( + &self, + ) -> zbus::Result>; + + /// ConnectionBus property + #[zbus(property)] + fn connection_bus(&self) -> zbus::Result; + + /// Ejectable property + #[zbus(property)] + fn ejectable(&self) -> zbus::Result; + + /// Id property + #[zbus(property)] + fn id(&self) -> zbus::Result; + + /// Media property + #[zbus(property)] + fn media(&self) -> zbus::Result; + + /// MediaAvailable property + #[zbus(property)] + fn media_available(&self) -> zbus::Result; + + /// MediaChangeDetected property + #[zbus(property)] + fn media_change_detected(&self) -> zbus::Result; + + /// MediaCompatibility property + #[zbus(property)] + fn media_compatibility(&self) -> zbus::Result>; + + /// MediaRemovable property + #[zbus(property)] + fn media_removable(&self) -> zbus::Result; + + /// Model property + #[zbus(property)] + fn model(&self) -> zbus::Result; + + /// Optical property + #[zbus(property)] + fn optical(&self) -> zbus::Result; + + /// OpticalBlank property + #[zbus(property)] + fn optical_blank(&self) -> zbus::Result; + + /// OpticalNumAudioTracks property + #[zbus(property)] + fn optical_num_audio_tracks(&self) -> zbus::Result; + + /// OpticalNumDataTracks property + #[zbus(property)] + fn optical_num_data_tracks(&self) -> zbus::Result; + + /// OpticalNumSessions property + #[zbus(property)] + fn optical_num_sessions(&self) -> zbus::Result; + + /// OpticalNumTracks property + #[zbus(property)] + fn optical_num_tracks(&self) -> zbus::Result; + + /// Removable property + #[zbus(property)] + fn removable(&self) -> zbus::Result; + + /// Revision property + #[zbus(property)] + fn revision(&self) -> zbus::Result; + + /// RotationRate property + #[zbus(property)] + fn rotation_rate(&self) -> zbus::Result; + + /// Seat property + #[zbus(property)] + fn seat(&self) -> zbus::Result; + + /// Serial property + #[zbus(property)] + fn serial(&self) -> zbus::Result; + + /// SiblingId property + #[zbus(property)] + fn sibling_id(&self) -> zbus::Result; + + /// Size property + #[zbus(property)] + fn size(&self) -> zbus::Result; + + /// SortKey property + #[zbus(property)] + fn sort_key(&self) -> zbus::Result; + + /// TimeDetected property + #[zbus(property)] + fn time_detected(&self) -> zbus::Result; + + /// TimeMediaDetected property + #[zbus(property)] + fn time_media_detected(&self) -> zbus::Result; + + /// Vendor property + #[zbus(property)] + fn vendor(&self) -> zbus::Result; + + /// WWN property + #[zbus(property, name = "WWN")] + fn wwn(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/udisks2/filesystem.rs b/extensions/core/src/dbus/udisks2/filesystem.rs new file mode 100644 index 000000000..a58348fb1 --- /dev/null +++ b/extensions/core/src/dbus/udisks2/filesystem.rs @@ -0,0 +1,88 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.Filesystem` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/block_devices/nvme0n1p2' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.Filesystem", + default_service = "org.freedesktop.UDisks2", + default_path = "/org/freedesktop/UDisks2/block_devices/nvme0n1p2" +)] +trait Filesystem { + /// Check method + fn check( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// Mount method + fn mount( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// Repair method + fn repair( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// Resize method + fn resize( + &self, + size: u64, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SetLabel method + fn set_label( + &self, + label: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SetUUID method + #[zbus(name = "SetUUID")] + fn set_uuid( + &self, + uuid: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// TakeOwnership method + fn take_ownership( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// Unmount method + fn unmount( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// MountPoints property + #[zbus(property)] + fn mount_points(&self) -> zbus::Result>>; + + /// Size property + #[zbus(property)] + fn size(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/udisks2/manager.rs b/extensions/core/src/dbus/udisks2/manager.rs new file mode 100644 index 000000000..46d818e97 --- /dev/null +++ b/extensions/core/src/dbus/udisks2/manager.rs @@ -0,0 +1,93 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.Manager` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/Manager' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.Manager", + default_service = "org.freedesktop.UDisks2", + default_path = "/org/freedesktop/UDisks2/Manager" +)] +trait Manager { + /// CanCheck method + fn can_check(&self, type_: &str) -> zbus::Result<(bool, String)>; + + /// CanFormat method + fn can_format(&self, type_: &str) -> zbus::Result<(bool, String)>; + + /// CanRepair method + fn can_repair(&self, type_: &str) -> zbus::Result<(bool, String)>; + + /// CanResize method + fn can_resize(&self, type_: &str) -> zbus::Result<(bool, u64, String)>; + + /// EnableModule method + fn enable_module(&self, name: &str, enable: bool) -> zbus::Result<()>; + + /// EnableModules method + fn enable_modules(&self, enable: bool) -> zbus::Result<()>; + + /// GetBlockDevices method + fn get_block_devices( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result>; + + /// LoopSetup method + fn loop_setup( + &self, + fd: zbus::zvariant::Fd<'_>, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// MDRaidCreate method + #[zbus(name = "MDRaidCreate")] + fn mdraid_create( + &self, + blocks: &[&zbus::zvariant::ObjectPath<'_>], + level: &str, + name: &str, + chunk: u64, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// ResolveDevice method + fn resolve_device( + &self, + devspec: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result>; + + /// DefaultEncryptionType property + #[zbus(property)] + fn default_encryption_type(&self) -> zbus::Result; + + /// SupportedEncryptionTypes property + #[zbus(property)] + fn supported_encryption_types(&self) -> zbus::Result>; + + /// SupportedFilesystems property + #[zbus(property)] + fn supported_filesystems(&self) -> zbus::Result>; + + /// Version property + #[zbus(property)] + fn version(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/udisks2/namespace.rs b/extensions/core/src/dbus/udisks2/namespace.rs new file mode 100644 index 000000000..1e7c14c72 --- /dev/null +++ b/extensions/core/src/dbus/udisks2/namespace.rs @@ -0,0 +1,77 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.NVMe.Namespace` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/block_devices/xxxx' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.NVMe.Namespace", + default_service = "org.freedesktop.UDisks2" +)] +trait Namespace { + /// FormatNamespace method + fn format_namespace( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// EUI64 property + #[zbus(property, name = "EUI64")] + fn eui64(&self) -> zbus::Result; + + /// FormatPercentRemaining property + #[zbus(property)] + fn format_percent_remaining(&self) -> zbus::Result; + + /// FormattedLBASize property + #[zbus(property, name = "FormattedLBASize")] + fn formatted_lbasize(&self) -> zbus::Result<(u16, u16, u8)>; + + /// LBAFormats property + #[zbus(property, name = "LBAFormats")] + fn lbaformats(&self) -> zbus::Result>; + + /// NGUID property + #[zbus(property, name = "NGUID")] + fn nguid(&self) -> zbus::Result; + + /// NSID property + #[zbus(property, name = "NSID")] + fn nsid(&self) -> zbus::Result; + + /// NamespaceCapacity property + #[zbus(property)] + fn namespace_capacity(&self) -> zbus::Result; + + /// NamespaceSize property + #[zbus(property)] + fn namespace_size(&self) -> zbus::Result; + + /// NamespaceUtilization property + #[zbus(property)] + fn namespace_utilization(&self) -> zbus::Result; + + /// UUID property + #[zbus(property, name = "UUID")] + fn uuid(&self) -> zbus::Result; + + /// WWN property + #[zbus(property, name = "WWN")] + fn wwn(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/udisks2/nvme.rs b/extensions/core/src/dbus/udisks2/nvme.rs new file mode 100644 index 000000000..2eba39e19 --- /dev/null +++ b/extensions/core/src/dbus/udisks2/nvme.rs @@ -0,0 +1,61 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.Manager.NVMe` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/Manager' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.Manager.NVMe", + default_service = "org.freedesktop.UDisks2", + default_path = "/org/freedesktop/UDisks2/Manager" +)] +trait NVMe { + /// Connect method + fn connect( + &self, + subsysnqn: &[u8], + transport: &str, + transport_addr: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// SetHostID method + #[zbus(name = "SetHostID")] + fn set_host_id( + &self, + hostid: &[u8], + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SetHostNQN method + #[zbus(name = "SetHostNQN")] + fn set_host_nqn( + &self, + hostnqn: &[u8], + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// HostID property + #[zbus(property, name = "HostID")] + fn host_id(&self) -> zbus::Result>; + + /// HostNQN property + #[zbus(property, name = "HostNQN")] + fn host_nqn(&self) -> zbus::Result>; +} diff --git a/extensions/core/src/dbus/udisks2/partition.rs b/extensions/core/src/dbus/udisks2/partition.rs new file mode 100644 index 000000000..5b2afbc9d --- /dev/null +++ b/extensions/core/src/dbus/udisks2/partition.rs @@ -0,0 +1,110 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.Partition` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/block_devices/nvme0n1p2' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.Partition", + default_service = "org.freedesktop.UDisks2", + default_path = "/org/freedesktop/UDisks2/block_devices/nvme0n1p2" +)] +trait Partition { + /// Delete method + fn delete( + &self, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// Resize method + fn resize( + &self, + size: u64, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SetFlags method + fn set_flags( + &self, + flags: u64, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SetName method + fn set_name( + &self, + name: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SetType method + fn set_type( + &self, + type_: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// SetUUID method + #[zbus(name = "SetUUID")] + fn set_uuid( + &self, + uuid: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result<()>; + + /// Flags property + #[zbus(property)] + fn flags(&self) -> zbus::Result; + + /// IsContained property + #[zbus(property)] + fn is_contained(&self) -> zbus::Result; + + /// IsContainer property + #[zbus(property)] + fn is_container(&self) -> zbus::Result; + + /// Name property + #[zbus(property)] + fn name(&self) -> zbus::Result; + + /// Number property + #[zbus(property)] + fn number(&self) -> zbus::Result; + + /// Offset property + #[zbus(property)] + fn offset(&self) -> zbus::Result; + + /// Size property + #[zbus(property)] + fn size(&self) -> zbus::Result; + + /// Table property + #[zbus(property)] + fn table(&self) -> zbus::Result; + + /// Type property + #[zbus(property)] + fn type_(&self) -> zbus::Result; + + /// UUID property + #[zbus(property, name = "UUID")] + fn uuid(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/udisks2/partition_table.rs b/extensions/core/src/dbus/udisks2/partition_table.rs new file mode 100644 index 000000000..6538514c3 --- /dev/null +++ b/extensions/core/src/dbus/udisks2/partition_table.rs @@ -0,0 +1,58 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UDisks2.PartitionTable` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UDisks2/block_devices/xxxx' from service 'org.freedesktop.UDisks2' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UDisks2.PartitionTable", + default_service = "org.freedesktop.UDisks2" +)] +trait PartitionTable { + /// CreatePartition method + fn create_partition( + &self, + offset: u64, + size: u64, + type_: &str, + name: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// CreatePartitionAndFormat method + #[allow(clippy::too_many_arguments)] + fn create_partition_and_format( + &self, + offset: u64, + size: u64, + type_: &str, + name: &str, + options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + format_type: &str, + format_options: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, + ) -> zbus::Result; + + /// Partitions property + #[zbus(property)] + fn partitions(&self) -> zbus::Result>; + + /// Type property + #[zbus(property)] + fn type_(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/upower.rs b/extensions/core/src/dbus/upower.rs new file mode 100644 index 000000000..250fd4125 --- /dev/null +++ b/extensions/core/src/dbus/upower.rs @@ -0,0 +1,64 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UPower` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UPower' from service 'org.freedesktop.UPower' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, + +pub mod device; + +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UPower", + default_service = "org.freedesktop.UPower", + default_path = "/org/freedesktop/UPower" +)] +trait UPower { + /// EnumerateDevices method + fn enumerate_devices(&self) -> zbus::Result>; + + /// GetCriticalAction method + fn get_critical_action(&self) -> zbus::Result; + + /// GetDisplayDevice method + fn get_display_device(&self) -> zbus::Result; + + /// DeviceAdded signal + #[zbus(signal)] + fn device_added(&self, device: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// DeviceRemoved signal + #[zbus(signal)] + fn device_removed(&self, device: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>; + + /// DaemonVersion property + #[zbus(property)] + fn daemon_version(&self) -> zbus::Result; + + /// LidIsClosed property + #[zbus(property)] + fn lid_is_closed(&self) -> zbus::Result; + + /// LidIsPresent property + #[zbus(property)] + fn lid_is_present(&self) -> zbus::Result; + + /// OnBattery property + #[zbus(property)] + fn on_battery(&self) -> zbus::Result; +} diff --git a/extensions/core/src/dbus/upower/device.rs b/extensions/core/src/dbus/upower/device.rs new file mode 100644 index 000000000..5ea61037e --- /dev/null +++ b/extensions/core/src/dbus/upower/device.rs @@ -0,0 +1,162 @@ +//! # D-Bus interface proxy for: `org.freedesktop.UPower.Device` +//! +//! This code was generated by `zbus-xmlgen` `4.1.0` from D-Bus introspection data. +//! Source: `Interface '/org/freedesktop/UPower/devices/DisplayDevice' from service 'org.freedesktop.UPower' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the [Writing a client proxy] section of the zbus +//! documentation. +//! +//! This type implements the [D-Bus standard interfaces], (`org.freedesktop.DBus.*`) for which the +//! following zbus API can be used: +//! +//! * [`zbus::fdo::PropertiesProxy`] +//! * [`zbus::fdo::IntrospectableProxy`] +//! * [`zbus::fdo::PeerProxy`] +//! +//! Consequently `zbus-xmlgen` did not generate code for the above interfaces. +//! +//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html +//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces, +use zbus::proxy; +#[proxy( + interface = "org.freedesktop.UPower.Device", + default_service = "org.freedesktop.UPower", + default_path = "/org/freedesktop/UPower/devices/DisplayDevice" +)] +trait Device { + /// GetHistory method + fn get_history( + &self, + type_: &str, + timespan: u32, + resolution: u32, + ) -> zbus::Result>; + + /// GetStatistics method + fn get_statistics(&self, type_: &str) -> zbus::Result>; + + /// Refresh method + fn refresh(&self) -> zbus::Result<()>; + + /// BatteryLevel property + #[zbus(property)] + fn battery_level(&self) -> zbus::Result; + + /// Capacity property + #[zbus(property)] + fn capacity(&self) -> zbus::Result; + + /// ChargeCycles property + #[zbus(property)] + fn charge_cycles(&self) -> zbus::Result; + + /// Energy property + #[zbus(property)] + fn energy(&self) -> zbus::Result; + + /// EnergyEmpty property + #[zbus(property)] + fn energy_empty(&self) -> zbus::Result; + + /// EnergyFull property + #[zbus(property)] + fn energy_full(&self) -> zbus::Result; + + /// EnergyFullDesign property + #[zbus(property)] + fn energy_full_design(&self) -> zbus::Result; + + /// EnergyRate property + #[zbus(property)] + fn energy_rate(&self) -> zbus::Result; + + /// HasHistory property + #[zbus(property)] + fn has_history(&self) -> zbus::Result; + + /// HasStatistics property + #[zbus(property)] + fn has_statistics(&self) -> zbus::Result; + + /// IconName property + #[zbus(property)] + fn icon_name(&self) -> zbus::Result; + + /// IsPresent property + #[zbus(property)] + fn is_present(&self) -> zbus::Result; + + /// IsRechargeable property + #[zbus(property)] + fn is_rechargeable(&self) -> zbus::Result; + + /// Luminosity property + #[zbus(property)] + fn luminosity(&self) -> zbus::Result; + + /// Model property + #[zbus(property)] + fn model(&self) -> zbus::Result; + + /// NativePath property + #[zbus(property)] + fn native_path(&self) -> zbus::Result; + + /// Online property + #[zbus(property)] + fn online(&self) -> zbus::Result; + + /// Percentage property + #[zbus(property)] + fn percentage(&self) -> zbus::Result; + + /// PowerSupply property + #[zbus(property)] + fn power_supply(&self) -> zbus::Result; + + /// Serial property + #[zbus(property)] + fn serial(&self) -> zbus::Result; + + /// State property + #[zbus(property)] + fn state(&self) -> zbus::Result; + + /// Technology property + #[zbus(property)] + fn technology(&self) -> zbus::Result; + + /// Temperature property + #[zbus(property)] + fn temperature(&self) -> zbus::Result; + + /// TimeToEmpty property + #[zbus(property)] + fn time_to_empty(&self) -> zbus::Result; + + /// TimeToFull property + #[zbus(property)] + fn time_to_full(&self) -> zbus::Result; + + /// Type property + #[zbus(property)] + fn type_(&self) -> zbus::Result; + + /// UpdateTime property + #[zbus(property)] + fn update_time(&self) -> zbus::Result; + + /// Vendor property + #[zbus(property)] + fn vendor(&self) -> zbus::Result; + + /// Voltage property + #[zbus(property)] + fn voltage(&self) -> zbus::Result; + + /// WarningLevel property + #[zbus(property)] + fn warning_level(&self) -> zbus::Result; +} diff --git a/extensions/core/src/disk.rs b/extensions/core/src/disk.rs new file mode 100644 index 000000000..12d39c2a7 --- /dev/null +++ b/extensions/core/src/disk.rs @@ -0,0 +1 @@ +pub mod udisks2; diff --git a/extensions/core/src/disk/udisks2.rs b/extensions/core/src/disk/udisks2.rs new file mode 100644 index 000000000..f3c2263a9 --- /dev/null +++ b/extensions/core/src/disk/udisks2.rs @@ -0,0 +1,494 @@ +pub mod block_device; +pub mod drive_device; +pub mod filesystem_device; +pub mod partition_device; + +use std::{ + collections::HashMap, + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, + time::Duration, +}; + +use block_device::BlockDevice; +use drive_device::DriveDevice; +use filesystem_device::FilesystemDevice; +use futures_util::stream::StreamExt; +use godot::{classes::Engine, obj::WithBaseField, prelude::*}; +use partition_device::PartitionDevice; +use zbus::fdo::{ManagedObjects, ObjectManagerProxy}; +use zbus::names::BusName; + +use crate::{dbus::RunError, get_dbus_system, get_dbus_system_blocking, RUNTIME}; + +pub const UDISKS2_BUS: &str = "org.freedesktop.UDisks2"; +const UDISKS2_PATH: &str = "/org/freedesktop/UDisks2"; + +/// List of mount points that should not be considered for formatting +const PROTECTED_MOUNTS: [&str; 10] = [ + "/", + "/boot", + "/boot/efi", + "/efi", + "/frzr_root", + "/frzr_root/boot", + "/home", + "/var", + "/var/cache", + "/var/log", +]; + +/// Supported UDisks2 DBus objects +#[derive(Debug, Clone, Copy)] +enum ObjectType { + Block, + Drive, + Partition, + Filesystem, +} + +impl ObjectType { + /// Returns the object type(s) from the list of implemented interfaces + fn from_ifaces(ifaces: Vec) -> Vec { + let mut types = vec![]; + + for iface in ifaces { + if iface.as_str() == "org.freedesktop.UDisks2.Drive" { + types.push(Self::Drive); + } + if iface.as_str() == "org.freedesktop.UDisks2.Block" { + types.push(Self::Block); + } + if iface.as_str() == "org.freedesktop.UDisks2.Partition" { + types.push(Self::Partition); + } + if iface.as_str() == "org.freedesktop.UDisks2.Filesystem" { + types.push(Self::Filesystem); + } + } + + types + } +} + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Started, + Stopped, + ObjectAdded { path: String, ifaces: Vec }, + ObjectRemoved { path: String, ifaces: Vec }, +} + +#[derive(GodotClass)] +#[class(base=Resource)] +pub struct UDisks2Instance { + base: Base, + rx: Receiver, + conn: Option, + drive_devices: HashMap>, + block_devices: HashMap>, + partition_devices: HashMap>, + filesystem_devices: HashMap>, +} + +#[godot_api] +impl UDisks2Instance { + /// Emitted when UDisks2 is detected as running + #[signal] + fn started(); + + /// Emitted when UDisks2 is detected as stopped + #[signal] + fn stopped(); + + #[signal] + fn drive_device_added(drive: Gd); + + #[signal] + fn drive_device_removed(dbus_path: GString); + + #[signal] + fn block_device_added(block: Gd); + + #[signal] + fn block_device_removed(dbus_path: GString); + + #[signal] + fn partition_added(partition: Gd); + + #[signal] + fn partition_removed(dbus_path: GString); + + #[signal] + fn filesystem_added(filesystem: Gd); + + #[signal] + fn filesystem_removed(dbus_path: GString); + + /// Emitted when any state change occurs and emits an [Array] of [BlockDevice] that have no + /// [FilesystemDevice] with mounts located in [PROTECTED_MOUNTS]. + #[signal] + fn unprotected_devices_updated(devices: Array>); + + /// Returns true if the UDisks2 service is currently running + #[func] + fn is_running(&self) -> bool { + let Some(conn) = self.conn.as_ref() else { + return false; + }; + let bus = BusName::from_static_str(UDISKS2_BUS).unwrap(); + let dbus = zbus::blocking::fdo::DBusProxy::new(conn).ok(); + let Some(dbus) = dbus else { + return false; + }; + dbus.name_has_owner(bus.clone()).unwrap_or_default() + } + + /// Returns a HashMap of all the objects managed by this dbus interface + fn get_managed_objects(&self) -> Result { + let Some(conn) = self.conn.as_ref() else { + return Err(zbus::fdo::Error::Disconnected( + "No DBus connection found".into(), + )); + }; + + let bus = BusName::from_static_str(UDISKS2_BUS).unwrap(); + let object_manager = zbus::blocking::fdo::ObjectManagerProxy::builder(conn) + .destination(bus) + .ok() + .and_then(|builder| builder.path(UDISKS2_PATH).ok()) + .and_then(|builder| builder.build().ok()); + let Some(object_manager) = object_manager else { + return Ok(ManagedObjects::new()); + }; + + object_manager.get_managed_objects() + } + + /// Returns a HashMap of all the objects managed by this dbus interface that don't have + /// [FilesystemDevice] objects with mounts in [PROTECTED_MOUNTS] + #[func] + fn get_unprotected_devices(&self) -> Array> { + let mut unprotected_devices = array![]; + 'outer: for (dbus_path, block_device) in self.block_devices.iter() { + let partitions = block_device.bind().get_partitions(); + if partitions.is_empty() { + if !self.partition_devices.contains_key(dbus_path) { + log::debug!( + "Adding {dbus_path} as unprotected device. It is not a partition_devices" + ); + unprotected_devices.push(block_device); + continue; + } + log::debug!("Skipping {dbus_path}. It is a partition_device."); + } else { + for partition_device in partitions.iter_shared() { + let Some(filesystem_device) = partition_device.bind().get_filesystem() else { + log::debug!( + "Adding {dbus_path} as unprotected device. It does not have a FilesystemDevice" + ); + unprotected_devices.push(block_device); + continue; + }; + + let mounts = filesystem_device.bind().get_mounts(); + for mount in mounts.as_slice() { + if PROTECTED_MOUNTS.contains(&mount.to_string().as_str()) { + continue 'outer; + } + } + } + log::debug!( + "Adding {dbus_path} as unprotected device. It does not have any mounts in PROTECTED_MOUNTS" + ); + unprotected_devices.push(block_device); + } + } + + unprotected_devices + } + + /// Process UDisks2 signals and emit them as Godot signals. This method + /// should be called every frame in the "_process" loop of a node. + #[func] + fn process(&mut self) { + let mut state_updated = false; + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + state_updated = true; + self.process_signal(signal); + } + if !state_updated { + return; + } + let unprotected_devices = self.get_unprotected_devices(); + self.base_mut().emit_signal( + "unprotected_devices_updated", + &[unprotected_devices.to_variant()], + ); + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Started => { + self.base_mut().emit_signal("started", &[]); + } + Signal::Stopped => { + self.base_mut().emit_signal("stopped", &[]); + } + Signal::ObjectAdded { path, ifaces } => { + let obj_types = ObjectType::from_ifaces(ifaces); + for obj_type in obj_types { + match obj_type { + ObjectType::Block => { + let block = BlockDevice::new(path.as_str()); + self.block_devices.insert(path.clone(), block.clone()); + self.base_mut() + .emit_signal("block_device_added", &[block.to_variant()]); + } + ObjectType::Drive => { + let drive = DriveDevice::new(path.as_str()); + self.drive_devices.insert(path.clone(), drive.clone()); + self.base_mut() + .emit_signal("drive_device_added", &[drive.to_variant()]); + } + ObjectType::Partition => { + let partition = PartitionDevice::new(path.as_str()); + self.partition_devices + .insert(path.clone(), partition.clone()); + self.base_mut() + .emit_signal("partition_added", &[partition.to_variant()]); + } + ObjectType::Filesystem => { + let fs = FilesystemDevice::new(path.as_str()); + self.filesystem_devices.insert(path.clone(), fs.clone()); + self.base_mut() + .emit_signal("filesystem_added", &[fs.to_variant()]); + } + } + } + } + Signal::ObjectRemoved { path, ifaces } => { + let obj_types = ObjectType::from_ifaces(ifaces); + for obj_type in obj_types { + match obj_type { + ObjectType::Block => { + self.block_devices.remove(&path); + self.base_mut() + .emit_signal("block_device_removed", &[path.to_variant()]); + } + ObjectType::Drive => { + self.drive_devices.remove(&path); + self.base_mut() + .emit_signal("drive_device_removed", &[path.to_variant()]); + } + ObjectType::Partition => { + self.partition_devices.remove(&path); + self.base_mut() + .emit_signal("partition_removed", &[path.to_variant()]); + } + ObjectType::Filesystem => { + self.filesystem_devices.remove(&path); + self.base_mut() + .emit_signal("filesystem_removed", &[path.to_variant()]); + } + } + } + } + } + } +} + +#[godot_api] +impl IResource for UDisks2Instance { + /// Called upon object initialization in the engine + fn init(base: Base) -> Self { + log::debug!("Initializing UDisks2 instance"); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + let conn = get_dbus_system_blocking().ok(); + + // Don't run in the editor + let engine = Engine::singleton(); + if engine.is_editor_hint() { + return Self { + base, + rx, + conn, + block_devices: Default::default(), + drive_devices: Default::default(), + partition_devices: Default::default(), + filesystem_devices: Default::default(), + }; + } + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx).await { + log::error!("Failed to run UDisks2 task: ${e:?}"); + } + }); + + // Create a new UDisks2 instance + let mut instance = Self { + base, + rx, + conn, + block_devices: HashMap::new(), + drive_devices: HashMap::new(), + partition_devices: HashMap::new(), + filesystem_devices: HashMap::new(), + }; + + // Perform initial object discovery + let mut block_devices = HashMap::new(); + let mut drive_devices = HashMap::new(); + let mut partition_devices = HashMap::new(); + let mut filesystem_devices = HashMap::new(); + let objects = instance.get_managed_objects().unwrap_or_default(); + for (path, ifaces) in objects.into_iter() { + let path = path.to_string(); + let ifaces: Vec = ifaces.into_keys().map(|v| v.to_string()).collect(); + let obj_types = ObjectType::from_ifaces(ifaces); + + for obj_type in obj_types { + match obj_type { + ObjectType::Block => { + let block = BlockDevice::new(path.as_str()); + block_devices.insert(path.clone(), block); + } + ObjectType::Drive => { + let drive = DriveDevice::new(path.as_str()); + drive_devices.insert(path.clone(), drive); + } + ObjectType::Partition => { + let partition = PartitionDevice::new(path.as_str()); + partition_devices.insert(path.clone(), partition); + } + ObjectType::Filesystem => { + let fs = FilesystemDevice::new(path.as_str()); + filesystem_devices.insert(path.clone(), fs); + } + } + } + } + instance.block_devices = block_devices; + instance.drive_devices = drive_devices; + instance.partition_devices = partition_devices; + instance.filesystem_devices = filesystem_devices; + + instance + } +} + +/// Runs UDisks2 tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning UDisks2 tasks"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Spawn a task to listen for UDisks2 start/stop + let dbus_conn = conn.clone(); + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + let bus = BusName::from_static_str(UDISKS2_BUS).unwrap(); + let mut is_running = { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + return; + }; + dbus.name_has_owner(bus.clone()).await.unwrap_or_default() + }; + + loop { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + break; + }; + let running = dbus.name_has_owner(bus.clone()).await.unwrap_or_default(); + if running != is_running { + let signal = if running { + Signal::Started + } else { + Signal::Stopped + }; + if signals_tx.send(signal).is_err() { + break; + } + } + is_running = running; + tokio::time::sleep(Duration::from_secs(5)).await; + } + }); + + // Get a proxy instance to ObjectManager + let bus = BusName::from_static_str(UDISKS2_BUS).unwrap(); + let object_manager: ObjectManagerProxy = ObjectManagerProxy::builder(&conn) + .destination(bus)? + .path(UDISKS2_PATH)? + .build() + .await?; + + // Spawn a task to listen for objects added + let mut ifaces_added = object_manager.receive_interfaces_added().await?; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + while let Some(signal) = ifaces_added.next().await { + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + + let path = args.object_path.to_string(); + let ifaces = args + .interfaces_and_properties + .keys() + .map(|v| v.to_string()) + .collect(); + let signal = Signal::ObjectAdded { path, ifaces }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + // Spawn a task to listen for objects removed + let mut ifaces_removed = object_manager.receive_interfaces_removed().await?; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + while let Some(signal) = ifaces_removed.next().await { + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + + let path = args.object_path.to_string(); + let ifaces = args.interfaces.iter().map(|v| v.to_string()).collect(); + let signal = Signal::ObjectRemoved { path, ifaces }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/disk/udisks2/block_device.rs b/extensions/core/src/disk/udisks2/block_device.rs new file mode 100644 index 000000000..d4d77d842 --- /dev/null +++ b/extensions/core/src/disk/udisks2/block_device.rs @@ -0,0 +1,154 @@ +use byte_unit::{Byte, UnitType}; +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::udisks2::{ + block::BlockProxyBlocking, partition_table::PartitionTableProxyBlocking, +}; +use crate::get_dbus_system_blocking; + +use super::drive_device::DriveDevice; +use super::partition_device::PartitionDevice; +use super::UDISKS2_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct BlockDevice { + base: Base, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + + #[allow(dead_code)] + #[var(get = get_readable_size)] + readable_size: GString, +} + +#[godot_api] +impl BlockDevice { + #[signal] + fn updated(); + + /// Create a new [BlockDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + // Create a channel to communicate with the signals task + log::debug!("BlockDevice created with path: {path}"); + + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + dbus_path: path, + readable_size: Default::default(), + } + }) + } + + /// Return a proxy instance to the device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + BlockProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Return a proxy instance to the partition table dbus interface + fn get_partition_table_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + PartitionTableProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [BlockDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{UDISKS2_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = BlockDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = BlockDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Get all the partitions that this block device contains + #[func] + pub fn get_partitions(&self) -> Array> { + let mut all_partitions = array![]; + let Some(proxy) = self.get_partition_table_proxy() else { + return array![]; + }; + + let partitions = proxy.partitions().unwrap_or_default(); + for partition in partitions { + let path = partition.as_str(); + let partition_device = PartitionDevice::new(path); + all_partitions.push(&partition_device); + } + + all_partitions + } + + /// Return the parent DriveDevice for this BlockDevice + #[func] + pub fn get_drive(&self) -> Option> { + let proxy = self.get_proxy()?; + let drive = proxy.drive().ok()?; + Some(DriveDevice::new(drive.as_str())) + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + /// Return the size type of the [BlockDevice] as a human readable String + #[func] + pub fn get_readable_size(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let size_bytes = proxy.size().unwrap_or(0); + + let size = Byte::from_u64(size_bytes).get_appropriate_unit(UnitType::Decimal); + format!("{size:.2}").to_godot() + } +} + +impl Drop for BlockDevice { + fn drop(&mut self) { + log::trace!("BlockDevice '{}' is being destroyed!", self.dbus_path); + } +} diff --git a/extensions/core/src/disk/udisks2/drive_device.rs b/extensions/core/src/disk/udisks2/drive_device.rs new file mode 100644 index 000000000..ad3a40455 --- /dev/null +++ b/extensions/core/src/disk/udisks2/drive_device.rs @@ -0,0 +1,141 @@ +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::udisks2::drive::DriveProxyBlocking; +use crate::get_dbus_system_blocking; + +use super::UDISKS2_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct DriveDevice { + base: Base, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, +} + +#[godot_api] +impl DriveDevice { + #[constant] + const INTERFACE_TYPE_HDD: u16 = 0; + #[constant] + const INTERFACE_TYPE_NVME: u16 = 1; + #[constant] + const INTERFACE_TYPE_SD: u16 = 2; + #[constant] + const INTERFACE_TYPE_SSD: u16 = 3; + #[constant] + const INTERFACE_TYPE_USB: u16 = 4; + #[constant] + const INTERFACE_TYPE_UNKNOWN: u16 = 5; + + #[signal] + fn updated(); + + /// Create a new [DriveDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + // Create a channel to communicate with the signals task + log::debug!("DriveDevice created with path: {path}"); + + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + dbus_path: path, + } + }) + } + + /// Return a proxy instance to the device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + DriveProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [DriveDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{UDISKS2_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = DriveDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = DriveDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Returns the drive devices interface type + #[func] + pub fn interface_type(&self) -> u16 { + let Some(proxy) = self.get_proxy() else { + return DriveDevice::INTERFACE_TYPE_UNKNOWN; + }; + let Ok(connection) = proxy.connection_bus() else { + return DriveDevice::INTERFACE_TYPE_UNKNOWN; + }; + match connection.as_str() { + "usb" => DriveDevice::INTERFACE_TYPE_USB, + "sdio" => DriveDevice::INTERFACE_TYPE_SD, + "" => { + let Ok(sort_key) = proxy.sort_key() else { + return DriveDevice::INTERFACE_TYPE_UNKNOWN; + }; + if sort_key.contains("hotplug") || sort_key.contains("removable") { + return DriveDevice::INTERFACE_TYPE_USB; + } else if sort_key.contains("nvme") { + return DriveDevice::INTERFACE_TYPE_NVME; + } else if sort_key.contains("sd_") { + let Ok(rotation_rate) = proxy.rotation_rate() else { + return DriveDevice::INTERFACE_TYPE_UNKNOWN; + }; + if rotation_rate > 0 { + return DriveDevice::INTERFACE_TYPE_HDD; + } + return DriveDevice::INTERFACE_TYPE_SSD; + } + DriveDevice::INTERFACE_TYPE_UNKNOWN + } + _ => DriveDevice::INTERFACE_TYPE_UNKNOWN, + } + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } +} + +impl Drop for DriveDevice { + fn drop(&mut self) { + log::trace!("DriveDevice '{}' is being destroyed!", self.dbus_path); + } +} diff --git a/extensions/core/src/disk/udisks2/filesystem_device.rs b/extensions/core/src/disk/udisks2/filesystem_device.rs new file mode 100644 index 000000000..69c99c684 --- /dev/null +++ b/extensions/core/src/disk/udisks2/filesystem_device.rs @@ -0,0 +1,109 @@ +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::udisks2::filesystem::FilesystemProxyBlocking; +use crate::get_dbus_system_blocking; + +use super::UDISKS2_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct FilesystemDevice { + base: Base, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, +} + +#[godot_api] +impl FilesystemDevice { + #[signal] + fn updated(); + + /// Create a new [FilesystemDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + // Create a channel to communicate with the signals task + log::debug!("FilesystemDevice created with path: {path}"); + + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + dbus_path: path, + } + }) + } + + /// Return a proxy instance to the device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + FilesystemProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [FilesystemDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{UDISKS2_BUS}{path}/filesystem"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = FilesystemDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = FilesystemDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Get all the mount points for this [FilesystemDevice] + #[func] + pub fn get_mounts(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let mut mount_points = PackedStringArray::new(); + let mount_points_bytes = proxy.mount_points().unwrap_or_default(); + for mount_point_bytes in mount_points_bytes { + let mount_point = String::from_utf8_lossy(mount_point_bytes.as_slice()); + mount_points.push(mount_point.to_string().as_str()); + } + + mount_points + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } +} + +impl Drop for FilesystemDevice { + fn drop(&mut self) { + log::trace!("FilesystemDevice '{}' is being destroyed!", self.dbus_path); + } +} diff --git a/extensions/core/src/disk/udisks2/partition_device.rs b/extensions/core/src/disk/udisks2/partition_device.rs new file mode 100644 index 000000000..82ff2b940 --- /dev/null +++ b/extensions/core/src/disk/udisks2/partition_device.rs @@ -0,0 +1,178 @@ +use byte_unit::{Byte, UnitType}; +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::udisks2::{ + block::BlockProxyBlocking, filesystem::FilesystemProxyBlocking, + partition::PartitionProxyBlocking, +}; +use crate::get_dbus_system_blocking; + +use super::filesystem_device::FilesystemDevice; +use super::UDISKS2_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct PartitionDevice { + base: Base, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_filesystem_type)] + filesystem_type: GString, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + + #[allow(dead_code)] + #[var(get = get_partition_name)] + partition_name: GString, + + #[allow(dead_code)] + #[var(get = get_readable_size)] + readable_size: GString, +} + +#[godot_api] +impl PartitionDevice { + #[signal] + fn updated(); + + /// Create a new [PartitionDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + // Create a channel to communicate with the signals task + log::debug!("PartitionDevice created with path: {path}"); + + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + dbus_path: path, + filesystem_type: Default::default(), + partition_name: Default::default(), + readable_size: Default::default(), + } + }) + } + /// Return a proxy instance to the block device dbus interface + fn get_block_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + BlockProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Return a proxy instance to the partition dbus interface + fn get_partition_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + PartitionProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Return a proxy instance to the filesystem dbus interface + fn get_filesystem_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + FilesystemProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [PartitionDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{UDISKS2_BUS}{path}/partition"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = PartitionDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = PartitionDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Get all the partitions that this [BlockDevice] contains + #[func] + pub fn get_filesystem(&self) -> Option> { + // Check if we have a filesystem dbus interface + self.get_filesystem_proxy()?; + + // If we have one, return a filesystem device + Some(FilesystemDevice::new(self.dbus_path.to_string().as_str())) + } + + /// Return the DBus path to the [Partitiondevice] + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + /// Return the filesystem type of the [BlockDevice] + #[func] + pub fn get_filesystem_type(&self) -> GString { + let Some(proxy) = self.get_block_proxy() else { + return Default::default(); + }; + proxy.id_type().unwrap_or_default().to_godot() + } + + /// Return the name of the [PartitionDevice] + #[func] + pub fn get_partition_name(&self) -> GString { + let Some(proxy) = self.get_partition_proxy() else { + return Default::default(); + }; + proxy.name().unwrap_or_default().to_godot() + } + + /// Return the size type of the [PartitionDevice] as a human readable String + #[func] + pub fn get_readable_size(&self) -> GString { + let Some(proxy) = self.get_partition_proxy() else { + return Default::default(); + }; + let size_bytes = proxy.size().unwrap_or(0); + + let size = Byte::from_u64(size_bytes).get_appropriate_unit(UnitType::Decimal); + format!("{size:.2}").to_godot() + } +} + +impl Drop for PartitionDevice { + fn drop(&mut self) { + log::trace!("PartitionDevice '{}' is being destroyed!", self.dbus_path); + } +} diff --git a/extensions/core/src/gamescope.rs b/extensions/core/src/gamescope.rs new file mode 100644 index 000000000..92e2570e5 --- /dev/null +++ b/extensions/core/src/gamescope.rs @@ -0,0 +1,158 @@ +pub mod x11_client; + +use std::collections::HashMap; +use std::env; +use x11_client::GamescopeXWayland; + +use godot::prelude::*; + +use godot::classes::{Engine, Resource}; + +#[derive(GodotClass)] +#[class(base=Resource)] +pub struct GamescopeInstance { + base: Base, + xwaylands: HashMap>, + xwayland_primary: String, + xwayland_ogui: String, + xwayland_game: String, +} + +#[godot_api] +impl GamescopeInstance { + /// Primary Gamescope xwayland instance + #[constant] + const XWAYLAND_TYPE_PRIMARY: u32 = 0; + + /// Xwayland instance that OpenGamepadUI is running on + #[constant] + const XWAYLAND_TYPE_OGUI: u32 = 1; + + /// Xwayland instance where games run + #[constant] + const XWAYLAND_TYPE_GAME: u32 = 2; + + /// Gamescope is hard-coded to look for STEAM_GAME=769 to determine if it is the + /// overlay app. + #[constant] + const OVERLAY_GAME_ID: u32 = 769; + + /// Return the Gamescope XWayland of the given type. + #[func] + pub fn get_xwayland(&self, kind: u32) -> Option> { + match kind { + GamescopeInstance::XWAYLAND_TYPE_PRIMARY => { + let xwayland = self.xwaylands.get(&self.xwayland_primary); + xwayland.cloned() + } + GamescopeInstance::XWAYLAND_TYPE_OGUI => { + let xwayland = self.xwaylands.get(&self.xwayland_ogui); + xwayland.cloned() + } + GamescopeInstance::XWAYLAND_TYPE_GAME => { + let xwayland = self.xwaylands.get(&self.xwayland_game); + xwayland.cloned() + } + _ => None, + } + } + + /// Return all known XWayland instances + #[func] + pub fn get_xwaylands(&self) -> Array> { + let mut xwaylands = array![]; + for xwayland in self.xwaylands.values() { + xwaylands.push(xwayland); + } + + xwaylands + } + + /// Returns the XWayland display with the given name (e.g. ":0") + #[func] + pub fn get_xwayland_by_name(&self, name: GString) -> Option> { + let name: String = name.into(); + self.xwaylands.get(&name).cloned() + } + + /// Process Gamescope signals and emit them as Godot signals. This method + /// should be called every frame in the "_process" loop of a node. + #[func] + pub fn process(&mut self) { + for (_, xwayland) in self.xwaylands.iter_mut() { + xwayland.bind_mut().process(); + } + } +} + +#[godot_api] +impl IResource for GamescopeInstance { + /// Called upon object initialization in the engine + fn init(base: Base) -> Self { + log::debug!("Initializing Gamescope instance"); + + // Don't run in the editor + let engine = Engine::singleton(); + if engine.is_editor_hint() { + return Self { + base, + xwaylands: Default::default(), + xwayland_primary: Default::default(), + xwayland_ogui: Default::default(), + xwayland_game: Default::default(), + }; + } + + // Discover any gamescope instances + let result = gamescope_x11_client::discover_gamescope_displays(); + let x11_displays = match result { + Ok(displays) => displays, + Err(e) => { + log::warn!("Failed to get Gamescope displays: {e:?}"); + return Self { + base, + xwaylands: HashMap::new(), + xwayland_primary: Default::default(), + xwayland_ogui: Default::default(), + xwayland_game: Default::default(), + }; + } + }; + + // Get the X11 display that the process knows about + let ogui_display = env::var("DISPLAY").unwrap_or(":0".into()); + + // Keep track of discovered XWaylands + let mut xwaylands = HashMap::new(); + let mut xwayland_primary = Default::default(); + let mut xwayland_ogui = Default::default(); + let mut xwayland_game = Default::default(); + + // Create an XWayland instance for each discovered XWayland display + for display in x11_displays { + log::debug!("Discovered XWayland display: {display}"); + let xwayland = GamescopeXWayland::new(display.as_str()); + + // Categorize the discovered displays + if display == ogui_display { + xwayland_ogui = display.clone(); + } + if xwayland.bind().get_is_primary() { + xwayland_primary = display.clone(); + } else { + xwayland_game = display.clone(); + } + + xwaylands.insert(display, xwayland); + } + + // Create a new Gamescope instance + Self { + base, + xwaylands, + xwayland_ogui, + xwayland_game, + xwayland_primary, + } + } +} diff --git a/extensions/core/src/gamescope/x11_client.rs b/extensions/core/src/gamescope/x11_client.rs new file mode 100644 index 000000000..4f3d46f86 --- /dev/null +++ b/extensions/core/src/gamescope/x11_client.rs @@ -0,0 +1,1076 @@ +use gamescope_x11_client::{ + atoms::GamescopeAtom, + xwayland::{BlurMode, Primary, WindowLifecycleEvent, XWayland}, +}; +use std::{ + collections::HashMap, + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, + time::Duration, +}; +use tokio::task::AbortHandle; + +use godot::{obj::WithBaseField, prelude::*}; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::RUNTIME; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + WindowCreated { window_id: u32 }, + WindowDestroyed { window_id: u32 }, + WindowPropertyChanged { window_id: u32, property: String }, + PropertyChanged { property: String }, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct GamescopeXWayland { + base: Base, + rx: Receiver, + tx: Sender, + xwayland: XWayland, + window_watch_handles: HashMap, + + /// The name of the XWayland instance (e.g. ":0") + #[var] + name: GString, + /// Returns true if this [GamescopeXWayland] is the primary instance + #[var] + is_primary: bool, + /// Returns the root window id of the [GamescopeXWayland] instance + #[var] + root_window_id: u32, + /// List of windows currently being watched for property changes + #[var(get = get_watched_windows)] + watched_windows: PackedInt64Array, + /// List of focusable apps + #[var(get = get_focusable_apps)] + focusable_apps: PackedInt64Array, + /// List of focusable windows + #[var(get = get_focusable_windows)] + focusable_windows: PackedInt64Array, + /// List of focusable window names + #[var(get = get_focusable_window_names)] + focusable_window_names: PackedStringArray, + /// Currently focused window id + #[var(get = get_focused_window)] + focused_window: u32, + /// Currently focused app id + #[var(get = get_focused_app)] + focused_app: u32, + /// Currently focused gfx app id + #[var(get = get_focused_app_gfx)] + focused_app_gfx: u32, + /// Whether or not the overlay window is currently focused + #[var(get = get_overlay_focused)] + overlay_focused: bool, + /// Current Gamescope FPS limit + #[var(get = get_fps_limit, set = set_fps_limit)] + fps_limit: u32, + /// Gamecope blur mode (0 - off, 1 - cond, 2 - always) + #[var(get = get_blur_mode, set = set_blur_mode)] + blur_mode: u32, + /// Gamescope blur radius + #[var(get = get_blur_radius, set = set_blur_radius)] + blur_radius: u32, + /// Whether or not Gamescope should be allowed to screen tear + #[var(get = get_allow_tearing, set = set_allow_tearing)] + allow_tearing: bool, + /// Current manually focused window + #[var(get = get_baselayer_window, set = set_baselayer_window)] + baselayer_window: u32, + /// Current manually focused app + #[var(get = get_baselayer_app, set = set_baselayer_app)] + baselayer_app: u32, +} + +#[godot_api] +impl GamescopeXWayland { + #[constant] + const BLUR_MODE_OFF: u32 = 0; + #[constant] + const BLUR_MODE_COND: u32 = 1; + #[constant] + const BLUR_MODE_ALWAYS: u32 = 2; + + #[signal] + fn window_created(window_id: u32); + + #[signal] + fn window_destroyed(window_id: u32); + + #[signal] + fn window_property_updated(window_id: u32, property: GString); + + #[signal] + fn focused_app_updated(from: u32, to: u32); + + #[signal] + fn focused_app_gfx_updated(from: u32, to: u32); + + #[signal] + fn focusable_apps_updated(from: PackedInt64Array, to: PackedInt64Array); + + #[signal] + fn focused_window_updated(from: u32, to: u32); + + #[signal] + fn focusable_windows_updated(from: PackedInt64Array, to: PackedInt64Array); + + #[signal] + fn baselayer_window_updated(from: u32, to: u32); + + #[signal] + fn baselayer_app_updated(from: u32, to: u32); + + /// Create a new [GamescopeXWayland] with the given name (e.g. ":0") + pub fn from_name(name: GString) -> Gd { + // Create a channel to communicate with the signals task + log::debug!("Gamescope XWayland created with name: {name}"); + let (tx, rx) = channel(); + + // Create an XWayland client instance for this display + let mut xwayland = XWayland::new(name.clone().into()); + if let Err(e) = xwayland.connect() { + log::error!("Failed to connect to XWayland display '{name}': {e:?}"); + } + let is_primary = xwayland.is_primary_instance().unwrap_or_default(); + let root_window_id = xwayland.get_root_window_id().unwrap_or_default(); + + // If this XWayland instance is a primary instance, listen for signals + if is_primary { + // Spawn a task to listen for property changes + if let Ok((_, property_rx)) = xwayland.listen_for_property_changes() { + let signals_tx = tx.clone(); + RUNTIME.spawn_blocking(move || { + for event in property_rx.into_iter() { + let signal = Signal::PropertyChanged { property: event }; + if let Err(e) = signals_tx.send(signal) { + log::error!("Error sending property changed signal: {e:?}"); + break; + } + } + }); + } else { + log::error!("Failed to listen for XWayland property changes"); + } + } + + // Spawn a task to listen for window creation events + if let Ok((_, windows_rx)) = xwayland.listen_for_window_lifecycle() { + let signals_tx = tx.clone(); + RUNTIME.spawn_blocking(move || { + for (event, window_id) in windows_rx.into_iter() { + let signal = match event { + WindowLifecycleEvent::Created => Signal::WindowCreated { window_id }, + WindowLifecycleEvent::Destroyed => Signal::WindowDestroyed { window_id }, + }; + if let Err(e) = signals_tx.send(signal) { + log::error!("Error sending window signal: {e:?}"); + break; + } + } + }); + } else { + log::error!("Failed to listen for XWayland windows created/destroyed"); + } + + // Setup the initial state + Gd::from_init_fn(|base| { + // Accept a base of type Base and directly forward it. + Self { + base, + rx, + tx, + name, + xwayland, + is_primary, + root_window_id, + watched_windows: Default::default(), + window_watch_handles: Default::default(), + focusable_apps: Default::default(), + focusable_windows: Default::default(), + focusable_window_names: Default::default(), + focused_window: Default::default(), + focused_app: Default::default(), + focused_app_gfx: Default::default(), + overlay_focused: Default::default(), + fps_limit: Default::default(), + blur_mode: Default::default(), + blur_radius: Default::default(), + allow_tearing: Default::default(), + baselayer_window: Default::default(), + baselayer_app: Default::default(), + } + }) + } + + /// Get or create a [GamescopeXWayland] with the given name. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(name: &str) -> Gd { + let res_path = format!("gamescope://xwayland/{name}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = GamescopeXWayland::from_name(name.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = GamescopeXWayland::from_name(name.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Returns the list of currently watched windows. + #[func] + pub fn get_watched_windows(&self) -> PackedInt64Array { + self.watched_windows.clone() + } + + /// Start watching the given window. The [WindowPropertyChanged] signal + /// will fire whenever a window property changes on the window. Use + /// [unwatch_window] to stop watching the given window. + #[func] + pub fn watch_window(&mut self, window_id: u32) -> i32 { + if self.watched_windows.contains(window_id as i64) { + log::warn!("Window {window_id} is already being watched"); + return 0; + } + self.watched_windows.push(window_id as i64); + + // Spawn a new thread to listen for window property changes + let (_, rx) = match self.xwayland.listen_for_window_property_changes(window_id) { + Ok(result) => result, + Err(e) => { + log::error!("Failed to watch window properties for window '{window_id}': {e:?}"); + return -1; + } + }; + + // Spawn a task to listen for window changes and emit signals + let signals_tx = self.tx.clone(); + let task = RUNTIME.spawn(async move { + log::debug!("Started listening for property changes on window: {window_id}"); + // NOTE: only async tasks support abort, so we need to resort to polling here + 'outer: loop { + // Consume all messages from the channel and emit signals + 'inner: loop { + let event = match rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break 'inner, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + let signal = Signal::WindowPropertyChanged { + window_id, + property: event, + }; + if let Err(e) = signals_tx.send(signal) { + log::error!("Failed to send property change signal: {e:?}"); + break 'outer; + } + } + + tokio::time::sleep(Duration::from_millis(40)).await; + } + + log::debug!("Stopped listening for property changes on window: {window_id}"); + }); + + // Keep a list of abort handles so the watch task can be cancelled. + self.window_watch_handles + .insert(window_id, task.abort_handle()); + + 0 + } + + /// Stop watching the given window. The [WindowPropertyChanged] signal will + /// no longer fire for the given window. + #[func] + pub fn unwatch_window(&mut self, window_id: u32) -> i32 { + let window_id = window_id as i64; + if !self.watched_windows.contains(window_id) { + return 0; + } + if let Some(idx) = self.watched_windows.find(window_id, None) { + self.watched_windows.remove(idx); + } + + // Cancel the listener task + let Some(task) = self.window_watch_handles.get(&(window_id as u32)) else { + log::error!("Task wasn't found but was being watched: {window_id}"); + return -1; + }; + + task.abort(); + + 0 + } + + /// Discover the process IDs that are associated with the given window + #[func] + pub fn get_pids_for_window(&self, window_id: u32) -> PackedInt64Array { + let pids = match self.xwayland.get_pids_for_window(window_id) { + Ok(pids) => pids, + Err(e) => { + log::error!("Failed to get pids for window '{window_id}': {e:?}"); + return PackedInt64Array::new(); + } + }; + let pids: Vec = pids.into_iter().map(|pid| pid as i64).collect(); + + pids.into() + } + + /// Returns the window id(s) for the given process ID. + #[func] + pub fn get_windows_for_pid(&self, pid: u32) -> PackedInt64Array { + let windows = match self.xwayland.get_windows_for_pid(pid) { + Ok(windows) => windows, + Err(e) => { + log::error!("Failed to get windows for pid '{pid}': {e:?}"); + return PackedInt64Array::new(); + } + }; + let windows: Vec = windows.into_iter().map(|id| id as i64).collect(); + + windows.into() + } + + /// Returns the window name of the given window + #[func] + fn get_window_name(&self, window_id: u32) -> GString { + let name = match self.xwayland.get_window_name(window_id) { + Ok(name) => name, + Err(e) => { + log::error!("Failed to get window name for window '{window_id}': {e:?}"); + return "".into(); + } + }; + + name.unwrap_or_default().into() + } + + /// Returns the window ids of the children of the given window + #[func] + fn get_window_children(&self, window_id: u32) -> PackedInt64Array { + let windows = match self.xwayland.get_window_children(window_id) { + Ok(windows) => windows, + Err(e) => { + log::error!("Failed to get window children for window '{window_id}': {e:?}"); + return PackedInt64Array::new(); + } + }; + let windows: Vec = windows.into_iter().map(|id| id as i64).collect(); + + windows.into() + } + + /// Recursively returns all child windows of the given window id + #[func] + fn get_all_windows(&self, window_id: u32) -> PackedInt64Array { + let windows = match self.xwayland.get_all_windows(window_id) { + Ok(windows) => windows, + Err(e) => { + log::error!("Failed to get all window children for window '{window_id}': {e:?}"); + return PackedInt64Array::new(); + } + }; + let windows: Vec = windows.into_iter().map(|id| id as i64).collect(); + + windows.into() + } + + /// Returns the currently set app ID on the given window. Returns zero if no + /// app id was found. + #[func] + fn get_app_id(&self, window_id: u32) -> u32 { + match self.xwayland.get_app_id(window_id) { + Ok(app_id) => app_id.unwrap_or_default(), + Err(e) => { + log::error!("Failed to get app id for window '{window_id}': {e:?}"); + 0 + } + } + } + + /// Sets the app ID on the given window. Returns zero if operation succeeds. + #[func] + fn set_app_id(&self, window_id: u32, app_id: u32) -> i32 { + if let Err(e) = self.xwayland.set_app_id(window_id, app_id) { + log::error!("Failed to set app id {app_id} on window '{window_id}': {e:?}"); + return -1; + } + 0 + } + + /// Removes the app ID on the given window. Returns zero if operation succeeds. + #[func] + fn remove_app_id(&self, window_id: u32) -> i32 { + if let Err(e) = self + .xwayland + .remove_xprop(window_id, GamescopeAtom::SteamGame) + { + log::error!("Failed to remove app id from window '{window_id}': {e:?}"); + return -1; + } + 0 + } + + /// Returns whether or not the given window has an app ID set + #[func] + fn has_app_id(&self, window_id: u32) -> bool { + match self.xwayland.has_app_id(window_id) { + Ok(v) => v, + Err(e) => { + log::error!("Failed to check window '{window_id}' for app id: {e:?}"); + false + } + } + } + + /// Returns whether or not the given window has the STEAM_NOTIFICATION property + #[func] + fn has_notification(&self, window_id: u32) -> bool { + match self + .xwayland + .has_xprop(window_id, GamescopeAtom::SteamNotification) + { + Ok(v) => v, + Err(e) => { + log::error!( + "Failed to check window '{window_id}' has STEAM_NOTIFICATION property: {e:?}" + ); + false + } + } + } + + /// Returns whether or not the given window has the STEAM_INPUT_FOCUS property + #[func] + fn has_input_focus(&self, window_id: u32) -> bool { + match self + .xwayland + .has_xprop(window_id, GamescopeAtom::SteamInputFocus) + { + Ok(v) => v, + Err(e) => { + log::error!( + "Failed to check window '{window_id}' has STEAM_INPUT_FOCUS property: {e:?}" + ); + false + } + } + } + + /// Returns whether or not the given window has the STEAM_OVERLAY property + #[func] + fn has_overlay(&self, window_id: u32) -> bool { + match self + .xwayland + .has_xprop(window_id, GamescopeAtom::SteamOverlay) + { + Ok(v) => v, + Err(e) => { + log::error!( + "Failed to check window '{window_id}' has STEAM_OVERLAY property: {e:?}" + ); + false + } + } + } + + /// --- XWayland Primary --- + + /// Return a list of focusable apps + #[func] + fn get_focusable_apps(&mut self) -> PackedInt64Array { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_focusable_apps() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get focusable apps: {e:?}"); + return Default::default(); + } + }; + let Some(focusable) = value else { + return Default::default(); + }; + let focusable: Vec = focusable.into_iter().map(|v| v as i64).collect(); + self.focusable_apps = focusable.into(); + self.focusable_apps.clone() + } + + /// Return a list of focusable windows + #[func] + fn get_focusable_windows(&mut self) -> PackedInt64Array { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_focusable_windows() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get focusable windows: {e:?}"); + return Default::default(); + } + }; + let Some(focusable) = value else { + return Default::default(); + }; + let focusable: Vec = focusable.into_iter().map(|v| v as i64).collect(); + self.focusable_windows = focusable.into(); + self.focusable_windows.clone() + } + + /// Returns a list of focusable window names + #[func] + fn get_focusable_window_names(&mut self) -> PackedStringArray { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_focusable_window_names() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get focusable windows: {e:?}"); + return Default::default(); + } + }; + let value: Vec = value.into_iter().map(GString::from).collect(); + self.focusable_window_names = value.into(); + self.focusable_window_names.clone() + } + + /// Return the currently focused window id. + #[func] + fn get_focused_window(&mut self) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_focused_window() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get focused window: {e:?}"); + return Default::default(); + } + }; + + self.focused_window = value.unwrap_or_default(); + self.focused_window + } + + /// Return the currently focused app id. + #[func] + fn get_focused_app(&mut self) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_focused_app() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get focused app: {e:?}"); + return Default::default(); + } + }; + + self.focused_app = value.unwrap_or_default(); + self.focused_app + } + + /// Return the currently focused gfx app id. + #[func] + fn get_focused_app_gfx(&mut self) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_focused_app_gfx() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get focused app gfx: {e:?}"); + return Default::default(); + } + }; + + self.focused_app_gfx = value.unwrap_or_default(); + self.focused_app_gfx + } + + /// Returns whether or not the overlay window is currently focused + #[func] + fn get_overlay_focused(&mut self) -> bool { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + + let focused = match self.xwayland.is_overlay_focused() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get overlay focused: {e:?}"); + Default::default() + } + }; + self.overlay_focused = focused; + self.overlay_focused + } + + /// The current Gamescope FPS limit + #[func] + fn get_fps_limit(&mut self) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_fps_limit() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get fps limit: {e:?}"); + return Default::default(); + } + }; + + self.fps_limit = value.unwrap_or_default(); + self.fps_limit + } + + /// Sets the current Gamescope FPS limit + #[func] + fn set_fps_limit(&mut self, fps: u32) { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return; + } + if let Err(e) = self.xwayland.set_fps_limit(fps) { + log::error!("Failed to set FPS limit to {fps}: {e:?}"); + } + self.fps_limit = fps; + } + + /// The Gamescope blur mode (0 - off, 1 - cond, 2 - always) + #[func] + fn get_blur_mode(&mut self) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_blur_mode() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get blur mode: {e:?}"); + return Default::default(); + } + }; + let Some(mode) = value else { + return Default::default(); + }; + + let blur_mode = match mode { + BlurMode::Off => 0, + BlurMode::Cond => 1, + BlurMode::Always => 2, + }; + self.blur_mode = blur_mode; + self.blur_mode + } + + /// Sets the Gamescope blur mode + #[func] + fn set_blur_mode(&mut self, mode: u32) { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let blur_mode = match mode { + 0 => BlurMode::Off, + 1 => BlurMode::Cond, + 2 => BlurMode::Always, + _ => BlurMode::Off, + }; + if let Err(e) = self.xwayland.set_blur_mode(blur_mode) { + log::error!("Failed to set blur mode to: {mode}: {e:?}"); + } + self.blur_mode = mode; + } + + // The blur radius size + #[func] + fn get_blur_radius(&self) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + self.blur_radius + } + + /// Sets the blur radius size + #[func] + fn set_blur_radius(&mut self, radius: u32) { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return; + } + if let Err(e) = self.xwayland.set_blur_radius(radius) { + log::error!("Failed to set blur radius to: {radius}: {e:?}"); + } + self.blur_radius = radius; + } + + /// Whether or not Gamescope should be allowed to screen tear + #[func] + fn get_allow_tearing(&self) -> bool { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + self.allow_tearing + } + + /// Sets whether or not Gamescope should be allowed to screen tear + #[func] + fn set_allow_tearing(&mut self, allow: bool) { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return; + } + if let Err(e) = self.xwayland.set_allow_tearing(allow) { + log::error!("Failed to set allow tearing to: {allow}: {e:?}"); + } + self.allow_tearing = allow; + } + + /// Returns true if the window with the given window ID exists in focusable apps + #[func] + fn is_focusable_app(&self, window_id: u32) -> bool { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + match self.xwayland.is_focusable_app(window_id) { + Ok(is_focusable) => is_focusable, + Err(e) => { + log::error!("Failed to check if window '{window_id}' is focusable app: {e:?}"); + Default::default() + } + } + } + + /// Sets the given window as the main launcher app. This will set an X window + /// property called STEAM_GAME to 769 (Steam), which will make Gamescope + /// treat the window as the main overlay. + #[func] + fn set_main_app(&self, window_id: u32) -> i32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + if let Err(e) = self.xwayland.set_main_app(window_id) { + log::error!("Failed to set window '{window_id}' as main app: {e:?}"); + return -1; + } + 0 + } + + /// Set the given window as the primary overlay input focus. This should be set to + /// "1" whenever the overlay wants to intercept input from a game. + #[func] + fn set_input_focus(&self, window_id: u32, value: u32) -> i32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + if let Err(e) = self.xwayland.set_input_focus(window_id, value) { + log::error!("Failed to set input focus on '{window_id}' to '{value}': {e:?}"); + return -1; + } + 0 + } + + /// Get the overlay status for the given window + #[func] + fn get_overlay(&self, window_id: u32) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + match self.xwayland.get_overlay(window_id) { + Ok(value) => value.unwrap_or_default(), + Err(e) => { + log::error!("Failed to get overlay status for window '{window_id}': {e:?}"); + 0 + } + } + } + + /// Set the given window as the main overlay window + #[func] + fn set_overlay(&self, window_id: u32, value: u32) -> i32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + if let Err(e) = self.xwayland.set_overlay(window_id, value) { + log::error!("Failed to set overlay on '{window_id}' to '{value}': {e:?}"); + return -1; + } + 0 + } + + /// Set the given window as a notification. This should be set to "1" when some + /// UI wants to be shown but not intercept input. + #[func] + fn set_notification(&self, window_id: u32, value: u32) -> i32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + if let Err(e) = self.xwayland.set_notification(window_id, value) { + log::error!("Failed to set notification on '{window_id}' to '{value}': {e:?}"); + return -1; + } + 0 + } + + /// Set the given window as an external overlay window + #[func] + fn set_external_overlay(&self, window_id: u32, value: u32) -> i32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + if let Err(e) = self.xwayland.set_external_overlay(window_id, value) { + log::error!("Failed to set external overlay on '{window_id}' to '{value}': {e:?}"); + return -1; + } + 0 + } + + /// Returns the currently set manual focus + #[func] + fn get_baselayer_window(&mut self) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_baselayer_window() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get baselayer window: {e:?}"); + return Default::default(); + } + }; + + self.baselayer_window = value.unwrap_or_default(); + self.baselayer_window + } + + /// Focuses the given window + #[func] + fn set_baselayer_window(&mut self, window_id: u32) { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return; + } + if let Err(e) = self.xwayland.set_baselayer_window(window_id) { + log::error!("Failed to set baselayer window to {window_id}: {e:?}"); + } + self.baselayer_window = window_id; + } + + /// Removes the baselayer property to un-focus windows + #[func] + fn remove_baselayer_window(&mut self) { + if let Err(e) = self.xwayland.remove_baselayer_window() { + log::error!("Failed to remove baselayer window: {e:?}"); + } + self.baselayer_window = 0; + } + + /// Returns the app id of the currently manually focused app + #[func] + fn get_baselayer_app(&mut self) -> u32 { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return Default::default(); + } + let value = match self.xwayland.get_baselayer_app_id() { + Ok(value) => value, + Err(e) => { + log::error!("Failed to get baselayer app id: {e:?}"); + return Default::default(); + } + }; + + self.baselayer_window = value.unwrap_or_default(); + self.baselayer_window + } + + /// Focuses the app with the given app id + #[func] + fn set_baselayer_app(&mut self, app_id: u32) { + if !self.is_primary { + log::error!("XWayland instance is not primary!"); + return; + } + if let Err(e) = self.xwayland.set_baselayer_app_id(app_id) { + log::error!("Failed to set baselayer app id to {app_id}: {e:?}"); + } + self.baselayer_window = app_id; + } + + /// Removes the baselayer property to un-focus apps + #[func] + fn remove_baselayer_app(&mut self) { + if let Err(e) = self.xwayland.remove_baselayer_app_id() { + log::error!("Failed to remove baselayer app: {e:?}"); + } + self.baselayer_window = 0; + } + + /// Request a screenshot from Gamescope + #[func] + fn request_screenshot(&self) { + if let Err(e) = self.xwayland.request_screenshot() { + log::error!("Failed to request screenshot: {e:?}"); + } + } + + /// Dispatches signals, called by [GamescopeInstance] + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + //log::trace!("Got signal: {signal:?}"); + match signal { + Signal::WindowCreated { window_id } => { + self.base_mut() + .emit_signal("window_created", &[window_id.to_variant()]); + } + Signal::WindowDestroyed { window_id } => { + self.base_mut() + .emit_signal("window_destroyed", &[window_id.to_variant()]); + } + Signal::WindowPropertyChanged { + window_id, + property, + } => { + self.base_mut().emit_signal( + "window_property_updated", + &[window_id.to_variant(), property.to_godot().to_variant()], + ); + } + Signal::PropertyChanged { property } => { + match property { + property if property == GamescopeAtom::FocusedApp.to_string() => { + let from = self.focused_app; + let to = self.get_focused_app(); + self.base_mut().emit_signal( + "focused_app_updated", + &[from.to_variant(), to.to_variant()], + ); + } + property if property == GamescopeAtom::FocusedAppGFX.to_string() => { + let from = self.focused_app_gfx; + let to = self.get_focused_app_gfx(); + self.base_mut().emit_signal( + "focused_app_gfx_updated", + &[from.to_variant(), to.to_variant()], + ); + } + property if property == GamescopeAtom::FocusableApps.to_string() => { + let from = self.focusable_apps.clone(); + let to = self.get_focusable_apps(); + self.base_mut().emit_signal( + "focusable_apps_updated", + &[from.to_variant(), to.to_variant()], + ); + } + property if property == GamescopeAtom::FocusedWindow.to_string() => { + let from = self.focused_window; + let to = self.get_focused_window(); + self.base_mut().emit_signal( + "focused_window_updated", + &[from.to_variant(), to.to_variant()], + ); + } + property if property == GamescopeAtom::FocusableWindows.to_string() => { + let from = self.focusable_windows.clone(); + let to = self.get_focusable_windows(); + self.base_mut().emit_signal( + "focusable_windows_updated", + &[from.to_variant(), to.to_variant()], + ); + } + property if property == GamescopeAtom::BaselayerWindow.to_string() => { + let from = self.baselayer_window; + let to = self.get_baselayer_window(); + self.base_mut().emit_signal( + "baselayer_window_updated", + &[from.to_variant(), to.to_variant()], + ); + } + property if property == GamescopeAtom::BaselayerAppId.to_string() => { + let from = self.baselayer_app; + let to = self.get_baselayer_app(); + self.base_mut().emit_signal( + "baselayer_app_updated", + &[from.to_variant(), to.to_variant()], + ); + } + _ => { + // Unknown prop changed + } + } + } + } + } +} + +#[godot_api] +impl IResource for GamescopeXWayland { + fn to_string(&self) -> GString { + format!("", self.name).into() + } +} + +impl Drop for GamescopeXWayland { + fn drop(&mut self) { + log::trace!("Gamescope XWayland '{}' is being destroyed!", self.name); + } +} diff --git a/extensions/core/src/input.rs b/extensions/core/src/input.rs new file mode 100644 index 000000000..f672b9116 --- /dev/null +++ b/extensions/core/src/input.rs @@ -0,0 +1 @@ +pub mod inputplumber; diff --git a/extensions/core/src/input/inputplumber.rs b/extensions/core/src/input/inputplumber.rs new file mode 100644 index 000000000..e0a570d27 --- /dev/null +++ b/extensions/core/src/input/inputplumber.rs @@ -0,0 +1,544 @@ +pub mod composite_device; +pub mod dbus_device; +pub mod event_device; +pub mod keyboard_device; +pub mod mouse_device; + +use dbus_device::DBusDevice; +use futures_util::stream::StreamExt; +use std::collections::HashMap; +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; +use std::time::Duration; + +use composite_device::CompositeDevice; +use godot::prelude::*; + +use godot::classes::{Engine, Resource}; +use zbus::fdo::ObjectManagerProxy; +use zbus::names::BusName; + +use crate::dbus::RunError; +use crate::{get_dbus_system, get_dbus_system_blocking, RUNTIME}; + +const INPUT_PLUMBER_BUS: &str = "org.shadowblip.InputPlumber"; +const INPUT_PLUMBER_PATH: &str = "/org/shadowblip/InputPlumber"; + +/// Supported InputPlumber DBus objects +#[derive(Debug)] +enum ObjectType { + Unknown, + CompositeDevice, + SourceEventDevice, + SourceHidRawDevice, + SourceIioDevice, + TargetDBusDevice, + TargetGamepadDevice, + TargetKeyboardDevice, + TargetMouseDevice, +} + +impl ObjectType { + fn from_dbus_path(path: &str) -> Self { + if path.contains("CompositeDevice") { + return Self::CompositeDevice; + } + if path.contains("dbus") { + return Self::TargetDBusDevice; + } + if path.contains("target") && path.contains("mouse") { + return Self::TargetMouseDevice; + } + if path.contains("target") && path.contains("keyboard") { + return Self::TargetKeyboardDevice; + } + if path.contains("target") && path.contains("gamepad") { + return Self::TargetGamepadDevice; + } + if path.contains("source") && path.contains("event") { + return Self::SourceEventDevice; + } + if path.contains("source") && path.contains("hidraw") { + return Self::SourceHidRawDevice; + } + if path.contains("source") && path.contains("iio") { + return Self::SourceIioDevice; + } + Self::Unknown + } +} + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Started, + Stopped, + ObjectAdded { path: String, kind: ObjectType }, + ObjectRemoved { path: String, kind: ObjectType }, +} + +/// Instance representing a client connection to InputPlumber over DBus. This +/// is represented as a resource so it can be accessed from anywhere in the scene +/// tree, but there must be a node that calls 'process()' on this resource every +/// frame in order to emit signals and process messages. +#[derive(GodotClass)] +#[class(base=Resource)] +pub struct InputPlumberInstance { + base: Base, + rx: Receiver, + conn: Option, + /// Map of DBus path to composite device resource. E.g. + /// {"/org/shadowblip/InputPlumber/CompositeDevice0": } + composite_devices: HashMap>, + /// Map of DBus path to dbus device resource. E.g. + /// {"/org/shadowblip/InputPlumber/target/dbus0": } + dbus_devices: HashMap>, + /// The current intercept mode set for all devices + #[var(get = get_intercept_mode, set = set_intercept_mode)] + intercept_mode: i64, + /// The current events that will trigger intercept mode + #[var(get = get_intercept_triggers, set = set_intercept_triggers)] + intercept_triggers: PackedStringArray, + /// The current target event for intercept mode + #[var(get = get_intercept_target, set = set_intercept_target)] + intercept_target: GString, +} + +#[godot_api] +impl InputPlumberInstance { + #[constant] + const INTERCEPT_MODE_NONE: i32 = 0; + #[constant] + const INTERCEPT_MODE_PASS: i32 = 1; + #[constant] + const INTERCEPT_MODE_ALL: i32 = 2; + + /// Emitted when InputPlumber is detected as running + #[signal] + fn started(); + + /// Emitted when InputPlumber is detected as stopped + #[signal] + fn stopped(); + + /// Emitted when a CompositeDevice is dicovered and identified as a new device + #[signal] + fn composite_device_added(device: Gd); + + /// Emitted when a CompositeDevice is removed + #[signal] + fn composite_device_removed(dbus_path: GString); + + /// Returns true if the InputPlumber service is currently running + #[func] + fn is_running(&self) -> bool { + let Some(conn) = self.conn.as_ref() else { + return false; + }; + let bus = BusName::from_static_str(INPUT_PLUMBER_BUS).unwrap(); + let dbus = zbus::blocking::fdo::DBusProxy::new(conn).ok(); + let Some(dbus) = dbus else { + return false; + }; + dbus.name_has_owner(bus.clone()).unwrap_or_default() + } + + /// Returns the [CompositeDevice] with the given DBus path. If the device + /// does not exist, null will be returned. + #[func] + fn get_composite_device(&self, dbus_path: GString) -> Option> { + let path = String::from(dbus_path); + let device = self.composite_devices.get(&path)?; + Some(device.clone()) + } + + /// Return all current composite devices + #[func] + fn get_composite_devices(&mut self) -> Array> { + let mut devices = array![]; + let objects = match self.get_managed_objects() { + Ok(paths) => paths, + Err(e) => { + log::error!("Failed to get managed objects: {e:?}"); + return devices; + } + }; + + for path in objects { + if !path.contains("CompositeDevice") { + continue; + } + let device = CompositeDevice::new(path.as_str()); + devices.push(&device); + } + + devices + } + + /// Returns the [DBusDevice] with the given DBus path. If the device + /// does not exist, null will be returned. + #[func] + fn get_dbus_device(&self, dbus_path: GString) -> Option> { + let path = String::from(dbus_path); + let device = self.dbus_devices.get(&path)?; + Some(device.clone()) + } + + /// Return all current dbus devices + #[func] + fn get_dbus_devices(&mut self) -> Array> { + let mut devices = array![]; + let objects = match self.get_managed_objects() { + Ok(paths) => paths, + Err(e) => { + log::error!("Failed to get managed objects: {e:?}"); + return devices; + } + }; + + for path in objects { + if !path.contains("target/dbus") { + continue; + } + let device = DBusDevice::new(path.as_str()); + devices.push(&device); + } + + devices + } + + /// Get managed objects + fn get_managed_objects(&self) -> Result, zbus::fdo::Error> { + let Some(conn) = self.conn.as_ref() else { + return Err(zbus::fdo::Error::Disconnected( + "No DBus connection found".into(), + )); + }; + + let bus = BusName::from_static_str(INPUT_PLUMBER_BUS).unwrap(); + let object_manager = zbus::blocking::fdo::ObjectManagerProxy::builder(conn) + .destination(bus) + .ok() + .and_then(|builder| builder.path(INPUT_PLUMBER_PATH).ok()) + .and_then(|builder| builder.build().ok()); + let Some(object_manager) = object_manager else { + return Ok(Vec::new()); + }; + + Ok(object_manager + .get_managed_objects()? + .keys() + .map(|v| v.to_string()) + .collect()) + } + + /// Process InputPlumber signals and emit them as Godot signals. This method + /// should be called every frame in the "_process" loop of a node. + #[func] + fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + + // Process signals from tracked devices + for (_, device) in self.dbus_devices.iter_mut() { + device.bind_mut().process(); + } + for (_, device) in self.composite_devices.iter_mut() { + device.bind_mut().process(); + } + } + + /// Gets the current intercept mode for all composite devices + #[func] + fn get_intercept_mode(&self) -> i64 { + self.intercept_mode + } + + /// Sets all composite devices to the specified intercept mode. + #[func] + fn set_intercept_mode(&mut self, mode: i64) { + if !(0..=2).contains(&mode) { + log::error!("Invalid intercept mode: {mode}"); + return; + } + self.intercept_mode = mode; + for (_, device) in self.composite_devices.iter() { + device.bind().set_intercept_mode(mode as i32); + } + } + + /// Gets the current triggers for activating intercept mode for all devices + #[func] + fn get_intercept_triggers(&self) -> PackedStringArray { + self.intercept_triggers.clone() + } + + /// Sets the current triggers for activating intercept mode for all devices + #[func] + fn set_intercept_triggers(&mut self, triggers: PackedStringArray) { + self.intercept_triggers = triggers; + } + + /// Gets the current target event for activating intercept mode for all devices + #[func] + fn get_intercept_target(&self) -> GString { + self.intercept_target.clone() + } + + /// Sets the current target event for activating intercept mode for all devices + #[func] + fn set_intercept_target(&mut self, target_event: GString) { + self.intercept_target = target_event; + } + + /// Sets all composite devices to use the specified intercept actions. + #[func] + fn set_intercept_activation(&mut self, triggers: PackedStringArray, target_event: GString) { + self.set_intercept_triggers(triggers.clone()); + self.set_intercept_target(target_event.clone()); + for (_, device) in self.composite_devices.iter() { + device + .bind() + .set_intercept_activation(triggers.clone(), target_event.clone()) + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Started => { + self.base_mut().emit_signal("started", &[]); + } + Signal::Stopped => { + // Clear all known devices + self.composite_devices.clear(); + self.dbus_devices.clear(); + self.base_mut().emit_signal("stopped", &[]); + } + Signal::ObjectAdded { path, kind } => { + self.on_object_added(path, kind); + } + Signal::ObjectRemoved { path, kind } => { + self.on_object_removed(path, kind); + } + } + } + + /// Track the given object and emit signals + fn on_object_added(&mut self, path: String, kind: ObjectType) { + match kind { + ObjectType::Unknown => (), + ObjectType::CompositeDevice => { + log::debug!("CompositeDevice added: {path}"); + let device = CompositeDevice::new(path.as_str()); + self.composite_devices.insert(path, device.clone()); + self.base_mut() + .emit_signal("composite_device_added", &[device.to_variant()]); + } + ObjectType::SourceEventDevice => (), + ObjectType::SourceHidRawDevice => (), + ObjectType::SourceIioDevice => (), + ObjectType::TargetDBusDevice => { + log::debug!("DBusDevice added: {path}"); + let device = DBusDevice::new(path.as_str()); + self.dbus_devices.insert(path, device); + } + ObjectType::TargetGamepadDevice => (), + ObjectType::TargetKeyboardDevice => (), + ObjectType::TargetMouseDevice => (), + } + } + + /// Remove the given object and emit signals + fn on_object_removed(&mut self, path: String, kind: ObjectType) { + match kind { + ObjectType::Unknown => (), + ObjectType::CompositeDevice => { + log::debug!("CompositeDevice device removed: {path}"); + self.composite_devices.remove(&path); + self.base_mut().emit_signal( + "composite_device_removed", + &[GString::from(path).to_variant()], + ); + } + ObjectType::SourceEventDevice => (), + ObjectType::SourceHidRawDevice => (), + ObjectType::SourceIioDevice => (), + ObjectType::TargetDBusDevice => { + log::debug!("DBusDevice device removed: {path}"); + self.dbus_devices.remove(&path); + } + ObjectType::TargetGamepadDevice => (), + ObjectType::TargetKeyboardDevice => (), + ObjectType::TargetMouseDevice => (), + } + } +} + +#[godot_api] +impl IResource for InputPlumberInstance { + /// Called upon object initialization in the engine + fn init(base: Base) -> Self { + log::debug!("Initializing InputPlumber instance"); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + let conn = get_dbus_system_blocking().ok(); + + // Don't run in the editor + let engine = Engine::singleton(); + if engine.is_editor_hint() { + return Self { + base, + rx, + conn, + composite_devices: Default::default(), + dbus_devices: Default::default(), + intercept_mode: Default::default(), + intercept_triggers: Default::default(), + intercept_target: Default::default(), + }; + } + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx).await { + log::error!("Failed to run InputPlumber task: ${e:?}"); + } + }); + + // Create a new InputPlumber instance + let mut instance = Self { + base, + rx, + conn, + composite_devices: HashMap::new(), + dbus_devices: HashMap::new(), + intercept_mode: 0, + intercept_triggers: PackedStringArray::from(&["Gamepad:Button:Guide".into()]), + intercept_target: "Gamepad:Button:Guide".into(), + }; + + // Do initial device discovery + let devices = instance.get_composite_devices(); + for device in devices.iter_shared() { + let path = device.bind().get_dbus_path(); + instance.composite_devices.insert(path.into(), device); + } + let dbus_devices = instance.get_dbus_devices(); + for dbus_device in dbus_devices.iter_shared() { + let path = dbus_device.bind().get_dbus_path(); + instance + .dbus_devices + .insert(path.into(), dbus_device.clone()); + } + instance + } +} + +/// Runs InputPlumber tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning inputplumber"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Spawn a task to listen for InputPlumber start/stop + let dbus_conn = conn.clone(); + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + let bus = BusName::from_static_str(INPUT_PLUMBER_BUS).unwrap(); + let mut is_running = { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + return; + }; + dbus.name_has_owner(bus.clone()).await.unwrap_or_default() + }; + + loop { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + break; + }; + let running = dbus.name_has_owner(bus.clone()).await.unwrap_or_default(); + if running != is_running { + let signal = if running { + Signal::Started + } else { + Signal::Stopped + }; + if signals_tx.send(signal).is_err() { + break; + } + } + is_running = running; + tokio::time::sleep(Duration::from_secs(5)).await; + } + }); + + // Get a proxy instance to ObjectManager + let bus = BusName::from_static_str(INPUT_PLUMBER_BUS).unwrap(); + let object_manager: ObjectManagerProxy = ObjectManagerProxy::builder(&conn) + .destination(bus)? + .path(INPUT_PLUMBER_PATH)? + .build() + .await?; + + // Spawn a task to listen for objects added + let mut ifaces_added = object_manager.receive_interfaces_added().await?; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + while let Some(signal) = ifaces_added.next().await { + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + + let path = args.object_path.to_string(); + let kind = ObjectType::from_dbus_path(path.as_str()); + let signal = Signal::ObjectAdded { path, kind }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + // Spawn a task to listen for objects removed + let mut ifaces_removed = object_manager.receive_interfaces_removed().await?; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + while let Some(signal) = ifaces_removed.next().await { + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + + let path = args.object_path.to_string(); + let kind = ObjectType::from_dbus_path(path.as_str()); + let signal = Signal::ObjectRemoved { path, kind }; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/input/inputplumber/composite_device.rs b/extensions/core/src/input/inputplumber/composite_device.rs new file mode 100644 index 000000000..89e0cc08c --- /dev/null +++ b/extensions/core/src/input/inputplumber/composite_device.rs @@ -0,0 +1,339 @@ +use godot::prelude::*; + +use godot::classes::{ProjectSettings, Resource, ResourceLoader}; + +use crate::dbus::inputplumber::composite_device::CompositeDeviceProxyBlocking; +use crate::dbus::DBusVariant; +use crate::get_dbus_system_blocking; + +use super::dbus_device::DBusDevice; +use super::keyboard_device::KeyboardDevice; +use super::mouse_device::MouseDevice; +use super::INPUT_PLUMBER_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct CompositeDevice { + base: Base, + + conn: Option, + path: String, + + /// The DBus path of the [CompositeDevice] + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + /// Name of the [CompositeDevice] + #[allow(dead_code)] + #[var(get = get_name)] + name: GString, + /// Name of the input profile that the [CompositeDevice] is using + #[allow(dead_code)] + #[var(get = get_profile_name)] + profile_name: GString, + /// Intercept mode of the [CompositeDevice] + #[allow(dead_code)] + #[var(get = get_intercept_mode, set = set_intercept_mode)] + intercept_mode: i32, + /// Capabilities from all source devices + #[allow(dead_code)] + #[var(get = get_capabilities)] + capabilities: PackedStringArray, + /// Capabilities from all target devices + #[allow(dead_code)] + #[var(get = get_target_capabilities)] + target_capabilities: PackedStringArray, + /// Target DBus devices associated with this composite device + #[allow(dead_code)] + #[var(get = get_dbus_devices)] + dbus_devices: Array>, + /// The source device paths of the composite device (e.g. /dev/input/event0) + #[allow(dead_code)] + #[var(get = get_source_device_paths)] + source_device_paths: PackedStringArray, +} + +#[godot_api] +impl CompositeDevice { + /// Create a new [CompositeDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + conn, + path: path.clone().into(), // Convert GString -> String. + dbus_path: path, + name: Default::default(), + profile_name: Default::default(), + intercept_mode: Default::default(), + capabilities: Default::default(), + target_capabilities: Default::default(), + dbus_devices: Default::default(), + source_device_paths: Default::default(), + base, + } + }) + } + + /// Return a proxy instance to the composite device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + CompositeDeviceProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [CompositeDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{INPUT_PLUMBER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = CompositeDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = CompositeDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Get the name of the [CompositeDevice] + #[func] + pub fn get_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.name().ok().unwrap_or_default().into() + } + + #[func] + pub fn get_profile_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.profile_name().ok().unwrap_or_default().into() + } + + /// Get the intercept mode of the composite device + #[func] + pub fn get_intercept_mode(&self) -> i32 { + let Some(proxy) = self.get_proxy() else { + return -1; + }; + proxy.intercept_mode().ok().unwrap_or_default() as i32 + } + + /// Set the intercept mode of the composite device + #[func] + pub fn set_intercept_mode(&self, mode: i32) { + let Some(proxy) = self.get_proxy() else { + return; + }; + let mode = mode as u32; + proxy.set_intercept_mode(mode).ok(); + } + + /// Get capabilities from all source devices + #[func] + pub fn get_capabilities(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return PackedStringArray::new(); + }; + let caps: Vec = proxy + .capabilities() + .ok() + .unwrap_or_default() + .into_iter() + .map(GString::from) + .collect(); + PackedStringArray::from(caps.as_slice()) + } + + /// Get capabilities from all target devices + #[func] + pub fn get_target_capabilities(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return PackedStringArray::new(); + }; + let caps: Vec = proxy + .target_capabilities() + .ok() + .unwrap_or_default() + .into_iter() + .map(GString::from) + .collect(); + PackedStringArray::from(caps.as_slice()) + } + + #[func] + pub fn get_dbus_devices(&self) -> Array> { + let mut devices = array![]; + let paths = self.get_dbus_devices_paths(); + for path in paths.as_slice() { + let dbus_path = String::from(path); + let device = DBusDevice::new(dbus_path.as_str()); + devices.push(&device); + } + devices + } + + #[func] + pub fn get_dbus_devices_paths(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return PackedStringArray::new(); + }; + let values: Vec = proxy + .dbus_devices() + .ok() + .unwrap_or_default() + .into_iter() + .map(GString::from) + .collect(); + PackedStringArray::from(values.as_slice()) + } + + /// Get the source device paths of the composite device (e.g. /dev/input/event0) + #[func] + pub fn get_source_device_paths(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return PackedStringArray::new(); + }; + let values: Vec = proxy + .source_device_paths() + .ok() + .unwrap_or_default() + .into_iter() + .map(GString::from) + .collect(); + PackedStringArray::from(values.as_slice()) + } + + /// Get the target devices for the composite device + #[func] + pub fn get_target_devices(&self) -> Array { + let Some(proxy) = self.get_proxy() else { + return array![]; + }; + let values = proxy.target_devices().ok().unwrap_or_default(); + let mut target_devices = array![]; + + // Build the Godot object based on the path + for path in values { + if path.contains("gamepad") { + // TODO + continue; + } + if path.contains("keyboard") { + let device = KeyboardDevice::new(path.as_str()); + target_devices.push(&device.to_variant()); + continue; + } + if path.contains("mouse") { + let device = MouseDevice::new(path.as_str()); + target_devices.push(&device.to_variant()); + continue; + } + if path.contains("dbus") { + let device = DBusDevice::new(path.as_str()); + target_devices.push(&device.to_variant()); + continue; + } + } + + target_devices + } + + /// set the target device types for the composite device (e.g. "keyboard", "mouse", etc.) + #[func] + pub fn set_target_devices(&self, devices: PackedStringArray) { + let Some(proxy) = self.get_proxy() else { + return; + }; + let device_types: Vec = devices.to_vec().into_iter().map(|v| v.into()).collect(); + let target_devices: Vec<&str> = device_types.iter().map(|v| v.as_str()).collect(); + proxy.set_target_devices(target_devices.as_slice()).ok(); + } + + /// Returns the DBus path to the [CompositeDevice] + #[func] + pub fn get_dbus_path(&self) -> GString { + self.path.clone().into() + } + + /// Load the device profile from the given path + #[func] + pub fn load_profile_path(&self, path: GString) { + let Some(proxy) = self.get_proxy() else { + return; + }; + let path = String::from(path); + let absolute_path = if path.starts_with("res://") || path.starts_with("user://") { + let project_settings = ProjectSettings::singleton(); + project_settings.globalize_path(path.as_str()).into() + } else { + path + }; + proxy.load_profile_path(absolute_path.as_str()).ok(); + } + + /// Write the given event to the appropriate target device, bypassing intercept + /// logic. + #[func] + pub fn send_event(&self, action: GString, value: Variant) { + let Some(proxy) = self.get_proxy() else { + return; + }; + let Some(value) = value.as_zvariant() else { + return; + }; + let event = String::from(action); + proxy.send_event(event.as_str(), &value).ok(); + } + + /// Write the given set of events as a button chord + #[func] + pub fn send_button_chord(&self, actions: PackedStringArray) { + let Some(proxy) = self.get_proxy() else { + return; + }; + let values: Vec = actions.to_vec().into_iter().map(|v| v.into()).collect(); + let str_values: Vec<&str> = values.iter().map(|v| v.as_str()).collect(); + proxy.send_button_chord(str_values.as_slice()).ok(); + } + + /// Set the events to look for to activate input interception while in + /// "PASS" mode. + #[func] + pub fn set_intercept_activation(&self, triggers: PackedStringArray, target_event: GString) { + let Some(proxy) = self.get_proxy() else { + return; + }; + let values: Vec = triggers.to_vec().into_iter().map(|v| v.into()).collect(); + let str_values: Vec<&str> = values.iter().map(|v| v.as_str()).collect(); + let target_event: String = target_event.into(); + proxy + .set_intercept_activation(str_values.as_slice(), target_event.as_str()) + .ok(); + } + + /// Dispatches signals + pub fn process(&mut self) {} +} diff --git a/extensions/core/src/input/inputplumber/dbus_device.rs b/extensions/core/src/input/inputplumber/dbus_device.rs new file mode 100644 index 000000000..f7d56b954 --- /dev/null +++ b/extensions/core/src/input/inputplumber/dbus_device.rs @@ -0,0 +1,217 @@ +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use futures_util::StreamExt; +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::inputplumber::dbus_device::DBusDeviceProxy; +use crate::{get_dbus_system, RUNTIME}; + +use super::{RunError, INPUT_PLUMBER_BUS}; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + InputEvent { + type_code: String, + value: f64, + }, + TouchEvent { + type_code: String, + index: u32, + is_touching: bool, + pressure: f64, + x: f64, + y: f64, + }, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct DBusDevice { + base: Base, + path: String, + rx: Receiver, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, +} + +#[godot_api] +impl DBusDevice { + #[signal] + fn input_event(type_code: GString, value: f64); + + #[signal] + fn touch_event( + type_code: GString, + index: i64, + is_touching: bool, + pressure: f64, + x: f64, + y: f64, + ); + + /// Create a new [DBusDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + // Create a channel to communicate with the signals task + log::debug!("DBusDevice created with path: {path}"); + let (tx, rx) = channel(); + let dbus_path = path.clone().into(); + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx, dbus_path).await { + log::error!("Failed to run DBusDevice task: ${e:?}"); + } + }); + + Gd::from_init_fn(|base| { + // Accept a base of type Base and directly forward it. + Self { + base, + path: path.clone().into(), // Convert GString -> String. + rx, + dbus_path: path, + } + }) + } + + /// Get or create a [DBusDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{INPUT_PLUMBER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = DBusDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = DBusDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + #[func] + pub fn get_dbus_path(&self) -> GString { + self.path.clone().into() + } + + /// Dispatches signals + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + log::trace!("Got signal: {signal:?}"); + match signal { + Signal::InputEvent { type_code, value } => { + self.base_mut().emit_signal( + "input_event", + &[type_code.to_godot().to_variant(), value.to_variant()], + ); + } + Signal::TouchEvent { + type_code, + index, + is_touching, + pressure, + x, + y, + } => { + self.base_mut().emit_signal( + "touch_event", + &[ + type_code.to_godot().to_variant(), + index.to_variant(), + is_touching.to_variant(), + pressure.to_variant(), + x.to_variant(), + y.to_variant(), + ], + ); + } + } + } +} + +impl Drop for DBusDevice { + fn drop(&mut self) { + log::trace!("DBusDevice '{}' is being destroyed!", self.dbus_path); + } +} + +/// Run the signals task +async fn run(tx: Sender, path: String) -> Result<(), RunError> { + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + let proxy = DBusDeviceProxy::builder(&conn).path(path)?.build().await?; + + let signals_tx = tx.clone(); + let mut input_events = proxy.receive_input_event().await?; + RUNTIME.spawn(async move { + while let Some(event) = input_events.next().await { + let Some(args) = event.args().ok() else { + break; + }; + let signal = Signal::InputEvent { + type_code: args.event.to_string(), + value: args.value, + }; + if signals_tx.send(signal).is_err() { + break; + } + } + log::debug!("DBusDevice input_event task stopped"); + }); + + let signals_tx = tx.clone(); + let mut touch_events = proxy.receive_touch_event().await?; + RUNTIME.spawn(async move { + while let Some(event) = touch_events.next().await { + let Some(args) = event.args().ok() else { + break; + }; + let signal = Signal::TouchEvent { + type_code: args.event.to_string(), + index: args.index, + is_touching: args.is_touching, + pressure: args.pressure, + x: args.x, + y: args.y, + }; + if signals_tx.send(signal).is_err() { + break; + } + } + log::debug!("DBusDevice touch_event task stopped"); + }); + + Ok(()) +} diff --git a/extensions/core/src/input/inputplumber/event_device.rs b/extensions/core/src/input/inputplumber/event_device.rs new file mode 100644 index 000000000..1d820ff03 --- /dev/null +++ b/extensions/core/src/input/inputplumber/event_device.rs @@ -0,0 +1,139 @@ +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{dbus::inputplumber::event_device::EventDeviceProxyBlocking, get_dbus_system_blocking}; + +use super::INPUT_PLUMBER_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct EventDevice { + base: Base, + path: String, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + #[allow(dead_code)] + #[var(get = get_name)] + name: GString, + #[allow(dead_code)] + #[var(get = get_device_path)] + device_path: GString, + #[allow(dead_code)] + #[var(get = get_phys_path)] + phys_path: GString, + #[allow(dead_code)] + #[var(get = get_sysfs_path)] + sysfs_path: GString, + #[allow(dead_code)] + #[var(get = get_unique_id)] + unique_id: GString, +} + +#[godot_api] +impl EventDevice { + /// Create a new [EventDevice] with the given DBus path + fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + path: path.clone().into(), + dbus_path: path, + name: Default::default(), + device_path: Default::default(), + phys_path: Default::default(), + sysfs_path: Default::default(), + unique_id: Default::default(), + } + }) + } + + /// Return a proxy instance to the composite device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + EventDeviceProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [DBusDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{INPUT_PLUMBER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = EventDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = EventDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + #[func] + pub fn get_dbus_path(&self) -> GString { + self.path.clone().into() + } + + /// Get the name of the [EventDevice] + #[func] + pub fn get_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.name().unwrap_or_default().into() + } + + #[func] + pub fn get_device_path(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.device_path().unwrap_or_default().into() + } + + #[func] + pub fn get_phys_path(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.phys_path().unwrap_or_default().into() + } + + #[func] + pub fn get_sysfs_path(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.sysfs_path().unwrap_or_default().into() + } + + #[func] + pub fn get_unique_id(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.unique_id().unwrap_or_default().into() + } +} diff --git a/extensions/core/src/input/inputplumber/keyboard_device.rs b/extensions/core/src/input/inputplumber/keyboard_device.rs new file mode 100644 index 000000000..9137d6111 --- /dev/null +++ b/extensions/core/src/input/inputplumber/keyboard_device.rs @@ -0,0 +1,100 @@ +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{dbus::inputplumber::keyboard::KeyboardProxyBlocking, get_dbus_system_blocking}; + +use super::INPUT_PLUMBER_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct KeyboardDevice { + base: Base, + path: String, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + #[allow(dead_code)] + #[var(get = get_name)] + name: GString, +} + +#[godot_api] +impl KeyboardDevice { + /// Create a new [KeyboardDevice] with the given DBus path + fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + path: path.clone().into(), + dbus_path: path, + name: Default::default(), + } + }) + } + + /// Return a proxy instance to the composite device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + KeyboardProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [KeyboardDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{INPUT_PLUMBER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = KeyboardDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = KeyboardDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + #[func] + pub fn get_dbus_path(&self) -> GString { + self.path.clone().into() + } + + /// Get the name of the [KeyboardDevice] + #[func] + pub fn get_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.name().unwrap_or_default().into() + } + + #[func] + pub fn send_key(&self, key: GString, value: bool) { + let Some(proxy) = self.get_proxy() else { + return; + }; + let key_code: String = key.into(); + proxy.send_key(key_code.as_str(), value).ok(); + } +} diff --git a/extensions/core/src/input/inputplumber/mouse_device.rs b/extensions/core/src/input/inputplumber/mouse_device.rs new file mode 100644 index 000000000..149d2ca5c --- /dev/null +++ b/extensions/core/src/input/inputplumber/mouse_device.rs @@ -0,0 +1,99 @@ +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{dbus::inputplumber::mouse::MouseProxyBlocking, get_dbus_system_blocking}; + +use super::INPUT_PLUMBER_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct MouseDevice { + base: Base, + path: String, + conn: Option, + + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + #[allow(dead_code)] + #[var(get = get_name)] + name: GString, +} + +#[godot_api] +impl MouseDevice { + /// Create a new [MouseDevice] with the given DBus path + fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + path: path.clone().into(), + dbus_path: path, + name: Default::default(), + } + }) + } + + /// Return a proxy instance to the composite device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + MouseProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [KeyboardDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{INPUT_PLUMBER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = MouseDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = MouseDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + #[func] + pub fn get_dbus_path(&self) -> GString { + self.path.clone().into() + } + + /// Get the name of the [KeyboardDevice] + #[func] + pub fn get_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return "".into(); + }; + proxy.name().unwrap_or_default().into() + } + + #[func] + pub fn move_cursor(&self, x: i64, y: i64) { + let Some(proxy) = self.get_proxy() else { + return; + }; + proxy.move_cursor(x as i32, y as i32).ok(); + } +} diff --git a/extensions/core/src/lib.rs b/extensions/core/src/lib.rs new file mode 100644 index 000000000..a374514d5 --- /dev/null +++ b/extensions/core/src/lib.rs @@ -0,0 +1,124 @@ +pub mod bluetooth; +pub mod dbus; +pub mod disk; +pub mod gamescope; +pub mod input; +pub mod logger; +pub mod network; +pub mod performance; +pub mod power; +pub mod resource; +pub mod system; +pub mod vdf; + +use std::{sync::Arc, time::Duration}; + +use godot::prelude::*; +use once_cell::sync::Lazy; +use tokio::{ + runtime::{Builder, Handle}, + sync::{ + mpsc::{channel, Receiver, Sender}, + Mutex, + }, +}; +use zbus::{ + blocking::{self}, + Connection, +}; + +/// Channel for shutting down the tokio runtime +type Channel = (Sender<()>, Arc>>); + +/// Global tokio runtime instance +pub static RUNTIME: Lazy = Lazy::new(tokio_init); +/// Shared connection to the DBus system bus +static DBUS_SYSTEM: Lazy>>> = Lazy::new(dbus_system_init); +/// Shared blocking connection to the DBus system bus +static DBUS_SYSTEM_BLOCKING: Lazy> = + Lazy::new(dbus_system_blocking_init); +/// Channel used to signal shutting down tokio runtime +static CHANNEL: Lazy = Lazy::new(get_channel); + +struct OpenGamepadUICore {} + +#[gdextension] +unsafe impl ExtensionLibrary for OpenGamepadUICore { + fn on_level_init(level: InitLevel) { + if level != InitLevel::Scene { + return; + } + logger::init(); + log::info!("Initializing OpenGamepadUI Core"); + } + + fn on_level_deinit(level: InitLevel) { + if level != InitLevel::Scene { + return; + } + log::info!("De-initializing OpenGamepadUI Core"); + tokio_deinit(); + } +} + +fn tokio_init() -> Handle { + log::debug!("Initializing tokio runtime"); + let runtime = Builder::new_multi_thread().enable_all().build().unwrap(); + let handle = runtime.handle().clone(); + + let rx = CHANNEL.1.clone(); + + std::thread::spawn(move || { + runtime.block_on(async { + log::debug!("Tokio runtime started"); + let _ = rx.lock().await.recv().await; + }); + log::debug!("Shutting down Tokio runtime"); + runtime.shutdown_timeout(Duration::from_secs(1)); + log::debug!("Tokio runtime stopped"); + }); + + handle +} + +fn tokio_deinit() { + let result = CHANNEL.0.clone().blocking_send(()); + if let Err(e) = result { + log::error!("Failed to shut down tokio runtime: {e}"); + } +} + +fn get_channel() -> (Sender<()>, Arc>>) { + let (tx, rx) = channel(1); + (tx, Arc::new(Mutex::new(rx))) +} + +fn dbus_system_init() -> Arc>> { + Arc::new(Mutex::new(None)) +} + +fn dbus_system_blocking_init() -> Option { + blocking::Connection::system().ok() +} + +/// Return or create a shared connection to the DBus system bus +pub async fn get_dbus_system() -> Result { + let mut conn = DBUS_SYSTEM.lock().await; + if conn.is_some() { + let conn_clone = conn.as_ref().unwrap().clone(); + Ok(conn_clone) + } else { + let new_conn = Connection::system().await?; + let conn_clone = new_conn.clone(); + *conn = Some(new_conn); + Ok(conn_clone) + } +} + +/// Return or create a shared blocking connection to the DBus system bus +pub fn get_dbus_system_blocking() -> Result { + if let Some(conn) = DBUS_SYSTEM_BLOCKING.as_ref() { + return Ok(conn.clone()); + } + blocking::Connection::system() +} diff --git a/extensions/core/src/logger.rs b/extensions/core/src/logger.rs new file mode 100644 index 000000000..b010e3733 --- /dev/null +++ b/extensions/core/src/logger.rs @@ -0,0 +1,70 @@ +use std::time::Instant; + +use godot::prelude::*; +use log::{Level, Metadata, Record}; +use once_cell::sync::Lazy; + +static INIT_TIME: Lazy = Lazy::new(Instant::now); +static LOG_LEVEL: Lazy = Lazy::new(level_from_env); +static LOGGER: Logger = Logger; + +struct Logger; + +impl log::Log for Logger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= LOG_LEVEL.to_owned() + } + + fn log(&self, record: &Record) { + if !self.enabled(record.metadata()) { + return; + } + + // Get the log prefix based on the level + let level = record.metadata().level(); + let (level_text, color) = match level { + Level::Error => ("ERROR", "[color=red]"), + Level::Warn => ("WARN", "[color=orange]"), + Level::Info => ("INFO", "[color=white]"), + Level::Debug => ("DEBUG", "[color=cyan]"), + Level::Trace => ("TRACE", "[color=magenta]"), + }; + + let time = INIT_TIME.elapsed().as_millis(); + let args = record.args(); + let file = record.file().unwrap_or_default(); + let line = record.line().unwrap_or_default(); + + godot_print_rich!("{color}{time} [{level_text}] [Core] {file}:{line}: {args}[/color]"); + } + + fn flush(&self) {} +} + +fn level_from_env() -> Level { + let level_str = std::env::var("LOG_LEVEL") + .unwrap_or("info".to_string()) + .to_lowercase(); + let level = str_to_level(level_str.as_str()); + + level +} + +fn str_to_level(value: &str) -> Level { + match value { + "error" => Level::Error, + "warn" => Level::Warn, + "info" => Level::Info, + "debug" => Level::Debug, + "trace" => Level::Trace, + _ => Level::Info, + } +} + +/// Initialize the core logger +pub fn init() { + if let Err(e) = log::set_logger(&LOGGER) { + godot_error!("Failed to initialize Rust logger: {e:?}"); + } + log::set_max_level(LOG_LEVEL.to_level_filter()); +} diff --git a/extensions/core/src/network.rs b/extensions/core/src/network.rs new file mode 100644 index 000000000..16684e7ec --- /dev/null +++ b/extensions/core/src/network.rs @@ -0,0 +1 @@ +pub mod network_manager; diff --git a/extensions/core/src/network/network_manager.rs b/extensions/core/src/network/network_manager.rs new file mode 100644 index 000000000..28adcd5ac --- /dev/null +++ b/extensions/core/src/network/network_manager.rs @@ -0,0 +1,663 @@ +pub mod access_point; +pub mod active_connection; +pub mod device; +pub mod device_wireless; +pub mod ip4_config; + +use crate::{ + dbus::{ + networkmanager::network_manager::{NetworkManagerProxy, NetworkManagerProxyBlocking}, + RunError, + }, + get_dbus_system, get_dbus_system_blocking, RUNTIME, +}; +use access_point::NetworkAccessPoint; +use active_connection::NetworkActiveConnection; +use device::NetworkDevice; +use device_wireless::NetworkDeviceWireless; +use futures_util::stream::StreamExt; +use ip4_config::NetworkIpv4Config; +use std::{ + collections::HashMap, + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, + time::Duration, +}; +use zbus::fdo::{ManagedObjects, ObjectManagerProxy}; + +use godot::{classes::Engine, obj::WithBaseField, prelude::*}; +use zbus::names::BusName; + +const NETWORK_MANAGER_BUS: &str = "org.freedesktop.NetworkManager"; +const OBJECT_MANAGER_PATH: &str = "/org/freedesktop"; + +/// Supported NetworkManager DBus objects +#[derive(Debug)] +enum ObjectType { + Unknown, + AccessPoint, + AgentManager, + ConnectionActive, + Device, + DeviceBluetooth, + DeviceGeneric, + DeviceWired, + DeviceWireless, + Dhcp4Config, + Dhcp6Config, + Ip4Config, + Ip6Config, + Settings, + SettingsConnection, +} + +impl ObjectType { + /// Returns the object type(s) from the list of implemented interfaces + fn from_ifaces(ifaces: Vec) -> Vec { + let types = ifaces + .into_iter() + .map(|iface| match iface.as_str() { + "org.freedesktop.NetworkManager.AccessPoint" => Self::AccessPoint, + "org.freedesktop.NetworkManager.AgentManager" => Self::AgentManager, + "org.freedesktop.NetworkManager.Connection.Active" => Self::ConnectionActive, + "org.freedesktop.NetworkManager.Device" => Self::Device, + "org.freedesktop.NetworkManager.Device.Bluetooth" => Self::DeviceBluetooth, + "org.freedesktop.NetworkManager.Device.Generic" => Self::DeviceGeneric, + "org.freedesktop.NetworkManager.Device.Wired" => Self::DeviceWired, + "org.freedesktop.NetworkManager.Device.Wireless" => Self::DeviceWireless, + "org.freedesktop.NetworkManager.DHCP4Config" => Self::Dhcp4Config, + "org.freedesktop.NetworkManager.DHCP6Config" => Self::Dhcp6Config, + "org.freedesktop.NetworkManager.IP4Config" => Self::Ip4Config, + "org.freedesktop.NetworkManager.IP6Config" => Self::Ip6Config, + "org.freedesktop.NetworkManager.Settings" => Self::Settings, + "org.freedesktop.NetworkManager.Settings.Connection" => Self::SettingsConnection, + _ => Self::Unknown, + }) + .collect(); + + types + } +} + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Started, + Stopped, + ObjectAdded { + path: String, + ifaces: Vec, + }, + ObjectRemoved { + path: String, + ifaces: Vec, + }, + PropertyStateChanged(u32), + PropertyConnectivityChanged(u32), + PropertyPrimaryConnectionChanged(String), +} + +#[derive(GodotClass)] +#[class(base=Resource)] +pub struct NetworkManagerInstance { + base: Base, + rx: Receiver, + conn: Option, + active_connections: HashMap>, + access_points: HashMap>, + devices: HashMap>, + devices_wireless: HashMap>, + ipv4_configs: HashMap>, + + /// Current connectivity status + #[allow(dead_code)] + #[var(get = get_connectivity)] + connectivity: i32, + /// The primary active connection being used to access the network + #[allow(dead_code)] + #[var(get = get_primary_connection)] + primary_connection: Option>, + /// The network connectivity state + #[allow(dead_code)] + #[var(get = get_state)] + state: u32, + /// Indicates if wireless is enabled or not + #[allow(dead_code)] + #[var(get = get_wireless_enabled, set = set_wireless_enabled)] + wireless_enabled: bool, +} + +#[godot_api] +impl NetworkManagerInstance { + /// networking state is unknown + #[constant] + const NM_STATE_UNKNOWN: u32 = 0; + /// networking is not enabled + #[constant] + const NM_STATE_ASLEEP: u32 = 10; + /// there is no active network connection + #[constant] + const NM_STATE_DISCONNECTED: u32 = 20; + /// network connections are being cleaned up + #[constant] + const NM_STATE_DISCONNECTING: u32 = 30; + /// a network connection is being started + #[constant] + const NM_STATE_CONNECTING: u32 = 40; + /// there is only local IPv4 and/or IPv6 connectivity + #[constant] + const NM_STATE_CONNECTED_LOCAL: u32 = 50; + /// there is only site-wide IPv4 and/or IPv6 connectivity + #[constant] + const NM_STATE_CONNECTED_SITE: u32 = 60; + /// there is global IPv4 and/or IPv6 Internet connectivity + #[constant] + const NM_STATE_CONNECTED_GLOBAL: u32 = 70; + + /// Network connectivity is unknown. + #[constant] + const NM_CONNECTIVITY_UNKNOWN: i32 = 1; + /// The host is not connected to any network. + #[constant] + const NM_CONNECTIVITY_NONE: i32 = 2; + /// The host is behind a captive portal and cannot reach the full Internet. + #[constant] + const NM_CONNECTIVITY_PORTAL: i32 = 3; + /// The host is connected to a network, but does not appear to be able to reach the full Internet. + #[constant] + const NM_CONNECTIVITY_LIMITED: i32 = 4; + /// The host is connected to a network, and appears to be able to reach the full Internet. + #[constant] + const NM_CONNECTIVITY_FULL: i32 = 5; + + /// Emitted when NetworkManager is detected as running + #[signal] + fn started(); + + /// Emitted when NetworkManager is detected as stopped + #[signal] + fn stopped(); + + /// Emitted when the network state changes + #[signal] + fn state_changed(state: u32); + + /// Emitted when network connectivity changes + #[signal] + fn connectivity_changed(connectivity: u32); + + /// Emitted when the primary connection changes + #[signal] + fn primary_connection_changed(connection: Option>); + + /// Return a proxy instance to the NetworkManager + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + NetworkManagerProxyBlocking::builder(conn).build().ok() + } else { + None + } + } + + /// Returns true if the NetworkManager service is currently running + #[func] + pub fn is_running(&self) -> bool { + let Some(conn) = self.conn.as_ref() else { + return false; + }; + let bus = BusName::from_static_str(NETWORK_MANAGER_BUS).unwrap(); + let dbus = zbus::blocking::fdo::DBusProxy::new(conn).ok(); + let Some(dbus) = dbus else { + return false; + }; + dbus.name_has_owner(bus.clone()).unwrap_or_default() + } + + /// The network connectivity state. + #[func] + pub fn get_connectivity(&self) -> i32 { + let Some(proxy) = self.get_proxy() else { + return NetworkManagerInstance::NM_CONNECTIVITY_UNKNOWN; + }; + let value = proxy + .connectivity() + .ok() + .unwrap_or(NetworkManagerInstance::NM_CONNECTIVITY_UNKNOWN as u32); + value as i32 + } + + /// The network connectivity state + #[func] + pub fn get_state(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return NetworkManagerInstance::NM_STATE_UNKNOWN; + }; + proxy.state().unwrap_or_default() + } + + /// Indicates if wireless is currently enabled or not + #[func] + pub fn get_wireless_enabled(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.wireless_enabled().unwrap_or_default() + } + + /// Set whether wireless networking should be enabled + #[func] + pub fn set_wireless_enabled(&self, value: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_wireless_enabled(value).unwrap_or_default() + } + + /// The primary active connection being used to access the network + #[func] + pub fn get_primary_connection(&self) -> Option> { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let path = proxy.primary_connection().unwrap_or_default(); + if path.is_empty() || path.as_str() == "/" { + return None; + } + let path = path.to_string(); + self.active_connections.get(&path).cloned() + } + + /// Returns an array of all network devices + #[func] + pub fn get_devices(&self) -> Array> { + let mut devices = array![]; + for device in self.devices.values() { + devices.push(device); + } + + devices + } + + /// Returns a HashMap of all the objects managed by this dbus interface + fn get_managed_objects(&self) -> Result { + let Some(conn) = self.conn.as_ref() else { + return Err(zbus::fdo::Error::Disconnected( + "No DBus connection found".into(), + )); + }; + + let bus = BusName::from_static_str(NETWORK_MANAGER_BUS).unwrap(); + let object_manager = zbus::blocking::fdo::ObjectManagerProxy::builder(conn) + .destination(bus) + .ok() + .and_then(|builder| builder.path(OBJECT_MANAGER_PATH).ok()) + .and_then(|builder| builder.build().ok()); + let Some(object_manager) = object_manager else { + return Ok(ManagedObjects::new()); + }; + + object_manager.get_managed_objects() + } + + /// Process NetworkManager signals and emit them as Godot signals. This method + /// should be called every frame in the "_process" loop of a node. + #[func] + fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + + // Process signals from tracked objects + for (_, device) in self.devices.iter_mut() { + device.bind_mut().process(); + } + for (_, device) in self.devices_wireless.iter_mut() { + device.bind_mut().process(); + } + for (_, connection) in self.active_connections.iter_mut() { + connection.bind_mut().process(); + } + for (_, ap) in self.access_points.iter_mut() { + ap.bind_mut().process(); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Started => { + self.base_mut().emit_signal("started", &[]); + } + Signal::Stopped => { + self.devices.clear(); + self.base_mut().emit_signal("stopped", &[]); + } + Signal::ObjectAdded { path, ifaces } => { + self.on_object_added(path.as_str(), ifaces); + } + Signal::ObjectRemoved { path, ifaces } => { + self.on_object_removed(path.as_str(), ifaces); + } + Signal::PropertyStateChanged(value) => { + self.base_mut() + .emit_signal("state_changed", &[value.to_variant()]); + } + Signal::PropertyConnectivityChanged(value) => { + self.base_mut() + .emit_signal("connectivity_changed", &[value.to_variant()]); + } + Signal::PropertyPrimaryConnectionChanged(path) => { + let conn = if path.is_empty() || path.as_str() == "/" { + None + } else { + Some(NetworkActiveConnection::new(path.as_str())) + }; + self.base_mut() + .emit_signal("primary_connection_changed", &[conn.to_variant()]); + } + } + } + + /// Track the given object and emit signals + fn on_object_added(&mut self, path: &str, types: Vec) { + for iface in types { + match iface { + ObjectType::Unknown => (), + ObjectType::AccessPoint => { + let ap = NetworkAccessPoint::new(path); + self.access_points.insert(path.to_string(), ap.clone()); + } + ObjectType::AgentManager => (), + ObjectType::ConnectionActive => { + let connection = NetworkActiveConnection::new(path); + self.active_connections + .insert(path.to_string(), connection.clone()); + } + ObjectType::Device => { + let device = NetworkDevice::new(path); + self.devices.insert(path.to_string(), device.clone()); + } + ObjectType::DeviceBluetooth => (), + ObjectType::DeviceGeneric => (), + ObjectType::DeviceWired => (), + ObjectType::DeviceWireless => { + let device = NetworkDeviceWireless::new(path); + self.devices_wireless + .insert(path.to_string(), device.clone()); + } + ObjectType::Dhcp4Config => (), + ObjectType::Dhcp6Config => (), + ObjectType::Ip4Config => { + let config = NetworkIpv4Config::new(path); + self.ipv4_configs.insert(path.to_string(), config.clone()); + } + ObjectType::Ip6Config => (), + ObjectType::Settings => (), + ObjectType::SettingsConnection => (), + } + } + } + + /// Remove the given object and emit signals + fn on_object_removed(&mut self, path: &str, types: Vec) { + for iface in types { + match iface { + ObjectType::Unknown => (), + ObjectType::AccessPoint => { + self.access_points.remove(path); + } + ObjectType::AgentManager => (), + ObjectType::ConnectionActive => { + self.active_connections.remove(path); + } + ObjectType::Device => { + self.devices.remove(path); + } + ObjectType::DeviceBluetooth => (), + ObjectType::DeviceGeneric => (), + ObjectType::DeviceWired => (), + ObjectType::DeviceWireless => { + self.devices_wireless.remove(path); + } + ObjectType::Dhcp4Config => (), + ObjectType::Dhcp6Config => (), + ObjectType::Ip4Config => { + self.ipv4_configs.remove(path); + } + ObjectType::Ip6Config => (), + ObjectType::Settings => (), + ObjectType::SettingsConnection => (), + } + } + } +} + +#[godot_api] +impl IResource for NetworkManagerInstance { + /// Called upon object initialization in the engine + fn init(base: Base) -> Self { + log::debug!("Initializing NetworkManager instance"); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + let conn = get_dbus_system_blocking().ok(); + + // Don't run in the editor + let engine = Engine::singleton(); + if engine.is_editor_hint() { + return Self { + base, + rx, + conn, + connectivity: NetworkManagerInstance::NM_CONNECTIVITY_UNKNOWN, + active_connections: Default::default(), + access_points: Default::default(), + devices: Default::default(), + devices_wireless: Default::default(), + ipv4_configs: Default::default(), + state: Default::default(), + wireless_enabled: Default::default(), + primary_connection: Default::default(), + }; + } + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx).await { + log::error!("Failed to run NetworkManager task: ${e:?}"); + } + }); + + // Create a new NetworkManager instance + let mut instance = Self { + base, + rx, + conn, + connectivity: NetworkManagerInstance::NM_CONNECTIVITY_UNKNOWN, + active_connections: Default::default(), + access_points: Default::default(), + devices: Default::default(), + devices_wireless: Default::default(), + ipv4_configs: Default::default(), + state: Default::default(), + wireless_enabled: Default::default(), + primary_connection: Default::default(), + }; + if !instance.is_running() { + return instance; + } + + // Do initial object discovery + let objects = instance.get_managed_objects().unwrap_or_default(); + for (path, ifaces) in objects.into_iter() { + let path = path.to_string(); + let ifaces: Vec = ifaces.into_keys().map(|v| v.to_string()).collect(); + let obj_types = ObjectType::from_ifaces(ifaces); + instance.on_object_added(path.as_str(), obj_types); + } + + instance + } +} + +/// Runs NetworkManager tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning networkmanager"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Spawn a task to listen for NetworkManager start/stop + let dbus_conn = conn.clone(); + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + let bus = BusName::from_static_str(NETWORK_MANAGER_BUS).unwrap(); + let mut is_running = { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + return; + }; + dbus.name_has_owner(bus.clone()).await.unwrap_or_default() + }; + + loop { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + break; + }; + let running = dbus.name_has_owner(bus.clone()).await.unwrap_or_default(); + if running != is_running { + let signal = if running { + Signal::Started + } else { + Signal::Stopped + }; + if signals_tx.send(signal).is_err() { + break; + } + } + is_running = running; + tokio::time::sleep(Duration::from_secs(5)).await; + } + }); + + // Get a proxy instance to ObjectManager + let bus = BusName::from_static_str(NETWORK_MANAGER_BUS).unwrap(); + let object_manager: ObjectManagerProxy = ObjectManagerProxy::builder(&conn) + .destination(bus)? + .path(OBJECT_MANAGER_PATH)? + .build() + .await?; + + // Spawn a task to listen for objects added/removed + let mut ifaces_added = object_manager.receive_interfaces_added().await?; + let mut ifaces_removed = object_manager.receive_interfaces_removed().await?; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + loop { + tokio::select! { + signal = ifaces_added.next() => { + let Some(signal) = signal else { + break; + }; + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + + let path = args.object_path.to_string(); + let ifaces = args + .interfaces_and_properties + .keys() + .map(|v| v.to_string()) + .collect(); + let ifaces = ObjectType::from_ifaces(ifaces); + let signal = Signal::ObjectAdded { path, ifaces }; + if signals_tx.send(signal).is_err() { + break; + } + } + signal = ifaces_removed.next() => { + let Some(signal) = signal else { + break; + }; + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + + let path = args.object_path.to_string(); + let ifaces = args.interfaces.iter().map(|v| v.to_string()).collect(); + let ifaces = ObjectType::from_ifaces(ifaces); + let signal = Signal::ObjectRemoved { path, ifaces }; + if signals_tx.send(signal).is_err() { + break; + } + } + } + } + }); + + // Get a proxy instance to Networkmanager + let network_manager = NetworkManagerProxy::builder(&conn).build().await?; + + // Spawn a task for property changes + let mut state_changed = network_manager.receive_state_changed().await; + let mut connectivity_changed = network_manager.receive_connectivity_changed().await; + let mut primary_changed = network_manager.receive_primary_connection_changed().await; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + loop { + tokio::select! { + signal = state_changed.next() => { + let Some(signal) = signal else { + break; + }; + let value = signal.get().await.unwrap_or_default(); + let signal = Signal::PropertyStateChanged(value); + if signals_tx.send(signal).is_err() { + break; + } + } + signal = connectivity_changed.next() => { + let Some(signal) = signal else { + break; + }; + let value = signal.get().await.unwrap_or_default(); + let signal = Signal::PropertyConnectivityChanged(value); + if signals_tx.send(signal).is_err() { + break; + } + } + signal = primary_changed.next() => { + let Some(signal) = signal else { + break; + }; + let value = signal.get().await.unwrap_or_default().to_string(); + let signal = Signal::PropertyPrimaryConnectionChanged(value); + if signals_tx.send(signal).is_err() { + break; + } + } + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/network/network_manager/access_point.rs b/extensions/core/src/network/network_manager/access_point.rs new file mode 100644 index 000000000..2d06318ff --- /dev/null +++ b/extensions/core/src/network/network_manager/access_point.rs @@ -0,0 +1,465 @@ +use std::collections::HashMap; + +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; +use zvariant::ObjectPath; + +use crate::dbus::networkmanager::access_point::{AccessPointProxy, AccessPointProxyBlocking}; +use crate::dbus::networkmanager::network_manager::NetworkManagerProxyBlocking; +use crate::dbus::networkmanager::settings::SettingsProxyBlocking; +use crate::dbus::RunError; +use crate::{get_dbus_system, get_dbus_system_blocking, RUNTIME}; +use futures_util::stream::StreamExt; +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use super::active_connection::NetworkActiveConnection; +use super::device::NetworkDevice; +use super::NETWORK_MANAGER_BUS; + +/// Signals that can be emitted by this resource +#[derive(Debug)] +enum Signal { + PropertyStrengthChanged(u8), +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct NetworkAccessPoint { + base: Base, + + conn: Option, + rx: Receiver, + path: String, + + /// The DBus path of the [NetworkAccessPoint] + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + /// The Service Set Identifier identifying the access point. + #[allow(dead_code)] + #[var(get = get_ssid)] + ssid: GString, + /// The current signal quality of the access point, in percent. + #[allow(dead_code)] + #[var(get = get_strength)] + strength: u8, + /// Flags describing the capabilities of the access point. + #[allow(dead_code)] + #[var(get = get_flags)] + flags: u32, + /// Flags describing the access point's capabilities according to WPA (Wifi Protected Access). + #[allow(dead_code)] + #[var(get = get_wpa_flags)] + wpa_flags: u32, + /// The radio channel frequency in use by the access point, in MHz. + #[allow(dead_code)] + #[var(get = get_frequency)] + frequency: u32, + /// The hardware address (BSSID) of the access point. + #[allow(dead_code)] + #[var(get = get_hardware_address)] + hardware_address: GString, + /// Describes the operating mode of the access point. + #[allow(dead_code)] + #[var(get = get_mode)] + mode: u32, + /// The maximum bitrate this access point is capable of, in kilobits/second (Kb/s). + #[allow(dead_code)] + #[var(get = get_max_bitrate)] + max_bitrate: u32, + /// The timestamp (in CLOCK_BOOTTIME seconds) for the last time the access point was found in scan results. A value of -1 means the access point has never been found in scan results. + #[allow(dead_code)] + #[var(get = get_last_seen)] + last_seen: i32, +} + +#[godot_api] +impl NetworkAccessPoint { + /// access point has no special capabilities + #[constant] + const NM_802_11_AP_FLAGS_NONE: u32 = 0x00000000; + /// access point requires authentication and encryption (usually means WEP) + #[constant] + const NM_802_11_AP_FLAGS_PRIVACY: u32 = 0x00000001; + + /// the access point has no special security requirements + #[constant] + const NM_802_11_AP_SEC_NONE: u32 = 0x00000000; + /// 40/64-bit WEP is supported for pairwise/unicast encryption + #[constant] + const NM_802_11_AP_SEC_PAIR_WEP40: u32 = 0x00000001; + /// 104/128-bit WEP is supported for pairwise/unicast encryption + #[constant] + const NM_802_11_AP_SEC_PAIR_WEP104: u32 = 0x00000002; + /// TKIP is supported for pairwise/unicast encryption + #[constant] + const NM_802_11_AP_SEC_PAIR_TKIP: u32 = 0x00000004; + /// AES/CCMP is supported for pairwise/unicast encryption + #[constant] + const NM_802_11_AP_SEC_PAIR_CCMP: u32 = 0x00000008; + /// 40/64-bit WEP is supported for group/broadcast encryption + #[constant] + const NM_802_11_AP_SEC_GROUP_WEP40: u32 = 0x00000010; + /// 104/128-bit WEP is supported for group/broadcast encryption + #[constant] + const NM_802_11_AP_SEC_GROUP_WEP104: u32 = 0x00000020; + /// TKIP is supported for group/broadcast encryption + #[constant] + const NM_802_11_AP_SEC_GROUP_TKIP: u32 = 0x00000040; + /// AES/CCMP is supported for group/broadcast encryption + #[constant] + const NM_802_11_AP_SEC_GROUP_CCMP: u32 = 0x00000080; + /// WPA/RSN Pre-Shared Key encryption is supported + #[constant] + const NM_802_11_AP_SEC_KEY_MGMT_PSK: u32 = 0x00000100; + /// 802.1x authentication and key management is supported + #[constant] + const NM_802_11_AP_SEC_KEY_MGMT_802_1X: u32 = 0x00000200; + + /// the device or access point mode is unknown + #[constant] + const NM_802_11_MODE_UNKNOWN: u32 = 0; + /// for both devices and access point objects, indicates the object is part of an Ad-Hoc 802.11 network without a central coordinating access point. + #[constant] + const NM_802_11_MODE_ADHOC: u32 = 1; + /// the device or access point is in infrastructure mode. For devices, this indicates the device is an 802.11 client/station. For access point objects, this indicates the object is an access point that provides connectivity to clients. + #[constant] + const NM_802_11_MODE_INFRA: u32 = 2; + /// the device is an access point/hotspot. Not valid for access point objects; used only for hotspot mode on the local machine. + #[constant] + const NM_802_11_MODE_AP: u32 = 3; + + /// Emitted whenever the connection strength changes + #[signal] + fn strength_changed(strength: u8); + + /// Create a new [NetworkAccessPoint] with the given DBus path + pub fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + + // Spawn a task using the shared tokio runtime to listen for signals + let dbus_path = path.to_string(); + RUNTIME.spawn(async move { + if let Err(e) = run(dbus_path, tx).await { + log::error!("Failed to run AccessPoint task: ${e:?}"); + } + }); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + rx, + path: path.clone().into(), + dbus_path: path, + ssid: Default::default(), + strength: Default::default(), + flags: Default::default(), + wpa_flags: Default::default(), + frequency: Default::default(), + hardware_address: Default::default(), + mode: Default::default(), + max_bitrate: Default::default(), + last_seen: Default::default(), + } + }) + } + + /// Return a proxy instance to the network device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + AccessPointProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Return a proxy instance to the settings interface + fn get_settings_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + SettingsProxyBlocking::builder(conn).build().ok() + } else { + None + } + } + + /// Return a proxy instance to the network manager interface + fn get_manager_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + NetworkManagerProxyBlocking::builder(conn).build().ok() + } else { + None + } + } + + /// Get or create a [NetworkAccessPoint] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = NetworkAccessPoint::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = NetworkAccessPoint::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + /// Start connecting to the access point with the given password. + #[func] + pub fn connect( + &self, + device: Gd, + password: GString, + ) -> Option> { + let Some(settings) = self.get_settings_proxy() else { + log::warn!("Unable to connect; settings not found"); + return None; + }; + let Some(network_manager) = self.get_manager_proxy() else { + log::warn!("Unable to connect; network manager not found"); + return None; + }; + let ssid = self.get_ssid().to_string(); + if ssid.is_empty() { + log::warn!("SSID is empty; unable to connect"); + return None; + } + + // Build the connection settings + let mut connection = HashMap::new(); + let mut conn = HashMap::new(); + let conn_type = zvariant::Value::from("802-11-wireless"); + conn.insert("type", &conn_type); + let id = zvariant::Value::from(ssid.clone()); + conn.insert("id", &id); + connection.insert("connection", conn); + + let mut wireless = HashMap::new(); + let wireless_ssid = zvariant::Value::from(ssid.as_bytes()); + wireless.insert("ssid", &wireless_ssid); + connection.insert("802-11-wireless", wireless); + + let key_mgmt = zvariant::Value::from("wpa-psk"); + let psk = zvariant::Value::from(password.to_string()); + if !password.is_empty() { + let mut security = HashMap::new(); + security.insert("key-mgmt", &key_mgmt); + security.insert("psk", &psk); + + connection.insert("802-11-wireless-security", security); + } + + // Add the connection + let connection_path = match settings.add_connection(connection) { + Ok(conn_path) => conn_path, + Err(e) => { + log::warn!("Unable to add new connection: {e:?}"); + return None; + } + }; + + let connection_path = ObjectPath::from(connection_path); + let device_path = match ObjectPath::try_from(device.bind().get_dbus_path().to_string()) { + Ok(path) => path, + Err(e) => { + log::warn!("Invalid device path: {e:?}"); + return None; + } + }; + let ap_path = match ObjectPath::try_from(self.dbus_path.to_string()) { + Ok(path) => path, + Err(e) => { + log::warn!("Invalid access point path: {e:?}"); + return None; + } + }; + + // Activate the connection + let active_connection_path = + match network_manager.activate_connection(&connection_path, &device_path, &ap_path) { + Ok(active_connection_path) => active_connection_path, + Err(e) => { + log::warn!("Failed to activate connection: {e:?}"); + return None; + } + }; + if active_connection_path.is_empty() { + return None; + } + + Some(NetworkActiveConnection::new( + active_connection_path.as_str(), + )) + } + + /// The Service Set Identifier identifying the access point. + #[func] + pub fn get_ssid(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let value = proxy.ssid().unwrap_or_default(); + String::from_utf8_lossy(value.as_slice()).to_string().into() + } + + /// The current signal quality of the access point, in percent. + #[func] + pub fn get_strength(&self) -> u8 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.strength().unwrap_or_default() + } + + /// Flags describing the capabilities of the access point. + #[func] + pub fn get_flags(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.flags().unwrap_or_default() + } + + /// Flags describing the access point's capabilities according to WPA (Wifi Protected Access). + #[func] + pub fn get_wpa_flags(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.wpa_flags().unwrap_or_default() + } + + /// The radio channel frequency in use by the access point, in MHz. + #[func] + pub fn get_frequency(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.frequency().unwrap_or_default() + } + + /// The hardware address (BSSID) of the access point. + #[func] + pub fn get_hardware_address(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.hw_address().unwrap_or_default().into() + } + + /// Describes the operating mode of the access point. + #[func] + pub fn get_mode(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.mode().unwrap_or_default() + } + + /// The maximum bitrate this access point is capable of, in kilobits/second (Kb/s). + #[func] + pub fn get_max_bitrate(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.max_bitrate().unwrap_or_default() + } + + /// The timestamp (in CLOCK_BOOTTIME seconds) for the last time the access point was found in scan results. A value of -1 means the access point has never been found in scan results. + #[func] + pub fn get_last_seen(&self) -> i32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.last_seen().unwrap_or_default() + } + + /// Process signals and emit them as Godot signals. + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::PropertyStrengthChanged(value) => { + self.base_mut() + .emit_signal("strength_changed", &[value.to_variant()]); + } + } + } +} + +/// Runs NetworkAccessPoint tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(path: String, tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning access point task for {path}"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Get a proxy instance to the device + let connection = AccessPointProxy::builder(&conn).path(path)?.build().await?; + + // Spawn a task to listen for property changes + let mut state_changed = connection.receive_strength_changed().await; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + loop { + tokio::select! { + signal = state_changed.next() => { + let Some(signal) = signal else { + break; + }; + let value = signal.get().await.unwrap_or_default(); + let signal = Signal::PropertyStrengthChanged(value); + if signals_tx.send(signal).is_err() { + break; + } + } + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/network/network_manager/active_connection.rs b/extensions/core/src/network/network_manager/active_connection.rs new file mode 100644 index 000000000..33ddc1c03 --- /dev/null +++ b/extensions/core/src/network/network_manager/active_connection.rs @@ -0,0 +1,224 @@ +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::networkmanager::active::{ActiveProxy, ActiveProxyBlocking}; +use crate::dbus::RunError; +use crate::{get_dbus_system, get_dbus_system_blocking, RUNTIME}; +use futures_util::stream::StreamExt; +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use super::device::NetworkDevice; +use super::NETWORK_MANAGER_BUS; + +/// Signals that can be emitted by this resource +#[derive(Debug)] +enum Signal { + PropertyStateChanged(u32), +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct NetworkActiveConnection { + base: Base, + + conn: Option, + rx: Receiver, + path: String, + + /// The DBus path of the [NetworkActiveConnection] + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + /// Array of devices which are part of this active connection + #[allow(dead_code)] + #[var(get = get_devices)] + devices: Array>, + /// Current state of the connection + #[allow(dead_code)] + #[var(get = get_state)] + state: u32, +} + +#[godot_api] +impl NetworkActiveConnection { + /// the state of the connection is unknown + #[constant] + const NM_ACTIVE_CONNECTION_STATE_UNKNOWN: u32 = 0; + /// a network connection is being prepared + #[constant] + const NM_ACTIVE_CONNECTION_STATE_ACTIVATING: u32 = 1; + /// there is a connection to the network + #[constant] + const NM_ACTIVE_CONNECTION_STATE_ACTIVATED: u32 = 2; + /// the network connection is being torn down and cleaned up + #[constant] + const NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: u32 = 3; + /// the network connection is disconnected and will be removed + #[constant] + const NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: u32 = 4; + + /// Emitted whenever the connection state changes + #[signal] + fn state_changed(state: u32); + + /// Create a new [NetworkActiveConnection] with the given DBus path + pub fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + + // Spawn a task using the shared tokio runtime to listen for signals + let dbus_path = path.to_string(); + RUNTIME.spawn(async move { + if let Err(e) = run(dbus_path, tx).await { + log::error!("Failed to run NetworkDevice task: ${e:?}"); + } + }); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + rx, + path: path.clone().into(), + dbus_path: path, + devices: Default::default(), + state: Default::default(), + } + }) + } + + /// Return a proxy instance to the network device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + ActiveProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [NetworkActiveConnection] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = NetworkActiveConnection::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = NetworkActiveConnection::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + /// Array of devices which are part of this active connection + #[func] + pub fn get_devices(&self) -> Array> { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let paths = proxy.devices().unwrap_or_default(); + let mut devices = array![]; + for path in paths { + let path = path.to_string(); + let device = NetworkDevice::new(path.as_str()); + devices.push(&device); + } + + devices + } + + /// The state of this active connection. + #[func] + pub fn get_state(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.state().unwrap_or_default() + } + + /// Process signals and emit them as Godot signals. + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::PropertyStateChanged(value) => { + self.base_mut() + .emit_signal("state_changed", &[value.to_variant()]); + } + } + } +} + +/// Runs NetworkDevice tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(path: String, tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning active connection task for {path}"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Get a proxy instance to the device + let connection = ActiveProxy::builder(&conn).path(path)?.build().await?; + + // Spawn a task to listen for property changes + let mut state_changed = connection.receive_state_changed().await; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + loop { + tokio::select! { + signal = state_changed.next() => { + let Some(signal) = signal else { + break; + }; + let value = signal.get().await.unwrap_or_default(); + let signal = Signal::PropertyStateChanged(value); + if signals_tx.send(signal).is_err() { + break; + } + } + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/network/network_manager/device.rs b/extensions/core/src/network/network_manager/device.rs new file mode 100644 index 000000000..4824be160 --- /dev/null +++ b/extensions/core/src/network/network_manager/device.rs @@ -0,0 +1,368 @@ +use godot::obj::WithBaseField; +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::networkmanager::device::{DeviceProxy, DeviceProxyBlocking}; +use crate::dbus::RunError; +use crate::{get_dbus_system, get_dbus_system_blocking, RUNTIME}; +use futures_util::stream::StreamExt; +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use super::device_wireless::NetworkDeviceWireless; +use super::ip4_config::NetworkIpv4Config; +use super::NETWORK_MANAGER_BUS; + +/// Signals that can be emitted by this resource +#[derive(Debug)] +enum Signal { + PropertyStateChanged(u32), +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct NetworkDevice { + base: Base, + + conn: Option, + rx: Receiver, + path: String, + + /// The DBus path of the [NetworkDevice] + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + /// The general type of the network device; ie Ethernet, WiFi, etc. + #[allow(dead_code)] + #[var(get = get_device_type)] + device_type: u32, + /// Current state of the device + #[allow(dead_code)] + #[var(get = get_state)] + state: u32, + /// The [NetworkIpv4Config] describing the configuration of the device. Null if device has no IP. + #[allow(dead_code)] + #[var(get = get_ip4_config)] + ip4_config: Option>, + /// The name of the device's control (and often data) interface. Note that non UTF-8 characters are backslash escaped, so the resulting name may be longer then 15 characters. Use g_strcompress() to revert the escaping. + #[allow(dead_code)] + #[var(get = get_interface)] + interface: GString, + /// The [NetworkDeviceWireless] for the device. Will be null if this is not a wireless device + #[allow(dead_code)] + #[var(get = get_wireless)] + wireless: Option>, +} + +#[godot_api] +impl NetworkDevice { + /// unknown device + #[constant] + const NM_DEVICE_TYPE_UNKNOWN: u32 = 0; + /// generic support for unrecognized device types + #[constant] + const NM_DEVICE_TYPE_GENERIC: u32 = 14; + /// a wired ethernet device + #[constant] + const NM_DEVICE_TYPE_ETHERNET: u32 = 1; + /// an 802.11 WiFi device + #[constant] + const NM_DEVICE_TYPE_WIFI: u32 = 2; + /// not used + #[constant] + const NM_DEVICE_TYPE_UNUSED1: u32 = 3; + /// not used + #[constant] + const NM_DEVICE_TYPE_UNUSED2: u32 = 4; + /// a Bluetooth device supporting PAN or DUN access protocols + #[constant] + const NM_DEVICE_TYPE_BT: u32 = 5; + /// an OLPC XO mesh networking device + #[constant] + const NM_DEVICE_TYPE_OLPC_MESH: u32 = 6; + /// an 802.16e Mobile WiMAX broadband device + #[constant] + const NM_DEVICE_TYPE_WIMAX: u32 = 7; + /// a modem supporting analog telephone, CDMA/EVDO, GSM/UMTS, or LTE network access protocols + #[constant] + const NM_DEVICE_TYPE_MODEM: u32 = 8; + /// an IP-over-InfiniBand device + #[constant] + const NM_DEVICE_TYPE_INFINIBAND: u32 = 9; + /// a bond master interface + #[constant] + const NM_DEVICE_TYPE_BOND: u32 = 10; + /// an 802.1Q VLAN interface + #[constant] + const NM_DEVICE_TYPE_VLAN: u32 = 11; + /// ADSL modem + #[constant] + const NM_DEVICE_TYPE_ADSL: u32 = 12; + /// a bridge master interface + #[constant] + const NM_DEVICE_TYPE_BRIDGE: u32 = 13; + /// a team master interface + #[constant] + const NM_DEVICE_TYPE_TEAM: u32 = 15; + /// a TUN or TAP interface + #[constant] + const NM_DEVICE_TYPE_TUN: u32 = 16; + /// a IP tunnel interface + #[constant] + const NM_DEVICE_TYPE_IP_TUNNEL: u32 = 17; + /// a MACVLAN interface + #[constant] + const NM_DEVICE_TYPE_MACVLAN: u32 = 18; + /// a VXLAN interface + #[constant] + const NM_DEVICE_TYPE_VXLAN: u32 = 19; + /// a VETH interface + #[constant] + const NM_DEVICE_TYPE_VETH: u32 = 20; + + /// the device's state is unknown + #[constant] + const NM_DEVICE_STATE_UNKNOWN: u32 = 0; + /// the device is recognized, but not managed by NetworkManager + #[constant] + const NM_DEVICE_STATE_UNMANAGED: u32 = 10; + /// the device is managed by NetworkManager, but is not available for use. Reasons may include the wireless switched off, missing firmware, no ethernet carrier, missing supplicant or modem manager, etc. + #[constant] + const NM_DEVICE_STATE_UNAVAILABLE: u32 = 20; + /// the device can be activated, but is currently idle and not connected to a network. + #[constant] + const NM_DEVICE_STATE_DISCONNECTED: u32 = 30; + /// the device is preparing the connection to the network. This may include operations like changing the MAC address, setting physical link properties, and anything else required to connect to the requested network. + #[constant] + const NM_DEVICE_STATE_PREPARE: u32 = 40; + /// the device is connecting to the requested network. This may include operations like associating with the WiFi AP, dialing the modem, connecting to the remote Bluetooth device, etc. + #[constant] + const NM_DEVICE_STATE_CONFIG: u32 = 50; + /// the device requires more information to continue connecting to the requested network. This includes secrets like WiFi passphrases, login passwords, PIN codes, etc. + #[constant] + const NM_DEVICE_STATE_NEED_AUTH: u32 = 60; + /// the device is requesting IPv4 and/or IPv6 addresses and routing information from the network. + #[constant] + const NM_DEVICE_STATE_IP_CONFIG: u32 = 70; + /// the device is checking whether further action is required for the requested network connection. This may include checking whether only local network access is available, whether a captive portal is blocking access to the Internet, etc. + #[constant] + const NM_DEVICE_STATE_IP_CHECK: u32 = 80; + /// the device is waiting for a secondary connection (like a VPN) which must activated before the device can be activated + #[constant] + const NM_DEVICE_STATE_SECONDARIES: u32 = 90; + /// the device has a network connection, either local or global. + #[constant] + const NM_DEVICE_STATE_ACTIVATED: u32 = 100; + /// a disconnection from the current network connection was requested, and the device is cleaning up resources used for that connection. The network connection may still be valid. + #[constant] + const NM_DEVICE_STATE_DEACTIVATING: u32 = 110; + /// the device failed to connect to the requested network and is cleaning up the connection request + #[constant] + const NM_DEVICE_STATE_FAILED: u32 = 120; + + /// Emitted whenever the device state changes + #[signal] + fn state_changed(state: u32); + + /// Create a new [NetworkDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + + // Spawn a task using the shared tokio runtime to listen for signals + let dbus_path = path.to_string(); + RUNTIME.spawn(async move { + if let Err(e) = run(dbus_path, tx).await { + log::error!("Failed to run NetworkDevice task: ${e:?}"); + } + }); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + rx, + path: path.clone().into(), // Convert GString -> String. + dbus_path: path, + device_type: Default::default(), + state: Default::default(), + wireless: Default::default(), + ip4_config: Default::default(), + interface: Default::default(), + } + }) + } + + /// Return a proxy instance to the network device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + DeviceProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [NetworkDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = NetworkDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = NetworkDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + /// The general type of the network device; ie Ethernet, WiFi, etc. + #[func] + pub fn get_device_type(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return NetworkDevice::NM_DEVICE_TYPE_UNKNOWN; + }; + proxy + .device_type() + .unwrap_or(NetworkDevice::NM_DEVICE_TYPE_UNKNOWN) + } + + /// Current state of the device + #[func] + pub fn get_state(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return NetworkDevice::NM_DEVICE_STATE_UNKNOWN; + }; + proxy + .state() + .unwrap_or(NetworkDevice::NM_DEVICE_STATE_UNKNOWN) + } + + /// The name of the device's control (and often data) interface. Note that non UTF-8 characters are backslash escaped, so the resulting name may be longer then 15 characters. Use g_strcompress() to revert the escaping. + #[func] + pub fn get_interface(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.interface().unwrap_or_default().into() + } + + /// The [NetworkIpv4Config] describing the configuration of the device. Null if device has no IP. + #[func] + pub fn get_ip4_config(&self) -> Option> { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let path = proxy.ip4_config().unwrap_or_default(); + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}{path}"); + + // Check to see if a resource exists + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + return Some(NetworkIpv4Config::new(path.as_str())); + } + + None + } + + /// Gets the wireless interface for this device. Returns null if this is not a wireless device. + #[func] + pub fn get_wireless(&self) -> Option> { + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}{}/wireless", self.dbus_path); + + // Check to see if a resource exists + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + return Some(NetworkDeviceWireless::new( + self.dbus_path.to_string().as_str(), + )); + } + + None + } + + /// Process NetworkDevice signals and emit them as Godot signals. + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::PropertyStateChanged(value) => { + self.base_mut() + .emit_signal("state_changed", &[value.to_variant()]); + } + } + } +} + +/// Runs NetworkDevice tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(path: String, tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning device task for {path}"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Get a proxy instance to the device + let device = DeviceProxy::builder(&conn).path(path)?.build().await?; + + // Spawn a task to listen for property changes + let mut state_changed = device.receive_state_changed().await; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + loop { + tokio::select! { + signal = state_changed.next() => { + let Some(signal) = signal else { + break; + }; + let value = signal.get().await.unwrap_or_default(); + let signal = Signal::PropertyStateChanged(value); + if signals_tx.send(signal).is_err() { + break; + } + } + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/network/network_manager/device_wireless.rs b/extensions/core/src/network/network_manager/device_wireless.rs new file mode 100644 index 000000000..24df965e3 --- /dev/null +++ b/extensions/core/src/network/network_manager/device_wireless.rs @@ -0,0 +1,298 @@ +use std::collections::HashMap; + +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::networkmanager::wireless::{WirelessProxy, WirelessProxyBlocking}; +use crate::dbus::RunError; +use crate::{get_dbus_system, get_dbus_system_blocking, RUNTIME}; +use futures_util::stream::StreamExt; +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use super::access_point::NetworkAccessPoint; +use super::NETWORK_MANAGER_BUS; + +/// Signals that can be emitted by this resource +#[derive(Debug)] +enum Signal { + AccessPointAdded(String), + AccessPointRemoved, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct NetworkDeviceWireless { + base: Base, + + conn: Option, + rx: Receiver, + path: String, + + /// The DBus path of the [NetworkDeviceWireless] + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + /// List of access point visible to this wireless device. + #[allow(dead_code)] + #[var(get = get_access_points)] + access_points: Array>, + /// The access point currently used by the wireless device. Null if no active access point. + #[allow(dead_code)] + #[var(get = get_active_access_point)] + active_access_point: Option>, + /// The bit rate currently used by the wireless device, in kilobits/second (Kb/s). + #[allow(dead_code)] + #[var(get = get_bitrate)] + bitrate: u32, + /// The active hardware address of the device. + #[allow(dead_code)] + #[var(get = get_hardware_address)] + hardware_address: GString, +} + +#[godot_api] +impl NetworkDeviceWireless { + /// Emitted when a new access point is detected + #[signal] + fn access_point_added(ap: Gd); + /// Emitted when an access point disappears + #[signal] + fn access_point_removed(); + + /// Create a new [NetworkDeviceWireless] with the given DBus path + pub fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + + // Spawn a task using the shared tokio runtime to listen for signals + let dbus_path = path.to_string(); + RUNTIME.spawn(async move { + if let Err(e) = run(dbus_path, tx).await { + log::error!("Failed to run NetworkDevice task: ${e:?}"); + } + }); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + rx, + path: path.clone().into(), + dbus_path: path, + hardware_address: Default::default(), + access_points: Default::default(), + active_access_point: Default::default(), + bitrate: Default::default(), + } + }) + } + + /// Return a proxy instance to the network device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + WirelessProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [NetworkDeviceWireless] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}{path}/wireless"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = NetworkDeviceWireless::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = NetworkDeviceWireless::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + /// The bit rate currently used by the wireless device, in kilobits/second (Kb/s). + #[func] + pub fn get_bitrate(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.bitrate().unwrap_or_default() + } + + /// List of access point visible to this wireless device. + #[func] + pub fn get_access_points(&self) -> Array> { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let mut resource_loader = ResourceLoader::singleton(); + let mut value = array![]; + let aps = proxy.get_access_points().unwrap_or_default(); + for ap in aps { + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}/{ap}"); + + // Check to see if the resource exists + if resource_loader.exists(res_path.as_str()) { + let access_point = NetworkAccessPoint::new(ap.as_str()); + value.push(&access_point); + } + } + + value + } + + /// The access point currently used by the wireless device. Null if no active access point. + #[func] + pub fn get_active_access_point(&self) -> Option> { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let ap = proxy.active_access_point().unwrap_or_default(); + if ap.is_empty() { + return None; + } + + let mut resource_loader = ResourceLoader::singleton(); + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}/{ap}"); + + // Check to see if the resource exists + if resource_loader.exists(res_path.as_str()) { + let access_point = NetworkAccessPoint::new(ap.as_str()); + return Some(access_point); + } + + None + } + + /// Request the device to scan + #[func] + pub fn request_scan(&self) { + let Some(proxy) = self.get_proxy() else { + return; + }; + proxy.request_scan(HashMap::new()).unwrap_or_default() + } + + /// The active hardware address of the device. + #[func] + pub fn get_hardware_address(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.hw_address().unwrap_or_default().into() + } + + /// Process signals and emit them as Godot signals. + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::AccessPointAdded(path) => { + let ap = NetworkAccessPoint::new(path.as_str()); + self.base_mut() + .emit_signal("access_point_added", &[ap.to_variant()]); + } + Signal::AccessPointRemoved => { + self.base_mut().emit_signal("access_point_removed", &[]); + } + } + } +} + +/// Runs NetworkDevice tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(path: String, tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning device task for {path}"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Get a proxy instance to the device + let device = WirelessProxy::builder(&conn).path(path)?.build().await?; + + // Spawn a task to listen for property changes + let mut access_point_added = device.receive_access_point_added().await?; + let mut access_point_removed = device.receive_access_point_removed().await?; + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + loop { + tokio::select! { + signal = access_point_added.next() => { + let Some(signal) = signal else { + break; + }; + let args = match signal.args() { + Ok(args) => args, + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + let access_point_path = args.access_point; + let signal = Signal::AccessPointAdded(access_point_path.to_string()); + if signals_tx.send(signal).is_err() { + break; + } + } + signal = access_point_removed.next() => { + let Some(signal) = signal else { + break; + }; + match signal.args() { + Ok(_) => (), + Err(e) => { + log::warn!("Failed to get signal args: ${e:?}"); + continue; + } + }; + let signal = Signal::AccessPointRemoved; + if signals_tx.send(signal).is_err() { + break; + } + } + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/network/network_manager/ip4_config.rs b/extensions/core/src/network/network_manager/ip4_config.rs new file mode 100644 index 000000000..fb0aa86df --- /dev/null +++ b/extensions/core/src/network/network_manager/ip4_config.rs @@ -0,0 +1,129 @@ +use godot::prelude::*; + +use godot::classes::{Resource, ResourceLoader}; + +use crate::dbus::networkmanager::ip4config::IP4ConfigProxyBlocking; +use crate::dbus::GodotVariant; +use crate::get_dbus_system_blocking; + +use super::NETWORK_MANAGER_BUS; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct NetworkIpv4Config { + base: Base, + + conn: Option, + path: String, + + /// The DBus path of the [NetworkIpv4Config] + #[allow(dead_code)] + #[var(get = get_dbus_path)] + dbus_path: GString, + /// Array of IP address data objects. All addresses will include "address" (an IP address string), and "prefix" (a uint). Some addresses may include additional attributes. + #[allow(dead_code)] + #[var(get = get_addresses)] + addresses: Array, + /// The gateway in use. + #[allow(dead_code)] + #[var(get = get_gateway)] + gateway: GString, +} + +#[godot_api] +impl NetworkIpv4Config { + /// Create a new [NetworkIpv4Config] with the given DBus path + pub fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + path: path.clone().into(), + dbus_path: path, + addresses: Default::default(), + gateway: Default::default(), + } + }) + } + + /// Return a proxy instance to the network device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + IP4ConfigProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [NetworkIpv4Config] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{NETWORK_MANAGER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists with path '{res_path}', loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = NetworkIpv4Config::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = NetworkIpv4Config::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the device + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone() + } + + /// The gateway in use. + #[func] + pub fn get_gateway(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.gateway().unwrap_or_default().to_godot() + } + + /// Array of IP address data objects. All addresses will include "address" (an IP address string), and "prefix" (a uint). Some addresses may include additional attributes. + #[func] + pub fn get_addresses(&self) -> Array { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let mut value = array![]; + let data = proxy.address_data().unwrap_or_default(); + for entry in data { + let mut dict = Dictionary::new(); + for (key, value) in entry.iter() { + let Some(value) = value.as_godot_variant() else { + continue; + }; + dict.set(key.to_godot(), value); + } + value.push(&dict); + } + + value + } + + /// Dispatches signals + pub fn process(&mut self) {} +} diff --git a/extensions/core/src/performance.rs b/extensions/core/src/performance.rs new file mode 100644 index 000000000..6ac1c52cb --- /dev/null +++ b/extensions/core/src/performance.rs @@ -0,0 +1 @@ +pub mod powerstation; diff --git a/extensions/core/src/performance/powerstation.rs b/extensions/core/src/performance/powerstation.rs new file mode 100644 index 000000000..5400ea9d6 --- /dev/null +++ b/extensions/core/src/performance/powerstation.rs @@ -0,0 +1,228 @@ +pub mod cpu; +pub mod cpu_core; +pub mod gpu; +pub mod gpu_card; +pub mod gpu_connector; + +use std::{ + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, + time::Duration, +}; + +use cpu::Cpu; +use godot::{classes::Engine, prelude::*}; +use gpu::Gpu; +use zbus::names::BusName; + +use crate::{dbus::RunError, get_dbus_system, get_dbus_system_blocking, RUNTIME}; + +pub const POWERSTATION_BUS: &str = "org.shadowblip.PowerStation"; +const POWERSTATION_CPU_PATH: &str = "/org/shadowblip/Performance/CPU"; +const POWERSTATION_GPU_PATH: &str = "/org/shadowblip/Performance/GPU"; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Started, + Stopped, +} + +/// PowerStation dbus proxy +#[derive(GodotClass)] +#[class(base=Resource)] +pub struct PowerStationInstance { + base: Base, + rx: Receiver, + conn: Option, + cpu_instance: Option>, + gpu_instance: Option>, + + #[allow(dead_code)] + #[var(get = get_cpu)] + cpu: Option>, + #[allow(dead_code)] + #[var(get = get_gpu)] + gpu: Option>, +} + +#[godot_api] +impl PowerStationInstance { + /// Emitted when PowerStation is detected as running + #[signal] + fn started(); + + /// Emitted when PowerStation is detected as stopped + #[signal] + fn stopped(); + + /// Returns true if the PowerStation service is currently running + #[func] + fn is_running(&self) -> bool { + let Some(conn) = self.conn.as_ref() else { + return false; + }; + let bus = BusName::from_static_str(POWERSTATION_BUS).unwrap(); + let dbus = zbus::blocking::fdo::DBusProxy::new(conn).ok(); + let Some(dbus) = dbus else { + return false; + }; + dbus.name_has_owner(bus.clone()).unwrap_or_default() + } + + /// Returns an instance of the CPU + #[func] + fn get_cpu(&self) -> Option> { + self.cpu_instance.clone() + } + + /// Returns an instance of the GPU + #[func] + fn get_gpu(&self) -> Option> { + self.gpu_instance.clone() + } + + /// Process UPower signals and emit them as Godot signals. This method + /// should be called every frame in the "_process" loop of a node. + #[func] + fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + + // Process any signals for the CPU instance + if let Some(cpu) = self.cpu_instance.as_mut() { + cpu.bind_mut().process(); + } + // Process any signals for the GPU instance + if let Some(gpu) = self.gpu_instance.as_mut() { + gpu.bind_mut().process(); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Started => { + // Create an instance for the CPU + self.cpu_instance = Some(Cpu::new(POWERSTATION_CPU_PATH)); + self.base_mut().emit_signal("started", &[]); + } + Signal::Stopped => { + self.cpu_instance = None; + self.base_mut().emit_signal("stopped", &[]); + } + } + } +} + +#[godot_api] +impl IResource for PowerStationInstance { + /// Called upon object initialization in the engine + fn init(base: Base) -> Self { + log::debug!("Initializing PowerStation instance"); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + let conn = get_dbus_system_blocking().ok(); + + // Don't run in the editor + let engine = Engine::singleton(); + if engine.is_editor_hint() { + return Self { + base, + rx, + conn, + cpu_instance: Default::default(), + gpu_instance: Default::default(), + cpu: Default::default(), + gpu: Default::default(), + }; + } + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx).await { + log::error!("Failed to run PowerStation task: ${e:?}"); + } + }); + + // Create CPU instance + let cpu = Some(Cpu::new(POWERSTATION_CPU_PATH)); + // Create GPU instance + let gpu = Some(Gpu::new(POWERSTATION_GPU_PATH)); + + // Create a new PowerStation instance + Self { + base, + rx, + conn, + cpu_instance: cpu, + gpu_instance: gpu, + cpu: None, + gpu: None, + } + } +} + +/// Runs PowerStation tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning PowerStation tasks"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Spawn a task to listen for PowerStation start/stop + let dbus_conn = conn.clone(); + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + let bus = BusName::from_static_str(POWERSTATION_BUS).unwrap(); + let mut is_running = { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + return; + }; + dbus.name_has_owner(bus.clone()).await.unwrap_or_default() + }; + let signal = if is_running { + Signal::Started + } else { + Signal::Stopped + }; + if signals_tx.send(signal).is_err() { + return; + } + + loop { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + break; + }; + let running = dbus.name_has_owner(bus.clone()).await.unwrap_or_default(); + if running != is_running { + let signal = if running { + Signal::Started + } else { + Signal::Stopped + }; + if signals_tx.send(signal).is_err() { + break; + } + } + is_running = running; + tokio::time::sleep(Duration::from_secs(5)).await; + } + }); + + Ok(()) +} diff --git a/extensions/core/src/performance/powerstation/cpu.rs b/extensions/core/src/performance/powerstation/cpu.rs new file mode 100644 index 000000000..943f49076 --- /dev/null +++ b/extensions/core/src/performance/powerstation/cpu.rs @@ -0,0 +1,334 @@ +use std::{ + collections::HashMap, + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, +}; + +use futures_util::StreamExt; +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{ + dbus::{ + powerstation::cpu::{CPUProxy, CPUProxyBlocking}, + RunError, + }, + get_dbus_system, get_dbus_system_blocking, RUNTIME, +}; + +use super::{cpu_core::CpuCore, POWERSTATION_BUS}; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Updated, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct Cpu { + base: Base, + path: String, + conn: Option, + rx: Receiver, + cores: HashMap>, + + #[allow(dead_code)] + #[var(get = get_boost_enabled, set = set_boost_enabled)] + boost_enabled: bool, + #[allow(dead_code)] + #[var(get = get_cores_count)] + cores_count: u32, + #[allow(dead_code)] + #[var(get = get_cores_enabled, set = set_cores_enabled)] + cores_enabled: u32, + #[allow(dead_code)] + #[var(get = get_features)] + features: PackedStringArray, + #[allow(dead_code)] + #[var(get = get_smt_enabled, set = set_smt_enabled)] + smt_enabled: bool, +} + +#[godot_api] +impl Cpu { + /// Create a new [Cpu] instance with the given DBus path + fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + let (tx, rx) = channel(); + + // Spawn a task to listen for CPU signals + let dbus_path = path.clone().into(); + RUNTIME.spawn(async move { + if let Err(e) = run(tx, dbus_path).await { + log::error!("Failed to run CPU task: ${e:?}"); + } + }); + + // Accept a base of type Base and directly forward it. + let mut instance = Self { + base, + conn, + path: path.clone().into(), + cores: HashMap::new(), + rx, + boost_enabled: Default::default(), + cores_count: Default::default(), + cores_enabled: Default::default(), + features: Default::default(), + smt_enabled: Default::default(), + }; + + // Discover any CPU cores + let mut cores = HashMap::new(); + if let Some(cpu) = instance.get_proxy() { + if let Ok(core_paths) = cpu.enumerate_cores() { + for core_path in core_paths { + let core = CpuCore::new(core_path.as_str()); + cores.insert(core_path.to_string(), core); + } + } + } + instance.cores = cores; + + instance + }) + } + + /// Return a proxy instance to the composite device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + CPUProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [DBusDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{POWERSTATION_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::trace!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = Cpu::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = Cpu::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the CPU instance + #[func] + pub fn get_dbus_path(&self) -> GString { + self.path.clone().into() + } + + /// Return all the CPU cores for the CPU + #[func] + pub fn get_cores(&self) -> Array> { + let mut cores = array![]; + for core in self.cores.values() { + cores.push(core); + } + + cores + } + + /// Returns whether or not the CPU has the given feature flag + #[func] + pub fn has_feature(&self, flag: GString) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy + .has_feature(flag.to_string().as_str()) + .unwrap_or(false) + } + + /// Returns whether or not boost is enabled + #[func] + pub fn get_boost_enabled(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.boost_enabled().unwrap_or_default() + } + + /// Sets boost to the given value + #[func] + pub fn set_boost_enabled(&self, enabled: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_boost_enabled(enabled).ok(); + } + + /// Returns the total number of detected CPU cores + #[func] + pub fn get_cores_count(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.cores_count().unwrap_or_default() + } + + /// Returns the number of enabled CPU cores + #[func] + pub fn get_cores_enabled(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.cores_enabled().unwrap_or_default() + } + + /// Set the number of enabled CPU cores. Cannot be less than 1. + #[func] + pub fn set_cores_enabled(&self, enabled_count: u32) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_cores_enabled(enabled_count).unwrap_or_default() + } + + /// Returns a list of supported CPU feature flags + #[func] + pub fn get_features(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let features = proxy.features().unwrap_or_default(); + let features: Vec = features.into_iter().map(|f| f.to_godot()).collect(); + features.into() + } + + /// Returns whether or not SMT is enabled + #[func] + pub fn get_smt_enabled(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.smt_enabled().unwrap_or_default() + } + + /// Set SMT to the given value + #[func] + pub fn set_smt_enabled(&self, enabled: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_smt_enabled(enabled).ok(); + } + + /// Dispatches signals + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + + // Process signals for any child cores + for (_, core) in self.cores.iter_mut() { + core.bind_mut().process(); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + log::trace!("Got signal: {signal:?}"); + match signal { + Signal::Updated => { + self.base_mut().emit_signal("updated", &[]); + } + } + } +} + +/// Runs CPU tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender, path: String) -> Result<(), RunError> { + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + let proxy = CPUProxy::builder(&conn).path(path)?.build().await?; + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_features_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_cores_count_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_smt_enabled_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_boost_enabled_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_cores_enabled_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/performance/powerstation/cpu_core.rs b/extensions/core/src/performance/powerstation/cpu_core.rs new file mode 100644 index 000000000..2de59816f --- /dev/null +++ b/extensions/core/src/performance/powerstation/cpu_core.rs @@ -0,0 +1,198 @@ +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use futures_util::StreamExt; +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{ + dbus::{ + powerstation::core::{CoreProxy, CoreProxyBlocking}, + RunError, + }, + get_dbus_system, get_dbus_system_blocking, RUNTIME, +}; + +use super::POWERSTATION_BUS; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Updated, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct CpuCore { + base: Base, + path: String, + conn: Option, + rx: Receiver, + + #[allow(dead_code)] + #[var(get = get_core_id)] + core_id: u32, + #[allow(dead_code)] + #[var(get = get_number)] + number: u32, + #[allow(dead_code)] + #[var(get = get_online, set = set_online)] + online: bool, +} + +#[godot_api] +impl CpuCore { + /// Create a new [EventDevice] with the given DBus path + fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + let (tx, rx) = channel(); + + // Spawn a task to listen for CPU signals + let dbus_path = path.clone().into(); + RUNTIME.spawn(async move { + if let Err(e) = run(tx, dbus_path).await { + log::error!("Failed to run CPU Core task: ${e:?}"); + } + }); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + path: path.clone().into(), + rx, + core_id: Default::default(), + number: Default::default(), + online: Default::default(), + } + }) + } + + /// Return a proxy instance to the composite device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + CoreProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [DBusDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{POWERSTATION_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::trace!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = CpuCore::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = CpuCore::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the CPU Core instance + #[func] + pub fn get_dbus_path(&self) -> GString { + self.path.clone().into() + } + + /// Return the core id of the CPU core + #[func] + pub fn get_core_id(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.core_id().unwrap_or_default() + } + + /// Return the core number of the CPU core + #[func] + pub fn get_number(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.number().unwrap_or_default() + } + + /// Return whether or not the CPU core is online + #[func] + pub fn get_online(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.online().unwrap_or_default() + } + + /// Set the online status of the core to the given value + #[func] + pub fn set_online(&self, online: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_online(online).unwrap_or_default() + } + + /// Dispatches signals + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + log::trace!("Got signal: {signal:?}"); + match signal { + Signal::Updated => { + self.base_mut().emit_signal("updated", &[]); + } + } + } +} + +/// Runs CPU tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender, path: String) -> Result<(), RunError> { + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + let proxy = CoreProxy::builder(&conn).path(path)?.build().await?; + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_online_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/performance/powerstation/gpu.rs b/extensions/core/src/performance/powerstation/gpu.rs new file mode 100644 index 000000000..a79ebbc24 --- /dev/null +++ b/extensions/core/src/performance/powerstation/gpu.rs @@ -0,0 +1,110 @@ +use std::collections::HashMap; + +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{dbus::powerstation::gpu::GPUProxyBlocking, get_dbus_system_blocking}; + +use super::{gpu_card::GpuCard, POWERSTATION_BUS}; + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct Gpu { + base: Base, + path: String, + conn: Option, + cards: HashMap>, +} + +#[godot_api] +impl Gpu { + /// Create a new [Cpu] instance with the given DBus path + fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + let mut instance = Self { + base, + conn, + path: path.clone().into(), + cards: HashMap::new(), + }; + + // Discover any GPU cards + let mut cards = HashMap::new(); + if let Some(gpu) = instance.get_proxy() { + if let Ok(card_paths) = gpu.enumerate_cards() { + for card_path in card_paths { + let core = GpuCard::new(card_path.as_str()); + cards.insert(card_path.to_string(), core); + } + } + } + instance.cards = cards; + + instance + }) + } + + /// Return a proxy instance to the composite device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + GPUProxyBlocking::builder(conn) + .path(self.path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [DBusDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{POWERSTATION_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::trace!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = Gpu::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = Gpu::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the CPU instance + #[func] + pub fn get_dbus_path(&self) -> GString { + self.path.clone().into() + } + + #[func] + pub fn get_cards(&self) -> Array> { + let mut cards = array![]; + for core in self.cards.values() { + cards.push(core); + } + + cards + } + + /// Dispatches signals + pub fn process(&mut self) { + // Process signals for any child cores + for (_, card) in self.cards.iter_mut() { + card.bind_mut().process(); + } + } +} diff --git a/extensions/core/src/performance/powerstation/gpu_card.rs b/extensions/core/src/performance/powerstation/gpu_card.rs new file mode 100644 index 000000000..25fc40c9d --- /dev/null +++ b/extensions/core/src/performance/powerstation/gpu_card.rs @@ -0,0 +1,633 @@ +use std::{ + collections::HashMap, + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, +}; + +use futures_util::StreamExt; +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{ + dbus::{ + powerstation::{ + card::{CardProxy, CardProxyBlocking}, + tdp::{TDPProxy, TDPProxyBlocking}, + }, + RunError, + }, + get_dbus_system, get_dbus_system_blocking, RUNTIME, +}; + +use super::{gpu_connector::GpuConnector, POWERSTATION_BUS}; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Updated, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct GpuCard { + base: Base, + dbus_path: String, + conn: Option, + rx: Receiver, + connectors: HashMap>, + + #[allow(dead_code)] + #[var(get = get_class)] + class: GString, + + #[allow(dead_code)] + #[var(get = get_class_id)] + class_id: GString, + + #[allow(dead_code)] + #[var(get = get_clock_limit_mhz_max)] + clock_limit_mhz_max: f64, + + #[allow(dead_code)] + #[var(get = get_clock_limit_mhz_min)] + clock_limit_mhz_min: f64, + + #[allow(dead_code)] + #[var(get = get_clock_value_mhz_max, set = set_clock_value_mhz_max)] + clock_value_mhz_max: f64, + + #[allow(dead_code)] + #[var(get = get_clock_value_mhz_min, set = set_clock_value_mhz_min)] + clock_value_mhz_min: f64, + + #[allow(dead_code)] + #[var(get = get_device)] + device: GString, + + #[allow(dead_code)] + #[var(get = get_device_id)] + device_id: GString, + + #[allow(dead_code)] + #[var(get = get_manual_clock, set = set_manual_clock)] + manual_clock: bool, + + #[allow(dead_code)] + #[var(get = get_name)] + name: GString, + + #[allow(dead_code)] + #[var(get = get_path)] + path: GString, + + #[allow(dead_code)] + #[var(get = get_revision_id)] + revision_id: GString, + + #[allow(dead_code)] + #[var(get = get_subdevice)] + subdevice: GString, + + #[allow(dead_code)] + #[var(get = get_subdevice_id)] + subdevice_id: GString, + + #[allow(dead_code)] + #[var(get = get_subvendor_id)] + subvendor_id: GString, + + #[allow(dead_code)] + #[var(get = get_vendor)] + vendor: GString, + + #[allow(dead_code)] + #[var(get = get_vendor_id)] + vendor_id: GString, + + #[allow(dead_code)] + #[var(get = get_boost, set = set_boost)] + boost: f64, + + #[allow(dead_code)] + #[var(get = get_power_profiles_available)] + power_profiles_available: PackedStringArray, + + #[allow(dead_code)] + #[var(get = get_power_profile, set = set_power_profile)] + power_profile: GString, + + #[allow(dead_code)] + #[var(get = get_tdp, set = set_tdp)] + tdp: f64, + + #[allow(dead_code)] + #[var(get = get_thermal_throttle_limit_c, set = set_thermal_throttle_limit_c)] + thermal_throttle_limit_c: f64, +} + +#[godot_api] +impl GpuCard { + /// Create a new [EventDevice] with the given DBus path + fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + let (tx, rx) = channel(); + + // Spawn a task to listen for GPU Card signals + let dbus_path = path.clone().into(); + RUNTIME.spawn(async move { + if let Err(e) = run(tx, dbus_path).await { + log::error!("Failed to run GPU Card task: ${e:?}"); + } + }); + + // Accept a base of type Base and directly forward it. + let mut instance = Self { + base, + conn, + dbus_path: path.clone().into(), + rx, + boost: Default::default(), + class: Default::default(), + class_id: Default::default(), + clock_limit_mhz_max: Default::default(), + clock_limit_mhz_min: Default::default(), + clock_value_mhz_max: Default::default(), + clock_value_mhz_min: Default::default(), + connectors: HashMap::new(), + device: Default::default(), + device_id: Default::default(), + manual_clock: Default::default(), + name: Default::default(), + path: Default::default(), + power_profile: Default::default(), + power_profiles_available: Default::default(), + revision_id: Default::default(), + subdevice: Default::default(), + subdevice_id: Default::default(), + subvendor_id: Default::default(), + tdp: Default::default(), + thermal_throttle_limit_c: Default::default(), + vendor: Default::default(), + vendor_id: Default::default(), + }; + + // Discover any connectors + let mut connectors = HashMap::new(); + if let Some(card) = instance.get_proxy() { + if let Ok(connector_paths) = card.enumerate_connectors() { + for conn_path in connector_paths { + let connector = GpuConnector::new(conn_path.as_str()); + connectors.insert(conn_path.to_string(), connector); + } + } + } + instance.connectors = connectors; + + instance + }) + } + + /// Return a proxy instance to the GPU card interface + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + CardProxyBlocking::builder(conn) + .path(self.dbus_path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Return a proxy instance to the TDP interface + fn get_tdp_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + TDPProxyBlocking::builder(conn) + .path(self.dbus_path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [DBusDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{POWERSTATION_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::trace!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = GpuCard::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = GpuCard::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the GPU connector instance + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone().into() + } + + /// Returns true if the card supports tdp + #[func] + pub fn supports_tdp(&self) -> bool { + let tdp = self.get_tdp(); + tdp > 1.0 + } + + /// Returns the connectors associated with this GPU card + #[func] + pub fn get_connectors(&self) -> Array> { + let mut connectors = array![]; + let Some(proxy) = self.get_proxy() else { + return connectors; + }; + let paths = proxy.enumerate_connectors().unwrap_or_default(); + for path in paths { + let connector = GpuConnector::new(path.as_str()); + connectors.push(&connector); + } + + connectors + } + + #[func] + pub fn get_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.name().unwrap_or_default().into() + } + + #[func] + pub fn get_path(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.path().unwrap_or_default().into() + } + + #[func] + pub fn get_thermal_throttle_limit_c(&self) -> f64 { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + proxy.thermal_throttle_limit_c().unwrap_or_default() + } + + #[func] + pub fn set_thermal_throttle_limit_c(&self, value: f64) { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + proxy + .set_thermal_throttle_limit_c(value) + .unwrap_or_default() + } + + #[func] + pub fn get_power_profiles_available(&self) -> PackedStringArray { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + let available = proxy.power_profiles_available().unwrap_or_default(); + let mut result = PackedStringArray::new(); + for profile in available.iter() { + let godot_profile = profile.to_godot(); + result.push(&godot_profile); + } + result + } + + #[func] + pub fn get_power_profile(&self) -> GString { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + proxy.power_profile().unwrap_or_default().into() + } + + #[func] + pub fn set_power_profile(&self, value: GString) { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + let value = value.to_string(); + proxy.set_power_profile(value.as_str()).unwrap_or_default() + } + + #[func] + pub fn get_boost(&self) -> f64 { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + proxy.boost().unwrap_or_default() + } + + #[func] + pub fn set_boost(&self, value: f64) { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + proxy.set_boost(value).unwrap_or_default() + } + + #[func] + pub fn get_manual_clock(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.manual_clock().unwrap_or_default() + } + + #[func] + pub fn set_manual_clock(&self, value: bool) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_manual_clock(value).unwrap_or_default() + } + + #[func] + pub fn get_clock_value_mhz_min(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.clock_value_mhz_min().unwrap_or_default() + } + + #[func] + pub fn set_clock_value_mhz_min(&self, value: f64) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_clock_value_mhz_min(value).unwrap_or_default() + } + + #[func] + pub fn get_clock_value_mhz_max(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.clock_value_mhz_max().unwrap_or_default() + } + + #[func] + pub fn set_clock_value_mhz_max(&self, value: f64) { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.set_clock_value_mhz_max(value).unwrap_or_default() + } + + #[func] + pub fn get_device_id(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.device_id().unwrap_or_default().into() + } + + #[func] + pub fn get_subdevice_id(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.subdevice_id().unwrap_or_default().into() + } + + #[func] + pub fn get_subvendor_id(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.subvendor_id().unwrap_or_default().into() + } + + #[func] + pub fn get_vendor(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.vendor().unwrap_or_default().into() + } + + #[func] + pub fn get_vendor_id(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.vendor_id().unwrap_or_default().into() + } + + #[func] + pub fn get_tdp(&self) -> f64 { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + proxy.tdp().unwrap_or_default() + } + + #[func] + pub fn set_tdp(&self, value: f64) { + let Some(proxy) = self.get_tdp_proxy() else { + return Default::default(); + }; + proxy.set_tdp(value).unwrap_or_default() + } + + #[func] + pub fn get_subdevice(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.subdevice().unwrap_or_default().into() + } + + #[func] + pub fn get_revision_id(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.revision_id().unwrap_or_default().into() + } + + #[func] + pub fn get_device(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.device().unwrap_or_default().into() + } + + #[func] + pub fn get_clock_limit_mhz_min(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.clock_limit_mhz_min().unwrap_or_default() + } + + #[func] + pub fn get_clock_limit_mhz_max(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.clock_limit_mhz_max().unwrap_or_default() + } + + #[func] + pub fn get_class_id(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.class_id().unwrap_or_default().into() + } + + #[func] + pub fn get_class(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.class().unwrap_or_default().into() + } + + /// Dispatches signals + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + + // Process child connector signals + for connector in self.connectors.values_mut() { + connector.bind_mut().process(); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + log::trace!("Got signal: {signal:?}"); + match signal { + Signal::Updated => { + self.base_mut().emit_signal("updated", &[]); + } + } + } +} + +/// Runs GPU connector tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender, path: String) -> Result<(), RunError> { + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + let proxy = CardProxy::builder(&conn) + .path(path.clone())? + .build() + .await?; + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_clock_value_mhz_min_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_clock_value_mhz_max_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_manual_clock_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let tdp_proxy = TDPProxy::builder(&conn).path(path)?.build().await?; + + let signals_tx = tx.clone(); + let mut property_changed = tdp_proxy.receive_tdp_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = tdp_proxy.receive_boost_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = tdp_proxy.receive_power_profile_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = tdp_proxy.receive_thermal_throttle_limit_c_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/performance/powerstation/gpu_connector.rs b/extensions/core/src/performance/powerstation/gpu_connector.rs new file mode 100644 index 000000000..043b02f37 --- /dev/null +++ b/extensions/core/src/performance/powerstation/gpu_connector.rs @@ -0,0 +1,258 @@ +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use futures_util::StreamExt; +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{ + dbus::{ + powerstation::connector::{ConnectorProxy, ConnectorProxyBlocking}, + RunError, + }, + get_dbus_system, get_dbus_system_blocking, RUNTIME, +}; + +use super::POWERSTATION_BUS; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Updated, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct GpuConnector { + base: Base, + dbus_path: String, + conn: Option, + rx: Receiver, + + #[allow(dead_code)] + #[var(get = get_dpms)] + dpms: bool, + #[allow(dead_code)] + #[var(get = get_enabled)] + enabled: bool, + #[allow(dead_code)] + #[var(get = get_id)] + id: u32, + #[allow(dead_code)] + #[var(get = get_modes)] + modes: PackedStringArray, + #[allow(dead_code)] + #[var(get = get_name)] + name: GString, + #[allow(dead_code)] + #[var(get = get_path)] + path: GString, + #[allow(dead_code)] + #[var(get = get_status)] + status: GString, +} + +#[godot_api] +impl GpuConnector { + /// Create a new [EventDevice] with the given DBus path + fn from_path(path: GString) -> Gd { + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + let (tx, rx) = channel(); + + // Spawn a task to listen for CPU signals + let dbus_path = path.clone().into(); + RUNTIME.spawn(async move { + if let Err(e) = run(tx, dbus_path).await { + log::error!("Failed to run CPU Core task: ${e:?}"); + } + }); + + // Accept a base of type Base and directly forward it. + Self { + base, + conn, + dbus_path: path.clone().into(), + rx, + dpms: Default::default(), + path: Default::default(), + enabled: Default::default(), + id: Default::default(), + modes: Default::default(), + name: Default::default(), + status: Default::default(), + } + }) + } + + /// Return a proxy instance to the composite device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + ConnectorProxyBlocking::builder(conn) + .path(self.dbus_path.clone()) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [DBusDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{POWERSTATION_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::trace!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = GpuConnector::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = GpuConnector::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + /// Return the DBus path to the GPU connector instance + #[func] + pub fn get_dbus_path(&self) -> GString { + self.dbus_path.clone().into() + } + + #[func] + pub fn get_dpms(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.dpms().unwrap_or_default() + } + + #[func] + pub fn get_enabled(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.enabled().unwrap_or_default() + } + + #[func] + pub fn get_id(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.id().unwrap_or_default() + } + + #[func] + pub fn get_modes(&self) -> PackedStringArray { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + let modes = proxy.modes().unwrap_or_default(); + let modes: Vec = modes.into_iter().map(|m| m.to_godot()).collect(); + modes.into() + } + + #[func] + pub fn get_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.name().unwrap_or_default().into() + } + + #[func] + pub fn get_path(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.path().unwrap_or_default().into() + } + + #[func] + pub fn get_status(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.status().unwrap_or_default().into() + } + + /// Dispatches signals + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + log::trace!("Got signal: {signal:?}"); + match signal { + Signal::Updated => { + self.base_mut().emit_signal("updated", &[]); + } + } + } +} + +/// Runs GPU connector tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender, path: String) -> Result<(), RunError> { + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + let proxy = ConnectorProxy::builder(&conn).path(path)?.build().await?; + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_modes_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_status_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut property_changed = proxy.receive_enabled_changed().await; + RUNTIME.spawn(async move { + while (property_changed.next().await).is_some() { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/power.rs b/extensions/core/src/power.rs new file mode 100644 index 000000000..18cf20513 --- /dev/null +++ b/extensions/core/src/power.rs @@ -0,0 +1,2 @@ +pub mod device; +pub mod upower; diff --git a/extensions/core/src/power/device.rs b/extensions/core/src/power/device.rs new file mode 100644 index 000000000..b4f0f70fd --- /dev/null +++ b/extensions/core/src/power/device.rs @@ -0,0 +1,668 @@ +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; + +use futures_util::StreamExt; +use godot::{classes::ResourceLoader, prelude::*}; + +use crate::{ + dbus::{ + upower::device::{DeviceProxy, DeviceProxyBlocking}, + RunError, + }, + get_dbus_system, get_dbus_system_blocking, RUNTIME, +}; + +use super::upower::UPOWER_BUS; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Updated, +} + +#[derive(GodotClass)] +#[class(no_init, base=Resource)] +pub struct UPowerDevice { + base: Base, + rx: Receiver, + conn: Option, + #[var] + dbus_path: GString, + #[allow(dead_code)] + #[var(get = get_battery_level)] + battery_level: u32, + #[allow(dead_code)] + #[var(get = get_charge_cycles)] + charge_cycles: i32, + #[allow(dead_code)] + #[var(get = get_energy)] + energy: f64, + #[allow(dead_code)] + #[var(get = get_energy_empty)] + energy_empty: f64, + #[allow(dead_code)] + #[var(get = get_energy_full)] + energy_full: f64, + #[allow(dead_code)] + #[var(get = get_energy_full_design)] + energy_full_design: f64, + #[allow(dead_code)] + #[var(get = get_energy_rate)] + energy_rate: f64, + #[allow(dead_code)] + #[var(get = get_has_history)] + has_history: bool, + #[allow(dead_code)] + #[var(get = get_has_statistics)] + has_statistics: bool, + #[allow(dead_code)] + #[var(get = get_icon_name)] + icon_name: GString, + #[allow(dead_code)] + #[var(get = get_is_present)] + is_present: bool, + #[allow(dead_code)] + #[var(get = get_is_rechargeable)] + is_rechargeable: bool, + #[allow(dead_code)] + #[var(get = get_luminosity)] + luminosity: f64, + #[allow(dead_code)] + #[var(get = get_model)] + model: GString, + #[allow(dead_code)] + #[var(get = get_native_path)] + native_path: GString, + #[allow(dead_code)] + #[var(get = get_online)] + online: bool, + #[allow(dead_code)] + #[var(get = get_percentage)] + percentage: f64, + #[allow(dead_code)] + #[var(get = get_power_supply)] + power_supply: bool, + #[allow(dead_code)] + #[var(get = get_serial)] + serial: GString, + #[allow(dead_code)] + #[var(get = get_state)] + state: u32, + #[allow(dead_code)] + #[var(get = get_technology)] + technology: u32, + #[allow(dead_code)] + #[var(get = get_temperature)] + temperature: f64, + #[allow(dead_code)] + #[var(get = get_time_to_empty)] + time_to_empty: i64, + #[allow(dead_code)] + #[var(get = get_time_to_full)] + time_to_full: i64, + #[allow(dead_code)] + #[var(get = get_type)] + type_: u32, + #[allow(dead_code)] + #[var(get = get_update_time)] + update_time: i64, + #[allow(dead_code)] + #[var(get = get_vendor)] + vendor: GString, + #[allow(dead_code)] + #[var(get = get_voltage)] + voltage: f64, + #[allow(dead_code)] + #[var(get = get_warning_level)] + warning_level: u32, +} + +#[godot_api] +impl UPowerDevice { + #[constant] + const TYPE_UNKNOWN: i32 = 0; + #[constant] + const TYPE_LINE_POWER: i32 = 1; + #[constant] + const TYPE_BATTERY: i32 = 2; + #[constant] + const TYPE_UPS: i32 = 3; + #[constant] + const TYPE_MONITOR: i32 = 4; + #[constant] + const TYPE_MOUSE: i32 = 5; + #[constant] + const TYPE_KEYBOARD: i32 = 6; + #[constant] + const TYPE_PDA: i32 = 7; + #[constant] + const TYPE_PHONE: i32 = 8; + #[constant] + const TYPE_MEDIA_PLAYER: i32 = 9; + #[constant] + const TYPE_TABLET: i32 = 10; + #[constant] + const TYPE_COMPUTER: i32 = 11; + #[constant] + const TYPE_GAMING_INPUT: i32 = 12; + #[constant] + const TYPE_PEN: i32 = 13; + #[constant] + const TYPE_TOUCHPAD: i32 = 14; + #[constant] + const TYPE_MODEM: i32 = 15; + #[constant] + const TYPE_NETWORK: i32 = 16; + #[constant] + const TYPE_HEADSET: i32 = 17; + #[constant] + const TYPE_SPEAKERS: i32 = 18; + #[constant] + const TYPE_HEADPHONES: i32 = 19; + #[constant] + const TYPE_VIDEO: i32 = 20; + #[constant] + const TYPE_OTHER_AUDIO: i32 = 21; + #[constant] + const TYPE_REMOTE_CONTROL: i32 = 22; + #[constant] + const TYPE_PRINTER: i32 = 23; + #[constant] + const TYPE_SCANNER: i32 = 24; + #[constant] + const TYPE_CAMERA: i32 = 25; + #[constant] + const TYPE_WEARABLE: i32 = 26; + #[constant] + const TYPE_TOY: i32 = 27; + #[constant] + const TYPE_BLUETOOTH_GENREIC: i32 = 28; + + #[constant] + const STATE_UNKNOWN: i32 = 0; + #[constant] + const STATE_CHARGING: i32 = 1; + #[constant] + const STATE_DISCHARGING: i32 = 2; + #[constant] + const STATE_EMPTY: i32 = 3; + #[constant] + const STATE_FULLY_CHARGED: i32 = 4; + #[constant] + const STATE_PENDING_CHARGE: i32 = 5; + #[constant] + const STATE_PENDING_DISCHARGE: i32 = 6; + + #[constant] + const TECHNOLOGY_UNKNOWN: i32 = 0; + #[constant] + const TECHNOLOGY_LITHIUM_ION: i32 = 1; + #[constant] + const TECHNOLOGY_LITHIUM_POLYMER: i32 = 2; + #[constant] + const TECHNOLOGY_LITHIUM_IRON_PHOSPHATE: i32 = 3; + #[constant] + const TECHNOLOGY_LEAD_ACID: i32 = 4; + #[constant] + const TECHNOLOGY_NICKEL_CADMIUM: i32 = 5; + #[constant] + const TECHNOLOGY_NICKEL_METAL_HYDRIDE: i32 = 6; + + #[constant] + const WARNING_LEVEL_UNKNOWN: i32 = 0; + #[constant] + const WARNING_LEVEL_NONE: i32 = 1; + #[constant] + const WARNING_LEVEL_DISCHARGING: i32 = 2; + #[constant] + const WARNING_LEVEL_LOW: i32 = 3; + #[constant] + const WARNING_LEVEL_CRITICAL: i32 = 4; + #[constant] + const WARNING_LEVEL_ACTION: i32 = 5; + + #[constant] + const BATTERY_LEVEL_UNKNOWN: i32 = 0; + #[constant] + const BATTERY_LEVEL_NONE: i32 = 1; + #[constant] + const BATTERY_LEVEL_LOW: i32 = 3; + #[constant] + const BATTERY_LEVEL_CRITICAL: i32 = 4; + #[constant] + const BATTERY_LEVEL_NORMAL: i32 = 6; + #[constant] + const BATTERY_LEVEL_HIGH: i32 = 7; + #[constant] + const BATTERY_LEVEL_FULL: i32 = 8; + + #[signal] + fn updated(); + + /// Create a new [UPowerDevice] with the given DBus path + pub fn from_path(path: GString) -> Gd { + // Create a channel to communicate with the signals task + let (tx, rx) = channel(); + let dbus_path = path.clone().into(); + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx, dbus_path).await { + log::error!("Failed to run UPowerDevice task: ${e:?}"); + } + }); + + Gd::from_init_fn(|base| { + // Create a connection to DBus + let conn = get_dbus_system_blocking().ok(); + + // Accept a base of type Base and directly forward it. + Self { + base, + rx, + conn, + dbus_path: path, + battery_level: Default::default(), + charge_cycles: Default::default(), + energy: Default::default(), + energy_empty: Default::default(), + energy_full: Default::default(), + energy_full_design: Default::default(), + energy_rate: Default::default(), + has_history: Default::default(), + has_statistics: Default::default(), + icon_name: Default::default(), + is_present: Default::default(), + is_rechargeable: Default::default(), + luminosity: Default::default(), + model: Default::default(), + native_path: Default::default(), + online: Default::default(), + percentage: Default::default(), + power_supply: Default::default(), + serial: Default::default(), + state: Default::default(), + technology: Default::default(), + temperature: Default::default(), + time_to_empty: Default::default(), + time_to_full: Default::default(), + type_: Default::default(), + update_time: Default::default(), + vendor: Default::default(), + voltage: Default::default(), + warning_level: Default::default(), + } + }) + } + + /// Return a proxy instance to the device + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + let path: String = self.dbus_path.clone().into(); + DeviceProxyBlocking::builder(conn) + .path(path) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } + + /// Get or create a [UPowerDevice] with the given DBus path. If an instance + /// already exists with the given path, then it will be loaded from the resource + /// cache. + pub fn new(path: &str) -> Gd { + let res_path = format!("dbus://{UPOWER_BUS}{path}"); + + // Check to see if a resource already exists for this device + let mut resource_loader = ResourceLoader::singleton(); + if resource_loader.exists(res_path.as_str()) { + if let Some(res) = resource_loader.load(res_path.as_str()) { + log::debug!("Resource already exists, loading that instead"); + let device: Gd = res.cast(); + device + } else { + let mut device = UPowerDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } else { + let mut device = UPowerDevice::from_path(path.to_string().into()); + device.take_over_path(res_path.as_str()); + device + } + } + + #[func] + pub fn get_battery_level(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.battery_level().ok().unwrap_or_default() + } + + #[func] + pub fn get_charge_cycles(&self) -> i32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.charge_cycles().ok().unwrap_or_default() + } + + #[func] + pub fn get_energy(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.energy().ok().unwrap_or_default() + } + + #[func] + pub fn get_energy_empty(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.energy_empty().ok().unwrap_or_default() + } + + #[func] + pub fn get_energy_full(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.energy_full().ok().unwrap_or_default() + } + + #[func] + pub fn get_energy_full_design(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.energy_full_design().ok().unwrap_or_default() + } + + #[func] + pub fn get_energy_rate(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.energy_rate().ok().unwrap_or_default() + } + + #[func] + pub fn get_has_history(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.has_history().ok().unwrap_or_default() + } + + #[func] + pub fn get_has_statistics(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.has_statistics().ok().unwrap_or_default() + } + + #[func] + pub fn get_icon_name(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.icon_name().ok().unwrap_or_default().into() + } + + #[func] + pub fn get_is_present(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.is_present().ok().unwrap_or_default() + } + + #[func] + pub fn get_is_rechargeable(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.is_rechargeable().ok().unwrap_or_default() + } + + #[func] + pub fn get_luminosity(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.luminosity().ok().unwrap_or_default() + } + + #[func] + pub fn get_model(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.model().ok().unwrap_or_default().into() + } + + #[func] + pub fn get_native_path(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.native_path().ok().unwrap_or_default().into() + } + + #[func] + pub fn get_online(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.online().ok().unwrap_or_default() + } + + #[func] + pub fn get_percentage(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.percentage().ok().unwrap_or_default() + } + + #[func] + pub fn get_power_supply(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.power_supply().ok().unwrap_or_default() + } + + #[func] + pub fn get_serial(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.serial().ok().unwrap_or_default().into() + } + + #[func] + pub fn get_state(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.state().ok().unwrap_or_default() + } + + #[func] + pub fn get_technology(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.technology().ok().unwrap_or_default() + } + + #[func] + pub fn get_temperature(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.temperature().ok().unwrap_or_default() + } + + #[func] + pub fn get_time_to_empty(&self) -> i64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.time_to_empty().ok().unwrap_or_default() + } + + #[func] + pub fn get_time_to_full(&self) -> i64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.time_to_full().ok().unwrap_or_default() + } + + #[func] + pub fn get_type(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.type_().ok().unwrap_or_default() + } + + #[func] + pub fn get_update_time(&self) -> i64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.update_time().ok().unwrap_or_default() as i64 + } + + #[func] + pub fn get_vendor(&self) -> GString { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.vendor().ok().unwrap_or_default().into() + } + + #[func] + pub fn get_voltage(&self) -> f64 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.voltage().ok().unwrap_or_default() + } + + #[func] + pub fn get_warning_level(&self) -> u32 { + let Some(proxy) = self.get_proxy() else { + return Default::default(); + }; + proxy.warning_level().ok().unwrap_or_default() + } + + /// Dispatches signals + pub fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Updated => { + self.base_mut().emit_signal("updated", &[]); + } + } + } +} + +/// Run the signals task +async fn run(tx: Sender, path: String) -> Result<(), RunError> { + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + let proxy = DeviceProxy::builder(&conn).path(path)?.build().await?; + + let signals_tx = tx.clone(); + let mut events = proxy.receive_percentage_changed().await; + RUNTIME.spawn(async move { + while let Some(_event) = events.next().await { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_icon_name_changed().await; + RUNTIME.spawn(async move { + while let Some(_event) = events.next().await { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_state_changed().await; + RUNTIME.spawn(async move { + while let Some(_event) = events.next().await { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_time_to_full_changed().await; + RUNTIME.spawn(async move { + while let Some(_event) = events.next().await { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_time_to_empty_changed().await; + RUNTIME.spawn(async move { + while let Some(_event) = events.next().await { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + let signals_tx = tx.clone(); + let mut events = proxy.receive_battery_level_changed().await; + RUNTIME.spawn(async move { + while let Some(_event) = events.next().await { + let signal = Signal::Updated; + if signals_tx.send(signal).is_err() { + break; + } + } + }); + + Ok(()) +} diff --git a/extensions/core/src/power/upower.rs b/extensions/core/src/power/upower.rs new file mode 100644 index 000000000..85c940909 --- /dev/null +++ b/extensions/core/src/power/upower.rs @@ -0,0 +1,217 @@ +use std::{ + collections::HashMap, + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, + time::Duration, +}; + +use godot::{classes::Engine, prelude::*}; +use zbus::names::BusName; + +use crate::{ + dbus::{upower::UPowerProxyBlocking, RunError}, + get_dbus_system, get_dbus_system_blocking, RUNTIME, +}; + +use super::device::UPowerDevice; + +pub const UPOWER_BUS: &str = "org.freedesktop.UPower"; +const UPOWER_PATH: &str = "/org/freedesktop/UPower"; +const DISPLAY_DEVICE_PATH: &str = "/org/freedesktop/UPower/devices/DisplayDevice"; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Started, + Stopped, +} + +/// UPower dbus proxy for power management +#[derive(GodotClass)] +#[class(base=Resource)] +pub struct UPowerInstance { + base: Base, + rx: Receiver, + conn: Option, + devices: HashMap>, + #[allow(dead_code)] + #[var(get = get_on_battery)] + on_battery: bool, +} + +#[godot_api] +impl UPowerInstance { + /// Emitted when UPower is detected as running + #[signal] + fn started(); + + /// Emitted when UPower is detected as stopped + #[signal] + fn stopped(); + + /// Returns true if the UPower service is currently running + #[func] + fn is_running(&self) -> bool { + let Some(conn) = self.conn.as_ref() else { + return false; + }; + let bus = BusName::from_static_str(UPOWER_BUS).unwrap(); + let dbus = zbus::blocking::fdo::DBusProxy::new(conn).ok(); + let Some(dbus) = dbus else { + return false; + }; + dbus.name_has_owner(bus.clone()).unwrap_or_default() + } + + /// Returns whether or not the device is running on battery power + #[func] + pub fn get_on_battery(&self) -> bool { + let Some(proxy) = self.get_proxy() else { + return false; + }; + proxy.on_battery().ok().unwrap_or_default() + } + + /// Get the object to the "display device", a composite device that represents the status icon to show in desktop environments. + #[func] + pub fn get_display_device(&mut self) -> Gd { + if let Some(device) = self.devices.get(DISPLAY_DEVICE_PATH) { + return device.clone(); + } + let device = UPowerDevice::new(DISPLAY_DEVICE_PATH); + self.devices + .insert(DISPLAY_DEVICE_PATH.to_string(), device.clone()); + device + } + + /// Process UPower signals and emit them as Godot signals. This method should be called every frame in the `_process` loop of a node. + #[func] + fn process(&mut self) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + + // Process signals for other known DBus objects + for (_, device) in self.devices.iter_mut() { + device.bind_mut().process(); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Started => { + self.base_mut().emit_signal("started", &[]); + } + Signal::Stopped => { + self.base_mut().emit_signal("stopped", &[]); + } + } + } + + /// Return a proxy instance to the UPower object + fn get_proxy(&self) -> Option { + if let Some(conn) = self.conn.as_ref() { + UPowerProxyBlocking::builder(conn) + .path(UPOWER_PATH) + .ok() + .and_then(|builder| builder.build().ok()) + } else { + None + } + } +} + +#[godot_api] +impl IResource for UPowerInstance { + /// Called upon object initialization in the engine + fn init(base: Base) -> Self { + log::debug!("Initializing UPower instance"); + + // Create a channel to communicate with the service + let (tx, rx) = channel(); + let conn = get_dbus_system_blocking().ok(); + + // Don't run in the editor + let engine = Engine::singleton(); + if engine.is_editor_hint() { + return Self { + base, + rx, + conn, + devices: Default::default(), + on_battery: Default::default(), + }; + } + + // Spawn a task using the shared tokio runtime to listen for signals + RUNTIME.spawn(async move { + if let Err(e) = run(tx).await { + log::error!("Failed to run UPower task: ${e:?}"); + } + }); + + // Create a new UPower instance + Self { + base, + rx, + conn, + devices: HashMap::new(), + on_battery: false, + } + } +} + +/// Runs UPower tasks in Tokio to listen for DBus signals and send them +/// over the given channel so they can be processed during each engine frame. +async fn run(tx: Sender) -> Result<(), RunError> { + log::debug!("Spawning UPower tasks"); + // Establish a connection to the system bus + let conn = get_dbus_system().await?; + + // Spawn a task to listen for UPower start/stop + let dbus_conn = conn.clone(); + let signals_tx = tx.clone(); + RUNTIME.spawn(async move { + let bus = BusName::from_static_str(UPOWER_BUS).unwrap(); + let mut is_running = { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + return; + }; + dbus.name_has_owner(bus.clone()).await.unwrap_or_default() + }; + + loop { + let dbus = zbus::fdo::DBusProxy::new(&dbus_conn).await.ok(); + let Some(dbus) = dbus else { + break; + }; + let running = dbus.name_has_owner(bus.clone()).await.unwrap_or_default(); + if running != is_running { + let signal = if running { + Signal::Started + } else { + Signal::Stopped + }; + if signals_tx.send(signal).is_err() { + break; + } + } + is_running = running; + tokio::time::sleep(Duration::from_secs(5)).await; + } + }); + + Ok(()) +} diff --git a/extensions/core/src/resource.rs b/extensions/core/src/resource.rs new file mode 100644 index 000000000..737ae3d28 --- /dev/null +++ b/extensions/core/src/resource.rs @@ -0,0 +1,2 @@ +pub mod resource_processor; +pub mod resource_registry; diff --git a/extensions/core/src/resource/resource_processor.rs b/extensions/core/src/resource/resource_processor.rs new file mode 100644 index 000000000..a378f18b5 --- /dev/null +++ b/extensions/core/src/resource/resource_processor.rs @@ -0,0 +1,42 @@ +use godot::{obj::WithBaseField, prelude::*}; + +use super::resource_registry::ResourceRegistry; + +/// Helper node to allow [Resource] objects to run during the process loop. +/// +/// The [ResourceProcessor] allows Godot [Resource] objects to run a [method process] function every frame. By design, Godot [Resource] objects do not have access to the scene tree and must be "invited" in by a [Node] in the scene. This node serves as that entrypoint, and should be added to the scene tree to execute [method process] on any [Resource] objects registered with a [ResourceRegistry]. +/// +/// Resources must register with the [ResourceRegistry] using [method ResourceRegistry.register] associated with this [ResourceProcessor] in order to be processed from the scene tree. +#[derive(GodotClass)] +#[class(init, base=Node)] +pub struct ResourceProcessor { + base: Base, + #[export] + registry: Gd, + initialized: bool, +} + +#[godot_api] +impl INode for ResourceProcessor { + fn process(&mut self, delta: f64) { + if !self.initialized { + // Add any child nodes from the registry + let children = self.registry.bind().get_children(); + for child in children.iter_shared() { + self.base_mut().add_child(&child); + } + + // Add any future children that get added to the registry + let ptr = self.to_gd(); + let method = Callable::from_object_method(&ptr, "add_child"); + self.registry.connect("child_added", &method); + + // Remove any children that get removed from the registry + let method = Callable::from_object_method(&ptr, "remove_child"); + self.registry.connect("child_removed", &method); + + self.initialized = true; + } + self.registry.bind_mut().process(delta); + } +} diff --git a/extensions/core/src/resource/resource_registry.rs b/extensions/core/src/resource/resource_registry.rs new file mode 100644 index 000000000..6fd4abf58 --- /dev/null +++ b/extensions/core/src/resource/resource_registry.rs @@ -0,0 +1,102 @@ +use godot::{classes::ResourceLoader, prelude::*}; + +/// Path to the main [ResourceRegistry] instance +const RESOURCE_REGISTRY: &str = "res://core/systems/resource/resource_registry.tres"; + +/// Class for registering [Resource] objects with a [method process] method that will get executed every frame by a [ResourceProcessor]. +/// +/// By design, [Resource] objects do not have access to the scene tree in order to be updated every frame during the [method process] loop. The [ResourceRegistry] provides a way for [Resource] objects to register themselves to have their [method process] method called every frame by a [ResourceProcessor] node. +/// +/// By saving the [ResourceRegistry] as a `.tres` file, [Resource] objects anywhere in the project can load the same [ResourceRegistry] instance and register themselves to run their [method process] method every frame by a [ResourceProcessor] node in the scene tree. +/// +/// Example +/// +/// [codeblock] +/// var registry := load("res://path/to/registry.tres") as ResourceRegistry +/// registry.register(self) +/// [/codeblock] +#[derive(GodotClass)] +#[class(init, base=Resource)] +pub struct ResourceRegistry { + base: Base, + resources: Array>, + child_nodes: Array>, +} + +#[godot_api] +impl ResourceRegistry { + #[signal] + fn child_added(child: Gd); + #[signal] + fn child_removed(child: Gd); + + /// Gets the main [ResourceRegistry] instance + #[func] + pub fn get_registry() -> Option> { + let mut resource_loader = ResourceLoader::singleton(); + let resource = resource_loader.load(RESOURCE_REGISTRY)?; + let registry: Gd = resource.cast(); + Some(registry) + } + + /// Register the given resource with the registry. The given resource will have its [method process] method called every frame by a [ResourceProcessor] in the scene tree. + #[func] + pub fn register(&mut self, resource: Gd) { + log::trace!("Registering resource: {resource}"); + if !resource.has_method("process") { + log::error!( + "Tried to register resource for processing, but resource has no process method: {resource}" + ); + return; + } + if self.resources.contains(&resource) { + log::trace!("Resource already registered: {resource}"); + return; + } + self.resources.push(&resource); + log::trace!("Registered resources: {}", self.resources); + } + + /// Unregister the given resource from the registry. + #[func] + pub fn unregister(&mut self, resource: Gd) { + log::trace!("Unregistering resource: {resource}"); + if !self.resources.contains(&resource) { + log::warn!("Resource is not registered: {resource}"); + return; + } + self.resources.erase(&resource); + log::trace!("Registered resources: {}", self.resources); + } + + /// Calls the `process()` method on all registered [Resource] objects. This should be called from a [Node] in the scene tree like the [ResourceProcessor]. + #[func] + pub fn process(&mut self, delta: f64) { + // Call process on each registered resource + for mut resource in self.resources.iter_shared() { + resource.call("process", &[delta.to_variant()]); + } + } + + /// Adds the given node to the [ResourceProcessor] node associated with this registry. This provides a way for resources to add nodes into the scene tree. + #[func] + pub fn add_child(&mut self, child: Gd) { + self.child_nodes.push(&child); + self.base_mut() + .emit_signal("child_added", &[child.to_variant()]); + } + + /// Removes the given node from the scene tree + #[func] + pub fn remove_child(&mut self, child: Gd) { + self.child_nodes.erase(&child); + self.base_mut() + .emit_signal("child_removed", &[child.to_variant()]); + } + + /// Returns a list of all nodes that should be added as children to a [ResourceProcessor] + #[func] + pub fn get_children(&self) -> Array> { + self.child_nodes.clone() + } +} diff --git a/extensions/core/src/system.rs b/extensions/core/src/system.rs new file mode 100644 index 000000000..e3beddbb3 --- /dev/null +++ b/extensions/core/src/system.rs @@ -0,0 +1,3 @@ +pub mod command; +pub mod pty; +pub mod subreaper; diff --git a/extensions/core/src/system/command.rs b/extensions/core/src/system/command.rs new file mode 100644 index 000000000..08f7b7418 --- /dev/null +++ b/extensions/core/src/system/command.rs @@ -0,0 +1,290 @@ +use std::{ + sync::mpsc::{channel, Receiver, Sender}, + time::{Duration, Instant}, +}; + +use godot::{obj::WithBaseField, prelude::*}; + +use crate::{resource::resource_registry::ResourceRegistry, RUNTIME}; + +/// Signals that can be emitted by this class +pub enum Signal { + Finished { + stdout: String, + stderr: String, + code: i32, + }, +} + +/// Class for executing OS commands asyncronously or syncronously. +/// +/// The [method execute] method will start executing the given command asyncronously and will fire the [signal finished] signal when the command has completed. The [member stdout], [member stderr], and [member code] will be populated with the commands output and exit code. The [member timeout] property can also be set to abort the running command after a certain amount of time. +/// +/// The [method execute_blocking] will execute the given command syncronously, blocking the main thread until the command has completed. +/// +/// When using the asyncronous method, the [ResourceProcessor] node [b]must[/b] be added to the scene tree or the [signal finished] signal will never fire. +#[derive(GodotClass)] +#[class(init, base=RefCounted)] +pub struct Command { + base: Base, + /// Receiver to listen for signals emitted from the async runtime + rx: Option>, + /// Transmitter for sending cancellation messages to the running command + cancel_tx: Option>, + /// Time when the command started executing + start_time: Option, + + /// Command to execute + #[var] + #[init(val = Default::default())] + command: GString, + /// Command arguments to execute + #[var] + #[init(val = Default::default())] + args: Array, + /// Standard output of the command after the command has completed. + #[var] + #[init(val = Default::default())] + stdout: GString, + /// Standard error output of the command after the command has completed. + #[var] + #[init(val = Default::default())] + stderr: GString, + /// The exit code of the command after executing + #[var] + #[init(val = Default::default())] + code: i32, + /// Optional timeout in seconds for the command to run when executing asyncronously. Zero indicates no timeout. + #[var] + #[init(val = Default::default())] + timeout: f64, +} + +#[godot_api] +impl Command { + /// Exit code for cancelled commands + #[constant] + const EXIT_CODE_CANCEL: i32 = 130; + + /// Emitted when the command has finished executing + #[signal] + fn finished(exit_code: i32); + + /// Creates a new [Command] with the given command and arguments + #[func] + fn create(command: GString, args: Array) -> Gd { + Gd::from_init_fn(|base| Self { + base, + rx: None, + cancel_tx: None, + start_time: None, + command, + args, + stdout: Default::default(), + stderr: Default::default(), + code: Default::default(), + timeout: Default::default(), + }) + } + + /// Cancels the executing command, sending a kill signal to the running process. + #[func] + pub fn cancel(&mut self) { + let Some(cancel) = self.cancel_tx.take() else { + return; + }; + if let Err(e) = cancel.blocking_send(()) { + log::warn!("Failed to send cancellation signal: {e:?}"); + } + } + + /// Process signals and emit them as Godot signals. + #[func] + pub fn process(&mut self, _delta: f64) { + // If a timeout was specified, check to see if the command should be + // cancelled. + if self.timeout > 0.0 { + if let Some(start_time) = self.start_time { + let elapsed = start_time.elapsed(); + let timeout = Duration::from_secs_f64(self.timeout); + if elapsed > timeout { + log::debug!( + "Timed out waiting for command: {} {:?}", + self.command, + self.args + ); + self.cancel(); + } + } + } + + // Get the signal receiver + let Some(rx) = self.rx.as_ref() else { + return; + }; + + // Drain all messages from the channel to process them + let mut signals = Vec::with_capacity(1); + loop { + let Ok(signal) = rx.try_recv() else { + break; + }; + signals.push(signal); + } + + for signal in signals { + self.process_signal(signal); + } + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Finished { + stdout, + stderr, + code, + } => { + self.stdout = stdout.to_godot(); + self.stderr = stderr.to_godot(); + self.code = code; + self.rx = None; + self.cancel_tx = None; + + // Schedule unregistering this object from the [ResourceProcessor] + if let Some(registry) = ResourceRegistry::get_registry().as_mut() { + let this: Gd = self.to_gd().upcast(); + registry.call_deferred("unregister", &[this.to_variant()]); + } + + self.base_mut() + .emit_signal("finished", &[code.to_variant()]); + } + } + } + + /// Execute the command asyncronously. Will fire the [signal finished] signal when the command has completed. Will return an error code if command is already executing. + #[func] + pub fn execute(&mut self) -> i32 { + if self.rx.is_some() { + log::error!("Command execution already in progress"); + return -1; + } + + // Convert the args + let cmd = self.command.to_string(); + let args: Vec = self.args.iter_shared().map(|v| v.to_string()).collect(); + + // Create a communication channel + let (tx, rx) = channel(); + self.rx = Some(rx); + + // Create a second channel for sending cancel signals to the executing program + let (cancel_tx, cancel_rx) = tokio::sync::mpsc::channel(1); + self.cancel_tx = Some(cancel_tx); + + // Spawn a task to run the command + RUNTIME.spawn(async move { + Command::run(cmd, args, tx, cancel_rx).await; + }); + + // Set the start time of the command + self.start_time = Some(Instant::now()); + + // Add to [ResourceProcessor] + if let Some(registry) = ResourceRegistry::get_registry().as_mut() { + let this: Gd = self.to_gd().upcast(); + registry.call_deferred("register", &[this.to_variant()]); + } else { + log::warn!("Unable to load ResourceRegistry. Signals will not fire unless this class's 'process' method is called every frame."); + } + + 0 + } + + /// Execute the command syncronously, blocking the current thread execution until the command has completed. + #[func] + pub fn execute_blocking(&mut self) -> i32 { + // Convert the args + let cmd = self.command.to_string(); + let args: Vec = self.args.iter_shared().map(|v| v.to_string()).collect(); + + let output = std::process::Command::new(cmd.as_str()) + .args(args.as_slice()) + .output(); + let output = match output { + Ok(out) => out, + Err(e) => { + log::error!("Failed to execute command '{cmd}' {args:?}: {e:?}"); + return -1; + } + }; + + let code = output.status.code().unwrap_or_default(); + let stdout = String::from_utf8_lossy(output.stdout.as_slice()).to_string(); + let stderr = String::from_utf8_lossy(output.stderr.as_slice()).to_string(); + + self.code = code; + self.stdout = stdout.to_godot(); + self.stderr = stderr.to_godot(); + + code + } + + /// Runs the given command asyncronously in the tokio runtime + async fn run( + cmd: String, + args: Vec, + tx: Sender, + mut cancel_rx: tokio::sync::mpsc::Receiver<()>, + ) { + // Build the command to execute + let task = tokio::process::Command::new(cmd.as_str()) + .args(args.as_slice()) + .kill_on_drop(true) + .output(); + + // Select between either the command completing or cancelling + tokio::select! { + // Branch if command finishes executing + output = task => { + // Construct the signal to send based on the output + let signal = match output { + Ok(output) => { + let code = output.status.code().unwrap_or_default(); + let stdout = String::from_utf8_lossy(output.stdout.as_slice()).to_string(); + let stderr = String::from_utf8_lossy(output.stderr.as_slice()).to_string(); + Signal::Finished { + stdout, + stderr, + code, + } + } + Err(e) => { + log::error!("Failed to execute command '{cmd}' {args:?}: {e:?}"); + Signal::Finished { + stdout: Default::default(), + stderr: Default::default(), + code: -1, + } + } + }; + + if let Err(e) = tx.send(signal) { + log::error!("Failed to send signal: {e:?}"); + } + }, + // Branch if cancellation signal was sent + _ = cancel_rx.recv() => { + let signal = Signal::Finished { + stdout: Default::default(), + stderr: Default::default(), + code: Command::EXIT_CODE_CANCEL, + }; + if let Err(e) = tx.send(signal) { + log::error!("Failed to send signal: {e:?}"); + } + } + } + } +} diff --git a/extensions/core/src/system/pty.rs b/extensions/core/src/system/pty.rs new file mode 100644 index 000000000..66d2c1cc2 --- /dev/null +++ b/extensions/core/src/system/pty.rs @@ -0,0 +1,385 @@ +use nix::pty::{openpty, Winsize}; +use std::{ + ffi::OsString, + sync::mpsc::{channel, Receiver, Sender, TryRecvError}, +}; +use tokio::{ + fs::File, + io::{AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, + process::{Child, Command}, + select, +}; + +use godot::{obj::WithBaseField, prelude::*}; + +use crate::RUNTIME; + +/// Signals that can be emitted +#[derive(Debug)] +enum Signal { + Started { pid: u32 }, + Finished { exit_code: i32 }, + LineWritten { line: String }, +} + +/// Commands that can be sent to a running PTY session +#[derive(Debug)] +enum PtyCommand { + Write { data: Vec }, + WriteLine { line: String }, +} + +/// Commands that can be sent to a running child process +#[derive(Debug)] +enum ProcessCommand { + Kill, +} + +#[derive(GodotClass)] +#[class(base=Node)] +pub struct Pty { + base: Base, + /// Receiver to listen for signals emitted from the async runtime + rx: Receiver, + /// Transmitter to send signals from the async runtime + tx: Sender, + /// Transmitter to send PTY writes to the PTY async task + pty_tx: Option>, + /// Transmitter to send commands to the Process async task + cmd_tx: Option>, + + /// Whether or not a process is currently running in the PTY + #[var(get = get_running)] + running: bool, + /// Number of rows the pseudo terminal should have + #[export] + rows: i32, + /// Number of columns the psuedo terminal should have + #[export] + columns: i32, + /// Width of the pseudo terminal in pixels + #[export] + width_px: i32, + /// Height of the pseudo terminal in pixels + #[export] + height_px: i32, +} + +#[godot_api] +impl Pty { + /// Emitted when a process is started in the PTY. Returns the PID of the + /// started process. + #[signal] + fn started(pid: i32); + + /// Emitted when a line is written to the PTY stdout + #[signal] + fn line_written(line: GString); + + /// Emitted when the underlying command has exited. Returns the exit code + /// of the child process. + #[signal] + fn finished(exit_code: i32); + + /// Returns whether or not the PTY is currently executing a process + #[func] + fn get_running(&self) -> bool { + self.running + } + + /// Write the given bytes to the running PTY. Returns an error code if the + /// PTY is not currently executing a process. + #[func] + fn write(&self, data: PackedByteArray) -> i32 { + let Some(pty_tx) = self.pty_tx.as_ref() else { + log::error!("PTY is not open to write line"); + return -1; + }; + let slice = data.as_slice(); + let data = slice.to_vec(); + let command = PtyCommand::Write { data }; + if let Err(e) = pty_tx.blocking_send(command) { + log::error!("Error sending write line to PTY: {e:?}"); + return -1; + } + + 0 + } + + /// Write the given line to the running PTY. Returns an error code if the + /// PTY is not currently executing a process. + #[func] + fn write_line(&self, line: GString) -> i32 { + let Some(pty_tx) = self.pty_tx.as_ref() else { + log::error!("PTY is not open to write line"); + return -1; + }; + let command = PtyCommand::WriteLine { line: line.into() }; + if let Err(e) = pty_tx.blocking_send(command) { + log::error!("Error sending write line to PTY: {e:?}"); + return -1; + } + + 0 + } + + /// Kill the currently running child process running in the PTY. Returns an + /// error code if the PTY is not currently executing a process. + #[func] + fn kill(&self) -> i32 { + let Some(cmd_tx) = self.cmd_tx.as_ref() else { + log::error!("PTY is not open to kill process"); + return -1; + }; + let command = ProcessCommand::Kill; + if let Err(e) = cmd_tx.blocking_send(command) { + log::error!("Error sending kill command to PTY: {e:?}"); + return -1; + } + 0 + } + + /// Execute the given command inside the PTY. This command is executed + /// asyncronously and will emit signals whenever new output is available. + #[func] + fn exec(&mut self, command: GString, args: PackedStringArray) -> i32 { + if self.running { + log::error!("PTY is already running a process"); + return -1; + } + + // Open a new PTY with the given dimensions + let window_size = Winsize { + ws_row: self.rows as u16, + ws_col: self.columns as u16, + ws_xpixel: self.width_px as u16, + ws_ypixel: self.height_px as u16, + }; + let pty = match openpty(Some(&window_size), None) { + Ok(pty) => pty, + Err(e) => { + log::error!("Failed to open pty: {e}"); + return -1; + } + }; + + log::debug!("Executing command async in pty"); + let command: String = command.into(); + let command = OsString::from(command); + let args: Vec = args.as_slice().iter().map(String::from).collect(); + + // Assign the different sides of the PTY + let master = pty.master; + let slave = pty.slave; + let stdin = slave.try_clone().unwrap(); + let stdout = slave.try_clone().unwrap(); + let stderr = slave; + + // Create a channel so process commands can be sent to the running process task + let (cmd_tx, cmd_rx) = tokio::sync::mpsc::channel(64); + self.cmd_tx = Some(cmd_tx); + + // Spawn a task to run the command + let signals_tx = self.tx.clone(); + RUNTIME.spawn(async move { + let mut binding = Command::new(command.clone()); + let cmd = binding + .args(args) + .stdin(stdin) + .stdout(stdout) + .stderr(stderr); + let child = match cmd.spawn() { + Ok(child) => child, + Err(e) => { + log::error!("Failed to spawn child process with command: {command:?} {e:?}"); + let signal = Signal::Finished { exit_code: -1 }; + if let Err(e) = signals_tx.send(signal) { + log::error!("Error sending exit code: {e:?}"); + } + return; + } + }; + + // Get the PID of the process and emit a started signal + let pid = child.id(); + if let Some(pid) = pid { + let signal = Signal::Started { pid }; + if let Err(e) = signals_tx.send(signal) { + log::error!("Error sending started signal: {e:?}"); + } + } + + // Wait for the process to finish + let exit_code = Pty::process_child(child, cmd_rx).await; + + // Send the exit code with the finished signal + let signal = Signal::Finished { exit_code }; + if let Err(e) = signals_tx.send(signal) { + log::error!("Error sending exit code: {e:?}"); + } + }); + + // Create a channel so input commands can be sent to the running PTY task + let (pty_tx, mut pty_rx) = tokio::sync::mpsc::channel(8192); + self.pty_tx = Some(pty_tx); + + // Spawn a task to read/write from/to the PTY + let signals_tx = self.tx.clone(); + RUNTIME.spawn(async move { + log::debug!("Task spawned to read/write PTY"); + + // Create readers/writers + let output = std::fs::File::from(master.try_clone().unwrap()); + let output: File = output.into(); + let input = std::fs::File::from(master); + let input: File = input.into(); + + let mut reader = BufReader::new(output); + let mut writer = BufWriter::new(input); + + // Select between read and write operations in a loop + loop { + let mut buffer = [0; 4096]; + select! { + // Handle stdout output + read_result = reader.read(&mut buffer[..]) => { + let bytes_read = match read_result { + Ok(n) => n, + Err(_e) => break, + }; + Pty::process_read(&buffer, bytes_read, &signals_tx); + } + // Handle stdin commands over channel + Some(cmd) = pty_rx.recv() => { + Pty::process_write(&mut writer, cmd).await; + } + } + } + log::debug!("Finished"); + }); + self.running = true; + + 0 + } + + /// Process waiting for the child process and any process commands (like kill). + /// Returns the exit code of the child when it finishes. + async fn process_child( + mut child: Child, + mut cmd_rx: tokio::sync::mpsc::Receiver, + ) -> i32 { + loop { + select! { + // Handle waiting for child exit + child_result = child.wait() => { + let status = match child_result { + Ok(status) => status, + Err(e) => { + log::error!("Error executing child: {e:?}"); + break -1; + } + }; + let exit_code = status.code().unwrap_or(0); + break exit_code; + } + // Handle process commands + Some(cmd) = cmd_rx.recv() => { + match cmd { + ProcessCommand::Kill => { + child.start_kill().unwrap_or_default(); + } + } + } + } + } + } + + /// Process reading output from the PTY + fn process_read(buffer: &[u8], bytes_read: usize, signals_tx: &Sender) { + let data = &buffer[..bytes_read]; + let text = String::from_utf8_lossy(data).to_string(); + let text = text.replace('\r', ""); + let lines = text.split('\n'); + for line in lines { + let line = line.to_string(); + let signal = Signal::LineWritten { line }; + if let Err(e) = signals_tx.send(signal) { + log::error!("Error sending line: {e:?}"); + } + } + } + + /// Process writing input to the PTY + async fn process_write(writer: &mut BufWriter, cmd: PtyCommand) { + match cmd { + PtyCommand::Write { data } => { + writer.write_all(data.as_slice()).await.unwrap(); + } + PtyCommand::WriteLine { line } => { + let line = format!("{line}\r"); + writer.write_all(line.as_bytes()).await.unwrap(); + } + }; + writer.flush().await.unwrap(); + } + + /// Process and dispatch the given signal + fn process_signal(&mut self, signal: Signal) { + match signal { + Signal::Started { pid } => { + self.base_mut().emit_signal("started", &[pid.to_variant()]); + } + Signal::Finished { exit_code } => { + self.running = false; + self.pty_tx = None; + self.cmd_tx = None; + self.base_mut() + .emit_signal("finished", &[exit_code.to_variant()]); + } + Signal::LineWritten { line } => { + self.base_mut() + .emit_signal("line_written", &[line.to_godot().to_variant()]); + } + } + } +} + +#[godot_api] +impl INode for Pty { + /// Called upon object initialization in the engine + fn init(base: Base) -> Self { + // Create a channel to communicate with the async runtime + let (tx, rx) = channel(); + + Self { + base, + rx, + tx, + pty_tx: None, + cmd_tx: None, + running: false, + rows: 8000, + columns: 8000, + width_px: 8000, + height_px: 8000, + } + } + + /// Executed every engine frame + fn process(&mut self, _delta: f64) { + // Drain all messages from the channel to process them + loop { + let signal = match self.rx.try_recv() { + Ok(value) => value, + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => { + log::error!("Backend thread is not running!"); + return; + } + }, + }; + self.process_signal(signal); + } + } +} diff --git a/extensions/core/src/system/subreaper.rs b/extensions/core/src/system/subreaper.rs new file mode 100644 index 000000000..a55a97bc8 --- /dev/null +++ b/extensions/core/src/system/subreaper.rs @@ -0,0 +1,126 @@ +//https://iximiuz.com/en/posts/dealing-with-processes-termination-in-Linux/ +// +//https://github.com/nix-rust/nix/pull/1550 + +use nix::{ + errno::Errno, + sys::{prctl, wait::wait}, + unistd::{execvp, fork, ForkResult}, +}; + +use std::ffi::{CStr, CString}; + +use godot::prelude::*; + +/// [SubReaper] provides methods for spawning a process in a subreaper. +#[derive(GodotClass)] +#[class(base=RefCounted)] +struct SubReaper { + base: Base, +} + +#[godot_api] +impl SubReaper { + /// Spawn a new subreaper process and execute the given command and arguments + /// under that subreaper. This will force all descendent processes to reparent + /// themselves to the subreaper instead of PID 1. + #[func] + pub fn create_process(command: GString, args: PackedStringArray) -> i32 { + match unsafe { fork() } { + // Parent process of the fork. Should return the subreaper process id + Ok(ForkResult::Parent { child }) => child.into(), + + // Child subreaper process of the fork. The subreaper process will execute + // the provided command and wait for all its child processes to exit. + Ok(ForkResult::Child) => { + Self::subreaper_create_process(command, args); + 0 + } + + // If forking fails, return an invalid PID + Err(e) => { + log::error!("Error forking command: {command} | {e}"); + -1 + } + } + } + + /// Sets the current process to be a child subreaper and executes the given + /// command. The subreaper process will wait until all children have exited + /// before stopping itself. + fn subreaper_create_process(command: GString, args: PackedStringArray) { + // Set the current process to be a subreaper. A subreaper MUST wait + // for all child processes to exit to clean up. + match prctl::set_child_subreaper(true) { + Ok(_) => (), + Err(e) => { + panic!("Error setting child as subreaper for command: {command} | {e}"); + } + }; + + // Spawn the desired process + match unsafe { fork() } { + // Parent process of the fork. Should wait for all child processes + // to exit. + Ok(ForkResult::Parent { child: _ }) => { + // Wait for all child processes to exit + loop { + match wait() { + Ok(_) => (), + Err(e) => { + if e == Errno::ECHILD { + break; + } + println!("Got unexpected error: {e}") + } + }; + } + + // No "quick_exit" exists, so instead use execvp to run + // a simple program to exit cleanly. + println!("No more children for subreaper. Exiting."); + let cmd = CString::new("true").unwrap(); + match execvp::<&CStr>(cmd.as_c_str(), &[cmd.as_c_str()]) { + Ok(_) => (), + Err(e) => { + panic!("Error exiting subreaper: {e:?}"); + } + } + } + + // Child process of the fork. Should execute the command. + Ok(ForkResult::Child) => { + // Convert the command to a CString + let command = CString::new(command.to_string().as_str()).unwrap(); + + // Build the arguments list. The first argument in this list + // MUST be the command that is being executed. + let mut c_args: Vec = vec![command.clone()]; + for arg in args.to_vec() { + let c_arg = CString::new(arg.to_string().as_str()).unwrap(); + c_args.push(c_arg); + } + + // Execute the command + match execvp(command.as_c_str(), c_args.as_slice()) { + Ok(_) => (), + Err(e) => { + panic!("Error executing command: {command:?} | {e}"); + } + } + } + + // Panic if forking fails. + Err(e) => { + panic!("Error forking subprocess for command: {command} | {e}"); + } + } + } +} + +#[godot_api] +impl IRefCounted for SubReaper { + fn init(base: Base) -> Self { + Self { base } + } +} diff --git a/extensions/core/src/vdf.rs b/extensions/core/src/vdf.rs new file mode 100644 index 000000000..fdff7a182 --- /dev/null +++ b/extensions/core/src/vdf.rs @@ -0,0 +1,265 @@ +use std::borrow::Cow; + +use godot::prelude::*; +use keyvalues_parser::{Obj, Value, Vdf as VdfParser}; + +/// Helper class for creating and parsing VDF data. +/// +/// The [Vdf] class enables the [Dictionary] data type to be converted to and from a VDF string. This is useful for (de)serializing data that use Valve's data format. +/// +/// [method stringify] is used to convert a [Dictionary] into a VDF string. +/// +/// [method parse] is used to convert any existing VDF data into a [Dictionary] that can be used within Godot. +#[derive(GodotClass)] +#[class(init, base=RefCounted)] +pub struct Vdf { + base: Base, + /// Contains the parsed VDF data in [Dictionary] form. + #[var] + data: Dictionary, + error_msg: Option, +} + +#[godot_api] +impl Vdf { + /// Returns an empty string if the last call to [method parse] was successful, or the error message if it failed. + #[func] + pub fn get_error_message(&self) -> GString { + let Some(msg) = self.error_msg.clone() else { + return GString::new(); + }; + msg.into() + } + + /// Converts a dictionary to VDF text and returns the result. + /// The VDF format only allows a single top-level key, so others will be ignored if the given [Dictionary] contains more than one top-level key. + #[func] + pub fn stringify(data: Dictionary) -> GString { + // Get the first entry in the dictionary. Other top-level entries are ignored. + let Some((key, value)) = data.iter_shared().next() else { + return GString::new(); + }; + + // Convert the key and value variants into VDF keys and values + let Some(key) = key.as_vdf() else { + return GString::new(); + }; + let Value::Str(key) = key else { + return GString::new(); + }; + let Some(value) = value.as_vdf() else { + return GString::new(); + }; + + let vdf = VdfParser::new(key, value); + + vdf.to_string().into() + } + + /// Attempts to parse the `vdf_text` provided. + /// Returns an Error. If the parse was successful, it returns OK and the result can be retrieved using [member data]. If unsuccessful, use [method get_error_message] to identify the source of the failure. + #[func] + pub fn parse(&mut self, vdf_text: GString) -> i32 { + let data = vdf_text.to_string(); + + // Try to parse the data + let vdf = match VdfParser::parse(data.as_str()) { + Ok(parsed) => parsed, + Err(e) => { + self.error_msg = Some(e.to_string()); + return -1; + } + }; + + // Convert the vdf data into a Godot Dictionary + let mut dict = dict! {}; + let key = vdf.key.to_string(); + let value = match vdf.value { + Value::Str(value) => value.to_string().to_variant(), + Value::Obj(obj) => Vdf::obj_to_dict(obj).to_variant(), + }; + + dict.set(key, value); + self.data = dict; + + 0 + } + + /// Attempts to parse the `vdf_string` provided and returns the parsed data. + /// Returns an empty [Dictionary] if parse failed. + #[func] + pub fn parse_string(vdf_string: GString) -> Dictionary { + let mut dict = dict! {}; + let data = vdf_string.to_string(); + + // Try to parse the data + let vdf = match VdfParser::parse(data.as_str()) { + Ok(parsed) => parsed, + Err(_) => { + return dict; + } + }; + + // Convert the vdf data into a Godot Dictionary + let key = vdf.key.to_string(); + let value = match vdf.value { + Value::Str(value) => value.to_string().to_variant(), + Value::Obj(obj) => Vdf::obj_to_dict(obj).to_variant(), + }; + dict.set(key, value); + + dict + } + + /// Convert the given VDF object into a Godot Dictionary + fn obj_to_dict(obj: Obj) -> Dictionary { + let mut dict = dict! {}; + + for vdf in obj.into_vdfs() { + let key = vdf.key.to_string(); + let value = match vdf.value { + Value::Str(value) => value.to_string().to_variant(), + Value::Obj(obj) => Vdf::obj_to_dict(obj).to_variant(), + }; + + dict.set(key, value); + } + + dict + } + + /// Convert the given Godot Dictionary into a VDF object + fn dict_to_obj<'a>(dict: &'a Dictionary, obj: &'a mut Obj) { + for (key, value) in dict.iter_shared() { + // Convert the key and value variants into VDF keys and values + let Some(key) = key.as_vdf() else { + continue; + }; + let Value::Str(key) = key else { + continue; + }; + let Some(value) = value.as_vdf() else { + continue; + }; + let value = Vdf::value_copy(&value); + + let value_vec = vec![value]; + obj.insert(Cow::from(key.to_string()), value_vec); + } + } + + /// Convert the given array into a VDF-friendly dictionary + fn array_to_dict(value: &Array) -> Dictionary { + let mut dict = dict! {}; + for (i, value) in value.iter_shared().enumerate() { + dict.set(i.to_string(), value); + } + + dict + } + + /// Creates a copied version of the given VDF value + fn value_copy<'a>(value: &Value) -> Value<'a> { + match value { + Value::Str(v) => Value::Str(Cow::from(v.to_string())), + Value::Obj(obj) => { + let mut obj_copy = Obj::new(); + + for (key, values) in obj.iter() { + let key = Cow::from(key.to_string()); + let mut copied_values = Vec::with_capacity(values.len()); + for value in values { + let new_value = Vdf::value_copy(value); + copied_values.push(new_value); + } + obj_copy.insert(key, copied_values); + } + + Value::Obj(obj_copy) + } + } + } +} + +/// Trait for converting Godot variant values into Vdf values +pub trait VdfVariant { + fn as_vdf(&self) -> Option; +} + +impl VdfVariant for Variant { + fn as_vdf(&self) -> Option { + match self.get_type() { + VariantType::NIL => None, + VariantType::BOOL => { + let value: bool = self.to(); + let value = if value { "true" } else { "false" }; + Some(Value::Str(Cow::from(value))) + } + VariantType::INT => { + let value: i64 = self.to(); + Some(Value::Str(Cow::from(value.to_string()))) + } + VariantType::FLOAT => { + let value: f64 = self.to(); + Some(Value::Str(Cow::from(value.to_string()))) + } + VariantType::STRING => { + let value: GString = self.to(); + let value: String = value.into(); + Some(Value::Str(Cow::from(value))) + } + VariantType::VECTOR2 => None, + VariantType::VECTOR2I => None, + VariantType::RECT2 => None, + VariantType::RECT2I => None, + VariantType::VECTOR3 => None, + VariantType::VECTOR3I => None, + VariantType::TRANSFORM2D => None, + VariantType::VECTOR4 => None, + VariantType::VECTOR4I => None, + VariantType::PLANE => None, + VariantType::QUATERNION => None, + VariantType::AABB => None, + VariantType::BASIS => None, + VariantType::TRANSFORM3D => None, + VariantType::PROJECTION => None, + VariantType::COLOR => None, + VariantType::STRING_NAME => None, + VariantType::NODE_PATH => None, + VariantType::RID => { + let value: i64 = self.to(); + Some(Value::Str(Cow::from(value.to_string()))) + } + VariantType::OBJECT => None, + VariantType::CALLABLE => None, + VariantType::SIGNAL => None, + VariantType::DICTIONARY => { + let value: Dictionary = self.to(); + let mut obj = Obj::new(); + Vdf::dict_to_obj(&value, &mut obj); + Some(Value::Obj(obj)) + } + VariantType::ARRAY => { + let value: Array = self.to(); + let dict = Vdf::array_to_dict(&value); + let mut obj = Obj::new(); + Vdf::dict_to_obj(&dict, &mut obj); + Some(Value::Obj(obj)) + } + VariantType::PACKED_BYTE_ARRAY => None, + VariantType::PACKED_INT32_ARRAY => None, + VariantType::PACKED_INT64_ARRAY => None, + VariantType::PACKED_FLOAT32_ARRAY => None, + VariantType::PACKED_FLOAT64_ARRAY => None, + VariantType::PACKED_STRING_ARRAY => None, + VariantType::PACKED_VECTOR2_ARRAY => None, + VariantType::PACKED_VECTOR3_ARRAY => None, + VariantType::PACKED_COLOR_ARRAY => None, + VariantType::PACKED_VECTOR4_ARRAY => None, + VariantType::MAX => None, + + // Unsupported conversion + _ => None, + } + } +} diff --git a/extensions/reaper/.gitignore b/extensions/reaper/.gitignore new file mode 100644 index 000000000..54b02da46 --- /dev/null +++ b/extensions/reaper/.gitignore @@ -0,0 +1,2 @@ +/target +*.so diff --git a/extensions/reaper/Cargo.toml b/extensions/reaper/Cargo.toml new file mode 100644 index 000000000..ca0494240 --- /dev/null +++ b/extensions/reaper/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "reaper" +version = "0.1.0" +edition = "2021" + +[dependencies] +nix = { version = "0.29.0", features = ["process"] } diff --git a/extensions/reaper/src/main.rs b/extensions/reaper/src/main.rs new file mode 100644 index 000000000..865e61cc8 --- /dev/null +++ b/extensions/reaper/src/main.rs @@ -0,0 +1,101 @@ +use std::{env, ffi::CString, process::exit}; + +use nix::{ + errno::Errno, + sys::{ + prctl, + wait::{wait, WaitStatus}, + }, + unistd::{execvp, fork, ForkResult}, +}; + +fn main() { + // Parse the arguments that should be passed to the child process + let args = env::args(); + let mut child_args = vec![]; + let mut end_of_args = false; + for arg in args { + if !end_of_args && arg.as_str() == "--" { + end_of_args = true; + continue; + } + if !end_of_args { + continue; + } + child_args.push(arg); + } + + // If "--" was not included as an argument, return an error. + if !end_of_args { + panic!("reaper: no '--' argument was found"); + } + if child_args.is_empty() { + panic!("reaper: no sub-command!"); + } + + // Set the current process to be a subreaper. A subreaper MUST wait + // for all child processes to exit to clean up. + if let Err(e) = prctl::set_child_subreaper(true) { + panic!("reaper: failed to set child subreaper: {e:?}"); + } + + // Fork to spawn the requested process and wait for the subprocesses to exit + match unsafe { fork() } { + // Parent process of the fork should wait for all child processes to exit + Ok(ForkResult::Parent { child }) => { + println!("reaper: got child PID: {child}"); + + // Keep track of child exit codes + let mut exit_codes = vec![]; + + // Wait for all child processes to exit + loop { + match wait() { + Ok(status) => { + if let WaitStatus::Exited(_, code) = status { + exit_codes.push(code); + } + } + Err(e) => { + if e == Errno::ECHILD { + break; + } + println!("reaper: got unexpected error: {e}") + } + }; + } + + println!("reaper: no more children exist; exiting"); + + // Return the exit code of the last exited child process + let exit_code = exit_codes.last().unwrap_or(&0); + exit(*exit_code); + } + + // Child process of the fork should execute the requested command + Ok(ForkResult::Child) => { + println!("reaper: executing command: {child_args:?}"); + + // Convert the command to a CString + let cmd = CString::new(child_args.first().unwrap().as_str()).unwrap(); + + // Build the arguments list + let mut c_args = vec![]; + for arg in child_args { + let c_arg = CString::new(arg.as_str()).unwrap(); + c_args.push(c_arg); + } + + // Execute the command + #[allow(irrefutable_let_patterns)] + if let Err(e) = execvp(cmd.as_c_str(), c_args.as_slice()) { + panic!("reaper: failed executing command: {e:?}"); + } + } + + // Panic if forking fails + Err(e) => { + panic!("reaper: failed to create fork: {e:?}"); + } + } +} diff --git a/gdext/.gitignore b/gdext/.gitignore deleted file mode 100644 index 5b2d46270..000000000 --- a/gdext/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.sconsign.dblite diff --git a/gdext/Makefile b/gdext/Makefile deleted file mode 100644 index e540f8b04..000000000 --- a/gdext/Makefile +++ /dev/null @@ -1,129 +0,0 @@ -NUM_CPU := $(shell nproc) - -# Variables to define all the extensions to build. If a new extension is added, -# they should be added to these lists. -ADDONS_PATH = ../addons -ALL_EXT_PATHS = ../addons/dbus ../addons/linuxthread ../addons/pty ../addons/unixsock ../addons/xlib -ALL_CPP_FILES = $(shell find ./godot-cpp -regex '.*\(\.cpp\|\.h\|\.hpp\)$$') godot-cpp/SConstruct - -ALL_SCONS_FILES = godot-cpp/SConstruct \ - godot-dbus/SConstruct \ - godot-linuxthread/SConstruct \ - godot-pty/SConstruct \ - godot-unix-socket/SConstruct \ - godot-xlib/SConstruct - -ALL_DEBUG_EXT = $(ADDONS_PATH)/dbus/bin/libdbus.linux.template_debug.x86_64.so \ - $(ADDONS_PATH)/linuxthread/bin/liblinuxthread.linux.template_debug.x86_64.so \ - $(ADDONS_PATH)/pty/bin/libpty.linux.template_debug.x86_64.so \ - $(ADDONS_PATH)/unixsock/bin/libunixsock.linux.template_debug.x86_64.so \ - $(ADDONS_PATH)/xlib/bin/libxlib.linux.template_debug.x86_64.so - -ALL_RELEASE_EXT = $(ADDONS_PATH)/dbus/bin/libdbus.linux.template_release.x86_64.so \ - $(ADDONS_PATH)/linuxthread/bin/liblinuxthread.linux.template_release.x86_64.so \ - $(ADDONS_PATH)/pty/bin/libpty.linux.template_release.x86_64.so \ - $(ADDONS_PATH)/unixsock/bin/libunixsock.linux.template_release.x86_64.so \ - $(ADDONS_PATH)/xlib/bin/libxlib.linux.template_release.x86_64.so - -ALL_GDEXT_FILES = $(ADDONS_PATH)/dbus/dbus.gdextension \ - $(ADDONS_PATH)/linuxthread/linuxthread.gdextension \ - $(ADDONS_PATH)/pty/pty.gdextension \ - $(ADDONS_PATH)/unixsock/unixsock.gdextension \ - $(ADDONS_PATH)/xlib/xlib.gdextension - -##@ General - -# The help target prints out all targets with their descriptions organized -# beneath their categories. The categories are represented by '##@' and the -# target descriptions by '##'. The awk commands is responsible for reading the -# entire set of makefiles included in this invocation, looking for lines of the -# file as xyz: ## something, and then pretty-format the target and help. Then, -# if there's a line with ##@ something, that gets pretty-printed as a category. -# More info on the usage of ANSI control characters for terminal formatting: -# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters -# More info on the awk command: -# http://linuxcommand.org/lc3_adv_awk.php - -.PHONY: help -help: ## Display this help. - @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) - -.PHONY: build -build: ## Build all GDExtensions - $(MAKE) release debug - -.PHONY: release -release: $(ALL_RELEASE_EXT) -$(ALL_RELEASE_EXT) &: $(ALL_GDEXT_FILES) $(ALL_CPP_FILES) - scons platform=linux -j$(NUM_CPU) target=template_release - -.PHONY: debug -debug: $(ALL_DEBUG_EXT) -$(ALL_DEBUG_EXT) &: $(ALL_GDEXT_FILES) $(ALL_CPP_FILES) - scons platform=linux -j$(NUM_CPU) target=template_debug - -$(ALL_GDEXT_FILES) &: $(ALL_SCONS_FILES) - mkdir -p $(ALL_EXT_PATHS) - cp ./godot-dbus/addons/dbus/dbus.gdextension $(ADDONS_PATH)/dbus - cp ./godot-linuxthread/addons/linuxthread/linuxthread.gdextension $(ADDONS_PATH)/linuxthread - cp ./godot-pty/addons/pty/pty.gdextension $(ADDONS_PATH)/pty - cp ./godot-unix-socket/addons/unixsock/unixsock.gdextension $(ADDONS_PATH)/unixsock - cp ./godot-xlib/addons/xlib/xlib.gdextension $(ADDONS_PATH)/xlib - -.PHONY: clean -clean: ## Clean all build artifacts - rm -rf $(ALL_EXT_PATHS) - find ./ -type f -name '*.o' -delete - find ./ -type f -name '*.a' -delete - find ./ -type f -name '*.os' -delete - find ./ -type f -name '*.so' -delete - -godot-cpp/SConstruct: - git submodule update --init godot-cpp - -godot-dbus/SConstruct: - git submodule update --init godot-dbus - -godot-linuxthread/SConstruct: - git submodule update --init godot-linuxthread - -godot-pty/SConstruct: - git submodule update --init godot-pty - -godot-unix-socket/SConstruct: - git submodule update --init godot-unix-socket - -godot-xlib/SConstruct: - git submodule update --init godot-xlib - -##@ Updates - -.PHONY: update-dbus -update-dbus: ## Update godot-dbus - cd godot-dbus - git fetch - git rebase origin/main - -.PHONY: update-linuxthread -update-linuxthread: ## Update godot-linuxthread - cd godot-linuxthread - git fetch - git rebase origin/main - -.PHONY: update-pty -update-pty: ## Update godot-pty - cd godot-pty - git fetch - git rebase origin/main - -.PHONY: update-unixsock -update-unixsock: ## Update godot-unixsock - cd godot-unixsock - git fetch - git rebase origin/main - -.PHONY: update-xlib -update-xlib: ## Update godot-xlib - cd godot-xlib - git fetch - git rebase origin/main diff --git a/gdext/README.md b/gdext/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/gdext/SConstruct b/gdext/SConstruct deleted file mode 100644 index df56d6dd8..000000000 --- a/gdext/SConstruct +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python -from SCons import __version__ as scons_raw_version -import os -import sys - -# Define path to godot-cpp dependency -godot_cpp_path = "godot-cpp" -if 'GODOT_CPP_PATH' in os.environ: - godot_cpp_path = os.environ['GODOT_CPP_PATH'] - -# Setup a standard path to output the extension -EXT_PATH = "../addons/{}/bin/lib{}{}{}" - -# Setup the environments from godot-cpp -env = SConscript(godot_cpp_path + "/SConstruct") -dbus_env = env.Clone() -thread_env = env.Clone() -pty_env = env.Clone() -unixsock_env = env.Clone() -xlib_env = env.Clone() - - -# --- godot-dbus --- - -# tweak this if you want to use different folders, or more folders, to store your source code in. -dbus_env.Append(CPPPATH=["godot-dbus/src/"]) -dbus_sources = Glob("godot-dbus/src/*.cpp") - -# Include dependency libraries for dbus -if 'PKG_CONFIG_PATH' in os.environ: - dbus_env['ENV']['PKG_CONFIG_PATH'] = os.environ['PKG_CONFIG_PATH'] -dbus_env.ParseConfig("pkg-config dbus-1 --cflags --libs") - -# Build the shared library -libdbus = dbus_env.SharedLibrary( - EXT_PATH.format( - "dbus", "dbus", dbus_env["suffix"], dbus_env["SHLIBSUFFIX"] - ), - source=dbus_sources, -) - -Default(libdbus) - - -# --- godot-linuxthread --- - -# tweak this if you want to use different folders, or more folders, to store your source code in. -thread_env.Append(CPPPATH=["godot-linuxthread/src/"]) -thread_sources = Glob("godot-linuxthread/src/*.cpp") - -# Build the shared library -libthread = thread_env.SharedLibrary( - EXT_PATH.format("linuxthread", - "linuxthread", thread_env["suffix"], thread_env["SHLIBSUFFIX"]), - source=thread_sources, -) - -Default(libthread) - - -# --- godot-pty --- - -# tweak this if you want to use different folders, or more folders, to store your source code in. -pty_env.Append(CPPPATH=["godot-pty/src/"]) -pty_sources = Glob("godot-pty/src/*.cpp") - -# Build the shared library -libpty = pty_env.SharedLibrary( - EXT_PATH.format("pty", - "pty", pty_env["suffix"], pty_env["SHLIBSUFFIX"]), - source=pty_sources, -) - -Default(libpty) - - -# --- godot-unix-socket --- - -# tweak this if you want to use different folders, or more folders, to store your source code in. -unixsock_env.Append(CPPPATH=["godot-unix-socket/src/"]) -unixsock_sources = Glob("godot-unix-socket/src/*.cpp") - -# Build the shared library -libunixsock = unixsock_env.SharedLibrary( - EXT_PATH.format("unixsock", - "unixsock", unixsock_env["suffix"], unixsock_env["SHLIBSUFFIX"]), - source=unixsock_sources, -) - -Default(libunixsock) - - -# --- godot-unix-socket --- - -# tweak this if you want to use different folders, or more folders, to store your source code in. -xlib_env.Append(CPPPATH=["godot-xlib/src/"]) -xlib_sources = Glob("godot-xlib/src/*.cpp") - -# Include dependency libraries for the extension -if 'PKG_CONFIG_PATH' in os.environ: - xlib_env['ENV']['PKG_CONFIG_PATH'] = os.environ['PKG_CONFIG_PATH'] -xlib_env.ParseConfig("pkg-config x11 --cflags --libs") -xlib_env.ParseConfig("pkg-config xres --cflags --libs") -xlib_env.ParseConfig("pkg-config xtst --cflags --libs") -xlib_env.ParseConfig("pkg-config xi --cflags --libs") - -# Build the shared library -libx11 = xlib_env.SharedLibrary( - EXT_PATH.format("xlib", - "xlib", xlib_env["suffix"], xlib_env["SHLIBSUFFIX"]), - source=xlib_sources, -) - -Default(libx11) diff --git a/gdext/godot-cpp b/gdext/godot-cpp deleted file mode 160000 index d6e5286cc..000000000 --- a/gdext/godot-cpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d6e5286cc19bbd5b2c626207d3b01a8f145c0f76 diff --git a/gdext/godot-dbus b/gdext/godot-dbus deleted file mode 160000 index a8f621416..000000000 --- a/gdext/godot-dbus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a8f62141612046b9bfff9f3b7d153ab7a122c899 diff --git a/gdext/godot-linuxthread b/gdext/godot-linuxthread deleted file mode 160000 index dbe785424..000000000 --- a/gdext/godot-linuxthread +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dbe785424ed5eb62aca2ee33a87922f22c017641 diff --git a/gdext/godot-pty b/gdext/godot-pty deleted file mode 160000 index cd3128ac0..000000000 --- a/gdext/godot-pty +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cd3128ac07eb3407e877dacea4e93cc524039112 diff --git a/gdext/godot-unix-socket b/gdext/godot-unix-socket deleted file mode 160000 index 3ce07d786..000000000 --- a/gdext/godot-unix-socket +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3ce07d7868cc6cd1028ce0ec681b2f6789b04221 diff --git a/gdext/godot-xlib b/gdext/godot-xlib deleted file mode 160000 index 95e8237dd..000000000 --- a/gdext/godot-xlib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 95e8237dd472bbed6acb1d18fa8b9e9514cbd33b diff --git a/package/rpm/opengamepadui.spec b/package/rpm/opengamepadui.spec index dda87ddf9..40007e551 100644 --- a/package/rpm/opengamepadui.spec +++ b/package/rpm/opengamepadui.spec @@ -28,6 +28,7 @@ make install PREFIX=%{buildroot}%{_prefix} INSTALL_PREFIX=%{_prefix} %files /usr/bin/opengamepadui /usr/share/opengamepadui/*.so +/usr/share/opengamepadui/reaper /usr/share/opengamepadui/scripts/* /usr/share/opengamepadui/opengamepad-ui.x86_64 /usr/share/opengamepadui/opengamepad-ui.pck diff --git a/project.godot b/project.godot index 2919adce4..5fe967111 100644 --- a/project.godot +++ b/project.godot @@ -20,7 +20,7 @@ config/name="Open Gamepad UI" run/main_scene="res://entrypoint.tscn" config/use_custom_user_dir=true config/custom_user_dir_name="opengamepadui" -config/features=PackedStringArray("4.2", "Forward Plus") +config/features=PackedStringArray("4.3", "Forward Plus") run/low_processor_mode=true boot_splash/bg_color=Color(0, 0, 0, 0) boot_splash/show_image=false @@ -56,53 +56,53 @@ import/blender/enabled=false ui_accept={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":4194309,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":4194310,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":4194309,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":4194310,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) ] } ui_select={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) ] } ui_focus_next={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null) ] } ui_focus_prev={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null) ] } ui_left={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":4194319,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":4194319,"location":0,"echo":false,"script":null) , null, null, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":true,"script":null) , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null) ] } ui_right={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":4194321,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":4194321,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":14,"pressure":0.0,"pressed":true,"script":null) , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null) ] } ui_up={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":4194320,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":4194320,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":11,"pressure":0.0,"pressed":true,"script":null) , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null) ] } ui_down={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":4194322,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":4194322,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":12,"pressure":0.0,"pressed":true,"script":null) , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null) ] @@ -114,13 +114,13 @@ ogui_guide={ } ogui_tab_right={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194324,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194324,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null) ] } ogui_tab_left={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194323,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194323,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null) ] } @@ -141,7 +141,7 @@ ogui_west={ } ogui_east={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) ] } @@ -151,23 +151,23 @@ ogui_guide_action={ } ogui_osk={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_qb={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194333,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194333,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_menu={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_back={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194308,"key_label":0,"unicode":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194308,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) ] } @@ -183,12 +183,12 @@ ogui_right_trigger={ } ogui_modifier={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_guide_action_qb={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194333,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194333,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_power={ @@ -213,22 +213,22 @@ ogui_scroll_right={ } ogui_search={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194336,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194336,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_volume_up={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194382,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194382,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_volume_down={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194380,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194380,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_volume_mute={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194381,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194381,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ogui_qam_ov={ @@ -289,6 +289,10 @@ pointing/emulate_touch_from_mouse=true locale/translations=PackedStringArray("res://assets/locale/en_US.po", "res://assets/locale/es_MX.po", "res://assets/locale/fr_FR.po", "res://assets/locale/zh_CH.po") locale/fallback="en_US" +[network] + +limits/debugger/max_queued_messages=4096 + [rendering] viewport/transparent_background=true diff --git a/rootfs/Makefile b/rootfs/Makefile index b383453c0..274d6cbd7 100644 --- a/rootfs/Makefile +++ b/rootfs/Makefile @@ -28,6 +28,8 @@ install: ## Install OpenGamepadUI (default: ~/.local) $(PREFIX)/share/opengamepadui/opengamepad-ui.x86_64 install -Dm644 usr/share/opengamepadui/opengamepad-ui.pck \ $(PREFIX)/share/opengamepadui/opengamepad-ui.pck + install -Dm755 usr/share/opengamepadui/reaper \ + $(PREFIX)/share/opengamepadui/reaper setcap 'cap_sys_nice=eip' $(PREFIX)/share/opengamepadui/opengamepad-ui.x86_64 || true mkdir -p $(PREFIX)/share/opengamepadui/scripts install -Dm755 usr/share/opengamepadui/scripts/manage_input \ @@ -51,6 +53,7 @@ uninstall: ## Uninstall OpenGamepadUI rm -rf $(PREFIX)/bin/opengamepadui rm -rf $(PREFIX)/share/opengamepadui/opengamepad-ui.x86_64 rm -rf $(PREFIX)/share/opengamepadui/opengamepad-ui.pck + rm -rf $(PREFIX)/share/opengamepadui/reaper rm -rf $(PREFIX)/share/opengamepadui/*.so rm -rf $(PREFIX)/lib/systemd/user/systemd-sysext-updater.service rm -rf $(PREFIX)/lib/systemd/user/ogui-overlay-mode.service