[gnome-builder: 54/139] libide-terminal: add libide-terminal static library



commit 5554d8f5415d4f8cadb3262714158b09b2a6b104
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jan 9 16:50:07 2019 -0800

    libide-terminal: add libide-terminal static library
    
    This adds the new libide-terminal static library that provides the
    reusable terminal components for plugins.
    
    It also includes some experimental new groundwork for a terminal workspace
    which may prove useful in the future for people who do not want a
    traditional IDE.

 src/libide/terminal/gtk/menus.ui                  |  12 +
 src/libide/terminal/ide-terminal-page-actions.c   | 335 ++++++++++
 src/libide/terminal/ide-terminal-page-actions.h   |  29 +
 src/libide/terminal/ide-terminal-page-private.h   |  66 ++
 src/libide/terminal/ide-terminal-page.c           | 765 ++++++++++++++++++++++
 src/libide/terminal/ide-terminal-page.h           |  45 ++
 src/libide/terminal/ide-terminal-page.ui          |  41 ++
 src/libide/terminal/ide-terminal-search-private.h |   5 +-
 src/libide/terminal/ide-terminal-search.c         |   7 +-
 src/libide/terminal/ide-terminal-search.h         |   7 +-
 src/libide/terminal/ide-terminal-surface.c        |  84 +++
 src/libide/terminal/ide-terminal-surface.h        |  39 ++
 src/libide/terminal/ide-terminal-surface.ui       |  10 +
 src/libide/terminal/ide-terminal-util.c           |  20 +-
 src/libide/terminal/ide-terminal-util.h           |   7 +-
 src/libide/terminal/ide-terminal-workspace.c      |  52 ++
 src/libide/terminal/ide-terminal-workspace.h      |  37 ++
 src/libide/terminal/ide-terminal-workspace.ui     |  33 +
 src/libide/terminal/ide-terminal.c                |   8 +-
 src/libide/terminal/ide-terminal.h                |   8 +-
 src/libide/terminal/libide-terminal.gresource.xml |  12 +
 src/libide/terminal/libide-terminal.h             |  38 ++
 src/libide/terminal/meson.build                   |  94 ++-
 23 files changed, 1720 insertions(+), 34 deletions(-)
