|
|
1.1 ! root 1: #!/usr/bin/env python ! 2: # ! 3: # Classes for Hatari emulator instance and mapping its congfiguration ! 4: # variables with its command line option. ! 5: # ! 6: # Copyright (C) 2008-2010 by Eero Tamminen <eerot at berlios> ! 7: # ! 8: # This program is free software; you can redistribute it and/or modify ! 9: # it under the terms of the GNU General Public License as published by ! 10: # the Free Software Foundation; either version 2 of the License, or ! 11: # (at your option) any later version. ! 12: # ! 13: # This program is distributed in the hope that it will be useful, ! 14: # but WITHOUT ANY WARRANTY; without even the implied warranty of ! 15: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 16: # GNU General Public License for more details. ! 17: ! 18: import os ! 19: import sys ! 20: import time ! 21: import signal ! 22: import socket ! 23: import select ! 24: from config import ConfigStore ! 25: ! 26: ! 27: # Running Hatari instance ! 28: class Hatari: ! 29: "running hatari instance and methods for communicating with it" ! 30: basepath = "/tmp/hatari-ui-" + str(os.getpid()) ! 31: logpath = basepath + ".log" ! 32: tracepath = basepath + ".trace" ! 33: debugpath = basepath + ".debug" ! 34: controlpath = basepath + ".socket" ! 35: server = None # singleton due to path being currently one per user ! 36: ! 37: def __init__(self, hataribin = None): ! 38: # collect hatari process zombies without waitpid() ! 39: signal.signal(signal.SIGCHLD, signal.SIG_IGN) ! 40: if hataribin: ! 41: self.hataribin = hataribin ! 42: else: ! 43: self.hataribin = "hatari" ! 44: self._create_server() ! 45: self.control = None ! 46: self.paused = False ! 47: self.pid = 0 ! 48: ! 49: def is_compatible(self): ! 50: "check Hatari compatibility and return error string if it's not" ! 51: for line in os.popen(self.hataribin + " -h").readlines(): ! 52: if line.find("--control-socket") >= 0: ! 53: return None ! 54: return "Hatari not found or it doesn't support the required --control-socket option!" ! 55: ! 56: def save_config(self): ! 57: os.popen(self.hataribin + " --saveconfig") ! 58: ! 59: def _create_server(self): ! 60: if self.server: ! 61: return ! 62: self.server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) ! 63: if os.path.exists(self.controlpath): ! 64: os.unlink(self.controlpath) ! 65: self.server.bind(self.controlpath) ! 66: self.server.listen(1) ! 67: ! 68: def _send_message(self, msg): ! 69: if self.control: ! 70: self.control.send(msg) ! 71: return True ! 72: else: ! 73: print "ERROR: no Hatari (control socket)" ! 74: return False ! 75: ! 76: def change_option(self, option): ! 77: "change_option(option), changes given Hatari cli option" ! 78: return self._send_message("hatari-option %s\n" % option) ! 79: ! 80: def set_path(self, key, path): ! 81: "set_path(key, path), sets path with given key" ! 82: return self._send_message("hatari-path %s %s\n" % (key, path)) ! 83: ! 84: def set_device(self, device, enabled): ! 85: # needed because CLI options cannot disable devices, only enable ! 86: "set_path(device, enabled), sets whether given device is enabled or not" ! 87: if enabled: ! 88: return self._send_message("hatari-enable %s\n" % device) ! 89: else: ! 90: return self._send_message("hatari-disable %s\n" % device) ! 91: ! 92: def trigger_shortcut(self, shortcut): ! 93: "trigger_shortcut(shortcut), triggers given Hatari (keyboard) shortcut" ! 94: return self._send_message("hatari-shortcut %s\n" % shortcut) ! 95: ! 96: def insert_event(self, event): ! 97: "insert_event(event), synthetizes given key/mouse Atari event" ! 98: return self._send_message("hatari-event %s\n" % event) ! 99: ! 100: def debug_command(self, cmd): ! 101: "debug_command(command), runs given Hatari debugger command" ! 102: return self._send_message("hatari-debug %s\n" % cmd) ! 103: ! 104: def pause(self): ! 105: "pause(), pauses Hatari emulation" ! 106: return self._send_message("hatari-stop\n") ! 107: ! 108: def unpause(self): ! 109: "unpause(), continues Hatari emulation" ! 110: return self._send_message("hatari-cont\n") ! 111: ! 112: def _open_output_file(self, hataricommand, option, path): ! 113: if os.path.exists(path): ! 114: os.unlink(path) ! 115: # TODO: why fifo doesn't work properly (blocks forever on read or ! 116: # reads only byte at the time and stops after first newline)? ! 117: #os.mkfifo(path) ! 118: #raw_input("attach strace now, then press Enter\n") ! 119: ! 120: # ask Hatari to open/create the requested output file... ! 121: hataricommand("%s %s" % (option, path)) ! 122: wait = 0.025 ! 123: # ...and wait for it to appear before returning it ! 124: for i in range(0, 8): ! 125: time.sleep(wait) ! 126: if os.path.exists(path): ! 127: return open(path, "r") ! 128: wait += wait ! 129: return None ! 130: ! 131: def open_debug_output(self): ! 132: "open_debug_output() -> file, opens Hatari debugger output file" ! 133: return self._open_output_file(self.debug_command, "f", self.debugpath) ! 134: ! 135: def open_trace_output(self): ! 136: "open_trace_output() -> file, opens Hatari tracing output file" ! 137: return self._open_output_file(self.change_option, "--trace-file", self.tracepath) ! 138: ! 139: def open_log_output(self): ! 140: "open_trace_output() -> file, opens Hatari debug log file" ! 141: return self._open_output_file(self.change_option, "--log-file", self.logpath) ! 142: ! 143: def get_lines(self, fileobj): ! 144: "get_lines(file) -> list of lines readable from given Hatari output file" ! 145: # wait until data is available, then wait for some more ! 146: # and only then the data can be read, otherwise its old ! 147: print "Request&wait data from Hatari..." ! 148: select.select([fileobj], [], []) ! 149: time.sleep(0.1) ! 150: print "...read the data lines" ! 151: lines = fileobj.readlines() ! 152: print "".join(lines) ! 153: return lines ! 154: ! 155: def enable_embed_info(self): ! 156: "enable_embed_info(), request embedded Hatari window ID change information" ! 157: self._send_message("hatari-embed-info\n") ! 158: ! 159: def get_embed_info(self): ! 160: "get_embed_info() -> (width, height), get embedded Hatari window size" ! 161: width, height = self.control.recv(12).split("x") ! 162: return (int(width), int(height)) ! 163: ! 164: def get_control_socket(self): ! 165: "get_control_socket() -> socket which can be checked for embed ID changes" ! 166: return self.control ! 167: ! 168: def is_running(self): ! 169: "is_running() -> bool, True if Hatari is running, False otherwise" ! 170: if not self.pid: ! 171: return False ! 172: try: ! 173: os.waitpid(self.pid, os.WNOHANG) ! 174: except OSError, value: ! 175: print "Hatari PID %d had exited in the meanwhile:\n\t%s" % (self.pid, value) ! 176: self.pid = 0 ! 177: return False ! 178: return True ! 179: ! 180: def run(self, extra_args = None, parent_win = None): ! 181: "run([parent window][,embedding args]), runs Hatari" ! 182: # if parent_win given, embed Hatari to it ! 183: pid = os.fork() ! 184: if pid < 0: ! 185: print "ERROR: fork()ing Hatari failed!" ! 186: return ! 187: if pid: ! 188: # in parent ! 189: self.pid = pid ! 190: if self.server: ! 191: print "WAIT hatari to connect to control socket...", ! 192: (self.control, addr) = self.server.accept() ! 193: print "connected!" ! 194: else: ! 195: # child runs Hatari ! 196: env = os.environ ! 197: if parent_win: ! 198: self._set_embed_env(env, parent_win) ! 199: # callers need to take care of confirming quitting ! 200: args = [self.hataribin, "--confirm-quit", "off"] ! 201: if self.server: ! 202: args += ["--control-socket", self.controlpath] ! 203: if extra_args: ! 204: args += extra_args ! 205: print "RUN:", args ! 206: os.execvpe(self.hataribin, args, env) ! 207: ! 208: def _set_embed_env(self, env, parent_win): ! 209: if sys.platform == 'win32': ! 210: win_id = parent_win.handle ! 211: else: ! 212: win_id = parent_win.xid ! 213: # tell SDL to use given widget's window ! 214: #env["SDL_WINDOWID"] = str(win_id) ! 215: ! 216: # above is broken: when SDL uses a window it hasn't created itself, ! 217: # it for some reason doesn't listen to any events delivered to that ! 218: # window nor implements XEMBED protocol to get them in a way most ! 219: # friendly to embedder: ! 220: # http://standards.freedesktop.org/xembed-spec/latest/ ! 221: # ! 222: # Instead we tell hatari to reparent itself after creating ! 223: # its own window into this program widget window ! 224: env["PARENT_WIN_ID"] = str(win_id) ! 225: ! 226: def kill(self): ! 227: "kill(), kill Hatari if it's running" ! 228: if self.pid: ! 229: os.kill(self.pid, signal.SIGKILL) ! 230: print "killed hatari with PID %d" % self.pid ! 231: self.pid = 0 ! 232: if self.control: ! 233: self.control.close() ! 234: self.control = None ! 235: ! 236: ! 237: # Mapping of requested values both to Hatari configuration ! 238: # and command line options. ! 239: # ! 240: # By default this doesn't allow setting any other configuration ! 241: # variables than the ones that were read from the configuration ! 242: # file i.e. you get an exception if configuration variables ! 243: # don't match to current Hatari. So before using this the current ! 244: # Hatari configuration should have been saved at least once. ! 245: # ! 246: # Because of some inconsistencies in the values (see e.g. sound), ! 247: # this cannot just do these according to some mapping table, but ! 248: # it needs actual method for (each) setting. ! 249: class HatariConfigMapping(ConfigStore): ! 250: _paths = { ! 251: "memauto": ("[Memory]", "szAutoSaveFileName", "Automatic memory snapshot"), ! 252: "memsave": ("[Memory]", "szMemoryCaptureFileName", "Manual memory snapshot"), ! 253: "midiin": ("[Midi]", "sMidiInFileName", "Midi input"), ! 254: "midiout": ("[Midi]", "sMidiOutFileName", "Midi output"), ! 255: "rs232in": ("[RS232]", "szInFileName", "RS232 I/O input"), ! 256: "rs232out": ("[RS232]", "szOutFileName", "RS232 I/O output"), ! 257: "printout": ("[Printer]", "szPrintToFileName", "Printer output"), ! 258: "soundout": ("[Sound]", "szYMCaptureFileName", "Sound output") ! 259: } ! 260: "access methods to Hatari configuration file variables and command line options" ! 261: def __init__(self, hatari): ! 262: userconfdir = ".hatari" ! 263: ConfigStore.__init__(self, userconfdir) ! 264: conffilename = "hatari.cfg" ! 265: self.load(self.get_filepath(conffilename)) ! 266: ! 267: self._hatari = hatari ! 268: self._lock_updates = False ! 269: self._options = [] ! 270: ! 271: def validate(self): ! 272: "exception is thrown if the loaded configuration isn't compatible" ! 273: for method in dir(self): ! 274: if '_' not in method: ! 275: continue ! 276: # check class getters ! 277: starts = method[:method.find("_")] ! 278: if starts != "get": ! 279: continue ! 280: # but ignore getters for other things than config ! 281: ends = method[method.rfind("_")+1:] ! 282: if ends in ("types", "names", "values", "changes", "checkpoint", "filepath"): ! 283: continue ! 284: if ends in ("floppy", "joystick"): ! 285: # use port '0' for checks ! 286: getattr(self, method)(0) ! 287: else: ! 288: getattr(self, method)() ! 289: ! 290: def _change_option(self, option): ! 291: if self._lock_updates: ! 292: self._options.append(option) ! 293: else: ! 294: self._hatari.change_option(option) ! 295: ! 296: def lock_updates(self): ! 297: "lock_updates(), collect Hatari configuration changes" ! 298: self._lock_updates = True ! 299: ! 300: def flush_updates(self): ! 301: "flush_updates(), apply collected Hatari configuration changes" ! 302: self._lock_updates = False ! 303: if not self._options: ! 304: return ! 305: self._hatari.change_option(" ".join(self._options)) ! 306: self._options = [] ! 307: ! 308: # ------------ paths --------------- ! 309: def get_paths(self): ! 310: paths = [] ! 311: for key, item in self._paths.items(): ! 312: paths.append((key, self.get(item[0], item[1]), item[2])) ! 313: return paths ! 314: ! 315: def set_paths(self, paths): ! 316: for key, path in paths: ! 317: self.set(self._paths[key][0], self._paths[key][1], path) ! 318: self._hatari.set_path(key, path) ! 319: ! 320: # ------------ midi --------------- ! 321: def get_midi(self): ! 322: return self.get("[Midi]", "bEnableMidi") ! 323: ! 324: def set_midi(self, value): ! 325: self.set("[Midi]", "bEnableMidi", value) ! 326: self._hatari.set_device("midi", value) ! 327: ! 328: # ------------ printer --------------- ! 329: def get_printer(self): ! 330: return self.get("[Printer]", "bEnablePrinting") ! 331: ! 332: def set_printer(self, value): ! 333: self.set("[Printer]", "bEnablePrinting", value) ! 334: self._hatari.set_device("printer", value) ! 335: ! 336: # ------------ RS232 --------------- ! 337: def get_rs232(self): ! 338: return self.get("[RS232]", "bEnableRS232") ! 339: ! 340: def set_rs232(self, value): ! 341: self.set("[RS232]", "bEnableRS232", value) ! 342: self._hatari.set_device("rs232", value) ! 343: ! 344: # ------------ machine --------------- ! 345: def get_machine_types(self): ! 346: return ("ST", "STE", "TT", "Falcon") ! 347: ! 348: def get_machine(self): ! 349: return self.get("[System]", "nMachineType") ! 350: ! 351: def set_machine(self, value): ! 352: self.set("[System]", "nMachineType", value) ! 353: self._change_option("--machine %s" % ("st", "ste", "tt", "falcon")[value]) ! 354: ! 355: # ------------ CPU level --------------- ! 356: def get_cpulevel_types(self): ! 357: return ("68000", "68010", "68020", "68EC030+FPU", "68040") ! 358: ! 359: def get_cpulevel(self): ! 360: return self.get("[System]", "nCpuLevel") ! 361: ! 362: def set_cpulevel(self, value): ! 363: self.set("[System]", "nCpuLevel", value) ! 364: self._change_option("--cpulevel %d" % value) ! 365: ! 366: # ------------ CPU clock --------------- ! 367: def get_cpuclock_types(self): ! 368: return ("8 MHz", "16 MHz", "32 MHz") ! 369: ! 370: def get_cpuclock(self): ! 371: clocks = {8:0, 16: 1, 32:2} ! 372: return clocks[self.get("[System]", "nCpuFreq")] ! 373: ! 374: def set_cpuclock(self, value): ! 375: clocks = [8, 16, 32] ! 376: if value < 0 or value > 2: ! 377: print "WARNING: CPU clock idx %d, clock fixed to 8 Mhz" % value ! 378: value = 8 ! 379: else: ! 380: value = clocks[value] ! 381: self.set("[System]", "nCpuFreq", value) ! 382: self._change_option("--cpuclock %d" % value) ! 383: ! 384: # ------------ DSP type --------------- ! 385: def get_dsp_types(self): ! 386: return ("None", "Dummy", "Emulated") ! 387: ! 388: def get_dsp(self): ! 389: return self.get("[System]", "nDSPType") ! 390: ! 391: def set_dsp(self, value): ! 392: self.set("[System]", "nDSPType", value) ! 393: self._change_option("--dsp %s" % ("none", "dummy", "emu")[value]) ! 394: ! 395: # ------------ compatible --------------- ! 396: def get_compatible(self): ! 397: return self.get("[System]", "bCompatibleCpu") ! 398: ! 399: def set_compatible(self, value): ! 400: self.set("[System]", "bCompatibleCpu", value) ! 401: self._change_option("--compatible %s" % str(value)) ! 402: ! 403: # ------------ Timer-D --------------- ! 404: def get_timerd(self): ! 405: return self.get("[System]", "bPatchTimerD") ! 406: ! 407: def set_timerd(self, value): ! 408: self.set("[System]", "bPatchTimerD", value) ! 409: self._change_option("--timer-d %s" % str(value)) ! 410: ! 411: # ------------ fastforward --------------- ! 412: def get_fastforward(self): ! 413: return self.get("[System]", "bFastForward") ! 414: ! 415: def set_fastforward(self, value): ! 416: self.set("[System]", "bFastForward", value) ! 417: self._change_option("--fast-forward %s" % str(value)) ! 418: ! 419: # ------------ sound --------------- ! 420: def get_sound_values(self): ! 421: # 48kHz, 44.1kHz and STE/TT/Falcon DMA 50066Hz divisable values ! 422: return ("6000", "6258", "8000", "11025", "12000", "12517", ! 423: "16000", "22050", "24000", "25033", "32000", ! 424: "48000", "44100", "50066") ! 425: ! 426: def get_sound(self): ! 427: enabled = self.get("[Sound]", "bEnableSound") ! 428: hz = str(self.get("[Sound]", "nPlaybackFreq")) ! 429: return (enabled, hz) ! 430: ! 431: def set_sound(self, enabled, hz): ! 432: # map get_sound_values() index to Hatari config ! 433: try: ! 434: hz = int(hz) ! 435: except ValueError: ! 436: hz = 0 ! 437: if hz < 6000 or hz > 50066: ! 438: hz = 44100 ! 439: self.set("[Sound]", "nPlaybackFreq", hz) ! 440: self.set("[Sound]", "bEnableSound", enabled) ! 441: value = str(hz) ! 442: # and to cli option ! 443: if enabled: ! 444: self._change_option("--sound %s" % value) ! 445: else: ! 446: self._change_option("--sound off") ! 447: return value ! 448: ! 449: ! 450: # ----------- joystick -------------- ! 451: def get_joystick_types(self): ! 452: return ("Disabled", "Real joystick", "Keyboard") ! 453: ! 454: def get_joystick_names(self): ! 455: return ( ! 456: "ST Joystick 0", ! 457: "ST Joystick 1", ! 458: "STE Joypad A", ! 459: "STE Joypad B", ! 460: "Parport stick 1", ! 461: "Parport stick 2" ! 462: ) ! 463: ! 464: def get_joystick(self, port): ! 465: # return index to get_joystick_values() array ! 466: return self.get("[Joystick%d]" % port, "nJoystickMode") ! 467: ! 468: def set_joystick(self, port, value): ! 469: # map get_sound_values() index to Hatari config ! 470: self.set("[Joystick%d]" % port, "nJoystickMode", value) ! 471: joytype = ("none", "real", "keys")[value] ! 472: self._change_option("--joy%d %s" % (port, joytype)) ! 473: ! 474: # ------------ floppy image dir --------------- ! 475: def get_floppydir(self): ! 476: return self.get("[Floppy]", "szDiskImageDirectory") ! 477: ! 478: def set_floppydir(self, path): ! 479: return self.set("[Floppy]", "szDiskImageDirectory", path) ! 480: ! 481: # ------------ floppy disk images --------------- ! 482: def get_floppy(self, drive): ! 483: return self.get("[Floppy]", "szDisk%cFileName" % ("A", "B")[drive]) ! 484: ! 485: def set_floppy(self, drive, filename): ! 486: self.set("[Floppy]", "szDisk%cFileName" % ("A", "B")[drive], filename) ! 487: self._change_option("--disk-%c %s" % (("a", "b")[drive], filename)) ! 488: ! 489: # ------------ slow FDC access --------------- ! 490: def get_slowfdc(self): ! 491: return self.get("[Floppy]", "bSlowFloppy") ! 492: ! 493: def set_slowfdc(self, value): ! 494: self.set("[Floppy]", "bSlowFloppy", value) ! 495: self._change_option("--slowfdc %s" % str(value)) ! 496: ! 497: # ------------- disk protection ------------- ! 498: def get_protection_types(self): ! 499: return ("Off", "On", "Auto") ! 500: ! 501: def get_floppy_protection(self): ! 502: return self.get("[Floppy]", "nWriteProtection") ! 503: ! 504: def get_hd_protection(self): ! 505: return self.get("[HardDisk]", "nWriteProtection") ! 506: ! 507: def set_floppy_protection(self, value): ! 508: self.set("[Floppy]", "nWriteProtection", value) ! 509: self._change_option("--protect-floppy %s" % self.get_protection_types()[value]) ! 510: ! 511: def set_hd_protection(self, value): ! 512: self.set("[HardDisk]", "nWriteProtection", value) ! 513: self._change_option("--protect-hd %s" % self.get_protection_types()[value]) ! 514: ! 515: # ------------ GEMDOS HD (dir) --------------- ! 516: def get_gemdos_dir(self): ! 517: self.get("[HardDisk]", "bUseHardDiskDirectory") # for validation ! 518: return self.get("[HardDisk]", "szHardDiskDirectory") ! 519: ! 520: def set_gemdos_dir(self, dirname): ! 521: if dirname and os.path.isdir(dirname): ! 522: self.set("[HardDisk]", "bUseHardDiskDirectory", True) ! 523: self.set("[HardDisk]", "szHardDiskDirectory", dirname) ! 524: self._change_option("--harddrive %s" % dirname) ! 525: ! 526: # ------------ ACSI HD (file) --------------- ! 527: def get_acsi_image(self): ! 528: self.get("[HardDisk]", "bUseHardDiskImage") # for validation ! 529: return self.get("[HardDisk]", "szHardDiskImage") ! 530: ! 531: def set_acsi_image(self, filename): ! 532: if filename and os.path.isfile(filename): ! 533: self.set("[HardDisk]", "bUseHardDiskImage", True) ! 534: self.set("[HardDisk]", "szHardDiskImage", filename) ! 535: self._change_option("--acsi %s" % filename) ! 536: ! 537: # ------------ IDE master (file) --------------- ! 538: def get_idemaster_image(self): ! 539: self.get("[HardDisk]", "bUseIdeMasterHardDiskImage") # for validation ! 540: return self.get("[HardDisk]", "szIdeMasterHardDiskImage") ! 541: ! 542: def set_idemaster_image(self, filename): ! 543: if filename and os.path.isfile(filename): ! 544: self.set("[HardDisk]", "bUseIdeMasterHardDiskImage", True) ! 545: self.set("[HardDisk]", "szIdeMasterHardDiskImage", filename) ! 546: self._change_option("--ide-master %s" % filename) ! 547: ! 548: # ------------ IDE slave (file) --------------- ! 549: def get_ideslave_image(self): ! 550: self.get("[HardDisk]", "bUseIdeSlaveHardDiskImage") # for validation ! 551: return self.get("[HardDisk]", "szIdeSlaveHardDiskImage") ! 552: ! 553: def set_ideslave_image(self, filename): ! 554: if filename and os.path.isfile(filename): ! 555: self.set("[HardDisk]", "bUseIdeSlaveHardDiskImage", True) ! 556: self.set("[HardDisk]", "szIdeSlaveHardDiskImage", filename) ! 557: self._change_option("--ide-slave %s" % filename) ! 558: ! 559: # ------------ TOS ROM --------------- ! 560: def get_tos(self): ! 561: return self.get("[ROM]", "szTosImageFileName") ! 562: ! 563: def set_tos(self, filename): ! 564: self.set("[ROM]", "szTosImageFileName", filename) ! 565: self._change_option("--tos %s" % filename) ! 566: ! 567: # ------------ memory --------------- ! 568: def get_memory_names(self): ! 569: # empty item in list shouldn't be shown, filter them out ! 570: return ("512kB", "1MB", "2MB", "4MB", "8MB", "14MB") ! 571: ! 572: def get_memory(self): ! 573: "return index to what get_memory_names() returns" ! 574: sizemap = (0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5) ! 575: memsize = self.get("[Memory]", "nMemorySize") ! 576: if memsize >= 0 and memsize < len(sizemap): ! 577: return sizemap[memsize] ! 578: return 1 # default = 1BM ! 579: ! 580: def set_memory(self, idx): ! 581: # map memory item index to memory size ! 582: sizemap = (0, 1, 2, 4, 8, 14) ! 583: if idx >= 0 and idx < len(sizemap): ! 584: memsize = sizemap[idx] ! 585: else: ! 586: memsize = 1 ! 587: self.set("[Memory]", "nMemorySize", memsize) ! 588: self._change_option("--memsize %d" % memsize) ! 589: ! 590: # ------------ monitor --------------- ! 591: def get_monitor_types(self): ! 592: return ("Mono", "RGB", "VGA", "TV") ! 593: ! 594: def get_monitor(self): ! 595: return self.get("[Screen]", "nMonitorType") ! 596: ! 597: def set_monitor(self, value): ! 598: self.set("[Screen]", "nMonitorType", value) ! 599: self._change_option("--monitor %s" % ("mono", "rgb", "vga", "tv")[value]) ! 600: ! 601: # ------------ frameskip --------------- ! 602: def get_frameskip_names(self): ! 603: return ( ! 604: "Disabled", ! 605: "1 frame", ! 606: "2 frames", ! 607: "3 frames", ! 608: "4 frames", ! 609: "Automatic" ! 610: ) ! 611: ! 612: def get_frameskip(self): ! 613: fs = self.get("[Screen]", "nFrameSkips") ! 614: print "Frameskip", fs ! 615: if fs < 0 or fs > 5: ! 616: return 5 ! 617: return fs ! 618: ! 619: def set_frameskip(self, value): ! 620: value = int(value) # guarantee correct type ! 621: self.set("[Screen]", "nFrameSkips", value) ! 622: self._change_option("--frameskips %d" % value) ! 623: ! 624: # ------------ spec512 --------------- ! 625: def get_spec512threshold(self): ! 626: return self.get("[Screen]", "nSpec512Threshold") ! 627: ! 628: def set_spec512threshold(self, value): ! 629: value = int(value) # guarantee correct type ! 630: self.set("[Screen]", "nSpec512Threshold", value) ! 631: self._change_option("--spec512 %d" % value) ! 632: ! 633: # ------------ show borders --------------- ! 634: def get_borders(self): ! 635: return self.get("[Screen]", "bAllowOverscan") ! 636: ! 637: def set_borders(self, value): ! 638: self.set("[Screen]", "bAllowOverscan", value) ! 639: self._change_option("--borders %s" % str(value)) ! 640: ! 641: # ------------ show statusbar --------------- ! 642: def get_statusbar(self): ! 643: return self.get("[Screen]", "bShowStatusbar") ! 644: ! 645: def set_statusbar(self, value): ! 646: self.set("[Screen]", "bShowStatusbar", value) ! 647: self._change_option("--statusbar %s" % str(value)) ! 648: ! 649: # ------------ show led --------------- ! 650: def get_led(self): ! 651: return self.get("[Screen]", "bShowDriveLed") ! 652: ! 653: def set_led(self, value): ! 654: self.set("[Screen]", "bShowDriveLed", value) ! 655: self._change_option("--drive-led %s" % str(value)) ! 656: ! 657: # ------------ monitor aspect ratio --------------- ! 658: def get_aspectcorrection(self): ! 659: return self.get("[Screen]", "bAspectCorrect") ! 660: ! 661: def set_aspectcorrection(self, value): ! 662: self.set("[Screen]", "bAspectCorrect", value) ! 663: self._change_option("--aspect %s" % str(value)) ! 664: ! 665: # ------------ max window size --------------- ! 666: def get_max_size(self): ! 667: w = self.get("[Screen]", "nMaxWidth") ! 668: h = self.get("[Screen]", "nMaxHeight") ! 669: return (w, h) ! 670: ! 671: def set_max_size(self, w, h): ! 672: # guarantee correct type (Gtk float -> config int) ! 673: w = int(w); h = int(h) ! 674: self.set("[Screen]", "nMaxWidth", w) ! 675: self.set("[Screen]", "nMaxHeight", h) ! 676: self._change_option("--max-width %d" % w) ! 677: self._change_option("--max-height %d" % h) ! 678: ! 679: # TODO: remove once UI doesn't need this anymore ! 680: def set_zoom(self, value): ! 681: print "Just setting Zoom, configuration doesn't anymore have setting for this." ! 682: if value: ! 683: zoom = 2 ! 684: else: ! 685: zoom = 1 ! 686: self._change_option("--zoom %d" % zoom) ! 687: ! 688: # ------------ configured Hatari window size --------------- ! 689: def get_window_size(self): ! 690: if self.get("[Screen]", "bFullScreen"): ! 691: print "WARNING: don't start Hatari UI with fullscreened Hatari!" ! 692: ! 693: # VDI resolution? ! 694: if self.get("[Screen]", "bUseExtVdiResolutions"): ! 695: width = self.get("[Screen]", "nVdiWidth") ! 696: height = self.get("[Screen]", "nVdiHeight") ! 697: return (width, height) ! 698: ! 699: # window sizes for other than ST & STE can differ ! 700: if self.get("[System]", "nMachineType") not in (0, 1): ! 701: print "WARNING: neither ST nor STE machine, window size inaccurate!" ! 702: videl = True ! 703: else: ! 704: videl = False ! 705: ! 706: # mono monitor? ! 707: if self.get_monitor() == 0: ! 708: return (640, 400) ! 709: ! 710: # no, color ! 711: width = 320 ! 712: height = 200 ! 713: # statusbar? ! 714: if self.get_statusbar(): ! 715: sbar = 12 ! 716: height += sbar ! 717: else: ! 718: sbar = 0 ! 719: # zoom? ! 720: maxw, maxh = self.get_max_size() ! 721: if 2*width <= maxw and 2*height <= maxh: ! 722: width *= 2 ! 723: height *= 2 ! 724: zoom = 2 ! 725: else: ! 726: zoom = 1 ! 727: # overscan borders? ! 728: if self.get_borders() and not videl: ! 729: # properly aligned borders on top of zooming ! 730: leftx = (maxw-width)/zoom ! 731: borderx = 2*(min(48,leftx/2)/16)*16 ! 732: lefty = (maxh-height)/zoom ! 733: bordery = min(29+47, lefty) ! 734: width += zoom*borderx ! 735: height += zoom*bordery ! 736: ! 737: return (width, height)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.