VirtualBox

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

Last change on this file since 101107 was 101107, checked in by vboxsync, 9 months ago

VBoxDbg: Rewrote the automatic window positioning to make it more flexible and to try keep the console window wide enough for at least 80 columns of text.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use