VirtualBox

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

Last change on this file since 6290 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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