Re: GDB Console



Hello,

I'm working on the console for a while now, so I decided to send my
current work on the mailing-list for comments on the general design.
I'm accepting all kinds of comments and critics on this patch, but
keep in mind I didn't send this patch for integration in master; I
still have a lot of cool coding stuff to do and also some boring stuff
to do (write doc, unit test, and changelog).

For people not interesting in reading the code but interesting on the
status, the code is currently working well and I'm using it everyday
for debugging without having huge issues. I don't even know how I
managed to use nemiver without a console for so many years :-).
Since I'm starting today my winter break today I'm hoping to get some
substantial progress during the next two weeks, and if you are
interested you can follow my work on my github [0].

Fabien

[0] https://github.com/Fabo/nemiver/tree/console
From b813ed40e17b3eb759c7be192a0802f1ea3b6423 Mon Sep 17 00:00:00 2001
From: Fabien Parent <parent f gmail com>
Date: Sat, 21 Jan 2012 18:58:05 +0100
Subject: [PATCH] WIP - Nemiver's console

---
 configure.ac                                    |    3 +-
 src/common/Makefile.am                          |    3 +-
 src/common/nmv-console.cc                       |  452 +++++++++++++++
 src/common/nmv-console.h                        |  112 ++++
 src/common/nmv-str-utils.h                      |   20 +
 src/dbgengine/Makefile.am                       |   11 +-
 src/dbgengine/nmv-dbg-console.cc                |  684 +++++++++++++++++++++++
 src/dbgengine/nmv-dbg-console.h                 |   58 ++
 src/persp/dbgperspective/nmv-dbg-perspective.cc |   74 +++
 src/persp/dbgperspective/nmv-dbg-perspective.h  |    4 +-
 src/uicommon/nmv-terminal.cc                    |    6 +
 src/uicommon/nmv-terminal.h                     |    1 +
 12 files changed, 1423 insertions(+), 5 deletions(-)
 create mode 100644 src/common/nmv-console.cc
 create mode 100644 src/common/nmv-console.h
 create mode 100644 src/dbgengine/nmv-dbg-console.cc
 create mode 100644 src/dbgengine/nmv-dbg-console.h

diff --git a/configure.ac b/configure.ac
index 1d45c0c..68430b8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -337,8 +337,9 @@ DEP_COMMON="gmodule-2.0 >= $LIBGMODULE_VERSION \
            glibmm-2.4 >= $LIBGLIBMM_VERSION"
 
 PKG_CHECK_MODULES(NEMIVERCOMMON, $DEP_COMMON)
+AC_CHECK_FUNCS(readline)
 NEMIVERCOMMON_CFLAGS="$NEMIVERCOMMON_CFLAGS $CPPUNIT_CFLAGS"
-NEMIVERCOMMON_LIBS="$NEMIVERCOMMON_LIBS $LIBGTOP_LIBS $CPPUNIT_LIBS"
+NEMIVERCOMMON_LIBS="$NEMIVERCOMMON_LIBS $LIBGTOP_LIBS $CPPUNIT_LIBS -lreadline"
 
 AC_SUBST(NEMIVERCOMMON_LIBS)
 AC_SUBST(NEMIVERCOMMON_CFLAGS)
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 5b61d90..8318d34 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -71,7 +71,8 @@ $(h)/nmv-sql-statement.cc \
 $(h)/nmv-insert-statement.cc \
 $(h)/nmv-delete-statement.cc \
 $(h)/nmv-proc-utils.cc \
-$(h)/nmv-proc-mgr.cc
+$(h)/nmv-proc-mgr.cc \
+$(h)/nmv-console.cc
 
 publicheaders_DATA=$(headers)
 publicheadersdir=$(NEMIVER_INCLUDE_DIR)/common
