summaryrefslogtreecommitdiffhomepage
path: root/mixgtk/mixgtk_cmd_dispatcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'mixgtk/mixgtk_cmd_dispatcher.c')
-rw-r--r--mixgtk/mixgtk_cmd_dispatcher.c547
1 files changed, 547 insertions, 0 deletions
diff --git a/mixgtk/mixgtk_cmd_dispatcher.c b/mixgtk/mixgtk_cmd_dispatcher.c
new file mode 100644
index 0000000..dbb2909
--- /dev/null
+++ b/mixgtk/mixgtk_cmd_dispatcher.c
@@ -0,0 +1,547 @@
+/* -*-c-*- -------------- mixgtk_cmd_dispatcher.c :
+ * Implementation of the functions declared in mixgtk_cmd_dispatcher.h
+ * ------------------------------------------------------------------
+ * $Id: mixgtk_cmd_dispatcher.c,v 1.24 2005/09/20 19:43:14 jao Exp $
+ * ------------------------------------------------------------------
+ * Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <mixlib/mix.h>
+
+#ifdef HAVE_LIBHISTORY
+# include <readline/history.h>
+#endif
+
+#ifdef MAKE_GUILE
+# include <mixguile/mixguile.h>
+#endif
+
+#include <gdk/gdkkeysyms.h>
+#include <mixlib/mix_vm_command.h>
+#include "mixgtk_mixvm.h"
+#include "mixgtk_mixal.h"
+#include "mixgtk_fontsel.h"
+#include "mixgtk_config.h"
+#include "mixgtk_cmd_dispatcher.h"
+
+
+/* a mix vm command dispatcher */
+typedef struct mixgtk_dispatch_
+{
+ mix_vm_cmd_dispatcher_t *dispatcher; /* the underlying cmd dispatcher */
+ FILE *out; /* the dispatcher's output file */
+ int fildes[2]; /* pipe for communication with the dispatcher */
+ GtkWidget *prompt; /* the command prompt widget */
+ GtkWidget *log; /* the dispatcher's messages echo area */
+ GtkWidget *status; /* the status bar widget */
+ guint context; /* context of the status bar messages */
+ gchar *last_file;
+} mixgtk_dispatch_data_t;
+
+static struct mixgtk_dispatch_ dis_data_ = {NULL};
+
+static GtkWidget *ext_dlg_ = NULL;
+static GtkWidget *ed_entry_ = NULL;
+static GtkWidget *asm_entry_ = NULL;
+
+static const gchar *ED_NAME_ = "editor_entry";
+static const gchar *ASM_NAME_ = "mixasm_entry";
+
+static const gchar *TITLE_FORMAT_ = "gmixvm - %s";
+
+static void
+log_command_ (mixgtk_dispatch_data_t *dis, const gchar *cmd)
+{
+ GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dis->log));
+ GtkTextIter end;
+
+ gtk_text_buffer_get_end_iter (buf, &end);
+ gtk_text_buffer_place_cursor (buf, &end);
+ gtk_text_buffer_insert_at_cursor (buf, "MIX> ", -1);
+ gtk_text_buffer_insert_at_cursor (buf, cmd, -1);
+ gtk_text_buffer_insert_at_cursor (buf, "\n", -1);
+
+#ifdef HAVE_LIBHISTORY
+ add_history ((char *)cmd);
+/* history_search ((char *)cmd, 0); */
+ history_set_pos (history_base + history_length - 1);
+#endif
+}
+
+static void
+flush_log_ (mixgtk_dispatch_data_t *dis)
+{
+ enum {BLKSIZE = 100};
+ static gchar BUFFER[BLKSIZE];
+
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dis->log));
+ GtkTextMark *mark;
+ GtkTextIter end;
+ ssize_t k;
+
+ gtk_text_buffer_get_end_iter (buffer, &end);
+ gtk_text_buffer_place_cursor (buffer, &end);
+
+ fflush (dis->out);
+ while ((k = read (dis->fildes[0], BUFFER, BLKSIZE)) != 0)
+ {
+ if (k == -1 && errno != EINTR) break;
+ if (k != -1)
+ gtk_text_buffer_insert_at_cursor (buffer, BUFFER, k);
+ }
+
+ mark = gtk_text_buffer_get_insert (buffer);
+ gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (dis->log), mark, 0,
+ TRUE, 0, 0);
+}
+
+/* hooks */
+static void
+global_post_hook_ (mix_vm_cmd_dispatcher_t *dis,
+ mix_vm_command_t cmd, const gchar *arg, gpointer data)
+{
+ flush_log_ ((mixgtk_dispatch_data_t *)data);
+ mixgtk_mixvm_update_vm_widgets ();
+}
+
+static void
+load_post_hook_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg,
+ gpointer data)
+{
+ static glong id = -1;
+
+ if (mix_vm_cmd_dispatcher_get_last_result (dis))
+ {
+ GtkWindow *mainw =
+ GTK_WINDOW (mixgtk_widget_factory_get_dialog (MIXGTK_MAIN));
+
+ if (dis_data_.last_file) g_free (dis_data_.last_file);
+ dis_data_.last_file = g_strdup_printf (TITLE_FORMAT_, arg);
+ gtk_window_set_title (mainw, dis_data_.last_file);
+
+ mixgtk_mixal_load_file ();
+ mixgtk_mixal_update ();
+ mixgtk_mixal_update_bp_all ();
+
+ if (id != -1)
+ gtk_statusbar_remove (GTK_STATUSBAR (dis_data_.status),
+ dis_data_.context, (guint)id);
+ id =
+ gtk_statusbar_push (GTK_STATUSBAR (dis_data_.status), dis_data_.context,
+ dis_data_.last_file);
+ }
+}
+
+static void
+run_post_hook_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg,
+ gpointer data)
+{
+ mixgtk_mixal_update ();
+}
+
+static void
+next_post_hook_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg,
+ gpointer data)
+{
+ mixgtk_mixal_update ();
+}
+
+static void
+linebp_post_hook_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg,
+ gpointer data)
+{
+ if (arg && strlen (arg)) mixgtk_mixal_update_bp_at_line (atoi (arg));
+}
+
+static void
+addrbp_post_hook_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg,
+ gpointer data)
+{
+ if (arg && strlen (arg)) mixgtk_mixal_update_bp_at_address (atoi (arg));
+}
+
+static void
+allbp_post_hook_ (mix_vm_cmd_dispatcher_t *dis, const gchar *arg,
+ gpointer data)
+{
+ mixgtk_mixal_update_bp_all ();
+}
+
+
+static void
+install_hooks_ (void)
+{
+ mix_vm_cmd_dispatcher_global_post_hook (dis_data_.dispatcher,
+ global_post_hook_,
+ (gpointer)(&dis_data_));
+ mix_vm_cmd_dispatcher_post_hook (dis_data_.dispatcher,
+ MIX_CMD_LOAD, load_post_hook_,
+ NULL);
+ mix_vm_cmd_dispatcher_post_hook (dis_data_.dispatcher,
+ MIX_CMD_RUN, run_post_hook_,
+ NULL);
+ mix_vm_cmd_dispatcher_post_hook (dis_data_.dispatcher,
+ MIX_CMD_NEXT, next_post_hook_,
+ NULL);
+ mix_vm_cmd_dispatcher_post_hook (dis_data_.dispatcher,
+ MIX_CMD_SBP, linebp_post_hook_,
+ NULL);
+ mix_vm_cmd_dispatcher_post_hook (dis_data_.dispatcher,
+ MIX_CMD_CBP, linebp_post_hook_,
+ NULL);
+ mix_vm_cmd_dispatcher_post_hook (dis_data_.dispatcher,
+ MIX_CMD_SBPA, addrbp_post_hook_,
+ NULL);
+ mix_vm_cmd_dispatcher_post_hook (dis_data_.dispatcher,
+ MIX_CMD_CBPA, addrbp_post_hook_,
+ NULL);
+ mix_vm_cmd_dispatcher_post_hook (dis_data_.dispatcher,
+ MIX_CMD_CABP, allbp_post_hook_,
+ NULL);
+}
+
+/* configuration stuff */
+static const gchar *EDITOR_KEY_ = "Editor";
+static const gchar *MIXASM_KEY_ = "Mixasm";
+
+static void
+read_config_ (void)
+{
+ const gchar *editor = mixgtk_config_get (EDITOR_KEY_);
+ const gchar *assem = mixgtk_config_get (MIXASM_KEY_);
+
+ if (!editor)
+ {
+ static const gchar *ENV[] = {"MDK_EDITOR", "X_EDITOR", NULL};
+ gchar *edit = NULL;
+ int k = 0;
+ while (!edit && ENV[k]) edit = getenv (ENV[k++]);
+ if (edit) edit = g_strconcat (edit, " %s", NULL);
+ else edit = g_strdup ("xterm -e vi %s");
+ mix_vm_cmd_dispatcher_set_editor (dis_data_.dispatcher, edit);
+ g_free (edit);
+ }
+ else
+ {
+ mix_vm_cmd_dispatcher_set_editor (dis_data_.dispatcher, editor);
+ }
+ if (!assem) assem = "mixasm %s";
+ mix_vm_cmd_dispatcher_set_assembler (dis_data_.dispatcher, assem);
+
+}
+
+void
+on_external_programs_activate ()
+{
+ if (!ext_dlg_)
+ {
+ ext_dlg_ = mixgtk_widget_factory_get_dialog (MIXGTK_EXTERNPROG_DIALOG);
+ g_return_if_fail (ext_dlg_ != NULL);
+ ed_entry_ = mixgtk_widget_factory_get_child_by_name
+ (MIXGTK_EXTERNPROG_DIALOG, ED_NAME_);
+ g_assert (ed_entry_);
+ asm_entry_ = mixgtk_widget_factory_get_child_by_name
+ (MIXGTK_EXTERNPROG_DIALOG, ASM_NAME_);
+ g_assert (asm_entry_);
+ }
+ gtk_entry_set_text (GTK_ENTRY (ed_entry_),
+ mix_vm_cmd_dispatcher_get_editor (dis_data_.dispatcher));
+ gtk_entry_set_text (GTK_ENTRY (asm_entry_),
+ mix_vm_cmd_dispatcher_get_assembler
+ (dis_data_.dispatcher));
+ gtk_widget_show (ext_dlg_);
+ if (gtk_dialog_run (GTK_DIALOG (ext_dlg_)) == GTK_RESPONSE_OK)
+ {
+ const gchar *value = gtk_entry_get_text (GTK_ENTRY (ed_entry_));
+ mix_vm_cmd_dispatcher_set_editor (dis_data_.dispatcher, value);
+ mixgtk_config_update (EDITOR_KEY_, value);
+ value = gtk_entry_get_text (GTK_ENTRY (asm_entry_));
+ mix_vm_cmd_dispatcher_set_assembler (dis_data_.dispatcher, value);
+ mixgtk_config_update (MIXASM_KEY_, value);
+ }
+
+ gtk_widget_hide (ext_dlg_);
+}
+
+/* initialise the command dispatcher */
+gboolean
+mixgtk_cmd_dispatcher_init (mixgtk_dialog_id_t top)
+{
+ static gboolean restart = FALSE;
+ gchar *text = NULL;
+
+ ext_dlg_ = NULL;
+ ed_entry_ = NULL;
+ asm_entry_ = NULL;
+
+ dis_data_.prompt =
+ mixgtk_widget_factory_get (top, MIXGTK_WIDGET_PROMPT);
+ g_return_val_if_fail (dis_data_.prompt != NULL, FALSE);
+
+ if (dis_data_.log)
+ text = gtk_editable_get_chars (GTK_EDITABLE (dis_data_.log), 0, -1);
+
+ dis_data_.log =
+ mixgtk_widget_factory_get (top, MIXGTK_WIDGET_LOG);
+
+ g_return_val_if_fail (dis_data_.log != NULL, FALSE);
+
+ if (text)
+ {
+ gtk_text_buffer_insert_at_cursor
+ (gtk_text_view_get_buffer (GTK_TEXT_VIEW (dis_data_.log)), text, -1);
+ g_free (text);
+ }
+
+ if (!dis_data_.dispatcher)
+ {
+ static const gchar *HISTORY_FILE = "gmixvm.history";
+ static gint HISTORY_SIZE = 100;
+ mix_config_t *config = mixgtk_config_get_mix_config ();
+
+ int r = pipe (dis_data_.fildes);
+ g_return_val_if_fail (r == 0, FALSE);
+ /* connect stdout/stderr to the pipe's write end */
+ if (dup2 (dis_data_.fildes[1], STDOUT_FILENO) == -1
+ || dup2 (dis_data_.fildes[1], STDOUT_FILENO) == -1)
+ return FALSE;
+ dis_data_.out = fdopen (dis_data_.fildes[1], "w");
+ g_return_val_if_fail (dis_data_.out != NULL, FALSE);
+ r = fcntl (dis_data_.fildes[0], F_GETFL, 0);
+ g_return_val_if_fail (r != -1, FALSE);
+ r |= O_NONBLOCK;
+ r = fcntl(dis_data_.fildes[0], F_SETFL, r);
+ g_return_val_if_fail (r != -1, FALSE);
+
+ if (!mix_config_get_history_file (config))
+ mix_config_set_history_file (config, HISTORY_FILE);
+ if (mix_config_get_history_size (config) == 0)
+ mix_config_set_history_size (config, HISTORY_SIZE);
+
+ dis_data_.dispatcher =
+ mix_vm_cmd_dispatcher_new_with_config (dis_data_.out,
+ dis_data_.out,
+ config);
+ mix_vm_cmd_dispatcher_print_time (dis_data_.dispatcher, FALSE);
+
+ install_hooks_ ();
+ }
+
+ dis_data_.status =
+ mixgtk_widget_factory_get (MIXGTK_MAIN, MIXGTK_WIDGET_STATUSBAR);
+ g_return_val_if_fail (dis_data_.status != NULL, FALSE);
+ dis_data_.context = gtk_statusbar_get_context_id (GTK_STATUSBAR
+ (dis_data_.status),
+ "cmd_dis_context");
+ if (!restart) read_config_ ();
+ if (dis_data_.last_file)
+ gtk_window_set_title
+ (GTK_WINDOW (mixgtk_widget_factory_get_dialog (MIXGTK_MAIN)),
+ dis_data_.last_file);
+
+ mixgtk_fontsel_set_font (MIX_FONT_LOG, dis_data_.log);
+ mixgtk_fontsel_set_font (MIX_FONT_PROMPT, dis_data_.prompt);
+
+ restart = TRUE;
+ return TRUE;
+}
+
+/* dispatch an externally provided command */
+#ifdef MAKE_GUILE
+static gboolean
+try_guile_ (const gchar *command)
+{
+ if (command && command[0] == '(' && command[strlen (command) - 1] == ')')
+ {
+ mixguile_interpret_command (command);
+ return TRUE;
+ }
+ return FALSE;
+}
+#else
+# define try_guile_(ignored) FALSE
+#endif
+
+void
+mixgtk_cmd_dispatcher_dispatch (const gchar *command)
+{
+ GtkWidget *entry = dis_data_.prompt;
+ g_return_if_fail (command != NULL);
+ g_assert (entry != NULL);
+ gtk_entry_set_text (GTK_ENTRY (entry), command);
+ log_command_ (&dis_data_, command);
+ if (!try_guile_ (command))
+ mix_vm_cmd_dispatcher_dispatch_text (dis_data_.dispatcher, command);
+ gtk_entry_set_text (GTK_ENTRY (entry), "");
+}
+
+/* get times */
+void
+mixgtk_cmd_dispatcher_get_times (gint *uptime, gint *progtime, gint *laptime)
+{
+ if (uptime != NULL)
+ *uptime = mix_vm_cmd_dispatcher_get_uptime (dis_data_.dispatcher);
+ if (progtime != NULL)
+ *progtime = mix_vm_cmd_dispatcher_get_progtime (dis_data_.dispatcher);
+ if (laptime != NULL)
+ *laptime = mix_vm_cmd_dispatcher_get_laptime (dis_data_.dispatcher);
+}
+
+/* get the underlying vm */
+mix_vm_t *
+mixgtk_cmd_dispatcher_get_vm (void)
+{
+ return (mix_vm_t *) mix_vm_cmd_dispatcher_get_vm (dis_data_.dispatcher);
+}
+
+/* get the current source file */
+const gchar *
+mixgtk_cmd_dispatcher_get_src_path (void)
+{
+ return mix_vm_cmd_dispatcher_get_src_file_path (dis_data_.dispatcher);
+}
+
+/* get the mix cmd dispatcher */
+mix_vm_cmd_dispatcher_t *
+mixgtk_cmd_dispatcher_get_mix_dispatcher (void)
+{
+ return dis_data_.dispatcher;
+}
+
+/* process commands */
+void
+complete_command_ (void)
+{
+ GtkEntry *entry = GTK_ENTRY (dis_data_.prompt);
+ gchar *prefix = NULL;
+ const gchar *text = gtk_entry_get_text (entry);
+ const GList *cmds =
+ mix_vm_cmd_dispatcher_complete (dis_data_.dispatcher, text, &prefix);
+
+ if (prefix != NULL)
+ {
+ GtkTextBuffer *buf =
+ gtk_text_view_get_buffer (GTK_TEXT_VIEW (dis_data_.log));
+
+ gtk_entry_set_text (entry, prefix);
+ if (g_list_length ((GList *)cmds) > 1)
+ {
+ static gchar BUFFER[25];
+ gint k = 0;
+ gtk_text_buffer_insert_at_cursor (buf, "Completions:\n", -1);
+ while (cmds)
+ {
+ g_snprintf (BUFFER, 25, "%-12s", (const char*)cmds->data);
+ ++k;
+ gtk_text_buffer_insert_at_cursor (buf, BUFFER, -1);
+ if (k%5 == 0)
+ gtk_text_buffer_insert_at_cursor (buf, "\n", -1);
+
+ cmds = cmds->next;
+ }
+ if (k%5 != 0)
+ gtk_text_buffer_insert_at_cursor (buf, "\n", -1);
+ }
+ else
+ {
+ gint pos = strlen (prefix);
+ gtk_editable_insert_text (GTK_EDITABLE (entry), " ", 1, &pos);
+ }
+ flush_log_ (&dis_data_);
+ g_free (prefix);
+ }
+}
+
+int
+on_command_prompt_key_press_event (GtkEntry *w, GdkEventKey *e, gpointer d)
+{
+ guint key = e->keyval;
+ gboolean result = FALSE;
+
+#ifdef HAVE_LIBHISTORY
+ HIST_ENTRY *entry = NULL;
+ if (key == GDK_Up)
+ {
+ entry = previous_history ();
+ if (entry && entry->line)
+ gtk_entry_set_text (w, entry->line);
+ result = TRUE;
+ }
+ if (key == GDK_Down)
+ {
+ entry = next_history ();
+ if (entry && entry->line)
+ gtk_entry_set_text (w, entry->line);
+ result = TRUE;
+ }
+#endif
+
+ if (key == GDK_Tab)
+ {
+ complete_command_ ();
+ result = TRUE;
+ }
+
+ if (result) gtk_editable_set_position (GTK_EDITABLE (w), -1);
+
+ return result;
+}
+
+void
+on_command_prompt_activate (GtkEntry *prompt, gpointer data)
+{
+ gchar *text =
+ g_strstrip (gtk_editable_get_chars (GTK_EDITABLE (prompt), 0, -1));
+ if (text && *text)
+ {
+ log_command_ (&dis_data_, text);
+ if (!try_guile_ (text))
+ mix_vm_cmd_dispatcher_dispatch_text (dis_data_.dispatcher, text);
+ gtk_entry_set_text (prompt, "");
+ }
+ g_free (text);
+}
+
+void
+on_log_font_activate ()
+{
+ GtkWidget *w[] = { dis_data_.log };
+ mixgtk_fontsel_query_font (MIX_FONT_LOG, w, 1);
+}
+
+void
+on_prompt_font_activate ()
+{
+ GtkWidget *w[] = { dis_data_.prompt };
+ mixgtk_fontsel_query_font (MIX_FONT_PROMPT, w, 1);
+}
+
+void
+mixgtk_cmd_dispatcher_update_fonts (void)
+{
+ mixgtk_fontsel_set_font (MIX_FONT_LOG, dis_data_.log);
+ mixgtk_fontsel_set_font (MIX_FONT_PROMPT, dis_data_.prompt);
+}