VirtualBox

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

Last change on this file since 99844 was 99835, checked in by vboxsync, 19 months ago

Debugger/Console: bugref:10450: Move to modern way of assigning shortcuts to be able to comply with Qt6 easier.

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