---
diff --git a/src/libide/terminal/gtk/menus.ui b/src/libide/terminal/gtk/menus.ui
new file mode 100644
index 000000000..b70d3ad94
--- /dev/null
+++ b/src/libide/terminal/gtk/menus.ui
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <menu id="ide-terminal-workspace-menu">
+    <section id="ide-terminal-workspace-menu-close">
+      <item>
+        <attribute name="id">ide-terminal-workspace-menu-close</attribute>
+        <attribute name="label" translatable="yes">Close</attribute>
+        <attribute name="action">win.close</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/src/libide/terminal/ide-terminal-page-actions.c b/src/libide/terminal/ide-terminal-page-actions.c
new file mode 100644
index 000000000..b4f96b5af
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-page-actions.c
@@ -0,0 +1,335 @@
+/* gb-editor-view-actions.c
+ *
+ * Copyright 2015 Sebastien Lafargue <slafargue gnome org>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-terminal-page"
+
+#include <glib/gi18n.h>
+#include <libide-gui.h>
+#include <libide-terminal.h>
+#include <string.h>
+
+#include "ide-terminal-page-actions.h"
+#include "ide-terminal-page-private.h"
+
+typedef struct
+{
+  VteTerminal    *terminal;
+  GFile          *file;
+  GOutputStream  *stream;
+  gchar          *buffer;
+} SaveTask;
+
+static void
+savetask_free (gpointer data)
+{
+  SaveTask *savetask = (SaveTask *)data;
+
+  if (savetask != NULL)
+    {
+      g_clear_object (&savetask->file);
+      g_clear_object (&savetask->stream);
+      g_clear_object (&savetask->terminal);
+      g_clear_pointer (&savetask->buffer, g_free);
+      g_slice_free (SaveTask, savetask);
+    }
+}
+
+static gboolean
+ide_terminal_page_actions_save_finish (IdeTerminalPage  *view,
+                                      GAsyncResult    *result,
+                                      GError         **error)
+{
+  IdeTask *task = (IdeTask *)result;
+
+  g_return_val_if_fail (ide_task_is_valid (result, view), FALSE);
+
+  g_return_val_if_fail (IDE_IS_TERMINAL_PAGE (view), FALSE);
+  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+  g_return_val_if_fail (IDE_IS_TASK (task), FALSE);
+
+  return ide_task_propagate_boolean (task, error);
+}
+
+static void
+save_worker (IdeTask      *task,
+             gpointer      source_object,
+             gpointer      task_data,
+             GCancellable *cancellable)
+{
+  SaveTask *savetask = (SaveTask *)task_data;
+  g_autoptr(GError) error = NULL;
+  gboolean ret;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_TASK (task));
+  g_assert (IDE_IS_TERMINAL_PAGE (source_object));
+  g_assert (savetask != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  if (savetask->buffer != NULL)
+    {
+      g_autoptr(GInputStream) input_stream = NULL;
+
+      input_stream = g_memory_input_stream_new_from_data (savetask->buffer, -1, NULL);
+      ret = g_output_stream_splice (G_OUTPUT_STREAM (savetask->stream),
+                                    G_INPUT_STREAM (input_stream),
+                                    G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+                                    cancellable,
+                                    &error);
+    }
+  else
+    {
+      ret = vte_terminal_write_contents_sync (savetask->terminal,
+                                              G_OUTPUT_STREAM (savetask->stream),
+                                              VTE_WRITE_DEFAULT,
+                                              cancellable,
+                                              &error);
+    }
+
+  if (ret)
+    ide_task_return_boolean (task, TRUE);
+  else
+    ide_task_return_error (task, g_steal_pointer (&error));
+}
+
+static void
+ide_terminal_page_actions_save_async (IdeTerminalPage       *view,
+                                     VteTerminal          *terminal,
+                                     GFile                *file,
+                                     GAsyncReadyCallback   callback,
+                                     GCancellable         *cancellable,
+                                     gpointer              user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(GFileOutputStream) output_stream = NULL;
+  g_autoptr(GError) error = NULL;
+  SaveTask *savetask;
+
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (view, cancellable, callback, user_data);
+
+  output_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, cancellable, &error);
+
+  if (output_stream != NULL)
+    {
+      savetask = g_slice_new0 (SaveTask);
+      savetask->file = g_object_ref (file);
+      savetask->stream = g_object_ref (G_OUTPUT_STREAM (output_stream));
+      savetask->terminal = g_object_ref (terminal);
+      savetask->buffer = g_steal_pointer (&view->selection_buffer);
+
+      ide_task_set_task_data (task, savetask, savetask_free);
+      save_worker (task, view, savetask, cancellable);
+    }
+  else
+    ide_task_return_error (task, g_steal_pointer (&error));
+}
+
+static void
+save_as_cb (GObject      *object,
+            GAsyncResult *result,
+            gpointer      user_data)
+{
+  IdeTask *task = (IdeTask *)result;
+  IdeTerminalPage *view = user_data;
+  SaveTask *savetask;
+  GFile *file;
+  GError *error = NULL;
+
+  savetask = ide_task_get_task_data (task);
+  file = g_object_ref (savetask->file);
+
+  if (!ide_terminal_page_actions_save_finish (view, result, &error))
+    {
+      g_object_unref (file);
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+    }
+  else
+    {
+      g_clear_object (&view->save_as_file_top);
+      view->save_as_file_top = file;
+    }
+}
+
+static GFile *
+get_last_focused_terminal_file (IdeTerminalPage *view)
+{
+  GFile *file = NULL;
+
+  if (G_IS_FILE (view->save_as_file_top))
+    file = view->save_as_file_top;
+
+  return file;
+}
+
+static VteTerminal *
+get_last_focused_terminal (IdeTerminalPage *view)
+{
+  return VTE_TERMINAL (view->terminal_top);
+}
+
+static gchar *
+gb_terminal_get_selected_text (IdeTerminalPage  *view,
+                               VteTerminal    **terminal_p)
+{
+  VteTerminal *terminal;
+  gchar *buf = NULL;
+
+  terminal = get_last_focused_terminal (view);
+  if (terminal_p != NULL)
+    *terminal_p = terminal;
+
+  if (vte_terminal_get_has_selection (terminal))
+    {
+      vte_terminal_copy_primary (terminal);
+      buf = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
+    }
+
+  return buf;
+}
+
+static void
+save_as_response (GtkWidget *widget,
+                  gint       response,
+                  gpointer   user_data)
+{
+  g_autoptr(IdeTerminalPage) view = user_data;
+  g_autoptr(GFile) file = NULL;
+  GtkFileChooser *chooser = (GtkFileChooser *)widget;
+  VteTerminal *terminal;
+
+  g_assert (GTK_IS_FILE_CHOOSER (chooser));
+  g_assert (IDE_IS_TERMINAL_PAGE (view));
+
+  switch (response)
+    {
+    case GTK_RESPONSE_OK:
+      file = gtk_file_chooser_get_file (chooser);
+      terminal = get_last_focused_terminal (view);
+      ide_terminal_page_actions_save_async (view, terminal, file, save_as_cb, NULL, view);
+      break;
+
+    case GTK_RESPONSE_CANCEL:
+      g_free (view->selection_buffer);
+
+    default:
+      break;
+    }
+
+  gtk_widget_destroy (widget);
+}
+
+static void
+ide_terminal_page_actions_save_as (GSimpleAction *action,
+                                  GVariant      *param,
+                                  gpointer       user_data)
+{
+  IdeTerminalPage *view = user_data;
+  GtkWidget *suggested;
+  GtkWidget *toplevel;
+  GtkWidget *dialog;
+  GFile *file = NULL;
+
+  g_assert (IDE_IS_TERMINAL_PAGE (view));
+
+  /* We can't get this later because the dialog makes the terminal
+   * unfocused and thus resets the selection
+   */
+  view->selection_buffer = gb_terminal_get_selected_text (view, NULL);
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+  dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
+                         "action", GTK_FILE_CHOOSER_ACTION_SAVE,
+                         "do-overwrite-confirmation", TRUE,
+                         "local-only", FALSE,
+                         "modal", TRUE,
+                         "select-multiple", FALSE,
+                         "show-hidden", FALSE,
+                         "transient-for", toplevel,
+                         "title", _("Save Terminal Content As"),
+                         NULL);
+
+  file = get_last_focused_terminal_file (view);
+  if (file != NULL)
+    gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), file, NULL);
+
+  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                          _("Cancel"), GTK_RESPONSE_CANCEL,
+                          _("Save"), GTK_RESPONSE_OK,
+                          NULL);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+  suggested = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+  gtk_style_context_add_class (gtk_widget_get_style_context (suggested),
+                               GTK_STYLE_CLASS_SUGGESTED_ACTION);
+
+  g_signal_connect (dialog, "response", G_CALLBACK (save_as_response), g_object_ref (view));
+
+  gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+ide_terminal_page_actions_reset (GSimpleAction *action,
+                                GVariant      *param,
+                                gpointer       user_data)
+{
+  IdeTerminalPage *self = user_data;
+  VteTerminal *terminal;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  terminal = get_last_focused_terminal (self);
+  vte_terminal_reset (terminal, TRUE, FALSE);
+}
+
+static void
+ide_terminal_page_actions_reset_and_clear (GSimpleAction *action,
+                                          GVariant      *param,
+                                          gpointer       user_data)
+{
+  IdeTerminalPage *self = user_data;
+  VteTerminal *terminal;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  terminal = get_last_focused_terminal (self);
+  vte_terminal_reset (terminal, TRUE, TRUE);
+}
+
+static GActionEntry IdeTerminalPageActions[] = {
+  { "save-as", ide_terminal_page_actions_save_as },
+  { "reset", ide_terminal_page_actions_reset },
+  { "reset-and-clear", ide_terminal_page_actions_reset_and_clear },
+};
+
+void
+ide_terminal_page_actions_init (IdeTerminalPage *self)
+{
+  g_autoptr(GSimpleActionGroup) group = NULL;
+
+  group = g_simple_action_group_new ();
+  g_action_map_add_action_entries (G_ACTION_MAP (group), IdeTerminalPageActions,
+                                   G_N_ELEMENTS (IdeTerminalPageActions), self);
+  gtk_widget_insert_action_group (GTK_WIDGET (self), "terminal-view", G_ACTION_GROUP (group));
+}
diff --git a/src/libide/terminal/ide-terminal-page-actions.h b/src/libide/terminal/ide-terminal-page-actions.h
new file mode 100644
index 000000000..925ed90d8
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-page-actions.h
@@ -0,0 +1,29 @@
+/* ide-terminal-page-actions.h
+ *
+ * opyright (C) 2015 Sebastien Lafargue <slafargue gnome org>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-terminal-page.h"
+
+G_BEGIN_DECLS
+
+void ide_terminal_page_actions_init (IdeTerminalPage *self);
+
+G_END_DECLS
diff --git a/src/libide/terminal/ide-terminal-page-private.h b/src/libide/terminal/ide-terminal-page-private.h
new file mode 100644
index 000000000..8005602e7
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-page-private.h
@@ -0,0 +1,66 @@
+/* ide-terminal-page-private.h
+ *
+ * Copyright 2015 Sebastien Lafargue <slafargue gnome org>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-foundry.h>
+#include <libide-gui.h>
+#include <libide-terminal.h>
+
+G_BEGIN_DECLS
+
+struct _IdeTerminalPage
+{
+  IdePage              parent_instance;
+
+  /*
+   * If we are spawning a process in a runtime instead of the
+   * host, then we will have a runtime pointer here.
+   */
+  IdeRuntime          *runtime;
+
+  GtkOverlay          *terminal_overlay_top;
+
+  GtkRevealer         *search_revealer_top;
+
+  IdeTerminal         *terminal_top;
+
+  GtkScrollbar        *top_scrollbar;
+
+  IdeTerminalSearch   *tsearch;
+
+  GFile               *save_as_file_top;
+
+  gchar               *selection_buffer;
+
+  gchar               *cwd;
+
+  VtePty              *pty;
+
+  gint64               last_respawn;
+
+  guint                manage_spawn : 1;
+  guint                top_has_spawned : 1;
+  guint                top_has_needs_attention : 1;
+  guint                run_on_host : 1;
+  guint                use_runner : 1;
+};
+
+G_END_DECLS
diff --git a/src/libide/terminal/ide-terminal-page.c b/src/libide/terminal/ide-terminal-page.c
new file mode 100644
index 000000000..27ba33298
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-page.c
@@ -0,0 +1,765 @@
+/* ide-terminal-page.c
+ *
+ * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-terminal-page"
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <glib/gi18n.h>
+#include <libide-foundry.h>
+#include <libide-gui.h>
+#include <libide-terminal.h>
+#include <stdlib.h>
+#include <vte/vte.h>
+#include <unistd.h>
+
+#define PCRE2_CODE_UNIT_WIDTH 0
+#include <pcre2.h>
+
+#include "ide-terminal-page.h"
+#include "ide-terminal-page-private.h"
+#include "ide-terminal-page-actions.h"
+
+G_DEFINE_TYPE (IdeTerminalPage, ide_terminal_page, IDE_TYPE_PAGE)
+
+enum {
+  PROP_0,
+  PROP_CWD,
+  PROP_MANAGE_SPAWN,
+  PROP_PTY,
+  PROP_RUNTIME,
+  PROP_RUN_ON_HOST,
+  PROP_USE_RUNNER,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void ide_terminal_page_connect_terminal (IdeTerminalPage *self,
+                                                VteTerminal     *terminal);
+static void gbp_terminal_respawn               (IdeTerminalPage *self,
+                                                VteTerminal     *terminal);
+
+static gboolean
+shell_supports_login (const gchar *shell)
+{
+  g_autofree gchar *name = NULL;
+
+  /* Shells that support --login */
+  static const gchar *supported[] = {
+    "bash",
+  };
+
+  if (shell == NULL)
+    return FALSE;
+
+  if (!(name = g_path_get_basename (shell)))
+    return FALSE;
+
+  for (guint i = 0; i < G_N_ELEMENTS (supported); i++)
+    {
+      if (g_str_equal (name, supported[i]))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+ide_terminal_page_wait_cb (GObject      *object,
+                           GAsyncResult *result,
+                           gpointer      user_data)
+{
+  IdeSubprocess *subprocess = (IdeSubprocess *)object;
+  VteTerminal *terminal = user_data;
+  IdeTerminalPage *self;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (VTE_IS_TERMINAL (terminal));
+
+  if (!ide_subprocess_wait_finish (subprocess, result, &error))
+    {
+      g_warning ("%s", error->message);
+      IDE_GOTO (failure);
+    }
+
+  self = (IdeTerminalPage *)gtk_widget_get_ancestor (GTK_WIDGET (terminal), IDE_TYPE_TERMINAL_PAGE);
+  if (self == NULL)
+    IDE_GOTO (failure);
+
+  if (!dzl_gtk_widget_action (GTK_WIDGET (self), "frame", "close-page", NULL))
+    {
+      if (!gtk_widget_in_destruction (GTK_WIDGET (terminal)))
+        gbp_terminal_respawn (self, terminal);
+    }
+
+failure:
+  g_clear_object (&terminal);
+
+  IDE_EXIT;
+}
+
+static void
+ide_terminal_page_run_cb (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  IdeRunner *runner = (IdeRunner *)object;
+  VteTerminal *terminal = user_data;
+  IdeTerminalPage *self;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_RUNNER (runner));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (VTE_IS_TERMINAL (terminal));
+
+  if (!ide_runner_run_finish (runner, result, &error))
+    {
+      g_warning ("%s", error->message);
+      IDE_GOTO (failure);
+    }
+
+  self = (IdeTerminalPage *)gtk_widget_get_ancestor (GTK_WIDGET (terminal), IDE_TYPE_TERMINAL_PAGE);
+  if (self == NULL)
+    IDE_GOTO (failure);
+
+  if (!dzl_gtk_widget_action (GTK_WIDGET (self), "frame", "close-page", NULL))
+    {
+      if (!gtk_widget_in_destruction (GTK_WIDGET (terminal)))
+        gbp_terminal_respawn (self, terminal);
+    }
+
+failure:
+  ide_object_destroy (IDE_OBJECT (runner));
+  g_clear_object (&terminal);
+
+  IDE_EXIT;
+}
+
+static gboolean
+terminal_has_notification_signal (void)
+{
+  GQuark quark;
+  guint signal_id;
+
+  return g_signal_parse_name ("notification-received",
+                              VTE_TYPE_TERMINAL,
+                              &signal_id,
+                              &quark,
+                              FALSE);
+}
+
+static void
+gbp_terminal_respawn (IdeTerminalPage *self,
+                      VteTerminal     *terminal)
+{
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *workpath = NULL;
+  g_autofree gchar *shell = NULL;
+  IdeBuildPipeline *pipeline = NULL;
+  IdeWorkbench *workbench;
+  IdeContext *context;
+  VtePty *pty = NULL;
+  gint64 now;
+  int tty_fd = -1;
+  gint stdout_fd = -1;
+  gint stderr_fd = -1;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  vte_terminal_reset (terminal, TRUE, TRUE);
+
+  if (!(workbench = ide_widget_get_workbench (GTK_WIDGET (self))))
+    IDE_EXIT;
+
+  /* Prevent flapping */
+  now = g_get_monotonic_time ();
+  if ((now - self->last_respawn) < (G_USEC_PER_SEC / 10))
+    IDE_EXIT;
+  self->last_respawn = now;
+
+  context = ide_widget_get_context (GTK_WIDGET (self));
+  workdir = ide_context_ref_workdir (context);
+  workpath = g_file_get_path (workdir);
+
+  if (ide_workbench_has_project (workbench))
+    {
+      IdeBuildManager *build_manager;
+
+      build_manager = ide_build_manager_from_context (context);
+      pipeline = ide_build_manager_get_pipeline (build_manager);
+    }
+
+  shell = g_strdup (ide_get_user_shell ());
+
+  pty = vte_terminal_pty_new_sync (terminal,
+                                   VTE_PTY_DEFAULT | VTE_PTY_NO_LASTLOG | VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP,
+                                   NULL,
+                                   &error);
+  if (pty == NULL)
+    IDE_GOTO (cleanup);
+
+  vte_terminal_set_pty (terminal, pty);
+
+  if (-1 == (tty_fd = ide_vte_pty_create_slave (pty)))
+    IDE_GOTO (cleanup);
+
+  if (self->runtime != NULL &&
+      !ide_runtime_contains_program_in_path (self->runtime, shell, NULL))
+    {
+      g_free (shell);
+      shell = g_strdup ("/bin/bash");
+    }
+
+  /* they want to use the runner API, which means we spawn in the
+   * program mount namespace, etc.
+   */
+  if (self->runtime != NULL && self->use_runner)
+    {
+      g_autoptr(IdeSimpleBuildTarget) target = NULL;
+      g_autoptr(IdeRunner) runner = NULL;
+      const gchar *argv[] = { shell, NULL };
+
+
+      target = ide_simple_build_target_new (context);
+      ide_simple_build_target_set_argv (target, argv);
+      ide_simple_build_target_set_cwd (target, self->cwd ?: workpath);
+
+      runner = ide_runtime_create_runner (self->runtime, IDE_BUILD_TARGET (target));
+
+      if (runner != NULL)
+        {
+          IdeEnvironment *env = ide_runner_get_environment (runner);
+
+          /* set_tty() will dup() the fd */
+          ide_runner_set_tty (runner, tty_fd);
+
+          ide_environment_setenv (env, "TERM", "xterm-256color");
+          ide_environment_setenv (env, "INSIDE_GNOME_BUILDER", PACKAGE_VERSION);
+          ide_environment_setenv (env, "SHELL", shell);
+
+          if (pipeline != NULL)
+            {
+              ide_environment_setenv (env, "BUILDDIR", ide_build_pipeline_get_builddir (pipeline));
+              ide_environment_setenv (env, "SRCDIR", ide_build_pipeline_get_srcdir (pipeline));
+            }
+
+          ide_runner_run_async (runner,
+                                NULL,
+                                ide_terminal_page_run_cb,
+                                g_object_ref (terminal));
+          IDE_GOTO (cleanup);
+        }
+    }
+
+  /* dup() is safe as it will inherit O_CLOEXEC */
+  if (-1 == (stdout_fd = dup (tty_fd)) || -1 == (stderr_fd = dup (tty_fd)))
+    IDE_GOTO (cleanup);
+
+  if (self->runtime != NULL)
+    launcher = ide_runtime_create_launcher (self->runtime, NULL);
+
+  if (launcher == NULL)
+    launcher = ide_subprocess_launcher_new (0);
+
+  ide_subprocess_launcher_set_flags (launcher, 0);
+  ide_subprocess_launcher_set_run_on_host (launcher, self->run_on_host);
+  ide_subprocess_launcher_set_clear_env (launcher, FALSE);
+  ide_subprocess_launcher_push_argv (launcher, shell);
+  if (shell_supports_login (shell))
+    ide_subprocess_launcher_push_argv (launcher, "--login");
+  ide_subprocess_launcher_take_stdin_fd (launcher, tty_fd);
+  ide_subprocess_launcher_take_stdout_fd (launcher, stdout_fd);
+  ide_subprocess_launcher_take_stderr_fd (launcher, stderr_fd);
+  ide_subprocess_launcher_setenv (launcher, "TERM", "xterm-256color", TRUE);
+  ide_subprocess_launcher_setenv (launcher, "INSIDE_GNOME_BUILDER", PACKAGE_VERSION, TRUE);
+  ide_subprocess_launcher_setenv (launcher, "SHELL", shell, TRUE);
+
+  if (self->cwd != NULL)
+    ide_subprocess_launcher_set_cwd (launcher, self->cwd);
+  else
+    ide_subprocess_launcher_set_cwd (launcher, workpath);
+
+  if (pipeline != NULL)
+    {
+      ide_subprocess_launcher_setenv (launcher, "BUILDDIR", ide_build_pipeline_get_builddir (pipeline), 
TRUE);
+      ide_subprocess_launcher_setenv (launcher, "SRCDIR", ide_build_pipeline_get_srcdir (pipeline), TRUE);
+    }
+
+  tty_fd = -1;
+  stdout_fd = -1;
+  stderr_fd = -1;
+
+  if (NULL == (subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
+    IDE_GOTO (cleanup);
+
+  ide_subprocess_wait_async (subprocess,
+                             NULL,
+                             ide_terminal_page_wait_cb,
+                             g_object_ref (terminal));
+
+cleanup:
+  if (tty_fd != -1)
+    close (tty_fd);
+
+  if (stdout_fd != -1)
+    close (stdout_fd);
+
+  if (stderr_fd != -1)
+    close (stderr_fd);
+
+  g_clear_object (&pty);
+
+  if (error != NULL)
+    g_warning ("%s", error->message);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_terminal_realize (GtkWidget *widget)
+{
+  IdeTerminalPage *self = (IdeTerminalPage *)widget;
+
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  GTK_WIDGET_CLASS (ide_terminal_page_parent_class)->realize (widget);
+
+  if (self->manage_spawn && !self->top_has_spawned)
+    {
+      self->top_has_spawned = TRUE;
+      gbp_terminal_respawn (self, VTE_TERMINAL (self->terminal_top));
+    }
+
+  if (!self->manage_spawn && self->pty != NULL)
+    vte_terminal_set_pty (VTE_TERMINAL (self->terminal_top), self->pty);
+}
+
+static void
+gbp_terminal_get_preferred_width (GtkWidget *widget,
+                                  gint      *min_width,
+                                  gint      *nat_width)
+{
+  /*
+   * Since we are placing the terminal in a GtkStack, we need
+   * to fake the size a bit. Otherwise, GtkStack tries to keep the
+   * widget at its natural size (which prevents us from getting
+   * appropriate size requests.
+   */
+  GTK_WIDGET_CLASS (ide_terminal_page_parent_class)->get_preferred_width (widget, min_width, nat_width);
+  *nat_width = *min_width;
+}
+
+static void
+gbp_terminal_get_preferred_height (GtkWidget *widget,
+                                   gint      *min_height,
+                                   gint      *nat_height)
+{
+  /*
+   * Since we are placing the terminal in a GtkStack, we need
+   * to fake the size a bit. Otherwise, GtkStack tries to keep the
+   * widget at its natural size (which prevents us from getting
+   * appropriate size requests.
+   */
+  GTK_WIDGET_CLASS (ide_terminal_page_parent_class)->get_preferred_height (widget, min_height, nat_height);
+  *nat_height = *min_height;
+}
+
+static void
+gbp_terminal_set_needs_attention (IdeTerminalPage *self,
+                                  gboolean         needs_attention)
+{
+  GtkWidget *parent;
+
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  parent = gtk_widget_get_parent (GTK_WIDGET (self));
+
+  if (GTK_IS_STACK (parent) &&
+      !gtk_widget_in_destruction (GTK_WIDGET (self)) &&
+      !gtk_widget_in_destruction (parent))
+    {
+      if (!gtk_widget_in_destruction (GTK_WIDGET (self->terminal_top)))
+        self->top_has_needs_attention = !!needs_attention;
+
+      gtk_container_child_set (GTK_CONTAINER (parent), GTK_WIDGET (self),
+                               "needs-attention", needs_attention,
+                               NULL);
+    }
+}
+
+static void
+notification_received_cb (VteTerminal     *terminal,
+                          const gchar     *summary,
+                          const gchar     *body,
+                          IdeTerminalPage *self)
+{
+  g_assert (VTE_IS_TERMINAL (terminal));
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  if (!gtk_widget_has_focus (GTK_WIDGET (terminal)))
+    gbp_terminal_set_needs_attention (self, TRUE);
+}
+
+static gboolean
+focus_in_event_cb (VteTerminal     *terminal,
+                   GdkEvent        *event,
+                   IdeTerminalPage *self)
+{
+  g_assert (VTE_IS_TERMINAL (terminal));
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  self->top_has_needs_attention = FALSE;
+  gbp_terminal_set_needs_attention (self, FALSE);
+  gtk_revealer_set_reveal_child (self->search_revealer_top, FALSE);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static void
+window_title_changed_cb (VteTerminal     *terminal,
+                         IdeTerminalPage *self)
+{
+  const gchar *title;
+
+  g_assert (VTE_IS_TERMINAL (terminal));
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  title = vte_terminal_get_window_title (VTE_TERMINAL (self->terminal_top));
+
+  if (title == NULL)
+    title = _("Untitled terminal");
+
+  ide_page_set_title (IDE_PAGE (self), title);
+}
+
+static void
+style_context_changed (GtkStyleContext *style_context,
+                       IdeTerminalPage *self)
+{
+  GtkStateFlags state;
+  GdkRGBA fg;
+  GdkRGBA bg;
+
+  g_assert (GTK_IS_STYLE_CONTEXT (style_context));
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  state = gtk_style_context_get_state (style_context);
+
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+  gtk_style_context_get_color (style_context, state, &fg);
+  gtk_style_context_get_background_color (style_context, state, &bg);
+  G_GNUC_END_IGNORE_DEPRECATIONS;
+
+  if (bg.alpha == 0.0)
+    gdk_rgba_parse (&bg, "#f6f7f8");
+
+  ide_page_set_primary_color_fg (IDE_PAGE (self), &fg);
+  ide_page_set_primary_color_bg (IDE_PAGE (self), &bg);
+}
+
+static IdePage *
+gbp_terminal_create_split (IdePage *page)
+{
+  g_assert (IDE_IS_TERMINAL_PAGE (page));
+
+  return g_object_new (IDE_TYPE_TERMINAL_PAGE,
+                       "visible", TRUE,
+                       NULL);
+}
+
+static void
+gbp_terminal_grab_focus (GtkWidget *widget)
+{
+  IdeTerminalPage *self = (IdeTerminalPage *)widget;
+
+  g_assert (IDE_IS_TERMINAL_PAGE (self));
+
+  gtk_widget_grab_focus (GTK_WIDGET (self->terminal_top));
+}
+
+static void
+ide_terminal_page_connect_terminal (IdeTerminalPage *self,
+                                    VteTerminal     *terminal)
+{
+  GtkAdjustment *vadj;
+
+  vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (terminal));
+
+  gtk_range_set_adjustment (GTK_RANGE (self->top_scrollbar), vadj);
+
+  g_signal_connect_object (terminal,
+                           "focus-in-event",
+                           G_CALLBACK (focus_in_event_cb),
+                           self,
+                           0);
+
+  g_signal_connect_object (terminal,
+                           "window-title-changed",
+                           G_CALLBACK (window_title_changed_cb),
+                           self,
+                           0);
+
+  if (terminal_has_notification_signal ())
+    {
+      g_signal_connect_object (terminal,
+                               "notification-received",
+                               G_CALLBACK (notification_received_cb),
+                               self,
+                               0);
+    }
+}
+
+static void
+ide_terminal_page_finalize (GObject *object)
+{
+  IdeTerminalPage *self = IDE_TERMINAL_PAGE (object);
+
+  g_clear_object (&self->save_as_file_top);
+  g_clear_pointer (&self->cwd, g_free);
+  g_clear_pointer (&self->selection_buffer, g_free);
+  g_clear_object (&self->pty);
+  g_clear_object (&self->runtime);
+
+  G_OBJECT_CLASS (ide_terminal_page_parent_class)->finalize (object);
+}
+
+static void
+ide_terminal_page_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  IdeTerminalPage *self = IDE_TERMINAL_PAGE (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;
+
+    case PROP_RUNTIME:
+      g_value_set_object (value, self->runtime);
+      break;
+
+    case PROP_RUN_ON_HOST:
+      g_value_set_boolean (value, self->run_on_host);
+      break;
+
+    case PROP_USE_RUNNER:
+      g_value_set_boolean (value, self->use_runner);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_terminal_page_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  IdeTerminalPage *self = IDE_TERMINAL_PAGE (object);
+
+  switch (prop_id)
+    {
+    case PROP_CWD:
+      self->cwd = g_value_dup_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;
+
+    case PROP_RUNTIME:
+      self->runtime = g_value_dup_object (value);
+      break;
+
+    case PROP_RUN_ON_HOST:
+      self->run_on_host = g_value_get_boolean (value);
+      break;
+
+    case PROP_USE_RUNNER:
+      self->use_runner = g_value_get_boolean (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_terminal_page_class_init (IdeTerminalPageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  IdePageClass *page_class = IDE_PAGE_CLASS (klass);
+
+  object_class->finalize = ide_terminal_page_finalize;
+  object_class->get_property = ide_terminal_page_get_property;
+  object_class->set_property = ide_terminal_page_set_property;
+
+  widget_class->realize = gbp_terminal_realize;
+  widget_class->get_preferred_width = gbp_terminal_get_preferred_width;
+  widget_class->get_preferred_height = gbp_terminal_get_preferred_height;
+  widget_class->grab_focus = gbp_terminal_grab_focus;
+
+  page_class->create_split = gbp_terminal_create_split;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-terminal/ui/ide-terminal-page.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeTerminalPage, terminal_top);
+  gtk_widget_class_bind_template_child (widget_class, IdeTerminalPage, top_scrollbar);
+  gtk_widget_class_bind_template_child (widget_class, IdeTerminalPage, terminal_overlay_top);
+
+  properties [PROP_CWD] =
+    g_param_spec_string ("cwd",
+                         "CWD",
+                         "The directory to spawn the terminal in",
+                         NULL,
+                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | 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 pseudo terminal to use",
+                         VTE_TYPE_PTY,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_RUNTIME] =
+    g_param_spec_object ("runtime",
+                         "Runtime",
+                         "The runtime to use for spawning",
+                         IDE_TYPE_RUNTIME,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_RUN_ON_HOST] =
+    g_param_spec_boolean ("run-on-host",
+                          "Run on Host",
+                          "If the process should be spawned on the host",
+                          TRUE,
+                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_USE_RUNNER] =
+    g_param_spec_boolean ("use-runner",
+                          "Use Runner",
+                          "If we should use the runner interface and build target",
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_terminal_page_init (IdeTerminalPage *self)
+{
+  GtkStyleContext *style_context;
+
+  self->run_on_host = TRUE;
+  self->manage_spawn = TRUE;
+
+  self->tsearch = g_object_new (IDE_TYPE_TERMINAL_SEARCH,
+                                "visible", TRUE,
+                                NULL);
+  self->search_revealer_top = ide_terminal_search_get_revealer (self->tsearch);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  ide_page_set_icon_name (IDE_PAGE (self), "utilities-terminal-symbolic");
+  ide_page_set_can_split (IDE_PAGE (self), TRUE);
+  ide_page_set_menu_id (IDE_PAGE (self), "terminal-page-document-menu");
+
+  gtk_overlay_add_overlay (self->terminal_overlay_top, GTK_WIDGET (self->tsearch));
+
+  ide_terminal_page_connect_terminal (self, VTE_TERMINAL (self->terminal_top));
+
+  ide_terminal_search_set_terminal (self->tsearch, VTE_TERMINAL (self->terminal_top));
+
+  ide_terminal_page_actions_init (self);
+
+  style_context = gtk_widget_get_style_context (GTK_WIDGET (self->terminal_top));
+  gtk_style_context_add_class (style_context, "terminal");
+  g_signal_connect_object (style_context,
+                           "changed",
+                           G_CALLBACK (style_context_changed),
+                           self,
+                           0);
+  style_context_changed (style_context, self);
+
+  gtk_widget_set_can_focus (GTK_WIDGET (self->terminal_top), TRUE);
+}
+
+void
+ide_terminal_page_set_pty (IdeTerminalPage *self,
+                           VtePty          *pty)
+{
+  g_return_if_fail (IDE_IS_TERMINAL_PAGE (self));
+  g_return_if_fail (VTE_IS_PTY (pty));
+
+  if (self->manage_spawn)
+    {
+      g_warning ("Cannot set pty when IdeTerminalPage manages tty");
+      return;
+    }
+
+  if (self->terminal_top)
+    {
+      vte_terminal_reset (VTE_TERMINAL (self->terminal_top), TRUE, TRUE);
+      vte_terminal_set_pty (VTE_TERMINAL (self->terminal_top), pty);
+    }
+}
+
+void
+ide_terminal_page_feed (IdeTerminalPage *self,
+                        const gchar     *message)
+{
+  g_return_if_fail (IDE_IS_TERMINAL_PAGE (self));
+
+  if (self->terminal_top != NULL)
+    vte_terminal_feed (VTE_TERMINAL (self->terminal_top), message, -1);
+}
diff --git a/src/libide/terminal/ide-terminal-page.h b/src/libide/terminal/ide-terminal-page.h
new file mode 100644
index 000000000..901bc5b05
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-page.h
@@ -0,0 +1,45 @@
+/* ide-terminal-page.h
+ *
+ * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_TERMINAL_INSIDE) && !defined (IDE_TERMINAL_COMPILATION)
+# error "Only <libide-terminal.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+#include <libide-gui.h>
+#include <vte/vte.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TERMINAL_PAGE (ide_terminal_page_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeTerminalPage, ide_terminal_page, IDE, TERMINAL_PAGE, IdePage)
+
+IDE_AVAILABLE_IN_3_32
+void ide_terminal_page_set_pty (IdeTerminalPage *self,
+                                VtePty          *pty);
+IDE_AVAILABLE_IN_3_32
+void ide_terminal_page_feed    (IdeTerminalPage *self,
+                                const gchar     *message);
+
+G_END_DECLS
diff --git a/src/libide/terminal/ide-terminal-page.ui b/src/libide/terminal/ide-terminal-page.ui
new file mode 100644
index 000000000..708915a26
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-page.ui
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.16 -->
+  <template class="IdeTerminalPage" parent="IdePage">
+    <property name="visible">true</property>
+    <child>
+      <object class="GtkPaned" id="paned">
+        <property name="expand">true</property>
+        <property name="orientation">vertical</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkOverlay" id="terminal_overlay_top">
+            <property name="expand">true</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkBox" id="top_container">
+                <property name="orientation">horizontal</property>
+                <property name="expand">true</property>
+                <property name="visible">true</property>
+                <child>
+                  <object class="IdeTerminal" id="terminal_top">
+                    <property name="audible-bell">false</property>
+                    <property name="expand">true</property>
+                    <property name="visible">true</property>
+                    <property name="scrollback-lines">0xffffffff</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkScrollbar" id="top_scrollbar">
+                    <property name="orientation">vertical</property>
+                    <property name="visible">true</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/libide/terminal/ide-terminal-search-private.h 
b/src/libide/terminal/ide-terminal-search-private.h
index 9880f5eb6..2ace9737d 100644
--- a/src/libide/terminal/ide-terminal-search-private.h
+++ b/src/libide/terminal/ide-terminal-search-private.h
@@ -21,8 +21,7 @@
 #pragma once
 
 #include <gtk/gtk.h>
-
-#include "search/ide-tagged-entry.h"
+#include <libide-gui.h>
 
 G_BEGIN_DECLS
 
@@ -33,7 +32,7 @@ struct _IdeTerminalSearch
   VteTerminal         *terminal;
 
   GtkRevealer         *search_revealer;
-  
+
   IdeTaggedEntry      *search_entry;
 
   GtkButton           *search_prev_button;
diff --git a/src/libide/terminal/ide-terminal-search.c b/src/libide/terminal/ide-terminal-search.c
index e0e31bfd7..6eb2f4629 100644
--- a/src/libide/terminal/ide-terminal-search.c
+++ b/src/libide/terminal/ide-terminal-search.c
@@ -25,14 +25,13 @@
 
 #include <fcntl.h>
 #include <glib/gi18n.h>
-#include <ide.h>
 #include <pcre2.h>
 #include <stdlib.h>
 #include <vte/vte.h>
 #include <unistd.h>
 
-#include "terminal/ide-terminal-search.h"
-#include "terminal/ide-terminal-search-private.h"
+#include "ide-terminal-search.h"
+#include "ide-terminal-search-private.h"
 
 G_DEFINE_TYPE (IdeTerminalSearch, ide_terminal_search, GTK_TYPE_BIN)
 
@@ -300,7 +299,7 @@ ide_terminal_search_class_init (IdeTerminalSearchClass *klass)
 
   object_class->get_property = ide_terminal_search_get_property;
 
-  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-terminal-search.ui");
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-terminal/ui/ide-terminal-search.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeTerminalSearch, search_prev_button);
   gtk_widget_class_bind_template_child (widget_class, IdeTerminalSearch, search_next_button);
   gtk_widget_class_bind_template_child (widget_class, IdeTerminalSearch, close_button);
diff --git a/src/libide/terminal/ide-terminal-search.h b/src/libide/terminal/ide-terminal-search.h
index e424f42d3..1081b0d05 100644
--- a/src/libide/terminal/ide-terminal-search.h
+++ b/src/libide/terminal/ide-terminal-search.h
@@ -20,9 +20,12 @@
 
 #pragma once
 
-#include <vte/vte.h>
+#if !defined (IDE_TERMINAL_INSIDE) && !defined (IDE_TERMINAL_COMPILATION)
+# error "Only <libide-terminal.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <vte/vte.h>
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/terminal/ide-terminal-surface.c b/src/libide/terminal/ide-terminal-surface.c
new file mode 100644
index 000000000..fca73db83
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-surface.c
@@ -0,0 +1,84 @@
+/* ide-terminal-surface.c
+ *
+ * Copyright 2018 Christian Hergert <unknown domain org>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-terminal-surface"
+
+#include "config.h"
+
+#include "ide-terminal-page.h"
+#include "ide-terminal-surface.h"
+
+struct _IdeTerminalSurface
+{
+  IdeSurface  parent_instance;
+
+  IdeGrid    *grid;
+};
+
+G_DEFINE_TYPE (IdeTerminalSurface, ide_terminal_surface, IDE_TYPE_SURFACE)
+
+/**
+ * ide_terminal_surface_new:
+ *
+ * Create a new #IdeTerminalSurface.
+ *
+ * Returns: (transfer full): a newly created #IdeTerminalSurface
+ *
+ * Since: 3.32
+ */
+IdeTerminalSurface *
+ide_terminal_surface_new (void)
+{
+  return g_object_new (IDE_TYPE_TERMINAL_SURFACE, NULL);
+}
+
+static void
+ide_terminal_surface_add (GtkContainer *container,
+                          GtkWidget    *child)
+{
+  IdeTerminalSurface *self = (IdeTerminalSurface *)container;
+
+  g_assert (IDE_IS_TERMINAL_SURFACE (self));
+
+  if (IDE_IS_TERMINAL_PAGE (child))
+    gtk_container_add (GTK_CONTAINER (self->grid), child);
+  else
+    GTK_CONTAINER_CLASS (ide_terminal_surface_parent_class)->add (container, child);
+}
+
+static void
+ide_terminal_surface_class_init (IdeTerminalSurfaceClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  container_class->add = ide_terminal_surface_add;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-terminal/ui/ide-terminal-surface.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeTerminalSurface, grid);
+}
+
+static void
+ide_terminal_surface_init (IdeTerminalSurface *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gtk_widget_set_name (GTK_WIDGET (self), "terminal");
+}
diff --git a/src/libide/terminal/ide-terminal-surface.h b/src/libide/terminal/ide-terminal-surface.h
new file mode 100644
index 000000000..4921aef4e
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-surface.h
@@ -0,0 +1,39 @@
+/* ide-terminal-surface.h
+ *
+ * Copyright 2018 Christian Hergert <unknown domain org>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_TERMINAL_INSIDE) && !defined (IDE_TERMINAL_COMPILATION)
+# error "Only <libide-terminal.h> can be included directly."
+#endif
+
+#include <libide-gui.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TERMINAL_SURFACE (ide_terminal_surface_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeTerminalSurface, ide_terminal_surface, IDE, TERMINAL_SURFACE, IdeSurface)
+
+IDE_AVAILABLE_IN_3_32
+IdeTerminalSurface *ide_terminal_surface_new (void);
+
+G_END_DECLS
diff --git a/src/libide/terminal/ide-terminal-surface.ui b/src/libide/terminal/ide-terminal-surface.ui
new file mode 100644
index 000000000..f0110988a
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-surface.ui
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeTerminalSurface" parent="IdeSurface">
+    <child>
+      <object class="IdeGrid" id="grid">
+        <property name="visible">true</property>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/libide/terminal/ide-terminal-util.c b/src/libide/terminal/ide-terminal-util.c
index b775be259..8f53aad66 100644
--- a/src/libide/terminal/ide-terminal-util.c
+++ b/src/libide/terminal/ide-terminal-util.c
@@ -23,15 +23,14 @@
 #include "config.h"
 
 #include <fcntl.h>
+#include <libide-io.h>
+#include <libide-threading.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <vte/vte.h>
 
-#include "subprocess/ide-subprocess.h"
-#include "subprocess/ide-subprocess-launcher.h"
-#include "terminal/ide-terminal-private.h"
-#include "terminal/ide-terminal-util.h"
-#include "util/ptyintercept.h"
+#include "ide-terminal-private.h"
+#include "ide-terminal-util.h"
 
 static const gchar *user_shell = "/bin/sh";
 
@@ -40,13 +39,13 @@ ide_vte_pty_create_slave (VtePty *pty)
 {
   gint master_fd;
 
-  g_return_val_if_fail (VTE_IS_PTY (pty), PTY_FD_INVALID);
+  g_return_val_if_fail (VTE_IS_PTY (pty), IDE_PTY_FD_INVALID);
 
   master_fd = vte_pty_get_fd (pty);
-  if (master_fd == PTY_FD_INVALID)
-    return PTY_FD_INVALID;
+  if (master_fd == IDE_PTY_FD_INVALID)
+    return IDE_PTY_FD_INVALID;
 
-  return pty_intercept_create_slave (master_fd, TRUE);
+  return ide_pty_intercept_create_slave (master_fd, TRUE);
 }
 
 /**
@@ -118,7 +117,8 @@ _ide_guess_shell (void)
 
   if (!g_shell_parse_argv (command, NULL, &argv, &error))
     {
-      g_warning ("Failed to parse command into argv: %s", error->message);
+      g_warning ("Failed to parse command into argv: %s",
+                 error ? error->message : "unknown error");
       return;
     }
 
diff --git a/src/libide/terminal/ide-terminal-util.h b/src/libide/terminal/ide-terminal-util.h
index 1f1ca4d55..a07b35efc 100644
--- a/src/libide/terminal/ide-terminal-util.h
+++ b/src/libide/terminal/ide-terminal-util.h
@@ -20,9 +20,12 @@
 
 #pragma once
 
-#include <vte/vte.h>
+#if !defined (IDE_TERMINAL_INSIDE) && !defined (IDE_TERMINAL_COMPILATION)
+# error "Only <libide-terminal.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <vte/vte.h>
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/terminal/ide-terminal-workspace.c b/src/libide/terminal/ide-terminal-workspace.c
new file mode 100644
index 000000000..ad1019e9b
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-workspace.c
@@ -0,0 +1,52 @@
+/* ide-terminal-workspace.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-terminal-workspace"
+
+#include "config.h"
+
+#include "ide-terminal-workspace.h"
+
+struct _IdeTerminalWorkspace
+{
+  IdeWorkspace  parent_instance;
+
+  IdeHeaderBar *header_bar;
+};
+
+G_DEFINE_TYPE (IdeTerminalWorkspace, ide_terminal_workspace, IDE_TYPE_WORKSPACE)
+
+static void
+ide_terminal_workspace_class_init (IdeTerminalWorkspaceClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  IdeWorkspaceClass *workspace_class = IDE_WORKSPACE_CLASS (klass);
+
+  ide_workspace_class_set_kind (workspace_class, "terminal");
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-terminal/ui/ide-terminal-workspace.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeTerminalWorkspace, header_bar);
+}
+
+static void
+ide_terminal_workspace_init (IdeTerminalWorkspace *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/src/libide/terminal/ide-terminal-workspace.h b/src/libide/terminal/ide-terminal-workspace.h
new file mode 100644
index 000000000..41ac6f5a1
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-workspace.h
@@ -0,0 +1,37 @@
+/* ide-terminal-workspace.h
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_TERMINAL_INSIDE) && !defined (IDE_TERMINAL_COMPILATION)
+# error "Only <libide-terminal.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+#include <libide-gui.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TERMINAL_WORKSPACE (ide_terminal_workspace_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeTerminalWorkspace, ide_terminal_workspace, IDE, TERMINAL_WORKSPACE, IdeWorkspace)
+
+G_END_DECLS
diff --git a/src/libide/terminal/ide-terminal-workspace.ui b/src/libide/terminal/ide-terminal-workspace.ui
new file mode 100644
index 000000000..fc03b508e
--- /dev/null
+++ b/src/libide/terminal/ide-terminal-workspace.ui
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeTerminalWorkspace" parent="IdeWorkspace">
+    <property name="default-width">750</property>
+    <property name="default-height">450</property>
+    <child type="titlebar">
+      <object class="IdeHeaderBar" id="header_bar">
+        <property name="show-close-button">true</property>
+        <property name="show-fullscreen-button">true</property>
+        <property name="menu-id">ide-terminal-workspace-menu</property>
+        <property name="visible">true</property>
+      </object>
+    </child>
+    <child internal-child="surfaces">
+      <object class="GtkStack" id="surfaces">
+        <property name="visible">true</property>
+        <child>
+          <object class="IdeTerminalSurface">
+            <property name="visible">true</property>
+            <child>
+              <object class="IdeTerminalPage">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="name">terminal</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/libide/terminal/ide-terminal.c b/src/libide/terminal/ide-terminal.c
index a8deec3a7..3410a2c4b 100644
--- a/src/libide/terminal/ide-terminal.c
+++ b/src/libide/terminal/ide-terminal.c
@@ -24,9 +24,9 @@
 
 #include <dazzle.h>
 #include <glib/gi18n.h>
-#include <ide.h>
+#include <libide-gui.h>
 
-#include "terminal/ide-terminal.h"
+#include "ide-terminal.h"
 
 #define BUILDER_PCRE2_MULTILINE 0x00000400u
 
@@ -274,7 +274,7 @@ ide_terminal_copy_link_address (IdeTerminal *self)
   g_assert (IDE_IS_TERMINAL (self));
   g_assert (priv->url != NULL);
 
-  if (dzl_str_empty0 (priv->url))
+  if (ide_str_empty0 (priv->url))
     return FALSE;
 
   gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (self), GDK_SELECTION_CLIPBOARD),
@@ -293,7 +293,7 @@ ide_terminal_open_link (IdeTerminal *self)
   g_assert (IDE_IS_TERMINAL (self));
   g_assert (priv->url != NULL);
 
-  if (dzl_str_empty0 (priv->url))
+  if (ide_str_empty0 (priv->url))
     return FALSE;
 
   if (NULL != (app = GTK_APPLICATION (g_application_get_default ())) &&
diff --git a/src/libide/terminal/ide-terminal.h b/src/libide/terminal/ide-terminal.h
index 3a5400e59..c4687e6c2 100644
--- a/src/libide/terminal/ide-terminal.h
+++ b/src/libide/terminal/ide-terminal.h
@@ -20,9 +20,12 @@
 
 #pragma once
 
-#include <vte/vte.h>
+#if !defined (IDE_TERMINAL_INSIDE) && !defined (IDE_TERMINAL_COMPILATION)
+# error "Only <libide-terminal.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <vte/vte.h>
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
@@ -43,6 +46,7 @@ struct _IdeTerminalClass
   gboolean (*open_link)           (IdeTerminal *self);
   gboolean (*copy_link_address)   (IdeTerminal *self);
 
+  /*< private >*/
   gpointer padding[16];
 };
 
diff --git a/src/libide/terminal/libide-terminal.gresource.xml 
b/src/libide/terminal/libide-terminal.gresource.xml
new file mode 100644
index 000000000..a8623ac95
--- /dev/null
+++ b/src/libide/terminal/libide-terminal.gresource.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/libide-terminal">
+    <file preprocess="xml-stripblanks">gtk/menus.ui</file>
+  </gresource>
+  <gresource prefix="/org/gnome/libide-terminal/ui">
+    <file preprocess="xml-stripblanks">ide-terminal-page.ui</file>
+    <file preprocess="xml-stripblanks">ide-terminal-search.ui</file>
+    <file preprocess="xml-stripblanks">ide-terminal-surface.ui</file>
+    <file preprocess="xml-stripblanks">ide-terminal-workspace.ui</file>
+  </gresource>
+</gresources>
diff --git a/src/libide/terminal/libide-terminal.h b/src/libide/terminal/libide-terminal.h
new file mode 100644
index 000000000..37838c104
--- /dev/null
+++ b/src/libide/terminal/libide-terminal.h
@@ -0,0 +1,38 @@
+/* libide-terminal.h
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+#include <libide-io.h>
+#include <libide-gui.h>
+#include <libide-threading.h>
+#include <vte/vte.h>
+
+#define IDE_TERMINAL_INSIDE
+
+#include "ide-terminal.h"
+#include "ide-terminal-page.h"
+#include "ide-terminal-search.h"
+#include "ide-terminal-surface.h"
+#include "ide-terminal-util.h"
+#include "ide-terminal-workspace.h"
+
+#undef IDE_TERMINAL_INSIDE
diff --git a/src/libide/terminal/meson.build b/src/libide/terminal/meson.build
index fca092cce..4ab3a67cd 100644
--- a/src/libide/terminal/meson.build
+++ b/src/libide/terminal/meson.build
@@ -1,16 +1,96 @@
-terminal_headers = [
-  'ide-terminal.h',
+libide_terminal_header_subdir = join_paths(libide_header_subdir, 'terminal')
+libide_include_directories += include_directories('.')
+
+libide_terminal_generated_headers = []
+
+#
+# Public API Headers
+#
+
+libide_terminal_public_headers = [
+  'ide-terminal-page.h',
   'ide-terminal-search.h',
+  'ide-terminal-surface.h',
   'ide-terminal-util.h',
+  'ide-terminal-workspace.h',
+  'ide-terminal.h',
+  'libide-terminal.h',
 ]
 
-terminal_sources = [
-  'ide-terminal.c',
+install_headers(libide_terminal_public_headers, subdir: libide_terminal_header_subdir)
+
+#
+# Sources
+#
+
+libide_terminal_private_headers = [
+  'ide-terminal-page-actions.h',
+  'ide-terminal-page-private.h',
+  'ide-terminal-private.h',
+  'ide-terminal-search-private.h',
+]
+
+libide_terminal_public_sources = [
+  'ide-terminal-page.c',
   'ide-terminal-search.c',
+  'ide-terminal-surface.c',
   'ide-terminal-util.c',
+  'ide-terminal-workspace.c',
+  'ide-terminal.c',
 ]
 
-libide_public_headers += files(terminal_headers)
-libide_public_sources += files(terminal_sources)
+libide_terminal_private_sources = [
+  'ide-terminal-page-actions.c',
+]
+
+#
+# Generated Resource Files
+#
+
+libide_terminal_resources = gnome.compile_resources(
+  'ide-terminal-resources',
+  'libide-terminal.gresource.xml',
+  c_name: 'ide_terminal',
+)
+libide_terminal_generated_headers += [libide_terminal_resources[1]]
+
+
+#
+# Dependencies
+#
+
+libide_terminal_deps = [
+  libgio_dep,
+  libgtk_dep,
+  libdazzle_dep,
+  libvte_dep,
+
+  libide_core_dep,
+  libide_io_dep,
+  libide_threading_dep,
+  libide_gui_dep,
+]
+
+#
+# Library Definitions
+#
+
+libide_terminal = static_library('ide-terminal-' + libide_api_version,
+   libide_terminal_public_sources + libide_terminal_private_sources + [libide_terminal_resources[0]],
+   dependencies: libide_terminal_deps,
+         c_args: libide_args + release_args + ['-DIDE_TERMINAL_COMPILATION'],
+)
+
+libide_terminal_dep = declare_dependency(
+              sources: libide_terminal_generated_headers,
+         dependencies: libide_terminal_deps,
+           link_whole: libide_terminal,
+  include_directories: include_directories('.'),
+)
 
-install_headers(terminal_headers, subdir: join_paths(libide_header_subdir, 'terminal'))
+gnome_builder_public_sources += files(libide_terminal_public_sources)
+gnome_builder_public_headers += files(libide_terminal_public_headers)
+gnome_builder_private_sources += files(libide_terminal_private_sources)
+gnome_builder_private_headers += files(libide_terminal_private_headers)
+gnome_builder_include_subdirs += libide_terminal_header_subdir
+gnome_builder_gir_extra_args += ['--c-include=libide-terminal.h', '-DIDE_TERMINAL_COMPILATION']


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