tools/*: remove bcc and as86 as they are no longer required to compile the system...
[virtualbox.git] / src / VBox / Debugger / VBoxDbgConsole.cpp
blob4754993f00293193f60e31bb23e5fd5c1d3ab673
1 /* $Id$ */
2 /** @file
3 * VBox Debugger GUI - Console.
4 */
6 /*
7 * Copyright (C) 2006-2010 Oracle Corporation
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 /*******************************************************************************
20 * Header Files *
21 *******************************************************************************/
22 #define LOG_GROUP LOG_GROUP_DBGG
23 #include "VBoxDbgConsole.h"
25 #include <QLabel>
26 #include <QApplication>
27 #include <QFont>
28 #include <QLineEdit>
29 #include <QHBoxLayout>
30 #include <QAction>
31 #include <QContextMenuEvent>
33 #include <VBox/dbg.h>
34 #include <VBox/vmm/cfgm.h>
35 #include <VBox/err.h>
37 #include <iprt/thread.h>
38 #include <iprt/tcp.h>
39 #include <VBox/log.h>
40 #include <iprt/assert.h>
41 #include <iprt/asm.h>
42 #include <iprt/alloc.h>
43 #include <iprt/string.h>
50 * V B o x D b g C o n s o l e O u t p u t
51 * V B o x D b g C o n s o l e O u t p u t
52 * V B o x D b g C o n s o l e O u t p u t
58 VBoxDbgConsoleOutput::VBoxDbgConsoleOutput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
59 : QTextEdit(pParent), m_uCurLine(0), m_uCurPos(0), m_hGUIThread(RTThreadNativeSelf())
61 setReadOnly(true);
62 setUndoRedoEnabled(false);
63 setOverwriteMode(false);
64 setPlainText("");
65 setTextInteractionFlags(Qt::TextBrowserInteraction);
66 setAutoFormatting(QTextEdit::AutoAll);
67 setTabChangesFocus(true);
68 setAcceptRichText(false);
70 #ifdef Q_WS_MAC
71 QFont Font("Monaco", 10, QFont::Normal, FALSE);
72 Font.setStyleStrategy(QFont::NoAntialias);
73 #else
74 QFont Font = font();
75 Font.setStyleHint(QFont::TypeWriter);
76 Font.setFamily("Courier [Monotype]");
77 #endif
78 setFont(Font);
80 /* green on black */
81 QPalette Pal(palette());
82 Pal.setColor(QPalette::All, QPalette::Base, QColor(Qt::black));
83 setPalette(Pal);
84 setTextColor(QColor(qRgb(0, 0xe0, 0)));
85 NOREF(pszName);
89 VBoxDbgConsoleOutput::~VBoxDbgConsoleOutput()
91 Assert(m_hGUIThread == RTThreadNativeSelf());
95 void
96 VBoxDbgConsoleOutput::appendText(const QString &rStr, bool fClearSelection)
98 Assert(m_hGUIThread == RTThreadNativeSelf());
100 if (rStr.isEmpty() || rStr.isNull() || !rStr.length())
101 return;
104 * Insert all in one go and make sure it's visible.
106 * We need to move the cursor and unselect any selected text before
107 * inserting anything, otherwise, text will disappear.
109 QTextCursor Cursor = textCursor();
110 if (!fClearSelection && Cursor.hasSelection())
112 QTextCursor SavedCursor = Cursor;
113 Cursor.clearSelection();
114 Cursor.movePosition(QTextCursor::End);
116 Cursor.insertText(rStr);
118 setTextCursor(SavedCursor);
120 else
122 if (Cursor.hasSelection())
123 Cursor.clearSelection();
124 if (!Cursor.atEnd())
125 Cursor.movePosition(QTextCursor::End);
127 Cursor.insertText(rStr);
129 setTextCursor(Cursor);
130 ensureCursorVisible();
139 * V B o x D b g C o n s o l e I n p u t
140 * V B o x D b g C o n s o l e I n p u t
141 * V B o x D b g C o n s o l e I n p u t
147 VBoxDbgConsoleInput::VBoxDbgConsoleInput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
148 : QComboBox(pParent), m_hGUIThread(RTThreadNativeSelf())
150 addItem(""); /* invariant: empty command line is the last item */
152 setEditable(true);
153 setInsertPolicy(NoInsert);
154 setAutoCompletion(false);
155 setMaxCount(50);
156 const QLineEdit *pEdit = lineEdit();
157 if (pEdit)
158 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
160 NOREF(pszName);
164 VBoxDbgConsoleInput::~VBoxDbgConsoleInput()
166 Assert(m_hGUIThread == RTThreadNativeSelf());
170 void
171 VBoxDbgConsoleInput::setLineEdit(QLineEdit *pEdit)
173 Assert(m_hGUIThread == RTThreadNativeSelf());
174 QComboBox::setLineEdit(pEdit);
175 if (lineEdit() == pEdit && pEdit)
176 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
180 void
181 VBoxDbgConsoleInput::returnPressed()
183 Assert(m_hGUIThread == RTThreadNativeSelf());
185 QString strCommand = currentText();
186 /* TODO: trim whitespace? */
187 if (strCommand.isEmpty())
188 return;
190 /* deal with the current command. */
191 emit commandSubmitted(strCommand);
195 * Add current command to history.
197 bool fNeedsAppending = true;
199 /* invariant: empty line at the end */
200 int iLastItem = count() - 1;
201 Assert(itemText(iLastItem).isEmpty());
203 /* have previous command? check duplicate. */
204 if (iLastItem > 0)
206 const QString strPrevCommand(itemText(iLastItem - 1));
207 if (strCommand == strPrevCommand)
208 fNeedsAppending = false;
211 if (fNeedsAppending)
213 /* history full? drop the oldest command. */
214 if (count() == maxCount())
216 removeItem(0);
217 --iLastItem;
220 /* insert before the empty line. */
221 insertItem(iLastItem, strCommand);
224 /* invariant: empty line at the end */
225 int iNewLastItem = count() - 1;
226 Assert(itemText(iNewLastItem).isEmpty());
228 /* select empty line to present "new" command line to the user */
229 setCurrentIndex(iNewLastItem);
239 * V B o x D b g C o n s o l e
240 * V B o x D b g C o n s o l e
241 * V B o x D b g C o n s o l e
247 VBoxDbgConsole::VBoxDbgConsole(VBoxDbgGui *a_pDbgGui, QWidget *a_pParent/* = NULL*/)
248 : VBoxDbgBaseWindow(a_pDbgGui, a_pParent), m_pOutput(NULL), m_pInput(NULL), m_fInputRestoreFocus(false),
249 m_pszInputBuf(NULL), m_cbInputBuf(0), m_cbInputBufAlloc(0),
250 m_pszOutputBuf(NULL), m_cbOutputBuf(0), m_cbOutputBufAlloc(0),
251 m_pTimer(NULL), m_fUpdatePending(false), m_Thread(NIL_RTTHREAD), m_EventSem(NIL_RTSEMEVENT),
252 m_fTerminate(false), m_fThreadTerminated(false)
254 setWindowTitle("VBoxDbg - Console");
257 * Create the output text box.
259 m_pOutput = new VBoxDbgConsoleOutput(this);
261 /* try figure a suitable size */
262 QLabel *pLabel = new QLabel( "11111111111111111111111111111111111111111111111111111111111111111111111111111112222222222", this);
263 pLabel->setFont(m_pOutput->font());
264 QSize Size = pLabel->sizeHint();
265 delete pLabel;
266 Size.setWidth((int)(Size.width() * 1.10));
267 Size.setHeight(Size.width() / 2);
268 resize(Size);
271 * Create the input combo box (with a label).
273 QHBoxLayout *pLayout = new QHBoxLayout();
274 //pLayout->setSizeConstraint(QLayout::SetMaximumSize);
276 pLabel = new QLabel(" Command ");
277 pLayout->addWidget(pLabel);
278 pLabel->setMaximumSize(pLabel->sizeHint());
279 pLabel->setAlignment(Qt::AlignCenter);
281 m_pInput = new VBoxDbgConsoleInput(NULL);
282 pLayout->addWidget(m_pInput);
283 m_pInput->setDuplicatesEnabled(false);
284 connect(m_pInput, SIGNAL(commandSubmitted(const QString &)), this, SLOT(commandSubmitted(const QString &)));
286 # if 0//def Q_WS_MAC
287 pLabel = new QLabel(" ");
288 pLayout->addWidget(pLabel);
289 pLabel->setMaximumSize(20, m_pInput->sizeHint().height() + 6);
290 pLabel->setMinimumSize(20, m_pInput->sizeHint().height() + 6);
291 # endif
293 QWidget *pHBox = new QWidget(this);
294 pHBox->setLayout(pLayout);
296 m_pInput->setEnabled(false); /* (we'll get a ready notification) */
300 * Vertical layout box on the whole widget.
302 QVBoxLayout *pVLayout = new QVBoxLayout();
303 pVLayout->setContentsMargins(0, 0, 0, 0);
304 pVLayout->setSpacing(5);
305 pVLayout->addWidget(m_pOutput);
306 pVLayout->addWidget(pHBox);
307 setLayout(pVLayout);
310 * The tab order is from input to output, not the other way around as it is by default.
312 setTabOrder(m_pInput, m_pOutput);
313 m_fInputRestoreFocus = true; /* hack */
316 * Setup the timer.
318 m_pTimer = new QTimer(this);
319 connect(m_pTimer, SIGNAL(timeout()), SLOT(updateOutput()));
322 * Init the backend structure.
324 m_Back.Core.pfnInput = backInput;
325 m_Back.Core.pfnRead = backRead;
326 m_Back.Core.pfnWrite = backWrite;
327 m_Back.Core.pfnSetReady = backSetReady;
328 m_Back.pSelf = this;
331 * Create the critical section, the event semaphore and the debug console thread.
333 int rc = RTCritSectInit(&m_Lock);
334 AssertRC(rc);
336 rc = RTSemEventCreate(&m_EventSem);
337 AssertRC(rc);
339 rc = RTThreadCreate(&m_Thread, backThread, this, 0, RTTHREADTYPE_DEBUGGER, RTTHREADFLAGS_WAITABLE, "VBoxDbgC");
340 AssertRC(rc);
341 if (RT_FAILURE(rc))
342 m_Thread = NIL_RTTHREAD;
345 * Shortcuts.
347 m_pFocusToInput = new QAction("", this);
348 m_pFocusToInput->setShortcut(QKeySequence("Ctrl+L"));
349 addAction(m_pFocusToInput);
350 connect(m_pFocusToInput, SIGNAL(triggered(bool)), this, SLOT(actFocusToInput()));
352 m_pFocusToOutput = new QAction("", this);
353 m_pFocusToOutput->setShortcut(QKeySequence("Ctrl+O"));
354 addAction(m_pFocusToOutput);
355 connect(m_pFocusToOutput, SIGNAL(triggered(bool)), this, SLOT(actFocusToOutput()));
359 VBoxDbgConsole::~VBoxDbgConsole()
361 Assert(isGUIThread());
364 * Wait for the thread.
366 ASMAtomicWriteBool(&m_fTerminate, true);
367 RTSemEventSignal(m_EventSem);
368 if (m_Thread != NIL_RTTHREAD)
370 int rc = RTThreadWait(m_Thread, 15000, NULL);
371 AssertRC(rc);
372 m_Thread = NIL_RTTHREAD;
376 * Free resources.
378 delete m_pTimer;
379 m_pTimer = NULL;
380 RTCritSectDelete(&m_Lock);
381 RTSemEventDestroy(m_EventSem);
382 m_EventSem = 0;
383 m_pOutput = NULL;
384 m_pInput = NULL;
385 if (m_pszInputBuf)
387 RTMemFree(m_pszInputBuf);
388 m_pszInputBuf = NULL;
390 m_cbInputBuf = 0;
391 m_cbInputBufAlloc = 0;
393 delete m_pFocusToInput;
394 m_pFocusToInput = NULL;
395 delete m_pFocusToOutput;
396 m_pFocusToOutput = NULL;
400 void
401 VBoxDbgConsole::commandSubmitted(const QString &rCommand)
403 Assert(isGUIThread());
405 lock();
406 RTSemEventSignal(m_EventSem);
408 QByteArray Utf8Array = rCommand.toUtf8();
409 const char *psz = Utf8Array.constData();
410 size_t cb = strlen(psz);
413 * Make sure we've got space for the input.
415 if (cb + m_cbInputBuf >= m_cbInputBufAlloc)
417 size_t cbNew = RT_ALIGN_Z(cb + m_cbInputBufAlloc + 1, 128);
418 void *pv = RTMemRealloc(m_pszInputBuf, cbNew);
419 if (!pv)
421 unlock();
422 return;
424 m_pszInputBuf = (char *)pv;
425 m_cbInputBufAlloc = cbNew;
429 * Add the input and output it.
431 memcpy(m_pszInputBuf + m_cbInputBuf, psz, cb);
432 m_cbInputBuf += cb;
433 m_pszInputBuf[m_cbInputBuf++] = '\n';
435 m_pOutput->appendText(rCommand + "\n", true /*fClearSelection*/);
436 m_pOutput->ensureCursorVisible();
438 m_fInputRestoreFocus = m_pInput->hasFocus(); /* dirty focus hack */
439 m_pInput->setEnabled(false);
441 Log(("VBoxDbgConsole::commandSubmitted: %s (input-enabled=%RTbool)\n", psz, m_pInput->isEnabled()));
442 unlock();
446 void
447 VBoxDbgConsole::updateOutput()
449 Assert(isGUIThread());
451 lock();
452 m_fUpdatePending = false;
453 if (m_cbOutputBuf)
455 m_pOutput->appendText(QString::fromUtf8((const char *)m_pszOutputBuf, (int)m_cbOutputBuf), false /*fClearSelection*/);
456 m_cbOutputBuf = 0;
458 unlock();
463 * Lock the object.
465 void
466 VBoxDbgConsole::lock()
468 RTCritSectEnter(&m_Lock);
473 * Unlocks the object.
475 void
476 VBoxDbgConsole::unlock()
478 RTCritSectLeave(&m_Lock);
484 * Checks if there is input.
486 * @returns true if there is input ready.
487 * @returns false if there not input ready.
488 * @param pBack Pointer to VBoxDbgConsole::m_Back.
489 * @param cMillies Number of milliseconds to wait on input data.
491 /*static*/ DECLCALLBACK(bool)
492 VBoxDbgConsole::backInput(PDBGCBACK pBack, uint32_t cMillies)
494 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
495 pThis->lock();
497 bool fRc = true;
498 if (!pThis->m_cbInputBuf)
501 * Wait outside the lock for the requested time, then check again.
503 pThis->unlock();
504 RTSemEventWait(pThis->m_EventSem, cMillies);
505 pThis->lock();
506 fRc = pThis->m_cbInputBuf
507 || ASMAtomicUoReadBool(&pThis->m_fTerminate);
510 pThis->unlock();
511 return fRc;
516 * Read input.
518 * @returns VBox status code.
519 * @param pBack Pointer to VBoxDbgConsole::m_Back.
520 * @param pvBuf Where to put the bytes we read.
521 * @param cbBuf Maximum nymber of bytes to read.
522 * @param pcbRead Where to store the number of bytes actually read.
523 * If NULL the entire buffer must be filled for a
524 * successful return.
526 /*static*/ DECLCALLBACK(int)
527 VBoxDbgConsole::backRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead)
529 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
530 Assert(pcbRead); /** @todo implement this bit */
531 if (pcbRead)
532 *pcbRead = 0;
534 pThis->lock();
535 int rc = VINF_SUCCESS;
536 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
538 if (pThis->m_cbInputBuf)
540 const char *psz = pThis->m_pszInputBuf;
541 size_t cbRead = RT_MIN(pThis->m_cbInputBuf, cbBuf);
542 memcpy(pvBuf, psz, cbRead);
543 psz += cbRead;
544 pThis->m_cbInputBuf -= cbRead;
545 if (*psz)
546 memmove(pThis->m_pszInputBuf, psz, pThis->m_cbInputBuf);
547 pThis->m_pszInputBuf[pThis->m_cbInputBuf] = '\0';
548 *pcbRead = cbRead;
551 else
552 rc = VERR_GENERAL_FAILURE;
553 pThis->unlock();
554 return rc;
559 * Write (output).
561 * @returns VBox status code.
562 * @param pBack Pointer to VBoxDbgConsole::m_Back.
563 * @param pvBuf What to write.
564 * @param cbBuf Number of bytes to write.
565 * @param pcbWritten Where to store the number of bytes actually written.
566 * If NULL the entire buffer must be successfully written.
568 /*static*/ DECLCALLBACK(int)
569 VBoxDbgConsole::backWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
571 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
572 int rc = VINF_SUCCESS;
574 pThis->lock();
575 if (cbBuf + pThis->m_cbOutputBuf >= pThis->m_cbOutputBufAlloc)
577 size_t cbNew = RT_ALIGN_Z(cbBuf + pThis->m_cbOutputBufAlloc + 1, 1024);
578 void *pv = RTMemRealloc(pThis->m_pszOutputBuf, cbNew);
579 if (!pv)
581 pThis->unlock();
582 if (pcbWritten)
583 *pcbWritten = 0;
584 return VERR_NO_MEMORY;
586 pThis->m_pszOutputBuf = (char *)pv;
587 pThis->m_cbOutputBufAlloc = cbNew;
591 * Add the output.
593 memcpy(pThis->m_pszOutputBuf + pThis->m_cbOutputBuf, pvBuf, cbBuf);
594 pThis->m_cbOutputBuf += cbBuf;
595 pThis->m_pszOutputBuf[pThis->m_cbOutputBuf] = '\0';
596 if (pcbWritten)
597 *pcbWritten = cbBuf;
599 if (ASMAtomicUoReadBool(&pThis->m_fTerminate))
600 rc = VERR_GENERAL_FAILURE;
603 * Tell the GUI thread to draw this text.
604 * We cannot do it from here without frequent crashes.
606 if (!pThis->m_fUpdatePending)
607 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kUpdate));
609 pThis->unlock();
611 return rc;
615 /*static*/ DECLCALLBACK(void)
616 VBoxDbgConsole::backSetReady(PDBGCBACK pBack, bool fReady)
618 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
619 if (fReady)
620 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kInputEnable));
625 * The Debugger Console Thread
627 * @returns VBox status code (ignored).
628 * @param Thread The thread handle.
629 * @param pvUser Pointer to the VBoxDbgConsole object.s
631 /*static*/ DECLCALLBACK(int)
632 VBoxDbgConsole::backThread(RTTHREAD Thread, void *pvUser)
634 VBoxDbgConsole *pThis = (VBoxDbgConsole *)pvUser;
635 LogFlow(("backThread: Thread=%p pvUser=%p\n", (void *)Thread, pvUser));
637 NOREF(Thread);
640 * Create and execute the console.
642 int rc = pThis->dbgcCreate(&pThis->m_Back.Core, 0);
644 ASMAtomicUoWriteBool(&pThis->m_fThreadTerminated, true);
645 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
646 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(rc == VINF_SUCCESS
647 ? VBoxDbgConsoleEvent::kTerminatedUser
648 : VBoxDbgConsoleEvent::kTerminatedOther));
649 LogFlow(("backThread: returns %Rrc (m_fTerminate=%RTbool)\n", rc, ASMAtomicUoReadBool(&pThis->m_fTerminate)));
650 return rc;
654 bool
655 VBoxDbgConsole::event(QEvent *pGenEvent)
657 Assert(isGUIThread());
658 if (pGenEvent->type() == (QEvent::Type)VBoxDbgConsoleEvent::kEventNumber)
660 VBoxDbgConsoleEvent *pEvent = (VBoxDbgConsoleEvent *)pGenEvent;
662 switch (pEvent->command())
664 /* make update pending. */
665 case VBoxDbgConsoleEvent::kUpdate:
666 lock();
667 if (!m_fUpdatePending)
669 m_fUpdatePending = true;
670 m_pTimer->setSingleShot(true);
671 m_pTimer->start(10);
673 unlock();
674 break;
676 /* Re-enable the input field and restore focus. */
677 case VBoxDbgConsoleEvent::kInputEnable:
678 Log(("VBoxDbgConsole: kInputEnable (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
679 m_pInput->setEnabled(true);
680 if ( m_fInputRestoreFocus
681 && !m_pInput->hasFocus())
682 m_pInput->setFocus(); /* this is a hack. */
683 m_fInputRestoreFocus = false;
684 break;
686 /* The thread terminated by user command (exit, quit, bye). */
687 case VBoxDbgConsoleEvent::kTerminatedUser:
688 Log(("VBoxDbgConsole: kTerminatedUser (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
689 m_pInput->setEnabled(false);
690 close();
691 break;
693 /* The thread terminated for some unknown reason., disable input */
694 case VBoxDbgConsoleEvent::kTerminatedOther:
695 Log(("VBoxDbgConsole: kTerminatedOther (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
696 m_pInput->setEnabled(false);
697 break;
699 /* paranoia */
700 default:
701 AssertMsgFailed(("command=%d\n", pEvent->command()));
702 break;
704 return true;
707 return VBoxDbgBaseWindow::event(pGenEvent);
711 void
712 VBoxDbgConsole::closeEvent(QCloseEvent *a_pCloseEvt)
714 if (m_fThreadTerminated)
716 a_pCloseEvt->accept();
717 delete this;
722 void
723 VBoxDbgConsole::actFocusToInput()
725 if (!m_pInput->hasFocus())
726 m_pInput->setFocus(Qt::ShortcutFocusReason);
730 void
731 VBoxDbgConsole::actFocusToOutput()
733 if (!m_pOutput->hasFocus())
734 m_pOutput->setFocus(Qt::ShortcutFocusReason);