From df867af6e5fafa311bec6fef835e3862e44103b9 Mon Sep 17 00:00:00 2001 From: Rolando Islas Date: Sun, 7 May 2017 06:27:59 -0700 Subject: [PATCH 1/3] Fix black bar on video (+2 squashed commits) Fix image buffer compatibility with pypy Reduce HID update interval Python 3 sockets seem to be slower than 2. Pypy3 brings them close. For now the HID updates will send 10 per second instead of 180. Reducing by half (90/s) shows an improvement, but 10/s does not seem to add noticeable latency. Large video updates and probably audio will still cause stuttering. --- src/server/control/util/controller.py | 2 +- src/server/data/constants.py | 2 +- src/server/net/wii/video.py | 17 ++++++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/server/control/util/controller.py b/src/server/control/util/controller.py index 41d4a58..f11efa8 100644 --- a/src/server/control/util/controller.py +++ b/src/server/control/util/controller.py @@ -12,7 +12,7 @@ class Controller: hid_seq_id = 0 hid_update_timestamp = 0 - HID_UPDATE_INTERVAL = int((1. / 180.) * 1000.) # 5 - leaving it since it may make sense later + HID_UPDATE_INTERVAL = int((1 / 10) * 1000) # should be 180 per second FIXME python 3 sockets are slow # Button buffers button_buffer = (0, 0) extra_button_buffer = (0, 0) diff --git a/src/server/data/constants.py b/src/server/data/constants.py index b0ebd60..f1d5d34 100644 --- a/src/server/data/constants.py +++ b/src/server/data/constants.py @@ -14,7 +14,7 @@ PORT_SERVER_CMD = 50002 # Video -WII_VIDEO_WIDTH = 854 +WII_VIDEO_WIDTH = 848 WII_VIDEO_HEIGHT = 480 WII_CAMERA_WIDTH = 640 WII_CAMERA_HEIGHT = 480 diff --git a/src/server/net/wii/video.py b/src/server/net/wii/video.py index 58bb3d6..5316d2a 100644 --- a/src/server/net/wii/video.py +++ b/src/server/net/wii/video.py @@ -107,17 +107,16 @@ def update(self, packet, test=False): # Get image nals = self.h264_nal_encapsulate(is_idr, self.frame) image_buffer = self.decoder.get_image_buffer(nals.tostring()) + if not image_buffer: + return # Check fps limit - if ConfigServer.fps < 60 and time.time() - self.last_sent_time < 1. / ConfigServer.fps: + if time.time() - self.last_sent_time < 1. / ConfigServer.fps: return # Reduce quality at the expense of CPU - if ConfigServer.quality < 100: - image = Image.frombuffer("RGB", (constants.WII_VIDEO_WIDTH, constants.WII_CAMERA_HEIGHT), - image_buffer, "raw", "RGB", 0, 1) - ib = BytesIO() - image.save(ib, "JPEG", quality=ConfigServer.quality) - ServiceVID.broadcast(ib.getvalue()) - else: - ServiceVID.broadcast(image_buffer) + image = Image.frombuffer("RGB", (constants.WII_VIDEO_WIDTH, constants.WII_CAMERA_HEIGHT), + bytes(image_buffer), "raw", "RGB", 0, 1) + ib = BytesIO() + image.save(ib, "JPEG", quality=ConfigServer.quality) + ServiceVID.broadcast(ib.getvalue()) # Update time self.last_sent_time = time.time() From ee59b6097830096a12211732feb201d3df271207 Mon Sep 17 00:00:00 2001 From: Rolando Islas Date: Sun, 7 May 2017 21:41:19 -0700 Subject: [PATCH 2/3] Fix devices being unmanaged incorrectly in NetworkManger's configuration Fix #25 --- src/server/util/interface_util.py | 34 ++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/server/util/interface_util.py b/src/server/util/interface_util.py index 12c0a1b..5a02525 100644 --- a/src/server/util/interface_util.py +++ b/src/server/util/interface_util.py @@ -75,18 +75,38 @@ def is_managed_by_network_manager(cls, interface): conf = open(constants.PATH_CONF_NETWORK_MANAGER) conf_data = conf.readlines() conf.close() + managed = False for line in conf_data: - if line.startswith("unmanaged") and "mac:" + cls.get_mac(interface) in line: - Logger.debug("Interface \"%s\" is unmanaged by network manager.", interface) - return False - return True + if line.startswith("unmanaged-devices=") and "mac:" + cls.get_mac(interface) not in line: + managed = True # Ensure configs with duplicates raise an unmanaged prompt + Logger.debug("Interface \"%s\" managed by network manager: %s", interface, managed) + return managed @classmethod def set_unmanaged_by_network_manager(cls, interface): Logger.debug("Adding interface \"%s-%s\" as an unmanaged interface to network manager", interface, cls.get_mac(interface)) - conf = open(constants.PATH_CONF_NETWORK_MANAGER, "a") - conf.writelines(["[keyfile]\n", "unmanaged-devices=mac:" + cls.get_mac(interface) + "\n"]) - conf.close() + with open(constants.PATH_CONF_NETWORK_MANAGER, "r") as conf_read: + conf = conf_read.read().splitlines() + added = False + entry = "mac:" + cls.get_mac(interface) + # Add Entry + for line in range(0, len(conf)): + # Add keyfile plugin if it's not enabled + if conf[line].startswith("plugins=") and "keyfile" not in conf[line]: + conf[line] += ",keyfile" + # Add unmanaged device + if conf[line].startswith("unmanaged-devices=") and entry not in conf[line]: + conf[line] += ";" + entry + added = True + # Add the initial unmanaged entry if it was not present + if not added: + conf.append("[keyfile]") + conf.append("unmanaged-devices=" + entry) + # Write + with open(constants.PATH_CONF_NETWORK_MANAGER, "w") as conf_write: + for line in conf: + conf_write.write(line + "\n") + # Restart the service ProcessUtil.call(["service", "network-manager", "restart"]) ProcessUtil.call(["service", "networking", "restart"]) From ba4fe932d33069d880aafd65e4fe80629abeab1b Mon Sep 17 00:00:00 2001 From: Rolando Islas Date: Mon, 8 May 2017 00:34:41 -0700 Subject: [PATCH 3/3] Add log and about tab --- src/server/data/constants.py | 3 +- src/server/ui/gui/frame/frame_about.py | 24 ++++++++++++++ src/server/ui/gui/frame/frame_get_key.py | 7 +++- src/server/ui/gui/frame/frame_log.py | 36 +++++++++++++++++++++ src/server/ui/gui/frame/frame_run_server.py | 11 +++++-- src/server/ui/gui/frame/frame_tab.py | 3 ++ src/server/ui/gui/gui_main.py | 16 +++++++-- src/server/util/status_sending_thread.py | 7 ++++ 8 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 src/server/ui/gui/frame/frame_about.py create mode 100644 src/server/ui/gui/frame/frame_log.py diff --git a/src/server/data/constants.py b/src/server/data/constants.py index f1d5d34..538d534 100644 --- a/src/server/data/constants.py +++ b/src/server/data/constants.py @@ -1,7 +1,8 @@ import os # Info -VERSION = "1.5" +VERSION = "1.6" +NAME = "DRC SIM Server" # Port PORT_WII_MSG = 50010 diff --git a/src/server/ui/gui/frame/frame_about.py b/src/server/ui/gui/frame/frame_about.py new file mode 100644 index 0000000..3561cc3 --- /dev/null +++ b/src/server/ui/gui/frame/frame_about.py @@ -0,0 +1,24 @@ +from tkinter import Label + +from src.server.data import constants +from src.server.ui.gui.frame.frame_tab import FrameTab + + +class FrameAbout(FrameTab): + def __init__(self, master=None, **kw): + super().__init__(master, **kw) + self.text_name = Label(self, text=constants.NAME) + self.text_version = Label(self, text="v" + constants.VERSION) + + self.text_name.grid(row=0, column=0) + self.text_version.grid(row=1, column=0) + self.grid_columnconfigure(0, weight=1) + + def activate(self): + pass + + def deactivate(self): + pass + + def kill_other_tabs(self): + return False diff --git a/src/server/ui/gui/frame/frame_get_key.py b/src/server/ui/gui/frame/frame_get_key.py index 45ac7ec..537525e 100644 --- a/src/server/ui/gui/frame/frame_get_key.py +++ b/src/server/ui/gui/frame/frame_get_key.py @@ -133,7 +133,8 @@ def activate(self): self.getting_psk = False self.set_code_text("") self.progress_bar.start() - self.progress_bar.grid_remove() + if not self.wpa_supplicant or not self.wpa_supplicant.get_status(): + self.progress_bar.grid_remove() self.dropdown_wii_u["values"] = InterfaceUtil.get_wiiu_compatible_interfaces() def deactivate(self): @@ -142,6 +143,7 @@ def deactivate(self): self.getting_psk = False if self.wpa_supplicant: self.wpa_supplicant.stop() + self.wpa_supplicant = None @staticmethod def get_image(location, width, height): @@ -157,3 +159,6 @@ def set_code_text(self, text): self.entry_pair_code.delete(0, END) self.entry_pair_code.insert(0, text) self.entry_pair_code.config(state="readonly") + + def kill_other_tabs(self): + return True diff --git a/src/server/ui/gui/frame/frame_log.py b/src/server/ui/gui/frame/frame_log.py new file mode 100644 index 0000000..aa2bc29 --- /dev/null +++ b/src/server/ui/gui/frame/frame_log.py @@ -0,0 +1,36 @@ +import os +import subprocess +from tkinter import Button, CENTER, messagebox + +from src.server.data import constants +from src.server.ui.gui.frame.frame_tab import FrameTab + + +class FrameLog(FrameTab): + def __init__(self, master=None, **kw): + super().__init__(master, **kw) + self.button_log = Button(self, text="Open Log in Console") + self.button_log.bind("", self.button_clicked) + self.button_log.place(relx=0.5, rely=0.5, anchor=CENTER) + self.log = None + + # noinspection PyUnusedLocal + def button_clicked(self, event): + tail = ["x-terminal-emulator", "-e", "tail", "-f"] + for file in ("drcsim", "cli", "gui", "wpa", "backend"): + tail.append(os.path.join(constants.PATH_LOG_DIR, file + ".log")) + self.deactivate() + try: + self.log = subprocess.Popen(tail, stdout=open(os.devnull, "w"), stderr=subprocess.PIPE) + except FileNotFoundError: + messagebox.showerror("Log Error", "Could not open log window.") + + def activate(self): + pass + + def deactivate(self): + if self.log and self.log.poll() is None: + self.log.kill() + + def kill_other_tabs(self): + return False diff --git a/src/server/ui/gui/frame/frame_run_server.py b/src/server/ui/gui/frame/frame_run_server.py index ab0851a..a4c8cc1 100644 --- a/src/server/ui/gui/frame/frame_run_server.py +++ b/src/server/ui/gui/frame/frame_run_server.py @@ -168,8 +168,10 @@ def stop_server(self, event=None): return if self.gamepad: self.gamepad.close() + self.gamepad = None if self.wpa_supplicant: self.wpa_supplicant.stop() + self.wpa_supplicant = None self.activate() def activate(self): @@ -181,8 +183,10 @@ def activate(self): self.dropdown_wiiu_interface["values"] = InterfaceUtil.get_wiiu_compatible_interfaces() self.dropdown_normal_interface["values"] = InterfaceUtil.get_all_interfaces() self.dropdown_region["values"] = ["NONE", "NA"] - self.label_wpa_status["text"] = WpaSupplicant.DISCONNECTED - self.label_backend_status["text"] = Gamepad.STOPPED + self.label_wpa_status["text"] = self.wpa_supplicant.get_status() \ + if self.wpa_supplicant and self.wpa_supplicant.get_status() else WpaSupplicant.DISCONNECTED + self.label_backend_status["text"] = self.gamepad.get_status() \ + if self.gamepad and self.gamepad.get_status() else Gamepad.STOPPED self.button_start.config(state="normal") self.button_stop.config(state="normal") self.label_interface_info.config(text="") @@ -194,3 +198,6 @@ def deactivate(self): """ LoggerGui.debug("FrameRunServer deactivated") self.stop_server() + + def kill_other_tabs(self): + return True diff --git a/src/server/ui/gui/frame/frame_tab.py b/src/server/ui/gui/frame/frame_tab.py index 2e1df1e..43871db 100644 --- a/src/server/ui/gui/frame/frame_tab.py +++ b/src/server/ui/gui/frame/frame_tab.py @@ -10,3 +10,6 @@ def activate(self): def deactivate(self): raise NotImplementedError() + + def kill_other_tabs(self): + raise NotImplementedError() diff --git a/src/server/ui/gui/gui_main.py b/src/server/ui/gui/gui_main.py index 479170b..9acb870 100644 --- a/src/server/ui/gui/gui_main.py +++ b/src/server/ui/gui/gui_main.py @@ -2,7 +2,9 @@ from tkinter.ttk import Notebook from src.server.data.resource import Resource +from src.server.ui.gui.frame.frame_about import FrameAbout from src.server.ui.gui.frame.frame_get_key import FrameGetKey +from src.server.ui.gui.frame.frame_log import FrameLog from src.server.ui.gui.frame.frame_run_server import FrameRunServer from src.server.util.logging.logger_gui import LoggerGui @@ -33,6 +35,12 @@ def __init__(self): # Get Key Frame self.frame_get_key = FrameGetKey(self.notebook) self.notebook.add(self.frame_get_key, text="Get Key") + # Log Frame + self.frame_log = FrameLog(self.notebook) + self.notebook.add(self.frame_log, text="Log") + # About Frame + self.frame_about = FrameAbout(self.notebook) + self.notebook.add(self.frame_about, text="About") @staticmethod def throw(*args): @@ -96,7 +104,9 @@ def on_tab_changed(self, event): tab_index = self.notebook.index(tab_id) tab_name = self.notebook.tab(tab_index, "text") LoggerGui.debug("Notebook tab changed to \"%s\" with id %d", tab_name, tab_index) - if self.tab_id: - self.notebook.children[self.tab_id].deactivate() - self.tab_id = tab_id.split(".")[len(tab_id.split(".")) - 1] + self.tab_id = tab_id.split(".")[len(tab_id.split(".")) - 1] # Parse notebook/tab id to only tab id + if self.notebook.children[self.tab_id].kill_other_tabs(): + for tab in self.notebook.children: + if tab != self.tab_id: + self.notebook.children[tab].deactivate() self.notebook.children[self.tab_id].activate() diff --git a/src/server/util/status_sending_thread.py b/src/server/util/status_sending_thread.py index c885bf4..708deae 100644 --- a/src/server/util/status_sending_thread.py +++ b/src/server/util/status_sending_thread.py @@ -17,6 +17,13 @@ def set_status(self, status): for listener in self.status_change_listeners: listener(status) + def get_status(self): + """ + Status getter + :return: status string + """ + return self.status + def add_status_change_listener(self, callback): """ Add a callback that will be called on status change.