[gnome-builder] terminal: add Run Output panel when running project



commit 68d434956e2cf2a2b5745177f63bfb968dd5f915
Author: Christian Hergert <chergert redhat com>
Date:   Fri Nov 11 21:31:27 2016 -0800

    terminal: add Run Output panel when running project
    
    This repurposes GbTerminalView for yet another view which shows the output
    from running the project. It creates a PTY slave and assigns it to the
    runner when IdeRunManager::run(IdeRunner) is emitted.

 plugins/terminal/Makefile.am                   |    2 +
 plugins/terminal/gb-terminal-util.c            |   59 +++++++++++++
 plugins/terminal/gb-terminal-util.h            |   30 +++++++
 plugins/terminal/gb-terminal-view-private.h    |    4 +-
 plugins/terminal/gb-terminal-view.c            |  102 +++++++++++++++++------
 plugins/terminal/gb-terminal-view.h            |    4 +
 plugins/terminal/gb-terminal-workbench-addin.c |  108 +++++++++++++++++++++++-
 7 files changed, 282 insertions(+), 27 deletions(-)
---
diff --git a/plugins/terminal/Makefile.am b/plugins/terminal/Makefile.am
index 6a33d76..17c2f13 100644
--- a/plugins/terminal/Makefile.am
+++ b/plugins/terminal/Makefile.am
@@ -16,6 +16,8 @@ libterminal_la_SOURCES = \
        gb-terminal-application-addin.h \
        gb-terminal-plugin.c \
        gb-terminal-private.h \
+       gb-terminal-util.c \
+       gb-terminal-util.h \
        gb-terminal-view.c \
        gb-terminal-view.h \
        gb-terminal-view-private.h \
diff --git a/plugins/terminal/gb-terminal-util.c b/plugins/terminal/gb-terminal-util.c
new file mode 100644
index 0000000..303a897
--- /dev/null
+++ b/plugins/terminal/gb-terminal-util.c
@@ -0,0 +1,59 @@
+/* gb-terminal-util.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gb-terminal-util"
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "gb-terminal-util.h"
+
+gint
+gb_vte_pty_create_slave (VtePty *pty)
+{
+  gint master_fd;
+#ifdef HAVE_PTSNAME_R
+  char name[PATH_MAX + 1];
+#else
+  const char *name;
+#endif
+
+  g_assert (VTE_IS_PTY (pty));
+
+  if (-1 == (master_fd = vte_pty_get_fd (pty)))
+    return -1;
+
+  if (grantpt (master_fd) != 0)
+    return -1;
+
+  if (unlockpt (master_fd) != 0)
+    return -1;
+
+#ifdef HAVE_PTSNAME_R
+  if (ptsname_r (master_fd, name, sizeof name - 1) != 0)
+    return -1;
+#else
+  if (NULL == (name = ptsname (master_fd)))
+    return -1;
+#endif
+
+  return open (name, O_RDWR | O_CLOEXEC);
+}
diff --git a/plugins/terminal/gb-terminal-util.h b/plugins/terminal/gb-terminal-util.h
new file mode 100644
index 0000000..e0fb774
--- /dev/null
+++ b/plugins/terminal/gb-terminal-util.h
@@ -0,0 +1,30 @@
+/* gb-terminal-util.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_TERMINAL_UTIL_H
+#define GB_TERMINAL_UTIL_H
+
+#include <vte/vte.h>
+
+G_BEGIN_DECLS
+
+int gb_vte_pty_create_slave (VtePty *pty);
+
+G_END_DECLS
+
+#endif /* GB_TERMINAL_UTIL_H */
diff --git a/plugins/terminal/gb-terminal-view-private.h b/plugins/terminal/gb-terminal-view-private.h
index 7d271e9..775ebdc 100644
--- a/plugins/terminal/gb-terminal-view-private.h
+++ b/plugins/terminal/gb-terminal-view-private.h
@@ -41,12 +41,14 @@ struct _GbTerminalView
 
   GtkWidget           *bottom_container;
 
+  VtePty              *pty;
+
   gint64               last_respawn;
 
+  guint                manage_spawn : 1;
   guint                top_has_spawned : 1;
   guint                bottom_has_spawned : 1;
   guint                bottom_has_focus : 1;
-
   guint                top_has_needs_attention : 1;
   guint                bottom_has_needs_attention : 1;
 };
