1 /*
2  * Copyright © 2001 Havoc Pennington
3  * Copyright © 2007, 2008, 2010, 2011 Christian Persch
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include "terminal-pcre2.hh"
22 #include "terminal-regex.hh"
23 #include "terminal-screen.hh"
24 #include "terminal-client-utils.hh"
25 
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <uuid.h>
33 
34 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
35 #include <sys/sysctl.h>
36 #endif
37 
38 #include <glib.h>
39 #include <glib/gi18n.h>
40 #include <gio/gio.h>
41 #include <gio/gunixfdlist.h>
42 
43 #include <gtk/gtk.h>
44 
45 #ifdef GDK_WINDOWING_X11
46 #include <gdk/gdkx.h>
47 #endif
48 
49 #include "terminal-accels.hh"
50 #include "terminal-app.hh"
51 #include "terminal-debug.hh"
52 #include "terminal-defines.hh"
53 #include "terminal-enums.hh"
54 #include "terminal-intl.hh"
55 #include "terminal-marshal.h"
56 #include "terminal-schemas.hh"
57 #include "terminal-screen-container.hh"
58 #include "terminal-util.hh"
59 #include "terminal-window.hh"
60 #include "terminal-info-bar.hh"
61 #include "terminal-libgsystem.hh"
62 
63 #include "eggshell.hh"
64 
65 #define URL_MATCH_CURSOR  (GDK_HAND2)
66 
67 typedef struct {
68   volatile int refcount;
69   char **argv; /* as passed */
70   char **exec_argv; /* as processed */
71   char **envv;
72   char *cwd;
73   gboolean as_shell;
74 
75   VtePtyFlags pty_flags;
76   GSpawnFlags spawn_flags;
77 
78   /* FD passing */
79   GUnixFDList *fd_list;
80   int n_fd_map;
81   int* fd_map;
82 
83   /* async exec callback */
84   TerminalScreenExecCallback callback;
85   gpointer callback_data;
86   GDestroyNotify callback_data_destroy_notify;
87 
88   /* Cancellable */
89   GCancellable *cancellable;
90 } ExecData;
91 
92 typedef struct
93 {
94   int tag;
95   TerminalURLFlavor flavor;
96 } TagData;
97 
98 struct _TerminalScreenPrivate
99 {
100   char *uuid;
101   gboolean registered; /* D-Bus interface is registered */
102 
103   GSettings *profile; /* never nullptr */
104   guint profile_changed_id;
105   guint profile_forgotten_id;
106   int child_pid;
107   GSList *match_tags;
108   gboolean exec_on_realize;
109   guint idle_exec_source;
110   ExecData *exec_data;
111 };
112 
113 enum
114 {
115   PROFILE_SET,
116   SHOW_POPUP_MENU,
117   MATCH_CLICKED,
118   CLOSE_SCREEN,
119   LAST_SIGNAL
120 };
121 
122 enum {
123   PROP_0,
124   PROP_PROFILE,
125   PROP_TITLE,
126 };
127 
128 enum
129 {
130   TARGET_COLOR,
131   TARGET_BGIMAGE,
132   TARGET_RESET_BG,
133   TARGET_MOZ_URL,
134   TARGET_NETSCAPE_URL,
135   TARGET_TAB
136 };
137 
138 static void terminal_screen_constructed (GObject             *object);
139 static void terminal_screen_dispose     (GObject             *object);
140 static void terminal_screen_finalize    (GObject             *object);
141 static void terminal_screen_drag_data_received (GtkWidget        *widget,
142                                                 GdkDragContext   *context,
143                                                 gint              x,
144                                                 gint              y,
145                                                 GtkSelectionData *selection_data,
146                                                 guint             info,
147                                                 guint             time);
148 static void terminal_screen_set_font (TerminalScreen *screen);
149 static void terminal_screen_system_font_changed_cb (GSettings *,
150                                                     const char*,
151                                                     TerminalScreen *screen);
152 static gboolean terminal_screen_popup_menu (GtkWidget *widget);
153 static gboolean terminal_screen_button_press (GtkWidget *widget,
154                                               GdkEventButton *event);
155 static void terminal_screen_child_exited  (VteTerminal *terminal,
156                                            int status);
157 
158 static void terminal_screen_window_title_changed      (VteTerminal *vte_terminal,
159                                                        TerminalScreen *screen);
160 
161 static void update_color_scheme                      (TerminalScreen *screen);
162 
163 static char* terminal_screen_check_hyperlink   (TerminalScreen            *screen,
164                                                 GdkEvent                  *event);
165 static void terminal_screen_check_extra (TerminalScreen *screen,
166                                          GdkEvent       *event,
167                                          char           **number_info,
168                                          char           **timestamp_info);
169 static char* terminal_screen_check_match       (TerminalScreen            *screen,
170                                                 GdkEvent                  *event,
171                                                 int                  *flavor);
172 
173 static void terminal_screen_show_info_bar (TerminalScreen *screen,
174                                            GError *error,
175                                            gboolean show_relaunch);
176 
177 
178 static char**terminal_screen_get_child_environment (TerminalScreen *screen,
179                                                     char **initial_envv,
180                                                     char **path,
181                                                     char **shell);
182 
183 static gboolean terminal_screen_get_child_command (TerminalScreen *screen,
184                                                    char          **exec_argv,
185                                                    const char     *path_env,
186                                                    const char     *shell_env,
187                                                    gboolean        shell,
188                                                    gboolean       *preserve_cwd_p,
189                                                    GSpawnFlags    *spawn_flags_p,
190                                                    char         ***argv_p,
191                                                    GError        **err);
192 
193 static void terminal_screen_queue_idle_exec (TerminalScreen *screen);
194 
195 static guint signals[LAST_SIGNAL];
196 
197 typedef struct {
198   const char *pattern;
199   TerminalURLFlavor flavor;
200 } TerminalRegexPattern;
201 
202 static const TerminalRegexPattern url_regex_patterns[] = {
203   { REGEX_URL_AS_IS, FLAVOR_AS_IS },
204   { REGEX_URL_HTTP,  FLAVOR_DEFAULT_TO_HTTP },
205   { REGEX_URL_FILE,  FLAVOR_AS_IS },
206   { REGEX_URL_VOIP,  FLAVOR_VOIP_CALL },
207   { REGEX_EMAIL,     FLAVOR_EMAIL },
208   { REGEX_NEWS_MAN,  FLAVOR_AS_IS },
209 };
210 
211 static const TerminalRegexPattern extra_regex_patterns[] = {
212   { "(0[Xx][[:xdigit:]]+|[[:digit:]]+)", FLAVOR_NUMBER },
213 };
214 
215 static VteRegex **url_regexes;
216 static VteRegex **extra_regexes;
217 static TerminalURLFlavor *url_regex_flavors;
218 static TerminalURLFlavor *extra_regex_flavors;
219 static guint n_url_regexes;
220 static guint n_extra_regexes;
221 
222 /* See bug #697024 */
223 #ifndef __linux__
224 
225 #undef dup3
226 #define dup3 fake_dup3
227 
228 static int
fake_dup3(int fd,int fd2,int flags)229 fake_dup3 (int fd, int fd2, int flags)
230 {
231   if (dup2 (fd, fd2) == -1)
232     return -1;
233 
234   return fcntl (fd2, F_SETFD, flags);
235 }
236 #endif /* !__linux__ */
237 
238 static char*
strv_to_string(char ** strv)239 strv_to_string (char **strv)
240 {
241   return strv ? g_strjoinv (" ", strv) : g_strdup ("(null)");
242 }
243 
244 static char*
exec_data_to_string(ExecData * data)245 exec_data_to_string (ExecData *data)
246 {
247   gs_free char *str1 = nullptr;
248   gs_free char *str2 = nullptr;
249   return data ? g_strdup_printf ("data %p argv:[%s] exec-argv:[%s] envv:%p(%u) as-shell:%s cwd:%s",
250                                  data,
251                                  (str1 = strv_to_string (data->argv)),
252                                  (str2 = strv_to_string (data->exec_argv)),
253                                  data->envv, data->envv ? g_strv_length (data->envv) : 0,
254                                  data->as_shell ? "true" : "false",
255                                  data->cwd)
256   : g_strdup ("(null)");
257 }
258 
259 static ExecData*
exec_data_new(void)260 exec_data_new (void)
261 {
262   ExecData *data = g_new0 (ExecData, 1);
263   data->refcount = 1;
264 
265   return data;
266 }
267 
268 static ExecData *
exec_data_clone(ExecData * data,gboolean preserve_argv)269 exec_data_clone (ExecData *data,
270                  gboolean preserve_argv)
271 {
272   if (data == nullptr)
273     return nullptr;
274 
275   ExecData *clone = exec_data_new ();
276   clone->envv = g_strdupv (data->envv);
277   clone->cwd = g_strdup (data->cwd);
278 
279   /* If FDs were passed, cannot repeat argv. Return data only for env and cwd */
280   if (!preserve_argv ||
281       data->fd_list != nullptr) {
282     clone->as_shell = TRUE;
283     return clone;
284   }
285 
286   clone->argv = g_strdupv (data->argv);
287   clone->as_shell = data->as_shell;
288 
289   return clone;
290 }
291 
292 static void
exec_data_callback(ExecData * data,GError * error,TerminalScreen * screen)293 exec_data_callback (ExecData *data,
294                     GError *error,
295                     TerminalScreen *screen)
296 {
297   if (data->callback)
298     data->callback (screen, error, data->callback_data);
299 }
300 
301 static ExecData*
exec_data_ref(ExecData * data)302 exec_data_ref (ExecData *data)
303 {
304   data->refcount++;
305   return data;
306 }
307 
308 static void
exec_data_unref(ExecData * data)309 exec_data_unref (ExecData *data)
310 {
311   if (data == nullptr)
312     return;
313 
314   if (--data->refcount > 0)
315     return;
316 
317   g_strfreev (data->argv);
318   g_strfreev (data->exec_argv);
319   g_strfreev (data->envv);
320   g_free (data->cwd);
321   g_clear_object (&data->fd_list);
322   g_free (data->fd_map);
323 
324   if (data->callback_data_destroy_notify && data->callback_data)
325     data->callback_data_destroy_notify (data->callback_data);
326 
327   g_clear_object (&data->cancellable);
328 
329   g_free (data);
330 }
331 
GS_DEFINE_CLEANUP_FUNCTION0(ExecData *,_terminal_local_unref_exec_data,exec_data_unref)332 GS_DEFINE_CLEANUP_FUNCTION0(ExecData*, _terminal_local_unref_exec_data, exec_data_unref)
333 #define terminal_unref_exec_data __attribute__((__cleanup__(_terminal_local_unref_exec_data)))
334 
335 static void
336 terminal_screen_clear_exec_data (TerminalScreen *screen,
337                                  gboolean cancelled)
338 {
339   TerminalScreenPrivate *priv = screen->priv;
340 
341   if (priv->exec_data == nullptr)
342     return;
343 
344   if (cancelled) {
345     gs_free_error GError *err = nullptr;
346     g_set_error_literal (&err, G_IO_ERROR, G_IO_ERROR_CANCELLED,
347                          "Spawning was cancelled");
348     exec_data_callback (priv->exec_data, err, screen);
349   }
350 
351   exec_data_unref (priv->exec_data);
352   priv->exec_data = nullptr;
353 }
354 
G_DEFINE_TYPE(TerminalScreen,terminal_screen,VTE_TYPE_TERMINAL)355 G_DEFINE_TYPE (TerminalScreen, terminal_screen, VTE_TYPE_TERMINAL)
356 
357 static void
358 free_tag_data (TagData *tagdata)
359 {
360   g_slice_free (TagData, tagdata);
361 }
362 
363 static void
precompile_regexes(const TerminalRegexPattern * regex_patterns,guint n_regexes,VteRegex *** regexes,TerminalURLFlavor ** regex_flavors)364 precompile_regexes (const TerminalRegexPattern *regex_patterns,
365                     guint n_regexes,
366                     VteRegex ***regexes,
367                     TerminalURLFlavor **regex_flavors)
368 {
369   guint i;
370 
371   *regexes = g_new0 (VteRegex*, n_regexes);
372   *regex_flavors = g_new0 (TerminalURLFlavor, n_regexes);
373 
374   for (i = 0; i < n_regexes; ++i)
375     {
376       GError *error = nullptr;
377 
378       (*regexes)[i] = vte_regex_new_for_match (regex_patterns[i].pattern, -1,
379                                                PCRE2_UTF | PCRE2_NO_UTF_CHECK | PCRE2_UCP | PCRE2_MULTILINE,
380                                                &error);
381       g_assert_no_error (error);
382 
383       if (!vte_regex_jit ((*regexes)[i], PCRE2_JIT_COMPLETE, &error) ||
384           !vte_regex_jit ((*regexes)[i], PCRE2_JIT_PARTIAL_SOFT, &error)) {
385         g_printerr ("Failed to JIT regex '%s': %s\n", regex_patterns[i].pattern, error->message);
386         g_clear_error (&error);
387       }
388 
389       (*regex_flavors)[i] = regex_patterns[i].flavor;
390     }
391 }
392 
393 static void
terminal_screen_class_enable_menu_bar_accel_notify_cb(GSettings * settings,const char * key,TerminalScreenClass * klass)394 terminal_screen_class_enable_menu_bar_accel_notify_cb (GSettings *settings,
395                                                        const char *key,
396                                                        TerminalScreenClass *klass)
397 {
398   static gboolean is_enabled = TRUE; /* the binding is enabled by default since GtkWidgetClass installs it */
399   gboolean enable;
400   GtkBindingSet *binding_set;
401 
402   enable = g_settings_get_boolean (settings, key);
403 
404   /* Only remove the 'skip' entry when we have added it previously! */
405   if (enable == is_enabled)
406     return;
407 
408   is_enabled = enable;
409 
410   binding_set = gtk_binding_set_by_class (klass);
411   if (enable)
412     gtk_binding_entry_remove (binding_set, GDK_KEY_F10, GDK_SHIFT_MASK);
413   else
414     gtk_binding_entry_skip (binding_set, GDK_KEY_F10, GDK_SHIFT_MASK);
415 }
416 
417 static TerminalWindow *
terminal_screen_get_window(TerminalScreen * screen)418 terminal_screen_get_window (TerminalScreen *screen)
419 {
420   GtkWidget *widget = GTK_WIDGET (screen);
421   GtkWidget *toplevel;
422 
423   toplevel = gtk_widget_get_toplevel (widget);
424   if (!gtk_widget_is_toplevel (toplevel))
425     return nullptr;
426 
427   return TERMINAL_WINDOW (toplevel);
428 }
429 
430 static void
terminal_screen_realize(GtkWidget * widget)431 terminal_screen_realize (GtkWidget *widget)
432 {
433   TerminalScreen *screen = TERMINAL_SCREEN (widget);
434 
435   GTK_WIDGET_CLASS (terminal_screen_parent_class)->realize (widget);
436 
437   terminal_screen_set_font (screen);
438 
439   TerminalScreenPrivate *priv = screen->priv;
440   if (priv->exec_on_realize)
441     terminal_screen_queue_idle_exec (screen);
442 
443   priv->exec_on_realize = FALSE;
444 
445 }
446 
447 static void
terminal_screen_update_style(TerminalScreen * screen)448 terminal_screen_update_style (TerminalScreen *screen)
449 {
450   update_color_scheme (screen);
451   terminal_screen_set_font (screen);
452 }
453 
454 static void
terminal_screen_style_updated(GtkWidget * widget)455 terminal_screen_style_updated (GtkWidget *widget)
456 {
457   TerminalScreen *screen = TERMINAL_SCREEN (widget);
458 
459   GTK_WIDGET_CLASS (terminal_screen_parent_class)->style_updated (widget);
460 
461   terminal_screen_update_style (screen);
462 }
463 
464 #ifdef ENABLE_DEBUG
465 static void
size_request(GtkWidget * widget,GtkRequisition * req)466 size_request (GtkWidget *widget,
467               GtkRequisition *req)
468 {
469   _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
470                          "[screen %p] size-request %d : %d\n",
471                          widget, req->width, req->height);
472 }
473 
474 static void
size_allocate(GtkWidget * widget,GtkAllocation * allocation)475 size_allocate (GtkWidget *widget,
476                GtkAllocation *allocation)
477 {
478   _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
479                          "[screen %p] size-alloc   %d : %d at (%d, %d)\n",
480                          widget, allocation->width, allocation->height, allocation->x, allocation->y);
481 }
482 #endif
483 
484 static void
terminal_screen_init(TerminalScreen * screen)485 terminal_screen_init (TerminalScreen *screen)
486 {
487   const GtkTargetEntry target_table[] = {
488     { (char *) "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, TARGET_TAB },
489     { (char *) "application/x-color", 0, TARGET_COLOR },
490     { (char *) "x-special/gnome-reset-background", 0, TARGET_RESET_BG },
491     { (char *) "text/x-moz-url",  0, TARGET_MOZ_URL },
492     { (char *) "_NETSCAPE_URL", 0, TARGET_NETSCAPE_URL }
493   };
494   VteTerminal *terminal = VTE_TERMINAL (screen);
495   TerminalScreenPrivate *priv;
496   TerminalApp *app;
497   GtkTargetList *target_list;
498   GtkTargetEntry *targets;
499   int n_targets;
500   guint i;
501   uuid_t u;
502   char uuidstr[37];
503 
504   priv = screen->priv = G_TYPE_INSTANCE_GET_PRIVATE (screen, TERMINAL_TYPE_SCREEN, TerminalScreenPrivate);
505 
506   uuid_generate (u);
507   uuid_unparse (u, uuidstr);
508   priv->uuid = g_strdup (uuidstr);
509 
510   vte_terminal_set_mouse_autohide (terminal, TRUE);
511 
512   priv->child_pid = -1;
513 
514   vte_terminal_set_allow_hyperlink (terminal, TRUE);
515 
516   for (i = 0; i < n_url_regexes; ++i)
517     {
518       TagData *tag_data;
519 
520       tag_data = g_slice_new (TagData);
521       tag_data->flavor = url_regex_flavors[i];
522       tag_data->tag = vte_terminal_match_add_regex (terminal, url_regexes[i], 0);
523       vte_terminal_match_set_cursor_type (terminal, tag_data->tag, URL_MATCH_CURSOR);
524 
525       priv->match_tags = g_slist_prepend (priv->match_tags, tag_data);
526     }
527 
528   /* Setup DND */
529   target_list = gtk_target_list_new (nullptr, 0);
530   gtk_target_list_add_uri_targets (target_list, 0);
531   gtk_target_list_add_text_targets (target_list, 0);
532   gtk_target_list_add_table (target_list, target_table, G_N_ELEMENTS (target_table));
533 
534   targets = gtk_target_table_new_from_list (target_list, &n_targets);
535 
536   gtk_drag_dest_set (GTK_WIDGET (screen),
537                      GtkDestDefaults(GTK_DEST_DEFAULT_MOTION |
538 				     GTK_DEST_DEFAULT_HIGHLIGHT |
539 				     GTK_DEST_DEFAULT_DROP),
540                      targets, n_targets,
541                      GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
542 
543   gtk_target_table_free (targets, n_targets);
544   gtk_target_list_unref (target_list);
545 
546   g_signal_connect (screen, "window-title-changed",
547                     G_CALLBACK (terminal_screen_window_title_changed),
548                     screen);
549 
550   app = terminal_app_get ();
551   g_signal_connect (terminal_app_get_desktop_interface_settings (app), "changed::" MONOSPACE_FONT_KEY_NAME,
552                     G_CALLBACK (terminal_screen_system_font_changed_cb), screen);
553 
554 #ifdef ENABLE_DEBUG
555   _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_GEOMETRY)
556     {
557       g_signal_connect_after (screen, "size-request", G_CALLBACK (size_request), nullptr);
558       g_signal_connect_after (screen, "size-allocate", G_CALLBACK (size_allocate), nullptr);
559     }
560 #endif
561 }
562 
563 static void
terminal_screen_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)564 terminal_screen_get_property (GObject *object,
565                               guint prop_id,
566                               GValue *value,
567                               GParamSpec *pspec)
568 {
569   TerminalScreen *screen = TERMINAL_SCREEN (object);
570 
571   switch (prop_id)
572     {
573       case PROP_PROFILE:
574         g_value_set_object (value, terminal_screen_get_profile (screen));
575         break;
576       case PROP_TITLE:
577         g_value_set_string (value, terminal_screen_get_title (screen));
578         break;
579       default:
580         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
581         break;
582     }
583 }
584 
585 static void
terminal_screen_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)586 terminal_screen_set_property (GObject *object,
587                               guint prop_id,
588                               const GValue *value,
589                               GParamSpec *pspec)
590 {
591   TerminalScreen *screen = TERMINAL_SCREEN (object);
592 
593   switch (prop_id)
594     {
595       case PROP_PROFILE:
596         terminal_screen_set_profile (screen, (GSettings*)g_value_get_object (value));
597         break;
598       case PROP_TITLE:
599         /* not writable */
600       default:
601         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
602         break;
603     }
604 }
605 
606 static void
terminal_screen_class_init(TerminalScreenClass * klass)607 terminal_screen_class_init (TerminalScreenClass *klass)
608 {
609   GObjectClass *object_class = G_OBJECT_CLASS (klass);
610   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
611   VteTerminalClass *terminal_class = VTE_TERMINAL_CLASS (klass);
612   GSettings *settings;
613 
614   object_class->constructed = terminal_screen_constructed;
615   object_class->dispose = terminal_screen_dispose;
616   object_class->finalize = terminal_screen_finalize;
617   object_class->get_property = terminal_screen_get_property;
618   object_class->set_property = terminal_screen_set_property;
619 
620   widget_class->realize = terminal_screen_realize;
621   widget_class->style_updated = terminal_screen_style_updated;
622   widget_class->drag_data_received = terminal_screen_drag_data_received;
623   widget_class->button_press_event = terminal_screen_button_press;
624   widget_class->popup_menu = terminal_screen_popup_menu;
625 
626   terminal_class->child_exited = terminal_screen_child_exited;
627 
628   signals[PROFILE_SET] =
629     g_signal_new (I_("profile-set"),
630                   G_OBJECT_CLASS_TYPE (object_class),
631                   G_SIGNAL_RUN_LAST,
632                   G_STRUCT_OFFSET (TerminalScreenClass, profile_set),
633                   nullptr, nullptr,
634                   g_cclosure_marshal_VOID__OBJECT,
635                   G_TYPE_NONE,
636                   1, G_TYPE_SETTINGS);
637 
638   signals[SHOW_POPUP_MENU] =
639     g_signal_new (I_("show-popup-menu"),
640                   G_OBJECT_CLASS_TYPE (object_class),
641                   G_SIGNAL_RUN_LAST,
642                   G_STRUCT_OFFSET (TerminalScreenClass, show_popup_menu),
643                   nullptr, nullptr,
644                   g_cclosure_marshal_VOID__POINTER,
645                   G_TYPE_NONE,
646                   1,
647                   G_TYPE_POINTER);
648 
649   signals[MATCH_CLICKED] =
650     g_signal_new (I_("match-clicked"),
651                   G_OBJECT_CLASS_TYPE (object_class),
652                   G_SIGNAL_RUN_LAST,
653                   G_STRUCT_OFFSET (TerminalScreenClass, match_clicked),
654                   g_signal_accumulator_true_handled, nullptr,
655                   _terminal_marshal_BOOLEAN__STRING_INT_UINT,
656                   G_TYPE_BOOLEAN,
657                   3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_UINT);
658 
659   signals[CLOSE_SCREEN] =
660     g_signal_new (I_("close-screen"),
661                   G_OBJECT_CLASS_TYPE (object_class),
662                   G_SIGNAL_RUN_LAST,
663                   G_STRUCT_OFFSET (TerminalScreenClass, close_screen),
664                   nullptr, nullptr,
665                   g_cclosure_marshal_VOID__VOID,
666                   G_TYPE_NONE,
667                   0);
668 
669   g_object_class_install_property
670     (object_class,
671      PROP_PROFILE,
672      g_param_spec_object ("profile", nullptr, nullptr,
673                           G_TYPE_SETTINGS,
674                           GParamFlags(G_PARAM_READWRITE |
675 				      G_PARAM_STATIC_NAME |
676 				      G_PARAM_STATIC_NICK |
677 				      G_PARAM_STATIC_BLURB)));
678 
679   g_object_class_install_property
680     (object_class,
681      PROP_TITLE,
682      g_param_spec_string ("title", nullptr, nullptr,
683                           nullptr,
684                           GParamFlags(G_PARAM_READABLE |
685 				      G_PARAM_STATIC_NAME |
686 				      G_PARAM_STATIC_NICK |
687 				      G_PARAM_STATIC_BLURB)));
688 
689   g_type_class_add_private (object_class, sizeof (TerminalScreenPrivate));
690 
691   n_url_regexes = G_N_ELEMENTS (url_regex_patterns);
692   precompile_regexes (url_regex_patterns, n_url_regexes, &url_regexes, &url_regex_flavors);
693   n_extra_regexes = G_N_ELEMENTS (extra_regex_patterns);
694   precompile_regexes (extra_regex_patterns, n_extra_regexes, &extra_regexes, &extra_regex_flavors);
695 
696   /* This fixes bug #329827 */
697   settings = terminal_app_get_global_settings (terminal_app_get ());
698   terminal_screen_class_enable_menu_bar_accel_notify_cb (settings, TERMINAL_SETTING_ENABLE_MENU_BAR_ACCEL_KEY, klass);
699   g_signal_connect (settings, "changed::" TERMINAL_SETTING_ENABLE_MENU_BAR_ACCEL_KEY,
700                     G_CALLBACK (terminal_screen_class_enable_menu_bar_accel_notify_cb), klass);
701 }
702 
703 static void
terminal_screen_constructed(GObject * object)704 terminal_screen_constructed (GObject *object)
705 {
706   TerminalScreen *screen = TERMINAL_SCREEN (object);
707   TerminalScreenPrivate *priv = screen->priv;
708 
709   G_OBJECT_CLASS (terminal_screen_parent_class)->constructed (object);
710 
711   terminal_app_register_screen (terminal_app_get (), screen);
712   priv->registered = TRUE;
713 }
714 
715 static void
terminal_screen_dispose(GObject * object)716 terminal_screen_dispose (GObject *object)
717 {
718   TerminalScreen *screen = TERMINAL_SCREEN (object);
719   TerminalScreenPrivate *priv = screen->priv;
720   GtkSettings *settings;
721 
722   /* Unset child PID so that when an eventual child-exited signal arrives,
723    * we don't emit "close".
724    */
725   priv->child_pid = -1;
726 
727   settings = gtk_widget_get_settings (GTK_WIDGET (screen));
728   g_signal_handlers_disconnect_matched (settings, G_SIGNAL_MATCH_DATA,
729                                         0, 0, nullptr, nullptr,
730                                         screen);
731 
732   if (priv->idle_exec_source != 0)
733     {
734       g_source_remove (priv->idle_exec_source);
735       priv->idle_exec_source = 0;
736     }
737 
738   terminal_screen_clear_exec_data (screen, TRUE);
739 
740   G_OBJECT_CLASS (terminal_screen_parent_class)->dispose (object);
741 
742   /* Unregister *after* chaining up to the parent's dispose,
743    * since that will terminate the child process if there still
744    * is any, and we need to get the dbus signal out
745    * from the TerminalReceiver.
746    */
747   if (priv->registered) {
748     terminal_app_unregister_screen (terminal_app_get (), screen);
749     priv->registered = FALSE;
750   }
751 }
752 
753 static void
terminal_screen_finalize(GObject * object)754 terminal_screen_finalize (GObject *object)
755 {
756   TerminalScreen *screen = TERMINAL_SCREEN (object);
757   TerminalScreenPrivate *priv = screen->priv;
758 
759   g_signal_handlers_disconnect_by_func (terminal_app_get_desktop_interface_settings (terminal_app_get ()),
760                                         (void*)terminal_screen_system_font_changed_cb,
761                                         screen);
762 
763   terminal_screen_set_profile (screen, nullptr);
764 
765   g_slist_free_full (priv->match_tags, (GDestroyNotify) free_tag_data);
766 
767   g_free (priv->uuid);
768 
769   G_OBJECT_CLASS (terminal_screen_parent_class)->finalize (object);
770 }
771 
772 TerminalScreen *
terminal_screen_new(GSettings * profile,const char * title,double zoom)773 terminal_screen_new (GSettings       *profile,
774                      const char      *title,
775                      double           zoom)
776 {
777   g_return_val_if_fail (G_IS_SETTINGS (profile), nullptr);
778 
779   TerminalScreen *screen = (TerminalScreen*)g_object_new (TERMINAL_TYPE_SCREEN, nullptr);
780 
781   terminal_screen_set_profile (screen, profile);
782 
783   vte_terminal_set_size (VTE_TERMINAL (screen),
784                          g_settings_get_int (profile, TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS_KEY),
785                          g_settings_get_int (profile, TERMINAL_PROFILE_DEFAULT_SIZE_ROWS_KEY));
786 
787   /* If given an initial title, strip it of control characters and
788    * feed it to the terminal.
789    */
790   if (title != nullptr) {
791     GString *seq;
792     const char *p;
793 
794     seq = g_string_new ("\033]0;");
795     for (p = title; *p; p = g_utf8_next_char (p)) {
796       gunichar c = g_utf8_get_char (p);
797       if (c < 0x20 || (c >= 0x7f && c <= 0x9f))
798         continue;
799       else if (c == ';')
800         break;
801 
802       g_string_append_unichar (seq, c);
803     }
804     g_string_append (seq, "\033\\");
805 
806     vte_terminal_feed (VTE_TERMINAL (screen), seq->str, seq->len);
807     g_string_free (seq, TRUE);
808   }
809 
810   vte_terminal_set_font_scale (VTE_TERMINAL (screen), zoom);
811   terminal_screen_set_font (screen);
812 
813   return screen;
814 }
815 
816 static gboolean
terminal_screen_reexec_from_exec_data(TerminalScreen * screen,ExecData * data,char ** envv,const char * cwd,GCancellable * cancellable,GError ** error)817 terminal_screen_reexec_from_exec_data (TerminalScreen *screen,
818                                        ExecData *data,
819                                        char **envv,
820                                        const char *cwd,
821                                        GCancellable *cancellable,
822                                        GError **error)
823 {
824   _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_PROCESSES) {
825     gs_free char *str = exec_data_to_string (data);
826     _terminal_debug_print (TERMINAL_DEBUG_PROCESSES,
827                            "[screen %p] reexec_from_data: envv:%p(%u) cwd:%s data:[%s]\n",
828                            screen,
829                            envv, envv ? g_strv_length (envv) : 0,
830                            cwd,
831                            str);
832   }
833 
834   return terminal_screen_exec (screen,
835                                data ? data->argv : nullptr,
836                                envv ? envv : data ? data->envv : nullptr,
837                                data ? data->as_shell : TRUE,
838                                /* If we have command line args, must always pass the cwd from the command line, too */
839                                data && data->argv ? data->cwd : cwd ? cwd : data ? data->cwd : nullptr,
840                                nullptr /* fd list */, nullptr /* fd array */,
841                                nullptr, nullptr, nullptr, /* callback + data + destroy notify */
842                                cancellable,
843                                error);
844 }
845 
846 gboolean
terminal_screen_reexec_from_screen(TerminalScreen * screen,TerminalScreen * parent_screen,GCancellable * cancellable,GError ** error)847 terminal_screen_reexec_from_screen (TerminalScreen *screen,
848                                     TerminalScreen *parent_screen,
849                                     GCancellable *cancellable,
850                                     GError **error)
851 {
852   g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), FALSE);
853 
854   if (parent_screen == nullptr)
855     return TRUE;
856 
857   g_return_val_if_fail (TERMINAL_IS_SCREEN (parent_screen), FALSE);
858 
859   terminal_unref_exec_data ExecData* data = exec_data_clone (parent_screen->priv->exec_data, FALSE);
860   gs_free char* cwd = terminal_screen_get_current_dir (parent_screen);
861 
862   _terminal_debug_print (TERMINAL_DEBUG_PROCESSES,
863                          "[screen %p] reexec_from_screen: parent:%p cwd:%s\n",
864                          screen,
865                          parent_screen,
866                          cwd);
867 
868   return terminal_screen_reexec_from_exec_data (screen,
869                                                 data,
870                                                 nullptr /* envv */,
871                                                 cwd,
872                                                 cancellable,
873                                                 error);
874 }
875 
876 gboolean
terminal_screen_reexec(TerminalScreen * screen,char ** envv,const char * cwd,GCancellable * cancellable,GError ** error)877 terminal_screen_reexec (TerminalScreen *screen,
878                         char **envv,
879                         const char *cwd,
880                         GCancellable *cancellable,
881                         GError **error)
882 {
883   g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), FALSE);
884 
885 
886   _terminal_debug_print (TERMINAL_DEBUG_PROCESSES,
887                          "[screen %p] reexec: envv:%p(%u) cwd:%s\n",
888                          screen,
889                          envv, envv ? g_strv_length (envv) : 0,
890                          cwd);
891 
892   return terminal_screen_reexec_from_exec_data (screen,
893                                                 screen->priv->exec_data,
894                                                 envv,
895                                                 cwd,
896                                                 cancellable,
897                                                 error);
898 }
899 
900 gboolean
terminal_screen_exec(TerminalScreen * screen,char ** argv,char ** initial_envv,gboolean as_shell,const char * cwd,GUnixFDList * fd_list,GVariant * fd_array,TerminalScreenExecCallback callback,gpointer user_data,GDestroyNotify destroy_notify,GCancellable * cancellable,GError ** error)901 terminal_screen_exec (TerminalScreen *screen,
902                       char **argv,
903                       char **initial_envv,
904                       gboolean as_shell,
905                       const char *cwd,
906                       GUnixFDList *fd_list,
907                       GVariant *fd_array,
908                       TerminalScreenExecCallback callback,
909                       gpointer user_data,
910                       GDestroyNotify destroy_notify,
911                       GCancellable *cancellable,
912                       GError **error)
913 {
914   g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), FALSE);
915   g_return_val_if_fail (cancellable == nullptr || G_IS_CANCELLABLE (cancellable), FALSE);
916   g_return_val_if_fail (error == nullptr || *error == nullptr, FALSE);
917   g_return_val_if_fail (gtk_widget_get_parent (GTK_WIDGET (screen)) != nullptr, FALSE);
918 
919   _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_PROCESSES) {
920     gs_free char *argv_str = nullptr;
921     _terminal_debug_print (TERMINAL_DEBUG_PROCESSES,
922                            "[screen %p] exec: argv:[%s] envv:%p(%u) as-shell:%s cwd:%s\n",
923                            screen,
924                            (argv_str = strv_to_string(argv)),
925                            initial_envv, initial_envv ? g_strv_length (initial_envv) : 0,
926                            as_shell ? "true":"false",
927                            cwd);
928   }
929 
930   TerminalScreenPrivate *priv = screen->priv;
931 
932   ExecData *data = exec_data_new ();
933   data->callback = callback;
934   data->callback_data = user_data;
935   data->callback_data_destroy_notify = destroy_notify;
936 
937   GError *err = nullptr;
938   if (priv->child_pid != -1) {
939     g_set_error_literal (&err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
940                          "Cannot launch a new child process while the terminal is still running another child process");
941 
942     terminal_screen_show_info_bar (screen, err, FALSE);
943     g_propagate_error (error, err);
944     exec_data_unref (data); /* frees the callback data */
945     return FALSE;
946   }
947 
948   gs_free char *path = nullptr;
949   gs_free char *shell = nullptr;
950   gs_strfreev char **envv = terminal_screen_get_child_environment (screen,
951                                                                   initial_envv,
952                                                                   &path,
953                                                                   &shell);
954 
955   gboolean preserve_cwd = FALSE;
956   GSpawnFlags spawn_flags = GSpawnFlags(G_SPAWN_SEARCH_PATH_FROM_ENVP |
957 					VTE_SPAWN_NO_PARENT_ENVV);
958   gs_strfreev char **exec_argv = nullptr;
959   if (!terminal_screen_get_child_command (screen,
960                                           argv,
961                                           path,
962                                           shell,
963                                           as_shell,
964                                           &preserve_cwd,
965                                           &spawn_flags,
966                                           &exec_argv,
967                                           &err)) {
968     terminal_screen_show_info_bar (screen, err, FALSE);
969     g_propagate_error (error, err);
970     exec_data_unref (data); /* frees the callback data */
971     return FALSE;
972   }
973 
974   if (!preserve_cwd) {
975     cwd = g_get_home_dir ();
976     envv = g_environ_unsetenv (envv, "PWD");
977   }
978 
979   data->fd_list = (GUnixFDList*)(fd_list ? g_object_ref(fd_list) : nullptr);
980 
981   if (fd_array) {
982     g_assert_nonnull(fd_list);
983     int n_fds = g_unix_fd_list_get_length(fd_list);
984 
985     gsize fd_array_data_len;
986     const int *fd_array_data = (int const*)g_variant_get_fixed_array (fd_array, &fd_array_data_len, 2 * sizeof (int));
987 
988     data->n_fd_map = fd_array_data_len;
989     data->fd_map = g_new (int, data->n_fd_map);
990     for (gsize i = 0; i < fd_array_data_len; i++) {
991       const int fd = fd_array_data[2 * i];
992       const int idx = fd_array_data[2 * i + 1];
993       g_assert_cmpint(idx, >=, 0);
994       g_assert_cmpuint(idx, <, n_fds);
995 
996       data->fd_map[idx] = fd;
997     }
998   } else {
999     data->n_fd_map = 0;
1000     data->fd_map = nullptr;
1001   }
1002 
1003   data->argv = g_strdupv (argv);
1004   data->exec_argv = g_strdupv (exec_argv);
1005   data->cwd = g_strdup (cwd);
1006   data->envv = g_strdupv (envv);
1007   data->as_shell = as_shell;
1008   data->pty_flags = VTE_PTY_DEFAULT;
1009   data->spawn_flags = spawn_flags;
1010   data->cancellable = (GCancellable*)(cancellable ? g_object_ref (cancellable) : nullptr);
1011 
1012   terminal_screen_clear_exec_data (screen, TRUE);
1013   priv->exec_data = data;
1014 
1015   terminal_screen_queue_idle_exec (screen);
1016 
1017   return TRUE;
1018 }
1019 
1020 const char*
terminal_screen_get_title(TerminalScreen * screen)1021 terminal_screen_get_title (TerminalScreen *screen)
1022 {
1023   return vte_terminal_get_window_title (VTE_TERMINAL (screen));
1024 }
1025 
1026 static void
terminal_screen_profile_changed_cb(GSettings * profile,const char * prop_name,TerminalScreen * screen)1027 terminal_screen_profile_changed_cb (GSettings     *profile,
1028                                     const char    *prop_name,
1029                                     TerminalScreen *screen)
1030 {
1031   TerminalScreenPrivate *priv = screen->priv;
1032   GObject *object = G_OBJECT (screen);
1033   VteTerminal *vte_terminal = VTE_TERMINAL (screen);
1034   TerminalWindow *window;
1035 
1036   g_object_freeze_notify (object);
1037 
1038   if ((window = terminal_screen_get_window (screen)))
1039     {
1040       /* We need these in line for the set_size in
1041        * update_on_realize
1042        */
1043       terminal_window_update_geometry (window);
1044     }
1045 
1046   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLLBAR_POLICY_KEY))
1047     _terminal_screen_update_scrollbar (screen);
1048 
1049   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_ENCODING_KEY))
1050     {
1051       gs_free char *charset = g_settings_get_string (profile, TERMINAL_PROFILE_ENCODING_KEY);
1052       const char *encoding = terminal_util_translate_encoding (charset);
1053       if (encoding != nullptr)
1054         vte_terminal_set_encoding (vte_terminal, encoding, nullptr);
1055     }
1056 
1057   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_CJK_UTF8_AMBIGUOUS_WIDTH_KEY))
1058     {
1059       int width;
1060 
1061       width = g_settings_get_enum (profile, TERMINAL_PROFILE_CJK_UTF8_AMBIGUOUS_WIDTH_KEY);
1062       vte_terminal_set_cjk_ambiguous_width (vte_terminal, width);
1063     }
1064 
1065   if (gtk_widget_get_realized (GTK_WIDGET (screen)) &&
1066       (!prop_name ||
1067        prop_name == I_(TERMINAL_PROFILE_USE_SYSTEM_FONT_KEY) ||
1068        prop_name == I_(TERMINAL_PROFILE_FONT_KEY) ||
1069        prop_name == I_(TERMINAL_PROFILE_CELL_WIDTH_SCALE_KEY) ||
1070        prop_name == I_(TERMINAL_PROFILE_CELL_HEIGHT_SCALE_KEY)))
1071     terminal_screen_set_font (screen);
1072 
1073   if (!prop_name ||
1074       prop_name == I_(TERMINAL_PROFILE_USE_THEME_COLORS_KEY) ||
1075       prop_name == I_(TERMINAL_PROFILE_FOREGROUND_COLOR_KEY) ||
1076       prop_name == I_(TERMINAL_PROFILE_BACKGROUND_COLOR_KEY) ||
1077       prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG_KEY) ||
1078       prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR_KEY) ||
1079       prop_name == I_(TERMINAL_PROFILE_CURSOR_COLORS_SET_KEY) ||
1080       prop_name == I_(TERMINAL_PROFILE_CURSOR_BACKGROUND_COLOR_KEY) ||
1081       prop_name == I_(TERMINAL_PROFILE_CURSOR_FOREGROUND_COLOR_KEY) ||
1082       prop_name == I_(TERMINAL_PROFILE_HIGHLIGHT_COLORS_SET_KEY) ||
1083       prop_name == I_(TERMINAL_PROFILE_HIGHLIGHT_BACKGROUND_COLOR_KEY) ||
1084       prop_name == I_(TERMINAL_PROFILE_HIGHLIGHT_FOREGROUND_COLOR_KEY) ||
1085       prop_name == I_(TERMINAL_PROFILE_PALETTE_KEY))
1086     update_color_scheme (screen);
1087 
1088   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_AUDIBLE_BELL_KEY))
1089       vte_terminal_set_audible_bell (vte_terminal, g_settings_get_boolean (profile, TERMINAL_PROFILE_AUDIBLE_BELL_KEY));
1090 
1091   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE_KEY))
1092     vte_terminal_set_scroll_on_keystroke (vte_terminal,
1093                                           g_settings_get_boolean (profile, TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE_KEY));
1094   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLL_ON_OUTPUT_KEY))
1095     vte_terminal_set_scroll_on_output (vte_terminal,
1096                                        g_settings_get_boolean (profile, TERMINAL_PROFILE_SCROLL_ON_OUTPUT_KEY));
1097   if (!prop_name ||
1098       prop_name == I_(TERMINAL_PROFILE_SCROLLBACK_LINES_KEY) ||
1099       prop_name == I_(TERMINAL_PROFILE_SCROLLBACK_UNLIMITED_KEY))
1100     {
1101       glong lines = g_settings_get_boolean (profile, TERMINAL_PROFILE_SCROLLBACK_UNLIMITED_KEY) ?
1102 		    -1 : g_settings_get_int (profile, TERMINAL_PROFILE_SCROLLBACK_LINES_KEY);
1103       vte_terminal_set_scrollback_lines (vte_terminal, lines);
1104     }
1105 
1106   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_BACKSPACE_BINDING_KEY))
1107   vte_terminal_set_backspace_binding (vte_terminal,
1108                                       VteEraseBinding(g_settings_get_enum (profile, TERMINAL_PROFILE_BACKSPACE_BINDING_KEY)));
1109 
1110   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_DELETE_BINDING_KEY))
1111   vte_terminal_set_delete_binding (vte_terminal,
1112                                    VteEraseBinding(g_settings_get_enum (profile, TERMINAL_PROFILE_DELETE_BINDING_KEY)));
1113 
1114   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_ENABLE_BIDI_KEY))
1115     vte_terminal_set_enable_bidi (vte_terminal,
1116                                   g_settings_get_boolean (profile, TERMINAL_PROFILE_ENABLE_BIDI_KEY));
1117   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_ENABLE_SHAPING_KEY))
1118     vte_terminal_set_enable_shaping (vte_terminal,
1119                                      g_settings_get_boolean (profile, TERMINAL_PROFILE_ENABLE_SHAPING_KEY));
1120 
1121   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_ENABLE_SIXEL_KEY))
1122     vte_terminal_set_enable_sixel (vte_terminal,
1123                                    g_settings_get_boolean (profile, TERMINAL_PROFILE_ENABLE_SIXEL_KEY));
1124 
1125   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_BOLD_IS_BRIGHT_KEY))
1126     vte_terminal_set_bold_is_bright (vte_terminal,
1127                                      g_settings_get_boolean (profile, TERMINAL_PROFILE_BOLD_IS_BRIGHT_KEY));
1128 
1129   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_CURSOR_BLINK_MODE_KEY))
1130     vte_terminal_set_cursor_blink_mode (vte_terminal,
1131                                         VteCursorBlinkMode(g_settings_get_enum (priv->profile, TERMINAL_PROFILE_CURSOR_BLINK_MODE_KEY)));
1132 
1133   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_CURSOR_SHAPE_KEY))
1134     vte_terminal_set_cursor_shape (vte_terminal,
1135                                    VteCursorShape(g_settings_get_enum (priv->profile, TERMINAL_PROFILE_CURSOR_SHAPE_KEY)));
1136 
1137   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_REWRAP_ON_RESIZE_KEY))
1138     vte_terminal_set_rewrap_on_resize (vte_terminal,
1139                                        g_settings_get_boolean (profile, TERMINAL_PROFILE_REWRAP_ON_RESIZE_KEY));
1140 
1141   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_TEXT_BLINK_MODE_KEY))
1142     vte_terminal_set_text_blink_mode (vte_terminal,
1143                                       VteTextBlinkMode(g_settings_get_enum (profile, TERMINAL_PROFILE_TEXT_BLINK_MODE_KEY)));
1144 
1145   if (!prop_name || prop_name == I_(TERMINAL_PROFILE_WORD_CHAR_EXCEPTIONS_KEY))
1146     {
1147       gs_free char *word_char_exceptions;
1148       g_settings_get (profile, TERMINAL_PROFILE_WORD_CHAR_EXCEPTIONS_KEY, "ms", &word_char_exceptions);
1149       vte_terminal_set_word_char_exceptions (vte_terminal, word_char_exceptions);
1150     }
1151 
1152   g_object_thaw_notify (object);
1153 }
1154 
1155 static void
update_color_scheme(TerminalScreen * screen)1156 update_color_scheme (TerminalScreen *screen)
1157 {
1158   GtkWidget *widget = GTK_WIDGET (screen);
1159   TerminalScreenPrivate *priv = screen->priv;
1160   GSettings *profile = priv->profile;
1161   gs_free GdkRGBA *colors;
1162   gsize n_colors;
1163   GdkRGBA fg, bg, bold, theme_fg, theme_bg;
1164   GdkRGBA cursor_bg, cursor_fg;
1165   GdkRGBA highlight_bg, highlight_fg;
1166   GdkRGBA *boldp;
1167   GdkRGBA *cursor_bgp = nullptr, *cursor_fgp = nullptr;
1168   GdkRGBA *highlight_bgp = nullptr, *highlight_fgp = nullptr;
1169   GtkStyleContext *context;
1170   gboolean use_theme_colors;
1171 
1172   context = gtk_widget_get_style_context (widget);
1173   gtk_style_context_get_color (context, gtk_style_context_get_state (context), &theme_fg);
1174   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1175   gtk_style_context_get_background_color (context, gtk_style_context_get_state (context), &theme_bg);
1176   G_GNUC_END_IGNORE_DEPRECATIONS
1177 
1178   use_theme_colors = g_settings_get_boolean (profile, TERMINAL_PROFILE_USE_THEME_COLORS_KEY);
1179   if (use_theme_colors ||
1180       (!terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_FOREGROUND_COLOR_KEY, &fg) ||
1181        !terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_BACKGROUND_COLOR_KEY, &bg)))
1182     {
1183       fg = theme_fg;
1184       bg = theme_bg;
1185     }
1186 
1187   if (!g_settings_get_boolean (profile, TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG_KEY) &&
1188       !use_theme_colors &&
1189       terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_BOLD_COLOR_KEY, &bold))
1190     boldp = &bold;
1191   else
1192     boldp = nullptr;
1193 
1194   if (g_settings_get_boolean (profile, TERMINAL_PROFILE_CURSOR_COLORS_SET_KEY) &&
1195       !use_theme_colors)
1196     {
1197       if (terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_CURSOR_BACKGROUND_COLOR_KEY, &cursor_bg))
1198         cursor_bgp = &cursor_bg;
1199       if (terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_CURSOR_FOREGROUND_COLOR_KEY, &cursor_fg))
1200         cursor_fgp = &cursor_fg;
1201     }
1202 
1203   if (g_settings_get_boolean (profile, TERMINAL_PROFILE_HIGHLIGHT_COLORS_SET_KEY) &&
1204       !use_theme_colors)
1205     {
1206       if (terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_HIGHLIGHT_BACKGROUND_COLOR_KEY, &highlight_bg))
1207         highlight_bgp = &highlight_bg;
1208       if (terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_HIGHLIGHT_FOREGROUND_COLOR_KEY, &highlight_fg))
1209         highlight_fgp = &highlight_fg;
1210     }
1211 
1212   colors = terminal_g_settings_get_rgba_palette (priv->profile, TERMINAL_PROFILE_PALETTE_KEY, &n_colors);
1213   vte_terminal_set_colors (VTE_TERMINAL (screen), &fg, &bg,
1214                            colors, n_colors);
1215   vte_terminal_set_color_bold (VTE_TERMINAL (screen), boldp);
1216   vte_terminal_set_color_cursor (VTE_TERMINAL (screen), cursor_bgp);
1217   vte_terminal_set_color_cursor_foreground (VTE_TERMINAL (screen), cursor_fgp);
1218   vte_terminal_set_color_highlight (VTE_TERMINAL (screen), highlight_bgp);
1219   vte_terminal_set_color_highlight_foreground (VTE_TERMINAL (screen), highlight_fgp);
1220 }
1221 
1222 static void
terminal_screen_set_font(TerminalScreen * screen)1223 terminal_screen_set_font (TerminalScreen *screen)
1224 {
1225   TerminalScreenPrivate *priv = screen->priv;
1226   GSettings *profile = priv->profile;
1227   PangoFontDescription *desc;
1228   int size;
1229 
1230   if (g_settings_get_boolean (profile, TERMINAL_PROFILE_USE_SYSTEM_FONT_KEY))
1231     {
1232       desc = terminal_app_get_system_font (terminal_app_get ());
1233     }
1234   else
1235     {
1236       gs_free char *font;
1237       font = g_settings_get_string (profile, TERMINAL_PROFILE_FONT_KEY);
1238       desc = pango_font_description_from_string (font);
1239     }
1240 
1241   size = pango_font_description_get_size (desc);
1242   /* Sanity check */
1243   if (size == 0) {
1244     if (pango_font_description_get_size_is_absolute (desc))
1245       pango_font_description_set_absolute_size (desc, 10);
1246     else
1247       pango_font_description_set_size (desc, 10);
1248   }
1249 
1250   vte_terminal_set_font (VTE_TERMINAL (screen), desc);
1251 
1252   pango_font_description_free (desc);
1253 
1254   vte_terminal_set_cell_width_scale (VTE_TERMINAL (screen),
1255                                      g_settings_get_double (profile, TERMINAL_PROFILE_CELL_WIDTH_SCALE_KEY));
1256   vte_terminal_set_cell_height_scale (VTE_TERMINAL (screen),
1257                                       g_settings_get_double (profile, TERMINAL_PROFILE_CELL_HEIGHT_SCALE_KEY));
1258 }
1259 
1260 static void
terminal_screen_system_font_changed_cb(GSettings * settings,const char * key,TerminalScreen * screen)1261 terminal_screen_system_font_changed_cb (GSettings      *settings,
1262                                         const char     *key,
1263                                         TerminalScreen *screen)
1264 {
1265   TerminalScreenPrivate *priv = screen->priv;
1266 
1267   if (!gtk_widget_get_realized (GTK_WIDGET (screen)))
1268     return;
1269 
1270   if (!g_settings_get_boolean (priv->profile, TERMINAL_PROFILE_USE_SYSTEM_FONT_KEY))
1271     return;
1272 
1273   terminal_screen_set_font (screen);
1274 }
1275 
1276 void
terminal_screen_set_profile(TerminalScreen * screen,GSettings * profile)1277 terminal_screen_set_profile (TerminalScreen *screen,
1278                              GSettings *profile)
1279 {
1280   TerminalScreenPrivate *priv = screen->priv;
1281   GSettings*old_profile;
1282 
1283   old_profile = priv->profile;
1284   if (profile == old_profile)
1285     return;
1286 
1287   if (priv->profile_changed_id)
1288     {
1289       g_signal_handler_disconnect (G_OBJECT (priv->profile),
1290                                    priv->profile_changed_id);
1291       priv->profile_changed_id = 0;
1292     }
1293 
1294   priv->profile = profile;
1295   if (profile)
1296     {
1297       g_object_ref (profile);
1298       priv->profile_changed_id =
1299         g_signal_connect (profile, "changed",
1300                           G_CALLBACK (terminal_screen_profile_changed_cb),
1301                           screen);
1302       terminal_screen_profile_changed_cb (profile, nullptr, screen);
1303 
1304       g_signal_emit (G_OBJECT (screen), signals[PROFILE_SET], 0, old_profile);
1305     }
1306 
1307   if (old_profile)
1308     g_object_unref (old_profile);
1309 
1310   g_object_notify (G_OBJECT (screen), "profile");
1311 }
1312 
1313 GSettings*
terminal_screen_get_profile(TerminalScreen * screen)1314 terminal_screen_get_profile (TerminalScreen *screen)
1315 {
1316   TerminalScreenPrivate *priv = screen->priv;
1317 
1318   return priv->profile;
1319 }
1320 
1321 GSettings*
terminal_screen_ref_profile(TerminalScreen * screen)1322 terminal_screen_ref_profile (TerminalScreen *screen)
1323 {
1324   TerminalScreenPrivate *priv = screen->priv;
1325 
1326   if (priv->profile != nullptr)
1327     return (GSettings*)g_object_ref (priv->profile);
1328   return nullptr;
1329 }
1330 
1331 static gboolean
should_preserve_cwd(TerminalPreserveWorkingDirectory preserve_cwd,const char * path,const char * arg0)1332 should_preserve_cwd (TerminalPreserveWorkingDirectory preserve_cwd,
1333                      const char *path,
1334                      const char *arg0)
1335 {
1336   switch (preserve_cwd) {
1337   case TERMINAL_PRESERVE_WORKING_DIRECTORY_SAFE: {
1338     gs_free char *resolved_arg0 = terminal_util_find_program_in_path (path, arg0);
1339     return resolved_arg0 != nullptr &&
1340       terminal_util_get_is_shell (resolved_arg0);
1341   }
1342 
1343   case TERMINAL_PRESERVE_WORKING_DIRECTORY_ALWAYS:
1344     return TRUE;
1345 
1346   case TERMINAL_PRESERVE_WORKING_DIRECTORY_NEVER:
1347   default:
1348     return FALSE;
1349   }
1350 }
1351 
1352 static gboolean
terminal_screen_get_child_command(TerminalScreen * screen,char ** argv,const char * path_env,const char * shell_env,gboolean as_shell,gboolean * preserve_cwd_p,GSpawnFlags * spawn_flags_p,char *** exec_argv_p,GError ** err)1353 terminal_screen_get_child_command (TerminalScreen *screen,
1354                                    char          **argv,
1355                                    const char     *path_env,
1356                                    const char     *shell_env,
1357                                    gboolean        as_shell,
1358                                    gboolean       *preserve_cwd_p,
1359                                    GSpawnFlags    *spawn_flags_p,
1360                                    char         ***exec_argv_p,
1361                                    GError        **err)
1362 {
1363   TerminalScreenPrivate *priv = screen->priv;
1364   GSettings *profile = priv->profile;
1365   TerminalPreserveWorkingDirectory preserve_cwd;
1366   char **exec_argv;
1367 
1368   g_assert (spawn_flags_p != nullptr && exec_argv_p != nullptr && preserve_cwd_p != nullptr);
1369 
1370   *exec_argv_p = exec_argv = nullptr;
1371 
1372   preserve_cwd = TerminalPreserveWorkingDirectory
1373     (g_settings_get_enum (profile, TERMINAL_PROFILE_PRESERVE_WORKING_DIRECTORY_KEY));
1374 
1375   if (argv)
1376     {
1377       exec_argv = g_strdupv (argv);
1378 
1379       /* argv and cwd come from the command line client, so it must always be used */
1380       *preserve_cwd_p = TRUE;
1381       *spawn_flags_p = GSpawnFlags(*spawn_flags_p | G_SPAWN_SEARCH_PATH_FROM_ENVP);
1382     }
1383   else if (g_settings_get_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND_KEY))
1384     {
1385       gs_free char *exec_argv_str;
1386 
1387       exec_argv_str = g_settings_get_string (profile, TERMINAL_PROFILE_CUSTOM_COMMAND_KEY);
1388       if (!g_shell_parse_argv (exec_argv_str, nullptr, &exec_argv, err))
1389         return FALSE;
1390 
1391       *preserve_cwd_p = should_preserve_cwd (preserve_cwd, path_env, exec_argv[0]);
1392       *spawn_flags_p = GSpawnFlags(*spawn_flags_p | G_SPAWN_SEARCH_PATH_FROM_ENVP);
1393     }
1394   else if (as_shell)
1395     {
1396       const char *only_name;
1397       char *shell;
1398       int argc = 0;
1399 
1400       shell = egg_shell (shell_env);
1401 
1402       only_name = strrchr (shell, '/');
1403       if (only_name != nullptr)
1404         only_name++;
1405       else {
1406         only_name = shell;
1407         *spawn_flags_p = GSpawnFlags(*spawn_flags_p | G_SPAWN_SEARCH_PATH_FROM_ENVP);
1408       }
1409 
1410       exec_argv = g_new (char*, 3);
1411 
1412       exec_argv[argc++] = shell;
1413 
1414       if (g_settings_get_boolean (profile, TERMINAL_PROFILE_LOGIN_SHELL_KEY))
1415         exec_argv[argc++] = g_strconcat ("-", only_name, nullptr);
1416       else
1417         exec_argv[argc++] = g_strdup (only_name);
1418 
1419       exec_argv[argc++] = nullptr;
1420 
1421       *preserve_cwd_p = should_preserve_cwd (preserve_cwd, path_env, shell);
1422       *spawn_flags_p = GSpawnFlags(*spawn_flags_p | G_SPAWN_FILE_AND_ARGV_ZERO);
1423     }
1424 
1425   else
1426     {
1427       g_set_error_literal (err, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
1428                            _("No command supplied nor shell requested"));
1429       return FALSE;
1430     }
1431 
1432   *exec_argv_p = exec_argv;
1433 
1434   return TRUE;
1435 }
1436 
1437 static char**
terminal_screen_get_child_environment(TerminalScreen * screen,char ** initial_envv,char ** path,char ** shell)1438 terminal_screen_get_child_environment (TerminalScreen *screen,
1439                                        char **initial_envv,
1440                                        char **path,
1441                                        char **shell)
1442 {
1443   TerminalApp *app = terminal_app_get ();
1444   char **env;
1445   gs_strfreev char** current_environ = nullptr;
1446   char *e, *v;
1447   GHashTable *env_table;
1448   GHashTableIter iter;
1449   GPtrArray *retval;
1450   guint i;
1451 
1452   env_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1453 
1454   if (initial_envv)
1455     env = initial_envv;
1456   else {
1457     env = current_environ = g_get_environ ();
1458     /* Remove this variable which we set in server.c:main() */
1459     env = g_environ_unsetenv (env, "G_ENABLE_DIAGNOSTIC");
1460   }
1461 
1462   for (i = 0; env[i]; ++i)
1463     {
1464       v = strchr (env[i], '=');
1465       if (v)
1466           g_hash_table_replace (env_table, g_strndup (env[i], v - env[i]), g_strdup (v + 1));
1467         else
1468           g_hash_table_replace (env_table, g_strdup (env[i]), nullptr);
1469     }
1470 
1471   /* Remove unwanted env variables */
1472   char const* const* filters = terminal_client_get_environment_filters ();
1473   for (i = 0; filters[i]; ++i)
1474     g_hash_table_remove (env_table, filters[i]);
1475 
1476   terminal_util_add_proxy_env (env_table);
1477 
1478   /* Add gnome-terminal private env vars used to communicate back to g-t-server */
1479   GDBusConnection *connection = g_application_get_dbus_connection (G_APPLICATION (app));
1480   g_hash_table_replace (env_table, g_strdup (TERMINAL_ENV_SERVICE_NAME),
1481                         g_strdup (g_dbus_connection_get_unique_name (connection)));
1482 
1483   g_hash_table_replace (env_table, g_strdup (TERMINAL_ENV_SCREEN),
1484                         terminal_app_dup_screen_object_path (app, screen));
1485 
1486   /* Convert to strv */
1487   retval = g_ptr_array_sized_new (g_hash_table_size (env_table));
1488   g_hash_table_iter_init (&iter, env_table);
1489   while (g_hash_table_iter_next (&iter, (gpointer *) &e, (gpointer *) &v))
1490     g_ptr_array_add (retval, g_strdup_printf ("%s=%s", e, v ? v : ""));
1491   g_ptr_array_add (retval, nullptr);
1492 
1493   *path = g_strdup ((char const*)g_hash_table_lookup (env_table, "PATH"));
1494   *shell = g_strdup ((char const*)g_hash_table_lookup (env_table, "SHELL"));
1495 
1496   g_hash_table_destroy (env_table);
1497   return (char **) g_ptr_array_free (retval, FALSE);
1498 }
1499 
1500 enum {
1501   RESPONSE_RELAUNCH,
1502   RESPONSE_EDIT_PREFERENCES
1503 };
1504 
1505 static void
info_bar_response_cb(GtkWidget * info_bar,int response,TerminalScreen * screen)1506 info_bar_response_cb (GtkWidget *info_bar,
1507                       int response,
1508                       TerminalScreen *screen)
1509 {
1510   gtk_widget_grab_focus (GTK_WIDGET (screen));
1511 
1512   switch (response) {
1513     case GTK_RESPONSE_CANCEL:
1514       gtk_widget_destroy (info_bar);
1515       g_signal_emit (screen, signals[CLOSE_SCREEN], 0);
1516       break;
1517     case RESPONSE_RELAUNCH:
1518       gtk_widget_destroy (info_bar);
1519       terminal_screen_reexec (screen, nullptr, nullptr, nullptr, nullptr);
1520       break;
1521     case RESPONSE_EDIT_PREFERENCES:
1522       terminal_app_edit_preferences (terminal_app_get (),
1523                                      terminal_screen_get_profile (screen),
1524                                      "custom-command-entry");
1525       break;
1526     default:
1527       gtk_widget_destroy (info_bar);
1528       break;
1529   }
1530 }
1531 
1532 static void
terminal_screen_show_info_bar(TerminalScreen * screen,GError * error,gboolean show_relaunch)1533 terminal_screen_show_info_bar (TerminalScreen *screen,
1534                                GError *error,
1535                                gboolean show_relaunch)
1536 {
1537   GtkWidget *info_bar;
1538 
1539   if (!gtk_widget_get_parent (GTK_WIDGET (screen)))
1540     return;
1541 
1542   info_bar = terminal_info_bar_new (GTK_MESSAGE_ERROR,
1543                                     _("_Preferences"), RESPONSE_EDIT_PREFERENCES,
1544                                     !show_relaunch ? nullptr : _("_Relaunch"), RESPONSE_RELAUNCH,
1545                                     nullptr);
1546   terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar),
1547                                  _("There was an error creating the child process for this terminal"));
1548   terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar),
1549                                  "%s", error->message);
1550   g_signal_connect (info_bar, "response",
1551                     G_CALLBACK (info_bar_response_cb), screen);
1552 
1553   gtk_widget_set_halign (info_bar, GTK_ALIGN_FILL);
1554   gtk_widget_set_valign (info_bar, GTK_ALIGN_START);
1555   gtk_overlay_add_overlay (GTK_OVERLAY (terminal_screen_container_get_from_screen (screen)),
1556                            info_bar);
1557   gtk_info_bar_set_default_response (GTK_INFO_BAR (info_bar), GTK_RESPONSE_CANCEL);
1558   gtk_widget_show (info_bar);
1559 }
1560 
1561 static void
spawn_result_cb(VteTerminal * terminal,GPid pid,GError * error,gpointer user_data)1562 spawn_result_cb (VteTerminal *terminal,
1563                  GPid pid,
1564                  GError *error,
1565                  gpointer user_data)
1566 {
1567   TerminalScreen *screen = TERMINAL_SCREEN (terminal);
1568   ExecData *exec_data = (ExecData*)user_data;
1569 
1570   /* Terminal was destroyed while the spawn operation was in progress; nothing to do. */
1571   if (terminal == nullptr)
1572     goto out;
1573 
1574   {
1575   TerminalScreenPrivate *priv = screen->priv;
1576 
1577   priv->child_pid = pid;
1578 
1579   if (error) {
1580      // FIXMEchpe should be unnecessary, vte already does this internally
1581     vte_terminal_set_pty (terminal, nullptr);
1582 
1583     gboolean can_reexec = TRUE; /* FIXME */
1584     terminal_screen_show_info_bar (screen, error, can_reexec);
1585   }
1586 
1587   /* Retain info for reexec, if possible */
1588   ExecData *new_exec_data = exec_data_clone (exec_data, TRUE);
1589   terminal_screen_clear_exec_data (screen, FALSE);
1590   priv->exec_data = new_exec_data;
1591   }
1592 
1593 out:
1594 
1595   /* Must do this even if the terminal was destroyed */
1596   exec_data_callback (exec_data, error, screen);
1597 
1598   exec_data_unref (exec_data);
1599 }
1600 
1601 static gboolean
idle_exec_cb(TerminalScreen * screen)1602 idle_exec_cb (TerminalScreen *screen)
1603 {
1604   TerminalScreenPrivate *priv = screen->priv;
1605 
1606   priv->idle_exec_source = 0;
1607 
1608   ExecData *data = priv->exec_data;
1609   _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_PROCESSES) {
1610     gs_free char *str = exec_data_to_string (data);
1611     _terminal_debug_print (TERMINAL_DEBUG_PROCESSES,
1612                            "[screen %p] now launching the child process: %s\n",
1613                            screen, str);
1614   }
1615 
1616   int n_fds;
1617   int *fds;
1618   if (data->fd_list) {
1619     fds = g_unix_fd_list_steal_fds(data->fd_list, &n_fds);
1620   } else {
1621     fds = nullptr;
1622     n_fds = 0;
1623   }
1624 
1625   VteTerminal *terminal = VTE_TERMINAL (screen);
1626   vte_terminal_spawn_with_fds_async (terminal,
1627                                      data->pty_flags,
1628                                      data->cwd,
1629                                      (char const* const*)data->exec_argv,
1630                                      (char const* const*)data->envv,
1631                                      fds, n_fds,
1632                                      data->fd_map, data->n_fd_map,
1633                                      data->spawn_flags,
1634                                      nullptr, nullptr, nullptr, /* child setup, data, destroy */
1635                                      -1,
1636                                      data->cancellable,
1637                                      spawn_result_cb,
1638                                      exec_data_ref (data));
1639 
1640   return FALSE; /* don't run again */
1641 }
1642 
1643 static void
terminal_screen_queue_idle_exec(TerminalScreen * screen)1644 terminal_screen_queue_idle_exec (TerminalScreen *screen)
1645 {
1646   TerminalScreenPrivate *priv = screen->priv;
1647 
1648   if (priv->idle_exec_source != 0)
1649     return;
1650 
1651   if (!gtk_widget_get_realized (GTK_WIDGET (screen))) {
1652     priv->exec_on_realize = TRUE;
1653     return;
1654   }
1655 
1656   _terminal_debug_print (TERMINAL_DEBUG_PROCESSES,
1657                          "[screen %p] scheduling launching the child process on idle\n",
1658                          screen);
1659 
1660   priv->idle_exec_source = g_idle_add ((GSourceFunc) idle_exec_cb, screen);
1661 }
1662 
1663 static TerminalScreenPopupInfo *
terminal_screen_popup_info_new(TerminalScreen * screen)1664 terminal_screen_popup_info_new (TerminalScreen *screen)
1665 {
1666   TerminalScreenPopupInfo *info;
1667 
1668   info = g_slice_new0 (TerminalScreenPopupInfo);
1669   info->ref_count = 1;
1670 
1671   return info;
1672 }
1673 
1674 TerminalScreenPopupInfo *
terminal_screen_popup_info_ref(TerminalScreenPopupInfo * info)1675 terminal_screen_popup_info_ref (TerminalScreenPopupInfo *info)
1676 {
1677   g_return_val_if_fail (info != nullptr, nullptr);
1678 
1679   info->ref_count++;
1680   return info;
1681 }
1682 
1683 void
terminal_screen_popup_info_unref(TerminalScreenPopupInfo * info)1684 terminal_screen_popup_info_unref (TerminalScreenPopupInfo *info)
1685 {
1686   g_return_if_fail (info != nullptr);
1687 
1688   if (--info->ref_count > 0)
1689     return;
1690 
1691   g_free (info->hyperlink);
1692   g_free (info->url);
1693   g_free (info->number_info);
1694   g_free (info->timestamp_info);
1695   g_slice_free (TerminalScreenPopupInfo, info);
1696 }
1697 
1698 static gboolean
terminal_screen_popup_menu(GtkWidget * widget)1699 terminal_screen_popup_menu (GtkWidget *widget)
1700 {
1701   TerminalScreen *screen = TERMINAL_SCREEN (widget);
1702   TerminalScreenPopupInfo *info;
1703 
1704   info = terminal_screen_popup_info_new (screen);
1705   info->button = 0;
1706   info->timestamp = gtk_get_current_event_time ();
1707 
1708   g_signal_emit (screen, signals[SHOW_POPUP_MENU], 0, info);
1709   terminal_screen_popup_info_unref (info);
1710 
1711   return TRUE;
1712 }
1713 
1714 static void
terminal_screen_do_popup(TerminalScreen * screen,GdkEventButton * event,char * hyperlink,char * url,int url_flavor,char * number_info,char * timestamp_info)1715 terminal_screen_do_popup (TerminalScreen *screen,
1716                           GdkEventButton *event,
1717                           char *hyperlink,
1718                           char *url,
1719                           int url_flavor,
1720                           char *number_info,
1721                           char *timestamp_info)
1722 {
1723   TerminalScreenPopupInfo *info;
1724 
1725   info = terminal_screen_popup_info_new (screen);
1726   info->button = event->button;
1727   info->state = event->state & gtk_accelerator_get_default_mod_mask ();
1728   info->timestamp = event->time;
1729   info->hyperlink = hyperlink; /* adopted */
1730   info->url = url; /* adopted */
1731   info->url_flavor = TerminalURLFlavor(url_flavor);
1732   info->number_info = number_info; /* adopted */
1733   info->timestamp_info = timestamp_info; /* adopted */
1734 
1735   g_signal_emit (screen, signals[SHOW_POPUP_MENU], 0, info);
1736   terminal_screen_popup_info_unref (info);
1737 }
1738 
1739 static gboolean
terminal_screen_button_press(GtkWidget * widget,GdkEventButton * event)1740 terminal_screen_button_press (GtkWidget      *widget,
1741                               GdkEventButton *event)
1742 {
1743   TerminalScreen *screen = TERMINAL_SCREEN (widget);
1744   gboolean (* button_press_event) (GtkWidget*, GdkEventButton*) =
1745     GTK_WIDGET_CLASS (terminal_screen_parent_class)->button_press_event;
1746   gs_free char *hyperlink = nullptr;
1747   gs_free char *url = nullptr;
1748   int url_flavor = 0;
1749   gs_free char *number_info = nullptr;
1750   gs_free char *timestamp_info = nullptr;
1751   guint state;
1752 
1753   state = event->state & gtk_accelerator_get_default_mod_mask ();
1754 
1755   hyperlink = terminal_screen_check_hyperlink (screen, (GdkEvent*)event);
1756   url = terminal_screen_check_match (screen, (GdkEvent*)event, &url_flavor);
1757   terminal_screen_check_extra (screen, (GdkEvent*)event, &number_info, &timestamp_info);
1758 
1759   if (hyperlink != nullptr &&
1760       (event->button == 1 || event->button == 2) &&
1761       (state & GDK_CONTROL_MASK))
1762     {
1763       gboolean handled = FALSE;
1764 
1765       g_signal_emit (screen, signals[MATCH_CLICKED], 0,
1766                      hyperlink,
1767                      FLAVOR_AS_IS,
1768                      state,
1769                      &handled);
1770       if (handled)
1771         return TRUE; /* don't do anything else such as select with the click */
1772     }
1773 
1774   if (url != nullptr &&
1775       (event->button == 1 || event->button == 2) &&
1776       (state & GDK_CONTROL_MASK))
1777     {
1778       gboolean handled = FALSE;
1779 
1780       g_signal_emit (screen, signals[MATCH_CLICKED], 0,
1781                      url,
1782                      url_flavor,
1783                      state,
1784                      &handled);
1785       if (handled)
1786         return TRUE; /* don't do anything else such as select with the click */
1787     }
1788 
1789   if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1790     {
1791       if (!(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)))
1792         {
1793           /* on right-click, we should first try to send the mouse event to
1794            * the client, and popup only if that's not handled. */
1795           if (button_press_event && button_press_event (widget, event))
1796             return TRUE;
1797 
1798           terminal_screen_do_popup (screen, event, hyperlink, url, url_flavor, number_info, timestamp_info);
1799           hyperlink = nullptr; /* adopted to the popup info */
1800           url = nullptr; /* ditto */
1801           number_info = nullptr; /* ditto */
1802           timestamp_info = nullptr; /* ditto */
1803           return TRUE;
1804         }
1805       else if (!(event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
1806         {
1807           /* do popup on shift+right-click */
1808           terminal_screen_do_popup (screen, event, hyperlink, url, url_flavor, number_info, timestamp_info);
1809           hyperlink = nullptr; /* adopted to the popup info */
1810           url = nullptr; /* ditto */
1811           number_info = nullptr; /* ditto */
1812           timestamp_info = nullptr; /* ditto */
1813           return TRUE;
1814         }
1815     }
1816 
1817   /* default behavior is to let the terminal widget deal with it */
1818   if (button_press_event)
1819     return button_press_event (widget, event);
1820 
1821   return FALSE;
1822 }
1823 
1824 /**
1825  * terminal_screen_get_current_dir:
1826  * @screen:
1827  *
1828  * Tries to determine the current working directory of the foreground process
1829  * in @screen's PTY.
1830  *
1831  * Returns: a newly allocated string containing the current working directory,
1832  *   or %nullptr on failure
1833  */
1834 char *
terminal_screen_get_current_dir(TerminalScreen * screen)1835 terminal_screen_get_current_dir (TerminalScreen *screen)
1836 {
1837   const char *uri;
1838 
1839   uri = vte_terminal_get_current_directory_uri (VTE_TERMINAL (screen));
1840   if (uri != nullptr)
1841     return g_filename_from_uri (uri, nullptr, nullptr);
1842 
1843   ExecData *data = screen->priv->exec_data;
1844   if (data && data->cwd)
1845     return g_strdup (data->cwd);
1846 
1847   return nullptr;
1848 }
1849 
1850 static void
terminal_screen_window_title_changed(VteTerminal * vte_terminal,TerminalScreen * screen)1851 terminal_screen_window_title_changed (VteTerminal *vte_terminal,
1852                                       TerminalScreen *screen)
1853 {
1854   g_object_notify (G_OBJECT (screen), "title");
1855 }
1856 
1857 static void
terminal_screen_child_exited(VteTerminal * terminal,int status)1858 terminal_screen_child_exited (VteTerminal *terminal,
1859                               int status)
1860 {
1861   TerminalScreen *screen = TERMINAL_SCREEN (terminal);
1862   TerminalScreenPrivate *priv = screen->priv;
1863   TerminalExitAction action;
1864 
1865   /* Don't do anything if we don't have a child */
1866   if (priv->child_pid == -1)
1867     return;
1868 
1869   /* No need to chain up to VteTerminalClass::child_exited since it's nullptr */
1870 
1871   _terminal_debug_print (TERMINAL_DEBUG_PROCESSES,
1872                          "[screen %p] child process exited\n",
1873                          screen);
1874 
1875   priv->child_pid = -1;
1876 
1877   action = TerminalExitAction(g_settings_get_enum (priv->profile, TERMINAL_PROFILE_EXIT_ACTION_KEY));
1878 
1879   switch (action)
1880     {
1881     case TERMINAL_EXIT_CLOSE:
1882       g_signal_emit (screen, signals[CLOSE_SCREEN], 0);
1883       break;
1884     case TERMINAL_EXIT_RESTART:
1885       terminal_screen_reexec (screen, nullptr, nullptr, nullptr, nullptr);
1886       break;
1887     case TERMINAL_EXIT_HOLD: {
1888       GtkWidget *info_bar;
1889 
1890       info_bar = terminal_info_bar_new (GTK_MESSAGE_INFO,
1891                                         _("_Relaunch"), RESPONSE_RELAUNCH,
1892                                         nullptr);
1893       if (WIFEXITED (status)) {
1894         terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar),
1895                                       _("The child process exited normally with status %d."), WEXITSTATUS (status));
1896       } else if (WIFSIGNALED (status)) {
1897         terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar),
1898                                       _("The child process was aborted by signal %d."), WTERMSIG (status));
1899       } else {
1900         terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar),
1901                                       _("The child process was aborted."));
1902       }
1903       g_signal_connect (info_bar, "response",
1904                         G_CALLBACK (info_bar_response_cb), screen);
1905 
1906       gtk_widget_set_halign (info_bar, GTK_ALIGN_FILL);
1907       gtk_widget_set_valign (info_bar, GTK_ALIGN_START);
1908       gtk_overlay_add_overlay (GTK_OVERLAY (terminal_screen_container_get_from_screen (screen)),
1909                                info_bar);
1910       gtk_info_bar_set_default_response (GTK_INFO_BAR (info_bar), RESPONSE_RELAUNCH);
1911       gtk_widget_show (info_bar);
1912       break;
1913     }
1914 
1915     default:
1916       break;
1917     }
1918 }
1919 
1920 static void
terminal_screen_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint timestamp)1921 terminal_screen_drag_data_received (GtkWidget        *widget,
1922                                     GdkDragContext   *context,
1923                                     gint              x,
1924                                     gint              y,
1925                                     GtkSelectionData *selection_data,
1926                                     guint             info,
1927                                     guint             timestamp)
1928 {
1929   TerminalScreen *screen = TERMINAL_SCREEN (widget);
1930   TerminalScreenPrivate *priv = screen->priv;
1931   const guchar *selection_data_data;
1932   GdkAtom selection_data_target;
1933   gint selection_data_length, selection_data_format;
1934 
1935   selection_data_data = gtk_selection_data_get_data (selection_data);
1936   selection_data_target = gtk_selection_data_get_target (selection_data);
1937   selection_data_length = gtk_selection_data_get_length (selection_data);
1938   selection_data_format = gtk_selection_data_get_format (selection_data);
1939 
1940 #if 0
1941   {
1942     GList *tmp;
1943 
1944     g_print ("info: %d\n", info);
1945     tmp = context->targets;
1946     while (tmp != nullptr)
1947       {
1948         GdkAtom atom = GDK_POINTER_TO_ATOM (tmp->data);
1949 
1950         g_print ("Target: %s\n", gdk_atom_name (atom));
1951 
1952         tmp = tmp->next;
1953       }
1954 
1955     g_print ("Chosen target: %s\n", gdk_atom_name (selection_data->target));
1956   }
1957 #endif
1958 
1959   if (gtk_targets_include_uri (&selection_data_target, 1))
1960     {
1961       gs_strfreev char **uris;
1962       gs_free char *text = nullptr;
1963       gsize len;
1964 
1965       uris = gtk_selection_data_get_uris (selection_data);
1966       if (!uris)
1967         return;
1968 
1969       terminal_util_transform_uris_to_quoted_fuse_paths (uris);
1970 
1971       text = terminal_util_concat_uris (uris, &len);
1972       vte_terminal_feed_child (VTE_TERMINAL (screen), text, len);
1973     }
1974   else if (gtk_targets_include_text (&selection_data_target, 1))
1975     {
1976       gs_free char *text;
1977 
1978       text = (char *) gtk_selection_data_get_text (selection_data);
1979       if (text && text[0])
1980         vte_terminal_feed_child (VTE_TERMINAL (screen), text, strlen (text));
1981     }
1982   else switch (info)
1983     {
1984     case TARGET_COLOR:
1985       {
1986         guint16 *data = (guint16 *)selection_data_data;
1987         GdkRGBA color;
1988 
1989         /* We accept drops with the wrong format, since the KDE color
1990          * chooser incorrectly drops application/x-color with format 8.
1991          * So just check for the data length.
1992          */
1993         if (selection_data_length != 8)
1994           return;
1995 
1996         color.red = (double) data[0] / 65535.;
1997         color.green = (double) data[1] / 65535.;
1998         color.blue = (double) data[2] / 65535.;
1999         color.alpha = 1.;
2000         /* FIXME: use opacity from data[3] */
2001 
2002         terminal_g_settings_set_rgba (priv->profile,
2003                                       TERMINAL_PROFILE_BACKGROUND_COLOR_KEY,
2004                                       &color);
2005         g_settings_set_boolean (priv->profile, TERMINAL_PROFILE_USE_THEME_COLORS_KEY, FALSE);
2006       }
2007       break;
2008 
2009     case TARGET_MOZ_URL:
2010       {
2011         char *utf8_data, *text;
2012         char *uris[2];
2013         gsize len;
2014 
2015         /* MOZ_URL is in UCS-2 but in format 8. BROKEN!
2016          *
2017          * The data contains the URL, a \n, then the
2018          * title of the web page.
2019          *
2020          * Note that some producers (e.g. dolphin) delimit with a \r\n
2021          * (see issue#293), so we need to handle that, too.
2022          */
2023         if (selection_data_format != 8 ||
2024             selection_data_length == 0 ||
2025             (selection_data_length % 2) != 0)
2026           return;
2027 
2028         utf8_data = g_utf16_to_utf8 ((const gunichar2*) selection_data_data,
2029                                      selection_data_length / 2,
2030                                      nullptr, nullptr, nullptr);
2031         if (!utf8_data)
2032           return;
2033 
2034         uris[0] = g_strdelimit(utf8_data, "\r\n", 0);
2035         uris[1] = nullptr;
2036         terminal_util_transform_uris_to_quoted_fuse_paths (uris); /* This may replace uris[0] */
2037 
2038         text = terminal_util_concat_uris (uris, &len);
2039         vte_terminal_feed_child (VTE_TERMINAL (screen), text, len);
2040         g_free (text);
2041         g_free (uris[0]);
2042       }
2043       break;
2044 
2045     case TARGET_NETSCAPE_URL:
2046       {
2047         char *utf8_data, *newline, *text;
2048         char *uris[2];
2049         gsize len;
2050 
2051         /* The data contains the URL, a \n, then the
2052          * title of the web page.
2053          */
2054         if (selection_data_length < 0 || selection_data_format != 8)
2055           return;
2056 
2057         utf8_data = g_strndup ((char *) selection_data_data, selection_data_length);
2058         newline = strchr (utf8_data, '\n');
2059         if (newline)
2060           *newline = '\0';
2061 
2062         uris[0] = utf8_data;
2063         uris[1] = nullptr;
2064         terminal_util_transform_uris_to_quoted_fuse_paths (uris); /* This may replace uris[0] */
2065 
2066         text = terminal_util_concat_uris (uris, &len);
2067         vte_terminal_feed_child (VTE_TERMINAL (screen), text, len);
2068         g_free (text);
2069         g_free (uris[0]);
2070       }
2071       break;
2072 
2073     case TARGET_RESET_BG:
2074       g_settings_reset (priv->profile, TERMINAL_PROFILE_BACKGROUND_COLOR_KEY);
2075       break;
2076 
2077     case TARGET_TAB:
2078       {
2079         GtkWidget *container;
2080         TerminalScreen *moving_screen;
2081         TerminalWindow *source_window;
2082         TerminalWindow *dest_window;
2083 
2084         container = *(GtkWidget**) selection_data_data;
2085         if (!GTK_IS_WIDGET (container))
2086           return;
2087 
2088         moving_screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (container));
2089         g_warn_if_fail (TERMINAL_IS_SCREEN (moving_screen));
2090         if (!TERMINAL_IS_SCREEN (moving_screen))
2091           return;
2092 
2093         source_window = terminal_screen_get_window (moving_screen);
2094         dest_window = terminal_screen_get_window (screen);
2095         terminal_window_move_screen (source_window, dest_window, moving_screen, -1);
2096 
2097         gtk_drag_finish (context, TRUE, TRUE, timestamp);
2098       }
2099       break;
2100 
2101     default:
2102       g_assert_not_reached ();
2103     }
2104 }
2105 
2106 void
_terminal_screen_update_scrollbar(TerminalScreen * screen)2107 _terminal_screen_update_scrollbar (TerminalScreen *screen)
2108 {
2109   TerminalScreenPrivate *priv = screen->priv;
2110   TerminalScreenContainer *container;
2111   GtkPolicyType vpolicy;
2112 
2113   container = terminal_screen_container_get_from_screen (screen);
2114   if (container == nullptr)
2115     return;
2116 
2117   vpolicy = GtkPolicyType(g_settings_get_enum (priv->profile, TERMINAL_PROFILE_SCROLLBAR_POLICY_KEY));
2118 
2119   terminal_screen_container_set_policy (container, GTK_POLICY_NEVER, vpolicy);
2120 }
2121 
2122 void
terminal_screen_get_size(TerminalScreen * screen,int * width_chars,int * height_chars)2123 terminal_screen_get_size (TerminalScreen *screen,
2124 			  int       *width_chars,
2125 			  int       *height_chars)
2126 {
2127   VteTerminal *terminal = VTE_TERMINAL (screen);
2128 
2129   *width_chars = vte_terminal_get_column_count (terminal);
2130   *height_chars = vte_terminal_get_row_count (terminal);
2131 }
2132 
2133 void
terminal_screen_get_cell_size(TerminalScreen * screen,int * cell_width_pixels,int * cell_height_pixels)2134 terminal_screen_get_cell_size (TerminalScreen *screen,
2135 			       int                  *cell_width_pixels,
2136 			       int                  *cell_height_pixels)
2137 {
2138   VteTerminal *terminal = VTE_TERMINAL (screen);
2139 
2140   *cell_width_pixels = vte_terminal_get_char_width (terminal);
2141   *cell_height_pixels = vte_terminal_get_char_height (terminal);
2142 }
2143 
2144 static char*
terminal_screen_check_hyperlink(TerminalScreen * screen,GdkEvent * event)2145 terminal_screen_check_hyperlink (TerminalScreen *screen,
2146                                  GdkEvent       *event)
2147 {
2148   return vte_terminal_hyperlink_check_event (VTE_TERMINAL (screen), event);
2149 }
2150 
2151 static char*
terminal_screen_check_match(TerminalScreen * screen,GdkEvent * event,int * flavor)2152 terminal_screen_check_match (TerminalScreen *screen,
2153                              GdkEvent       *event,
2154                              int       *flavor)
2155 {
2156   TerminalScreenPrivate *priv = screen->priv;
2157   GSList *tags;
2158   int tag;
2159   char *match;
2160 
2161   match = vte_terminal_match_check_event (VTE_TERMINAL (screen), event, &tag);
2162   for (tags = priv->match_tags; tags != nullptr; tags = tags->next)
2163     {
2164       TagData *tag_data = (TagData*) tags->data;
2165       if (tag_data->tag == tag)
2166 	{
2167 	  if (flavor)
2168 	    *flavor = tag_data->flavor;
2169 	  return match;
2170 	}
2171     }
2172 
2173   g_free (match);
2174   return nullptr;
2175 }
2176 
2177 static void
terminal_screen_check_extra(TerminalScreen * screen,GdkEvent * event,char ** number_info,char ** timestamp_info)2178 terminal_screen_check_extra (TerminalScreen *screen,
2179                              GdkEvent       *event,
2180                              char           **number_info,
2181                              char           **timestamp_info)
2182 {
2183   guint i;
2184   char **matches;
2185   gboolean flavor_number_found = FALSE;
2186 
2187   matches = g_newa (char *, n_extra_regexes);
2188   memset(matches, 0, sizeof(char*) * n_extra_regexes);
2189 
2190   if (
2191       vte_terminal_event_check_regex_simple (VTE_TERMINAL (screen),
2192                                              event,
2193                                              extra_regexes,
2194                                              n_extra_regexes,
2195                                              0,
2196                                              matches))
2197     {
2198       for (i = 0; i < n_extra_regexes; i++)
2199         {
2200           if (matches[i] != nullptr)
2201             {
2202               /* Store the first match for each flavor, free all the others */
2203               switch (extra_regex_flavors[i])
2204                 {
2205                 case FLAVOR_NUMBER:
2206                   if (!flavor_number_found)
2207                     {
2208                       *number_info = terminal_util_number_info (matches[i]);
2209                       *timestamp_info = terminal_util_timestamp_info (matches[i]);
2210                       flavor_number_found = TRUE;
2211                     }
2212                   g_free (matches[i]);
2213                   break;
2214                 default:
2215                   g_free (matches[i]);
2216                 }
2217             }
2218         }
2219     }
2220 }
2221 
2222 /**
2223  * terminal_screen_has_foreground_process:
2224  * @screen:
2225  * @process_name: (out) (allow-none): the basename of the program, or %nullptr
2226  * @cmdline: (out) (allow-none): the full command line, or %nullptr
2227  *
2228  * Checks whether there's a foreground process running in
2229  * this terminal.
2230  *
2231  * Returns: %TRUE iff there's a foreground process running in @screen
2232  */
2233 gboolean
terminal_screen_has_foreground_process(TerminalScreen * screen,char ** process_name,char ** cmdline)2234 terminal_screen_has_foreground_process (TerminalScreen *screen,
2235                                         char           **process_name,
2236                                         char           **cmdline)
2237 {
2238   TerminalScreenPrivate *priv = screen->priv;
2239   gs_free char *command = nullptr;
2240   gs_free char *data_buf = nullptr;
2241   gs_free char *basename = nullptr;
2242   gs_free char *name = nullptr;
2243   VtePty *pty;
2244   int fd;
2245 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
2246   int mib[4];
2247 #else
2248   char filename[64];
2249 #endif
2250   char *data;
2251   gsize i;
2252   gsize len;
2253   int fgpid;
2254 
2255   if (priv->child_pid == -1)
2256     return FALSE;
2257 
2258   pty = vte_terminal_get_pty (VTE_TERMINAL (screen));
2259   if (pty == nullptr)
2260     return FALSE;
2261 
2262   fd = vte_pty_get_fd (pty);
2263   if (fd == -1)
2264     return FALSE;
2265 
2266   fgpid = tcgetpgrp (fd);
2267   if (fgpid == -1 || fgpid == priv->child_pid)
2268     return FALSE;
2269 
2270 #if defined(__FreeBSD__) || defined(__DragonFly__)
2271   mib[0] = CTL_KERN;
2272   mib[1] = KERN_PROC;
2273   mib[2] = KERN_PROC_ARGS;
2274   mib[3] = fgpid;
2275   if (sysctl (mib, G_N_ELEMENTS (mib), nullptr, &len, nullptr, 0) == -1)
2276       return TRUE;
2277 
2278   data_buf = (char *) g_malloc0 (len);
2279   if (sysctl (mib, G_N_ELEMENTS (mib), data_buf, &len, nullptr, 0) == -1)
2280       return TRUE;
2281   data = data_buf;
2282 #elif defined(__OpenBSD__)
2283   mib[0] = CTL_KERN;
2284   mib[1] = KERN_PROC_ARGS;
2285   mib[2] = fgpid;
2286   mib[3] = KERN_PROC_ARGV;
2287   if (sysctl (mib, G_N_ELEMENTS (mib), nullptr, &len, nullptr, 0) == -1)
2288       return TRUE;
2289 
2290   data_buf = g_malloc0 (len);
2291   if (sysctl (mib, G_N_ELEMENTS (mib), data_buf, &len, nullptr, 0) == -1)
2292       return TRUE;
2293   data = ((char**)data_buf)[0];
2294 #else
2295   g_snprintf (filename, sizeof (filename), "/proc/%d/cmdline", fgpid);
2296   if (!g_file_get_contents (filename, &data_buf, &len, nullptr))
2297     return TRUE;
2298   data = data_buf;
2299 #endif
2300 
2301   basename = g_path_get_basename (data);
2302   if (!basename)
2303     return TRUE;
2304 
2305   name = g_filename_to_utf8 (basename, -1, nullptr, nullptr, nullptr);
2306   if (!name)
2307     return TRUE;
2308 
2309   if (!process_name && !cmdline)
2310     return TRUE;
2311 
2312   gs_transfer_out_value (process_name, &name);
2313 
2314   if (len > 0 && data[len - 1] == '\0')
2315     len--;
2316   for (i = 0; i < len; i++)
2317     {
2318       if (data[i] == '\0')
2319         data[i] = ' ';
2320     }
2321 
2322   command = g_filename_to_utf8 (data, -1, nullptr, nullptr, nullptr);
2323   if (!command)
2324     return TRUE;
2325 
2326   gs_transfer_out_value (cmdline, &command);
2327 
2328   return TRUE;
2329 }
2330 
2331 const char *
terminal_screen_get_uuid(TerminalScreen * screen)2332 terminal_screen_get_uuid (TerminalScreen *screen)
2333 {
2334   g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), nullptr);
2335 
2336   return screen->priv->uuid;
2337 }
2338