diff --git a/src/common/nmv-console.cc b/src/common/nmv-console.cc
new file mode 100644
index 0000000..8df71b9
--- /dev/null
+++ b/src/common/nmv-console.cc
@@ -0,0 +1,452 @@
+//Author: Fabien Parent
+/*
+ *This file is part of the Nemiver project
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+
+#define USE_VARARGS
+#define PREFER_STDARG
+
+#include "nmv-console.h"
+#include "nmv-str-utils.h"
+#include <map>
+#include <vector>
+#include <cstring>
+#include <cctype>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+NEMIVER_BEGIN_NAMESPACE(nemiver)
+NEMIVER_BEGIN_NAMESPACE(common)
+
+const char *const CONSOLE_PROMPT = "> ";
+const unsigned int COMMAND_EXECUTION_TIMEOUT_IN_SECONDS = 10;
+
+struct Console::Stream::Priv {
+    int fd;
+
+    Priv (int a_fd) :
+        fd (a_fd)
+    {
+    }
+
+    void
+    write (const std::string &a_msg) const
+    {
+        THROW_IF_FAIL (fd);
+        ::write (fd, a_msg.c_str (), a_msg.size ());
+    }
+};
+
+Console::Stream::Stream (int a_fd) :
+    m_priv (new Priv (a_fd))
+{
+}
+
+Console::Stream&
+Console::Stream::operator<< (const char *const a_string)
+{
+    THROW_IF_FAIL (m_priv);
+    m_priv->write (a_string);
+    return *this;
+}
+
+Console::Stream&
+Console::Stream::operator<< (const std::string &a_string)
+{
+    THROW_IF_FAIL (m_priv);
+    m_priv->write (a_string);
+    return *this;
+}
+
+Console::Stream&
+Console::Stream::operator<< (unsigned int a_uint)
+{
+    THROW_IF_FAIL (m_priv);
+    m_priv->write (str_utils::to_string (a_uint));
+    return *this;
+}
+
+Console::Stream&
+Console::Stream::operator<< (int a_int)
+{
+    THROW_IF_FAIL (m_priv);
+    m_priv->write (str_utils::to_string (a_int));
+    return *this;
+}
+
+struct Console::Priv {
+    std::map<std::string, Console::Command&> commands;
+    std::vector<Console::Command*> commands_vector;
+
+    int fd;
+    struct readline_state console_state;
+    struct readline_state saved_state;
+
+    Console::Stream stream;
+    Glib::RefPtr<Glib::IOSource> io_source;
+    sigc::connection cmd_execution_done_connection;
+    sigc::connection cmd_execution_timeout_connection;
+
+    Priv (int a_fd) :
+        fd (a_fd),
+        stream (a_fd),
+        io_source (Glib::IOSource::create (a_fd, Glib::IO_IN))
+    {
+        init ();
+    }
+
+    void
+    init ()
+    {
+        THROW_IF_FAIL (fd);
+        THROW_IF_FAIL (io_source);
+
+        if (consoles ().count (fd)) {
+            THROW ("Cannot create two consoles from the same file descriptor.");
+        }
+        consoles ()[fd] = this;
+
+        io_source->connect (sigc::mem_fun (*this, &Console::Priv::read_char));
+        io_source->attach ();
+
+        rl_save_state (&saved_state);
+        rl_instream = fdopen (fd, "r");
+        rl_outstream = fdopen (fd, "w");
+        rl_bind_key ('\t', &Console::Priv::on_tab_key_pressed);
+        rl_callback_handler_install (CONSOLE_PROMPT,
+                                     &Console::Priv::process_command);
+        rl_already_prompted = true;
+        rl_save_state (&console_state);
+        rl_restore_state (&saved_state);
+    }
+
+    bool
+    read_char (Glib::IOCondition)
+    {
+        NEMIVER_TRY
+
+        if (cmd_execution_done_connection.connected ())
+            return false;
+
+        rl_restore_state (&console_state);
+        rl_callback_read_char ();
+        rl_save_state (&console_state);
+        rl_restore_state (&saved_state);
+
+        NEMIVER_CATCH_NOX
+
+        return true;
+    }
+
+    void
+    do_completion (const std::string &a_completion)
+    {
+        size_t buffer_length = std::strlen (rl_line_buffer);
+        rl_extend_line_buffer (buffer_length + a_completion.size ());
+        std::memcpy (rl_line_buffer + rl_point + a_completion.size (),
+                     rl_line_buffer + rl_point,
+                     a_completion.size ());
+        for (size_t i = 0; i < a_completion.size(); i++) {
+            rl_line_buffer[rl_point + i] = a_completion[i];
+        }
+        rl_end += a_completion.size ();
+        rl_point += a_completion.size ();
+    }
+
+    void
+    do_command_completion (const std::string &a_line)
+    {
+        std::vector<Console::Command*> matches;
+        for (std::vector<Console::Command*>::const_iterator iter =
+                commands_vector.begin ();
+             iter != commands_vector.end ();
+             ++iter) {
+            if (*iter && !(*iter)->name ().find (a_line)) {
+                matches.push_back (*iter);
+            }
+        }
+
+        if (matches.size () == 1) {
+            std::string completion =
+                matches[0]->name ().substr (a_line.size ());
+            do_completion (completion);
+        } else if (matches.size () > 1) {
+            std::string msg;
+            std::string completion =
+                matches[0]->name ().substr (a_line.size ());
+            for (size_t i = 0; i < matches.size (); i++) {
+                size_t j = a_line.size ();
+                for (; j < matches[i]->name ().size ()
+                            && j < completion.size ()
+                            && matches[i]->name ()[j] == completion[j];
+                     j++) {
+                }
+                completion = completion.substr (0, j);
+                msg += matches[i]->name () + "\t";
+            }
+
+            rl_save_prompt ();
+            rl_message ("%s%s\n%s\n%s",
+                        rl_display_prompt,
+                        std::string (rl_line_buffer, rl_end).c_str (),
+                        msg.c_str (),
+                        rl_display_prompt);
+            rl_clear_message ();
+            rl_restore_prompt ();
+            do_completion (completion);
+        }
+    }
+
+    void
+    do_param_completion (std::vector<UString> &a_tokens)
+    {
+        if (!a_tokens.size ()) {
+            return;
+        }
+
+        Console::Command* command = 0;
+        for (std::vector<Console::Command*>::const_iterator iter =
+                commands_vector.begin ();
+             iter != commands_vector.end ();
+             ++iter) {
+            if (*iter && (*iter)->name () == a_tokens[0]) {
+                command = *iter;
+                break;
+            }
+        }
+
+        if (!command) {
+            return;
+        }
+
+        a_tokens.erase (a_tokens.begin ());
+        std::string line (rl_line_buffer, rl_point);
+        if (std::isspace (line[line.size () - 1])) {
+            stream << "\n";
+            command->display_usage (a_tokens, stream);
+            rl_forced_update_display ();
+        } else {
+            UString token = a_tokens.back ();
+            a_tokens.pop_back ();
+
+            std::vector<UString> completions;
+            std::vector<UString> matches;
+            command->completions (a_tokens, completions);
+            for (std::vector<UString>::iterator iter = completions.begin ();
+                 iter != completions.end ();
+                 ++iter) {
+                if (!iter->find (token)) {
+                    matches.push_back (*iter);
+                }
+            }
+
+            if (matches.size () == 1) {
+                std::string completion = matches[0].substr (token.size ());
+                do_completion (completion);
+            } else if (matches.size () > 1) {
+                std::string msg;
+                UString completion = matches[0].substr (token.size ());
+                for (size_t i = 0; i < matches.size (); i++) {
+                    size_t j = token.size ();
+                    for (; j < matches[i].size ()
+                                && j < completion.size ()
+                                && matches[i][j] == completion[j];
+                         j++) {
+                    }
+                    completion = completion.substr (0, j);
+                    msg += matches[i] + "\t";
+                }
+                rl_save_prompt ();
+                rl_message ("%s%s\n%s\n%s",
+                            rl_display_prompt,
+                            std::string (rl_line_buffer, rl_end).c_str (),
+                            msg.c_str (),
+                            rl_display_prompt);
+                rl_clear_message ();
+                rl_restore_prompt ();
+                do_completion (completion);
+            } else {
+                rl_complete (0, '\t');
+            }
+        }
+    }
+
+    void
+    execute_command (char *a_buffer)
+    {
+        std::string command_name;
+        std::vector<std::string> cmd_argv;
+
+        int size = std::strlen (a_buffer);
+        for (int i = 0, local_index = 0; i <= size; ++i, ++local_index) {
+            if (!std::isspace (a_buffer[local_index]) && i != size) {
+                continue;
+            }
+
+            a_buffer[local_index] = '\0';
+            if (command_name.empty ()) {
+                command_name = a_buffer;
+            } else {
+                cmd_argv.push_back (a_buffer);
+            }
+            a_buffer += local_index + 1;
+            local_index = 0;
+        }
+
+        if (command_name.empty ()) {
+            stream << CONSOLE_PROMPT;
+            return;
+        }
+
+        if (!commands.count (command_name)) {
+            stream << "Undefined command: " << command_name << ".\n"
+                   << CONSOLE_PROMPT;
+            return;
+        }
+
+        Command &command = commands.at (command_name);
+        cmd_execution_done_connection = command.done_signal ().connect
+            (sigc::mem_fun (*this, &Console::Priv::on_done_signal));
+        commands.at (command_name) (cmd_argv, stream);
+        cmd_execution_timeout_connection =
+            Glib::signal_timeout().connect_seconds
+                (sigc::mem_fun
+                    (*this, &Console::Priv::on_cmd_execution_timeout_signal),
+                 COMMAND_EXECUTION_TIMEOUT_IN_SECONDS);
+    }
+
+    bool
+    on_cmd_execution_timeout_signal ()
+    {
+        NEMIVER_TRY
+        on_done_signal ();
+        NEMIVER_CATCH_NOX
+
+        return true;
+    }
+
+    void
+    on_done_signal ()
+    {
+        NEMIVER_TRY
+
+        stream << CONSOLE_PROMPT;
+        cmd_execution_timeout_connection.disconnect();
+        cmd_execution_done_connection.disconnect ();
+
+        NEMIVER_CATCH_NOX
+    }
+
+    static int
+    on_tab_key_pressed (int, int)
+    {
+        NEMIVER_TRY
+
+        std::string line (rl_line_buffer, rl_point);
+        std::vector<UString> tokens_raw = str_utils::split (line, " ");
+        std::vector<UString> tokens;
+        for (size_t i = 0; i < tokens_raw.size (); i++) {
+            if (tokens_raw[i].size ()) {
+                tokens.push_back (tokens_raw[i]);
+            }
+        }
+
+        if (std::isspace (line[line.size () - 1]) || tokens.size () > 1) {
+            self ().do_param_completion (tokens);
+        } else {
+            self ().do_command_completion (line);
+        }
+
+        NEMIVER_CATCH_NOX
+
+        return 0;
+    }
+
+    static std::map<int, Console::Priv*>& consoles ()
+    {
+        static std::map<int, Console::Priv*> s_consoles;
+        return s_consoles;
+    }
+
+    static Console::Priv& self ()
+    {
+        int fd = fileno (rl_instream);
+        THROW_IF_FAIL (fd);
+        THROW_IF_FAIL (consoles ().count (fd));
+        Console::Priv *self = consoles ()[fd];
+        THROW_IF_FAIL (self);
+        return *self;
+    }
+
+    static void
+    process_command (char *a_command)
+    {
+        NEMIVER_TRY
+
+        THROW_IF_FAIL (a_command);
+        add_history (a_command);
+        self ().execute_command (a_command);
+
+        NEMIVER_CATCH_NOX
+
+        free (a_command);
+    }
+};
+
+Console::Console (int a_fd) :
+    m_priv (new Priv (a_fd))
+{
+}
+
+Console::~Console ()
+{
+}
+
+void
+Console::register_command (Console::Command &a_command)
+{
+    THROW_IF_FAIL (m_priv);
+
+    if (m_priv->commands.count (a_command.name ())) {
+        LOG ("Command '" << a_command.name () << "' is already registered in"
+             " the console. The previous command will be overwritten");
+    }
+
+    m_priv->commands.insert (std::make_pair<std::string, Command&>
+        (a_command.name (), a_command));
+    m_priv->commands_vector.push_back (&a_command);
+
+    const char **aliases = a_command.aliases ();
+    for (int i = 0; aliases && aliases[i]; i++) {
+        if (m_priv->commands.count (aliases[i])) {
+            LOG ("Command '" << aliases[i] << "' is already registered in"
+                 " the console. The previous command will be overwritten");
+        }
+        m_priv->commands.insert (std::make_pair<std::string, Command&>
+            (aliases[i], a_command));
+    }
+}
+
+NEMIVER_END_NAMESPACE(common)
+NEMIVER_END_NAMESPACE(nemiver)
+
diff --git a/src/common/nmv-console.h b/src/common/nmv-console.h
new file mode 100644
index 0000000..0ac41cf
--- /dev/null
+++ b/src/common/nmv-console.h
@@ -0,0 +1,112 @@
+//Author: Fabien Parent
+/*
+ *This file is part of the Nemiver project
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NMV_CONSOLE_H__
+#define __NMV_CONSOLE_H__
+
+#include "nmv-safe-ptr.h"
+#include "nmv-namespace.h"
+#include "nmv-exception.h"
+#include <string>
+#include <vector>
+
+NEMIVER_BEGIN_NAMESPACE(nemiver)
+NEMIVER_BEGIN_NAMESPACE(common)
+
+class Console {
+    struct Priv;
+    SafePtr<Priv> m_priv;
+
+public:
+    class Stream {
+        struct Priv;
+        SafePtr<Priv> m_priv;
+        
+    public:
+        explicit Stream (int a_fd);
+        Stream& operator<< (const char *const a_string);
+        Stream& operator<< (const std::string &a_string);
+        Stream& operator<< (unsigned int a_uint);
+        Stream& operator<< (int a_int);
+    };
+
+    class Command {
+        sigc::signal<void> m_done_signal;
+
+public:
+        sigc::signal<void>& done_signal ()
+        {
+            return m_done_signal;
+        }
+
+        virtual const std::string& name () const = 0;
+
+        virtual const char** aliases () const
+        {
+            return 0;
+        }
+
+        virtual void completions (const std::vector<UString>&,
+                                  std::vector<UString>&) const
+        {
+        }
+
+        virtual void display_usage (const std::vector<UString>&, Stream&) const
+        {
+        }
+
+        virtual void execute (const std::vector<std::string> &a_argv,
+                              Stream &a_output) = 0;
+
+        virtual void operator() (const std::vector<std::string> &a_argv,
+                         Stream &a_output)
+        {
+            execute (a_argv, a_output);
+        }
+
+        virtual ~Command ()
+        {
+        }
+    };
+
+    typedef Command AsynchronousCommand;
+    struct SynchronousCommand : public Command{
+        virtual void operator() (const std::vector<std::string> &a_argv,
+                         Stream &a_output)
+        {
+            execute (a_argv, a_output);
+            done_signal ().emit ();
+        }
+    };
+
+    explicit Console (int a_fd);
+    virtual ~Console ();
+    void register_command (Console::Command &a_command);
+};
+
+NEMIVER_END_NAMESPACE(common)
+NEMIVER_END_NAMESPACE(nemiver)
+
+#endif /* __NMV_CONSOLE_H__ */
+
diff --git a/src/common/nmv-str-utils.h b/src/common/nmv-str-utils.h
index 8c72b8c..9a9f856 100644
--- a/src/common/nmv-str-utils.h
+++ b/src/common/nmv-str-utils.h
@@ -25,6 +25,7 @@
 #ifndef __NMV_STR_UTILS_H__
 #define __NMV_STR_UTILS_H__
 #include "nmv-ustring.h"