diff --git a/plugins/terminal/gb-terminal-view.c b/plugins/terminal/gb-terminal-view.c
index d7979e8..42ceb64 100644
--- a/plugins/terminal/gb-terminal-view.c
+++ b/plugins/terminal/gb-terminal-view.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include "gb-terminal.h"
+#include "gb-terminal-util.h"
 #include "gb-terminal-view.h"
 #include "gb-terminal-view-private.h"
 #include "gb-terminal-view-actions.h"
@@ -37,6 +38,8 @@ G_DEFINE_TYPE (GbTerminalView, gb_terminal_view, IDE_TYPE_LAYOUT_VIEW)
 enum {
   PROP_0,
   PROP_FONT_NAME,
+  PROP_MANAGE_SPAWN,
+  PROP_PTY,
   LAST_PROP
 };
 
@@ -191,15 +194,9 @@ gb_terminal_respawn (GbTerminalView *self,
   VtePty *pty = NULL;
   GFile *workdir;
   gint64 now;
-  int master_fd = -1;
   int tty_fd = -1;
   gint stdout_fd = -1;
   gint stderr_fd = -1;
-#ifdef HAVE_PTSNAME_R
-  char name[PATH_MAX + 1];
-#else
-  const char *name;
-#endif
 
   IDE_ENTRY;
 
@@ -250,24 +247,7 @@ gb_terminal_respawn (GbTerminalView *self,
 
   vte_terminal_set_pty (terminal, pty);
 
-  if (-1 == (master_fd = vte_pty_get_fd (pty)))
-    IDE_GOTO (failure);
-
-  if (grantpt (master_fd) != 0)
-    IDE_GOTO (failure);
-
-  if (unlockpt (master_fd) != 0)
-    IDE_GOTO (failure);
-
-#ifdef HAVE_PTSNAME_R
-  if (ptsname_r (master_fd, name, sizeof name - 1) != 0)
-    IDE_GOTO (failure);
-#else
-  if (NULL == (name = ptsname (master_fd)))
-    IDE_GOTO (failure);
-#endif
-
-  if (-1 == (tty_fd = open (name, O_RDWR | O_CLOEXEC)))
+  if (-1 == (tty_fd = gb_vte_pty_create_slave (pty)))
     IDE_GOTO (failure);
 
   /* dup() is safe as it will inherit O_CLOEXEC */
@@ -326,11 +306,14 @@ gb_terminal_realize (GtkWidget *widget)
 
   GTK_WIDGET_CLASS (gb_terminal_view_parent_class)->realize (widget);
 
-  if (!self->top_has_spawned)
+  if (self->manage_spawn && !self->top_has_spawned)
     {
       self->top_has_spawned = TRUE;
       gb_terminal_respawn (self, self->terminal_top);
     }
+
+  if (!self->manage_spawn && self->pty != NULL)
+    vte_terminal_set_pty (self->terminal_top, self->pty);
 }
 
 static void
@@ -691,11 +674,35 @@ gb_terminal_view_finalize (GObject *object)
   g_clear_object (&self->save_as_file_top);
   g_clear_object (&self->save_as_file_bottom);
   g_clear_pointer (&self->selection_buffer, g_free);
+  g_clear_object (&self->pty);
 
   G_OBJECT_CLASS (gb_terminal_view_parent_class)->finalize (object);
 }
 
 static void
