class UICallbacks: tmpconfpath = os.path.expanduser("~/.hatari/.tmp.cfg") def __init__(self): # Hatari and configuration self.hatari = Hatari() error = self.hatari.is_compatible() if error: ErrorDialog(None).run(error) sys.exit(-1) self.config = HatariConfigMapping(self.hatari) try: self.config.validate() except (KeyError, AttributeError): NoteDialog(None).run("Loading Hatari configuration failed!\nRetrying after saving Hatari configuration.") self.hatari.save_config() self.config = HatariConfigMapping(self.hatari) self.config.validate() # windows are created when needed self.mainwin = None self.hatariwin = None self.debugui = None self.panels = {} # dialogs are created when needed self.aboutdialog = None self.inputdialog = None self.tracedialog = None self.resetdialog = None self.quitdialog = None self.killdialog = None self.floppydialog = None self.harddiskdialog = None self.displaydialog = None self.joystickdialog = None self.machinedialog = None self.peripheraldialog = None self.sounddialog = None self.pathdialog = None # used by run() self.memstate = None self.floppy = None self.io_id = None # TODO: Hatari UI own configuration settings save/load self.tracepoints = None def _reset_config_dialogs(self): self.floppydialog = None self.harddiskdialog = None self.displaydialog = None self.joystickdialog = None self.machinedialog = None self.peripheraldialog = None self.sounddialog = None self.pathdialog = None # ---------- create UI ---------------- def create_ui(self, accelgroup, menu, toolbars, fullscreen, embed): "create_ui(menu, toolbars, fullscreen, embed)" # add horizontal elements hbox = gtk.HBox() if toolbars["left"]: hbox.pack_start(toolbars["left"], False, True) if embed: self._add_uisocket(hbox) if toolbars["right"]: hbox.pack_start(toolbars["right"], False, True) # add vertical elements vbox = gtk.VBox() if menu: vbox.add(menu) if toolbars["top"]: vbox.pack_start(toolbars["top"], False, True) vbox.add(hbox) if toolbars["bottom"]: vbox.pack_start(toolbars["bottom"], False, True) # put them to main window mainwin = gtk.Window(gtk.WINDOW_TOPLEVEL) mainwin.set_title("%s %s" % (UInfo.name, UInfo.version)) mainwin.set_icon_from_file(UInfo.icon) if accelgroup: mainwin.add_accel_group(accelgroup) if fullscreen: mainwin.fullscreen() mainwin.add(vbox) mainwin.show_all() # for run and quit callbacks self.killdialog = KillDialog(mainwin) mainwin.connect("delete_event", self.quit) self.mainwin = mainwin def _add_uisocket(self, box): # add Hatari parent container to given box socket = gtk.Socket() # without this, closing Hatari would remove the socket widget socket.connect("plug-removed", lambda obj: True) socket.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) socket.set_events(gtk.gdk.ALL_EVENTS_MASK) socket.set_flags(gtk.CAN_FOCUS) # set max Hatari window size = desktop size self.config.set_desktop_size(gtk.gdk.screen_width(), gtk.gdk.screen_height()) # set initial embedded hatari size width, height = self.config.get_window_size() socket.set_size_request(width, height) # no resizing for the Hatari window box.pack_start(socket, False, False) self.hatariwin = socket # ------- run callback ----------- def _socket_cb(self, fd, event): if event != gobject.IO_IN: # hatari process died, make sure Hatari instance notices self.hatari.kill() return False width, height = self.hatari.get_embed_info() print("New size = %d x %d" % (width, height)) oldwidth, oldheight = self.hatariwin.get_size_request() self.hatariwin.set_size_request(width, height) if width < oldwidth or height < oldheight: # force also mainwin smaller (it automatically grows) self.mainwin.resize(width, height) return True def run(self, widget = None): if not self.killdialog.run(self.hatari): return if self.io_id: gobject.source_remove(self.io_id) args = ["--configfile"] # whether to use Hatari config or unsaved Hatari UI config? if self.config.is_changed(): args += [self.config.save_tmp(self.tmpconfpath)] else: args += [self.config.get_path()] if self.memstate: args += self.memstate # only way to change boot order is to specify disk on command line if self.floppy: args += self.floppy if self.hatariwin: size = self.hatariwin.window.get_size() self.hatari.run(args, self.hatariwin.window) # get notifications of Hatari window size changes self.hatari.enable_embed_info() socket = self.hatari.get_control_socket().fileno() events = gobject.IO_IN | gobject.IO_HUP | gobject.IO_ERR self.io_id = gobject.io_add_watch(socket, events, self._socket_cb) # all keyboard events should go to Hatari window self.hatariwin.grab_focus() else: self.hatari.run(args) def set_floppy(self, floppy): self.floppy = floppy # ------- quit callback ----------- def quit(self, widget, arg = None): # due to Gtk API, needs to return True when *not* quitting if not self.killdialog.run(self.hatari): return True if self.io_id: gobject.source_remove(self.io_id) if self.config.is_changed(): if not self.quitdialog: self.quitdialog = QuitSaveDialog(self.mainwin) if not self.quitdialog.run(self.config): return True gtk.main_quit() if os.path.exists(self.tmpconfpath): os.unlink(self.tmpconfpath) # continue to mainwin destroy if called by delete_event return False # ------- pause callback ----------- def pause(self, widget): if widget.get_active(): self.hatari.pause() else: self.hatari.unpause() # dialogs # ------- reset callback ----------- def reset(self, widget): if not self.resetdialog: self.resetdialog = ResetDialog(self.mainwin) self.resetdialog.run(self.hatari) # ------- about callback ----------- def about(self, widget): if not self.aboutdialog: self.aboutdialog = AboutDialog(self.mainwin) self.aboutdialog.run() # ------- input callback ----------- def inputs(self, widget): if not self.inputdialog: self.inputdialog = InputDialog(self.mainwin) self.inputdialog.run(self.hatari) # ------- floppydisk callback ----------- def floppydisk(self, widget): if not self.floppydialog: self.floppydialog = FloppyDialog(self.mainwin) self.floppydialog.run(self.config) # ------- harddisk callback ----------- def harddisk(self, widget): if not self.harddiskdialog: self.harddiskdialog = HardDiskDialog(self.mainwin) self.harddiskdialog.run(self.config) # ------- display callback ----------- def display(self, widget): if not self.displaydialog: self.displaydialog = DisplayDialog(self.mainwin) self.displaydialog.run(self.config) # ------- joystick callback ----------- def joystick(self, widget): if not self.joystickdialog: self.joystickdialog = JoystickDialog(self.mainwin) self.joystickdialog.run(self.config) # ------- machine callback ----------- def machine(self, widget): if not self.machinedialog: self.machinedialog = MachineDialog(self.mainwin) if self.machinedialog.run(self.config): self.hatari.trigger_shortcut("coldreset") # ------- peripheral callback ----------- def peripheral(self, widget): if not self.peripheraldialog: self.peripheraldialog = PeripheralDialog(self.mainwin) self.peripheraldialog.run(self.config) # ------- sound callback ----------- def sound(self, widget): if not self.sounddialog: self.sounddialog = SoundDialog(self.mainwin) self.sounddialog.run(self.config) # ------- path callback ----------- def path(self, widget): if not self.pathdialog: self.pathdialog = PathDialog(self.mainwin) self.pathdialog.run(self.config) # ------- debug callback ----------- def debugger(self, widget): if not self.debugui: self.debugui = HatariDebugUI(self.hatari) self.debugui.show() # ------- trace callback ----------- def trace(self, widget): if not self.tracedialog: self.tracedialog = TraceDialog(self.mainwin) self.tracepoints = self.tracedialog.run(self.hatari, self.tracepoints) # ------ snapshot load/save callbacks --------- def load(self, widget): path = os.path.expanduser("~/.hatari/hatari.sav") filename = get_open_filename("Select snapshot", self.mainwin, path) if filename: self.memstate = ["--memstate", filename] self.run() return True return False def save(self, widget): self.hatari.trigger_shortcut("savemem") # ------ config load/save callbacks --------- def config_load(self, widget): path = self.config.get_path() filename = get_open_filename("Select configuration file", self.mainwin, path) if filename: self.hatari.change_option("--configfile %s" % filename) self.config.load(filename) self._reset_config_dialogs() return True return False def config_save(self, widget): path = self.config.get_path() filename = get_save_filename("Save configuration as...", self.mainwin, path) if filename: self.config.save_as(filename) return True return False # ------- fast forward callback ----------- def set_fastforward(self, widget): self.config.set_fastforward(widget.get_active()) def get_fastforward(self): return self.config.get_fastforward() # ------- fullscreen callback ----------- def set_fullscreen(self, widget): # if user can select this, Hatari isn't in fullscreen self.hatari.change_option("--fullscreen") # ------- screenshot callback ----------- def screenshot(self, widget): self.hatari.trigger_shortcut("screenshot") # ------- record callbacks ----------- def recanim(self, widget): self.hatari.trigger_shortcut("recanim") def recsound(self, widget): self.hatari.trigger_shortcut("recsound") # ------- insert key special callback ----------- def keypress(self, widget, code): self.hatari.insert_event("keypress %s" % code) def textinsert(self, widget, text): HatariTextInsert(self.hatari, text) # ------- panel callback ----------- def panel(self, action, box): title = action.get_name() if title not in self.panels: window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_transient_for(self.mainwin) window.set_icon_from_file(UInfo.icon) window.set_title(title) window.add(box) window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) window.connect("delete_event", window_hide_cb) self.panels[title] = window else: window = self.panels[title] window.show_all() window.deiconify()