From 241c3ebee0016878ca4a8f23e54e52d3cf02b2fc Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Mon, 5 Jan 2026 21:14:51 +0200
Subject: [PATCH 55/55] gtk4(x): Fix sentried unit crash on right-click on city
 dialog

Fix similar case also on transport dialog.

They set unit on focus, and non-idle unit cannot be at focus.

Reported by Andrea Maria Marconi

See RM #1868

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 client/control.c                | 12 ++++++++++++
 client/control.h                |  1 +
 client/gui-gtk-4.0/citydlg.c    | 26 ++++++++++++++++++++------
 client/gui-gtk-4.0/unitselect.c |  6 +++++-
 client/gui-gtk-5.0/citydlg.c    | 26 ++++++++++++++++++++------
 client/gui-gtk-5.0/unitselect.c |  6 +++++-
 6 files changed, 63 insertions(+), 14 deletions(-)

diff --git a/client/control.c b/client/control.c
index 36e67041b9..1f1b312783 100644
--- a/client/control.c
+++ b/client/control.c
@@ -495,6 +495,18 @@ void clear_unit_orders(struct unit *punit)
   }
 }
 
+/**********************************************************************//**
+  Try to set unit to focus directly. Won't set unit to focus if
+  unit can't currently be at focus.
+**************************************************************************/
+void unit_focus_try(struct unit *punit)
+{
+  if (punit->activity == ACTIVITY_IDLE) {
+    /* Only idle units can be at focus. */
+    unit_focus_set(punit);
+  }
+}
+
 /**********************************************************************//**
   Sets the focus unit directly. The unit given will be given the
   focus; if NULL the focus will be cleared.
diff --git a/client/control.h b/client/control.h
index 333ce5f345..2188839d9b 100644
--- a/client/control.h
+++ b/client/control.h
@@ -200,6 +200,7 @@ void unit_focus_set_and_select(struct unit *punit);
 void unit_focus_add(struct unit *punit);
 void unit_focus_remove(struct unit *punit);
 void unit_focus_urgent(struct unit *punit);
+void unit_focus_try(struct unit *punit);
 
 void unit_focus_advance(bool accept_current);
 void unit_focus_update(void);
diff --git a/client/gui-gtk-4.0/citydlg.c b/client/gui-gtk-4.0/citydlg.c
index 2905285389..8698bcfbea 100644
--- a/client/gui-gtk-4.0/citydlg.c
+++ b/client/gui-gtk-4.0/citydlg.c
@@ -2786,7 +2786,7 @@ static gboolean middle_present_unit_release(GtkGestureClick *gesture,
       && NULL != (pcity = tile_city(unit_tile(punit)))
       && NULL != (pdialog = get_city_dialog(pcity))
       && can_client_issue_orders()) {
-    unit_focus_set(punit);
+    unit_focus_try(punit);
     close_city_dialog(pdialog);
   }
 
@@ -2809,7 +2809,7 @@ static gboolean middle_supported_unit_release(GtkGestureClick *gesture, int n_pr
       && NULL != (pcity = game_city_by_number(punit->homecity))
       && NULL != (pdialog = get_city_dialog(pcity))
       && can_client_issue_orders()) {
-    unit_focus_set(punit);
+    unit_focus_try(punit);
     close_city_dialog(pdialog);
   }
 
@@ -2827,7 +2827,7 @@ static gboolean right_unit_release(GtkGestureClick *gesture, int n_press,
 
   if (NULL != punit
       && can_client_issue_orders()) {
-    unit_focus_set(punit);
+    unit_focus_try(punit);
   }
 
   return TRUE;
@@ -2873,7 +2873,11 @@ static void unit_activate_callback(GSimpleAction *action, GVariant *parameter,
     player_unit_by_number(client_player(), GPOINTER_TO_INT(data));
 
   if (NULL != punit) {
-    unit_focus_set(punit);
+    /* FIXME: If unit is not idle to begin with, we can only request
+     *        idling, and as we have no server reply yet,
+     *        the unit_focus_try() below will fail. */
+    request_new_unit_activity(punit, ACTIVITY_IDLE);
+    unit_focus_try(punit);
   }
 
   close_citydlg_unit_popover(g_object_get_data(G_OBJECT(action), "dlg"));
