def __init__(self, args):
        global emacs_width, emacs_height, eaf_config_dir, proxy_string

        (emacs_width, emacs_height, proxy_host, proxy_port, proxy_type,
         config_dir, emacs_server_port, var_dict_string) = args
        emacs_width = int(emacs_width)
        emacs_height = int(emacs_height)
        eaf_config_dir = os.path.join(os.path.expanduser(config_dir), '')

        self.buffer_dict = {}
        self.view_dict = {}
        self.emacs_var_dict = {}

        self.update_emacs_var_dict(var_dict_string)

        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)

        self.server.logger.setLevel(logging.DEBUG)
        ch = logging.FileHandler(
            filename=os.path.expanduser('~/.emacs.d/eaf/epc_log.txt'),
            mode='w')
        ch.setLevel(logging.DEBUG)
        self.server.logger.addHandler(ch)

        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.allow_reuse_address = True

        self.server.register_instance(self)

        self.server_thread.start()

        # NOTE:
        # Emacs epc client need fetch port from Python process print.
        # And this print must be first print code, otherwise Emacs client will failed to start.
        self.server.print_port()

        self.emacs_server_connect = build_emacs_server_connect(
            int(emacs_server_port))

        self.first_start(self.webengine_include_private_codec())

        self.session_file = os.path.join(eaf_config_dir, "session.json")

        # Set Network proxy.
        if proxy_host != "" and proxy_port != "":
            proxy_string = "{0}://{1}:{2}".format(proxy_type, proxy_host,
                                                  proxy_port)

            proxy = QNetworkProxy()
            if proxy_type == "socks5":
                proxy.setType(QNetworkProxy.Socks5Proxy)
            elif proxy_type == "http":
                proxy.setType(QNetworkProxy.HttpProxy)

            proxy.setHostName(proxy_host)
            proxy.setPort(int(proxy_port))
            QNetworkProxy.setApplicationProxy(proxy)
    def __init__(self, args):
        global emacs_width, emacs_height, eaf_config_dir, proxy_string

        # Parse init arguments.
        (emacs_width, emacs_height, proxy_host, proxy_port, proxy_type,
         config_dir, emacs_server_port, var_dict_string) = args
        emacs_width = int(emacs_width)
        emacs_height = int(emacs_height)
        eaf_config_dir = os.path.join(os.path.expanduser(config_dir), '')

        # Init variables.
        self.buffer_dict = {}
        self.view_dict = {}
        self.emacs_var_dict = {}
        self.session_file = os.path.join(eaf_config_dir, "session.json")

        # Update Emacs var dictionary.
        self.update_emacs_var_dict(var_dict_string)

        # Init EPC client port.
        init_epc_client(int(emacs_server_port))

        # Build EPC server.
        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)
        self.server.logger.setLevel(logging.DEBUG)
        self.server.allow_reuse_address = True

        if not os.path.exists(eaf_config_dir):
            os.makedirs(eaf_config_dir)

        ch = logging.FileHandler(filename=os.path.join(eaf_config_dir,
                                                       'epc_log.txt'),
                                 mode='w')
        ch.setLevel(logging.DEBUG)
        self.server.logger.addHandler(ch)

        self.server.register_instance(
            self)  # register instance functions let elisp side call

        # Start EPC server with sub-thread, avoid block Qt main loop.
        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.start()

        # Pass epc port and webengine codec information to Emacs when first start EAF.
        eval_in_emacs('eaf--first-start', [
            self.server.server_address[1],
            self.webengine_include_private_codec()
        ])

        # Set Network proxy.
        self.proxy = (proxy_type, proxy_host, proxy_port)
        self.is_proxy = False

        if proxy_type != "" and proxy_host != "" and proxy_port != "":
            self.enable_proxy()
示例#3
0
class SampleGTKServer(object):

    def __init__(self):
        self.setup_gtk()
        self.setup_epc()

    def setup_gtk(self):
        gtk.threads_init()

        # Create a new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

        # Quit when window is closed
        self.window.connect("destroy", self.destroy)

        # Creates a new button
        self.button = gtk.Button("Push me!")
        self.button.connect("clicked", self.clicked, None)

        # Show widgets and window
        self.window.add(self.button)
        self.button.show()
        self.window.show()

    def setup_epc(self):
        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)

        # Setup logger
        self.server.logger.setLevel(logging.DEBUG)
        ch = logging.FileHandler(filename='python-epc.log', mode='w')
        ch.setLevel(logging.DEBUG)
        self.server.logger.addHandler(ch)

        # Setup server thread
        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.allow_reuse_address = True

        # Define and register RPC functions
        def destroy():
            gobject.idle_add(gtk.Widget.destroy, self.window)

        def set_button_label(label):
            gobject.idle_add(self.button.set_label, label)

        self.server.register_function(destroy)
        self.server.register_function(set_button_label)

    def clicked(self, widget, data=None):
        handler = self.server.clients[0]
        message = "Button '{0}' is clicked!".format(self.button.get_label())
        handler.call('message', [message])

    def destroy(self, widget, data=None):
        self.server.shutdown()
        gtk.main_quit()

    def main(self):
        self.server_thread.start()
        self.server.print_port()
        gtk.main()
示例#4
0
    def setup_epc(self):
        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)

        # Setup logger
        self.server.logger.setLevel(logging.DEBUG)
        ch = logging.FileHandler(filename='python-epc.log', mode='w')
        ch.setLevel(logging.DEBUG)
        self.server.logger.addHandler(ch)

        # Setup server thread
        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.allow_reuse_address = True

        # Define and register RPC functions
        def destroy():
            gobject.idle_add(gtk.Widget.destroy, self.window)

        def set_button_label(label):
            gobject.idle_add(self.button.set_label, label)

        self.server.register_function(destroy)
        self.server.register_function(set_button_label)
示例#5
0
    def restore_cookies(self):
        if os.path.exists(self.cookie_path):
            with open(self.cookie_path, 'r') as f:
                lines = ''
                for line in f:
                    lines = lines + line
                    allCookies = QNetworkCookie.parseCookies(lines)
                    QNetworkCookieJar.setAllCookies(self, allCookies)
                    
if __name__ == '__main__':
    import sys
    import signal

    app = QApplication(sys.argv)

    server = ThreadingEPCServer(('localhost', 0), log_traceback=True)

    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.allow_reuse_address = True

    emacs_xwindow_id = 0

    buffer_dict = {}
    
    cookie_jar = CookieJar()
    
    print_console_info = False
    
    if len(sys.argv) >= 2 and sys.argv[1] == "--enable-proxy":
        QNetworkProxy.setApplicationProxy(QNetworkProxy(QNetworkProxy.Socks5Proxy, "127.0.0.1", 7070))    
        
示例#6
0
        
        browser_xid = self.winId().__int__()
        browser_xwindow = xlib_display.create_resource_object("window", browser_xid)
        emacs_xwindow = xlib_display.create_resource_object("window", self.emacs_xid)
        
        browser_xwindow.reparent(emacs_xwindow, x, y)
        
        xlib_display.sync()
            
if __name__ == '__main__':
    import sys
    import signal
    
    app = QApplication(sys.argv)
    
    server = ThreadingEPCServer(('localhost', 0), log_traceback=True)
    
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.allow_reuse_address = True
    
    tray_view = None
    
    def call_method(method_name, args):
        handler = server.clients[0]
        handler.call(method_name, args)
        
    @postGui(False)    
    def init(emacs_xid, minibuffer_height):
        global tray_view
        
        tray_view = TrayView(emacs_xid, minibuffer_height)