+#include <sstream>
 
 NEMIVER_BEGIN_NAMESPACE (nemiver)
 NEMIVER_BEGIN_NAMESPACE (str_utils)
@@ -52,6 +53,25 @@ UString join (vector<UString>::const_iterator &a_from,
               vector<UString>::const_iterator &a_to,
               const UString &a_delim=" ");
 
+template<typename T>
+T
+from_string (const std::string &a_string)
+{
+    std::istringstream is (a_string);
+    T out;
+    is >> out;
+    return out;
+}
+
+template<typename T>
+std::string
+to_string (const T &a_value)
+{
+    std::ostringstream os;
+    os << a_value;
+    return os.str ();
+}
+
 template<typename S>
 void
 chomp (S &a_string)
diff --git a/src/dbgengine/Makefile.am b/src/dbgengine/Makefile.am
index d1cd044..a271197 100644
--- a/src/dbgengine/Makefile.am
+++ b/src/dbgengine/Makefile.am
@@ -20,7 +20,8 @@ noinst_LTLIBRARIES=\
 libgdbmiparser.la \
 libdbgcommon.la \
 libdebuggerutils.la \
-libgdbengine.la
+libgdbengine.la \
+libdbgconsole.la
 
 h=$(abs_srcdir)
 
@@ -85,6 +86,12 @@ $(h)/nmv-debugger-utils.cc
 
 libdebuggerutils_la_CFLAGS=-fPIC -DPIC
 