@@ -2894,7 +2898,12 @@ static void supported_unit_activate_close_callback(GSimpleAction *action,
     struct city *pcity =
       player_city_by_number(client_player(), punit->homecity);
 
-    unit_focus_set(punit);
+    /* FIXME: If unit is not idle to begin with, we can only request
+     *        idling, and as we have no server reply yet,
+     *        the unit_focus_try() below will fail. */
+    request_new_unit_activity(punit, ACTIVITY_IDLE);
+    unit_focus_try(punit);
+
     if (NULL != pcity) {
       struct city_dialog *pdialog = get_city_dialog(pcity);
 
@@ -2921,7 +2930,12 @@ static void present_unit_activate_close_callback(GSimpleAction *action,
   if (NULL != punit) {
     struct city *pcity = tile_city(unit_tile(punit));
 
-    unit_focus_set(punit);
+    /* FIXME: If unit is not idle to begin with, we can only request
+     *        idling, and as we have no server reply yet,
+     *        the unit_focus_try() below will fail. */
+    request_new_unit_activity(punit, ACTIVITY_IDLE);
+    unit_focus_try(punit);
+
     if (NULL != pcity) {
       struct city_dialog *pdialog = get_city_dialog(pcity);
 
diff --git a/client/gui-gtk-4.0/unitselect.c b/client/gui-gtk-4.0/unitselect.c
index 62c40a8f59..2e24e6fe24 100644
--- a/client/gui-gtk-4.0/unitselect.c
+++ b/client/gui-gtk-4.0/unitselect.c
@@ -1179,7 +1179,11 @@ static void usdlg_cmd_focus_real(GtkTreeView *view)
 
     punit = player_unit_by_number(client_player(), uid);
     if (punit && unit_owner(punit) == client_player()) {
-      unit_focus_set(punit);
+      /* FIXME: If unit is not idle to begin with, we can only request
+       *        idling, and as we have no server reply yet,
+       *        the unit_focus_try() below will fail. */
+      request_new_unit_activity(punit, ACTIVITY_IDLE);
+      unit_focus_try(punit);
       usdlg_destroy();
     }
   }
diff --git a/client/gui-gtk-5.0/citydlg.c b/client/gui-gtk-5.0/citydlg.c
index ca355f8845..85de8d60b7 100644
--- a/client/gui-gtk-5.0/citydlg.c
+++ b/client/gui-gtk-5.0/citydlg.c
@@ -3069,7 +3069,7 @@ static gboolean middle_present_unit_release(GtkGestureClick *gesture,
       && NULL != (pcity = tile_city(unit_tile(punit)))
       && NULL != (pdialog = get_city_dialog(pcity))
       && can_client_issue_orders()) {
-    unit_focus_set(punit);
+    unit_focus_try(punit);
     close_city_dialog(pdialog);
   }
 
@@ -3092,7 +3092,7 @@ static gboolean middle_supported_unit_release(GtkGestureClick *gesture, int n_pr
       && NULL != (pcity = game_city_by_number(punit->homecity))
       && NULL != (pdialog = get_city_dialog(pcity))
       && can_client_issue_orders()) {
-    unit_focus_set(punit);
+    unit_focus_try(punit);
     close_city_dialog(pdialog);
   }
 
@@ -3110,7 +3110,7 @@ static gboolean right_unit_release(GtkGestureClick *gesture, int n_press,
 
   if (NULL != punit
       && can_client_issue_orders()) {
-    unit_focus_set(punit);
+    unit_focus_try(punit);
   }
 
   return TRUE;
@@ -3156,7 +3156,11 @@ static void unit_activate_callback(GSimpleAction *action, GVariant *parameter,
     player_unit_by_number(client_player(), GPOINTER_TO_INT(data));
 
   if (NULL != punit) {
-    unit_focus_set(punit);
+    /* FIXME: If unit is not idle to begin with, we can only request
+     *        idling, and as we have no server reply yet,
+     *        the unit_focus_try() below will fail. */
+    request_new_unit_activity(punit, ACTIVITY_IDLE);
+    unit_focus_try(punit);
   }
 
   close_citydlg_unit_popover(g_object_get_data(G_OBJECT(action), "dlg"));
@@ -3177,7 +3181,12 @@ static void supported_unit_activate_close_callback(GSimpleAction *action,
     struct city *pcity =
       player_city_by_number(client_player(), punit->homecity);
 
-    unit_focus_set(punit);
+    /* FIXME: If unit is not idle to begin with, we can only request
+     *        idling, and as we have no server reply yet,
+     *        the unit_focus_try() below will fail. */
+    request_new_unit_activity(punit, ACTIVITY_IDLE);
+    unit_focus_try(punit);
+
     if (NULL != pcity) {
       struct city_dialog *pdialog = get_city_dialog(pcity);
 
@@ -3204,7 +3213,12 @@ static void present_unit_activate_close_callback(GSimpleAction *action,
   if (NULL != punit) {
     struct city *pcity = tile_city(unit_tile(punit));
 
-    unit_focus_set(punit);
+    /* FIXME: If unit is not idle to begin with, we can only request
+     *        idling, and as we have no server reply yet,
+     *        the unit_focus_try() below will fail. */
+    request_new_unit_activity(punit, ACTIVITY_IDLE);
+    unit_focus_try(punit);
+
     if (NULL != pcity) {
       struct city_dialog *pdialog = get_city_dialog(pcity);
 
diff --git a/client/gui-gtk-5.0/unitselect.c b/client/gui-gtk-5.0/unitselect.c
index a5388ee9d3..39fc385d7c 100644
--- a/client/gui-gtk-5.0/unitselect.c
+++ b/client/gui-gtk-5.0/unitselect.c
@@ -1179,7 +1179,11 @@ static void usdlg_cmd_focus_real(GtkTreeView *view)
 
     punit = player_unit_by_number(client_player(), uid);
     if (punit && unit_owner(punit) == client_player()) {
-      unit_focus_set(punit);
+      /* FIXME: If unit is not idle to begin with, we can only request
+       *        idling, and as we have no server reply yet,
+       *        the unit_focus_try() below will fail. */
+      request_new_unit_activity(punit, ACTIVITY_IDLE);
+      unit_focus_try(punit);
       usdlg_destroy();
     }
   }
-- 
2.51.0

