static void
gb_terminal_set_split_view (IdeLayoutView   *view,
                            gboolean         split_view)
{
  GbTerminalView *self = (GbTerminalView *)view;
  GtkStyleContext *style_context;

  g_assert (GB_IS_TERMINAL_VIEW (self));
  g_return_if_fail (GB_IS_TERMINAL_VIEW (self));

  if (split_view && (self->terminal_bottom != NULL))
    return;

  if (!split_view && (self->terminal_bottom == NULL))
    return;

  if (split_view)
    {
      style_context = gtk_widget_get_style_context (GTK_WIDGET (view));

      self->terminal_bottom = g_object_new (GB_TYPE_TERMINAL,
                                            "audible-bell", FALSE,
                                            "scrollback-lines", G_MAXUINT,
                                            "expand", TRUE,
                                            "visible", TRUE,
                                            NULL);
      gtk_container_add_with_properties (GTK_CONTAINER (self->bottom_container),
                                         GTK_WIDGET (self->terminal_bottom),
                                         "position", 0,
                                         NULL);
      gtk_widget_show (self->bottom_container);

      gb_terminal_view_connect_terminal (self, self->terminal_bottom);
      style_context_changed (style_context, GB_TERMINAL_VIEW (view));

      gtk_widget_grab_focus (GTK_WIDGET (self->terminal_bottom));

      if (!self->bottom_has_spawned)
        {
          self->bottom_has_spawned = TRUE;
          gb_terminal_respawn (self, self->terminal_bottom);
        }
    }
  else
    {
      gtk_container_remove (GTK_CONTAINER (self->bottom_container),
                            GTK_WIDGET (self->terminal_bottom));
      gtk_widget_hide (self->bottom_container);

      self->terminal_bottom = NULL;
      self->bottom_has_focus = FALSE;
      self->bottom_has_spawned = FALSE;
      self->bottom_has_needs_attention = FALSE;
      g_clear_object (&self->save_as_file_bottom);
      gtk_widget_grab_focus (GTK_WIDGET (self->terminal_top));
    }
}
static gboolean
focus_in_event_cb (VteTerminal    *terminal,
                   GdkEvent       *event,
                   GbTerminalView *self)
{
  const gchar *title;

  g_assert (VTE_IS_TERMINAL (terminal));
  g_assert (GB_IS_TERMINAL_VIEW (self));

  self->bottom_has_focus = (terminal != self->terminal_top);

  title = gb_terminal_get_title (GB_VIEW (self));
  if (self->document)
    gb_terminal_document_set_title (self->document, title);

  if (terminal == self->terminal_top)
    {
      self->top_has_needs_attention = FALSE;
      gb_terminal_set_needs_attention (self, FALSE, GTK_POS_TOP);
    }
  else if (terminal == self->terminal_bottom)
    {
      self->bottom_has_needs_attention = FALSE;
      gb_terminal_set_needs_attention (self, FALSE, GTK_POS_BOTTOM);
    }

  g_object_notify (G_OBJECT (self), "title");

  return GDK_EVENT_PROPAGATE;
}
static void
gb_terminal_set_needs_attention (GbTerminalView  *self,
                                 gboolean         needs_attention,
                                 GtkPositionType  position)
{
  GtkWidget *parent;

  g_assert (GB_IS_TERMINAL_VIEW (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 (position == GTK_POS_TOP &&
          !gtk_widget_in_destruction (GTK_WIDGET (self->terminal_top)))
        {
          self->top_has_needs_attention = TRUE;
        }
      else if (position == GTK_POS_BOTTOM &&
               self->terminal_bottom != NULL &&
               !gtk_widget_in_destruction (GTK_WIDGET (self->terminal_bottom)))
        {
          self->bottom_has_needs_attention = TRUE;
        }

      gtk_container_child_set (GTK_CONTAINER (parent), GTK_WIDGET (self),
                               "needs-attention",
                               !!(self->top_has_needs_attention || self->bottom_has_needs_attention) &&
                               needs_attention,
                               NULL);
    }
}
static void
style_context_changed (GtkStyleContext *style_context,
                       GbTerminalView  *self)
{
  GtkStateFlags state;
  GdkRGBA fg;
  GdkRGBA bg;

  g_assert (GTK_IS_STYLE_CONTEXT (style_context));
  g_assert (GB_IS_TERMINAL_VIEW (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");
    }

  vte_terminal_set_colors (self->terminal_top, &fg, &bg,
                           solarized_palette,
                           G_N_ELEMENTS (solarized_palette));

  if (self->terminal_bottom)
    vte_terminal_set_colors (self->terminal_bottom, &fg, &bg,
                             solarized_palette,
                             G_N_ELEMENTS (solarized_palette));
}
static GbDocument *
gb_terminal_view_get_document (GbView *view)
{
  g_return_val_if_fail (GB_IS_TERMINAL_VIEW (view), NULL);

  return GB_DOCUMENT (GB_TERMINAL_VIEW (view)->document);
}
static void
size_allocate_cb (VteTerminal    *terminal,
                  GtkAllocation  *alloc,
                  GbTerminalView *self)
{
  glong width;
  glong height;
  glong columns;
  glong rows;

  g_assert (VTE_IS_TERMINAL (terminal));
  g_assert (alloc != NULL);
  g_assert (GB_IS_TERMINAL_VIEW (self));

  if ((alloc->width == 0) || (alloc->height == 0))
    return;

  width = vte_terminal_get_char_width (terminal);
  height = vte_terminal_get_char_height (terminal);

  if ((width == 0) || (height == 0))
    return;

  columns = alloc->width / width;
  rows = alloc->height / height;

  if ((columns < 2) || (rows < 2))
    return;

  vte_terminal_set_size (terminal, columns, rows);
}
static void
gb_terminal_respawn (GbTerminalView *self,
                     VteTerminal    *terminal)
{
  g_autoptr(GPtrArray) args = NULL;
  g_autofree gchar *workpath = NULL;
  GtkWidget *toplevel;
  GError *error = NULL;
  IdeContext *context;
  IdeVcs *vcs;
  GFile *workdir;
  GPid child_pid;
  gint64 now;

  g_assert (GB_IS_TERMINAL_VIEW (self));

  vte_terminal_reset (terminal, TRUE, TRUE);

  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
  if (!IDE_IS_WORKBENCH (toplevel))
    return;

  /* Prevent flapping */
  now = g_get_monotonic_time ();
  if ((now - self->last_respawn) < (G_USEC_PER_SEC / 10))
    return;
  self->last_respawn = now;

  context = ide_workbench_get_context (IDE_WORKBENCH (toplevel));
  vcs = ide_context_get_vcs (context);
  workdir = ide_vcs_get_working_directory (vcs);
  workpath = g_file_get_path (workdir);

  args = g_ptr_array_new_with_free_func (g_free);
  g_ptr_array_add (args, vte_get_user_shell ());
  g_ptr_array_add (args, NULL);

  vte_terminal_spawn_sync (terminal,
                           VTE_PTY_DEFAULT | VTE_PTY_NO_LASTLOG | VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP,
                           workpath,
                           (gchar **)args->pdata,
                           NULL,
                           G_SPAWN_DEFAULT,
                           NULL,
                           NULL,
                           &child_pid,
                           NULL,
                           &error);

  if (error != NULL)
    {
      g_warning ("%s", error->message);
      g_clear_error (&error);
      return;
    }

  vte_terminal_watch_child (terminal, child_pid);
}
static void
window_title_changed_cb (VteTerminal    *terminal,
                         GbTerminalView *self)
{
  g_assert (VTE_IS_TERMINAL (terminal));
  g_assert (GB_IS_TERMINAL_VIEW (self));

  g_object_notify (G_OBJECT (self), "title");
}
Beispiel #9
0
static gboolean
gb_terminal_get_split_view (IdeLayoutView *view)
{
  GbTerminalView *self = (GbTerminalView *)view;

  g_assert (GB_IS_TERMINAL_VIEW (self));

  return (self->terminal_bottom != NULL);
}
static void
gb_terminal_grab_focus (GtkWidget *widget)
{
  GbTerminalView *self = (GbTerminalView *)widget;

  g_assert (GB_IS_TERMINAL_VIEW (self));

  if (self->bottom_has_focus && self->terminal_bottom)
    gtk_widget_grab_focus (GTK_WIDGET (self->terminal_bottom));
  else
    gtk_widget_grab_focus (GTK_WIDGET (self->terminal_top));
}
static IdeLayoutView *
gb_terminal_create_split (IdeLayoutView *view)
{
  IdeLayoutView *new_view;

  g_assert (GB_IS_TERMINAL_VIEW (view));

  new_view = g_object_new (GB_TYPE_TERMINAL_VIEW,
                          "visible", TRUE,
                          NULL);

  return new_view;
}
static void
child_exited_cb (VteTerminal    *terminal,
                 gint            exit_status,
                 GbTerminalView *self)
{
  g_assert (VTE_IS_TERMINAL (terminal));
  g_assert (GB_IS_TERMINAL_VIEW (self));

  if (!ide_widget_action (GTK_WIDGET (self), "view-stack", "close", NULL))
    {
      if (!gtk_widget_in_destruction (GTK_WIDGET (terminal)))
        gb_terminal_respawn (self, terminal);
    }
}
static GbView *
gb_terminal_create_split (GbView *view)
{
  GbView *new_view;

  g_assert (GB_IS_TERMINAL_VIEW (view));

  new_view = g_object_new (GB_TYPE_TERMINAL_VIEW,
                          "document", gb_terminal_view_get_document (view),
                          "visible", TRUE,
                          NULL);

  return new_view;
}
static gchar *
gb_terminal_get_title (IdeLayoutView *view)
{
  const gchar *title = NULL;
  GbTerminalView *self = (GbTerminalView *)view;

  g_assert (GB_IS_TERMINAL_VIEW (self));

  if (self->bottom_has_focus)
    title = vte_terminal_get_window_title (self->terminal_bottom);
  else
    title = vte_terminal_get_window_title (self->terminal_top);

  return g_strdup (title);
}
static const gchar *
gb_terminal_get_title (GbView *view)
{
  const gchar *title;
  GbTerminalView *self = (GbTerminalView *)view;

  g_assert (GB_IS_TERMINAL_VIEW (self));

  if (self->bottom_has_focus)
    title = vte_terminal_get_window_title (self->terminal_bottom);
  else
    title = vte_terminal_get_window_title (self->terminal_top);

  return title;
}
static void
gb_terminal_realize (GtkWidget *widget)
{
  GbTerminalView *self = (GbTerminalView *)widget;

  g_assert (GB_IS_TERMINAL_VIEW (self));

  GTK_WIDGET_CLASS (gb_terminal_view_parent_class)->realize (widget);

  if (!self->top_has_spawned)
    {
      self->top_has_spawned = TRUE;
      gb_terminal_respawn (self, self->terminal_top);
    }
}
static void
notification_received_cb (VteTerminal    *terminal,
                          const gchar    *summary,
                          const gchar    *body,
                          GbTerminalView *self)
{
  g_assert (VTE_IS_TERMINAL (terminal));
  g_assert (GB_IS_TERMINAL_VIEW (self));

  if (!gtk_widget_has_focus (GTK_WIDGET (terminal)))
    {
      if (terminal == self->terminal_top)
        gb_terminal_set_needs_attention (self, TRUE, GTK_POS_TOP);
      else if (terminal == self->terminal_bottom)
        gb_terminal_set_needs_attention (self, TRUE, GTK_POS_BOTTOM);
    }
}
static void
gb_terminal_view_set_document (GbTerminalView     *view,
                               GbTerminalDocument *document)
{
  g_return_if_fail (GB_IS_TERMINAL_VIEW (view));

  if (view->document != document)
    {
      if (view->document)
        g_clear_object (&view->document);

      if (document)
        view->document = g_object_ref (document);

      g_object_notify (G_OBJECT (view), "document");
    }
}
Beispiel #19
0
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);
    }
}
static void
gb_terminal_view_set_font_name (GbTerminalView *self,
                                const gchar    *font_name)
{
  PangoFontDescription *font_desc = NULL;

  g_assert (GB_IS_TERMINAL_VIEW (self));

  if (font_name != NULL)
    font_desc = pango_font_description_from_string (font_name);

  if (font_desc != NULL)
    {
      vte_terminal_set_font (self->terminal_top, font_desc);

      if (self->terminal_bottom)
        vte_terminal_set_font (self->terminal_bottom, font_desc);

      pango_font_description_free (font_desc);
    }
}
static gboolean
focus_in_event_cb (VteTerminal    *terminal,
                   GdkEvent       *event,
                   GbTerminalView *self)
{
  g_assert (VTE_IS_TERMINAL (terminal));
  g_assert (GB_IS_TERMINAL_VIEW (self));

  self->bottom_has_focus = (terminal != self->terminal_top);

  if (terminal == self->terminal_top)
    {
      self->top_has_needs_attention = FALSE;
      gb_terminal_set_needs_attention (self, FALSE, GTK_POS_TOP);
    }
  else if (terminal == self->terminal_bottom)
    {
      self->bottom_has_needs_attention = FALSE;
      gb_terminal_set_needs_attention (self, FALSE, GTK_POS_BOTTOM);
    }

  return GDK_EVENT_PROPAGATE;
}
Beispiel #22
0
static void
gb_terminal_respawn (GbTerminalView *self,
                     VteTerminal    *terminal)
{
  g_autoptr(GPtrArray) args = NULL;
  g_autoptr(IdeSubprocess) subprocess = NULL;
  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
  g_autofree gchar *workpath = NULL;
  g_autofree gchar *shell = NULL;
  GtkWidget *toplevel;
  GError *error = NULL;
  IdeContext *context;
  IdeVcs *vcs;
  VtePty *pty = NULL;
  GFile *workdir;
  gint64 now;
  int tty_fd = -1;
  gint stdout_fd = -1;
  gint stderr_fd = -1;

  IDE_ENTRY;

  g_assert (GB_IS_TERMINAL_VIEW (self));

  vte_terminal_reset (terminal, TRUE, TRUE);

  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
  if (!IDE_IS_WORKBENCH (toplevel))
    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_workbench_get_context (IDE_WORKBENCH (toplevel));
  vcs = ide_context_get_vcs (context);
  workdir = ide_vcs_get_working_directory (vcs);
  workpath = g_file_get_path (workdir);

  shell = gb_terminal_view_discover_shell (NULL, &error);

  if (shell == NULL)
    {
      g_warning ("Failed to discover user shell: %s", error->message);

      /* We prefer bash in flatpak over sh */
      if (ide_is_flatpak ())
        shell = g_strdup ("/bin/bash");
      else
        shell = vte_get_user_shell ();

      g_clear_error (&error);
    }

  args = g_ptr_array_new ();
  g_ptr_array_add (args, (gchar *)shell);
  g_ptr_array_add (args, NULL);

  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 (failure);

  vte_terminal_set_pty (terminal, pty);

  if (-1 == (tty_fd = gb_vte_pty_create_slave (pty)))
    IDE_GOTO (failure);

  /* dup() is safe as it will inherit O_CLOEXEC */
  if (-1 == (stdout_fd = dup (tty_fd)) || -1 == (stderr_fd = dup (tty_fd)))
    IDE_GOTO (failure);

  /* XXX: It would be nice to allow using the runtimes launcher */
  launcher = ide_subprocess_launcher_new (0);
  ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
  ide_subprocess_launcher_set_clear_env (launcher, FALSE);
  ide_subprocess_launcher_set_cwd (launcher, workpath);
  ide_subprocess_launcher_push_args (launcher, (const gchar * const *)args->pdata);
  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);

  tty_fd = -1;
  stdout_fd = -1;
  stderr_fd = -1;

  if (NULL == (subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
    IDE_GOTO (failure);

  ide_subprocess_wait_async (subprocess,
                             NULL,
                             gb_terminal_view_wait_cb,
                             g_object_ref (terminal));

failure:
  if (tty_fd != -1)
    close (tty_fd);

  if (stdout_fd != -1)
    close (stdout_fd);

  g_clear_object (&pty);

  if (error != NULL)
    {
      g_warning ("%s", error->message);
      g_clear_error (&error);
    }

  IDE_EXIT;
}