1 /** \file uimon.c
2 * \brief Native GTK3 UI monitor stuff
3 *
4 * \author Fabrizio Gennari <fabrizio.ge@tiscali.it>
5 */
6
7 /*
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 #include "vice.h"
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "novte/novte.h"
34
35 #define vte_terminal_new novte_terminal_new
36 #define vte_terminal_feed novte_terminal_feed
37 #define vte_terminal_get_column_count novte_terminal_get_column_count
38 #define vte_terminal_copy_clipboard novte_terminal_copy_clipboard
39 #define vte_terminal_get_row_count novte_terminal_get_row_count
40 #define vte_terminal_set_scrollback_lines novte_terminal_set_scrollback_lines
41 #define vte_terminal_set_scroll_on_output novte_terminal_set_scroll_on_output
42 #define vte_terminal_get_char_width novte_terminal_get_char_width
43 #define vte_terminal_get_char_height novte_terminal_get_char_height
44
45 #define VTE_TERMINAL(x) NOVTE_TERMINAL(x)
46 #define VteTerminal NoVteTerminal
47
48 #include <dirent.h>
49 #include <ctype.h>
50 #include <unistd.h>
51 #ifdef HAVE_SYS_IOCTL_H
52 #include <sys/ioctl.h>
53 #endif
54
55 #if (defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__))
56 #include <sys/stat.h>
57 #endif
58
59 #include "console.h"
60 #include "debug_gtk3.h"
61 #include "monitor.h"
62 #include "resources.h"
63 #include "lib.h"
64 #include "ui.h"
65 #include "linenoise.h"
66 #include "uimon.h"
67 #include "uimonarch.h"
68 #include "uimon-fallback.h"
69 #include "mon_command.h"
70 #include "vsync.h"
71 #include "uidata.h"
72
73 struct console_private_s {
74 GtkWidget *window;
75 GtkWidget *term;
76 char *input_buffer;
77 } fixed = {NULL, NULL, NULL};
78
79 static console_t vte_console;
80 static linenoiseCompletions command_lc = {0, NULL};
81 static linenoiseCompletions need_filename_lc = {0, NULL};
82
83 /* FIXME: this should perhaps be done using some function from archdep */
is_dir(struct dirent * de)84 static int is_dir(struct dirent *de)
85 {
86 #if 0 /* FIXME: mingw */
87
88 #if (defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__))
89 struct stat t;
90
91 stat(de->d_name, &t);
92 if ((t.st_mode & S_IFMT) == S_IFDIR) {
93 return 1;
94 }
95 #else
96 if (de->d_type == DT_DIR) {
97 return 1;
98 }
99 #endif
100
101 #endif
102 return 0;
103 }
104
native_monitor(void)105 static int native_monitor(void)
106 {
107 int res = 0;
108 resources_get_int("NativeMonitor", &res);
109 return res;
110 }
111
uimon_write_to_terminal(struct console_private_s * t,const char * data,glong length)112 void uimon_write_to_terminal(struct console_private_s *t,
113 const char *data,
114 glong length)
115 {
116 if(t->term) {
117 vte_terminal_feed(VTE_TERMINAL(t->term), data, length);
118 }
119 }
120
121
uimon_get_columns(struct console_private_s * t)122 int uimon_get_columns(struct console_private_s *t)
123 {
124 if(t->term) {
125 return vte_terminal_get_column_count(VTE_TERMINAL(t->term));
126 }
127 return 80;
128 }
129
append_char_to_input_buffer(char * old_input_buffer,char new_char)130 static char* append_char_to_input_buffer(char *old_input_buffer, char new_char)
131 {
132 char* new_input_buffer = lib_msprintf("%s%c",
133 old_input_buffer ? old_input_buffer : "",
134 new_char);
135 lib_free(old_input_buffer);
136 return new_input_buffer;
137 }
138
append_string_to_input_buffer(char * old_input_buffer,GtkWidget * terminal,GdkAtom clipboard_to_use)139 static char* append_string_to_input_buffer(char *old_input_buffer, GtkWidget *terminal, GdkAtom clipboard_to_use)
140 {
141 GtkClipboard *clipboard = gtk_widget_get_clipboard(terminal, clipboard_to_use);
142 gchar *new_string = gtk_clipboard_wait_for_text(clipboard);
143
144 if (new_string != NULL) {
145 char *new_input_buffer = lib_realloc(old_input_buffer, strlen(old_input_buffer) + strlen(new_string) + 1);
146 char *char_in, *char_out = new_input_buffer + strlen(new_input_buffer);
147
148 for (char_in = new_string; *char_in; char_in++) {
149 #if CHAR_MIN < 0
150 if (*char_in < 0 || *char_in >= 32) {
151 #else
152 /* char is unsigned on raspberry Pi 2B with GCC */
153 if (*char_in >= 32) {
154 #endif
155 *char_out++ = *char_in;
156 }
157 }
158 *char_out = 0;
159 g_free(new_string);
160
161 return new_input_buffer;
162 }
163 return old_input_buffer;
164 }
165
166 static gboolean plain_key_pressed(char **input_buffer, guint keyval)
167 {
168 switch (keyval) {
169 default:
170 if(keyval >= GDK_KEY_space && keyval <= GDK_KEY_ydiaeresis) {
171 *input_buffer = append_char_to_input_buffer(*input_buffer, (char)keyval);
172 return TRUE;
173 }
174 return FALSE;
175 case GDK_KEY_Return:
176 *input_buffer = append_char_to_input_buffer(*input_buffer, 13);
177 return TRUE;
178 case GDK_KEY_BackSpace:
179 *input_buffer = append_char_to_input_buffer(*input_buffer, 127);
180 return TRUE;
181 case GDK_KEY_Left:
182 *input_buffer = append_char_to_input_buffer(*input_buffer, 2);
183 return TRUE;
184 case GDK_KEY_Right:
185 *input_buffer = append_char_to_input_buffer(*input_buffer, 6);
186 return TRUE;
187 case GDK_KEY_Up:
188 *input_buffer = append_char_to_input_buffer(*input_buffer, 16);
189 return TRUE;
190 case GDK_KEY_Down:
191 *input_buffer = append_char_to_input_buffer(*input_buffer, 14);
192 return TRUE;
193 case GDK_KEY_Tab:
194 *input_buffer = append_char_to_input_buffer(*input_buffer, 9);
195 return TRUE;
196 case GDK_KEY_Delete:
197 *input_buffer = append_char_to_input_buffer(*input_buffer, 4);
198 return TRUE;
199 case GDK_KEY_Home:
200 *input_buffer = append_char_to_input_buffer(*input_buffer, 1);
201 return TRUE;
202 case GDK_KEY_End:
203 *input_buffer = append_char_to_input_buffer(*input_buffer, 5);
204 return TRUE;
205 case GDK_KEY_dead_tilde:
206 *input_buffer =
207 append_char_to_input_buffer(*input_buffer, GDK_KEY_asciitilde);
208 return TRUE;
209 }
210 }
211
212 static gboolean ctrl_plus_key_pressed(char **input_buffer, guint keyval, GtkWidget *terminal)
213 {
214 switch (keyval) {
215 default:
216 return FALSE;
217 case GDK_KEY_h:
218 case GDK_KEY_H:
219 *input_buffer = append_char_to_input_buffer(*input_buffer, 127);
220 return TRUE;
221 case GDK_KEY_b:
222 case GDK_KEY_B:
223 *input_buffer = append_char_to_input_buffer(*input_buffer, 2);
224 return TRUE;
225 case GDK_KEY_f:
226 case GDK_KEY_F:
227 *input_buffer = append_char_to_input_buffer(*input_buffer, 6);
228 return TRUE;
229 case GDK_KEY_p:
230 case GDK_KEY_P:
231 *input_buffer = append_char_to_input_buffer(*input_buffer, 16);
232 return TRUE;
233 case GDK_KEY_n:
234 case GDK_KEY_N:
235 *input_buffer = append_char_to_input_buffer(*input_buffer, 14);
236 return TRUE;
237 case GDK_KEY_t:
238 case GDK_KEY_T:
239 *input_buffer = append_char_to_input_buffer(*input_buffer, 20);
240 return TRUE;
241 case GDK_KEY_d:
242 case GDK_KEY_D:
243 /* ctrl-d, remove char at right of cursor */
244 *input_buffer = append_char_to_input_buffer(*input_buffer, 4);
245 return TRUE;
246 case GDK_KEY_u:
247 case GDK_KEY_U:
248 /* Ctrl+u, delete the whole line. */
249 *input_buffer = append_char_to_input_buffer(*input_buffer, 21);
250 return TRUE;
251 case GDK_KEY_k:
252 case GDK_KEY_K:
253 /* Ctrl+k, delete from current to end of line. */
254 *input_buffer = append_char_to_input_buffer(*input_buffer, 11);
255 return TRUE;
256 case GDK_KEY_a:
257 case GDK_KEY_A:
258 /* Ctrl+a, go to the start of the line */
259 *input_buffer = append_char_to_input_buffer(*input_buffer, 1);
260 return TRUE;
261 case GDK_KEY_e:
262 case GDK_KEY_E:
263 /* ctrl+e, go to the end of the line */
264 *input_buffer = append_char_to_input_buffer(*input_buffer, 5);
265 return TRUE;
266 case GDK_KEY_c:
267 case GDK_KEY_C:
268 vte_terminal_copy_clipboard(VTE_TERMINAL(terminal));
269 /* _format only exists in bleeding edge VTE 0.50 */
270 /* vte_terminal_copy_clipboard_format(VTE_TERMINAL(terminal), VTE_FORMAT_TEXT); */
271 return TRUE;
272 case GDK_KEY_v:
273 case GDK_KEY_V:
274 *input_buffer = append_string_to_input_buffer(*input_buffer, terminal, GDK_SELECTION_CLIPBOARD);
275 return TRUE;
276 }
277 }
278
279 static gboolean key_press_event (GtkWidget *widget,
280 GdkEventKey *event,
281 gpointer user_data)
282 {
283 char **input_buffer = (char **)user_data;
284 GdkModifierType state = 0;
285
286 gdk_event_get_state((GdkEvent*)event, &state);
287
288 if (*input_buffer && event->type == GDK_KEY_PRESS) {
289 switch (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {
290 case 0:
291 case GDK_SHIFT_MASK:
292 return plain_key_pressed(input_buffer, event->keyval);
293 case GDK_CONTROL_MASK:
294 return ctrl_plus_key_pressed(input_buffer, event->keyval, widget);
295 default:
296 return FALSE;
297 }
298 }
299 return FALSE;
300 }
301
302
303 static gboolean button_press_event(GtkWidget *widget,
304 GdkEvent *event,
305 gpointer user_data)
306 {
307 char **input_buffer = (char **)user_data;
308 GdkEventButton *button_event = (GdkEventButton*)event;
309
310 if (button_event->button != 2
311 || button_event->type != GDK_BUTTON_PRESS) {
312 return FALSE;
313 }
314
315 *input_buffer = append_string_to_input_buffer(*input_buffer, widget, GDK_SELECTION_PRIMARY);
316 return TRUE;
317 }
318
319 static gboolean close_window(GtkWidget *widget, GdkEvent *event, gpointer user_data)
320 {
321 char **input_buffer = (char **)user_data;
322 lib_free(*input_buffer);
323 *input_buffer = NULL;
324 return gtk_widget_hide_on_delete(widget);
325 }
326
327
328 int uimon_get_string(struct console_private_s *t, char* string, int string_len)
329 {
330 int retval=0;
331 while(retval<string_len) {
332 int i;
333
334 gtk_main_iteration();
335 if (!t->input_buffer) {
336 return -1;
337 }
338 for (i = 0; i < strlen(t->input_buffer) && retval < string_len; i++, retval++) {
339 string[retval]=t->input_buffer[i];
340 }
341 memmove(t->input_buffer, t->input_buffer + i, strlen(t->input_buffer) + 1 - i);
342 }
343 return retval;
344 }
345
346 static void get_terminal_size_in_chars(VteTerminal *terminal,
347 glong *width,
348 glong *height)
349 {
350 *width = vte_terminal_get_column_count(terminal);
351 *height = vte_terminal_get_row_count(terminal);
352 }
353
354 static void screen_resize_window_cb (VteTerminal *terminal,
355 gpointer* window)
356 {
357 glong width, height;
358 get_terminal_size_in_chars(terminal, &width, &height);
359 vte_console.console_xres = (unsigned int)width;
360 vte_console.console_yres = (unsigned int)height;
361 }
362
363 /* resize the terminal when the window is resized */
364 static void screen_resize_window_cb2 (VteTerminal *terminal,
365 gpointer* window)
366 {
367 int width, height;
368 int cwidth, cheight;
369 int newwidth, newheight;
370
371 gtk_window_get_size (GTK_WINDOW(fixed.window), &width, &height);
372 cwidth = vte_terminal_get_char_width (VTE_TERMINAL(fixed.term));
373 cheight = vte_terminal_get_char_height (VTE_TERMINAL(fixed.term));
374
375 newwidth = width / cwidth;
376 newheight = height / cheight;
377 if (newwidth < 1) {
378 newwidth = 1;
379 }
380 if (newheight < 1) {
381 newheight = 1;
382 }
383
384 vte_terminal_set_size(VTE_TERMINAL(fixed.term), newwidth, newheight);
385 }
386
387 /** \brief Create an icon by loading it from the vice.gresource file
388 *
389 * \return Standard C= icon ripped from the internet (but at least scalable)
390 * Which ofcourse sucks on Windows for some reason, *sigh*
391 */
392 static GdkPixbuf *get_default_icon(void)
393 {
394 return uidata_get_pixbuf("CBM_Logo.svg");
395 }
396
397 console_t *uimonfb_window_open(void);
398
399 console_t *uimon_window_open(void)
400 {
401 GtkWidget *scrollbar, *horizontal_container;
402 GdkGeometry hints;
403 GdkPixbuf *icon;
404
405 if (native_monitor()) {
406 return uimonfb_window_open();
407 }
408
409 if (fixed.window == NULL) {
410 fixed.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
411 gtk_window_set_title(GTK_WINDOW(fixed.window), "VICE monitor");
412 gtk_window_set_position(GTK_WINDOW(fixed.window), GTK_WIN_POS_CENTER);
413 gtk_widget_set_app_paintable(fixed.window, TRUE);
414 gtk_window_set_deletable(GTK_WINDOW(fixed.window), TRUE);
415
416 /* set a default C= icon for now */
417 icon = get_default_icon();
418 if (icon != NULL) {
419 gtk_window_set_icon(GTK_WINDOW(fixed.window), icon);
420 }
421
422 fixed.term = vte_terminal_new();
423 vte_terminal_set_scrollback_lines (VTE_TERMINAL(fixed.term), 1000);
424 vte_terminal_set_scroll_on_output (VTE_TERMINAL(fixed.term), TRUE);
425
426 /* allowed window widths are base_width + width_inc * N
427 * allowed window heights are base_height + height_inc * N
428 */
429 hints.width_inc = vte_terminal_get_char_width (VTE_TERMINAL(fixed.term));
430 hints.height_inc = vte_terminal_get_char_height (VTE_TERMINAL(fixed.term));
431 /* min size should be multiple of .._inc, else we get funky effects */
432 hints.min_width = hints.width_inc;
433 hints.min_height = hints.height_inc;
434 /* base size should be multiple of .._inc, else we get funky effects */
435 hints.base_width = hints.width_inc;
436 hints.base_height = hints.height_inc;
437 gtk_window_set_geometry_hints (GTK_WINDOW (fixed.window),
438 fixed.term,
439 &hints,
440 GDK_HINT_RESIZE_INC |
441 GDK_HINT_MIN_SIZE |
442 GDK_HINT_BASE_SIZE);
443 scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL,
444 gtk_scrollable_get_vadjustment (GTK_SCROLLABLE(fixed.term)));
445
446 horizontal_container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
447 gtk_container_add(GTK_CONTAINER(fixed.window), horizontal_container);
448 gtk_container_add(GTK_CONTAINER(horizontal_container), fixed.term);
449 gtk_container_add(GTK_CONTAINER(horizontal_container), scrollbar);
450
451 g_signal_connect(G_OBJECT(fixed.window), "delete-event",
452 G_CALLBACK(close_window), &fixed.input_buffer);
453
454 g_signal_connect(G_OBJECT(fixed.term), "key-press-event",
455 G_CALLBACK(key_press_event), &fixed.input_buffer);
456
457 g_signal_connect(G_OBJECT(fixed.term), "button-press-event",
458 G_CALLBACK(button_press_event), &fixed.input_buffer);
459
460 g_signal_connect(G_OBJECT(fixed.term), "text-modified",
461 G_CALLBACK (screen_resize_window_cb), NULL);
462
463 g_signal_connect(G_OBJECT(fixed.window), "configure-event",
464 G_CALLBACK (screen_resize_window_cb2), NULL);
465
466 vte_console.console_can_stay_open = 1;
467 }
468 return uimon_window_resume();
469 }
470
471 console_t *uimon_window_resume(void)
472 {
473 if (native_monitor()) {
474 return uimonfb_window_resume();
475 }
476
477 gtk_widget_show_all(fixed.window);
478 screen_resize_window_cb (VTE_TERMINAL(fixed.term), NULL);
479 gtk_window_present(GTK_WINDOW(fixed.window));
480 ui_dispatch_events();
481 return &vte_console;
482 }
483
484 void uimon_window_suspend(void)
485 {
486 if (native_monitor()) {
487 uimonfb_window_suspend();
488 return;
489 }
490 }
491
492 int uimon_out(const char *buffer)
493 {
494 const char *c;
495 if (native_monitor()) {
496 return uimonfb_out(buffer);
497 }
498 for(c = buffer; *c; c++) {
499 if(*c == '\n') {
500 uimon_write_to_terminal(&fixed, "\r", 1);
501 }
502 uimon_write_to_terminal(&fixed, c, 1);
503 }
504 return 0;
505 }
506
507 void uimon_window_close(void)
508 {
509 if (native_monitor()) {
510 uimonfb_window_close();
511 return;
512 }
513
514 /* only close window if there is one: this avoids a GTK_CRITICAL warning
515 * when using a remote monitor */
516 if (fixed.window != NULL) {
517 gtk_widget_hide(fixed.window);
518 }
519 }
520
521 void uimon_notify_change(void)
522 {
523 if (native_monitor()) {
524 uimonfb_notify_change();
525 return;
526 }
527 }
528
529 void uimon_set_interface(struct monitor_interface_s **interf, int i)
530 {
531 if (native_monitor()) {
532 uimonfb_set_interface(interf, i);
533 return;
534 }
535 }
536
537 static char* concat_strings(const char *string1, int nchars, const char *string2)
538 {
539 char *ret = lib_malloc(nchars + strlen(string2) + 1);
540 memcpy(ret, string1, nchars);
541 strcpy(ret + nchars, string2);
542 return ret;
543 }
544
545 static void fill_completions(const char *string_so_far, int initial_chars, int token_len, const linenoiseCompletions *possible_lc, linenoiseCompletions *lc)
546 {
547 int word_index;
548
549 lc->len = 0;
550 for(word_index = 0; word_index < possible_lc->len; word_index++) {
551 int i;
552 for(i = 0; i < token_len; i++) {
553 if (string_so_far[initial_chars + i] != possible_lc->cvec[word_index][i]) {
554 break;
555 }
556 }
557 if (i == token_len && possible_lc->cvec[word_index][token_len] != 0) {
558 char *string_to_append = concat_strings(string_so_far, initial_chars, possible_lc->cvec[word_index]);
559 linenoiseAddCompletion(lc, string_to_append);
560 lib_free(string_to_append);
561 }
562 }
563 }
564
565 static void find_next_token(const char *string_so_far, int start_of_search, int *start_of_token, int *token_len)
566 {
567 for(*start_of_token = start_of_search; string_so_far[*start_of_token] && isspace((int)(string_so_far[*start_of_token])); (*start_of_token)++);
568 for(*token_len = 0; string_so_far[*start_of_token + *token_len] && !isspace((int)(string_so_far[*start_of_token + *token_len])); (*token_len)++);
569 }
570
571 static gboolean is_token_in(const char *string_so_far, int token_len, const linenoiseCompletions *lc)
572 {
573 int i;
574 for(i = 0; i < lc->len; i++) {
575 if(strlen(lc->cvec[i]) == token_len && !strncmp(string_so_far, lc->cvec[i], token_len)) {
576 return TRUE;
577 }
578 }
579 return FALSE;
580 }
581
582 static void monitor_completions(const char *string_so_far, linenoiseCompletions *lc)
583 {
584 int start_of_token, token_len;
585 char *help_commands[] = {"help", "?"};
586 const linenoiseCompletions help_lc = {
587 sizeof(help_commands)/sizeof(*help_commands),
588 help_commands
589 };
590
591 find_next_token(string_so_far, 0, &start_of_token, &token_len);
592 if (!string_so_far[start_of_token + token_len]) {
593 fill_completions(string_so_far, start_of_token, token_len, &command_lc, lc);
594 return;
595 }
596 if (is_token_in(string_so_far + start_of_token, token_len, &help_lc)) {
597 find_next_token(string_so_far, start_of_token + token_len, &start_of_token, &token_len);
598 if (!string_so_far[start_of_token + token_len]){
599 fill_completions(string_so_far, start_of_token, token_len, &command_lc, lc);
600 return;
601 }
602 }
603 if (is_token_in(string_so_far + start_of_token, token_len, &need_filename_lc)) {
604 int start_of_path;
605 DIR* dir;
606 struct dirent *direntry;
607 struct linenoiseCompletions files_lc = {0, NULL};
608 int i;
609
610 for (start_of_token += token_len; string_so_far[start_of_token] && isspace((int)(string_so_far[start_of_token])); start_of_token++);
611 if (string_so_far[start_of_token] != '"') {
612 char *string_to_append = concat_strings(string_so_far, start_of_token, "\"");
613 linenoiseAddCompletion(lc, string_to_append);
614 lib_free(string_to_append);
615 return;
616 }
617 for (start_of_path = ++start_of_token, token_len = 0; string_so_far[start_of_token + token_len]; token_len++) {
618 if(string_so_far[start_of_token + token_len] == '"'
619 && string_so_far[start_of_token + token_len - 1] != '\\') {
620 return;
621 }
622 if(string_so_far[start_of_token + token_len] == '/') {
623 start_of_token += token_len + 1;
624 token_len = -1;
625 }
626 }
627 if (start_of_token == start_of_path) {
628 dir = opendir(".");
629 } else {
630 char *path = concat_strings(string_so_far + start_of_path, start_of_token - start_of_path, "");
631 dir = opendir(path);
632 lib_free(path);
633 }
634 if (dir) {
635 for (direntry = readdir(dir); direntry; direntry = readdir(dir)) {
636 if (strcmp(direntry->d_name, ".") && strcmp(direntry->d_name, "..")) {
637 char *entryname = lib_msprintf("%s%s", direntry->d_name, is_dir(direntry) ? "/" : "\"");
638 linenoiseAddCompletion(&files_lc, entryname);
639 lib_free(entryname);
640 }
641 }
642 fill_completions(string_so_far, start_of_token, token_len, &files_lc, lc);
643 for(i = 0; i < files_lc.len; i++) {
644 free(files_lc.cvec[i]);
645 }
646 closedir(dir);
647 return;
648 }
649 }
650 }
651
652 char *uimon_get_in(char **ppchCommandLine, const char *prompt)
653 {
654 char *p, *ret_string;
655
656 if (native_monitor()) {
657 return uimonfb_get_in(ppchCommandLine, prompt);
658 }
659
660 fixed.input_buffer = lib_stralloc("");;
661 linenoiseSetCompletionCallback(monitor_completions);
662 p = linenoise(prompt, &fixed);
663 if (p) {
664 if (*p) {
665 linenoiseHistoryAdd(p);
666 }
667 ret_string = lib_stralloc(p);
668 free(p);
669 } else {
670 ret_string = lib_stralloc("x");
671 }
672 lib_free(fixed.input_buffer);
673 fixed.input_buffer = NULL;
674
675 return ret_string;
676 }
677
678 int console_init(void)
679 {
680 int i = 0;
681 char *full_name;
682 char *short_name;
683 int takes_filename_as_arg;
684
685 if (native_monitor()) {
686 return consolefb_init();
687 }
688
689 while (mon_get_nth_command(i++, (const char **)&full_name, (const char **)&short_name, &takes_filename_as_arg)) {
690 if (strlen(full_name)) {
691 linenoiseAddCompletion(&command_lc, full_name);
692 if (strlen(short_name)) {
693 linenoiseAddCompletion(&command_lc, short_name);
694 }
695 if (takes_filename_as_arg) {
696 linenoiseAddCompletion(&need_filename_lc, full_name);
697 if (strlen(short_name)) {
698 linenoiseAddCompletion(&need_filename_lc, short_name);
699 }
700 }
701 }
702 }
703 return 0;
704 }
705
706 int console_close_all(void)
707 {
708 int i;
709
710 if (native_monitor()) {
711 return consolefb_close_all();
712 }
713
714 for(i = 0; i < command_lc.len; i++) {
715 free(command_lc.cvec[i]);
716 }
717 for(i = 0; i < need_filename_lc.len; i++) {
718 free(need_filename_lc.cvec[i]);
719 }
720 return 0;
721 }
722
723 /** \brief Callback to activate the ML-monitor
724 *
725 * \param[in,out] widget widget triggering the event
726 * \param[in] user_data data for the event (unused)
727 */
728 void ui_monitor_activate_callback(GtkWidget *widget, gpointer user_data)
729 {
730 int v;
731 int native = 0;
732
733 /*
734 * Determine if we use the spawing terminal or the (yet to write) Gtk3
735 * base monitor
736 */
737 if (resources_get_int("NativeMonitor", &native) < 0) {
738 debug_gtk3("failed to get value of resource 'NativeMonitor'.");
739 }
740 debug_gtk3("called, native monitor = %s.", native ? "true" : "false");
741
742 resources_get_int("MonitorServer", &v);
743
744 if (v == 0) {
745 #ifdef HAVE_FULLSCREEN
746 fullscreen_suspend(0);
747 #endif
748 vsync_suspend_speed_eval();
749 /* ui_autorepeat_on(); */
750
751 #ifdef HAVE_MOUSE
752 /* FIXME: restore mouse in case it was grabbed */
753 /* ui_restore_mouse(); */
754 #endif
755 if (!ui_emulation_is_paused()) {
756 monitor_startup_trap();
757 } else {
758 monitor_startup(e_default_space);
759 #ifdef HAVE_FULLSCREEN
760 fullscreen_resume();
761 #endif
762 }
763 }
764 }
765
766