+libdbgconsole_la_SOURCES= \
+$(h)/nmv-dbg-console.cc \
+$(h)/nmv-dbg-console.h
+
+libdbgconsole_la_CFLAGS=-fPIC -DPIC
+
 libgdbengine_la_SOURCES= \
 $(h)/nmv-gdb-engine.cc \
 $(h)/nmv-gdb-engine.h
@@ -96,7 +103,7 @@ publicheadersdir=$(NEMIVER_INCLUDE_DIR)/dynmods
 
 libgdbmod_la_LDFLAGS=-module -avoid-version -Wl,--as-needed
 libgdbmod_la_LIBADD=libgdbmiparser.la \
-libgdbengine.la libdebuggerutils.la @NEMIVERCOMMON_LIBS@ \
+libgdbengine.la libdebuggerutils.la libdbgconsole.la @NEMIVERCOMMON_LIBS@ \
 $(abs_top_builddir)/src/langs/libnemivercparser.la \
 $(abs_top_builddir)/src/common/libnemivercommon.la
 
diff --git a/src/dbgengine/nmv-dbg-console.cc b/src/dbgengine/nmv-dbg-console.cc
new file mode 100644
index 0000000..85de1ec
--- /dev/null
+++ b/src/dbgengine/nmv-dbg-console.cc
@@ -0,0 +1,684 @@
+//Author: Fabien Parent
+/*
+ *This file is part of the Nemiver project
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+
+#include "nmv-dbg-console.h"
+#include "nmv-i-debugger.h"
+#include "uicommon/nmv-terminal.h"
+#include "common/nmv-exception.h"
+#include "common/nmv-str-utils.h"
+#include "common/nmv-ustring.h"
+
+NEMIVER_BEGIN_NAMESPACE(nemiver)
+
+const char *const NEMIVER_DBG_CONSOLE_COOKIE = "nemiver-dbg-console";
+
+using namespace common;
+
+struct DebuggingData {
+    IDebugger &debugger;
+    IDebugger::Frame current_frame;
+    UString current_file_path;
+    std::vector<UString> source_files;
+
+    DebuggingData (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+};
+
+struct CommandContinue : public Console::SynchronousCommand {
+    IDebugger &debugger;
+
+    CommandContinue (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "continue";
+        return s_name;
+    }
+
+    const char**
+    aliases () const
+    {
+        static const char *s_aliases[] =
+        {
+            "c",
+            0
+        };
+        return s_aliases;
+    }
+
+    void
+    execute (const std::vector<std::string>&, Console::Stream&)
+    {
+        debugger.do_continue ();
+    }
+};
+
+struct CommandNext : public Console::SynchronousCommand {
+    IDebugger &debugger;
+
+    CommandNext (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "next";
+        return s_name;
+    }
+
+    const char**
+    aliases () const
+    {
+        static const char *s_aliases[] =
+        {
+            "n",
+            0
+        };
+        return s_aliases;
+    }
+
+    void
+    execute (const std::vector<std::string>&, Console::Stream&)
+    {
+        debugger.step_over ();
+    }
+};
+
+struct CommandStep : public Console::SynchronousCommand {
+    IDebugger &debugger;
+
+    CommandStep (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "step";
+        return s_name;
+    }
+
+    const char**
+    aliases () const
+    {
+        static const char *s_aliases[] =
+        {
+            "s",
+            0
+        };
+        return s_aliases;
+    }
+
+    void
+    execute (const std::vector<std::string>&,
+             Console::Stream&)
+    {
+        debugger.step_in ();
+    }
+};
+
+struct CommandNexti : public Console::SynchronousCommand {
+    IDebugger &debugger;
+
+    CommandNexti (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "nexti";
+        return s_name;
+    }
+
+    const char**
+    aliases () const
+    {
+        static const char *s_aliases[] =
+        {
+            "ni",
+            0
+        };
+        return s_aliases;
+    }
+
+    void
+    execute (const std::vector<std::string>&, Console::Stream&)
+    {
+        debugger.step_over_asm ();
+    }
+};
+
+struct CommandStepi : public Console::SynchronousCommand {
+    IDebugger &debugger;
+
+    CommandStepi (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "stepi";
+        return s_name;
+    }
+
+    const char**
+    aliases () const
+    {
+        static const char *s_aliases[] =
+        {
+            "si",
+            0
+        };
+        return s_aliases;
+    }
+
+    void
+    execute (const std::vector<std::string>&, Console::Stream&)
+    {
+        debugger.step_in_asm ();
+    }
+};
+
+struct CommandStop : public Console::SynchronousCommand {
+    IDebugger &debugger;
+
+    CommandStop (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "stop";
+        return s_name;
+    }
+
+    void
+    execute (const std::vector<std::string>&, Console::Stream&)
+    {
+        debugger.stop_target ();
+    }
+};
+
+struct CommandFinish : public Console::SynchronousCommand {
+    IDebugger &debugger;
+
+    CommandFinish (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "finish";
+        return s_name;
+    }
+
+    void
+    execute (const std::vector<std::string>&, Console::Stream&)
+    {
+        debugger.step_out ();
+    }
+};
+
+struct CommandCall : public Console::SynchronousCommand {
+    IDebugger &debugger;
+    std::string cmd;
+
+    CommandCall (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "call";
+        return s_name;
+    }
+
+    void
+    execute (const std::vector<std::string> &a_argv, Console::Stream &a_stream)
+    {
+        if (a_argv.size ()) {
+            cmd.clear ();
+        }
+
+        for (std::vector<std::string>::const_iterator iter = a_argv.begin ();
+             iter != a_argv.end ();
+             ++iter) {
+            cmd += *iter;
+        }
+
+        if (cmd.empty ()) {
+            a_stream << "The history is empty.\n";
+        } else {
+            debugger.call_function (cmd);
+        }
+    }
+};
+
+struct CommandThread : public Console::AsynchronousCommand {
+    IDebugger &debugger;
+
+    CommandThread (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "thread";
+        return s_name;
+    }
+
+    void completions (const std::vector<UString> &a_argv,
+                      std::vector<UString> &a_completion_vector) const
+    {
+        if (a_argv.size () == 0) {
+            a_completion_vector.push_back ("list");
+        }
+    }
+
+    void display_usage (const std::vector<UString> &a_argv,
+                        Console::Stream &a_stream) const
+    {
+        if (a_argv.size ()) {
+            return;
+        }
+
+        a_stream << "Usage:\n"
+                 << "\tthread\n"
+                 << "\tthread [THREAD ID]\n"
+                 << "\tthread list\n";
+    }
+
+    void
+    threads_listed_signal (const std::list<int> a_thread_ids,
+                           const UString &a_cookie,
+                           Console::Stream &a_stream)
+    {
+        NEMIVER_TRY
+
+        if (a_cookie != NEMIVER_DBG_CONSOLE_COOKIE) {
+            return;
+        }
+
+        a_stream << "Threads:\n";
+        for (std::list<int>::const_iterator iter = a_thread_ids.begin ();
+             iter != a_thread_ids.end ();
+             ++iter) {
+            a_stream << *iter << "\n";
+        }
+        done_signal().emit ();
+
+        NEMIVER_CATCH_NOX
+    }
+
+    void
+    execute (const std::vector<std::string> &a_argv, Console::Stream &a_stream)
+    {
+        if (a_argv.size () > 1) {
+            a_stream << "Too much parameters.\n";
+        } else if (!a_argv.size ()) {
+            a_stream << "Current thread ID: " << debugger.get_current_thread ()
+                     << ".\n";
+        } else if (str_utils::string_is_number (a_argv[0])) {
+            debugger.select_thread
+                (str_utils::from_string<unsigned int> (a_argv[0]));
+        } else if (a_argv[0] == "list") {
+            debugger.threads_listed_signal ().connect
+                (sigc::bind<Console::Stream&> (sigc::mem_fun
+                    (*this, &CommandThread::threads_listed_signal),
+                 a_stream));
+            debugger.list_threads (NEMIVER_DBG_CONSOLE_COOKIE);
+            return;
+        } else {
+            a_stream << "Invalid argument: " << a_argv[0] << ".\n";
+        }
+
+        done_signal().emit ();
+    }
+};
+
+struct CommandBreak : public Console::SynchronousCommand {
+    DebuggingData &dbg_data;
+
+    CommandBreak (DebuggingData &a_dbg_data) :
+        dbg_data (a_dbg_data)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "break";
+        return s_name;
+    }
+
+    const char**
+    aliases () const
+    {
+        static const char *s_aliases[] =
+        {
+            "b",
+            0
+        };
+        return s_aliases;
+    }
+
+    void display_usage (const std::vector<UString>&,
+                        Console::Stream &a_stream) const
+    {
+        a_stream << "Usage:\n"
+                 << "\tbreak\n"
+                 << "\tbreak [LINE]\n"
+                 << "\tbreak [FUNCTION]\n"
+                 << "\tbreak *[ADDRESS]\n"
+                 << "\tbreak +[OFFSET]\n"
+                 << "\tbreak -[OFFSET]\n";
+    }
+
+    void
+    execute (const std::vector<std::string> &a_argv, Console::Stream &a_stream)
+    {
+        IDebugger::Frame &frame = dbg_data.current_frame;
+        IDebugger &debugger = dbg_data.debugger;
+
+        if (frame.file_full_name ().empty () ||
+            dbg_data.current_file_path.empty ()) {
+            a_stream << "Cannot set a breakpoint at this position.\n";
+        }
+
+        if (a_argv.size () > 1) {
+            a_stream << "Too much parameters.\n";
+        } else if (a_argv.size () == 0) {
+            debugger.set_breakpoint (frame.file_full_name (), frame.line ());
+        }
+
+        const char first_param_char = a_argv[0][0];
+        if (str_utils::string_is_number (a_argv[0])) {
+            debugger.set_breakpoint (dbg_data.current_file_path,
+                                     str_utils::from_string<int> (a_argv[0]));
+        } else if ((first_param_char >= 'a' && first_param_char <= 'z')
+                   || first_param_char == '_') {
+            debugger.set_breakpoint (a_argv[0]);
+        } else if (first_param_char == '*') {
+            std::string addr (a_argv[0].substr (1));
+            if (str_utils::string_is_hexa_number (addr)) {
+                Address address (addr);
+                debugger.set_breakpoint (address);
+            } else {
+                a_stream << "Invalid address: " << addr << ".\n";
+            }
+        } else if (first_param_char == '+' || first_param_char == '-') {
+            std::string offset (a_argv[0].substr (1));
+            if (str_utils::string_is_decimal_number (offset)) {
+                int line = frame.line ();
+                if (first_param_char == '+') {
+                    line += str_utils::from_string<int> (offset);
+                } else {
+                    line -= str_utils::from_string<int> (offset);
+                }
+                debugger.set_breakpoint (frame.file_full_name (), line);
+            } else {
+                a_stream << "Invalid offset: " << offset << ".\n";
+            }
+        } else {
+            a_stream << "Invalid argument: " << a_argv[0] << ".\n";
+        }
+    }
+};
+
+struct CommandPrint : public Console::AsynchronousCommand {
+    IDebugger &debugger;
+    std::string expression;
+
+    CommandPrint (IDebugger &a_debugger) :
+        debugger (a_debugger)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "print";
+        return s_name;
+    }
+
+    void
+    on_variable_created_signal (const IDebugger::VariableSafePtr a_var,
+                                Console::Stream &a_stream)
+    {
+        NEMIVER_TRY
+
+        THROW_IF_FAIL (a_var);
+        a_stream << a_var->name () << " = " << a_var->value () << "\n";
+        debugger.delete_variable (a_var);
+        done_signal ().emit ();
+
+        NEMIVER_CATCH_NOX
+    }
+
+    void
+    execute (const std::vector<std::string> &a_argv, Console::Stream &a_stream)
+    {
+        if (a_argv.size ()) {
+            expression.clear ();
+        }
+
+        for (std::vector<std::string>::const_iterator iter = a_argv.begin ();
+             iter != a_argv.end ();
+             ++iter) {
+            expression += *iter;
+        }
+
+        if (expression.empty ()) {
+            a_stream << "No history\n";
+            done_signal ().emit ();
+            return;
+        }
+
+        debugger.create_variable
+            (expression, sigc::bind<Console::Stream&>
+                (sigc::mem_fun
+                    (*this, &CommandPrint::on_variable_created_signal),
+                 a_stream));
+    }
+};
+
+struct CommandOpen : public Console::SynchronousCommand {
+    DebuggingData &dbg_data;
+    sigc::signal<void, UString> file_opened_signal;
+
+    CommandOpen (DebuggingData &a_dbg_data) :
+        dbg_data (a_dbg_data)
+    {
+    }
+
+    const std::string&
+    name () const
+    {
+        static const std::string &s_name = "open";
+        return s_name;
+    }
+
+    const char**
+    aliases () const
+    {
+        static const char *s_aliases[] =
+        {
+            "o",
+            0
+        };
+        return s_aliases;
+    }
+
+    void completions (const std::vector<UString>&,
+                      std::vector<UString> &a_completion_vector) const
+    {
+        a_completion_vector.insert (a_completion_vector.begin (),
+                                    dbg_data.source_files.begin (),
+                                    dbg_data.source_files.end ());
+    }
+
+    void
+    execute (const std::vector<std::string> &a_argv, Console::Stream&)
+    {
+        for (std::vector<std::string>::const_iterator iter = a_argv.begin ();
+             iter != a_argv.end ();
+             ++iter) {
+            UString path = *iter;
+            if (path.size () && path[0] == '~') {
+                path = path.replace (0, 1, Glib::get_home_dir ());
+            }
+            file_opened_signal.emit (path);
+        }
+    }
+};
+
+struct DBGConsole::Priv {
+    DebuggingData data;
+
+    CommandContinue cmd_continue;
+    CommandNext cmd_next;
+    CommandStep cmd_step;
+    CommandBreak cmd_break;
+    CommandPrint cmd_print;
+    CommandCall cmd_call;
+    CommandFinish cmd_finish;
+    CommandThread cmd_thread;
+    CommandStop cmd_stop;
+    CommandNexti cmd_nexti;
+    CommandStepi cmd_stepi;
+    CommandOpen cmd_open;
+
+    Priv (IDebugger &a_debugger) :
+        data (a_debugger),
+        cmd_continue (a_debugger),
+        cmd_next (a_debugger),
+        cmd_step (a_debugger),
+        cmd_break (data),
+        cmd_print (a_debugger),
+        cmd_call (a_debugger),
+        cmd_finish (a_debugger),
+        cmd_thread (a_debugger),
+        cmd_stop (a_debugger),
+        cmd_nexti (a_debugger),
+        cmd_stepi (a_debugger),
+        cmd_open (data)
+    {
+        init_signals ();
+    }
+
+    void
+    on_stopped_signal (IDebugger::StopReason,
+                       bool,
+                       const IDebugger::Frame &a_frame,
+                       int,
+                       int,
+                       const UString&)
+    {
+        data.current_frame = a_frame;
+        data.current_file_path = a_frame.file_full_name ();
+    }
+
+    void
+    on_files_listed_signal (const std::vector<UString> &a_files, const UString&)
+    {
+        data.source_files = a_files;
+    }
+
+    void
+    init_signals ()
+    {
+        data.debugger.stopped_signal ().connect
+            (sigc::mem_fun (*this, &DBGConsole::Priv::on_stopped_signal));
+        data.debugger.files_listed_signal ().connect (sigc::mem_fun
+            (*this, &DBGConsole::Priv::on_files_listed_signal));
+    }
+};
+
+DBGConsole::DBGConsole (int a_fd, IDebugger &a_debugger) :
+    Console (a_fd),
+    m_priv (new Priv (a_debugger))
+{
+    register_command (m_priv->cmd_continue);
+    register_command (m_priv->cmd_next);
+    register_command (m_priv->cmd_step);
+    register_command (m_priv->cmd_break);
+    register_command (m_priv->cmd_print);
+    register_command (m_priv->cmd_call);
+    register_command (m_priv->cmd_finish);
+    register_command (m_priv->cmd_thread);
+    register_command (m_priv->cmd_stop);
+    register_command (m_priv->cmd_nexti);
+    register_command (m_priv->cmd_stepi);
+    register_command (m_priv->cmd_open);
+}
+
+void
+DBGConsole::current_file_path (const UString &a_file_path)
+{
+    THROW_IF_FAIL (m_priv);
+    m_priv->data.current_file_path = a_file_path;
+}
+
+const UString&
+DBGConsole::current_file_path () const
+{
+    THROW_IF_FAIL (m_priv);
+    return m_priv->data.current_file_path;
+}
+
+DBGConsole::~DBGConsole ()
+{
+}
+
+sigc::signal<void, UString>
+DBGConsole::file_opened_signal () const
+{
+    THROW_IF_FAIL (m_priv);
+    return m_priv->cmd_open.file_opened_signal;
+}
+
+NEMIVER_END_NAMESPACE(nemiver)
+
diff --git a/src/dbgengine/nmv-dbg-console.h b/src/dbgengine/nmv-dbg-console.h
new file mode 100644
index 0000000..d2a8b6b
--- /dev/null
+++ b/src/dbgengine/nmv-dbg-console.h
@@ -0,0 +1,58 @@
+//Author: Fabien Parent
+/*
+ *This file is part of the Nemiver project
+ *
+ *Nemiver 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,
+ *or (at your option) any later version.
+ *
+ *Nemiver 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 Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+#ifndef __NMV_DBG_CONSOLE_H__
+#define __NMV_DBG_CONSOLE_H__
+
+#include "common/nmv-console.h"
+
+NEMIVER_BEGIN_NAMESPACE(nemiver)
+
+namespace common {
+    class UString;
+}
+
+class IDebugger;
+
+using common::SafePtr;
+using common::UString;
+
+class DBGConsole : public common::Console {
+    struct Priv;
+    SafePtr<Priv> m_priv;
+
+public:
+    DBGConsole (int a_fd, IDebugger &a_debugger);
+    ~DBGConsole ();
+
+    void current_file_path (const UString &a_file_path);
+    const UString& current_file_path () const;
+
+    sigc::signal<void, UString> file_opened_signal () const;
+};
+
+NEMIVER_END_NAMESPACE(nemiver)
+
+#endif /* __NMV_DBG_CONSOLE_H__ */
+
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.cc b/src/persp/dbgperspective/nmv-dbg-perspective.cc
index 48cf7bb..e3b68da 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective.cc
+++ b/src/persp/dbgperspective/nmv-dbg-perspective.cc
@@ -53,6 +53,7 @@
 #include "common/nmv-str-utils.h"
 #include "common/nmv-address.h"
 #include "common/nmv-loc.h"
