1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles, Vas Crabb
3 //============================================================
4 //
5 //  debugview.c - Win32 debug window handling
6 //
7 //============================================================
8 
9 #include "emu.h"
10 #include "debugviewinfo.h"
11 
12 #include "debugwininfo.h"
13 #include "uimetrics.h"
14 #include "debugger.h"
15 #include "debug/debugcon.h"
16 #include "debug/debugcpu.h"
17 
18 #include "strconv.h"
19 
20 #include "winutil.h"
21 
22 
23 // debugger view styles
24 #define DEBUG_VIEW_STYLE    WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN
25 #define DEBUG_VIEW_STYLE_EX 0
26 
27 // combo box styles
28 #define COMBO_BOX_STYLE     WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL
29 #define COMBO_BOX_STYLE_EX  0
30 
31 // horizontal scroll bar styles
32 #define HSCROLL_STYLE       WS_CHILD | WS_VISIBLE | SBS_HORZ
33 #define HSCROLL_STYLE_EX    0
34 
35 // vertical scroll bar styles
36 #define VSCROLL_STYLE       WS_CHILD | WS_VISIBLE | SBS_VERT
37 #define VSCROLL_STYLE_EX    0
38 
39 
40 bool debugview_info::s_window_class_registered = false;
41 
42 
debugview_info(debugger_windows_interface & debugger,debugwin_info & owner,HWND parent,debug_view_type type)43 debugview_info::debugview_info(debugger_windows_interface &debugger, debugwin_info &owner, HWND parent, debug_view_type type) :
44 	debugbase_info(debugger),
45 	m_owner(owner),
46 	m_view(nullptr),
47 	m_wnd(nullptr),
48 	m_hscroll(nullptr),
49 	m_vscroll(nullptr)
50 {
51 	register_window_class();
52 
53 	// create the child view
54 	m_wnd = CreateWindowEx(DEBUG_VIEW_STYLE_EX, TEXT("MAMEDebugView"), nullptr, DEBUG_VIEW_STYLE,
55 			0, 0, 100, 100, parent, nullptr, GetModuleHandleUni(), this);
56 	if (m_wnd == nullptr)
57 		goto cleanup;
58 
59 	// create the scroll bars
60 	m_hscroll = CreateWindowEx(HSCROLL_STYLE_EX, TEXT("SCROLLBAR"), nullptr, HSCROLL_STYLE,
61 			0, 0, 100, CW_USEDEFAULT, m_wnd, nullptr, GetModuleHandleUni(), this);
62 	m_vscroll = CreateWindowEx(VSCROLL_STYLE_EX, TEXT("SCROLLBAR"), nullptr, VSCROLL_STYLE,
63 			0, 0, CW_USEDEFAULT, 100, m_wnd, nullptr, GetModuleHandleUni(), this);
64 	if ((m_hscroll == nullptr) || (m_vscroll == nullptr))
65 		goto cleanup;
66 
67 	// create the debug view
68 	m_view = machine().debug_view().alloc_view(type, &debugview_info::static_update, this);
69 	if (m_view == nullptr)
70 		goto cleanup;
71 
72 	return;
73 
74 cleanup:
75 	if (m_hscroll != nullptr)
76 		DestroyWindow(m_hscroll);
77 	m_hscroll = nullptr;
78 	if (m_vscroll != nullptr)
79 		DestroyWindow(m_vscroll);
80 	m_vscroll = nullptr;
81 	if (m_wnd != nullptr)
82 		DestroyWindow(m_wnd);
83 	m_wnd = nullptr;
84 	if (m_view != nullptr)
85 		machine().debug_view().free_view(*m_view);
86 	m_view = nullptr;
87 }
88 
89 
~debugview_info()90 debugview_info::~debugview_info()
91 {
92 	if (m_wnd != nullptr)
93 		DestroyWindow(m_wnd);
94 	if (m_view)
95 		machine().debug_view().free_view(*m_view);
96 }
97 
98 
is_valid() const99 bool debugview_info::is_valid() const
100 {
101 	return m_view && m_hscroll && m_vscroll && m_wnd;
102 }
103 
104 
prefwidth() const105 uint32_t debugview_info::prefwidth() const
106 {
107 	return (m_view->total_size().x * metrics().debug_font_width()) + metrics().vscroll_width();
108 }
109 
110 
maxwidth()111 uint32_t debugview_info::maxwidth()
112 {
113 	uint32_t max = m_view->total_size().x;
114 	debug_view_source const *const cursource = m_view->source();
115 	for (auto &source : m_view->source_list())
116 	{
117 		m_view->set_source(*source);
118 		uint32_t const chars = m_view->total_size().x;
119 		if (max < chars)
120 			max = chars;
121 	}
122 	if (cursource != nullptr)
123 		m_view->set_source(*cursource);
124 	return (max * metrics().debug_font_width()) + metrics().vscroll_width();
125 }
126 
127 
get_bounds(RECT & bounds) const128 void debugview_info::get_bounds(RECT &bounds) const
129 {
130 	GetWindowRect(m_wnd, &bounds);
131 }
132 
133 
set_bounds(RECT const & newbounds)134 void debugview_info::set_bounds(RECT const &newbounds)
135 {
136 	// account for the edges and set the bounds
137 	if (m_wnd)
138 		smart_set_window_bounds(m_wnd, GetParent(m_wnd), newbounds);
139 
140 	// update
141 	update();
142 }
143 
144 
send_vscroll(int delta)145 void debugview_info::send_vscroll(int delta)
146 {
147 	if (m_vscroll)
148 	{
149 		int message_type = SB_LINEUP;
150 		if (delta < 0)
151 		{
152 			message_type = SB_LINEDOWN;
153 			delta = -delta;
154 		}
155 		while (delta > 0)
156 		{
157 			SendMessage(m_wnd, WM_VSCROLL, message_type, (LPARAM)m_vscroll);
158 			delta--;
159 		}
160 	}
161 }
162 
163 
send_pageup()164 void debugview_info::send_pageup()
165 {
166 	if (m_vscroll)
167 	{
168 		SendMessage(m_wnd, WM_VSCROLL, SB_PAGELEFT, (LPARAM)m_vscroll);
169 	}
170 }
171 
172 
send_pagedown()173 void debugview_info::send_pagedown()
174 {
175 	if (m_vscroll)
176 	{
177 		SendMessage(m_wnd, WM_VSCROLL, SB_PAGERIGHT, (LPARAM)m_vscroll);
178 	}
179 }
180 
181 
source_name() const182 char const *debugview_info::source_name() const
183 {
184 	if (m_view != nullptr)
185 	{
186 		debug_view_source const *const source = m_view->source();
187 		if (source != nullptr)
188 			return source->name();
189 	}
190 	return "";
191 }
192 
193 
source_device() const194 device_t *debugview_info::source_device() const
195 {
196 	if (m_view != nullptr)
197 	{
198 		debug_view_source const *const source = m_view->source();
199 		if (source != nullptr)
200 			return source->device();
201 	}
202 	return nullptr;
203 }
204 
205 
source_is_visible_cpu() const206 bool debugview_info::source_is_visible_cpu() const
207 {
208 	if (m_view != nullptr)
209 	{
210 		const debug_view_source *const source = m_view->source();
211 		return (source != nullptr) && (machine().debugger().console().get_visible_cpu() == source->device());
212 	}
213 	return false;
214 }
215 
216 
set_source_index(int index)217 bool debugview_info::set_source_index(int index)
218 {
219 	if (m_view != nullptr)
220 	{
221 		const debug_view_source *const source = m_view->source(index);
222 		if (source != nullptr)
223 		{
224 			m_view->set_source(*source);
225 			return true;
226 		}
227 	}
228 	return false;
229 }
230 
231 
set_source_for_device(device_t & device)232 bool debugview_info::set_source_for_device(device_t &device)
233 {
234 	if (m_view != nullptr)
235 	{
236 		const debug_view_source *const source = m_view->source_for_device(&device);
237 		if (source != nullptr)
238 		{
239 			m_view->set_source(*source);
240 			return true;
241 		}
242 	}
243 	return false;
244 }
245 
246 
set_source_for_visible_cpu()247 bool debugview_info::set_source_for_visible_cpu()
248 {
249 	device_t *const curcpu = machine().debugger().console().get_visible_cpu();
250 	if (curcpu != nullptr)
251 		return set_source_for_device(*curcpu);
252 	else
253 		return false;
254 }
255 
256 
create_source_combobox(HWND parent,LONG_PTR userdata)257 HWND debugview_info::create_source_combobox(HWND parent, LONG_PTR userdata)
258 {
259 	// create a combo box
260 	HWND const result = CreateWindowEx(COMBO_BOX_STYLE_EX, TEXT("COMBOBOX"), nullptr, COMBO_BOX_STYLE,
261 			0, 0, 100, 1000, parent, nullptr, GetModuleHandleUni(), nullptr);
262 	SetWindowLongPtr(result, GWLP_USERDATA, userdata);
263 	SendMessage(result, WM_SETFONT, (WPARAM)metrics().debug_font(), (LPARAM)FALSE);
264 
265 	// populate the combobox
266 	debug_view_source const *const cursource = m_view->source();
267 	int maxlength = 0;
268 	for (auto &source : m_view->source_list())
269 	{
270 		int const length = strlen(source->name());
271 		if (length > maxlength)
272 			maxlength = length;
273 		auto t_name = osd::text::to_tstring(source->name());
274 		SendMessage(result, CB_ADDSTRING, 0, (LPARAM)t_name.c_str());
275 	}
276 	if (cursource != nullptr)
277 	{
278 		SendMessage(result, CB_SETCURSEL, m_view->source_index(*cursource), 0);
279 		SendMessage(result, CB_SETDROPPEDWIDTH, ((maxlength + 2) * metrics().debug_font_width()) + metrics().vscroll_width(), 0);
280 		m_view->set_source(*cursource);
281 	}
282 	return result;
283 }
284 
285 
draw_contents(HDC windc)286 void debugview_info::draw_contents(HDC windc)
287 {
288 	debug_view_char const *viewdata = m_view->viewdata();
289 	debug_view_xy const visarea = m_view->visible_size();
290 
291 	// get the client rect
292 	RECT client;
293 	GetClientRect(m_wnd, &client);
294 
295 	// create a compatible DC and an offscreen bitmap
296 	HDC const dc = CreateCompatibleDC(windc);
297 	if (dc == nullptr)
298 		return;
299 	HBITMAP const bitmap = CreateCompatibleBitmap(windc, client.right, client.bottom);
300 	if (bitmap == nullptr)
301 	{
302 		DeleteDC(dc);
303 		return;
304 	}
305 	HGDIOBJ const oldbitmap = SelectObject(dc, bitmap);
306 
307 	// set the font
308 	HGDIOBJ const oldfont = SelectObject(dc, metrics().debug_font());
309 	COLORREF const oldfgcolor = GetTextColor(dc);
310 	int const oldbkmode = GetBkMode(dc);
311 	SetBkMode(dc, TRANSPARENT);
312 
313 	// iterate over rows and columns
314 	for (uint32_t row = 0; row < visarea.y; row++)
315 	{
316 		// loop twice; once to fill the background and once to draw the text
317 		for (int iter = 0; iter < 2; iter++)
318 		{
319 			COLORREF fgcolor;
320 			COLORREF bgcolor = RGB(0xff,0xff,0xff);
321 			HBRUSH bgbrush = nullptr;
322 			int last_attrib = -1;
323 			TCHAR buffer[256];
324 			int count = 0;
325 			RECT bounds;
326 
327 			// initialize the text bounds
328 			bounds.left = bounds.right = 0;
329 			bounds.top = row * metrics().debug_font_height();
330 			bounds.bottom = bounds.top + metrics().debug_font_height();
331 
332 			// start with a brush on iteration #0
333 			if (iter == 0)
334 				bgbrush = CreateSolidBrush(bgcolor);
335 
336 			// iterate over columns
337 			for (uint32_t col = 0; col < visarea.x; col++)
338 			{
339 				// if the attribute changed, adjust the colors
340 				if (viewdata[col].attrib != last_attrib)
341 				{
342 					COLORREF oldbg = bgcolor;
343 
344 					// reset to standard colors
345 					fgcolor = RGB(0x00,0x00,0x00);
346 					bgcolor = RGB(0xff,0xff,0xff);
347 
348 					// pick new fg/bg colors
349 					if (viewdata[col].attrib & DCA_VISITED) bgcolor = RGB(0xc6, 0xe2, 0xff);
350 					if (viewdata[col].attrib & DCA_ANCILLARY) bgcolor = RGB(0xe0,0xe0,0xe0);
351 					if (viewdata[col].attrib & DCA_SELECTED) bgcolor = RGB(0xff,0x80,0x80);
352 					if (viewdata[col].attrib & DCA_CURRENT) bgcolor = RGB(0xff,0xff,0x00);
353 					if ((viewdata[col].attrib & DCA_SELECTED) && (viewdata[col].attrib & DCA_CURRENT)) bgcolor = RGB(0xff,0xc0,0x80);
354 					if (viewdata[col].attrib & DCA_CHANGED) fgcolor = RGB(0xff,0x00,0x00);
355 					if (viewdata[col].attrib & DCA_INVALID) fgcolor = RGB(0x00,0x00,0xff);
356 					if (viewdata[col].attrib & DCA_DISABLED) fgcolor = RGB((GetRValue(fgcolor) + GetRValue(bgcolor)) / 2, (GetGValue(fgcolor) + GetGValue(bgcolor)) / 2, (GetBValue(fgcolor) + GetBValue(bgcolor)) / 2);
357 					if (viewdata[col].attrib & DCA_COMMENT) fgcolor = RGB(0x00,0x80,0x00);
358 
359 					// flush any pending drawing
360 					if (count > 0)
361 					{
362 						bounds.right = bounds.left + (count * metrics().debug_font_width());
363 						if (iter == 0)
364 							FillRect(dc, &bounds, bgbrush);
365 						else
366 							ExtTextOut(dc, bounds.left, bounds.top, 0, nullptr, buffer, count, nullptr);
367 						bounds.left = bounds.right;
368 						count = 0;
369 					}
370 
371 					// set the new colors
372 					if (iter == 0 && oldbg != bgcolor)
373 					{
374 						DeleteObject(bgbrush);
375 						bgbrush = CreateSolidBrush(bgcolor);
376 					}
377 					else if (iter == 1)
378 						SetTextColor(dc, fgcolor);
379 					last_attrib = viewdata[col].attrib;
380 				}
381 
382 				// add this character to the buffer
383 				buffer[count++] = viewdata[col].byte;
384 			}
385 
386 			// flush any remaining stuff
387 			if (count > 0)
388 			{
389 				bounds.right = bounds.left + (count * metrics().debug_font_width());
390 				if (iter == 0)
391 					FillRect(dc, &bounds, bgbrush);
392 				else
393 					ExtTextOut(dc, bounds.left, bounds.top, 0, nullptr, buffer, count, nullptr);
394 			}
395 
396 			// erase to the end of the line
397 			if (iter == 0)
398 			{
399 				bounds.left = bounds.right;
400 				bounds.right = client.right;
401 				FillRect(dc, &bounds, bgbrush);
402 				DeleteObject(bgbrush);
403 			}
404 		}
405 
406 		// advance viewdata
407 		viewdata += visarea.x;
408 	}
409 
410 	// erase anything beyond the bottom with white
411 	GetClientRect(m_wnd, &client);
412 	client.top = visarea.y * metrics().debug_font_height();
413 	FillRect(dc, &client, (HBRUSH)GetStockObject(WHITE_BRUSH));
414 
415 	// reset the font
416 	SetBkMode(dc, oldbkmode);
417 	SetTextColor(dc, oldfgcolor);
418 	SelectObject(dc, oldfont);
419 
420 	// blit the final results
421 	BitBlt(windc, 0, 0, client.right, client.bottom, dc, 0, 0, SRCCOPY);
422 
423 	// undo the offscreen stuff
424 	SelectObject(dc, oldbitmap);
425 	DeleteObject(bitmap);
426 	DeleteDC(dc);
427 }
428 
429 
update()430 void debugview_info::update()
431 {
432 	RECT bounds, vscroll_bounds, hscroll_bounds;
433 	debug_view_xy totalsize, visiblesize, topleft;
434 	bool show_vscroll, show_hscroll;
435 	SCROLLINFO scrollinfo;
436 
437 	// get the view window bounds
438 	GetClientRect(m_wnd, &bounds);
439 	visiblesize.x = (bounds.right - bounds.left) / metrics().debug_font_width();
440 	visiblesize.y = (bounds.bottom - bounds.top) / metrics().debug_font_height();
441 
442 	// get the updated total rows/cols and left row/col
443 	totalsize = m_view->total_size();
444 	topleft = m_view->visible_position();
445 
446 	// determine if we need to show the scrollbars
447 	show_vscroll = show_hscroll = false;
448 	if (totalsize.x > visiblesize.x && bounds.bottom >= metrics().hscroll_height())
449 	{
450 		bounds.bottom -= metrics().hscroll_height();
451 		visiblesize.y = (bounds.bottom - bounds.top) / metrics().debug_font_height();
452 		show_hscroll = true;
453 	}
454 	if (totalsize.y > visiblesize.y && bounds.right >= metrics().vscroll_width())
455 	{
456 		bounds.right -= metrics().vscroll_width();
457 		visiblesize.x = (bounds.right - bounds.left) / metrics().debug_font_width();
458 		show_vscroll = true;
459 	}
460 	if (!show_vscroll && totalsize.y > visiblesize.y && bounds.right >= metrics().vscroll_width())
461 	{
462 		bounds.right -= metrics().vscroll_width();
463 		visiblesize.x = (bounds.right - bounds.left) / metrics().debug_font_width();
464 		show_vscroll = true;
465 	}
466 
467 	// compute the bounds of the scrollbars
468 	GetClientRect(m_wnd, &vscroll_bounds);
469 	vscroll_bounds.left = vscroll_bounds.right - metrics().vscroll_width();
470 	if (show_hscroll)
471 		vscroll_bounds.bottom -= metrics().hscroll_height();
472 
473 	GetClientRect(m_wnd, &hscroll_bounds);
474 	hscroll_bounds.top = hscroll_bounds.bottom - metrics().hscroll_height();
475 	if (show_vscroll)
476 		hscroll_bounds.right -= metrics().vscroll_width();
477 
478 	// if we hid the scrollbars, make sure we reset the top/left corners
479 	if (topleft.y + visiblesize.y > totalsize.y)
480 		topleft.y = std::max(totalsize.y - visiblesize.y, 0);
481 	if (topleft.x + visiblesize.x > totalsize.x)
482 		topleft.x = std::max(totalsize.x - visiblesize.x, 0);
483 
484 	// fill out the scroll info struct for the vertical scrollbar
485 	scrollinfo.cbSize = sizeof(scrollinfo);
486 	scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
487 	scrollinfo.nMin = 0;
488 	scrollinfo.nMax = totalsize.y - 1;
489 	scrollinfo.nPage = visiblesize.y;
490 	scrollinfo.nPos = topleft.y;
491 	SetScrollInfo(m_vscroll, SB_CTL, &scrollinfo, TRUE);
492 
493 	// fill out the scroll info struct for the horizontal scrollbar
494 	scrollinfo.cbSize = sizeof(scrollinfo);
495 	scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
496 	scrollinfo.nMin = 0;
497 	scrollinfo.nMax = totalsize.x - 1;
498 	scrollinfo.nPage = visiblesize.x;
499 	scrollinfo.nPos = topleft.x;
500 	SetScrollInfo(m_hscroll, SB_CTL, &scrollinfo, TRUE);
501 
502 	// update window info
503 	visiblesize.y++;
504 	visiblesize.x++;
505 	m_view->set_visible_size(visiblesize);
506 	m_view->set_visible_position(topleft);
507 
508 	// invalidate the bounds
509 	InvalidateRect(m_wnd, nullptr, FALSE);
510 
511 	// adjust the bounds of the scrollbars and show/hide them
512 	if (m_vscroll)
513 	{
514 		if (show_vscroll)
515 			smart_set_window_bounds(m_vscroll, m_wnd, vscroll_bounds);
516 		smart_show_window(m_vscroll, show_vscroll);
517 	}
518 	if (m_hscroll)
519 	{
520 		if (show_hscroll)
521 			smart_set_window_bounds(m_hscroll, m_wnd, hscroll_bounds);
522 		smart_show_window(m_hscroll, show_hscroll);
523 	}
524 }
525 
526 
process_scroll(WORD type,HWND wnd)527 uint32_t debugview_info::process_scroll(WORD type, HWND wnd)
528 {
529 	// get the current info
530 	SCROLLINFO scrollinfo;
531 	scrollinfo.cbSize = sizeof(scrollinfo);
532 	scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
533 	GetScrollInfo(wnd, SB_CTL, &scrollinfo);
534 
535 	// by default we stay put
536 	int32_t result = scrollinfo.nPos;
537 
538 	// determine the maximum value
539 	int32_t const maxval = (scrollinfo.nMax > scrollinfo.nPage) ? (scrollinfo.nMax - scrollinfo.nPage + 1) : 0;
540 
541 	// handle the message
542 	switch (type)
543 	{
544 	case SB_THUMBTRACK:
545 		result = scrollinfo.nTrackPos;
546 		break;
547 
548 	case SB_LEFT:
549 		result = 0;
550 		break;
551 
552 	case SB_RIGHT:
553 		result = maxval;
554 		break;
555 
556 	case SB_LINELEFT:
557 		result -= 1;
558 		break;
559 
560 	case SB_LINERIGHT:
561 		result += 1;
562 		break;
563 
564 	case SB_PAGELEFT:
565 		result -= scrollinfo.nPage - 1;
566 		break;
567 
568 	case SB_PAGERIGHT:
569 		result += scrollinfo.nPage - 1;
570 		break;
571 	}
572 
573 	// generic rangecheck
574 	if (result < 0)
575 		result = 0;
576 	if (result > maxval)
577 		result = maxval;
578 
579 	// set the new position
580 	scrollinfo.fMask = SIF_POS;
581 	scrollinfo.nPos = result;
582 	SetScrollInfo(wnd, SB_CTL, &scrollinfo, TRUE);
583 
584 	return (uint32_t)result;
585 }
586 
587 
view_proc(UINT message,WPARAM wparam,LPARAM lparam)588 LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam)
589 {
590 	// handle a few messages
591 	switch (message)
592 	{
593 	// paint: redraw the last bitmap
594 	case WM_PAINT:
595 		{
596 			PAINTSTRUCT pstruct;
597 			HDC const dc = BeginPaint(m_wnd, &pstruct);
598 			draw_contents(dc);
599 			EndPaint(m_wnd, &pstruct);
600 			break;
601 		}
602 
603 	// keydown: handle debugger keys
604 	case WM_SYSKEYDOWN:
605 		if (wparam != VK_F10)
606 			return DefWindowProc(m_wnd, message, wparam, lparam);
607 			// (fall through)
608 	case WM_KEYDOWN:
609 		{
610 			if (m_owner.handle_key(wparam, lparam))
611 			{
612 				m_owner.set_ignore_char_lparam(lparam);
613 			}
614 			else
615 			{
616 				switch (wparam)
617 				{
618 				case VK_UP:
619 					m_view->process_char(DCH_UP);
620 					m_owner.set_ignore_char_lparam(lparam);
621 					break;
622 
623 				case VK_DOWN:
624 					m_view->process_char(DCH_DOWN);
625 					m_owner.set_ignore_char_lparam(lparam);
626 					break;
627 
628 				case VK_LEFT:
629 					if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
630 						m_view->process_char(DCH_CTRLLEFT);
631 					else
632 						m_view->process_char(DCH_LEFT);
633 					m_owner.set_ignore_char_lparam(lparam);
634 					break;
635 
636 				case VK_RIGHT:
637 					if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
638 						m_view->process_char(DCH_CTRLRIGHT);
639 					else
640 						m_view->process_char(DCH_RIGHT);
641 					m_owner.set_ignore_char_lparam(lparam);
642 					break;
643 
644 				case VK_PRIOR:
645 					m_view->process_char(DCH_PUP);
646 					m_owner.set_ignore_char_lparam(lparam);
647 					break;
648 
649 				case VK_NEXT:
650 					m_view->process_char(DCH_PDOWN);
651 					m_owner.set_ignore_char_lparam(lparam);
652 					break;
653 
654 				case VK_HOME:
655 					if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
656 						m_view->process_char(DCH_CTRLHOME);
657 					else
658 						m_view->process_char(DCH_HOME);
659 					m_owner.set_ignore_char_lparam(lparam);
660 					break;
661 
662 				case VK_END:
663 					if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
664 						m_view->process_char(DCH_CTRLEND);
665 					else
666 						m_view->process_char(DCH_END);
667 					m_owner.set_ignore_char_lparam(lparam);
668 					break;
669 
670 				case VK_ESCAPE:
671 					m_owner.set_default_focus();
672 					m_owner.set_ignore_char_lparam(lparam);
673 					break;
674 
675 				case VK_TAB:
676 					if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
677 						m_owner.prev_view(this);
678 					else
679 						m_owner.next_view(this);
680 					m_owner.set_ignore_char_lparam(lparam);
681 					break;
682 				}
683 			}
684 			break;
685 		}
686 
687 	// char: ignore chars associated with keys we've handled
688 	case WM_CHAR:
689 		if (m_owner.check_ignore_char_lparam(lparam))
690 		{
691 			if (waiting_for_debugger() || !seq_pressed())
692 			{
693 				if (wparam >= 32 && wparam < 127)
694 				{
695 					if (m_view->cursor_supported())
696 						m_view->set_cursor_visible(true);
697 					m_view->process_char(wparam);
698 				}
699 				else
700 				{
701 					return DefWindowProc(m_wnd, message, wparam, lparam);
702 				}
703 			}
704 		}
705 		break;
706 
707 	// gaining focus
708 	case WM_SETFOCUS:
709 		if (m_view->cursor_supported())
710 			m_view->set_cursor_visible(true);
711 		break;
712 
713 	// losing focus
714 	case WM_KILLFOCUS:
715 		if (m_view->cursor_supported())
716 			m_view->set_cursor_visible(false);
717 		break;
718 
719 	// mouse click
720 	case WM_LBUTTONDOWN:
721 		{
722 			debug_view_xy topleft = m_view->visible_position();
723 			debug_view_xy newpos;
724 			newpos.x = topleft.x + GET_X_LPARAM(lparam) / metrics().debug_font_width();
725 			newpos.y = topleft.y + GET_Y_LPARAM(lparam) / metrics().debug_font_height();
726 			m_view->process_click(DCK_LEFT_CLICK, newpos);
727 			SetFocus(m_wnd);
728 			break;
729 		}
730 
731 	// hscroll
732 	case WM_HSCROLL:
733 		{
734 			debug_view_xy topleft = m_view->visible_position();
735 			topleft.x = process_scroll(LOWORD(wparam), (HWND)lparam);
736 			m_view->set_visible_position(topleft);
737 			machine().debug_view().flush_osd_updates();
738 			break;
739 		}
740 
741 	// vscroll
742 	case WM_VSCROLL:
743 		{
744 			debug_view_xy topleft = m_view->visible_position();
745 			topleft.y = process_scroll(LOWORD(wparam), (HWND)lparam);
746 			m_view->set_visible_position(topleft);
747 			machine().debug_view().flush_osd_updates();
748 			break;
749 		}
750 
751 	// everything else: defaults
752 	default:
753 		return DefWindowProc(m_wnd, message, wparam, lparam);
754 	}
755 
756 	return 0;
757 }
758 
759 
static_update(debug_view & view,void * osdprivate)760 void debugview_info::static_update(debug_view &view, void *osdprivate)
761 {
762 	auto *const info = (debugview_info *)osdprivate;
763 	assert(info->m_view == &view);
764 	info->update();
765 }
766 
767 
static_view_proc(HWND wnd,UINT message,WPARAM wparam,LPARAM lparam)768 LRESULT CALLBACK debugview_info::static_view_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
769 {
770 	if (message == WM_CREATE)
771 	{
772 		// set the info pointer
773 		CREATESTRUCT const *const createinfo = (CREATESTRUCT *)lparam;
774 		SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)createinfo->lpCreateParams);
775 		return 0;
776 	}
777 
778 	auto *const info = (debugview_info *)(uintptr_t)GetWindowLongPtr(wnd, GWLP_USERDATA);
779 	if (info == nullptr)
780 		return DefWindowProc(wnd, message, wparam, lparam);
781 
782 	assert((info->m_wnd == wnd) || (info->m_wnd == nullptr));
783 	return info->view_proc(message, wparam, lparam);
784 }
785 
786 
register_window_class()787 void debugview_info::register_window_class()
788 {
789 	if (!s_window_class_registered)
790 	{
791 		WNDCLASS wc = { 0 };
792 
793 		// initialize the description of the window class
794 		wc.lpszClassName    = TEXT("MAMEDebugView");
795 		wc.hInstance        = GetModuleHandleUni();
796 		wc.lpfnWndProc      = &debugview_info::static_view_proc;
797 		wc.hCursor          = LoadCursor(nullptr, IDC_ARROW);
798 		wc.hIcon            = LoadIcon(wc.hInstance, MAKEINTRESOURCE(2));
799 		wc.lpszMenuName     = nullptr;
800 		wc.hbrBackground    = nullptr;
801 		wc.style            = 0;
802 		wc.cbClsExtra       = 0;
803 		wc.cbWndExtra       = 0;
804 
805 		UnregisterClass(wc.lpszClassName, wc.hInstance);
806 
807 		// register the class; fail if we can't
808 		if (!RegisterClass(&wc))
809 			fatalerror("Unable to register debug view class\n");
810 
811 		s_window_class_registered = true;
812 	}
813 }
814