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, ×tamp_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