class EAF(object):
    def __init__(self, args):
        global emacs_width, emacs_height, eaf_config_dir, proxy_string

        # Parse init arguments.
        (emacs_width, emacs_height, proxy_host, proxy_port, proxy_type,
         config_dir, emacs_server_port, var_dict_string) = args
        emacs_width = int(emacs_width)
        emacs_height = int(emacs_height)
        eaf_config_dir = os.path.join(os.path.expanduser(config_dir), '')

        # Init variables.
        self.buffer_dict = {}
        self.view_dict = {}
        self.emacs_var_dict = {}
        self.session_file = os.path.join(eaf_config_dir, "session.json")

        # Update Emacs var dictionary.
        self.update_emacs_var_dict(var_dict_string)

        # Init EPC client port.
        init_epc_client(int(emacs_server_port))

        # Build EPC server.
        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)
        self.server.logger.setLevel(logging.DEBUG)
        self.server.allow_reuse_address = True

        if not os.path.exists(eaf_config_dir):
            os.makedirs(eaf_config_dir)

        ch = logging.FileHandler(filename=os.path.join(eaf_config_dir,
                                                       'epc_log.txt'),
                                 mode='w')
        ch.setLevel(logging.DEBUG)
        self.server.logger.addHandler(ch)

        self.server.register_instance(
            self)  # register instance functions let elisp side call

        # Start EPC server with sub-thread, avoid block Qt main loop.
        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.start()

        # Pass epc port and webengine codec information to Emacs when first start EAF.
        eval_in_emacs('eaf--first-start', [
            self.server.server_address[1],
            self.webengine_include_private_codec()
        ])

        # Set Network proxy.
        self.proxy = (proxy_type, proxy_host, proxy_port)
        self.is_proxy = False

        if proxy_type != "" and proxy_host != "" and proxy_port != "":
            self.enable_proxy()

    def enable_proxy(self):
        global proxy_string

        proxy_string = "{0}://{1}:{2}".format(self.proxy[0], self.proxy[1],
                                              self.proxy[2])

        proxy = QNetworkProxy()
        if self.proxy[0] == "socks5":
            proxy.setType(QNetworkProxy.Socks5Proxy)
        elif self.proxy[0] == "http":
            proxy.setType(QNetworkProxy.HttpProxy)
        proxy.setHostName(self.proxy[1])
        proxy.setPort(int(self.proxy[2]))

        self.is_proxy = True
        QNetworkProxy.setApplicationProxy(proxy)

    def disable_proxy(self):
        global proxy_string

        proxy_string = ""

        proxy = QNetworkProxy()
        proxy.setType(QNetworkProxy.NoProxy)

        self.is_proxy = False
        QNetworkProxy.setApplicationProxy(proxy)

    def toggle_proxy(self):
        if self.is_proxy:
            self.disable_proxy()
        else:
            self.enable_proxy()

    def build_emacs_server_connect(self, port):
        conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        conn.connect(('127.0.0.1', port))
        return conn

    def get_command_result(self, command):
        ''' Execute the command and return the result. '''
        if version_info >= (3, 7):
            return subprocess.run(command,
                                  check=False,
                                  shell=True,
                                  stdout=subprocess.PIPE,
                                  text=True).stdout
        else:
            return subprocess.run(command,
                                  check=False,
                                  shell=True,
                                  stdout=subprocess.PIPE).stdout

    def webengine_include_private_codec(self):
        ''' Return bool of whether the QtWebEngineProcess include private codec. '''
        if platform.system() in ["Windows", "Darwin"]:
            return "False"
        path = os.path.join(
            QLibraryInfo.location(QLibraryInfo.LibraryExecutablesPath),
            "QtWebEngineProcess")
        return self.get_command_result(
            "ldd {} | grep libavformat".format(path)) != ""

    @PostGui()
    def update_emacs_var_dict(self, var_dict_string):
        ''' Update Python side emacs_var_dict.(Fix issue #206) '''
        self.emacs_var_dict = json.loads(var_dict_string)

        for buffer in list(self.buffer_dict.values()):
            buffer.emacs_var_dict = self.emacs_var_dict

    @PostGui()
    def new_buffer(self, buffer_id, url, app_name, arguments):
        ''' Create new buffer. '''
        self.create_app(buffer_id, str(url),
                        "app.{0}.buffer".format(str(app_name)), str(arguments))

    @PostGui()
    def update_buffer_with_url(self, module_path, buffer_url, update_data):
        ''' Update buffer with url '''
        for buffer in list(self.buffer_dict.values()):
            if buffer.module_path == module_path and buffer.url == buffer_url:
                buffer.update_with_data(update_data)
                break

    @PostGui()
    def scroll_other_buffer(self, view_info, scroll_direction, scroll_type):
        ''' Scroll to other buffer '''
        (buffer_id, _, _, _, _) = view_info.split(":")
        if buffer_id in self.buffer_dict:
            self.buffer_dict[buffer_id].scroll_other_buffer(
                scroll_direction, scroll_type)

    def get_new_browser_window_buffer_id(self):
        ''' Return new browser window's buffer ID. '''
        import secrets

        return "{0}-{1}-{2}-{3}-{4}-{5}-{6}".format(secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2))

    def create_new_browser_window(self):
        ''' Create new browser window.'''
        # Generate buffer id same as eaf.el does.
        buffer_id = self.get_new_browser_window_buffer_id()

        # Create buffer for create new browser window.
        app_buffer = self.create_buffer(buffer_id, "http://0.0.0.0",
                                        "app.browser.buffer", "")

        # Create emacs buffer with buffer id.
        eval_in_emacs('eaf--create-new-browser-buffer', [buffer_id])

        # Return new QWebEngineView for create new browser window.
        return app_buffer.buffer_widget

    def create_app(self, buffer_id, url, module_path, arguments):
        ''' Create app using create_buffer.'''
        try:
            self.create_buffer(buffer_id, url, module_path, arguments)

            return ""
        except ImportError:
            import traceback
            traceback.print_exc()
            return "EAF: Something went wrong when trying to import {0}".format(
                module_path)

    def create_buffer(self, buffer_id, url, module_path, arguments):
        ''' Create buffer.'''
        global emacs_width, emacs_height, eaf_config_dir, proxy_string

        # Create application buffer.
        module = importlib.import_module(module_path)
        app_buffer = module.AppBuffer(buffer_id, url, eaf_config_dir,
                                      arguments, self.emacs_var_dict,
                                      module_path)

        # Add buffer to buffer dict.
        self.buffer_dict[buffer_id] = app_buffer

        # Resize buffer with emacs max window size,
        # view (QGraphicsView) will adjust visual area along with emacs window changed.
        app_buffer.buffer_widget.resize(emacs_width, emacs_height)

        # Handle dev tools signal.
        if getattr(app_buffer, "open_devtools_tab", False) and getattr(
                app_buffer.open_devtools_tab, "connect", False):
            app_buffer.open_devtools_tab.connect(self.open_devtools_tab)

        # Add create new window when create_new_browser_window_callback is call.
        if (app_buffer.__class__.__bases__[0].__name__ == "BrowserBuffer"):
            app_buffer.buffer_widget.create_new_browser_window_callback = self.create_new_browser_window

        # Set proxy for browser.
        if module_path == "app.browser.buffer":
            app_buffer.proxy_string = proxy_string

        # If arguments is devtools, create devtools page.
        if module_path == "app.browser.buffer" and arguments == "devtools" and self.devtools_page:
            self.devtools_page.setDevToolsPage(
                app_buffer.buffer_widget.web_page)
            self.devtools_page = None

        # Restore buffer session.
        self.restore_buffer_session(app_buffer)

        return app_buffer

    @PostGui()
    def update_views(self, args):
        ''' Update views.'''
        view_infos = args.split(",")

        # Show cursor anyway.
        QtWidgets.qApp.restoreOverrideCursor()

        # Do something if buffer's all view hide after update_views operation.
        old_view_buffer_ids = list(
            set(map(lambda v: v.buffer_id, self.view_dict.values())))
        new_view_buffer_ids = list(
            set(map(lambda v: v.split(":")[0], view_infos)))

        # Call all_views_hide interface when buffer's all views will hide.
        # We do something in app's buffer interface, such as videoplayer will pause video when all views hide.
        # Note, we must call this function before last view destroy,
        # such as QGraphicsVideoItem will report "Internal data stream error" error.
        for old_view_buffer_id in old_view_buffer_ids:
            if old_view_buffer_id not in new_view_buffer_ids:
                self.buffer_dict[old_view_buffer_id].all_views_hide()

        # Remove old key from view dict and destroy old view.
        for key in list(self.view_dict):
            if key not in view_infos:
                self.view_dict[key].destroy_view()
                self.view_dict.pop(key, None)

        # Create new view and update in view dict.
        if view_infos != ['']:
            for view_info in view_infos:
                if view_info not in self.view_dict:
                    (buffer_id, _, _, _, _, _) = view_info.split(":")
                    view = View(self.buffer_dict[buffer_id], view_info)
                    self.view_dict[view_info] = view

        # Call some_view_show interface when buffer's view switch back.
        # Note, this must call after new view create, otherwise some buffer,
        # such as QGraphicsVideoItem will report "Internal data stream error" error.
        if view_infos != ['']:
            for new_view_buffer_id in new_view_buffer_ids:
                if new_view_buffer_id not in old_view_buffer_ids:
                    self.buffer_dict[new_view_buffer_id].some_view_show()

        # Adjust buffer size along with views change.
        # Note: just buffer that option `fit_to_view' is False need to adjust,
        # if buffer option fit_to_view is True, buffer render adjust by view.resizeEvent()
        for buffer in list(self.buffer_dict.values()):
            if not buffer.fit_to_view:
                buffer_views = list(
                    filter(lambda v: v.buffer_id == buffer.buffer_id,
                           list(self.view_dict.values())))

                # Adjust buffer size to max view's size.
                if len(buffer_views) > 0:
                    max_view = max(buffer_views,
                                   key=lambda v: v.width * v.height)

                    buffer.buffer_widget.resize(max_view.width,
                                                max_view.height)
                # Adjust buffer size to emacs window size if not match view found.
                else:
                    buffer.buffer_widget.resize(emacs_width, emacs_height)

                # Send resize signal to buffer.
                buffer.resize_view()

    @PostGui()
    def kill_emacs(self):
        ''' Kill all buffurs from buffer dict.'''
        tmp_buffer_dict = {}
        for buffer_id in self.buffer_dict:
            tmp_buffer_dict[buffer_id] = self.buffer_dict[buffer_id]

        for buffer_id in tmp_buffer_dict:
            self.kill_buffer(buffer_id)

    @PostGui()
    def kill_buffer(self, buffer_id):
        ''' Kill all view based on buffer_id and clean buffer from buffer dict.'''
        # Kill all view base on buffer_id.
        for key in list(self.view_dict):
            if buffer_id == self.view_dict[key].buffer_id:
                self.view_dict[key].destroy_view()
                self.view_dict.pop(key, None)

        # Clean buffer from buffer dict.
        if buffer_id in self.buffer_dict:
            # Save buffer session.
            self.save_buffer_session(self.buffer_dict[buffer_id])

            self.buffer_dict[buffer_id].destroy_buffer()
            self.buffer_dict.pop(buffer_id, None)

    @PostGui()
    def execute_function(self, buffer_id, function_name, event_string):
        ''' Execute function and do not return anything. '''
        if buffer_id in self.buffer_dict:
            try:
                buffer = self.buffer_dict[buffer_id]
                buffer.current_event_string = event_string
                buffer.execute_function(function_name)
            except AttributeError:
                import traceback
                traceback.print_exc()
                message_to_emacs("Cannot execute function: " + function_name +
                                 " (" + buffer_id + ")")

    def call_function(self, buffer_id, function_name):
        ''' Call function and return the result. '''
        if buffer_id in self.buffer_dict:
            try:
                return str(
                    self.buffer_dict[buffer_id].call_function(function_name))
            except AttributeError:
                import traceback
                traceback.print_exc()
                message_to_emacs("Cannot call function: " + function_name)
                return ""

    def call_function_with_args(self, buffer_id, function_name, *args,
                                **kwargs):
        ''' Call function with arguments and return the result. '''
        if buffer_id in self.buffer_dict:
            try:
                return str(self.buffer_dict[buffer_id].call_function_with_args(
                    function_name, *args, **kwargs))
            except AttributeError:
                import traceback
                traceback.print_exc()
                message_to_emacs("Cannot call function: " + function_name)
                return ""

    def get_emacs_wsl_window_id(self):
        if platform.system() == "Windows":
            return gw.getActiveWindow()._hWnd

    def activate_emacs_wsl_window(self, frame_title):
        if platform.system() == "Windows":
            w = gw.getWindowsWithTitle(frame_title)
            w[0].activate()

    @PostGui()
    def action_quit(self, buffer_id):
        ''' Execute action_quit() for specified buffer.'''
        if buffer_id in self.buffer_dict:
            self.buffer_dict[buffer_id].action_quit()

    @PostGui()
    def send_key(self, buffer_id, event_string):
        ''' Send event to buffer when found match buffer.'''
        if buffer_id in self.buffer_dict:
            self.buffer_dict[buffer_id].fake_key_event(event_string)

    @PostGui()
    def send_key_sequence(self, buffer_id, event_string):
        ''' Send event to buffer when found match buffer.'''
        if buffer_id in self.buffer_dict:
            self.buffer_dict[buffer_id].fake_key_sequence(event_string)

    @PostGui()
    def handle_input_response(self, buffer_id, callback_tag, callback_result):
        ''' Handle input message for specified buffer.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                buffer.handle_input_response(callback_tag, callback_result)

    @PostGui()
    def cancel_input_response(self, buffer_id, callback_tag):
        ''' Cancel input message for specified buffer.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                buffer.cancel_input_response(callback_tag)

    @PostGui()
    def update_focus_text(self, buffer_id, new_text):
        ''' Update focus text for specified buffer.'''
        import base64

        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                buffer.set_focus_text(
                    base64.b64decode(new_text).decode("utf-8"))

    @PostGui()
    def update_multiple_sub_nodes(self, buffer_id, new_text):
        ''' Update multiplt sub nodes.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                for line in str(new_text).split("\n"):
                    buffer.add_texted_sub_node(line)

    @PostGui()
    def update_multiple_brother_nodes(self, buffer_id, new_text):
        ''' Update multiplt brother nodes.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                for line in str(new_text).split("\n"):
                    buffer.add_texted_brother_node(line)

    @PostGui()
    def update_multiple_middle_nodes(self, buffer_id, new_text):
        ''' Update multiplt middle nodes.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                for line in str(new_text).split("\n"):
                    buffer.add_texted_middle_node(line)

    def open_devtools_tab(self, web_page):
        ''' Open devtools tab'''
        self.devtools_page = web_page
        eval_in_emacs('eaf-open-devtool-page', [])

    def save_buffer_session(self, buf):
        ''' Save buffer session to file.'''
        # Create config file it not exist.
        if not os.path.exists(self.session_file):
            basedir = os.path.dirname(self.session_file)
            if not os.path.exists(basedir):
                os.makedirs(basedir)

            with open(self.session_file, 'a'):
                os.utime(self.session_file, None)

            print("Create session file %s" % (self.session_file))

        # Save buffer session to file.
        buf_session_data = buf.save_session_data()
        if buf_session_data != "":
            with open(self.session_file, "r+") as session_file:
                # Init session dict.
                session_dict = {}
                try:
                    session_dict = json.load(session_file)
                except ValueError:
                    pass

                # Init module path dict.
                if buf.module_path not in session_dict:
                    session_dict[buf.module_path] = {}

                # Update session data.
                session_dict[buf.module_path].update(
                    {buf.url: buf_session_data})

                # Clean session file and update new content.
                session_file.seek(0)
                session_file.truncate(0)
                json.dump(session_dict, session_file)

                print("Saved session: ", buf.module_path, buf.url,
                      buf_session_data)

    def restore_buffer_session(self, buf):
        ''' Restore buffer session from file.'''
        if os.path.exists(self.session_file):
            with open(self.session_file, "r+") as session_file:
                session_dict = {}
                try:
                    session_dict = json.load(session_file)
                except ValueError:
                    pass

                if buf.module_path in session_dict:
                    if buf.url in session_dict[buf.module_path]:
                        buf.restore_session_data(
                            session_dict[buf.module_path][buf.url])

                        print("Session restored: ", buf.buffer_id,
                              buf.module_path, self.session_file)
                    else:
                        print("Session is not restored, as no data about %s." %
                              (buf.url))
                else:
                    print(
                        "Session is not restored, as no data present in session file."
                    )
        else:
            print("Session is not restored, as %s cannot be found." %
                  (self.session_file))

    def cleanup(self):
        '''Do some cleanup before exit python process.'''
        close_epc_client()
    def __init__(self, args):
        global emacs_width, emacs_height, proxy_string

        # Parse init arguments.
        (emacs_width, emacs_height, emacs_server_port) = args
        emacs_width = int(emacs_width)
        emacs_height = int(emacs_height)

        # Init variables.
        self.module_dict = {}
        self.buffer_dict = {}
        self.view_dict = {}

        for name in [
                "scroll_other_buffer", "eval_js_function", "eval_js_code",
                "action_quit", "send_key", "send_key_sequence",
                "handle_search_forward", "handle_search_backward",
                "set_focus_text"
        ]:
            self.build_buffer_function(name)

        for name in [
                "execute_js_function", "execute_js_code", "execute_function",
                "execute_function_with_args"
        ]:
            self.build_buffer_return_function(name)

        # Init EPC client port.
        init_epc_client(int(emacs_server_port))

        # Build EPC server.
        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)
        self.server.allow_reuse_address = True

        # import logging
        # self.server = ThreadingEPCServer(('localhost', 0)
        # self.server.logger.setLevel(logging.DEBUG)

        eaf_config_dir = get_emacs_config_dir()
        self.session_file = os.path.join(eaf_config_dir, "session.json")

        if not os.path.exists(eaf_config_dir):
            os.makedirs(eaf_config_dir)

        # ch = logging.FileHandler(filename=os.path.join(eaf_config_dir, 'epc_log.txt'), mode='w')
        # formatter = logging.Formatter('%(asctime)s | %(levelname)-8s | %(lineno)04d | %(message)s')
        # ch.setFormatter(formatter)
        # ch.setLevel(logging.DEBUG)
        # self.server.logger.addHandler(ch)

        self.server.register_instance(
            self)  # register instance functions let elisp side call

        # Start EPC server with sub-thread, avoid block Qt main loop.
        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.start()

        # Pass epc port and webengine codec information to Emacs when first start EAF.
        eval_in_emacs('eaf--first-start', [self.server.server_address[1]])

        # Disable use system proxy, avoid page slow when no network connected.
        QNetworkProxyFactory.setUseSystemConfiguration(False)

        # Set Network proxy.
        (proxy_host, proxy_port, proxy_type) = get_emacs_vars(
            ["eaf-proxy-host", "eaf-proxy-port", "eaf-proxy-type"])

        self.proxy = (proxy_type, proxy_host, proxy_port)
        self.is_proxy = False

        if proxy_type != "" and proxy_host != "" and proxy_port != "":
            self.enable_proxy()
class EAF(object):
    def __init__(self, args):
        global emacs_width, emacs_height, proxy_string

        # Parse init arguments.
        (emacs_width, emacs_height, emacs_server_port) = args
        emacs_width = int(emacs_width)
        emacs_height = int(emacs_height)

        # Init variables.
        self.module_dict = {}
        self.buffer_dict = {}
        self.view_dict = {}

        for name in [
                "scroll_other_buffer", "eval_js_function", "eval_js_code",
                "action_quit", "send_key", "send_key_sequence",
                "handle_search_forward", "handle_search_backward",
                "set_focus_text"
        ]:
            self.build_buffer_function(name)

        for name in [
                "execute_js_function", "execute_js_code", "execute_function",
                "execute_function_with_args"
        ]:
            self.build_buffer_return_function(name)

        # Init EPC client port.
        init_epc_client(int(emacs_server_port))

        # Build EPC server.
        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)
        self.server.allow_reuse_address = True

        # import logging
        # self.server = ThreadingEPCServer(('localhost', 0)
        # self.server.logger.setLevel(logging.DEBUG)

        eaf_config_dir = get_emacs_config_dir()
        self.session_file = os.path.join(eaf_config_dir, "session.json")

        if not os.path.exists(eaf_config_dir):
            os.makedirs(eaf_config_dir)

        # ch = logging.FileHandler(filename=os.path.join(eaf_config_dir, 'epc_log.txt'), mode='w')
        # formatter = logging.Formatter('%(asctime)s | %(levelname)-8s | %(lineno)04d | %(message)s')
        # ch.setFormatter(formatter)
        # ch.setLevel(logging.DEBUG)
        # self.server.logger.addHandler(ch)

        self.server.register_instance(
            self)  # register instance functions let elisp side call

        # Start EPC server with sub-thread, avoid block Qt main loop.
        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.start()

        # Pass epc port and webengine codec information to Emacs when first start EAF.
        eval_in_emacs('eaf--first-start', [self.server.server_address[1]])

        # Disable use system proxy, avoid page slow when no network connected.
        QNetworkProxyFactory.setUseSystemConfiguration(False)

        # Set Network proxy.
        (proxy_host, proxy_port, proxy_type) = get_emacs_vars(
            ["eaf-proxy-host", "eaf-proxy-port", "eaf-proxy-type"])

        self.proxy = (proxy_type, proxy_host, proxy_port)
        self.is_proxy = False

        if proxy_type != "" and proxy_host != "" and proxy_port != "":
            self.enable_proxy()

    def enable_proxy(self):
        global proxy_string

        proxy_string = "{0}://{1}:{2}".format(self.proxy[0], self.proxy[1],
                                              self.proxy[2])

        proxy = QNetworkProxy()
        if self.proxy[0] == "socks5":
            proxy.setType(QNetworkProxy.Socks5Proxy)
        elif self.proxy[0] == "http":
            proxy.setType(QNetworkProxy.HttpProxy)
        proxy.setHostName(self.proxy[1])
        proxy.setPort(int(self.proxy[2]))

        self.is_proxy = True
        QNetworkProxy.setApplicationProxy(proxy)

    def disable_proxy(self):
        global proxy_string

        proxy_string = ""

        proxy = QNetworkProxy()
        proxy.setType(QNetworkProxy.NoProxy)

        self.is_proxy = False
        QNetworkProxy.setApplicationProxy(proxy)

    def toggle_proxy(self):
        if self.is_proxy:
            self.disable_proxy()
        else:
            self.enable_proxy()

    @PostGui()
    def update_buffer_with_url(self, module_path, buffer_url, update_data):
        ''' Update buffer with url '''
        if type(buffer_id) == str and buffer_id in self.buffer_dict:
            buffer = self.buffer_dict[buffer_id]
            if buffer.module_path == module_path and buffer.url == buffer_url:
                buffer.update_with_data(update_data)

    @PostGui()
    def new_buffer(self, buffer_id, url, module_path, arguments):
        ''' New buffer.
        new_buffer just clone of create_buffer with @PostGui elisp call asynchronously.
        '''
        self.create_buffer(buffer_id, url, module_path, arguments)

    def create_buffer(self, buffer_id, url, module_path, arguments):
        ''' Create buffer.
        create_buffer can't wrap with @PostGui, because need call by createNewWindow signal of browser.'''
        import importlib

        global emacs_width, emacs_height, proxy_string

        # Load module with app absolute path.
        if module_path in self.module_dict:
            module = self.module_dict[module_path]
        else:
            spec = importlib.util.spec_from_file_location(
                "AppBuffer", module_path)
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)
            self.module_dict[module_path] = module

        # Create application buffer.
        app_buffer = module.AppBuffer(buffer_id, url, arguments)

        # Add module_path.
        app_buffer.module_path = module_path

        # Add buffer to buffer dict.
        self.buffer_dict[buffer_id] = app_buffer

        # Resize buffer with emacs max window size,
        # view (QGraphicsView) will adjust visual area along with emacs window changed.
        app_buffer.buffer_widget.resize(emacs_width, emacs_height)

        # Handle dev tools signal.
        if getattr(app_buffer, "open_devtools_tab", False) and getattr(
                app_buffer.open_devtools_tab, "connect", False):
            app_buffer.open_devtools_tab.connect(self.open_devtools_tab)

        # Add create buffer interface for createWindow signal.
        if app_buffer.base_class_name() == "BrowserBuffer":
            app_buffer.create_buffer = self.create_buffer

        # Set proxy for browser.
        if app_buffer.base_class_name() == "BrowserBuffer":
            app_buffer.proxy_string = proxy_string

        # If arguments is devtools, create devtools page.
        if app_buffer.base_class_name(
        ) == "BrowserBuffer" and arguments == "devtools" and self.devtools_page:
            self.devtools_page.setDevToolsPage(
                app_buffer.buffer_widget.web_page)
            self.devtools_page = None

        # Restore buffer session.
        self.restore_buffer_session(app_buffer)

        return app_buffer

    @PostGui()
    def update_views(self, args):
        ''' Update views.'''
        from core.view import View

        view_infos = args.split(",")

        # Do something if buffer's all view hide after update_views operation.
        old_view_buffer_ids = list(
            set(map(lambda v: v.buffer_id, self.view_dict.values())))
        new_view_buffer_ids = list(
            set(map(lambda v: v.split(":")[0], view_infos)))

        # Call all_views_hide interface when buffer's all views will hide.
        # We do something in app's buffer interface, such as videoplayer will pause video when all views hide.
        # Note, we must call this function before last view destroy,
        # such as QGraphicsVideoItem will report "Internal data stream error" error.
        for old_view_buffer_id in old_view_buffer_ids:
            if old_view_buffer_id not in new_view_buffer_ids:
                if old_view_buffer_id in self.buffer_dict:
                    self.buffer_dict[old_view_buffer_id].all_views_hide()

        # Remove old key from view dict and destroy old view.
        for key in list(self.view_dict):
            if key not in view_infos:
                self.destroy_view_later(key)

        # NOTE:
        # Create new view and REPARENT view to Emacs window.
        if view_infos != ['']:
            for view_info in view_infos:
                if view_info not in self.view_dict:
                    (buffer_id, _, _, _, _, _) = view_info.split(":")
                    view = View(self.buffer_dict[buffer_id], view_info)
                    self.view_dict[view_info] = view

        # Call some_view_show interface when buffer's view switch back.
        # Note, this must call after new view create, otherwise some buffer,
        # such as QGraphicsVideoItem will report "Internal data stream error" error.
        if view_infos != ['']:
            for new_view_buffer_id in new_view_buffer_ids:
                if new_view_buffer_id not in old_view_buffer_ids:
                    if new_view_buffer_id in self.buffer_dict:
                        self.buffer_dict[new_view_buffer_id].some_view_show()

        # Adjust buffer size along with views change.
        # Note: just buffer that option `fit_to_view' is False need to adjust,
        # if buffer option fit_to_view is True, buffer render adjust by view.resizeEvent()
        for buffer in list(self.buffer_dict.values()):
            if not buffer.fit_to_view:
                buffer_views = list(
                    filter(lambda v: v.buffer_id == buffer.buffer_id,
                           list(self.view_dict.values())))

                # Adjust buffer size to max view's size.
                if len(buffer_views) > 0:
                    max_view = max(buffer_views,
                                   key=lambda v: v.width * v.height)

                    buffer.buffer_widget.resize(max_view.width,
                                                max_view.height)
                # Adjust buffer size to emacs window size if not match view found.
                else:
                    buffer.buffer_widget.resize(emacs_width, emacs_height)

                # Send resize signal to buffer.
                buffer.resize_view()

        # NOTE:
        # When you do switch buffer or kill buffer in Emacs, will call Python function 'update_views.
        # Screen will flick if destroy old view BEFORE reparent new view.
        #
        # So we call function 'destroy_view_now' at last to make sure destroy old view AFTER reparent new view.
        # Then screen won't flick.
        self.destroy_view_now()

    def destroy_view_later(self, key):
        '''Just record view id in global list 'destroy_view_list', and not destroy old view immediately.'''
        global destroy_view_list

        destroy_view_list.append(key)

    def destroy_view_now(self):
        '''Destroy all old view immediately.'''
        global destroy_view_list

        for key in destroy_view_list:
            if key in self.view_dict:
                self.view_dict[key].destroy_view()
            self.view_dict.pop(key, None)

        destroy_view_list = []

    @PostGui()
    def kill_buffer(self, buffer_id):
        ''' Kill all view based on buffer_id and clean buffer from buffer dict.'''
        # Kill all view base on buffer_id.
        for key in list(self.view_dict):
            if buffer_id == self.view_dict[key].buffer_id:
                self.destroy_view_later(key)

        # Clean buffer from buffer dict.
        if buffer_id in self.buffer_dict:
            # Save buffer session.
            self.save_buffer_session(self.buffer_dict[buffer_id])

            self.buffer_dict[buffer_id].destroy_buffer()
            self.buffer_dict.pop(buffer_id, None)

    @PostGui()
    def kill_emacs(self):
        ''' Kill all buffurs from buffer dict.'''
        tmp_buffer_dict = {}
        for buffer_id in self.buffer_dict:
            tmp_buffer_dict[buffer_id] = self.buffer_dict[buffer_id]

        for buffer_id in tmp_buffer_dict:
            self.kill_buffer(buffer_id)

    def build_buffer_function(self, name):
        @PostGui()
        def _do(*args):
            buffer_id = args[0]

            if type(buffer_id) == str and buffer_id in self.buffer_dict:
                try:
                    getattr(self.buffer_dict[buffer_id], name)(*args[1:])
                except AttributeError:
                    import traceback
                    traceback.print_exc()
                    message_to_emacs("Got error with : " + name + " (" +
                                     buffer_id + ")")

        setattr(self, name, _do)

    def build_buffer_return_function(self, name):
        def _do(*args):
            buffer_id = args[0]

            if type(buffer_id) == str and buffer_id in self.buffer_dict:
                try:
                    return getattr(self.buffer_dict[buffer_id],
                                   name)(*args[1:])
                except AttributeError:
                    import traceback
                    traceback.print_exc()
                    message_to_emacs("Got error with : " + name + " (" +
                                     buffer_id + ")")
                    return None

        setattr(self, name, _do)

    @PostGui()
    def eval_function(self, buffer_id, function_name, event_string):
        ''' Execute function and do not return anything. '''
        if type(buffer_id) == str and buffer_id in self.buffer_dict:
            try:
                buffer = self.buffer_dict[buffer_id]
                buffer.current_event_string = event_string
                getattr(buffer, function_name)()
            except AttributeError:
                import traceback
                traceback.print_exc()
                message_to_emacs("Cannot execute function: " + function_name +
                                 " (" + buffer_id + ")")

    def get_emacs_wsl_window_id(self):
        if platform.system() == "Windows":
            return gw.getActiveWindow()._hWnd

    def activate_emacs_win32_window(self, frame_title):
        if platform.system() == "Windows":
            w = gw.getWindowsWithTitle(frame_title)
            w[0].activate()

    @PostGui()
    def handle_input_response(self, buffer_id, callback_tag, callback_result):
        ''' Handle input message for specified buffer.'''
        if type(buffer_id) == str and buffer_id in self.buffer_dict:
            buffer = self.buffer_dict[buffer_id]

            buffer.handle_input_response(callback_tag, callback_result)
            buffer.stop_search_input_monitor_thread()

    @PostGui()
    def cancel_input_response(self, buffer_id, callback_tag):
        ''' Cancel input message for specified buffer.'''
        if type(buffer_id) == str and buffer_id in self.buffer_dict:
            buffer = self.buffer_dict[buffer_id]

            buffer.cancel_input_response(callback_tag)
            buffer.stop_marker_input_monitor_thread()
            buffer.stop_search_input_monitor_thread()

    @PostGui()
    def show_top_views(self):
        for key in list(self.view_dict):
            self.view_dict[key].try_show_top_view()

    @PostGui()
    def hide_top_views(self):
        for key in list(self.view_dict):
            self.view_dict[key].try_hide_top_view()

    def open_devtools_tab(self, web_page):
        ''' Open devtools tab'''
        self.devtools_page = web_page
        eval_in_emacs('eaf-open-devtool-page', [])

    def save_buffer_session(self, buf):
        ''' Save buffer session to file.'''
        # Create config file it not exist.
        if not os.path.exists(self.session_file):
            basedir = os.path.dirname(self.session_file)
            if not os.path.exists(basedir):
                os.makedirs(basedir)

            with open(self.session_file, 'a'):
                os.utime(self.session_file, None)

            print("Create session file %s" % (self.session_file))

        # Save buffer session to file.
        buf_session_data = buf.save_session_data()
        if buf_session_data != "":
            with open(self.session_file, "r+") as session_file:
                # Init session dict.
                session_dict = {}
                try:
                    session_dict = json.load(session_file)
                except ValueError:
                    pass

                # Init module path dict.
                if buf.module_path not in session_dict:
                    session_dict[buf.module_path] = {}

                # Update session data.
                session_dict[buf.module_path].update(
                    {buf.url: buf_session_data})

                # Clean session file and update new content.
                session_file.seek(0)
                session_file.truncate(0)
                json.dump(session_dict, session_file)

                print("Saved session: ", buf.module_path, buf.url,
                      buf_session_data)

    def restore_buffer_session(self, buf):
        ''' Restore buffer session from file.'''
        if os.path.exists(self.session_file):
            with open(self.session_file, "r+") as session_file:
                session_dict = {}
                try:
                    session_dict = json.load(session_file)
                except ValueError:
                    pass

                if buf.module_path in session_dict:
                    if buf.url in session_dict[buf.module_path]:
                        buf.restore_session_data(
                            session_dict[buf.module_path][buf.url])

    def cleanup(self):
        '''Do some cleanup before exit python process.'''
        close_epc_client()
