1/* 2 * Copyright 2011, Rene Gollent, rene@gollent.com. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "MemoryView.h" 8 9#include <algorithm> 10 11#include <stdio.h> 12 13#include <ByteOrder.h> 14#include <Looper.h> 15#include <Messenger.h> 16#include <ScrollView.h> 17#include <String.h> 18 19#include "Architecture.h" 20#include "Team.h" 21#include "TeamMemoryBlock.h" 22 23 24enum { 25 MSG_TARGET_ADDRESS_CHANGED = 'mtac' 26}; 27 28 29MemoryView::MemoryView(::Team* team) 30 : 31 BView("memoryView", B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE 32 | B_SUBPIXEL_PRECISE), 33 fTeam(team), 34 fTargetBlock(NULL), 35 fTargetAddress(0LL), 36 fCharWidth(0.0), 37 fLineHeight(0.0), 38 fTextCharsPerLine(0), 39 fHexBlocksPerLine(0), 40 fHexMode(HexMode8BitInt), 41 fTextMode(TextModeASCII) 42{ 43 Architecture* architecture = team->GetArchitecture(); 44 fTargetAddressSize = architecture->AddressSize() * 2; 45 fCurrentEndianMode = architecture->IsBigEndian() 46 ? EndianModeBigEndian : EndianModeLittleEndian; 47 48} 49 50 51MemoryView::~MemoryView() 52{ 53 if (fTargetBlock != NULL) 54 fTargetBlock->ReleaseReference(); 55} 56 57 58/*static */ MemoryView* 59MemoryView::Create(::Team* team) 60{ 61 MemoryView* self = new MemoryView(team); 62 63 try { 64 self->_Init(); 65 } catch(...) { 66 delete self; 67 throw; 68 } 69 70 return self; 71} 72 73 74void 75MemoryView::SetTargetAddress(TeamMemoryBlock* block, target_addr_t address) 76{ 77 fTargetAddress = address; 78 if (fTargetBlock != NULL) 79 fTargetBlock->ReleaseReference(); 80 81 fTargetBlock = block; 82 fTargetBlock->AcquireReference(); 83 MakeFocus(true); 84 BMessenger(this).SendMessage(MSG_TARGET_ADDRESS_CHANGED); 85} 86 87 88void 89MemoryView::AttachedToWindow() 90{ 91 BView::AttachedToWindow(); 92 SetViewColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR)); 93 SetFont(be_fixed_font); 94 fCharWidth = be_fixed_font->StringWidth("a"); 95 font_height fontHeight; 96 be_fixed_font->GetHeight(&fontHeight); 97 fLineHeight = ceilf(fontHeight.ascent + fontHeight.descent 98 + fontHeight.leading); 99} 100 101 102void 103MemoryView::Draw(BRect rect) 104{ 105 rect = Bounds(); 106 107 float divider = (fTargetAddressSize + 1) * fCharWidth; 108 StrokeLine(BPoint(divider, rect.top), 109 BPoint(divider, rect.bottom)); 110 111 if (fTargetBlock == NULL) 112 return; 113 114 uint32 hexBlockSize = (1 << fHexMode) + 1; 115 uint32 blockByteSize = hexBlockSize / 2; 116 if (fHexMode != HexModeNone && fTextMode != TextModeNone) { 117 divider += (fHexBlocksPerLine * hexBlockSize + 1) * fCharWidth; 118 StrokeLine(BPoint(divider, rect.top), 119 BPoint(divider, rect.bottom)); 120 } 121 122 char buffer[32]; 123 char textbuffer[512]; 124 125 int32 startLine = int32(rect.top / fLineHeight); 126 const char* currentAddress = (const char*)(fTargetBlock->Data() 127 + fHexBlocksPerLine * blockByteSize * startLine); 128 const char* maxAddress = (const char*)(fTargetBlock->Data() 129 + fTargetBlock->Size()); 130 const char* targetAddress = (const char *)fTargetBlock->Data() 131 + fTargetAddress - fTargetBlock->BaseAddress(); 132 BPoint drawPoint(1.0, (startLine + 1) * fLineHeight); 133 int32 currentBlocksPerLine = fHexBlocksPerLine; 134 int32 currentCharsPerLine = fTextCharsPerLine; 135 rgb_color addressColor = tint_color(HighColor(), B_LIGHTEN_1_TINT); 136 rgb_color dataColor = HighColor(); 137 font_height fh; 138 GetFontHeight(&fh); 139 target_addr_t lineAddress = fTargetBlock->BaseAddress() + startLine 140 * currentCharsPerLine; 141 for (; currentAddress < maxAddress && drawPoint.y < rect.bottom 142 + fLineHeight; drawPoint.y += fLineHeight) { 143 drawPoint.x = 1.0; 144 snprintf(buffer, sizeof(buffer), "%0*" B_PRIx64, 145 (int)fTargetAddressSize, lineAddress); 146 PushState(); 147 SetHighColor(tint_color(HighColor(), B_LIGHTEN_1_TINT)); 148 DrawString(buffer, drawPoint); 149 drawPoint.x += fCharWidth * (fTargetAddressSize + 2); 150 PopState(); 151 152 if (fHexMode != HexModeNone) { 153 if (currentAddress + (currentBlocksPerLine * blockByteSize) 154 > maxAddress) { 155 currentCharsPerLine = maxAddress - currentAddress; 156 currentBlocksPerLine = currentCharsPerLine 157 / blockByteSize; 158 } 159 160 for (int32 j = 0; j < currentBlocksPerLine; j++) { 161 const char* blockAddress = currentAddress + (j 162 * blockByteSize); 163 _GetNextHexBlock(buffer, 164 std::min((size_t)hexBlockSize, sizeof(buffer)), 165 blockAddress); 166 DrawString(buffer, drawPoint); 167 if (targetAddress >= blockAddress && targetAddress < 168 blockAddress + blockByteSize) { 169 PushState(); 170 SetHighColor(B_TRANSPARENT_COLOR); 171 SetDrawingMode(B_OP_INVERT); 172 FillRect(BRect(drawPoint.x, drawPoint.y - fh.ascent, 173 drawPoint.x + (hexBlockSize - 1) * fCharWidth, 174 drawPoint.y + fh.descent)); 175 PopState(); 176 } 177 178 drawPoint.x += fCharWidth * hexBlockSize; 179 } 180 181 if (currentBlocksPerLine < fHexBlocksPerLine) 182 drawPoint.x += fCharWidth * hexBlockSize 183 * (fHexBlocksPerLine - currentBlocksPerLine); 184 } 185 if (fTextMode != TextModeNone) { 186 drawPoint.x += fCharWidth; 187 for (int32 j = 0; j < currentCharsPerLine; j++) { 188 // filter non-printable characters 189 textbuffer[j] = currentAddress[j] > 32 ? currentAddress[j] 190 : '.'; 191 } 192 textbuffer[fTextCharsPerLine] = '\0'; 193 DrawString(textbuffer, drawPoint); 194 if (targetAddress >= currentAddress && targetAddress 195 < currentAddress + currentCharsPerLine) { 196 PushState(); 197 SetHighColor(B_TRANSPARENT_COLOR); 198 SetDrawingMode(B_OP_INVERT); 199 uint32 blockAddress = uint32(targetAddress - currentAddress); 200 if (fHexMode != HexModeNone) 201 blockAddress &= ~(blockByteSize - 1); 202 float startX = drawPoint.x + fCharWidth * blockAddress; 203 float endX = startX; 204 if (fHexMode != HexModeNone) 205 endX += fCharWidth * ((hexBlockSize - 1) / 2); 206 else 207 endX += fCharWidth; 208 FillRect(BRect(startX, drawPoint.y - fh.ascent, endX, 209 drawPoint.y + fh.descent)); 210 PopState(); 211 } 212 } 213 if (currentBlocksPerLine > 0) { 214 currentAddress += currentBlocksPerLine * blockByteSize; 215 lineAddress += currentBlocksPerLine * blockByteSize; 216 } else { 217 currentAddress += fTextCharsPerLine; 218 lineAddress += fTextCharsPerLine; 219 } 220 } 221} 222 223 224void 225MemoryView::FrameResized(float width, float height) 226{ 227 BView::FrameResized(width, height); 228 _RecalcScrollBars(); 229 Invalidate(); 230} 231 232 233void 234MemoryView::KeyDown(const char* bytes, int32 numBytes) 235{ 236 bool handled = true; 237 if (fTargetBlock != NULL) { 238 target_addr_t newAddress = fTargetAddress; 239 target_addr_t maxAddress = fTargetBlock->BaseAddress() 240 + fTargetBlock->Size() - 1; 241 int32 blockSize = 1; 242 if (fHexMode != HexModeNone) 243 blockSize = 1 << (fHexMode - 1); 244 int32 lineCount = int32(Bounds().Height() / fLineHeight); 245 246 switch(bytes[0]) { 247 case B_UP_ARROW: 248 { 249 newAddress -= blockSize * fHexBlocksPerLine; 250 break; 251 } 252 case B_DOWN_ARROW: 253 { 254 newAddress += blockSize * fHexBlocksPerLine; 255 break; 256 } 257 case B_LEFT_ARROW: 258 { 259 newAddress -= blockSize; 260 break; 261 } 262 case B_RIGHT_ARROW: 263 { 264 newAddress += blockSize; 265 break; 266 } 267 case B_PAGE_UP: 268 { 269 newAddress -= (blockSize * fHexBlocksPerLine) * lineCount; 270 break; 271 } 272 case B_PAGE_DOWN: 273 { 274 newAddress += (blockSize * fHexBlocksPerLine) * lineCount; 275 break; 276 } 277 case B_HOME: 278 { 279 newAddress = fTargetBlock->BaseAddress(); 280 break; 281 } 282 case B_END: 283 { 284 newAddress = maxAddress; 285 break; 286 } 287 default: 288 { 289 handled = false; 290 break; 291 } 292 } 293 if (handled) { 294 if (newAddress < fTargetBlock->BaseAddress()) 295 newAddress = fTargetAddress; 296 else if (newAddress > maxAddress) 297 newAddress = maxAddress; 298 299 if (newAddress != fTargetAddress) { 300 fTargetAddress = newAddress; 301 BMessenger(this).SendMessage(MSG_TARGET_ADDRESS_CHANGED); 302 } 303 } 304 } else 305 handled = false; 306 307 if (!handled) 308 BView::KeyDown(bytes, numBytes); 309} 310 311 312void 313MemoryView::MakeFocus(bool isFocused) 314{ 315 BScrollView* parent = dynamic_cast<BScrollView*>(Parent()); 316 if (parent != NULL) 317 parent->SetBorderHighlighted(isFocused); 318 319 BView::MakeFocus(isFocused); 320} 321 322 323void 324MemoryView::MessageReceived(BMessage* message) 325{ 326 switch(message->what) { 327 case MSG_TARGET_ADDRESS_CHANGED: 328 { 329 _RecalcScrollBars(); 330 ScrollToSelection(); 331 Invalidate(); 332 break; 333 } 334 case MSG_SET_HEX_MODE: 335 { 336 int32 mode; 337 if (message->FindInt32("mode", &mode) == B_OK) { 338 fHexMode = mode; 339 _RecalcScrollBars(); 340 Invalidate(); 341 } 342 break; 343 } 344 case MSG_SET_ENDIAN_MODE: 345 { 346 int32 mode; 347 if (message->FindInt32("mode", &mode) == B_OK) { 348 fCurrentEndianMode = mode; 349 Invalidate(); 350 } 351 break; 352 } 353 case MSG_SET_TEXT_MODE: 354 { 355 int32 mode; 356 if (message->FindInt32("mode", &mode) == B_OK) { 357 fTextMode = mode; 358 _RecalcScrollBars(); 359 Invalidate(); 360 } 361 break; 362 } 363 default: 364 { 365 BView::MessageReceived(message); 366 break; 367 } 368 } 369} 370 371 372void 373MemoryView::MouseDown(BPoint point) 374{ 375 if (!IsFocus()) 376 MakeFocus(true); 377 378 BView::MouseDown(point); 379} 380 381 382void 383MemoryView::ScrollToSelection() 384{ 385 if (fTargetBlock != NULL) { 386 target_addr_t offset = fTargetAddress - fTargetBlock->BaseAddress(); 387 int32 lineNumber = 0; 388 if (fHexBlocksPerLine > 0) 389 lineNumber = offset / (fHexBlocksPerLine * (1 << (fHexMode - 1))); 390 else if (fTextCharsPerLine > 0) 391 lineNumber = offset / fTextCharsPerLine; 392 float y = lineNumber * fLineHeight; 393 if (y < Bounds().top) 394 ScrollTo(0.0, y); 395 else if (y + fLineHeight > Bounds().bottom) 396 ScrollTo(0.0, y + fLineHeight - Bounds().Height()); 397 } 398} 399 400 401void 402MemoryView::TargetedByScrollView(BScrollView* scrollView) 403{ 404 BView::TargetedByScrollView(scrollView); 405 scrollView->ScrollBar(B_VERTICAL)->SetRange(0.0, 0.0); 406} 407 408 409void 410MemoryView::_Init() 411{ 412 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 413} 414 415 416void 417MemoryView::_RecalcScrollBars() 418{ 419 float max = 0.0; 420 BScrollBar *scrollBar = ScrollBar(B_VERTICAL); 421 if (fTargetBlock != NULL) { 422 BRect bounds = Bounds(); 423 // the left portion of the view is off limits since it 424 // houses the address offset of the current line 425 float baseWidth = bounds.Width() - ((fTargetAddressSize + 2) 426 * fCharWidth); 427 float hexWidth = 0.0; 428 float textWidth = 0.0; 429 int32 hexDigits = 1 << fHexMode; 430 int32 sizeFactor = 1 + hexDigits; 431 if (fHexMode != HexModeNone) { 432 if (fTextMode != TextModeNone) { 433 float hexProportion = sizeFactor / (float)(sizeFactor 434 + hexDigits / 2); 435 hexWidth = baseWidth * hexProportion; 436 // when sharing the display between hex and text, 437 // we allocate a 2 character space to separate the views 438 hexWidth -= 2 * fCharWidth; 439 textWidth = baseWidth - hexWidth; 440 } else 441 hexWidth = baseWidth; 442 } else if (fTextMode != TextModeNone) 443 textWidth = baseWidth; 444 445 int32 nybblesPerLine = int32(hexWidth / fCharWidth); 446 fHexBlocksPerLine = 0; 447 fTextCharsPerLine = 0; 448 if (fHexMode != HexModeNone) { 449 fHexBlocksPerLine = nybblesPerLine / sizeFactor; 450 fHexBlocksPerLine &= ~1; 451 if (fTextMode != TextModeNone) 452 fTextCharsPerLine = fHexBlocksPerLine * hexDigits / 2; 453 } else if (fTextMode != TextModeNone) 454 fTextCharsPerLine = int32(textWidth / fCharWidth); 455 456 int32 lineCount = 0; 457 float totalHeight = 0.0; 458 if (fHexBlocksPerLine > 0) { 459 lineCount = fTargetBlock->Size() / (fHexBlocksPerLine 460 * hexDigits / 2); 461 } else if (fTextCharsPerLine > 0) 462 lineCount = fTargetBlock->Size() / fTextCharsPerLine; 463 464 totalHeight = lineCount * fLineHeight; 465 if (totalHeight > 0.0) { 466 max = totalHeight - bounds.Height(); 467 scrollBar->SetProportion(bounds.Height() / totalHeight); 468 scrollBar->SetSteps(fLineHeight, bounds.Height()); 469 } 470 } 471 scrollBar->SetRange(0.0, max); 472} 473 474void 475MemoryView::_GetNextHexBlock(char* buffer, int32 bufferSize, 476 const char* address) 477{ 478 switch(fHexMode) { 479 case HexMode8BitInt: 480 { 481 snprintf(buffer, bufferSize, "%02" B_PRIx8, *address); 482 break; 483 } 484 case HexMode16BitInt: 485 { 486 uint16 data = *((const uint16*)address); 487 switch(fCurrentEndianMode) 488 { 489 case EndianModeBigEndian: 490 { 491 data = B_HOST_TO_BENDIAN_INT16(data); 492 } 493 break; 494 495 case EndianModeLittleEndian: 496 { 497 data = B_HOST_TO_LENDIAN_INT16(data); 498 } 499 break; 500 } 501 snprintf(buffer, bufferSize, "%04" B_PRIx16, 502 data); 503 break; 504 } 505 case HexMode32BitInt: 506 { 507 uint32 data = *((const uint32*)address); 508 switch(fCurrentEndianMode) 509 { 510 case EndianModeBigEndian: 511 { 512 data = B_HOST_TO_BENDIAN_INT32(data); 513 } 514 break; 515 516 case EndianModeLittleEndian: 517 { 518 data = B_HOST_TO_LENDIAN_INT32(data); 519 } 520 break; 521 } 522 snprintf(buffer, bufferSize, "%08" B_PRIx32, 523 data); 524 break; 525 } 526 case HexMode64BitInt: 527 { 528 uint64 data = *((const uint16*)address); 529 switch(fCurrentEndianMode) 530 { 531 case EndianModeBigEndian: 532 { 533 data = B_HOST_TO_BENDIAN_INT64(data); 534 } 535 break; 536 537 case EndianModeLittleEndian: 538 { 539 data = B_HOST_TO_LENDIAN_INT64(data); 540 } 541 break; 542 } 543 snprintf(buffer, bufferSize, "%0*" B_PRIx64, 544 16, data); 545 break; 546 } 547 } 548} 549