VirtualBox

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

Last change on this file since 8806 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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