1// license:BSD-3-Clause 2// copyright-holders:Vas Crabb 3//============================================================ 4// 5// debugwindowhandler.m - MacOS X Cocoa debug window handling 6// 7//============================================================ 8 9#include "emu.h" 10#import "debugwindowhandler.h" 11 12#import "debugconsole.h" 13#import "debugcommandhistory.h" 14#import "debugview.h" 15 16#include "debugger.h" 17#include "debug/debugcon.h" 18 19#include "util/xmlfile.h" 20 21 22//============================================================ 23// NOTIFICATIONS 24//============================================================ 25 26NSString *const MAMEHideDebuggerNotification = @"MAMEHideDebuggerNotification"; 27NSString *const MAMEShowDebuggerNotification = @"MAMEShowDebuggerNotification"; 28NSString *const MAMEAuxiliaryDebugWindowWillCloseNotification = @"MAMEAuxiliaryDebugWindowWillCloseNotification"; 29NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerConfigurationNotification"; 30 31 32//============================================================ 33// MAMEDebugWindowHandler class 34//============================================================ 35 36@implementation MAMEDebugWindowHandler 37 38+ (void)addCommonActionItems:(NSMenu *)menu { 39 [menu addItemWithTitle:@"Break" 40 action:@selector(debugBreak:) 41 keyEquivalent:@""]; 42 43 NSMenuItem *runParentItem = [menu addItemWithTitle:@"Run" 44 action:@selector(debugRun:) 45 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF5FunctionKey]]; 46 NSMenu *runMenu = [[NSMenu alloc] initWithTitle:@"Run"]; 47 [runParentItem setSubmenu:runMenu]; 48 [runMenu release]; 49 [runParentItem setKeyEquivalentModifierMask:0]; 50 [[runMenu addItemWithTitle:@"and Hide Debugger" 51 action:@selector(debugRunAndHide:) 52 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF12FunctionKey]] 53 setKeyEquivalentModifierMask:0]; 54 [[runMenu addItemWithTitle:@"to Next CPU" 55 action:@selector(debugRunToNextCPU:) 56 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF6FunctionKey]] 57 setKeyEquivalentModifierMask:0]; 58 [[runMenu addItemWithTitle:@"until Next Interrupt on Current CPU" 59 action:@selector(debugRunToNextInterrupt:) 60 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF7FunctionKey]] 61 setKeyEquivalentModifierMask:0]; 62 [[runMenu addItemWithTitle:@"until Next VBLANK" 63 action:@selector(debugRunToNextVBLANK:) 64 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF8FunctionKey]] 65 setKeyEquivalentModifierMask:0]; 66 67 NSMenuItem *stepParentItem = [menu addItemWithTitle:@"Step" action:NULL keyEquivalent:@""]; 68 NSMenu *stepMenu = [[NSMenu alloc] initWithTitle:@"Step"]; 69 [stepParentItem setSubmenu:stepMenu]; 70 [stepMenu release]; 71 [[stepMenu addItemWithTitle:@"Into" 72 action:@selector(debugStepInto:) 73 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF11FunctionKey]] 74 setKeyEquivalentModifierMask:0]; 75 [[stepMenu addItemWithTitle:@"Over" 76 action:@selector(debugStepOver:) 77 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]] 78 setKeyEquivalentModifierMask:0]; 79 [[stepMenu addItemWithTitle:@"Out" 80 action:@selector(debugStepOut:) 81 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]] 82 setKeyEquivalentModifierMask:NSShiftKeyMask]; 83 84 NSMenuItem *resetParentItem = [menu addItemWithTitle:@"Reset" action:NULL keyEquivalent:@""]; 85 NSMenu *resetMenu = [[NSMenu alloc] initWithTitle:@"Reset"]; 86 [resetParentItem setSubmenu:resetMenu]; 87 [resetMenu release]; 88 [[resetMenu addItemWithTitle:@"Soft" 89 action:@selector(debugSoftReset:) 90 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]] 91 setKeyEquivalentModifierMask:0]; 92 [[resetMenu addItemWithTitle:@"Hard" 93 action:@selector(debugHardReset:) 94 keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]] 95 setKeyEquivalentModifierMask:NSShiftKeyMask]; 96 97 [menu addItem:[NSMenuItem separatorItem]]; 98 99 NSMenuItem *newParentItem = [menu addItemWithTitle:@"New" action:NULL keyEquivalent:@""]; 100 NSMenu *newMenu = [[NSMenu alloc] initWithTitle:@"New"]; 101 [newParentItem setSubmenu:newMenu]; 102 [newMenu release]; 103 [newMenu addItemWithTitle:@"Memory Window" 104 action:@selector(debugNewMemoryWindow:) 105 keyEquivalent:@"d"]; 106 [newMenu addItemWithTitle:@"Disassembly Window" 107 action:@selector(debugNewDisassemblyWindow:) 108 keyEquivalent:@"a"]; 109 [newMenu addItemWithTitle:@"Error Log Window" 110 action:@selector(debugNewErrorLogWindow:) 111 keyEquivalent:@"l"]; 112 [newMenu addItemWithTitle:@"(Break|Watch)points Window" 113 action:@selector(debugNewPointsWindow:) 114 keyEquivalent:@"b"]; 115 [newMenu addItemWithTitle:@"Devices Window" 116 action:@selector(debugNewDevicesWindow:) 117 keyEquivalent:@"D"]; 118 119 [menu addItem:[NSMenuItem separatorItem]]; 120 121 [menu addItemWithTitle:@"Close Window" action:@selector(performClose:) keyEquivalent:@"w"]; 122 [menu addItemWithTitle:@"Quit" action:@selector(debugExit:) keyEquivalent:@"q"]; 123} 124 125 126+ (NSPopUpButton *)newActionButtonWithFrame:(NSRect)frame { 127 NSPopUpButton *actionButton = [[NSPopUpButton alloc] initWithFrame:frame pullsDown:YES]; 128 [actionButton setTitle:@""]; 129 [actionButton addItemWithTitle:@""]; 130 [actionButton setBezelStyle:NSShadowlessSquareBezelStyle]; 131 [actionButton setFocusRingType:NSFocusRingTypeNone]; 132 [[actionButton cell] setArrowPosition:NSPopUpArrowAtCenter]; 133 [[self class] addCommonActionItems:[actionButton menu]]; 134 return actionButton; 135} 136 137 138- (id)initWithMachine:(running_machine &)m title:(NSString *)t { 139 if (!(self = [super init])) 140 return nil; 141 142 window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 320, 240) 143 styleMask:(NSTitledWindowMask | 144 NSClosableWindowMask | 145 NSMiniaturizableWindowMask | 146 NSResizableWindowMask) 147 backing:NSBackingStoreBuffered 148 defer:YES]; 149 [window setReleasedWhenClosed:NO]; 150 [window setDelegate:self]; 151 [window setTitle:t]; 152 [window setContentMinSize:NSMakeSize(320, 240)]; 153 154 [[NSNotificationCenter defaultCenter] addObserver:self 155 selector:@selector(saveConfig:) 156 name:MAMESaveDebuggerConfigurationNotification 157 object:nil]; 158 [[NSNotificationCenter defaultCenter] addObserver:self 159 selector:@selector(showDebugger:) 160 name:MAMEShowDebuggerNotification 161 object:nil]; 162 [[NSNotificationCenter defaultCenter] addObserver:self 163 selector:@selector(hideDebugger:) 164 name:MAMEHideDebuggerNotification 165 object:nil]; 166 167 machine = &m; 168 169 return self; 170} 171 172 173- (void)dealloc { 174 [[NSNotificationCenter defaultCenter] removeObserver:self]; 175 176 if (window != nil) 177 { 178 [window orderOut:self]; 179 [window release]; 180 } 181 182 [super dealloc]; 183} 184 185 186- (void)activate { 187 [window makeKeyAndOrderFront:self]; 188} 189 190 191- (IBAction)debugBreak:(id)sender { 192 if (machine->debug_flags & DEBUG_FLAG_ENABLED) 193 machine->debugger().console().get_visible_cpu()->debug()->halt_on_next_instruction("User-initiated break\n"); 194} 195 196 197- (IBAction)debugRun:(id)sender { 198 machine->debugger().console().get_visible_cpu()->debug()->go(); 199} 200 201 202- (IBAction)debugRunAndHide:(id)sender { 203 [[NSNotificationCenter defaultCenter] postNotificationName:MAMEHideDebuggerNotification 204 object:self 205 userInfo:[NSDictionary dictionaryWithObject:[NSValue valueWithPointer:machine] 206 forKey:@"MAMEDebugMachine"]]; 207 machine->debugger().console().get_visible_cpu()->debug()->go(); 208} 209 210 211- (IBAction)debugRunToNextCPU:(id)sender { 212 machine->debugger().console().get_visible_cpu()->debug()->go_next_device(); 213} 214 215 216- (IBAction)debugRunToNextInterrupt:(id)sender { 217 machine->debugger().console().get_visible_cpu()->debug()->go_interrupt(); 218} 219 220 221- (IBAction)debugRunToNextVBLANK:(id)sender { 222 machine->debugger().console().get_visible_cpu()->debug()->go_vblank(); 223} 224 225 226- (IBAction)debugStepInto:(id)sender { 227 machine->debugger().console().get_visible_cpu()->debug()->single_step(); 228} 229 230 231- (IBAction)debugStepOver:(id)sender { 232 machine->debugger().console().get_visible_cpu()->debug()->single_step_over(); 233} 234 235 236- (IBAction)debugStepOut:(id)sender { 237 machine->debugger().console().get_visible_cpu()->debug()->single_step_out(); 238} 239 240 241- (IBAction)debugSoftReset:(id)sender { 242 machine->schedule_soft_reset(); 243 machine->debugger().console().get_visible_cpu()->debug()->go(); 244} 245 246 247- (IBAction)debugHardReset:(id)sender { 248 machine->schedule_hard_reset(); 249} 250 251 252- (IBAction)debugExit:(id)sender { 253 machine->schedule_exit(); 254} 255 256 257- (void)showDebugger:(NSNotification *)notification { 258 running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue]; 259 if (m == machine) 260 { 261 if (![window isVisible] && ![window isMiniaturized]) 262 [window orderFront:self]; 263 } 264} 265 266 267- (void)hideDebugger:(NSNotification *)notification { 268 running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue]; 269 if (m == machine) 270 [window orderOut:self]; 271} 272 273 274- (void)saveConfig:(NSNotification *)notification { 275 running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue]; 276 if (m == machine) 277 { 278 util::xml::data_node *parentnode = (util::xml::data_node *)[[[notification userInfo] objectForKey:@"MAMEDebugParentNode"] pointerValue]; 279 util::xml::data_node *node = parentnode->add_child("window", nullptr); 280 if (node) 281 [self saveConfigurationToNode:node]; 282 } 283} 284 285 286- (void)saveConfigurationToNode:(util::xml::data_node *)node { 287 NSRect frame = [window frame]; 288 node->set_attribute_float("position_x", frame.origin.x); 289 node->set_attribute_float("position_y", frame.origin.y); 290 node->set_attribute_float("size_x", frame.size.width); 291 node->set_attribute_float("size_y", frame.size.height); 292} 293 294 295- (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { 296 NSRect frame = [window frame]; 297 frame.origin.x = node->get_attribute_float("position_x", frame.origin.x); 298 frame.origin.y = node->get_attribute_float("position_y", frame.origin.y); 299 frame.size.width = node->get_attribute_float("size_x", frame.size.width); 300 frame.size.height = node->get_attribute_float("size_y", frame.size.height); 301 302 NSSize min = [window minSize]; 303 frame.size.width = std::max(frame.size.width, min.width); 304 frame.size.height = std::max(frame.size.height, min.height); 305 306 NSSize max = [window maxSize]; 307 frame.size.width = std::min(frame.size.width, max.width); 308 frame.size.height = std::min(frame.size.height, max.height); 309 310 [window setFrame:frame display:YES]; 311} 312 313@end 314 315 316//============================================================ 317// MAMEAuxiliaryDebugWindowHandler class 318//============================================================ 319 320@implementation MAMEAuxiliaryDebugWindowHandler 321 322+ (void)cascadeWindow:(NSWindow *)window { 323 static NSPoint lastPosition = { 0, 0 }; 324 if (NSEqualPoints(lastPosition, NSZeroPoint)) { 325 NSRect available = [[NSScreen mainScreen] visibleFrame]; 326 lastPosition = NSMakePoint(available.origin.x + 12, available.origin.y + available.size.height - 8); 327 } 328 lastPosition = [window cascadeTopLeftFromPoint:lastPosition]; 329} 330 331 332- (id)initWithMachine:(running_machine &)m title:(NSString *)t console:(MAMEDebugConsole *)c { 333 if (!(self = [super initWithMachine:m title:t])) 334 return nil; 335 console = c; 336 return self; 337} 338 339 340- (void)dealloc { 341 [super dealloc]; 342} 343 344 345- (IBAction)debugNewMemoryWindow:(id)sender { 346 [console debugNewMemoryWindow:sender]; 347} 348 349 350- (IBAction)debugNewDisassemblyWindow:(id)sender { 351 [console debugNewDisassemblyWindow:sender]; 352} 353 354 355- (IBAction)debugNewErrorLogWindow:(id)sender { 356 [console debugNewErrorLogWindow:sender]; 357} 358 359 360- (IBAction)debugNewPointsWindow:(id)sender { 361 [console debugNewPointsWindow:sender]; 362} 363 364 365- (IBAction)debugNewDevicesWindow:(id)sender { 366 [console debugNewDevicesWindow:sender]; 367} 368 369 370- (void)windowWillClose:(NSNotification *)notification { 371 [[NSNotificationCenter defaultCenter] postNotificationName:MAMEAuxiliaryDebugWindowWillCloseNotification 372 object:self]; 373} 374 375- (void)cascadeWindowWithDesiredSize:(NSSize)desired forView:(NSView *)view { 376 // convert desired size to an adjustment and apply it to the current window frame 377 NSSize const current = [view frame].size; 378 desired.width -= current.width; 379 desired.height -= current.height; 380 NSRect windowFrame = [window frame]; 381 windowFrame.size.width += desired.width; 382 windowFrame.size.height += desired.height; 383 384 // limit the size to the minimum size 385 NSSize const minimum = [window minSize]; 386 windowFrame.size.width = std::max(windowFrame.size.width, minimum.width); 387 windowFrame.size.height = std::max(windowFrame.size.height, minimum.height); 388 389 // limit the size to the main screen size 390 NSRect const available = [[NSScreen mainScreen] visibleFrame]; 391 windowFrame.size.width = std::min(windowFrame.size.width, available.size.width); 392 windowFrame.size.height = std::min(windowFrame.size.height, available.size.height); 393 394 // arbitrary additional height limit 395 windowFrame.size.height = std::min(windowFrame.size.height, CGFloat(320)); 396 397 // place it in the bottom right corner and apply 398 windowFrame.origin.x = available.origin.x + available.size.width - windowFrame.size.width; 399 windowFrame.origin.y = available.origin.y; 400 [window setFrame:windowFrame display:YES]; 401 [[self class] cascadeWindow:window]; 402} 403 404@end 405 406 407//============================================================ 408// MAMEExpreesionAuxiliaryDebugWindowHandler class 409//============================================================ 410 411@implementation MAMEExpressionAuxiliaryDebugWindowHandler 412 413- (id)initWithMachine:(running_machine &)m title:(NSString *)t console:(MAMEDebugConsole *)c { 414 if (!(self = [super initWithMachine:m title:t console:c])) 415 return nil; 416 history = [[MAMEDebugCommandHistory alloc] init]; 417 return self; 418} 419 420 421- (void)dealloc { 422 if (history != nil) 423 [history release]; 424 [super dealloc]; 425} 426 427 428- (id <MAMEDebugViewExpressionSupport>)documentView { 429 return nil; 430} 431 432 433- (NSString *)expression { 434 return [[self documentView] expression]; 435} 436 437- (void)setExpression:(NSString *)expression { 438 [history add:expression]; 439 [[self documentView] setExpression:expression]; 440 [expressionField setStringValue:expression]; 441 [expressionField selectText:self]; 442} 443 444 445- (IBAction)doExpression:(id)sender { 446 NSString *expr = [sender stringValue]; 447 if ([expr length] > 0) { 448 [history add:expr]; 449 [[self documentView] setExpression:expr]; 450 } else { 451 [sender setStringValue:[[self documentView] expression]]; 452 [history reset]; 453 } 454 [sender selectText:self]; 455} 456 457 458- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor 459{ 460 if (control == expressionField) 461 [history edit]; 462 463 return YES; 464} 465 466 467- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command { 468 if (control == expressionField) { 469 if (command == @selector(cancelOperation:)) { 470 [history reset]; 471 [expressionField setStringValue:[[self documentView] expression]]; 472 [expressionField selectText:self]; 473 return YES; 474 } else if (command == @selector(moveUp:)) { 475 NSString *hist = [history previous:[expressionField stringValue]]; 476 if (hist != nil) { 477 [expressionField setStringValue:hist]; 478 [expressionField selectText:self]; 479 } 480 return YES; 481 } else if (command == @selector(moveDown:)) { 482 NSString *hist = [history next:[expressionField stringValue]]; 483 if (hist != nil) { 484 [expressionField setStringValue:hist]; 485 [expressionField selectText:self]; 486 } 487 return YES; 488 } 489 } 490 return NO; 491} 492 493 494- (void)saveConfigurationToNode:(util::xml::data_node *)node { 495 [super saveConfigurationToNode:node]; 496 node->add_child("expression", [[self expression] UTF8String]); 497} 498 499 500- (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { 501 [super restoreConfigurationFromNode:node]; 502 util::xml::data_node const *const expr = node->get_child("expression"); 503 if (expr && expr->get_value()) 504 [self setExpression:[NSString stringWithUTF8String:expr->get_value()]]; 505} 506 507@end 508