示例#10
0
            "window", browser_xid)
        emacs_xwindow = xlib_display.create_resource_object(
            "window", self.emacs_xid)

        browser_xwindow.reparent(emacs_xwindow, x, y)

        xlib_display.sync()


if __name__ == '__main__':
    import sys
    import signal

    app = QApplication(sys.argv)

    server = ThreadingEPCServer(('localhost', 0), log_traceback=True)

    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.allow_reuse_address = True

    tray_view = None

    def call_method(method_name, args):
        handler = server.clients[0]
        handler.call(method_name, args)

    @postGui(False)
    def init(emacs_xid, minibuffer_height):
        global tray_view

        tray_view = TrayView(emacs_xid, minibuffer_height)
示例#11
0
#epc_browser.py
from browser import Browser
from epc.server import EPCServer
from epc.server import ThreadingEPCServer
import threading

global server
server = None
try:
    __IPYTHON__
    server = ThreadingEPCServer(('localhost', 0))
except:
    server = EPCServer(('localhost', 0))


class EpcBrowser(Browser):
    def setup(self):
        result = Browser.setup(self, visible=True, driver="firefox_firebug")
        return result


global browser
browser = EpcBrowser()
browser.setup()


def js_ex(string):
    print "js_ex "
    result = browser.js_ex(string)
    print result
    return result
