Path: blob/master/Windows/GEDebugger/CtrlDisplayListView.cpp
5659 views
#include <algorithm>1#include "Common/Data/Encoding/Utf8.h"2#include "Common/StringUtils.h"3#include "Common/System/Display.h"4#include "Windows/GEDebugger/CtrlDisplayListView.h"5#include "Windows/GEDebugger/GEDebugger.h"6#include "Windows/MainWindow.h"7#include "Windows/InputBox.h"8#include "Windows/W32Util/ContextMenu.h"9#include "Windows/main.h"10#include "Core/Config.h"11#include "GPU/Debugger/Breakpoints.h"12#include "GPU/GPUState.h"1314constexpr const wchar_t *szWindowClass = L"CtrlDisplayListView";1516void CtrlDisplayListView::registerClass()17{18WNDCLASSEX wndClass;1920wndClass.cbSize = sizeof(wndClass);21wndClass.lpszClassName = szWindowClass;22wndClass.hInstance = GetModuleHandle(0);23wndClass.lpfnWndProc = wndProc;24wndClass.hCursor = LoadCursor (NULL, IDC_ARROW);25wndClass.hIcon = 0;26wndClass.lpszMenuName = 0;27wndClass.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);28wndClass.style = 0;29wndClass.cbClsExtra = 0;30wndClass.cbWndExtra = sizeof(CtrlDisplayListView*);31wndClass.hIconSm = 0;3233RegisterClassEx(&wndClass);34}3536CtrlDisplayListView::CtrlDisplayListView(HWND _wnd)37: wnd(_wnd)38{39SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR) this);40SetWindowLong(wnd, GWL_STYLE, GetWindowLong(wnd,GWL_STYLE) | WS_VSCROLL);41SetScrollRange(wnd, SB_VERT, -1,1,TRUE);4243instructionSize = 4;4445// In small window mode, g_dpi_scale may have been adjusted.46const float fontScale = 1.0f / g_display.dpi_scale_real_y;47int fontHeight = g_Config.iFontHeight * fontScale;48int charWidth = g_Config.iFontWidth * fontScale;4950rowHeight = fontHeight + 2;5152font = CreateFont(fontHeight,charWidth,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,53L"Lucida Console");54boldfont = CreateFont(fontHeight,charWidth,0,0,FW_DEMIBOLD,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,55L"Lucida Console");5657pixelPositions.addressStart = 16;58pixelPositions.opcodeStart = pixelPositions.addressStart + 19*charWidth;5960hasFocus = false;61validDisplayList = false;62}6364CtrlDisplayListView::~CtrlDisplayListView() {65DeleteObject(font);66DeleteObject(boldfont);67}6869CtrlDisplayListView *CtrlDisplayListView::getFrom(HWND hwnd)70{71return (CtrlDisplayListView*) GetWindowLongPtr(hwnd, GWLP_USERDATA);72}7374LRESULT CALLBACK CtrlDisplayListView::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)75{76CtrlDisplayListView *win = CtrlDisplayListView::getFrom(hwnd);7778switch(msg) {79case WM_NCCREATE:80// Allocate a new CustCtrl structure for this window.81win = new CtrlDisplayListView(hwnd);8283// Continue with window creation.84return win != NULL;85case WM_NCDESTROY:86SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);87delete win;88break;89case WM_SIZE:90win->redraw();91break;92case WM_PAINT:93win->onPaint(wParam,lParam);94break;95case WM_SETFOCUS:96SetFocus(hwnd);97win->hasFocus=true;98win->redraw();99break;100case WM_KILLFOCUS:101win->hasFocus=false;102win->redraw();103break;104case WM_VSCROLL:105win->onVScroll(wParam,lParam);106break;107case WM_MOUSEWHEEL:108if (GET_WHEEL_DELTA_WPARAM(wParam) > 0)109{110win->scrollWindow(-3);111} else if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) {112win->scrollWindow(3);113}114break;115case WM_LBUTTONDOWN:116win->onMouseDown(wParam,lParam,1);117break;118case WM_RBUTTONDOWN:119win->onMouseDown(wParam,lParam,2);120break;121case WM_LBUTTONUP:122win->onMouseUp(wParam,lParam,1);123break;124case WM_RBUTTONUP:125win->onMouseUp(wParam,lParam,2);126break;127case WM_KEYDOWN:128case WM_SYSKEYDOWN:129win->onKeyDown(wParam,lParam);130return 0;131case WM_GETDLGCODE:132if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)133{134switch (wParam)135{136case VK_TAB:137return DLGC_WANTMESSAGE;138default:139return DLGC_WANTCHARS|DLGC_WANTARROWS;140}141}142return DLGC_WANTCHARS|DLGC_WANTARROWS;143}144145return DefWindowProc(hwnd, msg, wParam, lParam);146}147148void CtrlDisplayListView::redraw()149{150GetClientRect(wnd, &rect);151visibleRows = rect.bottom/rowHeight;152153RedrawWindow(wnd, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);154}155156157void CtrlDisplayListView::onPaint(WPARAM wParam, LPARAM lParam)158{159if (!validDisplayList || !gpuDebug)160return;161162PAINTSTRUCT ps;163HDC actualHdc = BeginPaint(wnd, &ps);164HDC hdc = CreateCompatibleDC(actualHdc);165HBITMAP hBM = CreateCompatibleBitmap(actualHdc, rect.right-rect.left, rect.bottom-rect.top);166SelectObject(hdc, hBM);167168SetBkMode(hdc, TRANSPARENT);169170HPEN nullPen=CreatePen(0,0,0xffffff);171HPEN condPen=CreatePen(0,0,0xFF3020);172HBRUSH nullBrush=CreateSolidBrush(0xffffff);173HBRUSH currentBrush=CreateSolidBrush(0xFFEfE8);174175HPEN oldPen=(HPEN)SelectObject(hdc,nullPen);176HBRUSH oldBrush=(HBRUSH)SelectObject(hdc,nullBrush);177HFONT oldFont = (HFONT)SelectObject(hdc,(HGDIOBJ)font);178179HICON breakPoint = (HICON)LoadIcon(GetModuleHandle(0),(LPCWSTR)IDI_STOP);180181auto disasm = gpuDebug->DisassembleOpRange(windowStart, windowStart + (visibleRows + 2) * instructionSize);182183for (int i = 0; i < visibleRows+2; i++)184{185unsigned int address=windowStart + i*instructionSize;186bool stall = address == list.stall;187188int rowY1 = rowHeight*i;189190// draw background191COLORREF backgroundColor = stall ? 0xCCCCFF : 0xFFFFFF;192COLORREF textColor = 0x000000;193194if (address >= selectRangeStart && address < selectRangeEnd)195{196if (hasFocus)197{198backgroundColor = address == curAddress ? 0xFF8822 : 0xFF9933;199textColor = 0xFFFFFF;200} else {201backgroundColor = 0xC0C0C0;202}203}204205HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);206HPEN backgroundPen = CreatePen(0,0,backgroundColor);207SelectObject(hdc,backgroundBrush);208SelectObject(hdc,backgroundPen);209Rectangle(hdc,0,rowY1,rect.right,rowY1+rowHeight);210211SelectObject(hdc,currentBrush);212SelectObject(hdc,nullPen);213214DeleteObject(backgroundBrush);215DeleteObject(backgroundPen);216217// display address/symbol218if (gpuDebug->GetBreakpoints()->IsAddressBreakpoint(address))219{220textColor = 0x0000FF;221int yOffset = std::max(-1,(rowHeight-14+1)/2);222DrawIconEx(hdc,2,rowY1+1+yOffset,breakPoint,32,32,0,0,DI_NORMAL);223}224SetTextColor(hdc,textColor);225226GPUDebugOp op = i < (int)disasm.size() ? disasm[i] : GPUDebugOp();227228char addressText[64];229snprintf(addressText,sizeof(addressText),"%08X %08X",op.pc,op.op);230TextOutA(hdc,pixelPositions.addressStart,rowY1+2,addressText,(int)strlen(addressText));231232if (address == list.pc)233{234TextOut(hdc,pixelPositions.opcodeStart-8,rowY1,L"\x25A0",1);235}236237const char* opcode = op.desc.c_str();238SelectObject(hdc,stall ? boldfont : font);239TextOutA(hdc,pixelPositions.opcodeStart,rowY1+2,opcode,(int)strlen(opcode));240SelectObject(hdc,font);241}242243SelectObject(hdc,oldFont);244SelectObject(hdc,oldPen);245SelectObject(hdc,oldBrush);246247// copy bitmap to the actual hdc248BitBlt(actualHdc, 0, 0, rect.right, rect.bottom, hdc, 0, 0, SRCCOPY);249DeleteObject(hBM);250DeleteDC(hdc);251252DeleteObject(nullPen);253DeleteObject(condPen);254255DeleteObject(nullBrush);256DeleteObject(currentBrush);257258DestroyIcon(breakPoint);259260EndPaint(wnd, &ps);261}262263void CtrlDisplayListView::toggleBreakpoint()264{265SendMessage(GetParent(wnd),WM_GEDBG_TOGGLEPCBREAKPOINT,curAddress,0);266}267268void CtrlDisplayListView::PromptBreakpointCond() {269std::string expression;270gpuDebug->GetBreakpoints()->GetAddressBreakpointCond(curAddress, &expression);271if (!InputBox_GetString(GetModuleHandle(NULL), wnd, L"Expression", expression, expression))272return;273274std::string error;275if (!gpuDebug->GetBreakpoints()->SetAddressBreakpointCond(curAddress, expression, &error))276MessageBox(wnd, ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);277}278279void CtrlDisplayListView::onMouseDown(WPARAM wParam, LPARAM lParam, int button)280{281if (!validDisplayList || !gpuDebug) {282return;283}284285int y = HIWORD(lParam);286287int line = y/rowHeight;288u32 newAddress = windowStart + line*instructionSize;289290bool extend = KeyDownAsync(VK_SHIFT);291if (button == 1)292{293if (newAddress == curAddress && hasFocus)294{295toggleBreakpoint();296}297} else if (button == 2)298{299// Maintain the current selection if right clicking into it.300if (newAddress >= selectRangeStart && newAddress < selectRangeEnd)301extend = true;302}303304setCurAddress(newAddress,extend);305306SetFocus(wnd);307redraw();308}309310void CtrlDisplayListView::onMouseUp(WPARAM wParam, LPARAM lParam, int button)311{312if (!validDisplayList || !gpuDebug) {313return;314}315316if (button == 2)317{318HMENU menu = GetContextMenu(ContextMenuID::DISPLAYLISTVIEW);319EnableMenuItem(menu, ID_GEDBG_SETCOND, gpuDebug->GetBreakpoints()->IsAddressBreakpoint(curAddress) ? MF_ENABLED : MF_GRAYED);320321switch (TriggerContextMenu(ContextMenuID::DISPLAYLISTVIEW, wnd, ContextPoint::FromEvent(lParam)))322{323case ID_DISASM_GOTOINMEMORYVIEW:324if (memoryWindow)325memoryWindow->Goto(curAddress);326break;327case ID_DISASM_TOGGLEBREAKPOINT:328toggleBreakpoint();329redraw();330break;331case ID_GEDBG_SETCOND:332PromptBreakpointCond();333break;334case ID_DISASM_COPYINSTRUCTIONDISASM:335{336int space = 256 * (selectRangeEnd - selectRangeStart) / instructionSize;337char *temp = new char[space];338339char *p = temp, *end = temp + space;340for (u32 pos = selectRangeStart; pos < selectRangeEnd && p < end; pos += instructionSize)341{342u32 opcode = Memory::Read_U32(pos);343GPUDebugOp op = gpuDebug->DisassembleOp(pos, opcode);344p += snprintf(p, end - p, "%s\r\n", op.desc.c_str());345}346347W32Util::CopyTextToClipboard(wnd, temp);348delete [] temp;349}350break;351case ID_DISASM_COPYADDRESS:352{353char temp[16];354snprintf(temp,sizeof(temp),"%08X",curAddress);355W32Util::CopyTextToClipboard(wnd, temp);356}357break;358case ID_DISASM_SETPCTOHERE:359{360gpuDebug->ResetListPC(list.id,curAddress);361list.pc = curAddress;362redraw();363}364break;365case ID_GEDBG_SETSTALLADDR:366{367gpuDebug->ResetListStall(list.id,curAddress);368list.stall = curAddress;369redraw();370}371break;372case ID_DISASM_COPYINSTRUCTIONHEX:373{374int space = 24 * (selectRangeEnd - selectRangeStart) / instructionSize;375char *temp = new char[space];376377char *p = temp, *end = temp + space;378for (u32 pos = selectRangeStart; pos < selectRangeEnd && p < end; pos += instructionSize)379p += snprintf(p, end - p, "%08X\r\n", Memory::ReadUnchecked_U32(pos));380381W32Util::CopyTextToClipboard(wnd, temp);382delete [] temp;383}384break;385case ID_DISASM_RUNTOHERE:386{387SendMessage(GetParent(wnd),WM_GEDBG_RUNTOWPARAM,curAddress,0);388redraw();389}390break;391case ID_GEDBG_GOTOPC:392setCurAddress(list.pc);393scrollAddressIntoView();394redraw();395break;396case ID_GEDBG_GOTOADDR:397{398std::string expression = StringFromFormat("%08x", curAddress);399if (!InputBox_GetString(GetModuleHandle(NULL), wnd, L"Address", expression, expression, InputBoxFlags::Selected)) {400break;401}402uint32_t newAddress = curAddress;403if (!GPUDebugExecExpression(gpuDebug, expression.c_str(), newAddress)) {404MessageBox(wnd, ConvertUTF8ToWString(getExpressionError()).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);405break;406}407if (!Memory::IsValidAddress(newAddress)) {408MessageBox(wnd, L"Address not in valid memory", L"Invalid address", MB_OK | MB_ICONEXCLAMATION);409break;410}411412setCurAddress(newAddress);413scrollAddressIntoView();414redraw();415}416break;417}418return;419}420421redraw();422}423424void CtrlDisplayListView::onVScroll(WPARAM wParam, LPARAM lParam)425{426switch (wParam & 0xFFFF)427{428case SB_LINEDOWN:429windowStart += instructionSize;430break;431case SB_LINEUP:432windowStart -= instructionSize;433break;434case SB_PAGEDOWN:435windowStart += visibleRows*instructionSize;436break;437case SB_PAGEUP:438windowStart -= visibleRows*instructionSize;439break;440default:441return;442}443redraw();444}445446void CtrlDisplayListView::onKeyDown(WPARAM wParam, LPARAM lParam)447{448u32 windowEnd = windowStart+visibleRows*instructionSize;449450switch (wParam & 0xFFFF)451{452case VK_DOWN:453setCurAddress(curAddress + instructionSize, KeyDownAsync(VK_SHIFT));454scrollAddressIntoView();455break;456case VK_UP:457setCurAddress(curAddress - instructionSize, KeyDownAsync(VK_SHIFT));458scrollAddressIntoView();459break;460case VK_NEXT:461if (curAddress != windowEnd - instructionSize && curAddressIsVisible()) {462setCurAddress(windowEnd - instructionSize, KeyDownAsync(VK_SHIFT));463scrollAddressIntoView();464} else {465setCurAddress(curAddress + visibleRows * instructionSize, KeyDownAsync(VK_SHIFT));466scrollAddressIntoView();467}468break;469case VK_PRIOR:470if (curAddress != windowStart && curAddressIsVisible()) {471setCurAddress(windowStart, KeyDownAsync(VK_SHIFT));472scrollAddressIntoView();473} else {474setCurAddress(curAddress - visibleRows * instructionSize, KeyDownAsync(VK_SHIFT));475scrollAddressIntoView();476}477break;478case VK_LEFT:479gotoAddr(list.pc);480return;481case VK_SPACE:482toggleBreakpoint();483break;484case VK_F10:485case VK_F11:486SendMessage(GetParent(wnd),WM_GEDBG_STEPDISPLAYLIST,0,0);487break;488}489redraw();490}491492void CtrlDisplayListView::scrollAddressIntoView()493{494u32 windowEnd = windowStart + visibleRows * instructionSize;495496if (curAddress < windowStart)497windowStart = curAddress;498else if (curAddress >= windowEnd)499windowStart = curAddress - visibleRows * instructionSize + instructionSize;500}501502bool CtrlDisplayListView::curAddressIsVisible()503{504u32 windowEnd = windowStart + visibleRows * instructionSize;505return curAddress >= windowStart && curAddress < windowEnd;506}507508509