+gb_terminal_view_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  GbTerminalView *self = GB_TERMINAL_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_MANAGE_SPAWN:
+      g_value_set_boolean (value, self->manage_spawn);
+      break;
+
+    case PROP_PTY:
+      g_value_set_object (value, self->pty);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
 gb_terminal_view_set_property (GObject      *object,
                                guint         prop_id,
                                const GValue *value,
@@ -709,6 +716,14 @@ gb_terminal_view_set_property (GObject      *object,
       gb_terminal_view_set_font_name (self, g_value_get_string (value));
       break;
 
+    case PROP_MANAGE_SPAWN:
+      self->manage_spawn = g_value_get_boolean (value);
+      break;
+
+    case PROP_PTY:
+      self->pty = g_value_dup_object (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -722,6 +737,7 @@ gb_terminal_view_class_init (GbTerminalViewClass *klass)
   IdeLayoutViewClass *view_class = IDE_LAYOUT_VIEW_CLASS (klass);
 
   object_class->finalize = gb_terminal_view_finalize;
+  object_class->get_property = gb_terminal_view_get_property;
   object_class->set_property = gb_terminal_view_set_property;
 
   widget_class->realize = gb_terminal_realize;
@@ -748,6 +764,20 @@ gb_terminal_view_class_init (GbTerminalViewClass *klass)
                          NULL,
                          (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_MANAGE_SPAWN] =
+    g_param_spec_boolean ("manage-spawn",
+                          "Manage Spawn",
+                          "Manage Spawn",
+                          TRUE,
+                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_PTY] =
+    g_param_spec_object ("pty",
+                         "Pty",
+                         "The psuedo terminal to use",
+                         VTE_TYPE_PTY,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_properties (object_class, LAST_PROP, properties);
 
   g_type_ensure (GB_TYPE_TERMINAL);
@@ -759,6 +789,8 @@ gb_terminal_view_init (GbTerminalView *self)
   GtkStyleContext *style_context;
   g_autoptr(GSettings) settings = NULL;
 
+  self->manage_spawn = TRUE;
+
   gtk_widget_init_template (GTK_WIDGET (self));
 
   gb_terminal_view_connect_terminal (self, self->terminal_top);
@@ -778,3 +810,23 @@ gb_terminal_view_init (GbTerminalView *self)
 
   gtk_widget_set_can_focus (GTK_WIDGET (self->terminal_top), TRUE);
 }
+
+void
+gb_terminal_view_set_pty (GbTerminalView *self,
+                          VtePty         *pty)
+{
+  g_return_if_fail (GB_IS_TERMINAL_VIEW (self));
+  g_return_if_fail (VTE_IS_PTY (pty));
+
+  if (self->manage_spawn)
+    {
+      g_warning ("Cannot set pty when GbTerminalView manages tty");
+      return;
+    }
+
+  if (self->terminal_top)
+    {
+      vte_terminal_reset (self->terminal_top, TRUE, TRUE);
+      vte_terminal_set_pty (self->terminal_top, pty);
+    }
+}
diff --git a/plugins/terminal/gb-terminal-view.h b/plugins/terminal/gb-terminal-view.h
index 5b8e7a3..b4169da 100644
--- a/plugins/terminal/gb-terminal-view.h
+++ b/plugins/terminal/gb-terminal-view.h
@@ -20,6 +20,7 @@
 #define GB_TERMINAL_VIEW_H
 
 #include <ide.h>
+#include <vte/vte.h>
 
 G_BEGIN_DECLS
 
@@ -27,6 +28,9 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbTerminalView, gb_terminal_view, GB, TERMINAL_VIEW, IdeLayoutView)
 
+void gb_terminal_view_set_pty (GbTerminalView *self,
+                               VtePty         *pty);
+
 G_END_DECLS
 
 #endif /* GB_TERMINAL_VIEW_H */
diff --git a/plugins/terminal/gb-terminal-workbench-addin.c b/plugins/terminal/gb-terminal-workbench-addin.c
index 8b0a412..c970857 100644
--- a/plugins/terminal/gb-terminal-workbench-addin.c
+++ b/plugins/terminal/gb-terminal-workbench-addin.c
@@ -16,9 +16,15 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define G_LOG_DOMAIN "gb-terminal-workbench-addin"
+
+#include "config.h"
+
 #include <glib/gi18n.h>
 #include <ide.h>
+#include <vte/vte.h>
 
+#include "gb-terminal-util.h"
 #include "gb-terminal-view.h"
 #include "gb-terminal-workbench-addin.h"
 
@@ -30,6 +36,9 @@ struct _GbTerminalWorkbenchAddin
 
   GbTerminalView *panel_terminal;
   GtkWidget      *panel_dock_widget;
+
+  GbTerminalView *run_terminal;
+  GtkWidget      *run_panel;
 };
 
 static void workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface);
@@ -62,17 +71,101 @@ new_terminal_activate_cb (GSimpleAction            *action,
 }
 
 static void
