VirtualBox

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

Last change on this file since 9728 was 9268, checked in by vboxsync, 16 years ago

reverted stylehint/family order.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/* $Id: VBoxDbgConsole.cpp 9268 2008-05-31 14:50:56Z vboxsync $ */
2/** @file
3 * VBox Debugger GUI - Console.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#include "VBoxDbgConsole.h"
27
28#include <qlabel.h>
29#include <qapplication.h>
30#include <qfont.h>
31#include <qtextview.h>
32#include <qlineedit.h>
33
34#include <VBox/dbg.h>
35#include <VBox/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
47
48
49/*
50 *
51 * V B o x D b g C o n s o l e O u t p u t
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 *
55 *
56 */
57
58
59VBoxDbgConsoleOutput::VBoxDbgConsoleOutput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
60 : QTextEdit(pParent, pszName), m_uCurLine(0), m_uCurPos(0), m_hGUIThread(RTThreadNativeSelf())
61{
62 setReadOnly(true);
63 setUndoRedoEnabled(false);
64 setOverwriteMode(true);
65 setTextFormat(PlainText); /* minimal HTML: setTextFormat(LogText); */
66
67#ifdef Q_WS_MAC
68 QFont Font("Monaco", 10, QFont::Normal, FALSE);
69 Font.setStyleStrategy(QFont::NoAntialias);
70#else
71 QFont Font = font();
72 Font.setStyleHint(QFont::TypeWriter);
73 Font.setFamily("Courier [Monotype]");
74#endif
75 setFont(Font);
76
77 /* green on black */
78 setPaper(QBrush(Qt::black));
79 setColor(QColor(qRgb(0, 0xe0, 0)));
80}
81
82VBoxDbgConsoleOutput::~VBoxDbgConsoleOutput()
83{
84 Assert(m_hGUIThread == RTThreadNativeSelf());
85}
86
87void VBoxDbgConsoleOutput::appendText(const QString &rStr)
88{
89 Assert(m_hGUIThread == RTThreadNativeSelf());
90
91 if (rStr.isEmpty() || rStr.isNull() || !rStr.length())
92 return;
93
94 /*
95 * Insert line by line.
96 */
97 unsigned cch = rStr.length();
98 unsigned iPos = 0;
99 while (iPos < cch)
100 {
101 int iPosNL = rStr.find('\n', iPos);
102 int iPosEnd = iPosNL >= 0 ? iPosNL : cch;
103 if ((unsigned)iPosNL != iPos)
104 {
105 QString Str = rStr.mid(iPos, iPosEnd - iPos);
106 if (m_uCurPos == 0)
107 append(Str);
108 else
109 insertAt(Str, m_uCurLine, m_uCurPos);
110 if (iPosNL >= 0)
111 {
112 m_uCurLine++;
113 m_uCurPos = 0;
114 }
115 else
116 m_uCurPos += Str.length();
117 }
118 else
119 {
120 m_uCurLine++;
121 m_uCurPos = 0;
122 }
123 /* next */
124 iPos = iPosEnd + 1;
125 }
126}
127
128
129
130
131/*
132 *
133 * V B o x D b g C o n s o l e I n p u t
134 * V B o x D b g C o n s o l e I n p u t
135 * V B o x D b g C o n s o l e I n p u t
136 *
137 *
138 */
139
140
141VBoxDbgConsoleInput::VBoxDbgConsoleInput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
142 : QComboBox(true, pParent, pszName), m_iBlankItem(0), m_hGUIThread(RTThreadNativeSelf())
143{
144 insertItem("", m_iBlankItem);
145 setInsertionPolicy(NoInsertion);
146 setMaxCount(50);
147 const QLineEdit *pEdit = lineEdit();
148 if (pEdit)
149 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
150}
151
152VBoxDbgConsoleInput::~VBoxDbgConsoleInput()
153{
154 Assert(m_hGUIThread == RTThreadNativeSelf());
155}
156
157void VBoxDbgConsoleInput::setLineEdit(QLineEdit *pEdit)
158{
159 Assert(m_hGUIThread == RTThreadNativeSelf());
160 QComboBox::setLineEdit(pEdit);
161 if (lineEdit() == pEdit && pEdit)
162 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
163}
164
165void VBoxDbgConsoleInput::returnPressed()
166{
167 Assert(m_hGUIThread == RTThreadNativeSelf());
168 /* deal with the current command. */
169 QString Str = currentText();
170 emit commandSubmitted(Str);
171
172 /* update the history and clear the entry field */
173 if (text(m_iBlankItem - 1) != Str)
174 {
175 changeItem(Str, m_iBlankItem);
176 removeItem(m_iBlankItem - maxCount() - 1);
177 insertItem("", ++m_iBlankItem);
178 }
179
180 clearEdit();
181 setCurrentItem(m_iBlankItem);
182}
183
184
185
186
187
188
189/*
190 *
191 * V B o x D b g C o n s o l e
192 * V B o x D b g C o n s o l e
193 * V B o x D b g C o n s o l e
194 *
195 *
196 */
197
198
199VBoxDbgConsole::VBoxDbgConsole(PVM pVM, QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
200 : VBoxDbgBase(pVM), m_pOutput(NULL), m_pInput(NULL),
201 m_pszInputBuf(NULL), m_cbInputBuf(0), m_cbInputBufAlloc(0),
202 m_pszOutputBuf(NULL), m_cbOutputBuf(0), m_cbOutputBufAlloc(0),
203 m_pTimer(NULL), m_fUpdatePending(false), m_Thread(NIL_RTTHREAD), m_EventSem(NIL_RTSEMEVENT), m_fTerminate(false)
204{
205 setCaption("VBoxDbg - Console");
206
207 NOREF(pszName);
208 NOREF(pParent);
209
210 /*
211 * Create the output text box.
212 */
213 m_pOutput = new VBoxDbgConsoleOutput(this);
214
215 /* try figure a suitable size */
216 QLabel *pLabel = new QLabel(NULL, "11111111111111111111111111111111111111111111111111111111111111111111111111111112222222222", this);
217 pLabel->setFont(m_pOutput->font());
218 QSize Size = pLabel->sizeHint();
219 delete pLabel;
220 Size.setWidth((int)(Size.width() * 1.10));
221 Size.setHeight(Size.width() / 2);
222 resize(Size);
223
224 /*
225 * Create the input combo box (with a label).
226 */
227 QHBox *pHBox = new QHBox(this);
228
229 pLabel = new QLabel(NULL, " Command ", pHBox);
230 pLabel->setMaximumSize(pLabel->sizeHint());
231 pLabel->setAlignment(AlignHCenter | AlignVCenter);
232
233 m_pInput = new VBoxDbgConsoleInput(pHBox);
234 m_pInput->setDuplicatesEnabled(false);
235 connect(m_pInput, SIGNAL(commandSubmitted(const QString &)), this, SLOT(commandSubmitted(const QString &)));
236
237#ifdef Q_WS_MAC
238 pLabel = new QLabel(NULL, " ", pHBox);
239 pLabel->setMaximumSize(20, m_pInput->sizeHint().height() + 6);
240 pLabel->setMinimumSize(20, m_pInput->sizeHint().height() + 6);
241#endif
242
243 /*
244 * The tab order is from input to output, not the otherway around as it is by default.
245 */
246 setTabOrder(m_pInput, m_pOutput);
247
248 /*
249 * Setup the timer.
250 */
251 m_pTimer = new QTimer(this);
252 connect(m_pTimer, SIGNAL(timeout()), SLOT(updateOutput()));
253
254 /*
255 * Init the backend structure.
256 */
257 m_Back.Core.pfnInput = backInput;
258 m_Back.Core.pfnRead = backRead;
259 m_Back.Core.pfnWrite = backWrite;
260 m_Back.pSelf = this;
261
262 /*
263 * Create the critical section, the event semaphore and the debug console thread.
264 */
265 int rc = RTCritSectInit(&m_Lock);
266 AssertRC(rc);
267
268 rc = RTSemEventCreate(&m_EventSem);
269 AssertRC(rc);
270
271 rc = RTThreadCreate(&m_Thread, backThread, this, 0, RTTHREADTYPE_DEBUGGER, RTTHREADFLAGS_WAITABLE, "VBoxDbgC");
272 AssertRC(rc);
273 if (VBOX_FAILURE(rc))
274 m_Thread = NIL_RTTHREAD;
275}
276
277VBoxDbgConsole::~VBoxDbgConsole()
278{
279 Assert(isGUIThread());
280
281 /*
282 * Wait for the thread.
283 */
284 ASMAtomicXchgSize(&m_fTerminate, true);
285 RTSemEventSignal(m_EventSem);
286 if (m_Thread != NIL_RTTHREAD)
287 {
288 int rc = RTThreadWait(m_Thread, 15000, NULL);
289 AssertRC(rc);
290 m_Thread = NIL_RTTHREAD;
291 }
292
293 /*
294 * Free resources.
295 */
296 delete m_pTimer;
297 m_pTimer = NULL;
298 RTCritSectDelete(&m_Lock);
299 RTSemEventDestroy(m_EventSem);
300 m_EventSem = 0;
301 m_pOutput = NULL;
302 m_pInput = NULL;
303 if (m_pszInputBuf)
304 {
305 RTMemFree(m_pszInputBuf);
306 m_pszInputBuf = NULL;
307 }
308 m_cbInputBuf = 0;
309 m_cbInputBufAlloc = 0;
310}
311
312void VBoxDbgConsole::commandSubmitted(const QString &rCommand)
313{
314 Assert(isGUIThread());
315
316 lock();
317 RTSemEventSignal(m_EventSem);
318
319 const char *psz = rCommand;//.utf8();
320 size_t cb = strlen(psz);
321
322 /*
323 * Make sure we've got space for the input.
324 */
325 if (cb + m_cbInputBuf >= m_cbInputBufAlloc)
326 {
327 size_t cbNew = RT_ALIGN_Z(cb + m_cbInputBufAlloc + 1, 128);
328 void *pv = RTMemRealloc(m_pszInputBuf, cbNew);
329 if (!pv)
330 {
331 unlock();
332 return;
333 }
334 m_pszInputBuf = (char *)pv;
335 m_cbInputBufAlloc = cbNew;
336 }
337
338 /*
339 * Add the input and output it.
340 */
341 memcpy(m_pszInputBuf + m_cbInputBuf, psz, cb);
342 m_cbInputBuf += cb;
343 m_pszInputBuf[m_cbInputBuf++] = '\n';
344
345 m_pOutput->appendText(rCommand + "\n");
346 m_pOutput->scrollToBottom();
347
348 m_fInputRestoreFocus = m_pInput->hasFocus(); /* dirty focus hack */
349 m_pInput->setEnabled(false);
350
351 unlock();
352}
353
354void VBoxDbgConsole::updateOutput()
355{
356 Assert(isGUIThread());
357
358 lock();
359 m_fUpdatePending = false;
360 if (m_cbOutputBuf)
361 {
362 m_pOutput->appendText(QString::fromUtf8((const char *)m_pszOutputBuf, m_cbOutputBuf));
363 m_cbOutputBuf = 0;
364 }
365 unlock();
366}
367
368
369/**
370 * Lock the object.
371 */
372void VBoxDbgConsole::lock()
373{
374 RTCritSectEnter(&m_Lock);
375}
376
377/**
378 * Unlocks the object.
379 */
380void VBoxDbgConsole::unlock()
381{
382 RTCritSectLeave(&m_Lock);
383}
384
385
386
387/**
388 * Checks if there is input.
389 *
390 * @returns true if there is input ready.
391 * @returns false if there not input ready.
392 * @param pBack Pointer to VBoxDbgConsole::m_Back.
393 * @param cMillies Number of milliseconds to wait on input data.
394 */
395/*static*/ DECLCALLBACK(bool) VBoxDbgConsole::backInput(PDBGCBACK pBack, uint32_t cMillies)
396{
397 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
398 pThis->lock();
399
400 /* questing for input means it's done processing. */
401 pThis->m_pInput->setEnabled(true);
402 /* dirty focus hack: */
403 if (pThis->m_fInputRestoreFocus)
404 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kInputRestoreFocus));
405
406 bool fRc = true;
407 if (!pThis->m_cbInputBuf)
408 {
409 pThis->unlock();
410 RTSemEventWait(pThis->m_EventSem, cMillies);
411 pThis->lock();
412 fRc = pThis->m_cbInputBuf || pThis->m_fTerminate;
413 }
414
415 pThis->unlock();
416 return fRc;
417}
418
419/**
420 * Read input.
421 *
422 * @returns VBox status code.
423 * @param pBack Pointer to VBoxDbgConsole::m_Back.
424 * @param pvBuf Where to put the bytes we read.
425 * @param cbBuf Maximum nymber of bytes to read.
426 * @param pcbRead Where to store the number of bytes actually read.
427 * If NULL the entire buffer must be filled for a
428 * successful return.
429 */
430/*static*/ DECLCALLBACK(int) VBoxDbgConsole::backRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead)
431{
432 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
433 Assert(pcbRead); /** @todo implement this bit */
434 if (pcbRead)
435 *pcbRead = 0;
436
437 pThis->lock();
438 int rc = VINF_SUCCESS;
439 if (!pThis->m_fTerminate)
440 {
441 if (pThis->m_cbInputBuf)
442 {
443 const char *psz = pThis->m_pszInputBuf;
444 size_t cbRead = RT_MIN(pThis->m_cbInputBuf, cbBuf);
445 memcpy(pvBuf, psz, cbRead);
446 psz += cbRead;
447 pThis->m_cbInputBuf -= cbRead;
448 if (*psz)
449 memmove(pThis->m_pszInputBuf, psz, pThis->m_cbInputBuf);
450 pThis->m_pszInputBuf[pThis->m_cbInputBuf] = '\0';
451 *pcbRead = cbRead;
452 }
453 }
454 else
455 rc = VERR_GENERAL_FAILURE;
456 pThis->unlock();
457 return rc;
458}
459
460/**
461 * Write (output).
462 *
463 * @returns VBox status code.
464 * @param pBack Pointer to VBoxDbgConsole::m_Back.
465 * @param pvBuf What to write.
466 * @param cbBuf Number of bytes to write.
467 * @param pcbWritten Where to store the number of bytes actually written.
468 * If NULL the entire buffer must be successfully written.
469 */
470/*static*/ DECLCALLBACK(int) VBoxDbgConsole::backWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
471{
472 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
473 int rc = VINF_SUCCESS;
474
475 pThis->lock();
476 if (cbBuf + pThis->m_cbOutputBuf >= pThis->m_cbOutputBufAlloc)
477 {
478 size_t cbNew = RT_ALIGN_Z(cbBuf + pThis->m_cbOutputBufAlloc + 1, 1024);
479 void *pv = RTMemRealloc(pThis->m_pszOutputBuf, cbNew);
480 if (!pv)
481 {
482 pThis->unlock();
483 if (pcbWritten)
484 *pcbWritten = 0;
485 return VERR_NO_MEMORY;
486 }
487 pThis->m_pszOutputBuf = (char *)pv;
488 pThis->m_cbOutputBufAlloc = cbNew;
489 }
490
491 /*
492 * Add the output.
493 */
494 memcpy(pThis->m_pszOutputBuf + pThis->m_cbOutputBuf, pvBuf, cbBuf);
495 pThis->m_cbOutputBuf += cbBuf;
496 pThis->m_pszOutputBuf[pThis->m_cbOutputBuf] = '\0';
497 if (pcbWritten)
498 *pcbWritten = cbBuf;
499
500 if (pThis->m_fTerminate)
501 rc = VERR_GENERAL_FAILURE;
502
503 /*
504 * Tell the GUI thread to draw this text.
505 * We cannot do it from here without frequent crashes.
506 */
507 if (!pThis->m_fUpdatePending)
508 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kUpdate));
509
510 pThis->unlock();
511
512 return rc;
513}
514
515/**
516 * The Debugger Console Thread
517 *
518 * @returns VBox status code (ignored).
519 * @param Thread The thread handle.
520 * @param pvUser Pointer to the VBoxDbgConsole object.s
521 */
522/*static*/ DECLCALLBACK(int) VBoxDbgConsole::backThread(RTTHREAD Thread, void *pvUser)
523{
524 VBoxDbgConsole *pThis = (VBoxDbgConsole *)pvUser;
525 LogFlow(("backThread: Thread=%p pvUser=%p\n", (void *)Thread, pvUser));
526
527 NOREF(Thread);
528
529 /*
530 * Create and execute the console.
531 */
532 int rc = pThis->dbgcCreate(&pThis->m_Back.Core, 0);
533 LogFlow(("backThread: returns %Vrc\n", rc));
534 if (!pThis->m_fTerminate)
535 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kTerminated));
536 return rc;
537}
538
539bool VBoxDbgConsole::event(QEvent *pGenEvent)
540{
541 Assert(isGUIThread());
542 if (pGenEvent->type() == (QEvent::Type)VBoxDbgConsoleEvent::kEventNumber)
543 {
544 VBoxDbgConsoleEvent *pEvent = (VBoxDbgConsoleEvent *)pGenEvent;
545
546 switch (pEvent->command())
547 {
548 /* make update pending. */
549 case VBoxDbgConsoleEvent::kUpdate:
550 if (!m_fUpdatePending)
551 {
552 m_fUpdatePending = true;
553 m_pTimer->start(10, true /* single shot */);
554 }
555 break;
556
557 /* dirty hack: restores the focus */
558 case VBoxDbgConsoleEvent::kInputRestoreFocus:
559 if (m_fInputRestoreFocus)
560 {
561 m_fInputRestoreFocus = false;
562 if (!m_pInput->hasFocus())
563 m_pInput->setFocus();
564 }
565 break;
566
567 /* the thread terminated */
568 case VBoxDbgConsoleEvent::kTerminated:
569 m_pInput->setEnabled(false);
570 break;
571
572 /* paranoia */
573 default:
574 AssertMsgFailed(("command=%d\n", pEvent->command()));
575 break;
576 }
577 return true;
578 }
579
580 return QVBox::event(pGenEvent);
581}
582
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