1 /*
2  *  conterm.c
3  *
4  *  Copyright 2012 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gdk/gdkkeysyms.h>
28 
29 #include "common.h"
30 
31 #define NFD 5
32 #define DS_COPY (DS_BASICS | DS_EXTRA_1)
33 
34 #ifdef G_OS_UNIX
35 #include <vte/vte.h>
36 #include <gp_vtecompat.h>
37 /* instead of detecting N kinds of *nix */
38 #if defined(HAVE_UTIL_H)
39 #include <util.h>
40 #elif defined(HAVE_LIBUTIL_H)
41 #include <libutil.h>
42 #elif defined(HAVE_PTY_H)
43 #include <pty.h>
44 #endif
45 int grantpt(int fd);
46 int unlockpt(int fd);
47 
48 static GtkWidget *program_window;
49 static VteTerminal *program_terminal;
50 static GtkWidget *terminal_parent;
51 static GtkWidget *terminal_window;
52 static GtkCheckMenuItem *terminal_show;
53 
on_terminal_show(G_GNUC_UNUSED const MenuItem * menu_item)54 void on_terminal_show(G_GNUC_UNUSED const MenuItem *menu_item)
55 {
56 	GtkWidget *terminal = GTK_WIDGET(program_terminal);
57 
58 	if (gtk_check_menu_item_get_active(terminal_show))
59 	{
60 		gtk_container_remove(GTK_CONTAINER(program_window), terminal);
61 		gtk_widget_set_size_request(terminal, pref_terminal_width, pref_terminal_height);
62 		gtk_container_add(GTK_CONTAINER(terminal_window), terminal);
63 		gtk_widget_show(terminal_parent);
64 		gtk_window_move(GTK_WINDOW(terminal_parent), pref_terminal_window_x,
65 			pref_terminal_window_y);
66 	}
67 	else
68 	{
69 		gtk_window_get_position(GTK_WINDOW(terminal_parent), &pref_terminal_window_x,
70 			&pref_terminal_window_y);
71 		gtk_widget_get_size_request(terminal, &pref_terminal_width, &pref_terminal_height);
72 		gtk_widget_hide(terminal_parent);
73 		gtk_container_remove(GTK_CONTAINER(terminal_window), terminal);
74 		gtk_widget_set_size_request(terminal, -1, -1);
75 		gtk_container_add(GTK_CONTAINER(program_window), terminal);
76 	}
77 }
78 
terminal_clear(void)79 void terminal_clear(void)
80 {
81 	vte_terminal_reset(program_terminal, TRUE, TRUE);
82 }
83 
terminal_standalone(gboolean alone)84 void terminal_standalone(gboolean alone)
85 {
86 	gtk_check_menu_item_set_active(terminal_show, alone);
87 }
88 
on_terminal_parent_delete(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEvent * event,G_GNUC_UNUSED gpointer gdata)89 static gboolean on_terminal_parent_delete(G_GNUC_UNUSED GtkWidget *widget,
90 	G_GNUC_UNUSED GdkEvent *event, G_GNUC_UNUSED gpointer gdata)
91 {
92 	terminal_standalone(FALSE);
93 	return TRUE;
94 }
95 
on_terminal_copy(G_GNUC_UNUSED const MenuItem * menu_item)96 static void on_terminal_copy(G_GNUC_UNUSED const MenuItem *menu_item)
97 {
98 	vte_terminal_copy_clipboard(program_terminal);
99 }
100 
on_terminal_paste(G_GNUC_UNUSED const MenuItem * menu_item)101 static void on_terminal_paste(G_GNUC_UNUSED const MenuItem *menu_item)
102 {
103 	vte_terminal_paste_clipboard(program_terminal);
104 }
105 
on_terminal_feed(G_GNUC_UNUSED const MenuItem * menu_item)106 static void on_terminal_feed(G_GNUC_UNUSED const MenuItem *menu_item)
107 {
108 	gdouble value = 4;
109 
110 	if (dialogs_show_input_numeric(_("Feed Terminal"), _("Enter char # (0..255):"), &value,
111 		0, 255, 1))
112 	{
113 		char text = (char) value;
114 		vte_terminal_feed_child(program_terminal, &text, 1);
115 	}
116 }
117 
on_terminal_select_all(G_GNUC_UNUSED const MenuItem * menu_item)118 static void on_terminal_select_all(G_GNUC_UNUSED const MenuItem *menu_item)
119 {
120 	vte_terminal_select_all(program_terminal);
121 }
122 
on_terminal_clear(G_GNUC_UNUSED const MenuItem * menu_item)123 static void on_terminal_clear(G_GNUC_UNUSED const MenuItem *menu_item)
124 {
125 	vte_terminal_reset(program_terminal, TRUE, TRUE);
126 }
127 
128 /* show terminal on program startup */
129 gboolean terminal_auto_show;
130 /* hide terminal on program exit */
131 gboolean terminal_auto_hide;
132 /* show terminal on program exit with non-zero exit code */
133 gboolean terminal_show_on_error;
134 
135 static MenuItem terminal_menu_items[] =
136 {
137 	{ "terminal_copy",          on_terminal_copy,         DS_COPY, NULL, NULL },
138 	{ "terminal_paste",         on_terminal_paste,        0, NULL, NULL },
139 	{ "terminal_feed",          on_terminal_feed,         0, NULL, NULL },
140 	{ "terminal_select_all",    on_terminal_select_all,   0, NULL, NULL },
141 	{ "terminal_clear",         on_terminal_clear,        0, NULL, NULL },
142 	{ "terminal_show_hide",     on_menu_display_booleans, 0, NULL, GINT_TO_POINTER(3) },
143 	{ "terminal_auto_show",     on_menu_update_boolean,   0, NULL, &terminal_auto_show },
144 	{ "terminal_auto_hide",     on_menu_update_boolean,   0, NULL, &terminal_auto_hide },
145 	{ "terminal_show_on_error", on_menu_update_boolean,   0, NULL, &terminal_show_on_error },
146 	{ NULL, NULL, 0, NULL, NULL }
147 };
148 
terminal_menu_extra_state(void)149 static guint terminal_menu_extra_state(void)
150 {
151 	return vte_terminal_get_has_selection(program_terminal) << DS_INDEX_1;
152 }
153 
154 static MenuInfo terminal_menu_info = { terminal_menu_items, terminal_menu_extra_state, 0 };
155 
on_vte_realize(VteTerminal * vte,G_GNUC_UNUSED gpointer gdata)156 void on_vte_realize(VteTerminal *vte, G_GNUC_UNUSED gpointer gdata)
157 {
158 	vte_terminal_set_emulation(vte, pref_vte_emulation);
159 	vte_terminal_set_font_from_string(vte, pref_vte_font);
160 	vte_terminal_set_scrollback_lines(vte, pref_vte_scrollback);
161 	vte_terminal_set_scroll_on_output(vte, TRUE);
162 	vte_terminal_set_color_foreground(vte, &pref_vte_colour_fore);
163 	vte_terminal_set_color_background(vte, &pref_vte_colour_back);
164 #if VTE_CHECK_VERSION(0, 17, 1)
165 	vte_terminal_set_cursor_blink_mode(vte,
166 		pref_vte_blinken ? VTE_CURSOR_BLINK_ON : VTE_CURSOR_BLINK_OFF);
167 #else
168 	vte_terminal_set_cursor_blinks(vte, pref_vte_blinken);
169 #endif
170 }
171 
172 static VteTerminal *debug_console = NULL;  /* NULL -> GtkTextView "context" */
173 
console_output(int fd,const char * text,gint length)174 static void console_output(int fd, const char *text, gint length)
175 {
176 	static const char fd_colors[NFD] = { '6', '7', '1', '7', '5' };
177 	static char setaf[5] = { '\033', '[', '3', '?', 'm' };
178 	static int last_fd = -1;
179 	gint i;
180 
181 	if (last_fd == 3 && fd != 0)
182 		vte_terminal_feed(debug_console, "\r\n", 2);
183 
184 	if (fd != last_fd)
185 	{
186 		setaf[3] = fd_colors[fd];
187 		vte_terminal_feed(debug_console, setaf, sizeof(setaf));
188 		last_fd = fd;
189 	}
190 
191 	if (length == -1)
192 		length = strlen(text);
193 
194 	for (i = 0; i < length; i++)
195 	{
196 		if (text[i] == '\n')
197 		{
198 			vte_terminal_feed(debug_console, text, i);
199 			vte_terminal_feed(debug_console, "\r", 2);
200 			length -= i;
201 			text += i;
202 			i = 0;
203 		}
204 	}
205 
206 	vte_terminal_feed(debug_console, text, length);
207 }
208 
console_output_nl(int fd,const char * text,gint length)209 static void console_output_nl(int fd, const char *text, gint length)
210 {
211 	dc_output(fd, text, length);
212 	vte_terminal_feed(debug_console, "\r\n", 2);
213 }
214 #endif  /* G_OS_UNIX */
215 
216 static GtkTextView *debug_context;
217 static GtkTextBuffer *context;
218 static GtkTextTag *fd_tags[NFD];
219 #define DC_LIMIT 32768  /* approx */
220 #define DC_DELTA 6144
221 static guint dc_chars = 0;
222 
context_output(int fd,const char * text,gint length)223 void context_output(int fd, const char *text, gint length)
224 {
225 	static int last_fd = -1;
226 	GtkTextIter end;
227 	gchar *utf8;
228 
229 	gtk_text_buffer_get_end_iter(context, &end);
230 
231 	if (last_fd == 3 && fd != 0)
232 		gtk_text_buffer_insert(context, &end, "\n", 1);
233 
234 	if (fd != last_fd)
235 		last_fd = fd;
236 
237 	if (length == -1)
238 		length = strlen(text);
239 
240 	dc_chars += length;
241 	utf8 = g_locale_to_utf8(text, length, NULL, NULL, NULL);
242 
243 	if (utf8)
244 	{
245 		gtk_text_buffer_insert_with_tags(context, &end, utf8, -1, fd_tags[fd], NULL);
246 		g_free(utf8);
247 	}
248 	else
249 		gtk_text_buffer_insert_with_tags(context, &end, text, length, fd_tags[fd], NULL);
250 
251 	if (dc_chars > DC_LIMIT + (DC_DELTA / 2))
252 	{
253 		GtkTextIter start, delta;
254 
255 		gtk_text_buffer_get_start_iter(context, &start);
256 		gtk_text_buffer_get_iter_at_offset(context, &delta, DC_DELTA);
257 		gtk_text_buffer_delete(context, &start, &delta);
258 		gtk_text_buffer_get_end_iter(context, &end);
259 		dc_chars = gtk_text_buffer_get_char_count(context);
260 	}
261 
262 	gtk_text_buffer_place_cursor(context, &end);
263 	gtk_text_view_scroll_mark_onscreen(debug_context, gtk_text_buffer_get_insert(context));
264 }
265 
context_output_nl(int fd,const char * text,gint length)266 void context_output_nl(int fd, const char *text, gint length)
267 {
268 	dc_output(fd, text, length);
269 	dc_output(fd, "\n", 1);
270 }
271 
on_console_button_3_press(G_GNUC_UNUSED GtkWidget * widget,GdkEventButton * event,GtkMenu * menu)272 static gboolean on_console_button_3_press(G_GNUC_UNUSED GtkWidget *widget,
273 	GdkEventButton *event, GtkMenu *menu)
274 {
275 	if (event->button == 3)
276 	{
277 		gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
278 		return TRUE;
279 	}
280 
281 	return FALSE;
282 }
283 
284 void (*dc_output)(int fd, const char *text, gint length);
285 void (*dc_output_nl)(int fd, const char *text, gint length);
286 
dc_error(const char * format,...)287 void dc_error(const char *format, ...)
288 {
289 	char *string;
290 	va_list args;
291 
292 	va_start(args, format);
293 	string = g_strdup_vprintf(format, args);
294 	va_end(args);
295 
296 	dc_output_nl(4, string, -1);
297 	g_free(string);
298 	plugin_blink();
299 }
300 
dc_clear(void)301 void dc_clear(void)
302 {
303 #ifdef G_OS_UNIX
304 	if (debug_console)
305 		vte_terminal_reset(debug_console, TRUE, TRUE);
306 	else
307 #endif
308 	{
309 		gtk_text_buffer_set_text(context, "", -1);
310 		dc_chars = 0;
311 	}
312 }
313 
dc_update(void)314 gboolean dc_update(void)
315 {
316 	if (thread_state == THREAD_AT_ASSEMBLER)
317 		debug_send_format(T, "04-data-disassemble -s $pc -e $pc+1 0");
318 
319 	return TRUE;
320 }
321 
on_console_copy(G_GNUC_UNUSED const MenuItem * menu_item)322 static void on_console_copy(G_GNUC_UNUSED const MenuItem *menu_item)
323 {
324 #ifdef G_OS_UNIX
325 	if (debug_console)
326 		vte_terminal_copy_clipboard(debug_console);
327 	else
328 #endif
329 	{
330 		g_signal_emit_by_name(debug_context, "copy-clipboard");
331 	}
332 }
333 
on_console_select_all(G_GNUC_UNUSED const MenuItem * menu_item)334 static void on_console_select_all(G_GNUC_UNUSED const MenuItem *menu_item)
335 {
336 #ifdef G_OS_UNIX
337 	if (debug_console)
338 		vte_terminal_select_all(program_terminal);
339 	else
340 #endif
341 	{
342 		g_signal_emit_by_name(debug_context, "select-all");
343 	}
344 }
345 
on_console_clear(G_GNUC_UNUSED const MenuItem * menu_item)346 static void on_console_clear(G_GNUC_UNUSED const MenuItem *menu_item)
347 {
348 	dc_clear();
349 }
350 
on_console_key_press(G_GNUC_UNUSED GtkWidget * widget,GdkEventKey * event,G_GNUC_UNUSED gpointer gdata)351 static gboolean on_console_key_press(G_GNUC_UNUSED GtkWidget *widget,
352 	GdkEventKey *event, G_GNUC_UNUSED gpointer gdata)
353 {
354 	gboolean insert = event->keyval == GDK_Insert || event->keyval == GDK_KP_Insert;
355 
356 	if ((insert || (event->keyval >= 0x21 && event->keyval <= 0x7F &&
357 		event->state <= GDK_SHIFT_MASK)) && (debug_state() & DS_ACTIVE))
358 	{
359 		char command[2] = { event->keyval, '\0' };
360 		view_command_line(insert ? NULL : command, NULL, NULL, TRUE);
361 		return TRUE;
362 	}
363 
364 	return FALSE;
365 }
366 
367 static MenuItem console_menu_items[] =
368 {
369 	{ "console_copy",       on_console_copy,       DS_COPY, NULL, NULL },
370 	{ "console_select_all", on_console_select_all, 0,       NULL, NULL },
371 	{ "console_clear",      on_console_clear,      0,       NULL, NULL },
372 	{ NULL, NULL, 0, NULL, NULL }
373 };
374 
console_menu_extra_state(void)375 static guint console_menu_extra_state(void)
376 {
377 #ifdef G_OS_UNIX
378 	if (debug_console)
379 		return vte_terminal_get_has_selection(debug_console) << DS_INDEX_1;
380 #endif
381 	return gtk_text_buffer_get_has_selection(context) << DS_INDEX_1;
382 }
383 
384 static MenuInfo console_menu_info = { console_menu_items, console_menu_extra_state, 0 };
385 
conterm_load_config(void)386 void conterm_load_config(void)
387 {
388 	gchar *configfile = g_build_filename(geany_data->app->configdir, "geany.conf", NULL);
389 	GKeyFile *config = g_key_file_new();
390 	gchar *tmp_string;
391 
392 	g_key_file_load_from_file(config, configfile, G_KEY_FILE_NONE, NULL);
393 	pref_vte_blinken = utils_get_setting_boolean(config, "VTE", "cursor_blinks", FALSE);
394 	pref_vte_emulation = utils_get_setting_string(config, "VTE", "emulation", "xterm");
395 	pref_vte_font = utils_get_setting_string(config, "VTE", "font", "Monospace 10");
396 	pref_vte_scrollback = utils_get_setting_integer(config, "VTE", "scrollback_lines", 500);
397 	tmp_string = utils_get_setting_string(config, "VTE", "colour_fore", "#ffffff");
398 #if !GTK_CHECK_VERSION(3, 14, 0)
399 	gdk_color_parse(tmp_string, &pref_vte_colour_fore);
400 #else
401 	gdk_rgba_parse(&pref_vte_colour_fore, tmp_string);
402 #endif
403 	g_free(tmp_string);
404 	tmp_string = utils_get_setting_string(config, "VTE", "colour_back", "#000000");
405 #if !GTK_CHECK_VERSION(3, 14, 0)
406 	gdk_color_parse(tmp_string, &pref_vte_colour_back);
407 #else
408 	gdk_rgba_parse(&pref_vte_colour_back, tmp_string);
409 #endif
410 	g_free(tmp_string);
411 	g_key_file_free(config);
412 	g_free(configfile);
413 }
414 
context_apply_config(GtkWidget * console)415 static void context_apply_config(GtkWidget *console)
416 {
417 #if !GTK_CHECK_VERSION(3, 0, 0)
418 	gtk_widget_modify_base(console, GTK_STATE_NORMAL, &pref_vte_colour_back);
419 	gtk_widget_modify_cursor(console, &pref_vte_colour_fore, &pref_vte_colour_back);
420 #else
421 	GString *css_string;
422 	GtkStyleContext *context;
423 	GtkCssProvider *provider;
424 	gchar *css_code, *color, *background_color;
425 
426 	color = gdk_rgba_to_string (&pref_vte_colour_fore);
427 	background_color = gdk_rgba_to_string (&pref_vte_colour_back);
428 
429 	gtk_widget_set_name(console, "scope-console");
430 	context = gtk_widget_get_style_context(console);
431 	provider = gtk_css_provider_new();
432 	gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
433 		GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
434 
435 	css_string = g_string_new(NULL);
436 	g_string_printf(css_string, "#scope-console { color: %s; background-color: %s; }",
437 		color, background_color);
438 	css_code = g_string_free(css_string, FALSE);
439 
440 	gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(provider), css_code, -1, NULL);
441 
442 	g_free(css_code);
443 	g_object_unref(provider);
444 #endif
445 	ui_widget_modify_font_from_string(console, pref_vte_font);
446 }
447 
conterm_apply_config(void)448 void conterm_apply_config(void)
449 {
450 #ifdef G_OS_UNIX
451 	on_vte_realize(program_terminal, NULL);
452 
453 	if (debug_console)
454 		on_vte_realize(debug_console, NULL);
455 	else
456 #endif
457 	{
458 		context_apply_config(GTK_WIDGET(debug_context));
459 	}
460 }
461 
462 #ifdef G_OS_UNIX
463 static int pty_slave = -1;
464 char *slave_pty_name = NULL;
465 #endif
466 
conterm_init(void)467 void conterm_init(void)
468 {
469 	GtkWidget *console;
470 #ifdef G_OS_UNIX
471 	gchar *error = NULL;
472 	int pty_master;
473 	char *pty_name;
474 #endif
475 
476 	conterm_load_config();
477 #ifdef G_OS_UNIX
478 	program_window = get_widget("program_window");
479 	console = vte_terminal_new();
480 	gtk_widget_show(console);
481 	program_terminal = VTE_TERMINAL(console);
482 	g_object_ref(program_terminal);
483 	gtk_container_add(GTK_CONTAINER(program_window), console);
484 	g_signal_connect_after(program_terminal, "realize", G_CALLBACK(on_vte_realize), NULL);
485 	terminal_parent = get_widget("terminal_parent");
486 	g_signal_connect(terminal_parent, "delete-event", G_CALLBACK(on_terminal_parent_delete),
487 		NULL);
488 	terminal_window = get_widget("terminal_window");
489 	terminal_show = GTK_CHECK_MENU_ITEM(get_widget("terminal_show"));
490 
491 	if (pref_terminal_padding)
492 	{
493 		gint vte_border_x, vte_border_y;
494 
495 #if GTK_CHECK_VERSION(3, 4, 0)
496 		GtkStyleContext *context;
497 		GtkBorder border;
498 
499 		context = gtk_widget_get_style_context (console);
500 		gtk_style_context_get_padding (context, GTK_STATE_FLAG_NORMAL, &border);
501 		vte_border_x = border.left + border.right;
502 		vte_border_y = border.top + border.bottom;
503 #elif VTE_CHECK_VERSION(0, 24, 0)
504 		GtkBorder *border = NULL;
505 
506 		gtk_widget_style_get(console, "inner-border", &border, NULL);
507 
508 		if (border)
509 		{
510 			vte_border_x = border->left + border->right;
511 			vte_border_y = border->top + border->bottom;
512 			gtk_border_free(border);
513 		}
514 		else
515 			vte_border_x = vte_border_y = 2;
516 #else  /* VTE 0.24.0 */
517 		/* VTE manual says "deprecated since 0.26", but it's since 0.24 */
518 		vte_terminal_get_padding(program_terminal, &vte_border_x, &vte_border_y);
519 #endif  /* VTE 0.24.0 */
520 		pref_terminal_width += vte_border_x;
521 		pref_terminal_height += vte_border_y;
522 		pref_terminal_padding = FALSE;
523 	}
524 
525 	if (openpty(&pty_master, &pty_slave, NULL, NULL, NULL) == 0 &&
526 		grantpt(pty_master) == 0 && unlockpt(pty_master) == 0 &&
527 		(pty_name = ttyname(pty_slave)) != NULL)
528 	{
529 #if VTE_CHECK_VERSION(0, 25, 0)
530 		GError *gerror = NULL;
531 		VtePty *pty = vte_pty_new_foreign(pty_master, &gerror);
532 
533 		if (pty)
534 		{
535 			vte_terminal_set_pty_object(program_terminal, pty);
536 			slave_pty_name = g_strdup(pty_name);
537 		}
538 		else
539 		{
540 			error = g_strdup(gerror->message);
541 			g_error_free(gerror);
542 		}
543 #else  /* VTE 0.25.0 */
544 		vte_terminal_set_pty(program_terminal, pty_master);
545 		slave_pty_name = g_strdup(pty_name);
546 #endif  /* VTE 0.25.0 */
547 	}
548 	else
549 		error = g_strdup_printf("pty: %s", g_strerror(errno));
550 
551 	if (error)
552 	{
553 		gtk_widget_set_sensitive(program_window, FALSE);
554 		gtk_widget_set_sensitive(GTK_WIDGET(terminal_show), FALSE);
555 		msgwin_status_add(_("Scope: %s."), error);
556 		g_free(error);
557 	}
558 	else
559 		menu_connect("terminal_menu", &terminal_menu_info, GTK_WIDGET(program_terminal));
560 #else  /* G_OS_UNIX */
561 	gtk_widget_hide(get_widget("program_window"));
562 #endif  /* G_OS_UNIX */
563 
564 #ifdef G_OS_UNIX
565 	if (pref_debug_console_vte)
566 	{
567 		console = vte_terminal_new();
568 		gtk_widget_show(console);
569 		debug_console = VTE_TERMINAL(console);
570 		dc_output = console_output;
571 		dc_output_nl = console_output_nl;
572 		g_signal_connect_after(debug_console, "realize", G_CALLBACK(on_vte_realize), NULL);
573 		menu_connect("console_menu", &console_menu_info, console);
574 	}
575 	else
576 #endif  /* G_OS_UNIX */
577 	{
578 		static const char *const colors[NFD] = { "#00C0C0", "#C0C0C0", "#C00000",
579 			"#C0C0C0", "#C000C0" };
580 		guint i;
581 
582 		console = get_widget("debug_context");
583 		context_apply_config(console);
584 		debug_context = GTK_TEXT_VIEW(console);
585 		dc_output = context_output;
586 		dc_output_nl = context_output_nl;
587 		context = gtk_text_view_get_buffer(debug_context);
588 
589 		for (i = 0; i < NFD; i++)
590 		{
591 			fd_tags[i] = gtk_text_buffer_create_tag(context, NULL, "foreground",
592 				colors[i], NULL);
593 		}
594 		g_signal_connect(console, "button-press-event",
595 			G_CALLBACK(on_console_button_3_press),
596 			menu_connect("console_menu", &console_menu_info, NULL));
597 	}
598 
599 	gtk_container_add(GTK_CONTAINER(get_widget("debug_window")), console);
600 	g_signal_connect(console, "key-press-event", G_CALLBACK(on_console_key_press), NULL);
601 }
602 
conterm_finalize(void)603 void conterm_finalize(void)
604 {
605 #ifdef G_OS_UNIX
606 	g_object_unref(program_terminal);
607 	g_free(slave_pty_name);
608 	close(pty_slave);
609 	/* close(pty_master) causes 100% CPU load on 0.24 and is not allowed on 0.26+ */
610 #endif  /* G_OS_UNIX */
611 }
612