+#include "nmv-dbg-console.h"
 #include "nmv-sess-mgr.h"
 #include "nmv-dbg-perspective.h"
 #include "nmv-source-editor.h"
@@ -118,6 +119,7 @@ const char *TARGET_TERMINAL_VIEW_TITLE = _("Target Terminal");
 const char *BREAKPOINTS_VIEW_TITLE     = _("Breakpoints");
 const char *REGISTERS_VIEW_TITLE       = _("Registers");
 const char *MEMORY_VIEW_TITLE          = _("Memory");
+const char *CONSOLE_VIEW_TITLE         = _("Console");
 
 const char *CAPTION_SESSION_NAME = "captionname";
 const char *SESSION_NAME = "sessionname";
@@ -412,6 +414,7 @@ private:
     void on_notebook_tabs_reordered(Gtk::Widget* a_page, guint a_page_num);
 
     void on_layout_changed ();
+    void on_file_opened (UString a_path);
     void on_activate_context_view ();
     void on_activate_target_terminal_view ();
     void on_activate_breakpoints_view ();
@@ -739,6 +742,10 @@ public:
 
     Gtk::ScrolledWindow& get_local_vars_inspector_scrolled_win ();
 
+    Terminal& console_terminal ();
+
+    Gtk::Box& console_box ();
+
     Terminal& get_terminal ();
 
     Gtk::Box& get_terminal_box ();
