VirtualBox

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

Last change on this file since 77409 was 77409, checked in by vboxsync, 6 years ago

FE/Qt: Showing machine's name in debug console's title bar.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.4 KB
Line 
1/* $Id: VBoxDbgConsole.cpp 77409 2019-02-21 11:47:48Z vboxsync $ */
2/** @file
3 * VBox Debugger GUI - Console.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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#include "VBoxDbgGui.h"
25
26#include <QLabel>
27#include <QApplication>
28#include <QFont>
29#include <QLineEdit>
30#include <QHBoxLayout>
31#include <QAction>
32#include <QContextMenuEvent>
33#include <QMenu>
34
35#include <VBox/dbg.h>
36#include <VBox/vmm/cfgm.h>
37#include <iprt/errcore.h>
38
39#include <iprt/thread.h>
40#include <iprt/tcp.h>
41#include <VBox/log.h>
42#include <iprt/assert.h>
43#include <iprt/asm.h>
44#include <iprt/alloc.h>
45#include <iprt/string.h>
46
47#include <VBox/com/string.h>
48
49
50
51/*
52 *
53 * V B o x D b g C o n s o l e O u t p u t
54 * V B o x D b g C o n s o l e O u t p u t
55 * V B o x D b g C o n s o l e O u t p u t
56 *
57 *
58 */
59
60/*static*/ const uint32_t VBoxDbgConsoleOutput::s_uMinFontSize = 6;
61
62
63VBoxDbgConsoleOutput::VBoxDbgConsoleOutput(QWidget *pParent/* = NULL*/, IVirtualBox *a_pVirtualBox /* = NULL */,
64 const char *pszName/* = NULL*/)
65 : QTextEdit(pParent), m_uCurLine(0), m_uCurPos(0), m_hGUIThread(RTThreadNativeSelf()), m_pVirtualBox(a_pVirtualBox)
66{
67 setReadOnly(true);
68 setUndoRedoEnabled(false);
69 setOverwriteMode(false);
70 setPlainText("");
71 setTextInteractionFlags(Qt::TextBrowserInteraction);
72 setAutoFormatting(QTextEdit::AutoAll);
73 setTabChangesFocus(true);
74 setAcceptRichText(false);
75
76 /*
77 * Create actions for color-scheme menu items.
78 */
79 m_pGreenOnBlackAction = new QAction(tr("Green On Black"), this);
80 m_pGreenOnBlackAction->setCheckable(true);
81 m_pGreenOnBlackAction->setShortcut(Qt::ControlModifier + Qt::Key_1);
82 m_pGreenOnBlackAction->setData((int)kGreenOnBlack);
83 connect(m_pGreenOnBlackAction, SIGNAL(triggered()), this, SLOT(sltSelectColorScheme()));
84
85 m_pBlackOnWhiteAction = new QAction(tr("Black On White"), this);
86 m_pBlackOnWhiteAction->setCheckable(true);
87 m_pBlackOnWhiteAction->setShortcut(Qt::ControlModifier + Qt::Key_2);
88 m_pBlackOnWhiteAction->setData((int)kBlackOnWhite);
89 connect(m_pBlackOnWhiteAction, SIGNAL(triggered()), this, SLOT(sltSelectColorScheme()));
90
91 /* Create action group for grouping of exclusive color-scheme menu items. */
92 QActionGroup *pActionColorGroup = new QActionGroup(this);
93 pActionColorGroup->addAction(m_pGreenOnBlackAction);
94 pActionColorGroup->addAction(m_pBlackOnWhiteAction);
95 pActionColorGroup->setExclusive(true);
96
97 /*
98 * Create actions for font menu items.
99 */
100 m_pCourierFontAction = new QAction(tr("Courier"), this);
101 m_pCourierFontAction->setCheckable(true);
102 m_pCourierFontAction->setShortcut(Qt::ControlModifier + Qt::Key_D);
103 m_pCourierFontAction->setData((int)kFontType_Courier);
104 connect(m_pCourierFontAction, SIGNAL(triggered()), this, SLOT(sltSelectFontType()));
105
106 m_pMonospaceFontAction = new QAction(tr("Monospace"), this);
107 m_pMonospaceFontAction->setCheckable(true);
108 m_pMonospaceFontAction->setShortcut(Qt::ControlModifier + Qt::Key_M);
109 m_pMonospaceFontAction->setData((int)kFontType_Monospace);
110 connect(m_pMonospaceFontAction, SIGNAL(triggered()), this, SLOT(sltSelectFontType()));
111
112 /* Create action group for grouping of exclusive font menu items. */
113 QActionGroup *pActionFontGroup = new QActionGroup(this);
114 pActionFontGroup->addAction(m_pCourierFontAction);
115 pActionFontGroup->addAction(m_pMonospaceFontAction);
116 pActionFontGroup->setExclusive(true);
117
118 /*
119 * Create actions for font size menu.
120 */
121 uint32_t const uDefaultFontSize = font().pointSize();
122 m_pActionFontSizeGroup = new QActionGroup(this);
123 for (uint32_t i = 0; i < RT_ELEMENTS(m_apFontSizeActions); i++)
124 {
125 char szTitle[32];
126 RTStrPrintf(szTitle, sizeof(szTitle), s_uMinFontSize + i != uDefaultFontSize ? "%upt" : "%upt (default)",
127 s_uMinFontSize + i);
128 m_apFontSizeActions[i] = new QAction(tr(szTitle), this);
129 m_apFontSizeActions[i]->setCheckable(true);
130 m_apFontSizeActions[i]->setData(i + s_uMinFontSize);
131 connect(m_apFontSizeActions[i], SIGNAL(triggered()), this, SLOT(sltSelectFontSize()));
132 m_pActionFontSizeGroup->addAction(m_apFontSizeActions[i]);
133 }
134
135 /*
136 * Set the defaults (which syncs with the menu item checked state).
137 */
138 /* color scheme: */
139 com::Bstr bstrColor;
140 HRESULT hrc = m_pVirtualBox ? m_pVirtualBox->GetExtraData(com::Bstr("DbgConsole/ColorScheme").raw(), bstrColor.asOutParam()) : E_FAIL;
141 if ( SUCCEEDED(hrc)
142 && bstrColor.compareUtf8("blackonwhite", com::Bstr::CaseInsensitive) == 0)
143 setColorScheme(kBlackOnWhite, false /*fSaveIt*/);
144 else
145 setColorScheme(kGreenOnBlack, false /*fSaveIt*/);
146
147 /* font: */
148 com::Bstr bstrFont;
149 hrc = m_pVirtualBox ? m_pVirtualBox->GetExtraData(com::Bstr("DbgConsole/Font").raw(), bstrFont.asOutParam()) : E_FAIL;
150 if ( SUCCEEDED(hrc)
151 && bstrFont.compareUtf8("monospace", com::Bstr::CaseInsensitive) == 0)
152 setFontType(kFontType_Monospace, false /*fSaveIt*/);
153 else
154 setFontType(kFontType_Courier, false /*fSaveIt*/);
155
156 /* font size: */
157 com::Bstr bstrFontSize;
158 hrc = m_pVirtualBox ? m_pVirtualBox->GetExtraData(com::Bstr("DbgConsole/FontSize").raw(), bstrFontSize.asOutParam()) : E_FAIL;
159 if (SUCCEEDED(hrc))
160 {
161 com::Utf8Str strFontSize(bstrFontSize);
162 uint32_t uFontSizePrf = strFontSize.strip().toUInt32();
163 if ( uFontSizePrf - s_uMinFontSize < (uint32_t)RT_ELEMENTS(m_apFontSizeActions)
164 && uFontSizePrf != uDefaultFontSize)
165 setFontSize(uFontSizePrf, false /*fSaveIt*/);
166 }
167
168 NOREF(pszName);
169}
170
171
172VBoxDbgConsoleOutput::~VBoxDbgConsoleOutput()
173{
174 Assert(m_hGUIThread == RTThreadNativeSelf());
175 if (m_pVirtualBox)
176 {
177 m_pVirtualBox->Release();
178 m_pVirtualBox = NULL;
179 }
180}
181
182
183void
184VBoxDbgConsoleOutput::contextMenuEvent(QContextMenuEvent *pEvent)
185{
186 /*
187 * Create the context menu and add the menu items.
188 */
189 QMenu *pMenu = createStandardContextMenu();
190 pMenu->addSeparator();
191
192 QMenu *pColorMenu = pMenu->addMenu(tr("Co&lor Scheme"));
193 pColorMenu->addAction(m_pGreenOnBlackAction);
194 pColorMenu->addAction(m_pBlackOnWhiteAction);
195
196 QMenu *pFontMenu = pMenu->addMenu(tr("&Font Family"));
197 pFontMenu->addAction(m_pCourierFontAction);
198 pFontMenu->addAction(m_pMonospaceFontAction);
199
200 QMenu *pFontSize = pMenu->addMenu(tr("Font &Size"));
201 for (unsigned i = 0; i < RT_ELEMENTS(m_apFontSizeActions); i++)
202 pFontSize->addAction(m_apFontSizeActions[i]);
203
204 pMenu->exec(pEvent->globalPos());
205 delete pMenu;
206}
207
208
209void
210VBoxDbgConsoleOutput::setColorScheme(VBoxDbgConsoleColor enmScheme, bool fSaveIt)
211{
212 const char *pszSetting;
213 QAction *pAction;
214 switch (enmScheme)
215 {
216 case kGreenOnBlack:
217 setStyleSheet("QTextEdit { background-color: black; color: rgb(0, 224, 0) }");
218 pszSetting = "GreenOnBlack";
219 pAction = m_pGreenOnBlackAction;
220 break;
221 case kBlackOnWhite:
222 setStyleSheet("QTextEdit { background-color: white; color: black }");
223 pszSetting = "BlackOnWhite";
224 pAction = m_pBlackOnWhiteAction;
225 break;
226 default:
227 AssertFailedReturnVoid();
228 }
229
230 m_enmColorScheme = kGreenOnBlack;
231
232 /* When going through a slot, the action is typically checked already by Qt. */
233 if (!pAction->isChecked())
234 pAction->setChecked(true);
235
236 /* Make this setting persistent. */
237 if (m_pVirtualBox && fSaveIt)
238 m_pVirtualBox->SetExtraData(com::Bstr("DbgConsole/ColorScheme").raw(), com::Bstr(pszSetting).raw());
239}
240
241
242void
243VBoxDbgConsoleOutput::setFontType(VBoxDbgConsoleFontType enmFontType, bool fSaveIt)
244{
245 QFont Font = font();
246 QAction *pAction;
247 const char *pszSetting;
248 switch (enmFontType)
249 {
250 case kFontType_Courier:
251#ifdef Q_WS_MAC
252 Font = QFont("Monaco", Font.pointSize(), QFont::Normal, FALSE);
253 Font.setStyleStrategy(QFont::NoAntialias);
254#else
255 Font.setStyleHint(QFont::TypeWriter);
256 Font.setFamily("Courier [Monotype]");
257#endif
258 pszSetting = "Courier";
259 pAction = m_pCourierFontAction;
260 break;
261
262 case kFontType_Monospace:
263 Font.setStyleHint(QFont::TypeWriter);
264 Font.setStyleStrategy(QFont::PreferAntialias);
265 Font.setFamily("Monospace [Monotype]");
266 pszSetting = "Monospace";
267 pAction = m_pMonospaceFontAction;
268 break;
269
270 default:
271 AssertFailedReturnVoid();
272 }
273
274 setFont(Font);
275
276 /* When going through a slot, the action is typically checked already by Qt. */
277 if (!pAction->isChecked())
278 pAction->setChecked(true);
279
280 /* Make this setting persistent. */
281 if (m_pVirtualBox && fSaveIt)
282 m_pVirtualBox->SetExtraData(com::Bstr("DbgConsole/Font").raw(), com::Bstr(pszSetting).raw());
283}
284
285
286void
287VBoxDbgConsoleOutput::setFontSize(uint32_t uFontSize, bool fSaveIt)
288{
289 uint32_t idxAction = uFontSize - s_uMinFontSize;
290 if (idxAction < (uint32_t)RT_ELEMENTS(m_apFontSizeActions))
291 {
292 if (!m_apFontSizeActions[idxAction]->isChecked())
293 m_apFontSizeActions[idxAction]->setChecked(true);
294
295 QFont Font = font();
296 Font.setPointSize(uFontSize);
297 setFont(Font);
298
299 /* Make this setting persistent if requested. */
300 if (fSaveIt && m_pVirtualBox)
301 m_pVirtualBox->SetExtraData(com::Bstr("DbgConsole/FontSize").raw(), com::BstrFmt("%u", uFontSize).raw());
302 }
303}
304
305
306void
307VBoxDbgConsoleOutput::sltSelectColorScheme()
308{
309 QAction *pAction = qobject_cast<QAction *>(sender());
310 if (pAction)
311 setColorScheme((VBoxDbgConsoleColor)pAction->data().toInt(), true /*fSaveIt*/);
312}
313
314
315void
316VBoxDbgConsoleOutput::sltSelectFontType()
317{
318 QAction *pAction = qobject_cast<QAction *>(sender());
319 if (pAction)
320 setFontType((VBoxDbgConsoleFontType)pAction->data().toInt(), true /*fSaveIt*/);
321}
322
323
324void
325VBoxDbgConsoleOutput::sltSelectFontSize()
326{
327 QAction *pAction = qobject_cast<QAction *>(sender());
328 if (pAction)
329 setFontSize(pAction->data().toUInt(), true /*fSaveIt*/);
330}
331
332
333void
334VBoxDbgConsoleOutput::appendText(const QString &rStr, bool fClearSelection)
335{
336 Assert(m_hGUIThread == RTThreadNativeSelf());
337
338 if (rStr.isEmpty() || rStr.isNull() || !rStr.length())
339 return;
340
341 /*
342 * Insert all in one go and make sure it's visible.
343 *
344 * We need to move the cursor and unselect any selected text before
345 * inserting anything, otherwise, text will disappear.
346 */
347 QTextCursor Cursor = textCursor();
348 if (!fClearSelection && Cursor.hasSelection())
349 {
350 QTextCursor SavedCursor = Cursor;
351 Cursor.clearSelection();
352 Cursor.movePosition(QTextCursor::End);
353
354 Cursor.insertText(rStr);
355
356 setTextCursor(SavedCursor);
357 }
358 else
359 {
360 if (Cursor.hasSelection())
361 Cursor.clearSelection();
362 if (!Cursor.atEnd())
363 Cursor.movePosition(QTextCursor::End);
364
365 Cursor.insertText(rStr);
366
367 setTextCursor(Cursor);
368 ensureCursorVisible();
369 }
370}
371
372
373
374
375/*
376 *
377 * V B o x D b g C o n s o l e I n p u t
378 * V B o x D b g C o n s o l e I n p u t
379 * V B o x D b g C o n s o l e I n p u t
380 *
381 *
382 */
383
384
385VBoxDbgConsoleInput::VBoxDbgConsoleInput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
386 : QComboBox(pParent), m_hGUIThread(RTThreadNativeSelf())
387{
388 addItem(""); /* invariant: empty command line is the last item */
389
390 setEditable(true);
391 setInsertPolicy(NoInsert);
392 setAutoCompletion(false);
393 setMaxCount(50);
394 const QLineEdit *pEdit = lineEdit();
395 if (pEdit)
396 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
397
398 NOREF(pszName);
399}
400
401
402VBoxDbgConsoleInput::~VBoxDbgConsoleInput()
403{
404 Assert(m_hGUIThread == RTThreadNativeSelf());
405}
406
407
408void
409VBoxDbgConsoleInput::setLineEdit(QLineEdit *pEdit)
410{
411 Assert(m_hGUIThread == RTThreadNativeSelf());
412 QComboBox::setLineEdit(pEdit);
413 if (lineEdit() == pEdit && pEdit)
414 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
415}
416
417
418void
419VBoxDbgConsoleInput::returnPressed()
420{
421 Assert(m_hGUIThread == RTThreadNativeSelf());
422
423 QString strCommand = currentText();
424 /** @todo trim whitespace? */
425 if (strCommand.isEmpty())
426 return;
427
428 /* deal with the current command. */
429 emit commandSubmitted(strCommand);
430
431
432 /*
433 * Add current command to history.
434 */
435 bool fNeedsAppending = true;
436
437 /* invariant: empty line at the end */
438 int iLastItem = count() - 1;
439 Assert(itemText(iLastItem).isEmpty());
440
441 /* have previous command? check duplicate. */
442 if (iLastItem > 0)
443 {
444 const QString strPrevCommand(itemText(iLastItem - 1));
445 if (strCommand == strPrevCommand)
446 fNeedsAppending = false;
447 }
448
449 if (fNeedsAppending)
450 {
451 /* history full? drop the oldest command. */
452 if (count() == maxCount())
453 {
454 removeItem(0);
455 --iLastItem;
456 }
457
458 /* insert before the empty line. */
459 insertItem(iLastItem, strCommand);
460 }
461
462 /* invariant: empty line at the end */
463 int iNewLastItem = count() - 1;
464 Assert(itemText(iNewLastItem).isEmpty());
465
466 /* select empty line to present "new" command line to the user */
467 setCurrentIndex(iNewLastItem);
468}
469
470
471
472
473
474
475/*
476 *
477 * V B o x D b g C o n s o l e
478 * V B o x D b g C o n s o l e
479 * V B o x D b g C o n s o l e
480 *
481 *
482 */
483
484
485VBoxDbgConsole::VBoxDbgConsole(VBoxDbgGui *a_pDbgGui, QWidget *a_pParent/* = NULL*/, IVirtualBox *a_pVirtualBox/* = NULL */)
486 : VBoxDbgBaseWindow(a_pDbgGui, a_pParent), m_pOutput(NULL), m_pInput(NULL), m_fInputRestoreFocus(false),
487 m_pszInputBuf(NULL), m_cbInputBuf(0), m_cbInputBufAlloc(0),
488 m_pszOutputBuf(NULL), m_cbOutputBuf(0), m_cbOutputBufAlloc(0),
489 m_pTimer(NULL), m_fUpdatePending(false), m_Thread(NIL_RTTHREAD), m_EventSem(NIL_RTSEMEVENT),
490 m_fTerminate(false), m_fThreadTerminated(false)
491{
492 QString strMachineName;
493 if (a_pDbgGui)
494 strMachineName = a_pDbgGui->getMachineName();
495
496 if (strMachineName.isEmpty())
497 setWindowTitle("VBoxDbg - Console");
498 else
499 setWindowTitle(QString("%1 - VBoxDbg - Console").arg(strMachineName));
500 /*
501 * Create the output text box.
502 */
503 m_pOutput = new VBoxDbgConsoleOutput(this, a_pVirtualBox);
504
505 /* try figure a suitable size */
506 QLabel *pLabel = new QLabel( "11111111111111111111111111111111111111111111111111111111111111111111111111111112222222222", this);
507 pLabel->setFont(m_pOutput->font());
508 QSize Size = pLabel->sizeHint();
509 delete pLabel;
510 Size.setWidth((int)(Size.width() * 1.10));
511 Size.setHeight(Size.width() / 2);
512 resize(Size);
513
514 /*
515 * Create the input combo box (with a label).
516 */
517 QHBoxLayout *pLayout = new QHBoxLayout();
518 //pLayout->setSizeConstraint(QLayout::SetMaximumSize);
519
520 pLabel = new QLabel(" Command ");
521 pLayout->addWidget(pLabel);
522 pLabel->setMaximumSize(pLabel->sizeHint());
523 pLabel->setAlignment(Qt::AlignCenter);
524
525 m_pInput = new VBoxDbgConsoleInput(NULL);
526 pLayout->addWidget(m_pInput);
527 m_pInput->setDuplicatesEnabled(false);
528 connect(m_pInput, SIGNAL(commandSubmitted(const QString &)), this, SLOT(commandSubmitted(const QString &)));
529
530# if 0//def Q_WS_MAC
531 pLabel = new QLabel(" ");
532 pLayout->addWidget(pLabel);
533 pLabel->setMaximumSize(20, m_pInput->sizeHint().height() + 6);
534 pLabel->setMinimumSize(20, m_pInput->sizeHint().height() + 6);
535# endif
536
537 QWidget *pHBox = new QWidget(this);
538 pHBox->setLayout(pLayout);
539
540 m_pInput->setEnabled(false); /* (we'll get a ready notification) */
541
542
543 /*
544 * Vertical layout box on the whole widget.
545 */
546 QVBoxLayout *pVLayout = new QVBoxLayout();
547 pVLayout->setContentsMargins(0, 0, 0, 0);
548 pVLayout->setSpacing(5);
549 pVLayout->addWidget(m_pOutput);
550 pVLayout->addWidget(pHBox);
551 setLayout(pVLayout);
552
553 /*
554 * The tab order is from input to output, not the other way around as it is by default.
555 */
556 setTabOrder(m_pInput, m_pOutput);
557 m_fInputRestoreFocus = true; /* hack */
558
559 /*
560 * Setup the timer.
561 */
562 m_pTimer = new QTimer(this);
563 connect(m_pTimer, SIGNAL(timeout()), SLOT(updateOutput()));
564
565 /*
566 * Init the backend structure.
567 */
568 m_Back.Core.pfnInput = backInput;
569 m_Back.Core.pfnRead = backRead;
570 m_Back.Core.pfnWrite = backWrite;
571 m_Back.Core.pfnSetReady = backSetReady;
572 m_Back.pSelf = this;
573
574 /*
575 * Create the critical section, the event semaphore and the debug console thread.
576 */
577 int rc = RTCritSectInit(&m_Lock);
578 AssertRC(rc);
579
580 rc = RTSemEventCreate(&m_EventSem);
581 AssertRC(rc);
582
583 rc = RTThreadCreate(&m_Thread, backThread, this, 0, RTTHREADTYPE_DEBUGGER, RTTHREADFLAGS_WAITABLE, "VBoxDbgC");
584 AssertRC(rc);
585 if (RT_FAILURE(rc))
586 m_Thread = NIL_RTTHREAD;
587
588 /*
589 * Shortcuts.
590 */
591 m_pFocusToInput = new QAction("", this);
592 m_pFocusToInput->setShortcut(QKeySequence("Ctrl+L"));
593 addAction(m_pFocusToInput);
594 connect(m_pFocusToInput, SIGNAL(triggered(bool)), this, SLOT(actFocusToInput()));
595
596 m_pFocusToOutput = new QAction("", this);
597 m_pFocusToOutput->setShortcut(QKeySequence("Ctrl+O"));
598 addAction(m_pFocusToOutput);
599 connect(m_pFocusToOutput, SIGNAL(triggered(bool)), this, SLOT(actFocusToOutput()));
600
601 addAction(m_pOutput->m_pBlackOnWhiteAction);
602 addAction(m_pOutput->m_pGreenOnBlackAction);
603 addAction(m_pOutput->m_pCourierFontAction);
604 addAction(m_pOutput->m_pMonospaceFontAction);
605}
606
607
608VBoxDbgConsole::~VBoxDbgConsole()
609{
610 Assert(isGUIThread());
611
612 /*
613 * Wait for the thread.
614 */
615 ASMAtomicWriteBool(&m_fTerminate, true);
616 RTSemEventSignal(m_EventSem);
617 if (m_Thread != NIL_RTTHREAD)
618 {
619 int rc = RTThreadWait(m_Thread, 15000, NULL);
620 AssertRC(rc);
621 m_Thread = NIL_RTTHREAD;
622 }
623
624 /*
625 * Free resources.
626 */
627 delete m_pTimer;
628 m_pTimer = NULL;
629 RTCritSectDelete(&m_Lock);
630 RTSemEventDestroy(m_EventSem);
631 m_EventSem = 0;
632 m_pOutput = NULL;
633 m_pInput = NULL;
634 if (m_pszInputBuf)
635 {
636 RTMemFree(m_pszInputBuf);
637 m_pszInputBuf = NULL;
638 }
639 m_cbInputBuf = 0;
640 m_cbInputBufAlloc = 0;
641
642 delete m_pFocusToInput;
643 m_pFocusToInput = NULL;
644 delete m_pFocusToOutput;
645 m_pFocusToOutput = NULL;
646}
647
648
649void
650VBoxDbgConsole::commandSubmitted(const QString &rCommand)
651{
652 Assert(isGUIThread());
653
654 lock();
655 RTSemEventSignal(m_EventSem);
656
657 QByteArray Utf8Array = rCommand.toUtf8();
658 const char *psz = Utf8Array.constData();
659 size_t cb = strlen(psz);
660
661 /*
662 * Make sure we've got space for the input.
663 */
664 if (cb + m_cbInputBuf >= m_cbInputBufAlloc)
665 {
666 size_t cbNew = RT_ALIGN_Z(cb + m_cbInputBufAlloc + 1, 128);
667 void *pv = RTMemRealloc(m_pszInputBuf, cbNew);
668 if (!pv)
669 {
670 unlock();
671 return;
672 }
673 m_pszInputBuf = (char *)pv;
674 m_cbInputBufAlloc = cbNew;
675 }
676
677 /*
678 * Add the input and output it.
679 */
680 memcpy(m_pszInputBuf + m_cbInputBuf, psz, cb);
681 m_cbInputBuf += cb;
682 m_pszInputBuf[m_cbInputBuf++] = '\n';
683
684 m_pOutput->appendText(rCommand + "\n", true /*fClearSelection*/);
685 m_pOutput->ensureCursorVisible();
686
687 m_fInputRestoreFocus = m_pInput->hasFocus(); /* dirty focus hack */
688 m_pInput->setEnabled(false);
689
690 Log(("VBoxDbgConsole::commandSubmitted: %s (input-enabled=%RTbool)\n", psz, m_pInput->isEnabled()));
691 unlock();
692}
693
694
695void
696VBoxDbgConsole::updateOutput()
697{
698 Assert(isGUIThread());
699
700 lock();
701 m_fUpdatePending = false;
702 if (m_cbOutputBuf)
703 {
704 m_pOutput->appendText(QString::fromUtf8((const char *)m_pszOutputBuf, (int)m_cbOutputBuf), false /*fClearSelection*/);
705 m_cbOutputBuf = 0;
706 }
707 unlock();
708}
709
710
711/**
712 * Lock the object.
713 */
714void
715VBoxDbgConsole::lock()
716{
717 RTCritSectEnter(&m_Lock);
718}
719
720
721/**
722 * Unlocks the object.
723 */
724void
725VBoxDbgConsole::unlock()
726{
727 RTCritSectLeave(&m_Lock);
728}
729
730
731
732/**
733 * Checks if there is input.
734 *
735 * @returns true if there is input ready.
736 * @returns false if there not input ready.
737 * @param pBack Pointer to VBoxDbgConsole::m_Back.
738 * @param cMillies Number of milliseconds to wait on input data.
739 */
740/*static*/ DECLCALLBACK(bool)
741VBoxDbgConsole::backInput(PDBGCBACK pBack, uint32_t cMillies)
742{
743 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
744 pThis->lock();
745
746 bool fRc = true;
747 if (!pThis->m_cbInputBuf)
748 {
749 /*
750 * Wait outside the lock for the requested time, then check again.
751 */
752 pThis->unlock();
753 RTSemEventWait(pThis->m_EventSem, cMillies);
754 pThis->lock();
755 fRc = pThis->m_cbInputBuf
756 || ASMAtomicUoReadBool(&pThis->m_fTerminate);
757 }
758
759 pThis->unlock();
760 return fRc;
761}
762
763
764/**
765 * Read input.
766 *
767 * @returns VBox status code.
768 * @param pBack Pointer to VBoxDbgConsole::m_Back.
769 * @param pvBuf Where to put the bytes we read.
770 * @param cbBuf Maximum nymber of bytes to read.
771 * @param pcbRead Where to store the number of bytes actually read.
772 * If NULL the entire buffer must be filled for a
773 * successful return.
774 */
775/*static*/ DECLCALLBACK(int)
776VBoxDbgConsole::backRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead)
777{
778 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
779 Assert(pcbRead); /** @todo implement this bit */
780 if (pcbRead)
781 *pcbRead = 0;
782
783 pThis->lock();
784 int rc = VINF_SUCCESS;
785 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
786 {
787 if (pThis->m_cbInputBuf)
788 {
789 const char *psz = pThis->m_pszInputBuf;
790 size_t cbRead = RT_MIN(pThis->m_cbInputBuf, cbBuf);
791 memcpy(pvBuf, psz, cbRead);
792 psz += cbRead;
793 pThis->m_cbInputBuf -= cbRead;
794 if (*psz)
795 memmove(pThis->m_pszInputBuf, psz, pThis->m_cbInputBuf);
796 pThis->m_pszInputBuf[pThis->m_cbInputBuf] = '\0';
797 *pcbRead = cbRead;
798 }
799 }
800 else
801 rc = VERR_GENERAL_FAILURE;
802 pThis->unlock();
803 return rc;
804}
805
806
807/**
808 * Write (output).
809 *
810 * @returns VBox status code.
811 * @param pBack Pointer to VBoxDbgConsole::m_Back.
812 * @param pvBuf What to write.
813 * @param cbBuf Number of bytes to write.
814 * @param pcbWritten Where to store the number of bytes actually written.
815 * If NULL the entire buffer must be successfully written.
816 */
817/*static*/ DECLCALLBACK(int)
818VBoxDbgConsole::backWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
819{
820 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
821 int rc = VINF_SUCCESS;
822
823 pThis->lock();
824 if (cbBuf + pThis->m_cbOutputBuf >= pThis->m_cbOutputBufAlloc)
825 {
826 size_t cbNew = RT_ALIGN_Z(cbBuf + pThis->m_cbOutputBufAlloc + 1, 1024);
827 void *pv = RTMemRealloc(pThis->m_pszOutputBuf, cbNew);
828 if (!pv)
829 {
830 pThis->unlock();
831 if (pcbWritten)
832 *pcbWritten = 0;
833 return VERR_NO_MEMORY;
834 }
835 pThis->m_pszOutputBuf = (char *)pv;
836 pThis->m_cbOutputBufAlloc = cbNew;
837 }
838
839 /*
840 * Add the output.
841 */
842 memcpy(pThis->m_pszOutputBuf + pThis->m_cbOutputBuf, pvBuf, cbBuf);
843 pThis->m_cbOutputBuf += cbBuf;
844 pThis->m_pszOutputBuf[pThis->m_cbOutputBuf] = '\0';
845 if (pcbWritten)
846 *pcbWritten = cbBuf;
847
848 if (ASMAtomicUoReadBool(&pThis->m_fTerminate))
849 rc = VERR_GENERAL_FAILURE;
850
851 /*
852 * Tell the GUI thread to draw this text.
853 * We cannot do it from here without frequent crashes.
854 */
855 if (!pThis->m_fUpdatePending)
856 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kUpdate));
857
858 pThis->unlock();
859
860 return rc;
861}
862
863
864/*static*/ DECLCALLBACK(void)
865VBoxDbgConsole::backSetReady(PDBGCBACK pBack, bool fReady)
866{
867 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
868 if (fReady)
869 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kInputEnable));
870}
871
872
873/**
874 * The Debugger Console Thread
875 *
876 * @returns VBox status code (ignored).
877 * @param Thread The thread handle.
878 * @param pvUser Pointer to the VBoxDbgConsole object.s
879 */
880/*static*/ DECLCALLBACK(int)
881VBoxDbgConsole::backThread(RTTHREAD Thread, void *pvUser)
882{
883 VBoxDbgConsole *pThis = (VBoxDbgConsole *)pvUser;
884 LogFlow(("backThread: Thread=%p pvUser=%p\n", (void *)Thread, pvUser));
885
886 NOREF(Thread);
887
888 /*
889 * Create and execute the console.
890 */
891 int rc = pThis->dbgcCreate(&pThis->m_Back.Core, 0);
892
893 ASMAtomicUoWriteBool(&pThis->m_fThreadTerminated, true);
894 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
895 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(rc == VINF_SUCCESS
896 ? VBoxDbgConsoleEvent::kTerminatedUser
897 : VBoxDbgConsoleEvent::kTerminatedOther));
898 LogFlow(("backThread: returns %Rrc (m_fTerminate=%RTbool)\n", rc, ASMAtomicUoReadBool(&pThis->m_fTerminate)));
899 return rc;
900}
901
902
903bool
904VBoxDbgConsole::event(QEvent *pGenEvent)
905{
906 Assert(isGUIThread());
907 if (pGenEvent->type() == (QEvent::Type)VBoxDbgConsoleEvent::kEventNumber)
908 {
909 VBoxDbgConsoleEvent *pEvent = (VBoxDbgConsoleEvent *)pGenEvent;
910
911 switch (pEvent->command())
912 {
913 /* make update pending. */
914 case VBoxDbgConsoleEvent::kUpdate:
915 lock();
916 if (!m_fUpdatePending)
917 {
918 m_fUpdatePending = true;
919 m_pTimer->setSingleShot(true);
920 m_pTimer->start(10);
921 }
922 unlock();
923 break;
924
925 /* Re-enable the input field and restore focus. */
926 case VBoxDbgConsoleEvent::kInputEnable:
927 Log(("VBoxDbgConsole: kInputEnable (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
928 m_pInput->setEnabled(true);
929 if ( m_fInputRestoreFocus
930 && !m_pInput->hasFocus())
931 m_pInput->setFocus(); /* this is a hack. */
932 m_fInputRestoreFocus = false;
933 break;
934
935 /* The thread terminated by user command (exit, quit, bye). */
936 case VBoxDbgConsoleEvent::kTerminatedUser:
937 Log(("VBoxDbgConsole: kTerminatedUser (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
938 m_pInput->setEnabled(false);
939 close();
940 break;
941
942 /* The thread terminated for some unknown reason., disable input */
943 case VBoxDbgConsoleEvent::kTerminatedOther:
944 Log(("VBoxDbgConsole: kTerminatedOther (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
945 m_pInput->setEnabled(false);
946 break;
947
948 /* paranoia */
949 default:
950 AssertMsgFailed(("command=%d\n", pEvent->command()));
951 break;
952 }
953 return true;
954 }
955
956 return VBoxDbgBaseWindow::event(pGenEvent);
957}
958
959
960void
961VBoxDbgConsole::keyReleaseEvent(QKeyEvent *pEvent)
962{
963 //RTAssertMsg2("VBoxDbgConsole::keyReleaseEvent: %d (%#x); mod=%#x\n", pEvent->key(), pEvent->key(), pEvent->modifiers());
964 switch (pEvent->key())
965 {
966 case Qt::Key_F5:
967 if (pEvent->modifiers() == 0)
968 commandSubmitted("g");
969 break;
970
971 case Qt::Key_F8:
972 if (pEvent->modifiers() == 0)
973 commandSubmitted("t");
974 break;
975
976 case Qt::Key_F10:
977 if (pEvent->modifiers() == 0)
978 commandSubmitted("p");
979 break;
980
981 case Qt::Key_F11:
982 if (pEvent->modifiers() == 0)
983 commandSubmitted("t");
984 else if (pEvent->modifiers() == Qt::ShiftModifier)
985 commandSubmitted("gu");
986 break;
987
988 case Qt::Key_Cancel: /* == break */
989 if (pEvent->modifiers() == Qt::ControlModifier)
990 commandSubmitted("stop");
991 break;
992 case Qt::Key_Delete:
993 if (pEvent->modifiers() == Qt::AltModifier)
994 commandSubmitted("stop");
995 break;
996 }
997}
998
999
1000void
1001VBoxDbgConsole::closeEvent(QCloseEvent *a_pCloseEvt)
1002{
1003 if (m_fThreadTerminated)
1004 {
1005 a_pCloseEvt->accept();
1006 delete this;
1007 }
1008}
1009
1010
1011void
1012VBoxDbgConsole::actFocusToInput()
1013{
1014 if (!m_pInput->hasFocus())
1015 m_pInput->setFocus(Qt::ShortcutFocusReason);
1016}
1017
1018
1019void
1020VBoxDbgConsole::actFocusToOutput()
1021{
1022 if (!m_pOutput->hasFocus())
1023 m_pOutput->setFocus(Qt::ShortcutFocusReason);
1024}
1025
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