diff --git a/gdb/inflow.c b/gdb/inflow.c
index 767cfd02c48..9c82306be62 100644
--- a/gdb/inflow.c
+++ b/gdb/inflow.c
@@ -311,67 +311,76 @@ child_terminal_inferior (struct target_ops *self)
   inferior *inf = current_inferior ();
   terminal_info *tinfo = get_inflow_inferior_data (inf);
 
-  if (gdb_has_a_terminal ()
-      && tinfo->ttystate != NULL
-      && sharing_input_terminal (inf))
-    {
-      int result;
+  if (!(gdb_has_a_terminal ()
+	&& tinfo->ttystate != NULL
+	&& sharing_input_terminal (inf)))
+    return;
 
-      /* Ignore SIGTTOU since it will happen when we try to set the
-	 terminal's state (if gdb_tty_state is currently
-	 ours_for_output).  */
-      scoped_ignore_sigttou ignore_sigttou;
+  /* Note that the old state will no longer be active.   */
+  gdb::observers::terminal_owner_changed.notify (gdb_tty_state, false);
+
+  /* Scope containing scoped_ignore_sigtto.  */
+  {
+    int result;
+
+    /* Ignore SIGTTOU since it will happen when we try to set the
+       terminal's state (if gdb_tty_state is currently
+       ours_for_output).  */
+    scoped_ignore_sigttou ignore_sigttou;
 
 #ifdef F_GETFL
-      result = fcntl (0, F_SETFL, tinfo->tflags);
-      OOPSY ("fcntl F_SETFL");
+    result = fcntl (0, F_SETFL, tinfo->tflags);
+    OOPSY ("fcntl F_SETFL");
 #endif
 
-      result = serial_set_tty_state (stdin_serial, tinfo->ttystate);
-      OOPSY ("setting tty state");
+    result = serial_set_tty_state (stdin_serial, tinfo->ttystate);
+    OOPSY ("setting tty state");
 
-      if (!job_control)
-	{
-	  sigint_ours = install_sigint_handler (SIG_IGN);
+    if (!job_control)
+      {
+	sigint_ours = install_sigint_handler (SIG_IGN);
 #ifdef SIGQUIT
-	  sigquit_ours = signal (SIGQUIT, SIG_IGN);
+	sigquit_ours = signal (SIGQUIT, SIG_IGN);
 #endif
-	}
+      }
 
-      if (job_control)
-	{
+    if (job_control)
+      {
 #ifdef HAVE_TERMIOS_H
-	  /* If we can't tell the inferior's actual process group,
-	     then restore whatever was the foreground pgrp the last
-	     time the inferior was running.  See also comments
-	     describing terminal_state::process_group.  */
+	/* If we can't tell the inferior's actual process group,
+	   then restore whatever was the foreground pgrp the last
+	   time the inferior was running.  See also comments
+	   describing terminal_state::process_group.  */
 #ifdef HAVE_GETPGID
-	  result = tcsetpgrp (0, getpgid (inf->pid));
+	result = tcsetpgrp (0, getpgid (inf->pid));
 #else
-	  result = tcsetpgrp (0, tinfo->process_group);
+	result = tcsetpgrp (0, tinfo->process_group);
 #endif
-	  if (result == -1)
-	    {
+	if (result == -1)
+	  {
 #if 0
-	      /* This fails if either GDB has no controlling terminal,
-		 e.g., running under 'setsid(1)', or if the inferior
-		 is not attached to GDB's controlling terminal.  E.g.,
-		 if it called setsid to create a new session or used
-		 the TIOCNOTTY ioctl, or simply if we've attached to a
-		 process running on another terminal and we couldn't
-		 tell whether it was sharing GDB's terminal (and so
-		 assumed yes).  */
-	      gdb_printf
-		(gdb_stderr,
-		 "[tcsetpgrp failed in child_terminal_inferior: %s]\n",
-		 safe_strerror (errno));
+	    /* This fails if either GDB has no controlling terminal,
+	       e.g., running under 'setsid(1)', or if the inferior
+	       is not attached to GDB's controlling terminal.  E.g.,
+	       if it called setsid to create a new session or used
+	       the TIOCNOTTY ioctl, or simply if we've attached to a
+	       process running on another terminal and we couldn't
+	       tell whether it was sharing GDB's terminal (and so
+	       assumed yes).  */
+	    gdb_printf
+	      (gdb_stderr,
+	       "[tcsetpgrp failed in child_terminal_inferior: %s]\n",
+	       safe_strerror (errno));
 #endif
-	    }
+	  }
 #endif
-	}
+      }
 
-      gdb_tty_state = target_terminal_state::is_inferior;
-    }
+    gdb_tty_state = target_terminal_state::is_inferior;
+  }
+
+  /* Note that the new state is active.   */
+  gdb::observers::terminal_owner_changed.notify (gdb_tty_state, true);
 }
 
 /* Put some of our terminal settings into effect,
@@ -447,55 +456,64 @@ child_terminal_ours_1 (target_terminal_state desired_state)
   if (!gdb_has_a_terminal ())
     return;
 
-  if (gdb_tty_state != desired_state)
-    {
-      int result ATTRIBUTE_UNUSED;
+  if (gdb_tty_state == desired_state)
+    return;
 
-      /* Ignore SIGTTOU since it will happen when we try to set the
-	 terminal's pgrp.  */
-      scoped_ignore_sigttou ignore_sigttou;
+  /* Note that the old state will no longer be active.   */
+  gdb::observers::terminal_owner_changed.notify (gdb_tty_state, false);
 
