diff --git a/core/global/keyboard_instance.gd b/core/global/keyboard_instance.gd index 172ba67f..d0f9caa9 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 2e68dd4e..602c68e2 100644 --- a/core/global/launch_manager.gd +++ b/core/global/launch_manager.gd @@ -52,6 +52,7 @@ var _sandbox := Sandbox.get_sandbox() var _current_app: RunningApp var _pid_to_windows := {} var _running: 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") @@ -135,7 +136,7 @@ func _init() -> void: var on_window_created := func(window_id: int): logger.debug("Window created:", window_id) self.check_running.call_deferred() - _xwayland_ogui.window_created.connect(on_window_created) + _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): @@ -191,9 +192,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() @@ -220,7 +251,7 @@ func launch(app: LibraryLaunchItem) -> RunningApp: 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: @@ -254,12 +285,7 @@ func launch(app: LibraryLaunchItem) -> RunningApp: # Launch the application process var running_app := RunningApp.spawn(app, env, exec, command) logger.info("Launched with PID: {0}".format([running_app.pid])) - - # 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 @@ -306,6 +332,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: @@ -463,6 +494,8 @@ func check_running() -> void: # 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 diff --git a/core/global/library_manager.gd b/core/global/library_manager.gd index 42845f05..38975b3c 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/systems/input/events/event.gd b/core/systems/input/events/event.gd index 632602be..c996fe2c 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/keyboard_opener.gd b/core/systems/input/keyboard_opener.gd new file mode 100644 index 00000000..372a5e6e --- /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/ui/card_ui/quick_bar/quick_bar_menu.tscn b/core/ui/card_ui/quick_bar/quick_bar_menu.tscn index e286eb28..f601892c 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=31 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"] @@ -109,6 +111,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 diff --git a/core/ui/common/osk/on_screen_keyboard.gd b/core/ui/common/osk/on_screen_keyboard.gd index 8b1fd741..80ece694 100644 --- a/core/ui/common/osk/on_screen_keyboard.gd +++ b/core/ui/common/osk/on_screen_keyboard.gd @@ -11,10 +11,13 @@ signal closed 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_TYPE_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) # TODO: Fix this + 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/powertools_menu.gd b/core/ui/common/quick_bar/powertools_menu.gd index c56ec8b7..5123d52a 100644 --- a/core/ui/common/quick_bar/powertools_menu.gd +++ b/core/ui/common/quick_bar/powertools_menu.gd @@ -110,13 +110,13 @@ func _ready() -> void: # 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_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 = 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_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) @@ -245,19 +245,19 @@ func _setup_interface() -> void: 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_slider.min_value = round(hardware_manager.gpu.tdp_min) + tdp_slider.max_value = round(hardware_manager.gpu.tdp_max) tdp_boost_slider.visible = true - tdp_boost_slider.max_value = hardware_manager.gpu.max_boost + tdp_boost_slider.max_value = round(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_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 - 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.min_value = round(card.clock_limit_mhz_min) + gpu_freq_max_slider.max_value = round(card.clock_limit_mhz_max) if card.thermal_throttle_limit_c > 0: gpu_temp_slider.visible = true diff --git a/core/ui/components/toggle.tscn b/core/ui/components/toggle.tscn index 4c68c13f..55a8fd1f 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/extensions/core/src/input/inputplumber/composite_device.rs b/extensions/core/src/input/inputplumber/composite_device.rs index 3f72eadf..89e0cc08 100644 --- a/extensions/core/src/input/inputplumber/composite_device.rs +++ b/extensions/core/src/input/inputplumber/composite_device.rs @@ -7,6 +7,8 @@ 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)] @@ -49,10 +51,6 @@ pub struct CompositeDevice { #[allow(dead_code)] #[var(get = get_source_device_paths)] source_device_paths: PackedStringArray, - /// Get the target device types for the composite device (e.g. "keyboard", "mouse", etc.) - #[allow(dead_code)] - #[var(get = get_target_devices, set = set_target_devices)] - target_devices: PackedStringArray, } #[godot_api] @@ -75,7 +73,6 @@ impl CompositeDevice { target_capabilities: Default::default(), dbus_devices: Default::default(), source_device_paths: Default::default(), - target_devices: Default::default(), base, } }) @@ -229,23 +226,42 @@ impl CompositeDevice { PackedStringArray::from(values.as_slice()) } - /// Get the target device types for the composite device (e.g. "keyboard", "mouse", etc.) + /// Get the target devices for the composite device #[func] - pub fn get_target_devices(&self) -> PackedStringArray { + pub fn get_target_devices(&self) -> Array { let Some(proxy) = self.get_proxy() else { - return PackedStringArray::new(); + return array![]; }; - let values: Vec = proxy - .target_devices() - .ok() - .unwrap_or_default() - .into_iter() - .map(GString::from) - .collect(); - PackedStringArray::from(values.as_slice()) + 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 } - /// get the target device types for the composite device (e.g. "keyboard", "mouse", etc.) + /// 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 { diff --git a/extensions/core/src/performance/powerstation/gpu.rs b/extensions/core/src/performance/powerstation/gpu.rs index 3a872582..a79ebbc2 100644 --- a/extensions/core/src/performance/powerstation/gpu.rs +++ b/extensions/core/src/performance/powerstation/gpu.rs @@ -31,7 +31,7 @@ impl Gpu { cards: HashMap::new(), }; - // Discover any CPU cores + // Discover any GPU cards let mut cards = HashMap::new(); if let Some(gpu) = instance.get_proxy() { if let Ok(card_paths) = gpu.enumerate_cards() { @@ -92,12 +92,12 @@ impl Gpu { #[func] pub fn get_cards(&self) -> Array> { - let mut cores = array![]; + let mut cards = array![]; for core in self.cards.values() { - cores.push(core); + cards.push(core); } - cores + cards } /// Dispatches signals