[gnome-terminal/gnome-3-34-ntfy-opn-ttl-ts: 6/16] screen: Track the current foreground process



commit 0d4fbcdae5ff15a0b7b21a35c7278d354cf6c660
Author: Debarshi Ray <debarshir gnome org>
Date:   Thu May 3 16:49:28 2018 +0200

    screen: Track the current foreground process
    
    ... using the interactive shell's precmd and preexec hooks, by
    monitoring the contents of the terminal, and a combination of
    tcgetpgrp(3) and /proc, instead of relying on the shell's history.
    
    The shell's history cannot be reliably used to find out the current
    foreground process. The history can be optionally turned off, and when
    job control is in play, it will mistakenly record 'fg' as the current
    process.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=711059

 src/terminal-screen.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)
---
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index db281cbf..1281a676 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -87,13 +87,17 @@ struct _TerminalScreenPrivate
   GSettings *profile; /* never NULL */
   guint profile_changed_id;
   guint profile_forgotten_id;
+  char *current_cmdline;
   char *initial_working_directory;
   char **initial_env;
   char **override_command;
+  gboolean between_preexec_and_precmd;
   gboolean shell;
   int child_pid;
   GSList *match_tags;
+  guint contents_changed_source_id;
   guint launch_child_source_id;
+  guint shell_preexec_source_id;
 };
 
 enum
@@ -147,6 +151,12 @@ static gboolean terminal_screen_do_exec (TerminalScreen *screen,
 static void terminal_screen_child_exited  (VteTerminal *terminal,
                                            int status);
 
+static void terminal_screen_contents_changed (VteTerminal *terminal);
+
+static void terminal_screen_shell_precmd (VteTerminal *terminal);
+
+static void terminal_screen_shell_preexec (VteTerminal *terminal);
+
 static void terminal_screen_window_title_changed      (VteTerminal *vte_terminal,
                                                        TerminalScreen *screen);
 
@@ -480,6 +490,9 @@ terminal_screen_class_init (TerminalScreenClass *klass)
   widget_class->hierarchy_changed = terminal_screen_hierarchy_changed;
 
   terminal_class->child_exited = terminal_screen_child_exited;
+  terminal_class->contents_changed = terminal_screen_contents_changed;
+  terminal_class->shell_precmd = terminal_screen_shell_precmd;
+  terminal_class->shell_preexec = terminal_screen_shell_preexec;
 
   signals[PROFILE_SET] =
     g_signal_new (I_("profile-set"),
@@ -586,12 +599,24 @@ terminal_screen_dispose (GObject *object)
                                         0, 0, NULL, NULL,
                                         screen);
 
+  if (priv->contents_changed_source_id != 0)
+    {
+      g_source_remove (priv->contents_changed_source_id);
+      priv->contents_changed_source_id = 0;
+    }
+
   if (priv->launch_child_source_id != 0)
     {
       g_source_remove (priv->launch_child_source_id);
       priv->launch_child_source_id = 0;
     }
 
+  if (priv->shell_preexec_source_id != 0)
+    {
+      g_source_remove (priv->shell_preexec_source_id);
+      priv->shell_preexec_source_id = 0;
+    }
+
   G_OBJECT_CLASS (terminal_screen_parent_class)->dispose (object);
 
   /* Unregister *after* chaining up to the parent's dispose,
@@ -617,6 +642,7 @@ terminal_screen_finalize (GObject *object)
 
   terminal_screen_set_profile (screen, NULL);
 
+  g_free (priv->current_cmdline);
   g_free (priv->initial_working_directory);
   g_strfreev (priv->override_command);
   g_strfreev (priv->initial_env);
@@ -1760,6 +1786,106 @@ terminal_screen_child_exited (VteTerminal *terminal,
     }
 }
 
+static gboolean
+terminal_screen_contents_changed_cb (TerminalScreen *screen)
+{
+  TerminalScreenPrivate *priv = screen->priv;
+  gs_free char *cmdline = NULL;
+
+  g_return_val_if_fail (priv->between_preexec_and_precmd, G_SOURCE_REMOVE);
+  g_return_val_if_fail (priv->shell_preexec_source_id == 0, G_SOURCE_REMOVE);
+
+  if (!terminal_screen_has_foreground_process (screen, NULL, &cmdline))
+    goto out;
+
+  if (g_strcmp0 (priv->current_cmdline, cmdline) == 0)
+    goto out;
+
+  priv->current_cmdline = g_steal_pointer (&cmdline);
+
+ out:
+  priv->contents_changed_source_id = 0;
+  return G_SOURCE_REMOVE;
+}
+
+static void
+terminal_screen_contents_changed (VteTerminal *terminal)
+{
+  TerminalScreen *screen = TERMINAL_SCREEN (terminal);
+  TerminalScreenPrivate *priv = screen->priv;
+
+  if (!priv->between_preexec_and_precmd)
+    return;
+
+  if (priv->shell_preexec_source_id != 0)
+    return;
+
+  if (priv->contents_changed_source_id != 0)
+    return;
+
+  priv->contents_changed_source_id = g_timeout_add (500,
+                                                    (GSourceFunc) terminal_screen_contents_changed_cb,
+                                                    screen);
+}
+
+static void
+terminal_screen_shell_precmd (VteTerminal *terminal)
+{
+  TerminalScreen *screen = TERMINAL_SCREEN (terminal);
+  TerminalScreenPrivate *priv = screen->priv;
+
+  priv->between_preexec_and_precmd = FALSE;
+
+  if (priv->contents_changed_source_id != 0)
+    {
+      g_source_remove (priv->contents_changed_source_id);
+      priv->contents_changed_source_id = 0;
+    }
+
+  if (priv->shell_preexec_source_id != 0)
+    {
+      g_source_remove (priv->shell_preexec_source_id);
+      priv->shell_preexec_source_id = 0;
+    }
+
+  g_clear_pointer (&priv->current_cmdline, g_free);
+}
+
+static gboolean
+terminal_screen_shell_preexec_cb (TerminalScreen *screen)
+{
+  TerminalScreenPrivate *priv = screen->priv;
+  gboolean retval = G_SOURCE_CONTINUE;
+  gs_free char *cmdline = NULL;
+
+  g_return_val_if_fail (priv->current_cmdline == NULL, G_SOURCE_REMOVE);
+
+  if (!terminal_screen_has_foreground_process (screen, NULL, &cmdline))
+    goto out;
+
+  priv->current_cmdline = g_steal_pointer (&cmdline);
+
+  priv->shell_preexec_source_id = 0;
+  retval = G_SOURCE_REMOVE;
+
+ out:
+  return retval;
+}
+
+static void
+terminal_screen_shell_preexec (VteTerminal *terminal)
+{
+  TerminalScreen *screen = TERMINAL_SCREEN (terminal);
+  TerminalScreenPrivate *priv = screen->priv;
+
+  g_return_if_fail (priv->contents_changed_source_id == 0);
+  g_return_if_fail (priv->shell_preexec_source_id == 0);
+
+  priv->between_preexec_and_precmd = TRUE;
+
+  priv->shell_preexec_source_id = g_timeout_add (200, (GSourceFunc) terminal_screen_shell_preexec_cb, 
screen);
+}
+
 static void
 terminal_screen_drag_data_received (GtkWidget        *widget,
                                     GdkDragContext   *context,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]