示例#12
0
    def __init__(self, args):
        global emacs_width, emacs_height, eaf_config_dir, proxy_string

        # Parse init arguments.
        (emacs_width, emacs_height, proxy_host, proxy_port, proxy_type,
         config_dir, emacs_server_port, var_dict_string) = args
        emacs_width = int(emacs_width)
        emacs_height = int(emacs_height)
        eaf_config_dir = os.path.join(os.path.expanduser(config_dir), '')

        # Init variables.
        self.buffer_dict = {}
        self.view_dict = {}
        self.emacs_var_dict = {}
        self.session_file = os.path.join(eaf_config_dir, "session.json")

        # Update Emacs var dictionary.
        self.update_emacs_var_dict(var_dict_string)

        # Build EPC client.
        self.client = EPCClient(("localhost", int(emacs_server_port)),
                                log_traceback=True)

        # Build EPC server.
        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)
        self.server.logger.setLevel(logging.DEBUG)
        self.server.allow_reuse_address = True

        if not os.path.exists(eaf_config_dir):
            os.makedirs(eaf_config_dir)

        ch = logging.FileHandler(filename=os.path.join(eaf_config_dir,
                                                       'epc_log.txt'),
                                 mode='w')
        ch.setLevel(logging.DEBUG)
        self.server.logger.addHandler(ch)

        self.server.register_instance(
            self)  # register instance functions let elisp side call

        # Start EPC server with sub-thread, avoid block Qt main loop.
        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.start()

        # Pass epc port and webengine codec information to Emacs when first start EAF.
        self.first_start(self.server.server_address[1],
                         self.webengine_include_private_codec())

        # Set Network proxy.
        if proxy_host != "" and proxy_port != "":
            proxy_string = "{0}://{1}:{2}".format(proxy_type, proxy_host,
                                                  proxy_port)

            proxy = QNetworkProxy()
            if proxy_type == "socks5":
                proxy.setType(QNetworkProxy.Socks5Proxy)
            elif proxy_type == "http":
                proxy.setType(QNetworkProxy.HttpProxy)

            proxy.setHostName(proxy_host)
            proxy.setPort(int(proxy_port))
            QNetworkProxy.setApplicationProxy(proxy)
