VirtualBox

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

Last change on this file since 4199 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

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