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"); }
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"); } }
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; }
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; }