class EAF(object):
    def __init__(self, args):
        global emacs_width, emacs_height, eaf_config_dir, proxy_string

        (emacs_width, emacs_height, proxy_host, proxy_port, proxy_type,
         config_dir, emacs_server_port, var_dict_string) = args
        emacs_width = int(emacs_width)
        emacs_height = int(emacs_height)
        eaf_config_dir = os.path.join(os.path.expanduser(config_dir), '')

        self.buffer_dict = {}
        self.view_dict = {}
        self.emacs_var_dict = {}

        self.update_emacs_var_dict(var_dict_string)

        self.server = ThreadingEPCServer(('localhost', 0), log_traceback=True)

        self.server.logger.setLevel(logging.DEBUG)
        ch = logging.FileHandler(
            filename=os.path.expanduser('~/.emacs.d/eaf/epc_log.txt'),
            mode='w')
        ch.setLevel(logging.DEBUG)
        self.server.logger.addHandler(ch)

        self.server_thread = threading.Thread(target=self.server.serve_forever)
        self.server_thread.allow_reuse_address = True

        self.server.register_instance(self)

        self.server_thread.start()

        # NOTE:
        # Emacs epc client need fetch port from Python process print.
        # And this print must be first print code, otherwise Emacs client will failed to start.
        self.server.print_port()

        self.emacs_server_connect = build_emacs_server_connect(
            int(emacs_server_port))

        self.first_start(self.webengine_include_private_codec())

        self.session_file = os.path.join(eaf_config_dir, "session.json")

        # Set Network proxy.
        if proxy_host != "" and proxy_port != "":
            proxy_string = "{0}://{1}:{2}".format(proxy_type, proxy_host,
                                                  proxy_port)

            proxy = QNetworkProxy()
            if proxy_type == "socks5":
                proxy.setType(QNetworkProxy.Socks5Proxy)
            elif proxy_type == "http":
                proxy.setType(QNetworkProxy.HttpProxy)

            proxy.setHostName(proxy_host)
            proxy.setPort(int(proxy_port))
            QNetworkProxy.setApplicationProxy(proxy)

    def get_command_result(self, command):
        ''' Execute the command and return the result. '''
        if version_info >= (3, 7):
            return subprocess.run(command,
                                  check=False,
                                  shell=True,
                                  stdout=subprocess.PIPE,
                                  text=True).stdout
        else:
            return subprocess.run(command,
                                  check=False,
                                  shell=True,
                                  stdout=subprocess.PIPE).stdout

    def webengine_include_private_codec(self):
        ''' Return bool of whether the QtWebEngineProcess include private codec. '''
        path = os.path.join(
            QLibraryInfo.location(QLibraryInfo.LibraryExecutablesPath),
            "QtWebEngineProcess")
        return self.get_command_result(
            "ldd {} | grep libavformat".format(path)) != ""

    @PostGui()
    def update_emacs_var_dict(self, var_dict_string):
        ''' Update Python side emacs_var_dict.(Fix issue #206) '''
        self.emacs_var_dict = json.loads(var_dict_string)

        for buffer in list(self.buffer_dict.values()):
            buffer.emacs_var_dict = self.emacs_var_dict

    @PostGui()
    def new_buffer(self, buffer_id, url, app_name, arguments):
        ''' Create new buffer. '''
        self.create_app(buffer_id, str(url),
                        "app.{0}.buffer".format(str(app_name)), str(arguments))

    @PostGui()
    def update_buffer_with_url(self, module_path, buffer_url, update_data):
        ''' Update buffer with url '''
        for buffer in list(self.buffer_dict.values()):
            if buffer.module_path == module_path and buffer.url == buffer_url:
                buffer.update_with_data(update_data)
                break

    @PostGui()
    def scroll_other_buffer(self, view_info, scroll_direction, scroll_type):
        ''' Scroll to other buffer '''
        (buffer_id, _, _, _, _) = view_info.split(":")
        if buffer_id in self.buffer_dict:
            self.buffer_dict[buffer_id].scroll_other_buffer(
                scroll_direction, scroll_type)

    def get_new_browser_window_buffer_id(self):
        ''' Return new browser window's buffer ID. '''
        import secrets

        return "{0}-{1}-{2}-{3}-{4}-{5}-{6}".format(secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2),
                                                    secrets.token_hex(2))

    def create_new_browser_window(self):
        ''' Create new browser window.'''
        # Generate buffer id same as eaf.el does.
        buffer_id = self.get_new_browser_window_buffer_id()

        # Create buffer for create new browser window.
        app_buffer = self.create_buffer(buffer_id, "http://0.0.0.0",
                                        "app.browser.buffer", "")

        # Create emacs buffer with buffer id.
        self.create_new_browser_buffer(buffer_id)

        # Return new QWebEngineView for create new browser window.
        return app_buffer.buffer_widget

    def create_app(self, buffer_id, url, module_path, arguments):
        ''' Create app using create_buffer.'''
        try:
            self.create_buffer(buffer_id, url, module_path, arguments)

            return ""
        except ImportError:
            import traceback
            traceback.print_exc()
            return "EAF: Something went wrong when trying to import {0}".format(
                module_path)

    def create_buffer(self, buffer_id, url, module_path, arguments):
        ''' Create buffer.'''
        global emacs_width, emacs_height, eaf_config_dir, proxy_string

        # Create application buffer.
        module = importlib.import_module(module_path)
        app_buffer = module.AppBuffer(buffer_id, url, eaf_config_dir,
                                      arguments, self.emacs_var_dict,
                                      module_path)

        # Add buffer to buffer dict.
        self.buffer_dict[buffer_id] = app_buffer

        # Resize buffer with emacs max window size,
        # view (QGraphicsView) will adjust visual area along with emacs window changed.
        app_buffer.buffer_widget.resize(emacs_width, emacs_height)

        # Monitor buffer signals.
        app_buffer.update_buffer_details.connect(self.update_buffer_details)
        app_buffer.translate_text.connect(self.translate_text)
        app_buffer.open_url_in_new_tab.connect(self.open_url_in_new_tab)
        app_buffer.duplicate_page_in_new_tab.connect(
            self.duplicate_page_in_new_tab)
        app_buffer.open_url_in_background_tab.connect(
            self.open_url_in_background_tab)
        app_buffer.goto_left_tab.connect(self.goto_left_tab)
        app_buffer.goto_right_tab.connect(self.goto_right_tab)

        # Send message to emacs.
        app_buffer.input_message.connect(self.input_message)

        # Handle buffer close request.
        app_buffer.request_close_buffer.connect(self.request_kill_buffer)

        # Handle message to emacs.
        app_buffer.message_to_emacs.connect(self.message_to_emacs)

        # Handle set emacs var signal.
        app_buffer.set_emacs_var.connect(self.set_emacs_var)

        # Handle eval form in emacs.
        app_buffer.eval_in_emacs.connect(self.eval_in_emacs)

        # Handle get_focus_text signal.
        if getattr(app_buffer, "get_focus_text", False) and getattr(
                app_buffer.get_focus_text, "connect", False):
            app_buffer.get_focus_text.connect(self.atomic_edit)

        if getattr(
                app_buffer.buffer_widget, "get_focus_text", False) and getattr(
                    app_buffer.buffer_widget.get_focus_text, "connect", False):
            app_buffer.buffer_widget.get_focus_text.connect(self.atomic_edit)

        # Handle get_sub_node_id signal.
        if getattr(app_buffer, "get_sub_node_id", False) and getattr(
                app_buffer.get_sub_node_id, "connect", False):
            app_buffer.get_sub_node_id.connect(self.add_multiple_sub_nodes)

        # Handle get_brother_node_id signal.
        if getattr(app_buffer, "get_brother_node_id", False) and getattr(
                app_buffer.get_brother_node_id, "connect", False):
            app_buffer.get_brother_node_id.connect(
                self.add_multiple_brother_nodes)

        # Handle get_middle_node_id signal.
        if getattr(app_buffer, "get_middle_node_id", False) and getattr(
                app_buffer.get_middle_node_id, "connect", False):
            app_buffer.get_middle_node_id.connect(
                self.add_multiple_middle_nodes)

        # Handle trigger_focus_event signal.
        if getattr(app_buffer.buffer_widget, "trigger_focus_event",
                   False) and getattr(
                       app_buffer.buffer_widget.trigger_focus_event, "connect",
                       False):
            app_buffer.buffer_widget.trigger_focus_event.connect(
                self.focus_emacs_buffer)

        # Handle export_org_json signal.
        if getattr(app_buffer, "export_org_json", False) and getattr(
                app_buffer.export_org_json, "connect", False):
            app_buffer.export_org_json.connect(self.export_org_json)

        # Handle dev tools signal.
        if getattr(app_buffer, "open_dev_tools_tab", False) and getattr(
                app_buffer.open_dev_tools_tab, "connect", False):
            app_buffer.open_dev_tools_tab.connect(self.open_dev_tools_tab)

        # Handle fulllscreen signal.
        if getattr(app_buffer, "enter_fullscreen_request", False) and getattr(
                app_buffer.enter_fullscreen_request, "connect", False):
            app_buffer.enter_fullscreen_request.connect(
                self.enter_fullscreen_request)

        if getattr(app_buffer, "exit_fullscreen_request", False) and getattr(
                app_buffer.exit_fullscreen_request, "connect", False):
            app_buffer.exit_fullscreen_request.connect(
                self.exit_fullscreen_request)

        # Add create new window when create_new_browser_window_callback is call.
        if module_path == "app.browser.buffer" or module_path == "app.terminal.buffer":
            app_buffer.buffer_widget.create_new_browser_window_callback = self.create_new_browser_window

        elif module_path == "app.rss-reader.buffer":
            app_buffer.buffer_widget.browser.create_new_browser_window_callback = self.create_new_browser_window

        if module_path == "app.browser.buffer":
            app_buffer.proxy_string = proxy_string

        # If arguments is dev_tools, create dev tools page.
        if module_path == "app.browser.buffer" and arguments == "dev_tools" and self.dev_tools_page:
            self.dev_tools_page.setDevToolsPage(
                app_buffer.buffer_widget.web_page)
            self.dev_tools_page = None

        # Restore buffer session.
        self.restore_buffer_session(app_buffer)

        return app_buffer

    @PostGui()
    def update_views(self, args):
        ''' Update views.'''
        view_infos = args.split(",")

        # Show cursor anyway.
        QtWidgets.qApp.restoreOverrideCursor()

        # Do something if buffer's all view hide after update_views operation.
        old_view_buffer_ids = list(
            set(map(lambda v: v.buffer_id, self.view_dict.values())))
        new_view_buffer_ids = list(
            set(map(lambda v: v.split(":")[0], view_infos)))

        # Call all_views_hide interface when buffer's all views will hide.
        # We do something in app's buffer interface, such as videoplayer will pause video when all views hide.
        # Note, we must call this function before last view destroy,
        # such as QGraphicsVideoItem will report "Internal data stream error" error.
        for old_view_buffer_id in old_view_buffer_ids:
            if old_view_buffer_id not in new_view_buffer_ids:
                self.buffer_dict[old_view_buffer_id].all_views_hide()

        # Remove old key from view dict and destroy old view.
        for key in list(self.view_dict):
            if key not in view_infos:
                self.view_dict[key].destroy_view()
                self.view_dict.pop(key, None)

        # Create new view and update in view dict.
        if view_infos != ['']:
            for view_info in view_infos:
                if view_info not in self.view_dict:
                    (buffer_id, _, _, _, _, _) = view_info.split(":")
                    view = View(self.buffer_dict[buffer_id], view_info)
                    self.view_dict[view_info] = view

                    view.trigger_focus_event.connect(self.focus_emacs_buffer)

        # Call some_view_show interface when buffer's view switch back.
        # Note, this must call after new view create, otherwise some buffer,
        # such as QGraphicsVideoItem will report "Internal data stream error" error.
        if view_infos != ['']:
            for new_view_buffer_id in new_view_buffer_ids:
                if new_view_buffer_id not in old_view_buffer_ids:
                    self.buffer_dict[new_view_buffer_id].some_view_show()

        # Adjust buffer size along with views change.
        # Note: just buffer that option `fit_to_view' is False need to adjust,
        # if buffer option fit_to_view is True, buffer render adjust by view.resizeEvent()
        for buffer in list(self.buffer_dict.values()):
            if not buffer.fit_to_view:
                buffer_views = list(
                    filter(lambda v: v.buffer_id == buffer.buffer_id,
                           list(self.view_dict.values())))

                # Adjust buffer size to max view's size.
                if len(buffer_views) > 0:
                    max_view = max(buffer_views,
                                   key=lambda v: v.width * v.height)

                    buffer.buffer_widget.resize(max_view.width,
                                                max_view.height)
                # Adjust buffer size to emacs window size if not match view found.
                else:
                    buffer.buffer_widget.resize(emacs_width, emacs_height)

                # Send resize signal to buffer.
                buffer.resize_view()

    @PostGui()
    def kill_emacs(self):
        ''' Kill all buffurs from buffer dict.'''
        tmp_buffer_dict = {}
        for buffer_id in self.buffer_dict:
            tmp_buffer_dict[buffer_id] = self.buffer_dict[buffer_id]

        for buffer_id in tmp_buffer_dict:
            self.kill_buffer(buffer_id)

    @PostGui()
    def kill_buffer(self, buffer_id):
        ''' Kill all view based on buffer_id and clean buffer from buffer dict.'''
        # Kill all view base on buffer_id.
        for key in list(self.view_dict):
            if buffer_id == self.view_dict[key].buffer_id:
                self.view_dict[key].destroy_view()
                self.view_dict.pop(key, None)

        # Clean buffer from buffer dict.
        if buffer_id in self.buffer_dict:
            # Save buffer session.
            self.save_buffer_session(self.buffer_dict[buffer_id])

            self.buffer_dict[buffer_id].destroy_buffer()
            self.buffer_dict.pop(buffer_id, None)

    @PostGui()
    def execute_function(self, buffer_id, function_name, event_string):
        ''' Execute function and do not return anything. '''
        if buffer_id in self.buffer_dict:
            try:
                buffer = self.buffer_dict[buffer_id]
                buffer.current_event_string = event_string
                buffer.execute_function(function_name)
            except AttributeError:
                import traceback
                traceback.print_exc()
                self.message_to_emacs("Cannot execute function: " +
                                      function_name + " (" + buffer_id + ")")

    def call_function(self, buffer_id, function_name):
        ''' Call function and return the result. '''
        if buffer_id in self.buffer_dict:
            try:
                return str(
                    self.buffer_dict[buffer_id].call_function(function_name))
            except AttributeError:
                import traceback
                traceback.print_exc()
                self.message_to_emacs("Cannot call function: " + function_name)
                return ""

    def call_function_with_args(self, buffer_id, function_name, args_string):
        ''' Call function with arguments and return the result. '''
        if buffer_id in self.buffer_dict:
            try:
                return str(self.buffer_dict[buffer_id].call_function_with_args(
                    function_name, args_string))
            except AttributeError:
                import traceback
                traceback.print_exc()
                self.message_to_emacs("Cannot call function: " + function_name)
                return ""

    @PostGui()
    def action_quit(self, buffer_id):
        ''' Execute action_quit() for specified buffer.'''
        if buffer_id in self.buffer_dict:
            self.buffer_dict[buffer_id].action_quit()

    @PostGui()
    def send_key(self, buffer_id, event_string):
        ''' Send event to buffer when found match buffer.'''
        if buffer_id in self.buffer_dict:
            self.buffer_dict[buffer_id].fake_key_event(event_string)

    @PostGui()
    def send_key_sequence(self, buffer_id, event_string):
        ''' Send event to buffer when found match buffer.'''
        if buffer_id in self.buffer_dict:
            self.buffer_dict[buffer_id].fake_key_sequence(event_string)

    @PostGui()
    def handle_input_response(self, buffer_id, callback_tag, callback_result):
        ''' Handle input message for specified buffer.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                buffer.handle_input_response(callback_tag, callback_result)

    @PostGui()
    def cancel_input_response(self, buffer_id, callback_tag):
        ''' Cancel input message for specified buffer.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                buffer.cancel_input_response(callback_tag)

    @PostGui()
    def update_focus_text(self, buffer_id, new_text):
        ''' Update focus text for specified buffer.'''
        import base64

        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                buffer.set_focus_text(
                    base64.b64decode(new_text).decode("utf-8"))

    @PostGui()
    def update_multiple_sub_nodes(self, buffer_id, new_text):
        ''' Update multiplt sub nodes.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                for line in str(new_text).split("\n"):
                    buffer.add_texted_sub_node(line)

    @PostGui()
    def update_multiple_brother_nodes(self, buffer_id, new_text):
        ''' Update multiplt brother nodes.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                for line in str(new_text).split("\n"):
                    buffer.add_texted_brother_node(line)

    @PostGui()
    def update_multiple_middle_nodes(self, buffer_id, new_text):
        ''' Update multiplt middle nodes.'''
        for buffer in list(self.buffer_dict.values()):
            if buffer.buffer_id == buffer_id:
                for line in str(new_text).split("\n"):
                    buffer.add_texted_middle_node(line)

    def eval_in_emacs(self, fname, fargs_list):
        code = "(" + str(fname)
        for farg in fargs_list:
            arg = str(farg).replace('"', '\\"')
            if len(arg) > 0 and arg[0] == ">":
                arg = arg[1:]
            elif len(arg) == 0 or arg[0] != "'":
                arg = '"' + arg + '"'
            code = code + ' ' + arg
        code += ")"
        self.emacs_server_connect.send(str.encode(code))

    def add_multiple_sub_nodes(self, buffer_id):
        self.eval_in_emacs('eaf--add-multiple-sub-nodes', [buffer_id])

    def add_multiple_brother_nodes(self, buffer_id):
        self.eval_in_emacs('eaf--add-multiple-brother-nodes', [buffer_id])

    def add_multiple_middle_nodes(self, buffer_id):
        self.eval_in_emacs('eaf--add-multiple-middle-nodes', [buffer_id])

    def focus_emacs_buffer(self, message):
        self.eval_in_emacs('eaf-focus-buffer', [message])

    def first_start(self, webengine_include_private_codec):
        self.eval_in_emacs('eaf--first-start',
                           [webengine_include_private_codec])

    def update_buffer_details(self, buffer_id, title, url):
        self.eval_in_emacs('eaf--update-buffer-details',
                           [buffer_id, title, url])

    def open_url_in_new_tab(self, url):
        self.eval_in_emacs('eaf-open-browser', [url])

    def duplicate_page_in_new_tab(self, url):
        self.eval_in_emacs('eaf-browser--duplicate-page-in-new-tab', [url])

    def open_dev_tools_page(self):
        self.eval_in_emacs('eaf-open-dev-tool-page', [])

    def open_url_in_background_tab(self, url):
        self.eval_in_emacs('eaf-open-browser-in-background', [url])

    def goto_left_tab(self):
        self.eval_in_emacs('eaf-goto-left-tab', [])

    def goto_right_tab(self):
        self.eval_in_emacs('eaf-goto-right-tab', [])

    def translate_text(self, text):
        self.eval_in_emacs('eaf-translate-text', [text])

    def input_message(self, buffer_id, message, callback_tag, input_type,
                      input_content):
        self.eval_in_emacs(
            'eaf--input-message',
            [buffer_id, message, callback_tag, input_type, input_content])

    def create_new_browser_buffer(self, buffer_id):
        self.eval_in_emacs('eaf--create-new-browser-buffer', [buffer_id])

    def request_kill_buffer(self, buffer_id):
        self.eval_in_emacs('eaf-request-kill-buffer', [buffer_id])

    def message_to_emacs(self, message):
        self.eval_in_emacs('eaf--show-message', [message])

    def set_emacs_var(self, var_name, var_value, eaf_specific):
        self.eval_in_emacs('eaf--set-emacs-var',
                           [var_name, var_value, eaf_specific])

    def atomic_edit(self, buffer_id, focus_text):
        self.eval_in_emacs('eaf--atomic-edit', [buffer_id, focus_text])

    def export_org_json(self, org_json_content, org_file_path):
        self.eval_in_emacs('eaf--export-org-json',
                           [org_json_content, org_file_path])

    def enter_fullscreen_request(self):
        self.eval_in_emacs('eaf--enter-fullscreen-request', [])

    def exit_fullscreen_request(self):
        self.eval_in_emacs('eaf--exit_fullscreen_request', [])

    def open_dev_tools_tab(self, web_page):
        ''' Open dev-tools tab'''
        self.dev_tools_page = web_page
        self.open_dev_tools_page()

    def save_buffer_session(self, buf):
        ''' Save buffer session to file.'''
        # Create config file it not exist.
        if not os.path.exists(self.session_file):
            basedir = os.path.dirname(self.session_file)
            if not os.path.exists(basedir):
                os.makedirs(basedir)

            with open(self.session_file, 'a'):
                os.utime(self.session_file, None)

            print("Create session file %s" % (self.session_file))

        # Save buffer session to file.
        buf_session_data = buf.save_session_data()
        if buf_session_data != "":
            with open(self.session_file, "r+") as session_file:
                # Init session dict.
                session_dict = {}
                try:
                    session_dict = json.load(session_file)
                except ValueError:
                    pass

                # Init module path dict.
                if buf.module_path not in session_dict:
                    session_dict[buf.module_path] = {}

                # Update session data.
                session_dict[buf.module_path].update(
                    {buf.url: buf_session_data})

                # Clean session file and update new content.
                session_file.seek(0)
                session_file.truncate(0)
                json.dump(session_dict, session_file)

                print("Saved session: ", buf.module_path, buf.url,
                      buf_session_data)

    def restore_buffer_session(self, buf):
        ''' Restore buffer session from file.'''
        if os.path.exists(self.session_file):
            with open(self.session_file, "r+") as session_file:
                session_dict = {}
                try:
                    session_dict = json.load(session_file)
                except ValueError:
                    pass

                if buf.module_path in session_dict:
                    if buf.url in session_dict[buf.module_path]:
                        buf.restore_session_data(
                            session_dict[buf.module_path][buf.url])

                        print("Session restored: ", buf.buffer_id,
                              buf.module_path, self.session_file)
                    else:
                        print("Session is not restored, as no data about %s." %
                              (buf.url))
                else:
                    print(
                        "Session is not restored, as no data present in session file."
                    )
        else:
            print("Session is not restored, as %s cannot be found." %
                  (self.session_file))