VirtualBox

source: vbox/trunk/src/VBox/Debugger/VBoxDbgConsole.cpp@ 37989

Last change on this file since 37989 was 35346, checked in by vboxsync, 14 years ago

VMM reorg: Moving the public include files from include/VBox to include/VBox/vmm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: VBoxDbgConsole.cpp 35346 2010-12-27 16:13:13Z vboxsync $ */
2/** @file
3 * VBox Debugger GUI - Console.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
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.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGG
23#include "VBoxDbgConsole.h"
24
25#include <QLabel>
26#include <QApplication>
27#include <QFont>
28#include <QLineEdit>
29#include <QHBoxLayout>
30#include <QAction>
31#include <QContextMenuEvent>
32
33#include <VBox/dbg.h>
34#include <VBox/vmm/cfgm.h>
35#include <VBox/err.h>
36
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>
44
45
46
47
48/*
49 *
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
53 *
54 *
55 */
56
57
58VBoxDbgConsoleOutput::VBoxDbgConsoleOutput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
59 : QTextEdit(pParent), m_uCurLine(0), m_uCurPos(0), m_hGUIThread(RTThreadNativeSelf())
60{
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);
69
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);
79
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);
86}
87
88
89VBoxDbgConsoleOutput::~VBoxDbgConsoleOutput()
90{
91 Assert(m_hGUIThread == RTThreadNativeSelf());
92}
93
94
95void
96VBoxDbgConsoleOutput::appendText(const QString &rStr)
97{
98 Assert(m_hGUIThread == RTThreadNativeSelf());
99
100 if (rStr.isEmpty() || rStr.isNull() || !rStr.length())
101 return;
102
103 /*
104 * Insert all in one go and make sure it's visible.
105 */
106 QTextCursor Cursor = textCursor();
107 if (!Cursor.atEnd())
108 moveCursor(QTextCursor::End); /* make sure we append the text */
109 Cursor.insertText(rStr);
110 ensureCursorVisible();
111}
112
113
114
115
116/*
117 *
118 * V B o x D b g C o n s o l e I n p u t
119 * V B o x D b g C o n s o l e I n p u t
120 * V B o x D b g C o n s o l e I n p u t
121 *
122 *
123 */
124
125
126VBoxDbgConsoleInput::VBoxDbgConsoleInput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
127 : QComboBox(pParent), m_iBlankItem(0), m_hGUIThread(RTThreadNativeSelf())
128{
129 insertItem(m_iBlankItem, "");
130 setEditable(true);
131 setInsertPolicy(NoInsert);
132 setAutoCompletion(false);
133 setMaxCount(50);
134 const QLineEdit *pEdit = lineEdit();
135 if (pEdit)
136 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
137
138 NOREF(pszName);
139}
140
141
142VBoxDbgConsoleInput::~VBoxDbgConsoleInput()
143{
144 Assert(m_hGUIThread == RTThreadNativeSelf());
145}
146
147
148void
149VBoxDbgConsoleInput::setLineEdit(QLineEdit *pEdit)
150{
151 Assert(m_hGUIThread == RTThreadNativeSelf());
152 QComboBox::setLineEdit(pEdit);
153 if (lineEdit() == pEdit && pEdit)
154 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
155}
156
157
158void
159VBoxDbgConsoleInput::returnPressed()
160{
161 Assert(m_hGUIThread == RTThreadNativeSelf());
162 /* deal with the current command. */
163 QString Str = currentText();
164 emit commandSubmitted(Str);
165
166 /* update the history and clear the entry field */
167 QString PrevStr = m_iBlankItem > 0 ? itemText(m_iBlankItem - 1) : "";
168 if (PrevStr != Str)
169 {
170 setItemText(m_iBlankItem, Str);
171 if ( m_iBlankItem > 0
172 && m_iBlankItem >= maxCount() - 1)
173 removeItem(m_iBlankItem - maxCount() - 1);
174 insertItem(++m_iBlankItem, "");
175 }
176
177 clearEditText();
178 setCurrentIndex(m_iBlankItem);
179}
180
181
182
183
184
185
186/*
187 *
188 * V B o x D b g C o n s o l e
189 * V B o x D b g C o n s o l e
190 * V B o x D b g C o n s o l e
191 *
192 *
193 */
194
195
196VBoxDbgConsole::VBoxDbgConsole(VBoxDbgGui *a_pDbgGui, QWidget *a_pParent/* = NULL*/)
197 : VBoxDbgBaseWindow(a_pDbgGui, a_pParent), m_pOutput(NULL), m_pInput(NULL), m_fInputRestoreFocus(false),
198 m_pszInputBuf(NULL), m_cbInputBuf(0), m_cbInputBufAlloc(0),
199 m_pszOutputBuf(NULL), m_cbOutputBuf(0), m_cbOutputBufAlloc(0),
200 m_pTimer(NULL), m_fUpdatePending(false), m_Thread(NIL_RTTHREAD), m_EventSem(NIL_RTSEMEVENT),
201 m_fTerminate(false), m_fThreadTerminated(false)
202{
203 setWindowTitle("VBoxDbg - Console");
204
205 /*
206 * Create the output text box.
207 */
208 m_pOutput = new VBoxDbgConsoleOutput(this);
209
210 /* try figure a suitable size */
211 QLabel *pLabel = new QLabel( "11111111111111111111111111111111111111111111111111111111111111111111111111111112222222222", this);
212 pLabel->setFont(m_pOutput->font());
213 QSize Size = pLabel->sizeHint();
214 delete pLabel;
215 Size.setWidth((int)(Size.width() * 1.10));
216 Size.setHeight(Size.width() / 2);
217 resize(Size);
218
219 /*
220 * Create the input combo box (with a label).
221 */
222 QHBoxLayout *pLayout = new QHBoxLayout();
223 //pLayout->setSizeConstraint(QLayout::SetMaximumSize);
224
225 pLabel = new QLabel(" Command ");
226 pLayout->addWidget(pLabel);
227 pLabel->setMaximumSize(pLabel->sizeHint());
228 pLabel->setAlignment(Qt::AlignCenter);
229
230 m_pInput = new VBoxDbgConsoleInput(NULL);
231 pLayout->addWidget(m_pInput);
232 m_pInput->setDuplicatesEnabled(false);
233 connect(m_pInput, SIGNAL(commandSubmitted(const QString &)), this, SLOT(commandSubmitted(const QString &)));
234
235# if 0//def Q_WS_MAC
236 pLabel = new QLabel(" ");
237 pLayout->addWidget(pLabel);
238 pLabel->setMaximumSize(20, m_pInput->sizeHint().height() + 6);
239 pLabel->setMinimumSize(20, m_pInput->sizeHint().height() + 6);
240# endif
241
242 QWidget *pHBox = new QWidget(this);
243 pHBox->setLayout(pLayout);
244
245 m_pInput->setEnabled(false); /* (we'll get a ready notification) */
246
247
248 /*
249 * Vertical layout box on the whole widget.
250 */
251 QVBoxLayout *pVLayout = new QVBoxLayout();
252 pVLayout->setContentsMargins(0, 0, 0, 0);
253 pVLayout->setSpacing(5);
254 pVLayout->addWidget(m_pOutput);
255 pVLayout->addWidget(pHBox);
256 setLayout(pVLayout);
257
258 /*
259 * The tab order is from input to output, not the other way around as it is by default.
260 */
261 setTabOrder(m_pInput, m_pOutput);
262
263 /*
264 * Setup the timer.
265 */
266 m_pTimer = new QTimer(this);
267 connect(m_pTimer, SIGNAL(timeout()), SLOT(updateOutput()));
268
269 /*
270 * Init the backend structure.
271 */
272 m_Back.Core.pfnInput = backInput;
273 m_Back.Core.pfnRead = backRead;
274 m_Back.Core.pfnWrite = backWrite;
275 m_Back.Core.pfnSetReady = backSetReady;
276 m_Back.pSelf = this;
277
278 /*
279 * Create the critical section, the event semaphore and the debug console thread.
280 */
281 int rc = RTCritSectInit(&m_Lock);
282 AssertRC(rc);
283
284 rc = RTSemEventCreate(&m_EventSem);
285 AssertRC(rc);
286
287 rc = RTThreadCreate(&m_Thread, backThread, this, 0, RTTHREADTYPE_DEBUGGER, RTTHREADFLAGS_WAITABLE, "VBoxDbgC");
288 AssertRC(rc);
289 if (RT_FAILURE(rc))
290 m_Thread = NIL_RTTHREAD;
291
292 /*
293 * Shortcuts.
294 */
295 m_pFocusToInput = new QAction("", this);
296 m_pFocusToInput->setShortcut(QKeySequence("Ctrl+L"));
297 addAction(m_pFocusToInput);
298 connect(m_pFocusToInput, SIGNAL(triggered(bool)), this, SLOT(actFocusToInput()));
299
300 m_pFocusToOutput = new QAction("", this);
301 m_pFocusToOutput->setShortcut(QKeySequence("Ctrl+O"));
302 addAction(m_pFocusToOutput);
303 connect(m_pFocusToOutput, SIGNAL(triggered(bool)), this, SLOT(actFocusToOutput()));
304}
305
306
307VBoxDbgConsole::~VBoxDbgConsole()
308{
309 Assert(isGUIThread());
310
311 /*
312 * Wait for the thread.
313 */
314 ASMAtomicWriteBool(&m_fTerminate, true);
315 RTSemEventSignal(m_EventSem);
316 if (m_Thread != NIL_RTTHREAD)
317 {
318 int rc = RTThreadWait(m_Thread, 15000, NULL);
319 AssertRC(rc);
320 m_Thread = NIL_RTTHREAD;
321 }
322
323 /*
324 * Free resources.
325 */
326 delete m_pTimer;
327 m_pTimer = NULL;
328 RTCritSectDelete(&m_Lock);
329 RTSemEventDestroy(m_EventSem);
330 m_EventSem = 0;
331 m_pOutput = NULL;
332 m_pInput = NULL;
333 if (m_pszInputBuf)
334 {
335 RTMemFree(m_pszInputBuf);
336 m_pszInputBuf = NULL;
337 }
338 m_cbInputBuf = 0;
339 m_cbInputBufAlloc = 0;
340
341 delete m_pFocusToInput;
342 m_pFocusToInput = NULL;
343 delete m_pFocusToOutput;
344 m_pFocusToOutput = NULL;
345}
346
347
348void
349VBoxDbgConsole::commandSubmitted(const QString &rCommand)
350{
351 Assert(isGUIThread());
352
353 lock();
354 RTSemEventSignal(m_EventSem);
355
356 QByteArray Utf8Array = rCommand.toUtf8();
357 const char *psz = Utf8Array.constData();
358 size_t cb = strlen(psz);
359
360 /*
361 * Make sure we've got space for the input.
362 */
363 if (cb + m_cbInputBuf >= m_cbInputBufAlloc)
364 {
365 size_t cbNew = RT_ALIGN_Z(cb + m_cbInputBufAlloc + 1, 128);
366 void *pv = RTMemRealloc(m_pszInputBuf, cbNew);
367 if (!pv)
368 {
369 unlock();
370 return;
371 }
372 m_pszInputBuf = (char *)pv;
373 m_cbInputBufAlloc = cbNew;
374 }
375
376 /*
377 * Add the input and output it.
378 */
379 memcpy(m_pszInputBuf + m_cbInputBuf, psz, cb);
380 m_cbInputBuf += cb;
381 m_pszInputBuf[m_cbInputBuf++] = '\n';
382
383 m_pOutput->appendText(rCommand + "\n");
384 m_pOutput->ensureCursorVisible();
385
386 m_fInputRestoreFocus = m_pInput->hasFocus(); /* dirty focus hack */
387 m_pInput->setEnabled(false);
388
389 Log(("VBoxDbgConsole::commandSubmitted: %s (input-enabled=%RTbool)\n", psz, m_pInput->isEnabled()));
390 unlock();
391}
392
393
394void
395VBoxDbgConsole::updateOutput()
396{
397 Assert(isGUIThread());
398
399 lock();
400 m_fUpdatePending = false;
401 if (m_cbOutputBuf)
402 {
403 m_pOutput->appendText(QString::fromUtf8((const char *)m_pszOutputBuf, (int)m_cbOutputBuf));
404 m_cbOutputBuf = 0;
405 }
406 unlock();
407}
408
409
410/**
411 * Lock the object.
412 */
413void
414VBoxDbgConsole::lock()
415{
416 RTCritSectEnter(&m_Lock);
417}
418
419
420/**
421 * Unlocks the object.
422 */
423void
424VBoxDbgConsole::unlock()
425{
426 RTCritSectLeave(&m_Lock);
427}
428
429
430
431/**
432 * Checks if there is input.
433 *
434 * @returns true if there is input ready.
435 * @returns false if there not input ready.
436 * @param pBack Pointer to VBoxDbgConsole::m_Back.
437 * @param cMillies Number of milliseconds to wait on input data.
438 */
439/*static*/ DECLCALLBACK(bool)
440VBoxDbgConsole::backInput(PDBGCBACK pBack, uint32_t cMillies)
441{
442 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
443 pThis->lock();
444
445 bool fRc = true;
446 if (!pThis->m_cbInputBuf)
447 {
448 /*
449 * Wait outside the lock for the requested time, then check again.
450 */
451 pThis->unlock();
452 RTSemEventWait(pThis->m_EventSem, cMillies);
453 pThis->lock();
454 fRc = pThis->m_cbInputBuf
455 || ASMAtomicUoReadBool(&pThis->m_fTerminate);
456 }
457
458 pThis->unlock();
459 return fRc;
460}
461
462
463/**
464 * Read input.
465 *
466 * @returns VBox status code.
467 * @param pBack Pointer to VBoxDbgConsole::m_Back.
468 * @param pvBuf Where to put the bytes we read.
469 * @param cbBuf Maximum nymber of bytes to read.
470 * @param pcbRead Where to store the number of bytes actually read.
471 * If NULL the entire buffer must be filled for a
472 * successful return.
473 */
474/*static*/ DECLCALLBACK(int)
475VBoxDbgConsole::backRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead)
476{
477 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
478 Assert(pcbRead); /** @todo implement this bit */
479 if (pcbRead)
480 *pcbRead = 0;
481
482 pThis->lock();
483 int rc = VINF_SUCCESS;
484 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
485 {
486 if (pThis->m_cbInputBuf)
487 {
488 const char *psz = pThis->m_pszInputBuf;
489 size_t cbRead = RT_MIN(pThis->m_cbInputBuf, cbBuf);
490 memcpy(pvBuf, psz, cbRead);
491 psz += cbRead;
492 pThis->m_cbInputBuf -= cbRead;
493 if (*psz)
494 memmove(pThis->m_pszInputBuf, psz, pThis->m_cbInputBuf);
495 pThis->m_pszInputBuf[pThis->m_cbInputBuf] = '\0';
496 *pcbRead = cbRead;
497 }
498 }
499 else
500 rc = VERR_GENERAL_FAILURE;
501 pThis->unlock();
502 return rc;
503}
504
505
506/**
507 * Write (output).
508 *
509 * @returns VBox status code.
510 * @param pBack Pointer to VBoxDbgConsole::m_Back.
511 * @param pvBuf What to write.
512 * @param cbBuf Number of bytes to write.
513 * @param pcbWritten Where to store the number of bytes actually written.
514 * If NULL the entire buffer must be successfully written.
515 */
516/*static*/ DECLCALLBACK(int)
517VBoxDbgConsole::backWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
518{
519 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
520 int rc = VINF_SUCCESS;
521
522 pThis->lock();
523 if (cbBuf + pThis->m_cbOutputBuf >= pThis->m_cbOutputBufAlloc)
524 {
525 size_t cbNew = RT_ALIGN_Z(cbBuf + pThis->m_cbOutputBufAlloc + 1, 1024);
526 void *pv = RTMemRealloc(pThis->m_pszOutputBuf, cbNew);
527 if (!pv)
528 {
529 pThis->unlock();
530 if (pcbWritten)
531 *pcbWritten = 0;
532 return VERR_NO_MEMORY;
533 }
534 pThis->m_pszOutputBuf = (char *)pv;
535 pThis->m_cbOutputBufAlloc = cbNew;
536 }
537
538 /*
539 * Add the output.
540 */
541 memcpy(pThis->m_pszOutputBuf + pThis->m_cbOutputBuf, pvBuf, cbBuf);
542 pThis->m_cbOutputBuf += cbBuf;
543 pThis->m_pszOutputBuf[pThis->m_cbOutputBuf] = '\0';
544 if (pcbWritten)
545 *pcbWritten = cbBuf;
546
547 if (ASMAtomicUoReadBool(&pThis->m_fTerminate))
548 rc = VERR_GENERAL_FAILURE;
549
550 /*
551 * Tell the GUI thread to draw this text.
552 * We cannot do it from here without frequent crashes.
553 */
554 if (!pThis->m_fUpdatePending)
555 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kUpdate));
556
557 pThis->unlock();
558
559 return rc;
560}
561
562
563/*static*/ DECLCALLBACK(void)
564VBoxDbgConsole::backSetReady(PDBGCBACK pBack, bool fReady)
565{
566 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
567 if (fReady)
568 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kInputEnable));
569}
570
571
572/**
573 * The Debugger Console Thread
574 *
575 * @returns VBox status code (ignored).
576 * @param Thread The thread handle.
577 * @param pvUser Pointer to the VBoxDbgConsole object.s
578 */
579/*static*/ DECLCALLBACK(int)
580VBoxDbgConsole::backThread(RTTHREAD Thread, void *pvUser)
581{
582 VBoxDbgConsole *pThis = (VBoxDbgConsole *)pvUser;
583 LogFlow(("backThread: Thread=%p pvUser=%p\n", (void *)Thread, pvUser));
584
585 NOREF(Thread);
586
587 /*
588 * Create and execute the console.
589 */
590 int rc = pThis->dbgcCreate(&pThis->m_Back.Core, 0);
591
592 ASMAtomicUoWriteBool(&pThis->m_fThreadTerminated, true);
593 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
594 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(rc == VINF_SUCCESS
595 ? VBoxDbgConsoleEvent::kTerminatedUser
596 : VBoxDbgConsoleEvent::kTerminatedOther));
597 LogFlow(("backThread: returns %Rrc (m_fTerminate=%RTbool)\n", rc, ASMAtomicUoReadBool(&pThis->m_fTerminate)));
598 return rc;
599}
600
601
602bool
603VBoxDbgConsole::event(QEvent *pGenEvent)
604{
605 Assert(isGUIThread());
606 if (pGenEvent->type() == (QEvent::Type)VBoxDbgConsoleEvent::kEventNumber)
607 {
608 VBoxDbgConsoleEvent *pEvent = (VBoxDbgConsoleEvent *)pGenEvent;
609
610 switch (pEvent->command())
611 {
612 /* make update pending. */
613 case VBoxDbgConsoleEvent::kUpdate:
614 lock();
615 if (!m_fUpdatePending)
616 {
617 m_fUpdatePending = true;
618 m_pTimer->setSingleShot(true);
619 m_pTimer->start(10);
620 }
621 unlock();
622 break;
623
624 /* Re-enable the input field and restore focus. */
625 case VBoxDbgConsoleEvent::kInputEnable:
626 Log(("VBoxDbgConsole: kInputEnable (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
627 m_pInput->setEnabled(true);
628 if ( m_fInputRestoreFocus
629 && !m_pInput->hasFocus())
630 m_pInput->setFocus(); /* this is a hack. */
631 m_fInputRestoreFocus = false;
632 break;
633
634 /* The thread terminated by user command (exit, quit, bye). */
635 case VBoxDbgConsoleEvent::kTerminatedUser:
636 Log(("VBoxDbgConsole: kTerminatedUser (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
637 m_pInput->setEnabled(false);
638 close();
639 break;
640
641 /* The thread terminated for some unknown reason., disable input */
642 case VBoxDbgConsoleEvent::kTerminatedOther:
643 Log(("VBoxDbgConsole: kTerminatedOther (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
644 m_pInput->setEnabled(false);
645 break;
646
647 /* paranoia */
648 default:
649 AssertMsgFailed(("command=%d\n", pEvent->command()));
650 break;
651 }
652 return true;
653 }
654
655 return VBoxDbgBaseWindow::event(pGenEvent);
656}
657
658
659void
660VBoxDbgConsole::closeEvent(QCloseEvent *a_pCloseEvt)
661{
662 if (m_fThreadTerminated)
663 {
664 a_pCloseEvt->accept();
665 delete this;
666 }
667}
668
669
670void
671VBoxDbgConsole::actFocusToInput()
672{
673 if (!m_pInput->hasFocus())
674 m_pInput->setFocus(Qt::ShortcutFocusReason);
675}
676
677
678void
679VBoxDbgConsole::actFocusToOutput()
680{
681 if (!m_pOutput->hasFocus())
682 m_pOutput->setFocus(Qt::ShortcutFocusReason);
683}
684
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette