From d485a7324337e14d3856d1318548f62823a2cb47 Mon Sep 17 00:00:00 2001 From: Jonathan Harris <jonathan@marginal.org.uk> Date: Sat, 31 Oct 2015 01:25:18 +0000 Subject: [PATCH] Poll logs for current system. --- EDMarketConnector.py | 14 ++ config.py | 1 + monitor.py | 296 +++++++++++++++++++++++++++++++++++++++++++ prefs.py | 85 ++++++++----- 4 files changed, 367 insertions(+), 29 deletions(-) create mode 100644 monitor.py diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 4ba78592..83107598 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -30,6 +30,7 @@ import eddb import prefs from config import appname, applongname, config from hotkey import hotkeymgr +from monitor import monitor l10n.Translations().install() EDDB = eddb.EDDB() @@ -169,6 +170,12 @@ class AppWindow: self.w.bind_all('<<Invoke>>', self.getandsend) # user-generated hotkeymgr.register(self.w, config.getint('hotkey_code'), config.getint('hotkey_mods')) + # Install log monitoring + monitor.set_callback(self.system_change) + if (config.getint('output') & config.OUT_LOG_AUTO) and (config.getint('output') & (config.OUT_LOG_AUTO|config.OUT_LOG_EDSM)): + monitor.enable_logging() + monitor.start() + # call after credentials have changed def login(self): self.status['text'] = _('Logging in...') @@ -362,6 +369,13 @@ class AppWindow: except: pass + def system_change(self, system, timestamp): + if self.system['text'] != system: + # TODO: EDSM lookup and csv and/or EDSM log + self.system['text'] = system + self.station['text'] = EDDB.system(system) and self.STATION_UNDOCKED or '' + self.status['text'] = strftime(_('Last updated at {HH}:{MM}:{SS}').format(HH='%H', MM='%M', SS='%S').encode('utf-8'), localtime(timestamp)).decode('utf-8') + def edsmpoll(self): result = self.edsm.result if result['done']: diff --git a/config.py b/config.py index d2d5e058..530ab3d2 100644 --- a/config.py +++ b/config.py @@ -74,6 +74,7 @@ class Config: #OUT_STAT = 64 # No longer available OUT_SHIP_CORIOLIS = 128 OUT_LOG_EDSM = 256 + OUT_LOG_AUTO = 512 if platform=='darwin': diff --git a/monitor.py b/monitor.py new file mode 100644 index 00000000..14869100 --- /dev/null +++ b/monitor.py @@ -0,0 +1,296 @@ +import re +import threading +from os import listdir, pardir, rename, unlink +from os.path import exists, isdir, isfile, join +from platform import machine +from sys import platform +from time import strptime, localtime, mktime, sleep, time +from datetime import datetime + +if __debug__: + from traceback import print_exc + +if platform=='darwin': + from AppKit import NSWorkspace + from Foundation import NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask + +elif platform=='win32': + import ctypes + + CSIDL_LOCAL_APPDATA = 0x001C + CSIDL_PROGRAM_FILESX86 = 0x002A + + # _winreg that ships with Python 2 doesn't support unicode, so do this instead + from ctypes.wintypes import * + + HKEY_LOCAL_MACHINE = 0x80000002 + KEY_READ = 0x00020019 + REG_SZ = 1 + + RegOpenKeyEx = ctypes.windll.advapi32.RegOpenKeyExW + RegOpenKeyEx.restype = LONG + RegOpenKeyEx.argtypes = [HKEY, LPCWSTR, DWORD, DWORD, ctypes.POINTER(HKEY)] + + RegCloseKey = ctypes.windll.advapi32.RegCloseKey + RegCloseKey.restype = LONG + RegCloseKey.argtypes = [HKEY] + + RegQueryValueEx = ctypes.windll.advapi32.RegQueryValueExW + RegQueryValueEx.restype = LONG + RegQueryValueEx.argtypes = [HKEY, LPCWSTR, LPCVOID, ctypes.POINTER(DWORD), LPCVOID, ctypes.POINTER(DWORD)] + + RegEnumKeyEx = ctypes.windll.advapi32.RegEnumKeyExW + RegEnumKeyEx.restype = LONG + RegEnumKeyEx.argtypes = [HKEY, DWORD, LPWSTR, ctypes.POINTER(DWORD), ctypes.POINTER(DWORD), LPWSTR, ctypes.POINTER(DWORD), ctypes.POINTER(FILETIME)] + + WNDENUMPROC = ctypes.WINFUNCTYPE(BOOL, HWND, ctypes.POINTER(DWORD)) + EnumWindows = ctypes.windll.user32.EnumWindows + EnumWindows.argtypes = [WNDENUMPROC, LPARAM] + GetWindowText = ctypes.windll.user32.GetWindowTextW + GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int] + GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW + + @WNDENUMPROC + def EnumWindowsProc(hwnd, lParam): + l = GetWindowTextLength(hwnd) + 1 + buf = ctypes.create_unicode_buffer(l) + if GetWindowText(hwnd, buf, l) and buf.value.startswith('Elite - Dangerous'): + lParam[0] = 1 + return False # stop enumeration + return True + + +class EDLogs: + + def __init__(self): + self.logdir = self._logdir() + self.logging_enabled = self._logging_enabled + self._restart_required = False + self.observer = None + self.callback = None + + def set_callback(self, callback): + self.callback = callback + + def enable_logging(self): + if self.logging_enabled(): + return True + elif self._enable_logging(): + self._restart_required = self._ED_is_running() + return True + else: + return False + + def restart_required(self): + if not self._ED_is_running(): + self._restart_required = False + return self._restart_required + + def logging_enabled_in_file(self, appconf): + if not isfile(appconf): + return False + + with open(appconf, 'rU') as f: + content = f.read().lower() + start = content.find('<network') + end = content.find('</network>') + if start >= 0 and end >= 0: + return bool(re.search('verboselogging\s*=\s*\"1\"', content[start+8:end])) + else: + return False + + def enable_logging_in_file(self, appconf): + try: + if not exists(appconf): + with open(appconf, 'wt') as f: + f.write('<AppConfig>\n\t<Network\n\t\tVerboseLogging="1"\n\t>\n\t</Network>\n</AppConfig>\n') + return True + + with open(appconf, 'rU') as f: + content = f.read() + f.close() + backup = appconf[:-4] + '_backup.xml' + if exists(backup): + unlink(backup) + rename(appconf, backup) + + with open(appconf, 'wt') as f: + start = content.lower().find('<network') + if start >= 0: + f.write(content[:start+8] + '\n\t\tVerboseLogging="1"' + content[start+8:]) + else: + start = content.lower().find("</appconfig>") + if start >= 0: + f.write(content[:start] + '\t<Network\n\t\tVerboseLogging="1"\n\t>\n\t</Network>\n' + content[start:]) + else: + f.write(content) # eh ? + return False + + assert self._logging_enabled() + return self.logging_enabled_in_file(appconf) + except: + if __debug__: print_exc() + return False + + def start(self): + self.stop() + if not self.logdir or not self.callback: + return False + self.observer = threading.Thread(target = self.worker, name = 'netLog worker') + self.observer.daemon = True + self.observer.start() + + def stop(self): + if self.observer: + self.observer.stop() + self.observer = None + + def worker(self): + regexp = re.compile('{(.+)} System:[^\(]*\(([^\)]+)') + + # Seek to the end of the latest log file + logfiles = sorted([x for x in listdir(self.logdir) if x.startswith('netLog.')]) + logfile = logfiles and logfiles[-1] or None + if logfile: + loghandle = open(join(self.logdir, logfile), 'rt') + loghandle.seek(0, 2) # seek to EOF + else: + loghandle = None + + while True: + # Check whether new log file started, e.g. client restarted. Assumes logs sort alphabetically. + logfiles = sorted([x for x in listdir(self.logdir) if x.startswith('netLog.')]) + newlogfile = logfiles and logfiles[-1] or None + if logfile != newlogfile: + logfile = newlogfile + if loghandle: + loghandle.close() + loghandle = open(join(self.logdir, logfile), 'rt') + + if logfile: + system = visited = None + loghandle.seek(0, 1) # reset EOF flag + + for line in loghandle: + match = regexp.match(line) + if match: + system, visited = match.group(2), match.group(1) + + if system: + self._restart_required = False # clearly logging is working + # Convert local time string to UTC date and time + visited_struct = strptime(visited, '%H:%M:%S') + now = localtime() + if now.tm_hour == 0 and visited_struct.tm_hour == 23: + # Crossed midnight between timestamp and poll + now = localtime(time()-60) + datetime_struct = datetime(now.tm_year, now.tm_mon, now.tm_mday, visited_struct.tm_hour, visited_struct.tm_min, visited_struct.tm_sec).timetuple() # still local time + self.callback(system, mktime(datetime_struct)) + + sleep(10) # New system gets posted to log file before hyperspace ends, so don't need to poll too often + + + if platform=='darwin': + + def _logdir(self): + # https://support.frontier.co.uk/kb/faq.php?id=97 + suffix = join('Frontier Developments', 'Elite Dangerous') + paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True) + if len(paths) and isdir(paths[0]) and isfile(join(paths[0], suffix, 'AppNetCfg.xml')) and isdir(join(paths[0], suffix, 'Logs')): + return join(paths[0], suffix, 'Logs') + else: + return None + + def _logging_enabled(self): + return self.logdir and self.logging_enabled_in_file(join(self.logdir, pardir, 'AppConfigLocal.xml')) + + def _enable_logging(self): + return self.logdir and self.enable_logging_in_file(join(self.logdir, pardir, 'AppConfigLocal.xml')) + + def _ED_is_running(self): + for x in NSWorkspace.sharedWorkspace().runningApplications(): + if x.bundleIdentifier() == 'uk.co.frontier.EliteDangerous': + return True + else: + return False + + elif platform=='win32': + + def _logdir(self): + # First try under the Launcher + key = HKEY() + if not RegOpenKeyEx(HKEY_LOCAL_MACHINE, + machine().endswith('64') and + r'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall' or # Assumes that the launcher is a 32bit process + r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', + 0, KEY_READ, ctypes.byref(key)): + buf = ctypes.create_unicode_buffer(MAX_PATH) + i = 0 + while True: + size = DWORD(MAX_PATH) + if RegEnumKeyEx(key, i, buf, ctypes.byref(size), None, None, None, None): + break + + subkey = HKEY() + if not RegOpenKeyEx(key, buf, 0, KEY_READ, ctypes.byref(subkey)): + valtype = DWORD() + valsize = DWORD((len('Frontier Developments')+1)*2) + valbuf = ctypes.create_unicode_buffer(valsize.value / 2) + if not RegQueryValueEx(subkey, 'Publisher', 0, ctypes.byref(valtype), valbuf, ctypes.byref(valsize)) and valtype.value == REG_SZ and valbuf.value == 'Frontier Developments': + if not RegQueryValueEx(subkey, 'InstallLocation', 0, ctypes.byref(valtype), None, ctypes.byref(valsize)) and valtype.value == REG_SZ: + valbuf = ctypes.create_unicode_buffer(valsize.value / 2) + if not RegQueryValueEx(subkey, 'InstallLocation', 0, ctypes.byref(valtype), valbuf, ctypes.byref(valsize)): + custpath = join(valbuf.value, 'Products') + if isdir(custpath): + for d in listdir(custpath): + if d.startswith('FORC-FDEV-D-1') and isfile(join(custpath, d, 'AppConfig.xml')) and isdir(join(custpath, d, 'Logs')): + RegCloseKey(subkey) + RegCloseKey(key) + return join(custpath, d, 'Logs') + RegCloseKey(subkey) + i += 1 + RegCloseKey(key) + + # https://support.elitedangerous.com/kb/faq.php?id=108 + programs = ctypes.create_unicode_buffer(MAX_PATH) + ctypes.windll.shell32.SHGetSpecialFolderPathW(0, programs, CSIDL_PROGRAM_FILESX86, 0) + applocal = ctypes.create_unicode_buffer(MAX_PATH) + ctypes.windll.shell32.SHGetSpecialFolderPathW(0, applocal, CSIDL_LOCAL_APPDATA, 0) + for base in [join(programs.value, 'Steam', 'steamapps', 'common', 'Elite Dangerous', 'Products'), + join(programs.value, 'Frontier', 'Products'), + join(applocal.value, 'Frontier_Developments', 'Products')]: + if isdir(base): + for d in listdir(base): + if d.startswith('FORC-FDEV-D-1') and isfile(join(base, d, 'AppConfig.xml')) and isdir(join(base, d, 'Logs')): + return join(base, d, 'Logs') + + return None + + def _logging_enabled(self): + return self.logdir and (self.logging_enabled_in_file(join(self.logdir, pardir, 'AppConfigLocal.xml')) or + self.logging_enabled_in_file(join(self.logdir, pardir, 'AppConfig.xml'))) + + def _enable_logging(self): + return self.logdir and self.enable_logging_in_file(isfile(join(self.logdir, pardir, 'AppConfigLocal.xml')) and join(self.logdir, pardir, 'AppConfigLocal.xml') or join(self.logdir, pardir, 'AppConfig.xml')) + + def _ED_is_running(self): + retval = DWORD(0) + EnumWindows(EnumWindowsProc, ctypes.addressof(retval)) + return bool(retval) + + elif platform=='linux2': + + def _logdir(self): + return None + + def _logging_enabled(self): + return False + + def _enable_logging(self): + return False + + def _ED_is_running(self): + return False + +# singleton +monitor = EDLogs() diff --git a/prefs.py b/prefs.py index 23f24e37..7511b1fe 100644 --- a/prefs.py +++ b/prefs.py @@ -11,6 +11,7 @@ from ttkHyperlinkLabel import HyperlinkLabel from config import applongname, config from hotkey import hotkeymgr +from monitor import monitor if platform == 'darwin': @@ -71,7 +72,7 @@ class PreferencesDialog(tk.Toplevel): frame.grid(sticky=tk.NSEW) credframe = ttk.LabelFrame(frame, text=_('Credentials')) # Section heading in settings - credframe.grid(padx=10, pady=10, sticky=tk.NSEW) + credframe.grid(padx=10, pady=5, sticky=tk.NSEW) credframe.columnconfigure(1, weight=1) ttk.Label(credframe, text=_('Please log in with your Elite: Dangerous account details')).grid(row=0, columnspan=2, sticky=tk.W) # Use same text as E:D Launcher's login dialog @@ -90,30 +91,35 @@ class PreferencesDialog(tk.Toplevel): child.grid_configure(padx=5, pady=3) outframe = ttk.LabelFrame(frame, text=_('Output')) # Section heading in settings - outframe.grid(padx=10, pady=10, sticky=tk.NSEW) + outframe.grid(padx=10, pady=5, sticky=tk.NSEW) output = config.getint('output') or (config.OUT_EDDN | config.OUT_SHIP_EDS) - ttk.Label(outframe, text=_('Please choose what data to save')).grid(row=0, columnspan=2, padx=5, pady=3, sticky=tk.W) - self.out_eddn= tk.IntVar(value = (output & config.OUT_EDDN) and 1 or 0) - ttk.Checkbutton(outframe, text=_('Send station data to the Elite Dangerous Data Network'), variable=self.out_eddn, command=self.outvarchanged).grid(row=1, columnspan=2, padx=5, sticky=tk.W) - self.out_csv = tk.IntVar(value = (output & config.OUT_CSV ) and 1 or 0) - ttk.Checkbutton(outframe, text=_('Market data in CSV format file'), variable=self.out_csv, command=self.outvarchanged).grid(row=2, columnspan=2, padx=5, sticky=tk.W) - self.out_bpc = tk.IntVar(value = (output & config.OUT_BPC ) and 1 or 0) - ttk.Checkbutton(outframe, text=_("Market data in Slopey's BPC format file"), variable=self.out_bpc, command=self.outvarchanged).grid(row=3, columnspan=2, padx=5, sticky=tk.W) - self.out_td = tk.IntVar(value = (output & config.OUT_TD ) and 1 or 0) - ttk.Checkbutton(outframe, text=_('Market data in Trade Dangerous format file'), variable=self.out_td, command=self.outvarchanged).grid(row=4, columnspan=2, padx=5, sticky=tk.W) - self.out_ship_eds= tk.IntVar(value = (output & config.OUT_SHIP_EDS) and 1 or 0) - ttk.Checkbutton(outframe, text=_('Ship loadout in E:D Shipyard format file'), variable=self.out_ship_eds, command=self.outvarchanged).grid(row=5, columnspan=2, padx=5, pady=(5,0), sticky=tk.W) - self.out_ship_coriolis= tk.IntVar(value = (output & config.OUT_SHIP_CORIOLIS) and 1 or 0) - ttk.Checkbutton(outframe, text=_('Ship loadout in Coriolis format file'), variable=self.out_ship_coriolis, command=self.outvarchanged).grid(row=6, columnspan=2, padx=5, sticky=tk.W) - self.out_log_edsm = tk.IntVar(value = (output & config.OUT_LOG_EDSM ) and 1 or 0) - ttk.Checkbutton(outframe, text=_('Send flight log to Elite Dangerous Star Map'), variable=self.out_log_edsm, command=self.outvarchanged).grid(row=7, columnspan=2, padx=5, pady=(5,0), sticky=tk.W) - self.out_log_file = tk.IntVar(value = (output & config.OUT_LOG_FILE ) and 1 or 0) - ttk.Checkbutton(outframe, text=_('Flight log in CSV format file'), variable=self.out_log_file, command=self.outvarchanged).grid(row=8, columnspan=2, padx=5, sticky=tk.W) + ttk.Label(outframe, text=_('Please choose what data to save')).grid(row=0, padx=5, pady=3, sticky=tk.W) + self.out_eddn= tk.IntVar(value = (output & config.OUT_EDDN) and 1) + ttk.Checkbutton(outframe, text=_('Send station data to the Elite Dangerous Data Network'), variable=self.out_eddn, command=self.outvarchanged).grid(row=1, padx=5, sticky=tk.W) + self.out_csv = tk.IntVar(value = (output & config.OUT_CSV ) and 1) + ttk.Checkbutton(outframe, text=_('Market data in CSV format file'), variable=self.out_csv, command=self.outvarchanged).grid(row=2, padx=5, sticky=tk.W) + self.out_bpc = tk.IntVar(value = (output & config.OUT_BPC ) and 1) + ttk.Checkbutton(outframe, text=_("Market data in Slopey's BPC format file"), variable=self.out_bpc, command=self.outvarchanged).grid(row=3, padx=5, sticky=tk.W) + self.out_td = tk.IntVar(value = (output & config.OUT_TD ) and 1) + ttk.Checkbutton(outframe, text=_('Market data in Trade Dangerous format file'), variable=self.out_td, command=self.outvarchanged).grid(row=4, padx=5, sticky=tk.W) + self.out_ship_eds= tk.IntVar(value = (output & config.OUT_SHIP_EDS) and 1) + ttk.Checkbutton(outframe, text=_('Ship loadout in E:D Shipyard format file'), variable=self.out_ship_eds, command=self.outvarchanged).grid(row=5, padx=5, pady=(5,0), sticky=tk.W) + self.out_ship_coriolis= tk.IntVar(value = (output & config.OUT_SHIP_CORIOLIS) and 1) + ttk.Checkbutton(outframe, text=_('Ship loadout in Coriolis format file'), variable=self.out_ship_coriolis, command=self.outvarchanged).grid(row=6, padx=5, sticky=tk.W) + self.out_log_edsm = tk.IntVar(value = (output & config.OUT_LOG_EDSM) and 1) + ttk.Checkbutton(outframe, text=_('Send flight log to Elite Dangerous Star Map'), variable=self.out_log_edsm, command=self.outvarchanged).grid(row=7, padx=5, pady=(5,0), sticky=tk.W) + self.out_log_file = tk.IntVar(value = (output & config.OUT_LOG_FILE) and 1) + ttk.Checkbutton(outframe, text=_('Flight log in CSV format file'), variable=self.out_log_file, command=self.outvarchanged).grid(row=8, padx=5, sticky=tk.W) + self.out_log_auto = tk.IntVar(value = monitor.logdir and (output & config.OUT_LOG_AUTO) and 1 or 0) + if monitor.logdir: + self.out_log_auto_button = ttk.Checkbutton(outframe, text=_('Automatically make a log entry on entering a system'), variable=self.out_log_auto, command=self.outvarchanged) # Output setting + self.out_log_auto_button.grid(row=9, padx=5, sticky=tk.W) + self.out_log_auto_text = ttk.Label(outframe) self.dir_label = ttk.Label(frame, text=_('File location'), foreground=style.lookup('TLabelframe.Label', 'foreground')) # Section heading in settings dirframe = ttk.LabelFrame(frame, labelwidget = self.dir_label) - dirframe.grid(padx=10, pady=10, sticky=tk.NSEW) + dirframe.grid(padx=10, pady=5, sticky=tk.NSEW) dirframe.columnconfigure(0, weight=1) self.outdir = ttk.Entry(dirframe, takefocus=False) @@ -128,7 +134,7 @@ class PreferencesDialog(tk.Toplevel): self.edsm_label = HyperlinkLabel(frame, text=_('Elite Dangerous Star Map credentials'), disabledforeground=style.lookup('TLabelframe.Label', 'foreground'), url='http://www.edsm.net/settings/api', underline=True) # Section heading in settings edsmframe = ttk.LabelFrame(frame, labelwidget = self.edsm_label) - edsmframe.grid(padx=10, pady=10, sticky=tk.NSEW) + edsmframe.grid(padx=10, pady=5, sticky=tk.NSEW) edsmframe.columnconfigure(1, weight=1) ttk.Label(edsmframe, text=_('Cmdr name')).grid(row=0, sticky=tk.W) # EDSM & privacy setting @@ -151,7 +157,7 @@ class PreferencesDialog(tk.Toplevel): self.hotkey_play = tk.IntVar(value = not config.getint('hotkey_mute')) hotkeyframe = ttk.LabelFrame(frame, text=platform == 'darwin' and _('Keyboard shortcut') or # Section heading in settings on OSX _('Hotkey')) # Section heading in settings on Windows - hotkeyframe.grid(padx=10, pady=10, sticky=tk.NSEW) + hotkeyframe.grid(padx=10, pady=5, sticky=tk.NSEW) hotkeyframe.columnconfigure(1, weight=1) if platform == 'darwin' and not was_accessible_at_launch: if AXIsProcessTrusted(): @@ -171,10 +177,10 @@ class PreferencesDialog(tk.Toplevel): self.hotkey_play_btn.grid(row=2, columnspan=2, padx=5, sticky=tk.W) privacyframe = ttk.LabelFrame(frame, text=_('Privacy')) # Section heading in settings - privacyframe.grid(padx=10, pady=10, sticky=tk.NSEW) + privacyframe.grid(padx=10, pady=5, sticky=tk.NSEW) self.out_anon= tk.IntVar(value = config.getint('anonymous') and 1) - ttk.Label(privacyframe, text=_('How do you want to be identified in the saved data')).grid(row=0, columnspan=2, padx=5, sticky=tk.W) + ttk.Label(privacyframe, text=_('How do you want to be identified in the saved data')).grid(row=0, padx=5, sticky=tk.W) ttk.Radiobutton(privacyframe, text=_('Cmdr name'), variable=self.out_anon, value=0).grid(padx=5, sticky=tk.W) # Privacy setting ttk.Radiobutton(privacyframe, text=_('Pseudo-anonymized ID'), variable=self.out_anon, value=1).grid(padx=5, sticky=tk.W) # Privacy setting @@ -182,17 +188,20 @@ class PreferencesDialog(tk.Toplevel): self.protocol("WM_DELETE_WINDOW", self.apply) # close button applies changes else: buttonframe = ttk.Frame(frame) - buttonframe.grid(padx=10, pady=10, sticky=tk.NSEW) + buttonframe.grid(padx=10, pady=5, sticky=tk.NSEW) buttonframe.columnconfigure(0, weight=1) ttk.Label(buttonframe).grid(row=0, column=0) # spacer - ttk.Button(buttonframe, text=_('OK'), command=self.apply).grid(row=0, column=1, sticky=tk.E) + button = ttk.Button(buttonframe, text=_('OK'), command=self.apply) + button.grid(row=0, column=1, sticky=tk.E) + button.bind("<Return>", lambda event:self.apply()) self.protocol("WM_DELETE_WINDOW", self._destroy) # Selectively disable buttons depending on output settings self.outvarchanged() - # disable hotkey for the duration + # disable hotkey and log monitoring for the duration hotkeymgr.unregister() + monitor.stop() # wait for window to appear on screen before calling grab_set self.wait_visibility() @@ -205,6 +214,20 @@ class PreferencesDialog(tk.Toplevel): self.outbutton['state'] = local and tk.NORMAL or tk.DISABLED self.outdir['state'] = local and 'readonly' or tk.DISABLED + if monitor.logdir: + log = self.out_log_edsm.get() or self.out_log_file.get() + self.out_log_auto_button['state'] = log and tk.NORMAL or tk.DISABLED + + self.out_log_auto_text['text'] = '' + if log and self.out_log_auto.get(): + if not monitor.enable_logging(): + self.out_log_auto_text['text'] = "Can't enable automatic logging!" # Shouldn't happen - don't translate + self.out_log_auto_text.grid(row=10, padx=(25,5), sticky=tk.EW) + elif monitor.restart_required(): + self.out_log_auto_text['text'] = _('Re-start Elite: Dangerous for automatic logging') # Output settings prompt + self.out_log_auto_text.grid(row=10, padx=(25,5), sticky=tk.EW) + + edsm = self.out_log_edsm.get() self.edsm_label['state'] = edsm and tk.NORMAL or tk.DISABLED self.edsm_cmdr['state'] = edsm and tk.NORMAL or tk.DISABLED @@ -290,7 +313,7 @@ class PreferencesDialog(tk.Toplevel): config.set('username', self.username.get().strip()) config.set('password', self.password.get().strip()) - config.set('output', (self.out_eddn.get() and config.OUT_EDDN or 0) + (self.out_bpc.get() and config.OUT_BPC or 0) + (self.out_td.get() and config.OUT_TD or 0) + (self.out_csv.get() and config.OUT_CSV or 0) + (self.out_ship_eds.get() and config.OUT_SHIP_EDS or 0) + (self.out_log_file.get() and config.OUT_LOG_FILE or 0) + (self.out_ship_coriolis.get() and config.OUT_SHIP_CORIOLIS or 0) + (self.out_log_edsm.get() and config.OUT_LOG_EDSM or 0)) + config.set('output', (self.out_eddn.get() and config.OUT_EDDN) + (self.out_bpc.get() and config.OUT_BPC) + (self.out_td.get() and config.OUT_TD) + (self.out_csv.get() and config.OUT_CSV) + (self.out_ship_eds.get() and config.OUT_SHIP_EDS) + (self.out_log_file.get() and config.OUT_LOG_FILE) + (self.out_ship_coriolis.get() and config.OUT_SHIP_CORIOLIS) + (self.out_log_edsm.get() and config.OUT_LOG_EDSM) + (self.out_log_auto.get() and config.OUT_LOG_AUTO)) config.set('outdir', expanduser(self.outdir.get())) config.set('edsm_cmdrname', self.edsm_cmdr.get().strip()) @@ -309,8 +332,11 @@ class PreferencesDialog(tk.Toplevel): self.callback() def _destroy(self): - # Re-enable hotkey monitoring before exit + # Re-enable hotkey and log monitoring before exit hotkeymgr.register(self.parent, config.getint('hotkey_code'), config.getint('hotkey_mods')) + if (config.getint('output') & config.OUT_LOG_AUTO) and (config.getint('output') & (config.OUT_LOG_AUTO|config.OUT_LOG_EDSM)): + monitor.enable_logging() + monitor.start() self.destroy() if platform == 'darwin': @@ -371,6 +397,7 @@ class AuthenticationDialog(tk.Toplevel): self.code.focus_set() ttk.Label(frame).grid(row=1, column=2) # spacer self.button = ttk.Button(frame, text=_('OK'), command=self.apply, state=tk.DISABLED) + self.button.bind("<Return>", lambda event:self.apply()) self.button.grid(row=1, column=3, sticky=tk.E) for child in frame.winfo_children():