VirtualBox

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

Last change on this file since 67981 was 65522, checked in by vboxsync, 7 years ago

Debugger: make font + color scheme setting persistent

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

© 2023 Oracle
ContactPrivacy policyTerms of Use