@@ -128,12 +128,28 @@ func _init() -> void:
128128 self .check_running .call_deferred ()
129129 # If focusable apps has changed and the currently focused app no longer exists,
130130 # remove the manual focus
131- var baselayer_app := _xwayland_primary .baselayer_app
132- to .append (_xwayland_primary .focused_app )
133- if baselayer_app > 0 and not baselayer_app in to :
134- _xwayland_primary .remove_baselayer_app ()
131+ const keep_app_ids := [GamescopeInstance .EXTRA_UNKNOWN_GAME_ID , GamescopeInstance .OVERLAY_GAME_ID ]
132+ var baselayer_apps := _xwayland_primary .baselayer_apps
133+ var new_baselayer_apps := PackedInt64Array ()
134+ for app_id in baselayer_apps :
135+ if app_id in keep_app_ids :
136+ new_baselayer_apps .push_back (app_id )
137+ continue
138+ if app_id in to :
139+ new_baselayer_apps .push_back (app_id )
140+ if new_baselayer_apps != baselayer_apps :
141+ _xwayland_primary .baselayer_apps = new_baselayer_apps
135142 _xwayland_primary .focusable_apps_updated .connect (on_focusable_apps_changed )
136143
144+ # Listen for when focusable windows change
145+ var on_focusable_windows_changed := func (_from : PackedInt64Array , to : PackedInt64Array ):
146+ # If focusable windows has changed and the currently focused window no longer exists,
147+ # remove the manual focus
148+ var baselayer_window := _xwayland_primary .baselayer_window
149+ if baselayer_window > 0 and not baselayer_window in to :
150+ _xwayland_primary .remove_baselayer_window ()
151+ _xwayland_primary .focusable_windows_updated .connect (on_focusable_windows_changed )
152+
137153 # Listen for signals from the secondary Gamescope XWayland
138154 if _xwayland_game :
139155 # Listen for window created/destroyed events
@@ -219,6 +235,29 @@ func launch(app: LibraryLaunchItem) -> RunningApp:
219235 _execute_hooks (app , AppLifecycleHook .TYPE .EXIT )
220236 running_app .state_changed .connect (on_app_state_changed )
221237
238+ # Focus any new windows that get created
239+ var switch_to_new_window := func (old_windows : PackedInt64Array , new_windows : PackedInt64Array ):
240+ for window in new_windows :
241+ if window in old_windows :
242+ continue
243+ running_app .switch_window (window )
244+ break
245+ running_app .window_ids_changed .connect (switch_to_new_window )
246+
247+ # Remove/restore focus if any windows get removed
248+ var remove_focus := func (old_windows : PackedInt64Array , new_windows : PackedInt64Array ):
249+ if not _xwayland_primary :
250+ return
251+ var focused_window := _xwayland_primary .baselayer_window
252+ if not focused_window in old_windows :
253+ return
254+ if new_windows .is_empty ():
255+ _xwayland_primary .remove_baselayer_window ()
256+ return
257+ var new_window := new_windows [0 ]
258+ running_app .switch_window (new_window )
259+ running_app .window_ids_changed .connect (remove_focus )
260+
222261 return running_app
223262
224263
@@ -513,6 +552,7 @@ func _on_app_state_changed(from: RunningApp.STATE, to: RunningApp.STATE, app: Ru
513552 logger .debug ("Currently running apps:" , _running )
514553 if state_machine .has_state (in_game_state ) and _running .size () == 0 :
515554 logger .info ("No more apps are running. Removing in-game state." )
555+ _current_app = null
516556 _xwayland_primary .remove_baselayer_window ()
517557 state_machine .remove_state (in_game_state )
518558 state_machine .remove_state (in_game_menu_state )
@@ -522,6 +562,8 @@ func _on_app_state_changed(from: RunningApp.STATE, to: RunningApp.STATE, app: Ru
522562# Removes the given PID from our list of running apps
523563func _remove_running (app : RunningApp ):
524564 logger .info ("Removing app" , app , "from running apps." )
565+ if app == _current_app :
566+ _current_app = null
525567 _running .erase (app )
526568 _apps_by_name .erase (app .launch_item .name )
527569 _apps_by_pid .erase (app .pid )
@@ -532,20 +574,75 @@ func _remove_running(app: RunningApp):
532574func check_running () -> void :
533575 # Find the root window
534576 if not _xwayland_game :
577+ logger .warn ("No XWayland instance exists to check for running apps" )
535578 return
536579 var root_id := _xwayland_game .root_window_id
537580 if root_id < 0 :
538581 return
582+
583+ # Get all windows and their geometry
539584 var all_windows := _xwayland_game .get_all_windows (root_id )
585+ var all_window_sizes := _xwayland_game .get_window_sizes (all_windows )
586+ logger .trace ("Found windows:" , all_windows )
587+ logger .trace ("Found window sizes:" , all_window_sizes )
588+
589+ # Only consider valid windows of a certain size
590+ var all_valid_windows := PackedInt64Array ()
591+ var i := 0
592+ for window in all_windows :
593+ var size := all_window_sizes [i ]
594+ if size .x > 20 and size .y > 20 :
595+ all_valid_windows .push_back (window )
596+ i += 1
597+ logger .trace ("Found valid windows:" , all_valid_windows )
598+
599+ # Get a list of all running processes
600+ var all_pids := Reaper .get_pids ()
601+
602+ # All OGUI processes should have an OGUI_ID environment variable set. Look
603+ # at every running process and sort them by OGUI_ID.
604+ var ogui_id_to_pids : Dictionary [String , PackedInt64Array ] = {}
605+ for pid in all_pids :
606+ var env := Reaper .get_pid_environment (pid )
607+ if not env .is_empty ():
608+ logger .trace ("Found environment for pid" , pid , ":" , env )
609+ if not "OGUI_ID" in env :
610+ continue
611+ var id := env ["OGUI_ID" ] as String
612+ if not id in ogui_id_to_pids :
613+ ogui_id_to_pids [id ] = PackedInt64Array ()
614+ ogui_id_to_pids [id ].push_back (pid )
615+ if ! ogui_id_to_pids .is_empty ():
616+ logger .debug ("Running processes:" , ogui_id_to_pids )
540617
541618 # Update our view of running processes and what windows they have
542- _update_pids (all_windows )
619+ _update_pids (all_valid_windows )
543620
544621 # Update the state of all running apps
545622 for app in _running :
546- app .update ()
623+ var app_pids := PackedInt64Array ()
624+ if app .ogui_id in ogui_id_to_pids :
625+ app_pids = ogui_id_to_pids [app .ogui_id ]
626+ app .update (all_valid_windows , app_pids )
627+ for app in _running_background :
628+ var app_pids := PackedInt64Array ()
629+ if app .ogui_id in ogui_id_to_pids :
630+ app_pids = ogui_id_to_pids [app .ogui_id ]
631+ app .update (all_valid_windows , app_pids )
632+
633+ # Look for orphan windows
634+ var windows_with_app := PackedInt64Array ()
635+ var orphan_windows := PackedInt64Array ()
636+ for app in _running :
637+ windows_with_app .append_array (app .window_ids .duplicate ())
547638 for app in _running_background :
548- app .update ()
639+ windows_with_app .append_array (app .window_ids .duplicate ())
640+ for window in all_valid_windows :
641+ if window in windows_with_app :
642+ continue
643+ orphan_windows .push_back (window )
644+ if not orphan_windows .is_empty ():
645+ logger .warn ("Found orphan windows:" , orphan_windows )
549646
550647
551648# Updates our mapping of PIDs to Windows. This gives us a good view of what
@@ -717,8 +814,9 @@ func _make_running_app(launch_item: LibraryLaunchItem, pid: int, display: String
717814
718815# Returns the parent app if the focused app is a child of a currently running app.
719816func _get_app_from_running_pid_groups (pid : int ) -> RunningApp :
817+ var pids_with_ogui_id := PackedInt64Array ()
720818 for app in _running :
721- if pid in app .get_child_pids ():
819+ if pid in app .get_child_pids (pids_with_ogui_id ):
722820 return app
723821 return null
724822
0 commit comments