diff options
| author | suchmememanyskill <38142618+suchmememanyskill@users.noreply.github.com> | 2023-03-22 01:37:23 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-21 17:37:23 -0700 |
| commit | fd325ef1cc1d3e78b5e7686819e05606cc79d963 (patch) | |
| tree | 1372e0efa0ca47b0045b4d29c40bb3a8caeadfc1 /backend/localsocket.py | |
| parent | faf46ba53354b6dcfbfae25e605bf567acd19376 (diff) | |
| download | decky-loader-fd325ef1cc1d3e78b5e7686819e05606cc79d963.tar.gz decky-loader-fd325ef1cc1d3e78b5e7686819e05606cc79d963.zip | |
Add cross-platform support to decky (#387)
* Import generic watchdog observer over platform specific import
* Use os.path rather than genericpath
* Split off socket management in plugin.py
* Don't specify multiprocessing start type
Default on linux is already fork
* Move all platform-specific functions to seperate files
TODO: make plugin.py platform agnostic
* fix import
* add backwards compat to helpers.py
* add backwards compatibility to helpers.py harder
* Testing autobuild for win
* Testing autobuild for win, try 2
* Testing autobuild for win, try 3
* Testing autobuild for win, try 4
* Create the plugins folder before attempting to use it
* Implement win get_username()
* Create win install script
* Fix branch guess from version
* Create .loader.version in install script
* Add .cmd shim to facilitate auto-restarts
* Properly fix branch guess from version
* Fix updater on windows
* Try 2 of fixing updates for windows
* Test
* pain
* Update install script
* Powershell doesn't believe in utf8
* Powershell good
* add ON_LINUX variable to localplatform
* Fix more merge issues
* test
* Move custom imports to main.py
* Move custom imports to after __main__ check
Due to windows' default behaviour being spawn, it will spawn a new process and thus import into sys.path multiple times
* Log errors in get_system_pythonpaths() and get_loader_version() +
split get_system_pythonpaths() on newline
* Remove whitespace in result of get_system_pythonpaths()
* use python3 on linux and python on windows in get_system_pythonpaths()
* Remove fork-specific urls
* Fix MIME types not working on Windows
Diffstat (limited to 'backend/localsocket.py')
| -rw-r--r-- | backend/localsocket.py | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/backend/localsocket.py b/backend/localsocket.py new file mode 100644 index 00000000..ef0e3933 --- /dev/null +++ b/backend/localsocket.py @@ -0,0 +1,132 @@ +import asyncio, time, random +from localplatform import ON_WINDOWS + +BUFFER_LIMIT = 2 ** 20 # 1 MiB + +class UnixSocket: + def __init__(self, on_new_message): + ''' + on_new_message takes 1 string argument. + It's return value gets used, if not None, to write data to the socket. + Method should be async + ''' + self.socket_addr = f"/tmp/plugin_socket_{time.time()}" + self.on_new_message = on_new_message + self.socket = None + self.reader = None + self.writer = None + + async def setup_server(self): + self.socket = await asyncio.start_unix_server(self._listen_for_method_call, path=self.socket_addr, limit=BUFFER_LIMIT) + + async def _open_socket_if_not_exists(self): + if not self.reader: + retries = 0 + while retries < 10: + try: + self.reader, self.writer = await asyncio.open_unix_connection(self.socket_addr, limit=BUFFER_LIMIT) + return True + except: + await asyncio.sleep(2) + retries += 1 + return False + else: + return True + + async def get_socket_connection(self): + if not await self._open_socket_if_not_exists(): + return None, None + + return self.reader, self.writer + + async def close_socket_connection(self): + if self.writer != None: + self.writer.close() + + self.reader = None + + async def read_single_line(self) -> str|None: + reader, writer = await self.get_socket_connection() + + if self.reader == None: + return None + + return await self._read_single_line(reader) + + async def write_single_line(self, message : str): + reader, writer = await self.get_socket_connection() + + if self.writer == None: + return; + + await self._write_single_line(writer, message) + + async def _read_single_line(self, reader) -> str: + line = bytearray() + while True: + try: + line.extend(await reader.readuntil()) + except asyncio.LimitOverrunError: + line.extend(await reader.read(reader._limit)) + continue + except asyncio.IncompleteReadError as err: + line.extend(err.partial) + break + else: + break + + return line.decode("utf-8") + + async def _write_single_line(self, writer, message : str): + if not message.endswith("\n"): + message += "\n" + + writer.write(message.encode("utf-8")) + await writer.drain() + + async def _listen_for_method_call(self, reader, writer): + while True: + line = await self._read_single_line(reader) + + try: + res = await self.on_new_message(line) + except Exception as e: + return + + if res != None: + await self._write_single_line(writer, res) + +class PortSocket (UnixSocket): + def __init__(self, on_new_message): + ''' + on_new_message takes 1 string argument. + It's return value gets used, if not None, to write data to the socket. + Method should be async + ''' + super().__init__(on_new_message) + self.host = "127.0.0.1" + self.port = random.sample(range(40000, 60000), 1)[0] + + async def setup_server(self): + self.socket = await asyncio.start_server(self._listen_for_method_call, host=self.host, port=self.port, limit=BUFFER_LIMIT) + + async def _open_socket_if_not_exists(self): + if not self.reader: + retries = 0 + while retries < 10: + try: + self.reader, self.writer = await asyncio.open_connection(host=self.host, port=self.port, limit=BUFFER_LIMIT) + return True + except: + await asyncio.sleep(2) + retries += 1 + return False + else: + return True + +if ON_WINDOWS: + class LocalSocket (PortSocket): + pass +else: + class LocalSocket (UnixSocket): + pass
\ No newline at end of file |
