diff --git a/assets/ui/icons/screenshot-2-fill.svg b/assets/ui/icons/screenshot-2-fill.svg new file mode 100644 index 000000000..3b17d1b74 --- /dev/null +++ b/assets/ui/icons/screenshot-2-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/ui/icons/screenshot-2-fill.svg.import b/assets/ui/icons/screenshot-2-fill.svg.import new file mode 100644 index 000000000..b3ec7ee69 --- /dev/null +++ b/assets/ui/icons/screenshot-2-fill.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bagy3jlijp6ta" +path="res://.godot/imported/screenshot-2-fill.svg-88f27de4581ef5c9a924861c67ede490.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/ui/icons/screenshot-2-fill.svg" +dest_files=["res://.godot/imported/screenshot-2-fill.svg-88f27de4581ef5c9a924861c67ede490.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/systems/screenshot/screenshotter.gd b/core/systems/screenshot/screenshotter.gd new file mode 100644 index 000000000..074af2a72 --- /dev/null +++ b/core/systems/screenshot/screenshotter.gd @@ -0,0 +1,86 @@ +@tool +@icon("res://assets/ui/icons/screenshot-2-fill.svg") +extends Node +class_name Screenshotter + +## Take a gamescope screenshot when a signal fires +## +## The [Screenshotter] can be added as a child to any node that exposes signals. +## Upon entering the scene tree, the [Screenshotter] connects to a given signal +## on its parent, and will take a screenshot when the given signal fires. + +signal screenshot_taken + +var gamescope := load("res://core/global/gamescope.tres") as Gamescope +var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager + +## Signal on our parent to connect to. When this signal fires, the [Screenshotter] +## will take a screenshot with gamescope +var on_signal: String +## Path to move the screenshot file to +@export var destination_folder := "user://screenshots" +## Whether or not a notification should be shown after taking a screenshot +@export var show_notification := true + +var logger := Log.get_logger("Screenshotter") + + +func _ready() -> void: + notify_property_list_changed() + get_parent().connect(on_signal, _on_signal) + + +func _on_signal(): + # Take a screenshot + gamescope.request_screenshot() + + # Wait for gamescope to write the screenshot + await get_tree().create_timer(3).timeout + var msg := "Screenshot saved" + + # Move the screenshot to the destination + var output_file := "/tmp/gamescope.png" + if FileAccess.file_exists(output_file): + var date := Time.get_time_string_from_system() + var file_name := "opengamepadui-{0}.png".format([date.replace(":", "_")]) + var dest_path := "/".join([destination_folder, file_name]) + DirAccess.make_dir_recursive_absolute(destination_folder) + var err := DirAccess.rename_absolute(output_file, dest_path) + if err != OK: + msg = "Unable to save screenshot to " + ProjectSettings.globalize_path(dest_path) + logger.error(msg) + else: + msg = "Saved screenshot to: " + ProjectSettings.globalize_path(dest_path) + logger.info(msg) + screenshot_taken.emit() + + # Send a notification if configured + if show_notification: + var notify := Notification.new(msg) + notification_manager.show(notify) + + +# 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/screenshot/screenshotter.tscn b/core/systems/screenshot/screenshotter.tscn new file mode 100644 index 000000000..e5fffef1a --- /dev/null +++ b/core/systems/screenshot/screenshotter.tscn @@ -0,0 +1,7 @@ +[gd_scene load_steps=2 format=3 uid="uid://c02r5ym6x7he3"] + +[ext_resource type="Script" path="res://core/systems/screenshot/screenshotter.gd" id="1_eqvtv"] + +[node name="Screenshotter" type="Node"] +script = ExtResource("1_eqvtv") +on_signal = "" diff --git a/core/ui/card_ui/qam/quick_access_menu.tscn b/core/ui/card_ui/qam/quick_access_menu.tscn index 80debceaf..fe31cafcf 100644 --- a/core/ui/card_ui/qam/quick_access_menu.tscn +++ b/core/ui/card_ui/qam/quick_access_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=24 format=3 uid="uid://hroo3ll4inrb"] +[gd_scene load_steps=26 format=3 uid="uid://hroo3ll4inrb"] [ext_resource type="Script" path="res://core/ui/card_ui/qam/quick_access_menu.gd" id="1_56jo7"] [ext_resource type="PackedScene" uid="uid://shvyhrv5sx3v" path="res://core/systems/state/state_watcher.tscn" id="2_6rvrx"] @@ -15,8 +15,10 @@ [ext_resource type="Resource" uid="uid://cr544el0cqjlm" path="res://assets/state/state_machines/global_state_machine.tres" id="13_u3r8o"] [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="Texture2D" uid="uid://bagy3jlijp6ta" path="res://assets/ui/icons/screenshot-2-fill.svg" id="15_2xd16"] [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"] +[ext_resource type="PackedScene" uid="uid://c02r5ym6x7he3" path="res://core/systems/screenshot/screenshotter.tscn" id="16_8agkl"] [ext_resource type="PackedScene" uid="uid://b0cyl6fdqxevn" path="res://core/systems/input/scroller_joystick.tscn" id="17_qgen2"] [ext_resource type="Resource" uid="uid://dpc1o781f43ef" path="res://core/ui/card_ui/qam/quick_access_menu_focus.tres" id="18_4nxly"] [ext_resource type="PackedScene" uid="uid://bjy50kdrebgre" path="res://core/ui/card_ui/qam/notifications_card.tscn" id="19_pppbi"] @@ -107,6 +109,14 @@ state_machine = ExtResource("13_u3r8o") state = ExtResource("14_gr3i0") on_signal = "button_up" +[node name="ScreenshotButton" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer" instance=ExtResource("9_6qs1m")] +custom_minimum_size = Vector2(26, 26) +layout_mode = 2 +texture = ExtResource("15_2xd16") + +[node name="Screenshotter" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer/ScreenshotButton" instance=ExtResource("16_8agkl")] +on_signal = "button_up" + [node name="Spacer" type="Control" parent="MarginContainer/PanelContainer/MarginContainer/VBoxContainer/ButtonContainer"] layout_mode = 2 size_flags_horizontal = 3