+on_run_manager_run (GbTerminalWorkbenchAddin *self,
+                    IdeRunner                *runner,
+                    IdeRunManager            *run_manager)
+{
+  IdeEnvironment *env;
+  VtePty *pty = NULL;
+  int tty_fd;
+
+  IDE_ENTRY;
+
+  g_assert (GB_IS_TERMINAL_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_RUNNER (runner));
+  g_assert (IDE_IS_RUN_MANAGER (run_manager));
+
+  /*
+   * We need to create a new or re-use our existing terminal view
+   * for run output. Additionally, we need to override the stdin,
+   * stdout, and stderr file-descriptors to our pty master for the
+   * terminal instance.
+   */
+
+  pty = vte_pty_new_sync (VTE_PTY_DEFAULT, NULL, NULL);
+
+  if (pty == NULL)
+    {
+      g_warning ("Failed to allocate PTY for run output");
+      IDE_GOTO (failure);
+    }
+
+  if (self->run_terminal == NULL)
+    {
+      IdePerspective *perspective;
+      GbTerminalView *view;
+      GtkWidget *bottom_pane;
+      GtkWidget *panel;
+
+      view = g_object_new (GB_TYPE_TERMINAL_VIEW,
+                           "manage-spawn", FALSE,
+                           "pty", pty,
+                           "visible", TRUE,
+                           NULL);
+      ide_set_weak_pointer (&self->run_terminal, view);
+
+      panel = g_object_new (PNL_TYPE_DOCK_WIDGET,
+                            "child", self->run_terminal,
+                            "expand", TRUE,
+                            "title", _("Run Output"),
+                            "visible", TRUE,
+                            NULL);
+      ide_set_weak_pointer (&self->run_panel, panel);
+
+      perspective = ide_workbench_get_perspective_by_name (self->workbench, "editor");
+      g_assert (IDE_IS_LAYOUT (perspective));
+
+      bottom_pane = pnl_dock_bin_get_bottom_edge (PNL_DOCK_BIN (perspective));
+      gtk_container_add (GTK_CONTAINER (bottom_pane), GTK_WIDGET (self->run_panel));
+    }
+  else
+    {
+      gb_terminal_view_set_pty (self->run_terminal, pty);
+    }
+
+  if (-1 != (tty_fd = gb_vte_pty_create_slave (pty)))
+    {
+      ide_runner_set_tty (runner, tty_fd);
+      close (tty_fd);
+    }
+
+  env = ide_runner_get_environment (runner);
+  ide_environment_setenv (env, "TERM", "xterm-256color");
+  ide_environment_setenv (env, "INSIDE_GNOME_BUILDER", PACKAGE_VERSION);
+
+failure:
+
+  g_clear_object (&pty);
+
+  IDE_EXIT;
+}
+
+static void
 gb_terminal_workbench_addin_load (IdeWorkbenchAddin *addin,
                                   IdeWorkbench      *workbench)
 {
   GbTerminalWorkbenchAddin *self = (GbTerminalWorkbenchAddin *)addin;
   IdePerspective *perspective;
   GtkWidget *bottom_pane;
+  IdeContext *context;
+  IdeRunManager *run_manager;
   g_autoptr(GSimpleAction) action = NULL;
 
   g_assert (GB_IS_TERMINAL_WORKBENCH_ADDIN (self));
   g_assert (IDE_IS_WORKBENCH (workbench));
 
+  context = ide_workbench_get_context (workbench);
+
   ide_set_weak_pointer (&self->workbench, workbench);
 
   action = g_simple_action_new ("new-terminal", NULL);
@@ -107,6 +200,13 @@ gb_terminal_workbench_addin_load (IdeWorkbenchAddin *addin,
 
   bottom_pane = pnl_dock_bin_get_bottom_edge (PNL_DOCK_BIN (perspective));
   gtk_container_add (GTK_CONTAINER (bottom_pane), GTK_WIDGET (self->panel_dock_widget));
+
+  run_manager = ide_context_get_run_manager (context);
+  g_signal_connect_object (run_manager,
+                           "run",
+                           G_CALLBACK (on_run_manager_run),
+                           self,
+                           G_CONNECT_SWAPPED);
 }
 
 static void
@@ -122,7 +222,13 @@ gb_terminal_workbench_addin_unload (IdeWorkbenchAddin *addin,
   if (self->panel_dock_widget != NULL)
     {
       gtk_widget_destroy (self->panel_dock_widget);
-      self->panel_dock_widget = NULL;
+      ide_clear_weak_pointer (&self->panel_dock_widget);
+    }
+
+  if (self->run_panel != NULL)
+    {
+      gtk_widget_destroy (self->run_panel);
+      ide_clear_weak_pointer (&self->run_panel);
     }
 }
 


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