VirtualBox

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

Last change on this file since 76410 was 72197, checked in by vboxsync, 7 years ago

VBoxDbg: Console font size menu. Don't call setExtraData during construction in response to defaults or getExtraData values.

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