[31510] | 1 | /* $Id: VBoxDbgBase.cpp 104615 2024-05-13 16:12:02Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VBox Debugger GUI - Base classes.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[31510] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[31510] | 26 | */
|
---|
| 27 |
|
---|
[57358] | 28 |
|
---|
| 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header Files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
[31510] | 32 | #define LOG_GROUP LOG_GROUP_DBGG
|
---|
[76474] | 33 | #include <iprt/errcore.h>
|
---|
[31510] | 34 | #include <iprt/asm.h>
|
---|
| 35 | #include <iprt/assert.h>
|
---|
| 36 | #include <limits.h>
|
---|
| 37 | #include "VBoxDbgBase.h"
|
---|
| 38 | #include "VBoxDbgGui.h"
|
---|
| 39 |
|
---|
| 40 | #include <QApplication>
|
---|
| 41 | #include <QWidgetList>
|
---|
| 42 |
|
---|
| 43 |
|
---|
| 44 |
|
---|
| 45 | VBoxDbgBase::VBoxDbgBase(VBoxDbgGui *a_pDbgGui)
|
---|
[93468] | 46 | : m_pDbgGui(a_pDbgGui), m_pUVM(NULL), m_pVMM(NULL), m_hGUIThread(RTThreadNativeSelf())
|
---|
[31510] | 47 | {
|
---|
[63452] | 48 | NOREF(m_pDbgGui); /* shut up warning. */
|
---|
| 49 |
|
---|
[31510] | 50 | /*
|
---|
| 51 | * Register
|
---|
| 52 | */
|
---|
[44340] | 53 | m_pUVM = a_pDbgGui->getUvmHandle();
|
---|
[93468] | 54 | m_pVMM = a_pDbgGui->getVMMFunctionTable();
|
---|
| 55 | if (m_pUVM && m_pVMM)
|
---|
[31510] | 56 | {
|
---|
[93468] | 57 | m_pVMM->pfnVMR3RetainUVM(m_pUVM);
|
---|
[44340] | 58 |
|
---|
[93468] | 59 | int rc = m_pVMM->pfnVMR3AtStateRegister(m_pUVM, atStateChange, this);
|
---|
[31510] | 60 | AssertRC(rc);
|
---|
| 61 | }
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 |
|
---|
| 65 | VBoxDbgBase::~VBoxDbgBase()
|
---|
| 66 | {
|
---|
| 67 | /*
|
---|
| 68 | * If the VM is still around.
|
---|
| 69 | */
|
---|
| 70 | /** @todo need to do some locking here? */
|
---|
[93468] | 71 | PUVM pUVM = ASMAtomicXchgPtrT(&m_pUVM, NULL, PUVM);
|
---|
| 72 | PCVMMR3VTABLE pVMM = ASMAtomicXchgPtrT(&m_pVMM, NULL, PCVMMR3VTABLE);
|
---|
| 73 | if (pUVM && pVMM)
|
---|
[31510] | 74 | {
|
---|
[93468] | 75 | int rc = pVMM->pfnVMR3AtStateDeregister(pUVM, atStateChange, this);
|
---|
[31510] | 76 | AssertRC(rc);
|
---|
[44340] | 77 |
|
---|
[93468] | 78 | pVMM->pfnVMR3ReleaseUVM(pUVM);
|
---|
[31510] | 79 | }
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 |
|
---|
| 83 | int
|
---|
| 84 | VBoxDbgBase::stamReset(const QString &rPat)
|
---|
| 85 | {
|
---|
| 86 | QByteArray Utf8Array = rPat.toUtf8();
|
---|
| 87 | const char *pszPat = !rPat.isEmpty() ? Utf8Array.constData() : NULL;
|
---|
[93468] | 88 | PUVM pUVM = m_pUVM;
|
---|
| 89 | PCVMMR3VTABLE pVMM = m_pVMM;
|
---|
| 90 | if ( pUVM
|
---|
| 91 | && pVMM
|
---|
| 92 | && pVMM->pfnVMR3GetStateU(pUVM) < VMSTATE_DESTROYING)
|
---|
| 93 | return pVMM->pfnSTAMR3Reset(pUVM, pszPat);
|
---|
[31510] | 94 | return VERR_INVALID_HANDLE;
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 |
|
---|
| 98 | int
|
---|
| 99 | VBoxDbgBase::stamEnum(const QString &rPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
|
---|
| 100 | {
|
---|
| 101 | QByteArray Utf8Array = rPat.toUtf8();
|
---|
| 102 | const char *pszPat = !rPat.isEmpty() ? Utf8Array.constData() : NULL;
|
---|
[93468] | 103 | PUVM pUVM = m_pUVM;
|
---|
| 104 | PCVMMR3VTABLE pVMM = m_pVMM;
|
---|
| 105 | if ( pUVM
|
---|
| 106 | && pVMM
|
---|
| 107 | && pVMM->pfnVMR3GetStateU(pUVM) < VMSTATE_DESTROYING)
|
---|
| 108 | return pVMM->pfnSTAMR3Enum(pUVM, pszPat, pfnEnum, pvUser);
|
---|
[31510] | 109 | return VERR_INVALID_HANDLE;
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 |
|
---|
| 113 | int
|
---|
[86327] | 114 | VBoxDbgBase::dbgcCreate(PCDBGCIO pIo, unsigned fFlags)
|
---|
[31510] | 115 | {
|
---|
[93468] | 116 | PUVM pUVM = m_pUVM;
|
---|
| 117 | PCVMMR3VTABLE pVMM = m_pVMM;
|
---|
| 118 | if ( pUVM
|
---|
| 119 | && pVMM
|
---|
| 120 | && pVMM->pfnVMR3GetStateU(pUVM) < VMSTATE_DESTROYING)
|
---|
| 121 | return pVMM->pfnDBGCCreate(pUVM, pIo, fFlags);
|
---|
[31510] | 122 | return VERR_INVALID_HANDLE;
|
---|
| 123 | }
|
---|
| 124 |
|
---|
| 125 |
|
---|
| 126 | /*static*/ DECLCALLBACK(void)
|
---|
[93468] | 127 | VBoxDbgBase::atStateChange(PUVM pUVM, PCVMMR3VTABLE pVMM, VMSTATE enmState, VMSTATE /*enmOldState*/, void *pvUser)
|
---|
[31510] | 128 | {
|
---|
[44393] | 129 | VBoxDbgBase *pThis = (VBoxDbgBase *)pvUser; NOREF(pUVM);
|
---|
[31510] | 130 | switch (enmState)
|
---|
| 131 | {
|
---|
| 132 | case VMSTATE_TERMINATED:
|
---|
[44340] | 133 | {
|
---|
[31510] | 134 | /** @todo need to do some locking here? */
|
---|
[93468] | 135 | PUVM pUVM2 = ASMAtomicXchgPtrT(&pThis->m_pUVM, NULL, PUVM);
|
---|
| 136 | PCVMMR3VTABLE pVMM2 = ASMAtomicXchgPtrT(&pThis->m_pVMM, NULL, PCVMMR3VTABLE);
|
---|
| 137 | if (pUVM2 && pVMM2)
|
---|
[44340] | 138 | {
|
---|
[44393] | 139 | Assert(pUVM2 == pUVM);
|
---|
[93468] | 140 | Assert(pVMM2 == pVMM);
|
---|
[31510] | 141 | pThis->sigTerminated();
|
---|
[93468] | 142 | pVMM->pfnVMR3ReleaseUVM(pUVM2);
|
---|
[44340] | 143 | }
|
---|
[31510] | 144 | break;
|
---|
[44340] | 145 | }
|
---|
[31510] | 146 |
|
---|
| 147 | case VMSTATE_DESTROYING:
|
---|
| 148 | pThis->sigDestroying();
|
---|
| 149 | break;
|
---|
| 150 |
|
---|
| 151 | default:
|
---|
| 152 | break;
|
---|
| 153 | }
|
---|
[93468] | 154 | RT_NOREF(pVMM);
|
---|
[31510] | 155 | }
|
---|
| 156 |
|
---|
| 157 |
|
---|
| 158 | void
|
---|
| 159 | VBoxDbgBase::sigDestroying()
|
---|
| 160 | {
|
---|
| 161 | }
|
---|
| 162 |
|
---|
| 163 |
|
---|
| 164 | void
|
---|
| 165 | VBoxDbgBase::sigTerminated()
|
---|
| 166 | {
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 |
|
---|
| 170 |
|
---|
| 171 |
|
---|
| 172 | //
|
---|
| 173 | //
|
---|
| 174 | //
|
---|
| 175 | // V B o x D b g B a s e W i n d o w
|
---|
| 176 | // V B o x D b g B a s e W i n d o w
|
---|
| 177 | // V B o x D b g B a s e W i n d o w
|
---|
| 178 | //
|
---|
| 179 | //
|
---|
| 180 | //
|
---|
| 181 |
|
---|
| 182 | unsigned VBoxDbgBaseWindow::m_cxBorder = 0;
|
---|
| 183 | unsigned VBoxDbgBaseWindow::m_cyBorder = 0;
|
---|
| 184 |
|
---|
| 185 |
|
---|
[77412] | 186 | VBoxDbgBaseWindow::VBoxDbgBaseWindow(VBoxDbgGui *a_pDbgGui, QWidget *a_pParent, const char *a_pszTitle)
|
---|
| 187 | : QWidget(a_pParent, Qt::Window), VBoxDbgBase(a_pDbgGui), m_pszTitle(a_pszTitle), m_fPolished(false)
|
---|
[101107] | 188 | , m_x(INT_MAX), m_y(INT_MAX), m_cx(0), m_cy(0), m_cxMinHint(0), m_enmAttraction(VBoxDbgBaseWindow::kAttractionVmNone)
|
---|
[31510] | 189 | {
|
---|
[77412] | 190 | /* Set the title, using the parent one as prefix when possible: */
|
---|
| 191 | if (!parent())
|
---|
| 192 | {
|
---|
| 193 | QString strMachineName = a_pDbgGui->getMachineName();
|
---|
| 194 | if (strMachineName.isEmpty())
|
---|
| 195 | setWindowTitle(QString("VBoxDbg - %1").arg(m_pszTitle));
|
---|
| 196 | else
|
---|
| 197 | setWindowTitle(QString("%1 - VBoxDbg - %2").arg(strMachineName).arg(m_pszTitle));
|
---|
| 198 | }
|
---|
| 199 | else
|
---|
| 200 | {
|
---|
| 201 | setWindowTitle(QString("%1 - %2").arg(parentWidget()->windowTitle()).arg(m_pszTitle));
|
---|
| 202 |
|
---|
| 203 | /* Install an event filter so we can make adjustments when the parent title changes: */
|
---|
| 204 | parent()->installEventFilter(this);
|
---|
| 205 | }
|
---|
[31510] | 206 | }
|
---|
| 207 |
|
---|
| 208 |
|
---|
| 209 | VBoxDbgBaseWindow::~VBoxDbgBaseWindow()
|
---|
| 210 | {
|
---|
| 211 |
|
---|
| 212 | }
|
---|
| 213 |
|
---|
| 214 |
|
---|
| 215 | void
|
---|
| 216 | VBoxDbgBaseWindow::vShow()
|
---|
| 217 | {
|
---|
| 218 | show();
|
---|
| 219 | /** @todo this ain't working right. HELP! */
|
---|
| 220 | setWindowState(windowState() & ~Qt::WindowMinimized);
|
---|
| 221 | //activateWindow();
|
---|
| 222 | //setFocus();
|
---|
| 223 | vPolishSizeAndPos();
|
---|
| 224 | }
|
---|
| 225 |
|
---|
| 226 |
|
---|
| 227 | void
|
---|
| 228 | VBoxDbgBaseWindow::vReposition(int a_x, int a_y, unsigned a_cx, unsigned a_cy, bool a_fResize)
|
---|
| 229 | {
|
---|
[101093] | 230 | /* Don't modify if maximized.
|
---|
| 231 | We miss the desired size + position here, but never mind for now. */
|
---|
| 232 | if (!(windowState() & Qt::WindowMaximized))
|
---|
[31510] | 233 | {
|
---|
[101093] | 234 | if (a_fResize)
|
---|
| 235 | {
|
---|
| 236 | m_cx = a_cx;
|
---|
| 237 | m_cy = a_cy;
|
---|
[31510] | 238 |
|
---|
[101107] | 239 | QSize const BorderSize = vGetBorderSize();
|
---|
[101093] | 240 | resize(a_cx - BorderSize.width(), a_cy - BorderSize.height());
|
---|
| 241 | }
|
---|
| 242 |
|
---|
| 243 | m_x = a_x;
|
---|
| 244 | m_y = a_y;
|
---|
| 245 | move(a_x, a_y);
|
---|
[31510] | 246 | }
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 |
|
---|
[101107] | 250 | QSize
|
---|
| 251 | VBoxDbgBaseWindow::vGetBorderSize()
|
---|
| 252 | {
|
---|
| 253 | QSize BorderSize = frameSize() - size();
|
---|
| 254 |
|
---|
| 255 | #ifdef Q_WS_X11 /* (from the qt gui) */
|
---|
| 256 | /*
|
---|
| 257 | * On X11, there is no way to determine frame geometry (including WM
|
---|
| 258 | * decorations) before the widget is shown for the first time. Stupidly
|
---|
| 259 | * enumerate other top level widgets to find the thickest frame.
|
---|
| 260 | */
|
---|
| 261 | if (BorderSize == QSize(0, 0))
|
---|
| 262 | {
|
---|
| 263 | if (!m_cxBorder && !m_cyBorder) /* (only till we're successful) */
|
---|
| 264 | {
|
---|
| 265 | int cxExtra = 0;
|
---|
| 266 | int cyExtra = 0;
|
---|
| 267 |
|
---|
| 268 | QWidgetList WidgetList = QApplication::topLevelWidgets();
|
---|
| 269 | for (QListIterator<QWidget *> it(WidgetList); it.hasNext(); )
|
---|
| 270 | {
|
---|
| 271 | QWidget *pCurWidget = it.next();
|
---|
| 272 | if (pCurWidget->isVisible())
|
---|
| 273 | {
|
---|
| 274 | int const cxFrame = pCurWidget->frameGeometry().width() - pCurWidget->width();
|
---|
| 275 | cxExtra = qMax(cxExtra, cxFrame);
|
---|
| 276 | int const cyFrame = pCurWidget->frameGeometry().height() - pCurWidget->height();
|
---|
| 277 | cyExtra = qMax(cyExtra, cyFrame);
|
---|
| 278 | if (cyExtra && cxExtra)
|
---|
| 279 | break;
|
---|
| 280 | }
|
---|
| 281 | }
|
---|
| 282 |
|
---|
| 283 | if (cxExtra || cyExtra)
|
---|
| 284 | {
|
---|
| 285 | m_cxBorder = cxExtra;
|
---|
| 286 | m_cyBorder = cyExtra;
|
---|
| 287 | }
|
---|
| 288 | }
|
---|
| 289 | BorderSize.setWidth(m_cxBorder);
|
---|
| 290 | BorderSize.setHeight(m_cyBorder);
|
---|
| 291 | }
|
---|
| 292 | #endif /* X11 */
|
---|
| 293 |
|
---|
| 294 | return BorderSize;
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 |
|
---|
[31510] | 298 | bool
|
---|
| 299 | VBoxDbgBaseWindow::event(QEvent *a_pEvt)
|
---|
| 300 | {
|
---|
| 301 | bool fRc = QWidget::event(a_pEvt);
|
---|
[85844] | 302 | if ( a_pEvt->type() == QEvent::Paint
|
---|
| 303 | || a_pEvt->type() == QEvent::UpdateRequest
|
---|
| 304 | || a_pEvt->type() == QEvent::LayoutRequest) /** @todo Someone with Qt knowledge should figure out how to properly do this. */
|
---|
| 305 | vPolishSizeAndPos();
|
---|
[31510] | 306 | return fRc;
|
---|
| 307 | }
|
---|
| 308 |
|
---|
| 309 |
|
---|
[77412] | 310 | bool VBoxDbgBaseWindow::eventFilter(QObject *pWatched, QEvent *pEvent)
|
---|
| 311 | {
|
---|
| 312 | /* We're only interested in title changes to the parent so we can amend our own title: */
|
---|
| 313 | if ( pWatched == parent()
|
---|
| 314 | && pEvent->type() == QEvent::WindowTitleChange)
|
---|
| 315 | setWindowTitle(QString("%1 - %2").arg(parentWidget()->windowTitle()).arg(m_pszTitle));
|
---|
| 316 |
|
---|
| 317 | /* Forward to base-class: */
|
---|
| 318 | return QWidget::eventFilter(pWatched, pEvent);
|
---|
| 319 | }
|
---|
| 320 |
|
---|
| 321 |
|
---|
[31510] | 322 | void
|
---|
| 323 | VBoxDbgBaseWindow::vPolishSizeAndPos()
|
---|
| 324 | {
|
---|
| 325 | /* Ignore if already done or no size set. */
|
---|
| 326 | if ( m_fPolished
|
---|
| 327 | || (m_x == INT_MAX && m_y == INT_MAX))
|
---|
| 328 | return;
|
---|
| 329 |
|
---|
[104615] | 330 | if (VBoxDbgBaseWindow::vGetBorderSize() != QSize(0,0))
|
---|
[31510] | 331 | m_fPolished = true;
|
---|
| 332 |
|
---|
| 333 | vReposition(m_x, m_y, m_cx, m_cy, m_cx || m_cy);
|
---|
| 334 | }
|
---|
| 335 |
|
---|