--- hatari/tools/hconsole/hconsole.py 2019/04/09 08:49:54 1.1 +++ hatari/tools/hconsole/hconsole.py 2019/04/09 08:54:53 1.1.1.6 @@ -5,7 +5,7 @@ # devices and changing Hatari command line options (even for things you # cannot change from the UI) from the console while Hatari is running. # -# Copyright (C) 2008-2011 by Eero Tamminen +# Copyright (C) 2008-2014 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 @@ -33,10 +33,46 @@ if str is bytes: def input(prompt): return raw_input(prompt) -# Atari scancodes for special keys, needed when wanting to -# output something else than alphanumberic keys to Hatari -# (when using keypress/down/up commands) class Scancode: + "Atari scancodes for keys without alphanumeric characters" + # US keyboard scancode mapping for characters which need shift + Shifted = { + '!': "0x2", + '@': "0x3", + '#': "0x4", + '$': "0x5", + '%': "0x6", + '^': "0x7", + '&': "0x8", + '*': "0x9", + '(': "10", + ')': "11", + '_': "12", + '+': "13", + '~': "41", + '{': "26", + '}': "27", + ':': "39", + '"': "40", + '|': "43", + '<': "51", + '>': "52", + '?': "53" + } + # US keyboard scancode mapping for characters which don't need shift + UnShifted = { + '-': "12", + '=': "13", + '[': "26", + ']': "27", + ';': "39", + "'": "40", + '\\': "43", + '",': "51", + '.': "52", + '/': "53" + } + # special keys without corresponding character Tab = "15" Return = "28" Enter = "114" @@ -64,7 +100,7 @@ class Hatari: controlpath = "/tmp/hatari-console-" + str(os.getpid()) + ".socket" hataribin = "hatari" - def __init__(self, args = None): + def __init__(self, args): # collect hatari process zombies without waitpid() signal.signal(signal.SIGCHLD, signal.SIG_IGN) self._assert_hatari_compatibility() @@ -80,14 +116,24 @@ class Hatari: if not self.run_hatari(args): print("ERROR: failed to run Hatari") sys.exit(1) + self.shiftdown = False self.verbose = False def _assert_hatari_compatibility(self): - for line in os.popen(self.hataribin + " -h").readlines(): + "check Hatari compatibility and return error string if it's not" + error = True + pipe = os.popen(self.hataribin + " -h") + for line in pipe.readlines(): if line.find("--control-socket") >= 0: - return - print("ERROR: Hatari not found or it doesn't support the required --control-socket option!") - sys.exit(-1) + error = False + break + try: + pipe.close() + except IOError: + pass + if error: + print("ERROR: %s" % error) + sys.exit(-1) def is_running(self): if not self.pid: @@ -97,6 +143,9 @@ class Hatari: except OSError as value: print("Hatari PID %d had exited in the meanwhile:\n\t%s" % (self.pid, value)) self.pid = 0 + if self.control: + self.control.close() + self.control = None return False return True @@ -128,7 +177,7 @@ class Hatari: self.control.sendall(bytes(msg + "\n", "ASCII")) # KLUDGE: wait so that Hatari output comes before next prompt if fast: - interval = self.interval/2 + interval = self.interval/4 else: interval = self.interval time.sleep(interval) @@ -142,16 +191,38 @@ class Hatari: def trigger_shortcut(self, shortcut): return self.send_message("hatari-shortcut %s" % shortcut) + + def _shift_up(self): + if self.shiftdown: + self.shiftdown = False + return self.send_message("hatari-event keyup %s" % Scancode.LeftShift, True) + return True + + def _unshifted_keypress(self, key): + self._shift_up() + if key == ' ': + # white space gets stripped, use scancode instead + key = Scancode.Space + return self.send_message("hatari-event keypress %s" % key, True) + + def _shifted_keypress(self, key): + if not self.shiftdown: + self.shiftdown = True + self.send_message("hatari-event keydown %s" % Scancode.LeftShift, True) + return self.send_message("hatari-event keypress %s" % key, True) def send_string(self, text): print("string:", text) - for item in text: - if item == ' ': - # white space gets stripped, use scancode instead - item = "57" - if not self.send_message("hatari-event keypress %s" % item, True): + for key in text: + if key in Scancode.Shifted: + ok = self._shifted_keypress(Scancode.Shifted[key]) + elif key in Scancode.UnShifted: + ok = self._unshifted_keypress(Scancode.UnShifted[key]) + else: + ok = self._unshifted_keypress(key) + if not ok: return False - return True + return self._shift_up() def insert_event(self, event): if event.startswith("text "): @@ -181,11 +252,13 @@ class Hatari: print("debug output", self.verbose) def kill_hatari(self): - if self.pid: + if self.is_running(): os.kill(self.pid, signal.SIGKILL) print("killed hatari with PID %d" % self.pid) - self.control = None self.pid = 0 + if self.control: + self.control.close() + self.control = None # command line parsing with readline @@ -218,36 +291,40 @@ class CommandInput: class Tokens: - # update with: hatari -h|grep -- --|sed 's/^ *\(--[^ ]*\).*$/ "\1",/' + # update with: hatari -h|grep -- --|sed 's/^ *\(--[^ ]*\).*$/ "\1",/'|grep -v -e control-socket -e 'joy<' option_tokens = [ "--help", "--version", "--confirm-quit", "--configfile", + "--keymap", "--fast-forward", "--mono", "--monitor", "--fullscreen", "--window", "--grab", - "--zoom", - "--max-width", - "--max-height", - "--aspect", - "--borders", "--frameskips", "--statusbar", "--drive-led", - "--spec512", "--bpp", + "--borders", + "--desktop-st", + "--spec512", + "--zoom", + "--desktop", + "--max-width", + "--max-height", + "--force-max", + "--aspect", "--vdi", "--vdi-planes", "--vdi-width", "--vdi-height", + "--crop", "--avirecord", "--avi-vcodec", "--avi-fps", - "--avi-crop", "--avi-file", "--joy0", "--joy1", @@ -263,7 +340,7 @@ class Tokens: "--rs232-out", "--disk-a", "--disk-b", - "--slowfdc", + "--fastfdc", "--protect-floppy", "--protect-hd", "--harddrive", @@ -271,24 +348,31 @@ class Tokens: "--ide-master", "--ide-slave", "--memsize", + "--memstate", "--tos", + "--patch-tos", "--cartridge", - "--memstate", "--cpulevel", "--cpuclock", "--compatible", "--machine", "--blitter", - "--timer-d", "--dsp", + "--timer-d", + "--fast-boot", + "--rtc", + "--mic", "--sound", - "--keymap", + "--sound-buffer-size", + "--ym-mixing", "--debug", "--bios-intercept", + "--conout", "--trace", "--trace-file", "--parse", "--saveconfig", + "--no-parachute", "--log-file", "--log-level", "--alert-level", @@ -343,8 +427,8 @@ class Tokens: "dspreg", "dspsymbols", "evaluate", - "exec", "help", + "history", "info", "loadbin", "lock", @@ -352,6 +436,8 @@ class Tokens: "memdump", "memwrite", "parse", + "profile", + "quit", "savebin", "setopt", "stateload", @@ -360,7 +446,7 @@ class Tokens: "trace" ] - def __init__(self, hatari): + def __init__(self, hatari, do_exit = True): self.process_tokens = { "kill": hatari.kill_hatari, "pause": hatari.toggle_pause @@ -374,6 +460,8 @@ class Tokens: "verbose": hatari.toggle_verbose } self.hatari = hatari + # whether to exit when Hatari disappears + self.do_exit = do_exit def get_tokens(self): tokens = [] @@ -451,10 +539,13 @@ give help when you give them invalid inp def process_command(self, line): if not self.hatari.is_running(): - print("Exiting as there's no Hatari (anymore)...") + print("There's no Hatari (anymore)!") + if not self.do_exit: + return False + print("Exiting...") sys.exit(0) if not line: - return + return False first = line.split()[0] # multiple items @@ -479,13 +570,14 @@ give help when you give them invalid inp self.help_tokens[line]() else: print("ERROR: unknown hatari-console command:", line) - + return False + return True class Main: - def __init__(self): - args, self.file, self.exit = self.parse_args(sys.argv) + def __init__(self, options, do_exit=True): + args, self.file, self.exit = self.parse_args(options) hatari = Hatari(args) - self.tokens = Tokens(hatari) + self.tokens = Tokens(hatari, do_exit) self.command = CommandInput(self.tokens.get_tokens()) def parse_args(self, args): @@ -552,9 +644,9 @@ For example: self.tokens.do_script("script " + filename) def run(self, line): - "helper method for scripts using hatari-console" - self.tokens.process_command(line) + "helper method for running Hatari commands with hatari-console, returns False on error" + return self.tokens.process_command(line) if __name__ == "__main__": - Main().loop() + Main(sys.argv).loop()