@@ -897,6 +904,9 @@ struct DBGPerspective::Priv {
     Path2MonitorMap path_2_monitor_map;
     SafePtr<LocalVarsInspector> variables_editor;
     SafePtr<Gtk::ScrolledWindow> variables_editor_scrolled_win;
+    SafePtr<Terminal> console_terminal;
+    SafePtr<DBGConsole> dbg_console;
+    SafePtr<Gtk::Box> console_box;
     SafePtr<Terminal> terminal;
     SafePtr<Gtk::Box> terminal_box;
     SafePtr<Gtk::ScrolledWindow> breakpoints_scrolled_win;
@@ -1761,6 +1771,17 @@ DBGPerspective::on_switch_page_signal (Gtk::Widget *a_page,
     NEMIVER_TRY
     m_priv->current_page_num = a_page_num;
     LOG_DD ("current_page_num: " << m_priv->current_page_num);
+
+    map<int, SourceEditor*>::iterator iter, nil;
+    nil = m_priv->pagenum_2_source_editor_map.end ();
+    iter = m_priv->pagenum_2_source_editor_map.find (m_priv->current_page_num);
+    if (iter != nil) {
+        SourceEditor *editor = get_current_source_editor ();
+        if (editor) {
+            THROW_IF_FAIL (m_priv->dbg_console);
+            m_priv->dbg_console->current_file_path (editor->get_path ());
+        }
+    }
     NEMIVER_CATCH
 }
 
@@ -2811,6 +2832,14 @@ DBGPerspective::on_layout_changed ()
 }
 
 void
