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