VirtualBox

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

Last change on this file was 103491, checked in by vboxsync, 3 months ago

VBoxDbg: Fixed console output performance troubles caused by 'Courier [Monospace]' on Windows, changing the default font to the system's default fixed font and dropping the '[Monospace]' from the family. Also an disabled experiment switching to QPlainTextEdit. bugref:10604

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

© 2023 Oracle
ContactPrivacy policyTerms of Use