+DBGPerspective::on_file_opened (UString a_file)
+{
+    NEMIVER_TRY
+    open_file (a_file, -1);
+    NEMIVER_CATCH
+}
+
+void
 DBGPerspective::on_activate_context_view ()
 {
     LOG_FUNCTION_SCOPE_NORMAL_DD;
@@ -3685,6 +3714,11 @@ DBGPerspective::init_body ()
         (sigc::mem_fun (this, &DBGPerspective::on_notebook_tabs_reordered));
 #endif
 
+    IDebuggerSafePtr& dbg = debugger ();
+    THROW_IF_FAIL (dbg);
+    m_priv->dbg_console.reset
+                (new DBGConsole (console_terminal ().slave_fd (), *dbg));
+
     UString layout = DBG_PERSPECTIVE_DEFAULT_LAYOUT;
     NEMIVER_TRY
     conf_mgr.get_key_value (CONF_KEY_DBG_PERSPECTIVE_LAYOUT, layout);
@@ -3732,6 +3766,10 @@ DBGPerspective::init_signals ()
 
     m_priv->layout_mgr.layout_changed_signal ().connect (sigc::mem_fun
             (*this, &DBGPerspective::on_layout_changed));
+
+    THROW_IF_FAIL (m_priv->dbg_console);
+    m_priv->dbg_console->file_opened_signal ().connect (sigc::mem_fun
+        (*this, &DBGPerspective::on_file_opened));
 }
 
 /// Connect slots (callbacks) to the signals emitted by the
