|
|
1.1.1.12! root 1: #!/usr/bin/python2 1.1 root 2: # 3: # Classes for the Hatari UI dialogs 4: # 1.1.1.12! root 5: # Copyright (C) 2008-2016 by Eero Tamminen 1.1 root 6: # 7: # This program is free software; you can redistribute it and/or modify 8: # it under the terms of the GNU General Public License as published by 9: # the Free Software Foundation; either version 2 of the License, or 10: # (at your option) any later version. 11: # 12: # This program is distributed in the hope that it will be useful, 13: # but WITHOUT ANY WARRANTY; without even the implied warranty of 14: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15: # GNU General Public License for more details. 16: 17: import os 18: # use correct version of pygtk/gtk 19: import pygtk 20: pygtk.require('2.0') 21: import gtk 22: import pango 23: 24: from uihelpers import UInfo, HatariTextInsert, create_table_dialog, \ 25: table_add_entry_row, table_add_widget_row, table_add_separator, \ 1.1.1.4 root 26: table_add_radio_rows, table_set_col_offset, create_button, FselEntry, \ 27: FselAndEjectFactory 1.1 root 28: 29: 30: # ----------------- 31: # Dialog base class 32: 33: class HatariUIDialog: 34: def __init__(self, parent): 35: "<any>Dialog(parent) -> object" 36: self.parent = parent 37: self.dialog = None 38: 39: def run(self): 40: """run() -> response. Shows dialog and returns response, 41: subclasses overriding run() require also an argument.""" 42: response = self.dialog.run() 43: self.dialog.hide() 44: return response 45: 46: 47: # --------------------------- 48: # Note/Todo/Error/Ask dialogs 49: 50: class NoteDialog(HatariUIDialog): 1.1.1.3 root 51: button = gtk.BUTTONS_OK 1.1 root 52: icontype = gtk.MESSAGE_INFO 53: textpattern = "\n%s" 54: def run(self, text): 55: "run(text), show message dialog with given text" 56: dialog = gtk.MessageDialog(self.parent, 57: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 1.1.1.3 root 58: self.icontype, self.button, self.textpattern % text) 1.1 root 59: dialog.run() 60: dialog.destroy() 61: 62: class TodoDialog(NoteDialog): 63: textpattern = "\nTODO: %s" 64: 65: class ErrorDialog(NoteDialog): 1.1.1.3 root 66: button = gtk.BUTTONS_CLOSE 1.1 root 67: icontype = gtk.MESSAGE_ERROR 68: textpattern = "\nERROR: %s" 69: 70: 71: class AskDialog(HatariUIDialog): 72: def run(self, text): 73: "run(text) -> bool, show question dialog and return True if user OKed it" 74: dialog = gtk.MessageDialog(self.parent, 75: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 76: gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, text) 77: response = dialog.run() 78: dialog.destroy() 79: return (response == gtk.RESPONSE_YES) 80: 81: 82: # --------------------------- 83: # About dialog 84: 85: class AboutDialog(HatariUIDialog): 86: def __init__(self, parent): 87: dialog = gtk.AboutDialog() 88: dialog.set_transient_for(parent) 89: dialog.set_name(UInfo.name) 90: dialog.set_version(UInfo.version) 1.1.1.5 root 91: dialog.set_website("http://hatari.tuxfamily.org/") 1.1 root 92: dialog.set_website_label("Hatari emulator www-site") 93: dialog.set_authors(["Eero Tamminen"]) 94: dialog.set_artists(["The logo is from Hatari"]) 95: dialog.set_logo(gtk.gdk.pixbuf_new_from_file(UInfo.logo)) 96: dialog.set_translator_credits("translator-credits") 1.1.1.3 root 97: dialog.set_copyright(UInfo.copyright) 1.1 root 98: dialog.set_license(""" 1.1.1.2 root 99: This software is licenced under GPL v2 or later. 1.1 root 100: 101: You can see the whole license at: 102: http://www.gnu.org/licenses/info/GPLv2.html""") 103: self.dialog = dialog 104: 105: 106: # --------------------------- 107: # Input dialog 108: 109: class InputDialog(HatariUIDialog): 110: def __init__(self, parent): 111: dialog = gtk.Dialog("Key/mouse input", parent, 112: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 113: ("Close", gtk.RESPONSE_CLOSE)) 114: 115: entry = gtk.Entry() 116: entry.connect("activate", self._entry_cb) 117: insert = create_button("Insert", self._entry_cb) 1.1.1.4 root 118: insert.set_tooltip_text("Insert given text to Hatari window") 119: enter = create_button("Enter key", self._enter_cb) 120: enter.set_tooltip_text("Simulate Enter key press") 1.1 root 121: 122: hbox1 = gtk.HBox() 123: hbox1.add(gtk.Label("Text:")) 124: hbox1.add(entry) 125: hbox1.add(insert) 1.1.1.4 root 126: hbox1.add(enter) 1.1 root 127: dialog.vbox.add(hbox1) 128: 1.1.1.4 root 129: rclick = gtk.Button("Right click") 1.1 root 130: rclick.connect("pressed", self._rightpress_cb) 131: rclick.connect("released", self._rightrelease_cb) 1.1.1.4 root 132: rclick.set_tooltip_text("Simulate Atari right button press & release") 133: dclick = create_button("Double click", self._doubleclick_cb) 134: dclick.set_tooltip_text("Simulate Atari left button double-click") 1.1 root 135: 136: hbox2 = gtk.HBox() 137: hbox2.add(dclick) 1.1.1.4 root 138: hbox2.add(rclick) 1.1 root 139: dialog.vbox.add(hbox2) 140: 141: dialog.show_all() 142: self.dialog = dialog 143: self.entry = entry 144: 145: def _entry_cb(self, widget): 146: text = self.entry.get_text() 147: if text: 148: HatariTextInsert(self.hatari, text) 149: self.entry.set_text("") 150: 1.1.1.4 root 151: def _enter_cb(self, widget): 152: self.hatari.insert_event("keypress 28") # Enter key scancode 153: 1.1 root 154: def _doubleclick_cb(self, widget): 155: self.hatari.insert_event("doubleclick") 156: 157: def _rightpress_cb(self, widget): 1.1.1.4 root 158: self.hatari.insert_event("rightdown") 1.1 root 159: 160: def _rightrelease_cb(self, widget): 1.1.1.4 root 161: self.hatari.insert_event("rightup") 1.1 root 162: 163: def run(self, hatari): 164: "run(hatari), do text/mouse click input" 165: self.hatari = hatari 166: self.dialog.run() 167: self.dialog.hide() 168: 169: 170: # --------------------------- 171: # Quit and Save dialog 172: 173: class QuitSaveDialog(HatariUIDialog): 174: def __init__(self, parent): 175: dialog = gtk.Dialog("Quit and Save?", parent, 176: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 177: ("Save changes", gtk.RESPONSE_YES, 178: "Discard changes", gtk.RESPONSE_NO, 179: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) 180: dialog.vbox.add(gtk.Label("You have unsaved configuration changes:")) 181: viewport = gtk.Viewport() 182: viewport.add(gtk.Label()) 183: scrolledwindow = gtk.ScrolledWindow() 184: scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 185: scrolledwindow.add(viewport) 186: dialog.vbox.add(scrolledwindow) 187: dialog.show_all() 188: self.scrolledwindow = scrolledwindow 189: self.viewport = viewport 190: self.dialog = dialog 191: 192: def run(self, config): 193: "run(config) -> False if canceled, True otherwise or if no changes" 194: changes = [] 195: for key, value in config.get_changes(): 196: changes.append("%s = %s" % (key, str(value))) 197: if not changes: 198: return True 199: child = self.viewport.get_child() 200: child.set_text(config.get_path() + ":\n" + "\n".join(changes)) 201: width, height = child.get_size_request() 202: if height < 320: 203: self.scrolledwindow.set_size_request(width, height) 204: else: 205: self.scrolledwindow.set_size_request(-1, 320) 206: self.viewport.show_all() 207: 208: response = self.dialog.run() 209: self.dialog.hide() 210: if response == gtk.RESPONSE_CANCEL: 211: return False 212: if response == gtk.RESPONSE_YES: 213: config.save() 214: return True 215: 216: 217: # --------------------------- 218: # Kill Hatari dialog 219: 220: class KillDialog(HatariUIDialog): 221: def __init__(self, parent): 222: self.dialog = gtk.MessageDialog(parent, 223: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 224: gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, 225: """\ 226: Hatari emulator is already/still running and it needs to be terminated first. However, if it contains unsaved data, that will be lost. 227: 228: Terminate Hatari anyway?""") 229: 230: def run(self, hatari): 231: "run(hatari) -> True if Hatari killed, False if left running" 232: if not hatari.is_running(): 233: return True 234: # Hatari is running, OK to kill? 235: response = self.dialog.run() 236: self.dialog.hide() 237: if response == gtk.RESPONSE_OK: 238: hatari.kill() 239: return True 240: return False 241: 242: 243: # --------------------------- 244: # Reset Hatari dialog 245: 246: class ResetDialog(HatariUIDialog): 247: COLD = 1 248: WARM = 2 249: def __init__(self, parent): 250: self.dialog = gtk.Dialog("Reset Atari?", parent, 251: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 252: ("Cold reset", self.COLD, "Warm reset", self.WARM, 253: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) 254: label = gtk.Label("\nRebooting will lose changes in currently\nrunning Atari programs.\n\nReset anyway?\n") 255: self.dialog.vbox.add(label) 256: label.show() 257: 258: def run(self, hatari): 259: "run(hatari) -> True if Hatari rebooted, False if canceled" 260: if not hatari.is_running(): 261: return False 262: # Hatari is running, how to reboot? 263: response = self.dialog.run() 264: self.dialog.hide() 265: if response == self.COLD: 266: hatari.trigger_shortcut("coldreset") 267: elif response == self.WARM: 268: hatari.trigger_shortcut("warmreset") 269: else: 270: return False 271: return True 272: 273: 274: # ---------------------------------- 275: # Floppy image dialog 276: 1.1.1.3 root 277: class FloppyDialog(HatariUIDialog): 1.1 root 278: def _create_dialog(self, config): 1.1.1.3 root 279: table, self.dialog = create_table_dialog(self.parent, "Floppy images", 4, 2) 1.1.1.4 root 280: factory = FselAndEjectFactory() 1.1.1.3 root 281: 1.1 root 282: row = 0 283: self.floppy = [] 284: path = config.get_floppydir() 285: for drive in ("A", "B"): 1.1.1.9 root 286: label = "Disk %c image:" % drive 1.1.1.4 root 287: fname = config.get_floppy(row) 288: fsel, box = factory.get(label, path, fname, gtk.FILE_CHOOSER_ACTION_OPEN) 1.1 root 289: table_add_widget_row(table, row, label, box) 1.1.1.4 root 290: self.floppy.append(fsel) 1.1 root 291: row += 1 292: 1.1.1.3 root 293: protect = gtk.combo_box_new_text() 294: for text in config.get_protection_types(): 295: protect.append_text(text) 296: protect.set_active(config.get_floppy_protection()) 1.1.1.4 root 297: protect.set_tooltip_text("Write protect floppy image contents") 1.1.1.3 root 298: table_add_widget_row(table, row, "Write protection:", protect) 1.1.1.9 root 299: row += 1 1.1.1.3 root 300: 1.1.1.9 root 301: ds = gtk.CheckButton("Double sided drives") 302: ds.set_active(config.get_doublesided()) 303: ds.set_tooltip_text("Whether drives are double or single sided. Can affect behavior of some games") 304: table_add_widget_row(table, row, None, ds) 305: row += 1 306: 307: driveb = gtk.CheckButton("Drive B connected") 308: driveb.set_active(config.get_floppy_drives()[1]) 309: driveb.set_tooltip_text("Wheter drive B is connected. Can affect behavior of some demos & games") 310: table_add_widget_row(table, row, None, driveb) 1.1.1.3 root 311: row += 1 1.1.1.9 root 312: 1.1.1.5 root 313: fastfdc = gtk.CheckButton("Fast floppy access") 314: fastfdc.set_active(config.get_fastfdc()) 1.1.1.6 root 315: fastfdc.set_tooltip_text("Can cause incompatibilities with some games/demos") 1.1.1.5 root 316: table_add_widget_row(table, row, None, fastfdc) 1.1.1.3 root 317: 1.1 root 318: table.show_all() 319: 1.1.1.3 root 320: self.protect = protect 1.1.1.5 root 321: self.fastfdc = fastfdc 1.1.1.9 root 322: self.driveb = driveb 323: self.ds = ds 1.1 root 324: 325: def run(self, config): 326: "run(config), show disk image dialog" 327: if not self.dialog: 328: self._create_dialog(config) 329: response = self.dialog.run() 330: self.dialog.hide() 331: 332: if response == gtk.RESPONSE_APPLY: 333: config.lock_updates() 334: for drive in range(2): 335: config.set_floppy(drive, self.floppy[drive].get_filename()) 1.1.1.4 root 336: config.set_floppy_protection(self.protect.get_active()) 1.1.1.9 root 337: config.set_doublesided(self.ds.get_active()) 1.1.1.5 root 338: config.set_fastfdc(self.fastfdc.get_active()) 1.1.1.9 root 339: drives = (config.get_floppy_drives()[0], self.driveb.get_active()) 340: config.set_floppy_drives(drives) 1.1 root 341: config.flush_updates() 342: 1.1.1.3 root 343: 344: # ---------------------------------- 345: # Hard disk dialog 346: 347: class HardDiskDialog(HatariUIDialog): 348: def _create_dialog(self, config): 349: table, self.dialog = create_table_dialog(self.parent, "Hard disks", 4, 4, "Set and reboot") 1.1.1.4 root 350: factory = FselAndEjectFactory() 1.1.1.3 root 351: 1.1.1.4 root 352: row = 0 1.1.1.3 root 353: label = "ASCI HD image:" 354: path = config.get_acsi_image() 1.1.1.4 root 355: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN) 1.1.1.3 root 356: table_add_widget_row(table, row, label, box, True) 357: self.acsi = fsel 358: row += 1 359: 360: label = "IDE HD master image:" 361: path = config.get_idemaster_image() 1.1.1.4 root 362: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN) 1.1.1.3 root 363: table_add_widget_row(table, row, label, box, True) 364: self.idemaster = fsel 365: row += 1 366: 367: label = "IDE HD slave image:" 368: path = config.get_ideslave_image() 1.1.1.4 root 369: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN) 1.1.1.3 root 370: table_add_widget_row(table, row, label, box, True) 371: self.ideslave = fsel 372: row += 1 1.1.1.10 root 373: 374: table_add_widget_row(table, row, " ", gtk.HSeparator(), True) 375: row += 1 376: 1.1.1.3 root 377: label = "GEMDOS drive directory:" 1.1.1.10 root 378: path = config.get_hd_dir() 1.1.1.4 root 379: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) 1.1.1.3 root 380: table_add_widget_row(table, row, label, box, True) 1.1.1.10 root 381: self.hddir = fsel 382: row += 1 383: 384: hddrive = gtk.combo_box_new_text() 385: for text in config.get_hd_drives(): 386: hddrive.append_text(text) 387: hddrive.set_tooltip_text("Whether GEMDOS HD emulation uses fixed drive letter, or first free drive letter after ASCI & IDE drives (detection unreliable)") 388: table_add_widget_row(table, row, "GEMDOS HD drive:", hddrive) 389: self.hddrive = hddrive 1.1.1.3 root 390: row += 1 391: 392: protect = gtk.combo_box_new_text() 393: for text in config.get_protection_types(): 394: protect.append_text(text) 1.1.1.9 root 395: protect.set_tooltip_text("Whether/how to write protect (GEMDOS HD) emulation files, 'auto' means using host files' own properties") 1.1.1.3 root 396: table_add_widget_row(table, row, "Write protection:", protect) 397: self.protect = protect 1.1.1.9 root 398: row += 1 399: 400: lower = gtk.combo_box_new_text() 401: for text in config.get_hd_cases(): 402: lower.append_text(text) 403: lower.set_tooltip_text("What to do with names of files created by Atari programs through GEMDOS HD emulation") 404: table_add_widget_row(table, row, "File names:", lower) 405: self.lower = lower 1.1.1.3 root 406: 407: table.show_all() 1.1 root 408: 1.1.1.3 root 409: def _get_config(self, config): 410: path = config.get_acsi_image() 411: if path: 412: self.acsi.set_filename(path) 413: path = config.get_idemaster_image() 414: if path: 415: self.idemaster.set_filename(path) 416: path = config.get_ideslave_image() 417: if path: 418: self.ideslave.set_filename(path) 1.1.1.10 root 419: path = config.get_hd_dir() 420: if path: 421: self.hddir.set_filename(path) 422: self.hddrive.set_active(config.get_hd_drive()) 1.1.1.3 root 423: self.protect.set_active(config.get_hd_protection()) 1.1.1.9 root 424: self.lower.set_active(config.get_hd_case()) 425: 1.1.1.3 root 426: def _set_config(self, config): 427: config.lock_updates() 428: config.set_acsi_image(self.acsi.get_filename()) 429: config.set_idemaster_image(self.idemaster.get_filename()) 430: config.set_ideslave_image(self.ideslave.get_filename()) 1.1.1.10 root 431: config.set_hd_dir(self.hddir.get_filename()) 432: config.set_hd_drive(self.hddrive.get_active()) 1.1.1.3 root 433: config.set_hd_protection(self.protect.get_active()) 1.1.1.9 root 434: config.set_hd_case(self.lower.get_active()) 1.1.1.3 root 435: config.flush_updates() 436: 437: def run(self, config): 438: "run(config) -> bool, whether to reboot" 439: if not self.dialog: 440: self._create_dialog(config) 441: 442: self._get_config(config) 443: response = self.dialog.run() 444: self.dialog.hide() 445: if response == gtk.RESPONSE_APPLY: 446: self._set_config(config) 447: return True 448: return False 449: 450: 1.1 root 451: # --------------------------- 452: # Display dialog 453: 454: class DisplayDialog(HatariUIDialog): 455: 456: def _create_dialog(self, config): 457: 458: skip = gtk.combo_box_new_text() 459: for text in config.get_frameskip_names(): 460: skip.append_text(text) 461: skip.set_active(config.get_frameskip()) 1.1.1.9 root 462: skip.set_tooltip_text("Set how many frames are skipped to speed up emulation") 463: 464: slow = gtk.combo_box_new_text() 465: for text in config.get_slowdown_names(): 466: slow.append_text(text) 467: slow.set_active(0) 468: slow.set_tooltip_text("VBL wait multiplier to slow down emulation. Breaks sound and large enough slowdown causes mouse clicks not to work.") 1.1 root 469: 1.1.1.3 root 470: maxw, maxh = config.get_max_size() 1.1.1.6 root 471: topw, toph = config.get_desktop_size() 472: maxadjw = gtk.Adjustment(maxw, 320, topw, 8, 40) 473: maxadjh = gtk.Adjustment(maxh, 200, toph, 8, 40) 1.1.1.3 root 474: scalew = gtk.HScale(maxadjw) 475: scaleh = gtk.HScale(maxadjh) 476: scalew.set_digits(0) 477: scaleh.set_digits(0) 1.1.1.4 root 478: scalew.set_tooltip_text("Preferred/maximum zoomed width") 479: scaleh.set_tooltip_text("Preferred/maximum zoomed height") 480: 1.1.1.7 root 481: force_max = gtk.CheckButton("Force max resolution (Falcon)") 482: force_max.set_active(config.get_force_max()) 483: force_max.set_tooltip_text("Whether to force maximum resolution to help recording videos of demos which do resolution changes") 484: 485: desktop = gtk.CheckButton("Keep desktop resolution (Falcon/TT)") 1.1.1.4 root 486: desktop.set_active(config.get_desktop()) 1.1.1.7 root 487: desktop.set_tooltip_text("Whether to keep screen resolution in fullscreen and (try to) scale Atari screen by an integer factor instead") 488: 489: desktop_st = gtk.CheckButton("Keep desktop resolution (ST/STE)") 490: desktop_st.set_active(config.get_desktop_st()) 491: desktop_st.set_tooltip_text("Whether to keep screen resolution in fullscreen to avoid potential sound skips + delay (NO SCALING)") 1.1 root 492: 1.1.1.7 root 493: borders = gtk.CheckButton("Atari screen borders") 1.1 root 494: borders.set_active(config.get_borders()) 1.1.1.7 root 495: borders.set_tooltip_text("Whether to show overscan borders in ST/STE low/mid-rez or in Falcon color resolutions. Visible border area is affected by max. zoom size") 1.1 root 496: 1.1.1.3 root 497: statusbar = gtk.CheckButton("Show statusbar") 1.1 root 498: statusbar.set_active(config.get_statusbar()) 1.1.1.4 root 499: statusbar.set_tooltip_text("Whether to show statusbar with floppy leds etc") 1.1 root 500: 1.1.1.3 root 501: led = gtk.CheckButton("Show overlay led") 1.1 root 502: led.set_active(config.get_led()) 1.1.1.4 root 503: led.set_tooltip_text("Whether to show overlay drive led when statusbar isn't visible") 504: 505: crop = gtk.CheckButton("Remove statusbar from screen capture") 506: crop.set_active(config.get_crop()) 507: crop.set_tooltip_text("Whether to crop statusbar from screenshots and video recordings") 1.1 root 508: 509: dialog = gtk.Dialog("Display settings", self.parent, 510: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 511: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY, 512: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) 513: 1.1.1.3 root 514: dialog.vbox.add(gtk.Label("Max zoomed size:")) 515: dialog.vbox.add(scalew) 516: dialog.vbox.add(scaleh) 1.1.1.7 root 517: dialog.vbox.add(force_max) 1.1.1.4 root 518: dialog.vbox.add(desktop) 1.1.1.7 root 519: dialog.vbox.add(desktop_st) 1.1 root 520: dialog.vbox.add(borders) 1.1.1.9 root 521: dialog.vbox.add(gtk.Label("Frameskip:")) 522: dialog.vbox.add(skip) 523: dialog.vbox.add(gtk.Label("Slowdown:")) 524: dialog.vbox.add(slow) 1.1 root 525: dialog.vbox.add(statusbar) 526: dialog.vbox.add(led) 1.1.1.4 root 527: dialog.vbox.add(crop) 1.1 root 528: dialog.vbox.show_all() 529: 530: self.dialog = dialog 531: self.skip = skip 1.1.1.9 root 532: self.slow = slow 1.1.1.3 root 533: self.maxw = maxadjw 534: self.maxh = maxadjh 1.1.1.7 root 535: self.force_max = force_max 1.1.1.4 root 536: self.desktop = desktop 1.1.1.7 root 537: self.desktop_st = desktop_st 1.1 root 538: self.borders = borders 539: self.statusbar = statusbar 540: self.led = led 1.1.1.4 root 541: self.crop = crop 1.1 root 542: 543: def run(self, config): 544: "run(config), show display dialog" 545: if not self.dialog: 546: self._create_dialog(config) 547: response = self.dialog.run() 548: self.dialog.hide() 549: if response == gtk.RESPONSE_APPLY: 550: config.lock_updates() 551: config.set_frameskip(self.skip.get_active()) 1.1.1.9 root 552: config.set_slowdown(self.slow.get_active()) 1.1.1.3 root 553: config.set_max_size(self.maxw.get_value(), self.maxh.get_value()) 1.1.1.7 root 554: config.set_force_max(self.force_max.get_active()) 1.1.1.4 root 555: config.set_desktop(self.desktop.get_active()) 1.1.1.7 root 556: config.set_desktop_st(self.desktop_st.get_active()) 1.1 root 557: config.set_borders(self.borders.get_active()) 558: config.set_statusbar(self.statusbar.get_active()) 559: config.set_led(self.led.get_active()) 1.1.1.4 root 560: config.set_crop(self.crop.get_active()) 1.1 root 561: config.flush_updates() 562: 563: 564: # ---------------------------------- 565: # Joystick dialog 566: 567: class JoystickDialog(HatariUIDialog): 568: def _create_dialog(self, config): 1.1.1.3 root 569: table, self.dialog = create_table_dialog(self.parent, "Joystick settings", 9, 2) 1.1 root 570: 571: joy = 0 572: self.joy = [] 573: joytypes = config.get_joystick_types() 574: for label in config.get_joystick_names(): 575: combo = gtk.combo_box_new_text() 576: for text in joytypes: 577: combo.append_text(text) 578: combo.set_active(config.get_joystick(joy)) 579: widget = table_add_widget_row(table, joy, "%s:" % label, combo) 580: self.joy.append(widget) 581: joy += 1 582: 583: table.show_all() 584: 585: def run(self, config): 586: "run(config), show joystick dialog" 587: if not self.dialog: 588: self._create_dialog(config) 589: response = self.dialog.run() 590: self.dialog.hide() 591: 592: if response == gtk.RESPONSE_APPLY: 593: config.lock_updates() 594: for joy in range(6): 595: config.set_joystick(joy, self.joy[joy].get_active()) 596: config.flush_updates() 597: 598: 599: # --------------------------------------- 600: # Peripherals (midi,printer,rs232) dialog 601: 602: class PeripheralDialog(HatariUIDialog): 603: def _create_dialog(self, config): 604: midi = gtk.CheckButton("Enable MIDI") 605: midi.set_active(config.get_midi()) 606: 607: printer = gtk.CheckButton("Enable printer output") 608: printer.set_active(config.get_printer()) 609: 610: rs232 = gtk.CheckButton("Enable RS232") 611: rs232.set_active(config.get_rs232()) 612: 613: dialog = gtk.Dialog("Peripherals", self.parent, 614: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 615: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY, 616: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) 617: dialog.vbox.add(midi) 618: dialog.vbox.add(printer) 619: dialog.vbox.add(rs232) 620: dialog.vbox.show_all() 621: 622: self.dialog = dialog 623: self.printer = printer 624: self.rs232 = rs232 625: self.midi = midi 626: 627: def run(self, config): 628: "run(config), show peripherals dialog" 629: if not self.dialog: 630: self._create_dialog(config) 631: response = self.dialog.run() 632: self.dialog.hide() 633: 634: if response == gtk.RESPONSE_APPLY: 635: config.lock_updates() 636: config.set_midi(self.midi.get_active()) 637: config.set_printer(self.printer.get_active()) 638: config.set_rs232(self.rs232.get_active()) 639: config.flush_updates() 640: 641: 642: # --------------------------------------- 643: # Path dialog 644: 645: class PathDialog(HatariUIDialog): 646: def _create_dialog(self, config): 647: paths = config.get_paths() 1.1.1.3 root 648: table, self.dialog = create_table_dialog(self.parent, "File path settings", len(paths), 2) 1.1 root 649: paths.sort() 650: row = 0 651: self.paths = [] 652: for (key, path, label) in paths: 653: fsel = FselEntry(self.dialog, self._validate_fname, key) 654: fsel.set_filename(path) 655: self.paths.append((key, fsel)) 656: table_add_widget_row(table, row, label, fsel.get_container()) 657: row += 1 658: table.show_all() 659: 660: def _validate_fname(self, key, fname): 661: if key != "soundout": 662: return True 663: if fname.rsplit(".", 1)[-1].lower() in ("ym", "wav"): 664: return True 665: ErrorDialog(self.dialog).run("Sound output file name:\n\t%s\nneeds to end with '.ym' or '.wav'." % fname) 666: return False 667: 668: def run(self, config): 669: "run(config), show paths dialog" 670: if not self.dialog: 671: self._create_dialog(config) 672: response = self.dialog.run() 673: self.dialog.hide() 674: 675: if response == gtk.RESPONSE_APPLY: 676: paths = [] 677: for key, fsel in self.paths: 678: paths.append((key, fsel.get_filename())) 679: config.set_paths(paths) 680: 681: 682: # --------------------------- 683: # Sound dialog 684: 685: class SoundDialog(HatariUIDialog): 686: 687: def _create_dialog(self, config): 1.1.1.6 root 688: enabled, curhz = config.get_sound() 689: 1.1 root 690: self.enabled = gtk.CheckButton("Sound enabled") 691: self.enabled.set_active(enabled) 1.1.1.6 root 692: 693: hz = gtk.combo_box_new_text() 694: for text in config.get_sound_values(): 695: hz.append_text(text) 696: hz.set_active(curhz) 697: self.hz = hz 698: 699: ymmixer = gtk.combo_box_new_text() 700: for text in config.get_ymmixer_types(): 701: ymmixer.append_text(text) 702: ymmixer.set_active(config.get_ymmixer()) 703: self.ymmixer = ymmixer 704: 1.1.1.9 root 705: adj = gtk.Adjustment(config.get_bufsize(), 0, 110, 10, 10, 10) 706: bufsize = gtk.HScale(adj) 707: bufsize.set_digits(0) 708: bufsize.set_tooltip_text("0 = use default value. In some situations, SDL default may cause large (~0.5s) sound delay at lower frequency. If you have this problem, try with e.g. 20 ms, otherwise keep at 0.") 709: self.bufsize = bufsize 710: 711: self.sync = gtk.CheckButton("Emulation speed synched to sound output") 712: self.sync.set_tooltip_text("Constantly adjust emulation screen update rate to match sound output. Can help if you suffer from sound buffer under/overflow.") 713: self.sync.set_active(config.get_sync()) 714: 1.1.1.6 root 715: self.mic = gtk.CheckButton("Enable (Falcon) microphone") 716: self.mic.set_active(config.get_mic()) 1.1 root 717: 718: dialog = gtk.Dialog("Sound settings", self.parent, 719: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 720: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY, 721: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) 722: dialog.vbox.add(self.enabled) 1.1.1.6 root 723: dialog.vbox.add(gtk.Label("Sound frequency::")) 724: dialog.vbox.add(hz) 725: dialog.vbox.add(gtk.Label("YM voices mixing method:")) 726: dialog.vbox.add(ymmixer) 1.1.1.9 root 727: dialog.vbox.add(gtk.Label("SDL sound buffer size (ms):")) 728: dialog.vbox.add(bufsize) 729: dialog.vbox.add(self.sync) 1.1.1.6 root 730: dialog.vbox.add(self.mic) 1.1 root 731: dialog.vbox.show_all() 732: self.dialog = dialog 733: 734: def run(self, config): 735: "run(config), show sound dialog" 736: if not self.dialog: 737: self._create_dialog(config) 738: response = self.dialog.run() 739: self.dialog.hide() 740: if response == gtk.RESPONSE_APPLY: 1.1.1.6 root 741: config.lock_updates() 742: config.set_mic(self.mic.get_active()) 1.1.1.9 root 743: config.set_sync(self.sync.get_active()) 744: config.set_bufsize(self.bufsize.get_value()) 1.1.1.6 root 745: config.set_ymmixer(self.ymmixer.get_active()) 1.1 root 746: enabled = self.enabled.get_active() 1.1.1.6 root 747: hz = self.hz.get_active() 748: config.set_sound(enabled, hz) 749: config.flush_updates() 1.1 root 750: 751: 752: # --------------------------- 753: # Trace settings dialog 754: 755: class TraceDialog(HatariUIDialog): 756: # you can get this list with: 757: # hatari --trace help 2>&1|awk '/all$/{next} /^ [^-]/ {printf("\"%s\",\n", $1)}' 758: tracepoints = [ 759: "video_sync", 760: "video_res", 761: "video_color", 762: "video_border_v", 763: "video_border_h", 764: "video_addr", 765: "video_hbl", 766: "video_vbl", 767: "video_ste", 768: "mfp_exception", 769: "mfp_start", 770: "mfp_read", 771: "mfp_write", 1.1.1.4 root 772: "psg_read", 773: "psg_write", 1.1 root 774: "cpu_pairing", 775: "cpu_disasm", 776: "cpu_exception", 777: "int", 778: "fdc", 1.1.1.8 root 779: "acia", 1.1 root 780: "ikbd_cmds", 781: "ikbd_acia", 782: "ikbd_exec", 783: "blitter", 784: "bios", 785: "xbios", 786: "gemdos", 787: "vdi", 1.1.1.4 root 788: "aes", 1.1 root 789: "io_read", 790: "io_write", 1.1.1.4 root 791: "dmasound", 792: "crossbar", 793: "videl", 794: "dsp_host_interface", 795: "dsp_host_command", 796: "dsp_host_ssi", 797: "dsp_interrupt", 798: "dsp_disasm", 1.1.1.8 root 799: "dsp_disasm_reg", 800: "dsp_disasm_mem", 801: "dsp_state", 802: "dsp_symbols", 803: "cpu_symbols", 1.1.1.9 root 804: "nvram", 805: "scsi_cmd", 806: "natfeats", 1.1.1.10 root 807: "keymap", 808: "midi", 809: "ide", 1.1.1.11 root 810: "os_base", 811: "scsidrv" 1.1 root 812: ] 813: def __init__(self, parent): 1.1.1.6 root 814: self.savedpoints = None 1.1 root 815: hbox1 = gtk.HBox() 816: hbox1.add(create_button("Load", self._load_traces)) 817: hbox1.add(create_button("Clear", self._clear_traces)) 818: hbox1.add(create_button("Save", self._save_traces)) 819: hbox2 = gtk.HBox() 820: vboxes = [] 1.1.1.4 root 821: for idx in (0,1,2,3): 1.1 root 822: vboxes.append(gtk.VBox()) 823: hbox2.add(vboxes[idx]) 824: 825: count = 0 1.1.1.4 root 826: per_side = (len(self.tracepoints)+3)/4 1.1 root 827: self.tracewidgets = {} 828: for trace in self.tracepoints: 1.1.1.2 root 829: name = trace.replace("_", "-") 830: widget = gtk.CheckButton(name) 1.1 root 831: self.tracewidgets[trace] = widget 832: vboxes[count/per_side].pack_start(widget, False, True) 833: count += 1 834: 835: dialog = gtk.Dialog("Trace settings", parent, 836: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 837: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY, 838: gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) 839: dialog.vbox.add(hbox1) 840: dialog.vbox.add(gtk.Label("Select trace points:")) 841: dialog.vbox.add(hbox2) 842: dialog.vbox.show_all() 843: self.dialog = dialog 844: 845: def _get_traces(self): 846: traces = [] 847: for trace in self.tracepoints: 848: if self.tracewidgets[trace].get_active(): 849: traces.append(trace) 850: if traces: 851: return ",".join(traces) 852: return "none" 853: 854: def _set_traces(self, tracepoints): 855: self._clear_traces() 1.1.1.6 root 856: if not tracepoints: 857: return 1.1 root 858: for trace in tracepoints.split(","): 859: if trace in self.tracewidgets: 860: self.tracewidgets[trace].set_active(True) 861: else: 1.1.1.4 root 862: print("ERROR: unknown trace setting '%s'" % trace) 1.1 root 863: 864: def _clear_traces(self, widget = None): 865: for trace in self.tracepoints: 866: self.tracewidgets[trace].set_active(False) 867: 868: def _load_traces(self, widget): 869: # this doesn't load traces, just sets them from internal variable 870: # that run method gets from caller and sets. It's up to caller 871: # whether the saving or loading happens actually to disk 872: self._set_traces(self.savedpoints) 873: 874: def _save_traces(self, widget): 875: self.savedpoints = self._get_traces() 876: 877: def run(self, hatari, savedpoints): 878: "run(hatari,tracepoints) -> tracepoints, caller saves tracepoints" 879: self.savedpoints = savedpoints 880: while 1: 881: response = self.dialog.run() 882: if response == gtk.RESPONSE_APPLY: 883: hatari.change_option("--trace %s" % self._get_traces()) 884: else: 885: self.dialog.hide() 886: return self.savedpoints 887: 888: 889: # ------------------------------------------ 890: # Machine dialog for settings needing reboot 891: 892: class MachineDialog(HatariUIDialog): 1.1.1.3 root 893: def _machine_cb(self, widget, data): 894: if not widget.get_active(): 895: return 896: machine = data.lower() 897: if machine == "ste" or machine == "st": 898: self.clocks[0].set_active(True) 899: self.cpulevel.set_active(0) 900: elif machine == "falcon": 901: self.clocks[1].set_active(True) 1.1.1.4 root 902: self.dsps[2].set_active(True) 1.1.1.3 root 903: self.cpulevel.set_active(3) 904: elif machine == "tt": 905: self.clocks[2].set_active(True) 906: self.cpulevel.set_active(3) 907: 1.1 root 908: def _create_dialog(self, config): 1.1.1.3 root 909: table, self.dialog = create_table_dialog(self.parent, "Machine configuration", 6, 4, "Set and reboot") 910: 911: row = 0 912: self.machines = table_add_radio_rows(table, row, "Machine:", 913: config.get_machine_types(), self._machine_cb) 914: row += 1 1.1 root 915: 1.1.1.4 root 916: self.dsps = table_add_radio_rows(table, row, "DSP type:", config.get_dsp_types()) 1.1.1.3 root 917: row += 1 918: 919: # start next table column 1.1 root 920: row = 0 1.1.1.3 root 921: table_set_col_offset(table, 2) 922: self.monitors = table_add_radio_rows(table, row, "Monitor:", config.get_monitor_types()) 1.1 root 923: row += 1 1.1.1.3 root 924: 925: self.clocks = table_add_radio_rows(table, row, "CPU clock:", config.get_cpuclock_types()) 926: row += 1 927: 928: # fullspan at bottom 929: fullspan = True 930: 1.1 root 931: combo = gtk.combo_box_new_text() 1.1.1.3 root 932: for text in config.get_cpulevel_types(): 1.1 root 933: combo.append_text(text) 1.1.1.3 root 934: self.cpulevel = table_add_widget_row(table, row, "CPU type:", combo, fullspan) 1.1 root 935: row += 1 1.1.1.3 root 936: 1.1 root 937: combo = gtk.combo_box_new_text() 938: for text in config.get_memory_names(): 939: combo.append_text(text) 1.1.1.3 root 940: self.memory = table_add_widget_row(table, row, "Memory:", combo, fullspan) 1.1 root 941: row += 1 1.1.1.10 root 942: 943: self.ttram = gtk.Adjustment(config.get_ttram(), 0, 260, 4, 4, 4) 944: ttram = gtk.HScale(self.ttram) 945: ttram.set_digits(0) 946: ttram.set_tooltip_text("TT-RAM needs Falcon/TT with WinUAE CPU core and implies 32-bit addressing. 0 = disabled, 24-bit addressing.") 947: table_add_widget_row(table, row, "TT-RAM", ttram, fullspan) 948: row += 1 1.1 root 949: 950: label = "TOS image:" 951: fsel = self._fsel(label, gtk.FILE_CHOOSER_ACTION_OPEN) 1.1.1.3 root 952: self.tos = table_add_widget_row(table, row, label, fsel, fullspan) 1.1 root 953: row += 1 954: 1.1.1.3 root 955: vbox = gtk.VBox() 956: self.compatible = gtk.CheckButton("Compatible CPU") 957: self.timerd = gtk.CheckButton("Patch Timer-D") 1.1.1.9 root 958: self.compatible.set_tooltip_text("Needed for overscan and other timing sensitive things to work correctly") 959: self.timerd.set_tooltip_text("Improves ST/STE emulation performance, but some rare demos/games don't work with this") 1.1.1.3 root 960: vbox.add(self.compatible) 961: vbox.add(self.timerd) 962: table_add_widget_row(table, row, "Misc.:", vbox, fullspan) 1.1 root 963: row += 1 964: 965: table.show_all() 966: 967: def _fsel(self, label, action): 968: fsel = gtk.FileChooserButton(label) 969: # Hatari cannot access URIs 970: fsel.set_local_only(True) 971: fsel.set_width_chars(12) 972: fsel.set_action(action) 973: return fsel 974: 975: def _get_config(self, config): 1.1.1.3 root 976: self.machines[config.get_machine()].set_active(True) 977: self.monitors[config.get_monitor()].set_active(True) 978: self.clocks[config.get_cpuclock()].set_active(True) 979: self.dsps[config.get_dsp()].set_active(True) 980: self.cpulevel.set_active(config.get_cpulevel()) 1.1 root 981: self.memory.set_active(config.get_memory()) 1.1.1.10 root 982: self.ttram.set_value(config.get_ttram()) 1.1 root 983: tos = config.get_tos() 984: if tos: 985: self.tos.set_filename(tos) 986: self.compatible.set_active(config.get_compatible()) 987: self.timerd.set_active(config.get_timerd()) 988: 1.1.1.3 root 989: def _get_active_radio(self, radios): 990: idx = 0 991: for radio in radios: 992: if radio.get_active(): 993: return idx 994: idx += 1 995: 1.1 root 996: def _set_config(self, config): 997: config.lock_updates() 1.1.1.3 root 998: config.set_machine(self._get_active_radio(self.machines)) 999: config.set_monitor(self._get_active_radio(self.monitors)) 1000: config.set_cpuclock(self._get_active_radio(self.clocks)) 1001: config.set_dsp(self._get_active_radio(self.dsps)) 1002: config.set_cpulevel(self.cpulevel.get_active()) 1.1 root 1003: config.set_memory(self.memory.get_active()) 1.1.1.10 root 1004: config.set_ttram(self.ttram.get_value()) 1.1 root 1005: config.set_tos(self.tos.get_filename()) 1006: config.set_compatible(self.compatible.get_active()) 1007: config.set_timerd(self.timerd.get_active()) 1008: config.flush_updates() 1009: 1010: def run(self, config): 1011: "run(config) -> bool, whether to reboot" 1012: if not self.dialog: 1013: self._create_dialog(config) 1014: 1015: self._get_config(config) 1016: response = self.dialog.run() 1017: self.dialog.hide() 1018: if response == gtk.RESPONSE_APPLY: 1019: self._set_config(config) 1020: return True 1021: return False
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.