-      /* Set tty state to our_ttystate.  */
-      serial_set_tty_state (stdin_serial, our_terminal_info.ttystate);
+  /* Scope containing scoped_ignore_sigtto.  */
+  {
+    int result ATTRIBUTE_UNUSED;
 
-      /* If we only want output, then leave the inferior's pgrp in the
-	 foreground, so that Ctrl-C/Ctrl-Z reach the inferior
-	 directly.  */
-      if (job_control && desired_state == target_terminal_state::is_ours)
-	{
+    /* Ignore SIGTTOU since it will happen when we try to set the
+       terminal's pgrp.  */
+    scoped_ignore_sigttou ignore_sigttou;
+
+    /* Set tty state to our_ttystate.  */
+    serial_set_tty_state (stdin_serial, our_terminal_info.ttystate);
+
+    /* If we only want output, then leave the inferior's pgrp in the
+       foreground, so that Ctrl-C/Ctrl-Z reach the inferior
+       directly.  */
+    if (job_control && desired_state == target_terminal_state::is_ours)
+      {
 #ifdef HAVE_TERMIOS_H
-	  result = tcsetpgrp (0, our_terminal_info.process_group);
+	result = tcsetpgrp (0, our_terminal_info.process_group);
 #if 0
-	  /* This fails on Ultrix with EINVAL if you run the testsuite
-	     in the background with nohup, and then log out.  GDB never
-	     used to check for an error here, so perhaps there are other
-	     such situations as well.  */
-	  if (result == -1)
-	    gdb_printf (gdb_stderr,
-			"[tcsetpgrp failed in child_terminal_ours: %s]\n",
-			safe_strerror (errno));
+	/* This fails on Ultrix with EINVAL if you run the testsuite
+	   in the background with nohup, and then log out.  GDB never
+	   used to check for an error here, so perhaps there are other
+	   such situations as well.  */
+	if (result == -1)
+	  gdb_printf (gdb_stderr,
+		      "[tcsetpgrp failed in child_terminal_ours: %s]\n",
+		      safe_strerror (errno));
 #endif
 #endif /* termios */
-	}
+      }
 
-      if (!job_control && desired_state == target_terminal_state::is_ours)
-	{
-	  if (sigint_ours.has_value ())
-	    install_sigint_handler (*sigint_ours);
-	  sigint_ours.reset ();
+    if (!job_control && desired_state == target_terminal_state::is_ours)
+      {
+	if (sigint_ours.has_value ())
+	  install_sigint_handler (*sigint_ours);
+	sigint_ours.reset ();
 #ifdef SIGQUIT
-	  if (sigquit_ours.has_value ())
-	    signal (SIGQUIT, *sigquit_ours);
-	  sigquit_ours.reset ();
+	if (sigquit_ours.has_value ())
+	  signal (SIGQUIT, *sigquit_ours);
+	sigquit_ours.reset ();
 #endif
-	}
+      }
 
 #ifdef F_GETFL
-      result = fcntl (0, F_SETFL, our_terminal_info.tflags);
+    result = fcntl (0, F_SETFL, our_terminal_info.tflags);
 #endif
 
-      gdb_tty_state = desired_state;
-    }
+    gdb_tty_state = desired_state;
+  }
+
+  /* Note that the new state is active.   */
+  gdb::observers::terminal_owner_changed.notify (gdb_tty_state, true);
 }
 
 /* Interrupt the inferior.  Implementation of target_interrupt for
diff --git a/gdb/observable.c b/gdb/observable.c
index 82563e3fab4..2ae9f325899 100644
--- a/gdb/observable.c
+++ b/gdb/observable.c
@@ -82,6 +82,7 @@ DEFINE_OBSERVABLE (gdb_exiting);
 DEFINE_OBSERVABLE (connection_removed);
 DEFINE_OBSERVABLE (target_pre_wait);
 DEFINE_OBSERVABLE (target_post_wait);
+DEFINE_OBSERVABLE (terminal_owner_changed);
 
 } /* namespace observers */
 } /* namespace gdb */
diff --git a/gdb/observable.h b/gdb/observable.h
index e4e6f021f1a..9a4144a13ca 100644
--- a/gdb/observable.h
+++ b/gdb/observable.h
@@ -22,6 +22,7 @@
 
 #include "gdbsupport/observable.h"
 #include "target/waitstatus.h"
+#include "target/target.h"
 
 struct bpstat;
 struct so_list;
@@ -276,6 +277,10 @@ extern observable <ptid_t /* ptid */> target_pre_wait;
 /* About to leave target_wait (). */
 extern observable <ptid_t /* event_ptid */> target_post_wait;
 
+/* When the terminal owner changes.  */
+extern observable <target_terminal_state /* gdb_tty_state */, bool /* active */>
+    terminal_owner_changed;
+
 } /* namespace observers */
 
 } /* namespace gdb */
