HatariWii/python-ui/dialogs.py

1025 lines
36 KiB
Python

#!/usr/bin/env python
#
# Classes for the Hatari UI dialogs
#
# Copyright (C) 2008-2015 by Eero Tamminen
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
import os
# use correct version of pygtk/gtk
import pygtk
pygtk.require('2.0')
import gtk
import pango
from uihelpers import UInfo, HatariTextInsert, create_table_dialog, \
table_add_entry_row, table_add_widget_row, table_add_separator, \
table_add_radio_rows, table_set_col_offset, create_button, FselEntry, \
FselAndEjectFactory
# -----------------
# Dialog base class
class HatariUIDialog:
def __init__(self, parent):
"<any>Dialog(parent) -> object"
self.parent = parent
self.dialog = None
def run(self):
"""run() -> response. Shows dialog and returns response,
subclasses overriding run() require also an argument."""
response = self.dialog.run()
self.dialog.hide()
return response
# ---------------------------
# Note/Todo/Error/Ask dialogs
class NoteDialog(HatariUIDialog):
button = gtk.BUTTONS_OK
icontype = gtk.MESSAGE_INFO
textpattern = "\n%s"
def run(self, text):
"run(text), show message dialog with given text"
dialog = gtk.MessageDialog(self.parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
self.icontype, self.button, self.textpattern % text)
dialog.run()
dialog.destroy()
class TodoDialog(NoteDialog):
textpattern = "\nTODO: %s"
class ErrorDialog(NoteDialog):
button = gtk.BUTTONS_CLOSE
icontype = gtk.MESSAGE_ERROR
textpattern = "\nERROR: %s"
class AskDialog(HatariUIDialog):
def run(self, text):
"run(text) -> bool, show question dialog and return True if user OKed it"
dialog = gtk.MessageDialog(self.parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, text)
response = dialog.run()
dialog.destroy()
return (response == gtk.RESPONSE_YES)
# ---------------------------
# About dialog
class AboutDialog(HatariUIDialog):
def __init__(self, parent):
dialog = gtk.AboutDialog()
dialog.set_transient_for(parent)
dialog.set_name(UInfo.name)
dialog.set_version(UInfo.version)
dialog.set_website("http://hatari.tuxfamily.org/")
dialog.set_website_label("Hatari emulator www-site")
dialog.set_authors(["Eero Tamminen"])
dialog.set_artists(["The logo is from Hatari"])
dialog.set_logo(gtk.gdk.pixbuf_new_from_file(UInfo.logo))
dialog.set_translator_credits("translator-credits")
dialog.set_copyright(UInfo.copyright)
dialog.set_license("""
This software is licenced under GPL v2 or later.
You can see the whole license at:
http://www.gnu.org/licenses/info/GPLv2.html""")
self.dialog = dialog
# ---------------------------
# Input dialog
class InputDialog(HatariUIDialog):
def __init__(self, parent):
dialog = gtk.Dialog("Key/mouse input", parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
("Close", gtk.RESPONSE_CLOSE))
entry = gtk.Entry()
entry.connect("activate", self._entry_cb)
insert = create_button("Insert", self._entry_cb)
insert.set_tooltip_text("Insert given text to Hatari window")
enter = create_button("Enter key", self._enter_cb)
enter.set_tooltip_text("Simulate Enter key press")
hbox1 = gtk.HBox()
hbox1.add(gtk.Label("Text:"))
hbox1.add(entry)
hbox1.add(insert)
hbox1.add(enter)
dialog.vbox.add(hbox1)
rclick = gtk.Button("Right click")
rclick.connect("pressed", self._rightpress_cb)
rclick.connect("released", self._rightrelease_cb)
rclick.set_tooltip_text("Simulate Atari right button press & release")
dclick = create_button("Double click", self._doubleclick_cb)
dclick.set_tooltip_text("Simulate Atari left button double-click")
hbox2 = gtk.HBox()
hbox2.add(dclick)
hbox2.add(rclick)
dialog.vbox.add(hbox2)
dialog.show_all()
self.dialog = dialog
self.entry = entry
def _entry_cb(self, widget):
text = self.entry.get_text()
if text:
HatariTextInsert(self.hatari, text)
self.entry.set_text("")
def _enter_cb(self, widget):
self.hatari.insert_event("keypress 28") # Enter key scancode
def _doubleclick_cb(self, widget):
self.hatari.insert_event("doubleclick")
def _rightpress_cb(self, widget):
self.hatari.insert_event("rightdown")
def _rightrelease_cb(self, widget):
self.hatari.insert_event("rightup")
def run(self, hatari):
"run(hatari), do text/mouse click input"
self.hatari = hatari
self.dialog.run()
self.dialog.hide()
# ---------------------------
# Quit and Save dialog
class QuitSaveDialog(HatariUIDialog):
def __init__(self, parent):
dialog = gtk.Dialog("Quit and Save?", parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
("Save changes", gtk.RESPONSE_YES,
"Discard changes", gtk.RESPONSE_NO,
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
dialog.vbox.add(gtk.Label("You have unsaved configuration changes:"))
viewport = gtk.Viewport()
viewport.add(gtk.Label())
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolledwindow.add(viewport)
dialog.vbox.add(scrolledwindow)
dialog.show_all()
self.scrolledwindow = scrolledwindow
self.viewport = viewport
self.dialog = dialog
def run(self, config):
"run(config) -> False if canceled, True otherwise or if no changes"
changes = []
for key, value in config.get_changes():
changes.append("%s = %s" % (key, str(value)))
if not changes:
return True
child = self.viewport.get_child()
child.set_text(config.get_path() + ":\n" + "\n".join(changes))
width, height = child.get_size_request()
if height < 320:
self.scrolledwindow.set_size_request(width, height)
else:
self.scrolledwindow.set_size_request(-1, 320)
self.viewport.show_all()
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_CANCEL:
return False
if response == gtk.RESPONSE_YES:
config.save()
return True
# ---------------------------
# Kill Hatari dialog
class KillDialog(HatariUIDialog):
def __init__(self, parent):
self.dialog = gtk.MessageDialog(parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL,
"""\
Hatari emulator is already/still running and it needs to be terminated first. However, if it contains unsaved data, that will be lost.
Terminate Hatari anyway?""")
def run(self, hatari):
"run(hatari) -> True if Hatari killed, False if left running"
if not hatari.is_running():
return True
# Hatari is running, OK to kill?
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_OK:
hatari.kill()
return True
return False
# ---------------------------
# Reset Hatari dialog
class ResetDialog(HatariUIDialog):
COLD = 1
WARM = 2
def __init__(self, parent):
self.dialog = gtk.Dialog("Reset Atari?", parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
("Cold reset", self.COLD, "Warm reset", self.WARM,
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
label = gtk.Label("\nRebooting will lose changes in currently\nrunning Atari programs.\n\nReset anyway?\n")
self.dialog.vbox.add(label)
label.show()
def run(self, hatari):
"run(hatari) -> True if Hatari rebooted, False if canceled"
if not hatari.is_running():
return False
# Hatari is running, how to reboot?
response = self.dialog.run()
self.dialog.hide()
if response == self.COLD:
hatari.trigger_shortcut("coldreset")
elif response == self.WARM:
hatari.trigger_shortcut("warmreset")
else:
return False
return True
# ----------------------------------
# Floppy image dialog
class FloppyDialog(HatariUIDialog):
def _create_dialog(self, config):
table, self.dialog = create_table_dialog(self.parent, "Floppy images", 4, 2)
factory = FselAndEjectFactory()
row = 0
self.floppy = []
path = config.get_floppydir()
for drive in ("A", "B"):
label = "Disk %c image:" % drive
fname = config.get_floppy(row)
fsel, box = factory.get(label, path, fname, gtk.FILE_CHOOSER_ACTION_OPEN)
table_add_widget_row(table, row, label, box)
self.floppy.append(fsel)
row += 1
protect = gtk.combo_box_new_text()
for text in config.get_protection_types():
protect.append_text(text)
protect.set_active(config.get_floppy_protection())
protect.set_tooltip_text("Write protect floppy image contents")
table_add_widget_row(table, row, "Write protection:", protect)
row += 1
ds = gtk.CheckButton("Double sided drives")
ds.set_active(config.get_doublesided())
ds.set_tooltip_text("Whether drives are double or single sided. Can affect behavior of some games")
table_add_widget_row(table, row, None, ds)
row += 1
driveb = gtk.CheckButton("Drive B connected")
driveb.set_active(config.get_floppy_drives()[1])
driveb.set_tooltip_text("Wheter drive B is connected. Can affect behavior of some demos & games")
table_add_widget_row(table, row, None, driveb)
row += 1
fastfdc = gtk.CheckButton("Fast floppy access")
fastfdc.set_active(config.get_fastfdc())
fastfdc.set_tooltip_text("Can cause incompatibilities with some games/demos")
table_add_widget_row(table, row, None, fastfdc)
table.show_all()
self.protect = protect
self.fastfdc = fastfdc
self.driveb = driveb
self.ds = ds
def run(self, config):
"run(config), show disk image dialog"
if not self.dialog:
self._create_dialog(config)
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_APPLY:
config.lock_updates()
for drive in range(2):
config.set_floppy(drive, self.floppy[drive].get_filename())
config.set_floppy_protection(self.protect.get_active())
config.set_doublesided(self.ds.get_active())
config.set_fastfdc(self.fastfdc.get_active())
drives = (config.get_floppy_drives()[0], self.driveb.get_active())
config.set_floppy_drives(drives)
config.flush_updates()
# ----------------------------------
# Hard disk dialog
class HardDiskDialog(HatariUIDialog):
def _create_dialog(self, config):
table, self.dialog = create_table_dialog(self.parent, "Hard disks", 4, 4, "Set and reboot")
factory = FselAndEjectFactory()
row = 0
label = "ASCI HD image:"
path = config.get_acsi_image()
fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
table_add_widget_row(table, row, label, box, True)
self.acsi = fsel
row += 1
label = "IDE HD master image:"
path = config.get_idemaster_image()
fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
table_add_widget_row(table, row, label, box, True)
self.idemaster = fsel
row += 1
label = "IDE HD slave image:"
path = config.get_ideslave_image()
fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
table_add_widget_row(table, row, label, box, True)
self.ideslave = fsel
row += 1
table_add_widget_row(table, row, " ", gtk.HSeparator(), True)
row += 1
label = "GEMDOS drive directory:"
path = config.get_hd_dir()
fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
table_add_widget_row(table, row, label, box, True)
self.hddir = fsel
row += 1
hddrive = gtk.combo_box_new_text()
for text in config.get_hd_drives():
hddrive.append_text(text)
hddrive.set_tooltip_text("Whether GEMDOS HD emulation uses fixed drive letter, or first free drive letter after ASCI & IDE drives (detection unreliable)")
table_add_widget_row(table, row, "GEMDOS HD drive:", hddrive)
self.hddrive = hddrive
row += 1
protect = gtk.combo_box_new_text()
for text in config.get_protection_types():
protect.append_text(text)
protect.set_tooltip_text("Whether/how to write protect (GEMDOS HD) emulation files, 'auto' means using host files' own properties")
table_add_widget_row(table, row, "Write protection:", protect)
self.protect = protect
row += 1
lower = gtk.combo_box_new_text()
for text in config.get_hd_cases():
lower.append_text(text)
lower.set_tooltip_text("What to do with names of files created by Atari programs through GEMDOS HD emulation")
table_add_widget_row(table, row, "File names:", lower)
self.lower = lower
table.show_all()
def _get_config(self, config):
path = config.get_acsi_image()
if path:
self.acsi.set_filename(path)
path = config.get_idemaster_image()
if path:
self.idemaster.set_filename(path)
path = config.get_ideslave_image()
if path:
self.ideslave.set_filename(path)
path = config.get_hd_dir()
if path:
self.hddir.set_filename(path)
self.hddrive.set_active(config.get_hd_drive())
self.protect.set_active(config.get_hd_protection())
self.lower.set_active(config.get_hd_case())
def _set_config(self, config):
config.lock_updates()
config.set_acsi_image(self.acsi.get_filename())
config.set_idemaster_image(self.idemaster.get_filename())
config.set_ideslave_image(self.ideslave.get_filename())
config.set_hd_dir(self.hddir.get_filename())
config.set_hd_drive(self.hddrive.get_active())
config.set_hd_protection(self.protect.get_active())
config.set_hd_case(self.lower.get_active())
config.flush_updates()
def run(self, config):
"run(config) -> bool, whether to reboot"
if not self.dialog:
self._create_dialog(config)
self._get_config(config)
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_APPLY:
self._set_config(config)
return True
return False
# ---------------------------
# Display dialog
class DisplayDialog(HatariUIDialog):
def _create_dialog(self, config):
skip = gtk.combo_box_new_text()
for text in config.get_frameskip_names():
skip.append_text(text)
skip.set_active(config.get_frameskip())
skip.set_tooltip_text("Set how many frames are skipped to speed up emulation")
slow = gtk.combo_box_new_text()
for text in config.get_slowdown_names():
slow.append_text(text)
slow.set_active(0)
slow.set_tooltip_text("VBL wait multiplier to slow down emulation. Breaks sound and large enough slowdown causes mouse clicks not to work.")
maxw, maxh = config.get_max_size()
topw, toph = config.get_desktop_size()
maxadjw = gtk.Adjustment(maxw, 320, topw, 8, 40)
maxadjh = gtk.Adjustment(maxh, 200, toph, 8, 40)
scalew = gtk.HScale(maxadjw)
scaleh = gtk.HScale(maxadjh)
scalew.set_digits(0)
scaleh.set_digits(0)
scalew.set_tooltip_text("Preferred/maximum zoomed width")
scaleh.set_tooltip_text("Preferred/maximum zoomed height")
force_max = gtk.CheckButton("Force max resolution (Falcon)")
force_max.set_active(config.get_force_max())
force_max.set_tooltip_text("Whether to force maximum resolution to help recording videos of demos which do resolution changes")
desktop = gtk.CheckButton("Keep desktop resolution (Falcon/TT)")
desktop.set_active(config.get_desktop())
desktop.set_tooltip_text("Whether to keep screen resolution in fullscreen and (try to) scale Atari screen by an integer factor instead")
desktop_st = gtk.CheckButton("Keep desktop resolution (ST/STE)")
desktop_st.set_active(config.get_desktop_st())
desktop_st.set_tooltip_text("Whether to keep screen resolution in fullscreen to avoid potential sound skips + delay (NO SCALING)")
borders = gtk.CheckButton("Atari screen borders")
borders.set_active(config.get_borders())
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")
statusbar = gtk.CheckButton("Show statusbar")
statusbar.set_active(config.get_statusbar())
statusbar.set_tooltip_text("Whether to show statusbar with floppy leds etc")
led = gtk.CheckButton("Show overlay led")
led.set_active(config.get_led())
led.set_tooltip_text("Whether to show overlay drive led when statusbar isn't visible")
crop = gtk.CheckButton("Remove statusbar from screen capture")
crop.set_active(config.get_crop())
crop.set_tooltip_text("Whether to crop statusbar from screenshots and video recordings")
dialog = gtk.Dialog("Display settings", self.parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
dialog.vbox.add(gtk.Label("Max zoomed size:"))
dialog.vbox.add(scalew)
dialog.vbox.add(scaleh)
dialog.vbox.add(force_max)
dialog.vbox.add(desktop)
dialog.vbox.add(desktop_st)
dialog.vbox.add(borders)
dialog.vbox.add(gtk.Label("Frameskip:"))
dialog.vbox.add(skip)
dialog.vbox.add(gtk.Label("Slowdown:"))
dialog.vbox.add(slow)
dialog.vbox.add(statusbar)
dialog.vbox.add(led)
dialog.vbox.add(crop)
dialog.vbox.show_all()
self.dialog = dialog
self.skip = skip
self.slow = slow
self.maxw = maxadjw
self.maxh = maxadjh
self.force_max = force_max
self.desktop = desktop
self.desktop_st = desktop_st
self.borders = borders
self.statusbar = statusbar
self.led = led
self.crop = crop
def run(self, config):
"run(config), show display dialog"
if not self.dialog:
self._create_dialog(config)
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_APPLY:
config.lock_updates()
config.set_frameskip(self.skip.get_active())
config.set_slowdown(self.slow.get_active())
config.set_max_size(self.maxw.get_value(), self.maxh.get_value())
config.set_force_max(self.force_max.get_active())
config.set_desktop(self.desktop.get_active())
config.set_desktop_st(self.desktop_st.get_active())
config.set_borders(self.borders.get_active())
config.set_statusbar(self.statusbar.get_active())
config.set_led(self.led.get_active())
config.set_crop(self.crop.get_active())
config.flush_updates()
# ----------------------------------
# Joystick dialog
class JoystickDialog(HatariUIDialog):
def _create_dialog(self, config):
table, self.dialog = create_table_dialog(self.parent, "Joystick settings", 9, 2)
joy = 0
self.joy = []
joytypes = config.get_joystick_types()
for label in config.get_joystick_names():
combo = gtk.combo_box_new_text()
for text in joytypes:
combo.append_text(text)
combo.set_active(config.get_joystick(joy))
widget = table_add_widget_row(table, joy, "%s:" % label, combo)
self.joy.append(widget)
joy += 1
table.show_all()
def run(self, config):
"run(config), show joystick dialog"
if not self.dialog:
self._create_dialog(config)
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_APPLY:
config.lock_updates()
for joy in range(6):
config.set_joystick(joy, self.joy[joy].get_active())
config.flush_updates()
# ---------------------------------------
# Peripherals (midi,printer,rs232) dialog
class PeripheralDialog(HatariUIDialog):
def _create_dialog(self, config):
midi = gtk.CheckButton("Enable MIDI")
midi.set_active(config.get_midi())
printer = gtk.CheckButton("Enable printer output")
printer.set_active(config.get_printer())
rs232 = gtk.CheckButton("Enable RS232")
rs232.set_active(config.get_rs232())
dialog = gtk.Dialog("Peripherals", self.parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
dialog.vbox.add(midi)
dialog.vbox.add(printer)
dialog.vbox.add(rs232)
dialog.vbox.show_all()
self.dialog = dialog
self.printer = printer
self.rs232 = rs232
self.midi = midi
def run(self, config):
"run(config), show peripherals dialog"
if not self.dialog:
self._create_dialog(config)
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_APPLY:
config.lock_updates()
config.set_midi(self.midi.get_active())
config.set_printer(self.printer.get_active())
config.set_rs232(self.rs232.get_active())
config.flush_updates()
# ---------------------------------------
# Path dialog
class PathDialog(HatariUIDialog):
def _create_dialog(self, config):
paths = config.get_paths()
table, self.dialog = create_table_dialog(self.parent, "File path settings", len(paths), 2)
paths.sort()
row = 0
self.paths = []
for (key, path, label) in paths:
fsel = FselEntry(self.dialog, self._validate_fname, key)
fsel.set_filename(path)
self.paths.append((key, fsel))
table_add_widget_row(table, row, label, fsel.get_container())
row += 1
table.show_all()
def _validate_fname(self, key, fname):
if key != "soundout":
return True
if fname.rsplit(".", 1)[-1].lower() in ("ym", "wav"):
return True
ErrorDialog(self.dialog).run("Sound output file name:\n\t%s\nneeds to end with '.ym' or '.wav'." % fname)
return False
def run(self, config):
"run(config), show paths dialog"
if not self.dialog:
self._create_dialog(config)
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_APPLY:
paths = []
for key, fsel in self.paths:
paths.append((key, fsel.get_filename()))
config.set_paths(paths)
# ---------------------------
# Sound dialog
class SoundDialog(HatariUIDialog):
def _create_dialog(self, config):
enabled, curhz = config.get_sound()
self.enabled = gtk.CheckButton("Sound enabled")
self.enabled.set_active(enabled)
hz = gtk.combo_box_new_text()
for text in config.get_sound_values():
hz.append_text(text)
hz.set_active(curhz)
self.hz = hz
ymmixer = gtk.combo_box_new_text()
for text in config.get_ymmixer_types():
ymmixer.append_text(text)
ymmixer.set_active(config.get_ymmixer())
self.ymmixer = ymmixer
adj = gtk.Adjustment(config.get_bufsize(), 0, 110, 10, 10, 10)
bufsize = gtk.HScale(adj)
bufsize.set_digits(0)
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.")
self.bufsize = bufsize
self.sync = gtk.CheckButton("Emulation speed synched to sound output")
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.")
self.sync.set_active(config.get_sync())
self.mic = gtk.CheckButton("Enable (Falcon) microphone")
self.mic.set_active(config.get_mic())
dialog = gtk.Dialog("Sound settings", self.parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
dialog.vbox.add(self.enabled)
dialog.vbox.add(gtk.Label("Sound frequency::"))
dialog.vbox.add(hz)
dialog.vbox.add(gtk.Label("YM voices mixing method:"))
dialog.vbox.add(ymmixer)
dialog.vbox.add(gtk.Label("SDL sound buffer size (ms):"))
dialog.vbox.add(bufsize)
dialog.vbox.add(self.sync)
dialog.vbox.add(self.mic)
dialog.vbox.show_all()
self.dialog = dialog
def run(self, config):
"run(config), show sound dialog"
if not self.dialog:
self._create_dialog(config)
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_APPLY:
config.lock_updates()
config.set_mic(self.mic.get_active())
config.set_sync(self.sync.get_active())
config.set_bufsize(self.bufsize.get_value())
config.set_ymmixer(self.ymmixer.get_active())
enabled = self.enabled.get_active()
hz = self.hz.get_active()
config.set_sound(enabled, hz)
config.flush_updates()
# ---------------------------
# Trace settings dialog
class TraceDialog(HatariUIDialog):
# you can get this list with:
# hatari --trace help 2>&1|awk '/all$/{next} /^ [^-]/ {printf("\"%s\",\n", $1)}'
tracepoints = [
"video_sync",
"video_res",
"video_color",
"video_border_v",
"video_border_h",
"video_addr",
"video_hbl",
"video_vbl",
"video_ste",
"mfp_exception",
"mfp_start",
"mfp_read",
"mfp_write",
"psg_read",
"psg_write",
"cpu_pairing",
"cpu_disasm",
"cpu_exception",
"int",
"fdc",
"acia",
"ikbd_cmds",
"ikbd_acia",
"ikbd_exec",
"blitter",
"bios",
"xbios",
"gemdos",
"vdi",
"aes",
"io_read",
"io_write",
"dmasound",
"crossbar",
"videl",
"dsp_host_interface",
"dsp_host_command",
"dsp_host_ssi",
"dsp_interrupt",
"dsp_disasm",
"dsp_disasm_reg",
"dsp_disasm_mem",
"dsp_state",
"dsp_symbols",
"cpu_symbols",
"nvram",
"scsi_cmd",
"natfeats",
"keymap",
"midi",
"ide",
]
def __init__(self, parent):
self.savedpoints = None
hbox1 = gtk.HBox()
hbox1.add(create_button("Load", self._load_traces))
hbox1.add(create_button("Clear", self._clear_traces))
hbox1.add(create_button("Save", self._save_traces))
hbox2 = gtk.HBox()
vboxes = []
for idx in (0,1,2,3):
vboxes.append(gtk.VBox())
hbox2.add(vboxes[idx])
count = 0
per_side = (len(self.tracepoints)+3)/4
self.tracewidgets = {}
for trace in self.tracepoints:
name = trace.replace("_", "-")
widget = gtk.CheckButton(name)
self.tracewidgets[trace] = widget
vboxes[count/per_side].pack_start(widget, False, True)
count += 1
dialog = gtk.Dialog("Trace settings", parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
dialog.vbox.add(hbox1)
dialog.vbox.add(gtk.Label("Select trace points:"))
dialog.vbox.add(hbox2)
dialog.vbox.show_all()
self.dialog = dialog
def _get_traces(self):
traces = []
for trace in self.tracepoints:
if self.tracewidgets[trace].get_active():
traces.append(trace)
if traces:
return ",".join(traces)
return "none"
def _set_traces(self, tracepoints):
self._clear_traces()
if not tracepoints:
return
for trace in tracepoints.split(","):
if trace in self.tracewidgets:
self.tracewidgets[trace].set_active(True)
else:
print("ERROR: unknown trace setting '%s'" % trace)
def _clear_traces(self, widget = None):
for trace in self.tracepoints:
self.tracewidgets[trace].set_active(False)
def _load_traces(self, widget):
# this doesn't load traces, just sets them from internal variable
# that run method gets from caller and sets. It's up to caller
# whether the saving or loading happens actually to disk
self._set_traces(self.savedpoints)
def _save_traces(self, widget):
self.savedpoints = self._get_traces()
def run(self, hatari, savedpoints):
"run(hatari,tracepoints) -> tracepoints, caller saves tracepoints"
self.savedpoints = savedpoints
while 1:
response = self.dialog.run()
if response == gtk.RESPONSE_APPLY:
hatari.change_option("--trace %s" % self._get_traces())
else:
self.dialog.hide()
return self.savedpoints
# ------------------------------------------
# Machine dialog for settings needing reboot
class MachineDialog(HatariUIDialog):
def _machine_cb(self, widget, data):
if not widget.get_active():
return
machine = data.lower()
if machine == "ste" or machine == "st":
self.clocks[0].set_active(True)
self.cpulevel.set_active(0)
elif machine == "falcon":
self.clocks[1].set_active(True)
self.dsps[2].set_active(True)
self.cpulevel.set_active(3)
elif machine == "tt":
self.clocks[2].set_active(True)
self.cpulevel.set_active(3)
def _create_dialog(self, config):
table, self.dialog = create_table_dialog(self.parent, "Machine configuration", 6, 4, "Set and reboot")
row = 0
self.machines = table_add_radio_rows(table, row, "Machine:",
config.get_machine_types(), self._machine_cb)
row += 1
self.dsps = table_add_radio_rows(table, row, "DSP type:", config.get_dsp_types())
row += 1
# start next table column
row = 0
table_set_col_offset(table, 2)
self.monitors = table_add_radio_rows(table, row, "Monitor:", config.get_monitor_types())
row += 1
self.clocks = table_add_radio_rows(table, row, "CPU clock:", config.get_cpuclock_types())
row += 1
# fullspan at bottom
fullspan = True
combo = gtk.combo_box_new_text()
for text in config.get_cpulevel_types():
combo.append_text(text)
self.cpulevel = table_add_widget_row(table, row, "CPU type:", combo, fullspan)
row += 1
combo = gtk.combo_box_new_text()
for text in config.get_memory_names():
combo.append_text(text)
self.memory = table_add_widget_row(table, row, "Memory:", combo, fullspan)
row += 1
self.ttram = gtk.Adjustment(config.get_ttram(), 0, 260, 4, 4, 4)
ttram = gtk.HScale(self.ttram)
ttram.set_digits(0)
ttram.set_tooltip_text("TT-RAM needs Falcon/TT with WinUAE CPU core and implies 32-bit addressing. 0 = disabled, 24-bit addressing.")
table_add_widget_row(table, row, "TT-RAM", ttram, fullspan)
row += 1
label = "TOS image:"
fsel = self._fsel(label, gtk.FILE_CHOOSER_ACTION_OPEN)
self.tos = table_add_widget_row(table, row, label, fsel, fullspan)
row += 1
vbox = gtk.VBox()
self.compatible = gtk.CheckButton("Compatible CPU")
self.rtc = gtk.CheckButton("Real-time clock")
self.timerd = gtk.CheckButton("Patch Timer-D")
self.compatible.set_tooltip_text("Needed for overscan and other timing sensitive things to work correctly")
self.rtc.set_tooltip_text("Some rare games and demos don't work with this")
self.timerd.set_tooltip_text("Improves ST/STE emulation performance, but some rare demos/games don't work with this")
vbox.add(self.compatible)
vbox.add(self.timerd)
vbox.add(self.rtc)
table_add_widget_row(table, row, "Misc.:", vbox, fullspan)
row += 1
table.show_all()
def _fsel(self, label, action):
fsel = gtk.FileChooserButton(label)
# Hatari cannot access URIs
fsel.set_local_only(True)
fsel.set_width_chars(12)
fsel.set_action(action)
return fsel
def _get_config(self, config):
self.machines[config.get_machine()].set_active(True)
self.monitors[config.get_monitor()].set_active(True)
self.clocks[config.get_cpuclock()].set_active(True)
self.dsps[config.get_dsp()].set_active(True)
self.cpulevel.set_active(config.get_cpulevel())
self.memory.set_active(config.get_memory())
self.ttram.set_value(config.get_ttram())
tos = config.get_tos()
if tos:
self.tos.set_filename(tos)
self.compatible.set_active(config.get_compatible())
self.timerd.set_active(config.get_timerd())
self.rtc.set_active(config.get_rtc())
def _get_active_radio(self, radios):
idx = 0
for radio in radios:
if radio.get_active():
return idx
idx += 1
def _set_config(self, config):
config.lock_updates()
config.set_machine(self._get_active_radio(self.machines))
config.set_monitor(self._get_active_radio(self.monitors))
config.set_cpuclock(self._get_active_radio(self.clocks))
config.set_dsp(self._get_active_radio(self.dsps))
config.set_cpulevel(self.cpulevel.get_active())
config.set_memory(self.memory.get_active())
config.set_ttram(self.ttram.get_value())
config.set_tos(self.tos.get_filename())
config.set_compatible(self.compatible.get_active())
config.set_timerd(self.timerd.get_active())
config.set_rtc(self.rtc.get_active())
config.flush_updates()
def run(self, config):
"run(config) -> bool, whether to reboot"
if not self.dialog:
self._create_dialog(config)
self._get_config(config)
response = self.dialog.run()
self.dialog.hide()
if response == gtk.RESPONSE_APPLY:
self._set_config(config)
return True
return False