VirtualBox

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

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

Fixed the console crash: setFocus was called from the console thread.

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