@@ -4928,6 +4966,9 @@ DBGPerspective::add_views_to_layout ()
 {
     THROW_IF_FAIL (m_priv);
 
+    m_priv->layout ().add_view (console_box (),
+                                CONSOLE_VIEW_TITLE,
+                                CONSOLE_VIEW_INDEX);
 #ifdef WITH_MEMORYVIEW
     m_priv->layout ().add_view (get_memory_view ().widget (),
                                 MEMORY_VIEW_TITLE,
@@ -8011,6 +8052,39 @@ DBGPerspective::get_local_vars_inspector_scrolled_win ()
     return *m_priv->variables_editor_scrolled_win;
 }
 
+Terminal&
+DBGPerspective::console_terminal ()
+{
+    THROW_IF_FAIL (m_priv);
+    if (!m_priv->console_terminal) {
+        string relative_path = Glib::build_filename ("menus",
+                                                     "terminalmenu.xml");
+        string absolute_path;
+        THROW_IF_FAIL (build_absolute_resource_path
+            (Glib::filename_to_utf8 (relative_path), absolute_path));
+
+        m_priv->console_terminal.reset (new Terminal
+            (absolute_path, workbench ().get_ui_manager ()));
+    }
+    THROW_IF_FAIL (m_priv->console_terminal);
+    return *m_priv->console_terminal;
+}
+
+Gtk::Box&
+DBGPerspective::console_box ()
+{
+    THROW_IF_FAIL (m_priv);
+    if (!m_priv->console_box) {
+        m_priv->console_box.reset (new Gtk::HBox);
+        THROW_IF_FAIL (m_priv->console_box);
+        Gtk::VScrollbar *scrollbar = Gtk::manage (new Gtk::VScrollbar);
+        m_priv->console_box->pack_end (*scrollbar, false, false, 0);
+        m_priv->console_box->pack_start (console_terminal ().widget ());
+        scrollbar->set_adjustment (console_terminal ().adjustment ());
+    }
+    THROW_IF_FAIL (m_priv->console_box);
+    return *m_priv->console_box;
+}
 
 Terminal&
 DBGPerspective::get_terminal ()
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.h b/src/persp/dbgperspective/nmv-dbg-perspective.h
index ddb00ff..cc0ea7b 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective.h
+++ b/src/persp/dbgperspective/nmv-dbg-perspective.h
@@ -39,6 +39,7 @@ extern const char *TARGET_TERMINAL_VIEW_TITLE;
 extern const char *BREAKPOINTS_VIEW_TITLE;
 extern const char *REGISTERS_VIEW_TITLE;
 extern const char *MEMORY_VIEW_TITLE;
+extern const char *CONSOLE_VIEW_TITLE;
 
 enum ViewsIndex
 {
@@ -47,8 +48,9 @@ enum ViewsIndex
     BREAKPOINTS_VIEW_INDEX,
     REGISTERS_VIEW_INDEX,
 #ifdef WITH_MEMORYVIEW
-    MEMORY_VIEW_INDEX
+    MEMORY_VIEW_INDEX,
 #endif // WITH_MEMORYVIEW
+    CONSOLE_VIEW_INDEX
 };
 
 class SourceEditor;
diff --git a/src/uicommon/nmv-terminal.cc b/src/uicommon/nmv-terminal.cc
index 5222bd1..3ae6da1 100644
--- a/src/uicommon/nmv-terminal.cc
+++ b/src/uicommon/nmv-terminal.cc
@@ -335,6 +335,12 @@ Terminal::feed (const UString &a_text)
         vte_terminal_feed (m_priv->vte, a_text.c_str (), a_text.size ());
 }
 
+int
+Terminal::slave_fd () const
+{
+    THROW_IF_FAIL (m_priv);
+    return m_priv->slave_pty;
+}
 
 NEMIVER_END_NAMESPACE(nemiver)
 
diff --git a/src/uicommon/nmv-terminal.h b/src/uicommon/nmv-terminal.h
index 6f5b9ed..13d8818 100644
--- a/src/uicommon/nmv-terminal.h
+++ b/src/uicommon/nmv-terminal.h
@@ -67,6 +67,7 @@ public:
     Gtk::Widget& widget () const;
     Glib::RefPtr<Gtk::Adjustment> adjustment () const;
     UString slave_pts_name () const;
+    int slave_fd () const;
     void modify_font (const Pango::FontDescription &font_desc);
     void feed (const UString &a_text);
 };//end class Terminal
-- 
1.7.9



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]