From e0a8e0aa533a49ca61abff23fbd198e48f52fd5a Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Sep 2023 13:39:31 +0200 Subject: [PATCH] win32 port Signed-off-by: falkTX --- mod/__init__.py | 11 +++++++- mod/hmi.py | 11 +++++++- mod/host.py | 23 +++++++++------ mod/webserver.py | 35 +++++++++++++---------- server.py | 13 +++++++-- utils/sha1/sha1.h | 4 ++- utils/utils.h | 4 +++ utils/utils_lilv.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 135 insertions(+), 32 deletions(-) diff --git a/mod/__init__.py b/mod/__init__.py index a76716f4..d7ddbfc3 100644 --- a/mod/__init__.py +++ b/mod/__init__.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later import os +import sys import re import json import shutil @@ -14,6 +15,9 @@ from mod.settings import HARDWARE_DESC_FILE +WINDOWS = sys.platform == 'win32' + + def jsoncall(method): @wraps(method) def wrapper(self, *args, **kwargs): @@ -39,6 +43,11 @@ def json_handler(obj): return None +def os_sync(): + if not WINDOWS: + os.sync() + + def check_environment(): from mod.settings import (LV2_PEDALBOARDS_DIR, DEFAULT_PEDALBOARD, DEFAULT_PEDALBOARD_COPY, @@ -92,7 +101,7 @@ def check_environment(): # remove previous update file if os.path.exists(UPDATE_MOD_OS_FILE) and not os.path.exists("/root/check-upgrade-system"): os.remove(UPDATE_MOD_OS_FILE) - os.sync() + os_sync() if os.path.exists(UPDATE_MOD_OS_HERLPER_FILE): os.remove(UPDATE_MOD_OS_HERLPER_FILE) diff --git a/mod/hmi.py b/mod/hmi.py index 27818f39..030eae5f 100644 --- a/mod/hmi.py +++ b/mod/hmi.py @@ -54,9 +54,14 @@ from mod.settings import LOG import logging -import serial import time +try: + import serial + haveSerial = True +except ImportError: + haveSerial = False + class SerialIOStream(BaseIOStream): def __init__(self, sp): self.sp = sp @@ -110,6 +115,10 @@ def isFake(self): # this can be overriden by subclasses to avoid any connection in DEV mode def init(self, callback): + if not haveSerial: + print("ERROR: This system has no python serial support") + return + ioloop = IOLoop.instance() try: sp = None diff --git a/mod/host.py b/mod/host.py index b18d135c..d97e9ef5 100644 --- a/mod/host.py +++ b/mod/host.py @@ -21,14 +21,19 @@ from random import randint from tornado import gen, iostream from tornado.ioloop import IOLoop, PeriodicCallback -from PIL import Image import os, json, socket, time, logging import shutil +# only used for HMI screenshots, optional +try: + from PIL import Image +except ImportError: + pass + from mod import ( TextFileFlusher, get_hardware_descriptor, get_nearest_valid_scalepoint_value, get_unique_name, - read_file_contents, safe_json_load, normalize_for_hw, symbolify + read_file_contents, safe_json_load, normalize_for_hw, os_sync, symbolify ) from mod.addressings import Addressings from mod.bank import ( @@ -2898,7 +2903,7 @@ def preset_save_new(self, instance, name, callback): def add_bundle_callback(ok): preseturi = "file://%s.ttl" % os.path.join(presetbundle, symbolname) pluginData['preset'] = preseturi - os.sync() + os_sync() callback({ 'ok' : True, 'bundle': presetbundle, @@ -2907,7 +2912,7 @@ def add_bundle_callback(ok): def host_callback(ok): if not ok: - os.sync() + os_sync() callback({ 'ok': False, }) @@ -2936,7 +2941,7 @@ def preset_save_replace(self, instance, olduri, presetbundle, name, callback): def add_bundle_callback(ok): preseturi = "file://%s.ttl" % os.path.join(presetbundle, symbolname) pluginData['preset'] = preseturi - os.sync() + os_sync() callback({ 'ok' : True, 'bundle': presetbundle, @@ -2946,7 +2951,7 @@ def add_bundle_callback(ok): def host_callback(ok): if not ok: shutil.rmtree(presetbundle) - os.sync() + os_sync() callback({ 'ok': False, }) @@ -3677,7 +3682,7 @@ def load(self, bundlepath, isDefault=False, abort_catcher=None): else: save_last_bank_and_pedalboard(0, "") - os.sync() + os_sync() return self.pedalboard_name @@ -3981,7 +3986,7 @@ def save(self, title, asNew, callback): save_last_bank_and_pedalboard(0, bundlepath) def state_saved_cb(ok): - os.sync() + os_sync() callback(True, bundlepath, newTitle) # ask host to save any needed extra state @@ -6151,7 +6156,7 @@ def hmi_save_current_pedalboard(self, callback): return def host_callback(ok): - os.sync() + os_sync() callback(True) logging.debug("hmi save current pedalboard") diff --git a/mod/webserver.py b/mod/webserver.py index fe62a8c7..7963e9d9 100644 --- a/mod/webserver.py +++ b/mod/webserver.py @@ -14,7 +14,6 @@ from base64 import b64decode, b64encode from datetime import timedelta from random import randint -from signal import signal, SIGUSR1, SIGUSR2 from tornado import gen, iostream, web, websocket from tornado.escape import squeeze, url_escape, xhtml_escape from tornado.ioloop import IOLoop @@ -22,6 +21,12 @@ from tornado.util import unicode_type from uuid import uuid4 +try: + from signal import signal, SIGUSR1, SIGUSR2 + haveSignal = True +except ImportError: + haveSignal = False + from mod.profile import Profile from mod.settings import (APP, LOG, DEV_API, HTML_DIR, DOWNLOAD_TMP_DIR, DEVICE_KEY, DEVICE_WEBSERVER_PORT, @@ -36,9 +41,9 @@ DEV_HOST, UNTITLED_PEDALBOARD_NAME, MODEL_CPU, MODEL_TYPE, PEDALBOARDS_LABS_HTTP_ADDRESS) from mod import ( - TextFileFlusher, + TextFileFlusher, WINDOWS, check_environment, jsoncall, safe_json_load, - get_hardware_descriptor, get_unique_name, symbolify, + get_hardware_descriptor, get_unique_name, os_sync, symbolify, ) from mod.bank import list_banks, save_banks, remove_pedalboard_from_banks from mod.session import SESSION @@ -141,7 +146,7 @@ def install_bundles_in_tmp_dir(callback): 'installed': installed, } - os.sync() + os_sync() callback(resp) def run_command(args, cwd, callback): @@ -188,7 +193,7 @@ def restart_services(restartJACK2, restartUI): @gen.coroutine def start_restore(): - os.sync() + os_sync() yield gen.Task(SESSION.hmi.restore, datatype='boolean') def _reset_get_all_pedalboards_cache_with_refresh_1(): @@ -597,12 +602,12 @@ def post(self): yield gen.Task(run_command, ["systemctl", "stop", servicename], None) if not finished: - os.sync() + os_sync() self.write(True) @gen.coroutine def reboot(self): - os.sync() + os_sync() yield gen.Task(run_command, ["reboot"], None) class SystemCleanup(JsonRequestHandler): @@ -652,7 +657,7 @@ def post(self): yield gen.Task(run_command, ["systemctl", "stop", "jack2"], None) yield gen.Task(run_command, ["rm", "-rf"] + stuffToDelete, None) - os.sync() + os_sync() self.write({ 'ok' : True, @@ -674,7 +679,7 @@ def process_file(self, basename, callback=lambda:None): run_command(['mv', src, dst], None, self.move_file_finished) def move_file_finished(self, resp): - os.sync() + os_sync() self.result = True self.sfr_callback() @@ -1778,7 +1783,7 @@ def index(self): context = { 'default_icon_template': default_icon_template, 'default_settings_template': default_settings_template, - 'default_pedalboard': DEFAULT_PEDALBOARD, + 'default_pedalboard': json.dumps(DEFAULT_PEDALBOARD), 'cloud_url': CLOUD_HTTP_ADDRESS, 'cloud_labs_url': CLOUD_LABS_HTTP_ADDRESS, 'plugins_url': PLUGINS_HTTP_ADDRESS, @@ -1792,7 +1797,7 @@ def index(self): 'factory_pedalboards': hwdesc.get('factory_pedalboards', False), 'platform': hwdesc.get('platform', "Unknown"), 'addressing_pages': int(hwdesc.get('addressing_pages', 0)), - 'lv2_plugin_dir': LV2_PLUGIN_DIR, + 'lv2_plugin_dir': json.dumps(LV2_PLUGIN_DIR), 'bundlepath': SESSION.host.pedalboard_path, 'title': squeeze(pbname.replace("'", "\\'")), 'size': json.dumps(SESSION.host.pedalboard_size), @@ -1941,7 +1946,7 @@ def post(self, size): elif os.path.exists(USING_256_FRAMES_FILE): os.remove(USING_256_FRAMES_FILE) - os.sync() + os_sync() newsize = set_jack_buffer_size(size) self.write({ @@ -2124,7 +2129,7 @@ def get(self): if os.path.exists(tokensConf): os.remove(tokensConf) - os.sync() + os_sync() self.write(True) @@ -2399,7 +2404,7 @@ def signal_boot_check(): run_command(["hmi-reset"], None, signal_boot_check_step2) def signal_boot_check_step2(r): - os.sync() + os_sync() run_command(["reboot"], None, None) def signal_upgrade_check(): @@ -2451,7 +2456,7 @@ def prepare(isModApp = False): get_all_plugins() print("Done!") - if not isModApp: + if haveSignal and not isModApp: signal(SIGUSR1, signal_recv) signal(SIGUSR2, signal_recv) set_process_name("mod-ui") diff --git a/server.py b/server.py index 45faaf58..b37f99fe 100755 --- a/server.py +++ b/server.py @@ -29,7 +29,16 @@ def create_dummy_credentials(): except Exception as ex: print('Can\'t create a device key: {0}'.format(ex)) -ROOT = os.path.dirname(os.path.realpath(__file__)) +if os.path.isfile(sys.argv[0]): + # running through cx-freeze, do an early import of everything we need + import json + import uuid + from tornado import gen, iostream, web, websocket + ROOT = os.path.dirname(sys.argv[0]) +else: + ROOT = os.path.dirname(os.path.realpath(__file__)) + sys.path = [ os.path.dirname(os.path.realpath(__file__)) ] + sys.path + DATA_DIR = os.environ.get("MOD_DATA_DIR", os.path.join(ROOT, 'data')) os.makedirs(DATA_DIR, exist_ok=True) @@ -51,8 +60,6 @@ def create_dummy_credentials(): if not os.path.isfile(os.environ['MOD_API_KEY']): print('WARN: Missing file {0} with the public API KEY'.format(os.environ['MOD_API_KEY'])) -sys.path = [ os.path.dirname(os.path.realpath(__file__)) ] + sys.path - from mod import webserver webserver.run() diff --git a/utils/sha1/sha1.h b/utils/sha1/sha1.h index 2b93f4b2..e54b2bed 100644 --- a/utils/sha1/sha1.h +++ b/utils/sha1/sha1.h @@ -17,7 +17,9 @@ # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define SHA_BIG_ENDIAN # endif -#else // ! defined __LITTLE_ENDIAN__ +#elif defined _WIN32 +/* assume little endian */ +#else # include // machine/endian.h # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define SHA_BIG_ENDIAN diff --git a/utils/utils.h b/utils/utils.h index 2b9d3fa9..0affa3f0 100644 --- a/utils/utils.h +++ b/utils/utils.h @@ -11,7 +11,11 @@ extern "C" { #include #endif +#ifdef _WIN32 +#define MOD_API __declspec (dllexport) +#else #define MOD_API __attribute__ ((visibility("default"))) +#endif typedef enum { kPluginLicenseNonCommercial = 0, diff --git a/utils/utils_lilv.cpp b/utils/utils_lilv.cpp index c6a0a662..ab887352 100644 --- a/utils/utils_lilv.cpp +++ b/utils/utils_lilv.cpp @@ -44,7 +44,48 @@ # include #endif -#define OS_SEP '/' +#ifdef _WIN32 +# include +# include +# include + +typedef unsigned int uint; + +char* realpath(const char* const name, char* const resolved) +{ + if (name == nullptr) + return nullptr; + + if (_access(name, 4) != 0) + return nullptr; + + char* retname = nullptr; + + if ((retname = resolved) == nullptr) + retname = static_cast(malloc(PATH_MAX + 2)); + + if (retname == nullptr) + return nullptr; + + return _fullpath(retname, name, PATH_MAX); +} + +void setenv(const char* const key, const char* const value, int) +{ + SetEnvironmentVariableA(key, value); +} + +void unsetenv(const char* const key) +{ + SetEnvironmentVariableA(key, nullptr); +} +#endif + +#ifdef _WIN32 +# define OS_SEP '\\' +#else +# define OS_SEP '/' +#endif #define MOD_LICENSE__interface "http://moddevices.com/ns/ext/license#interface" @@ -110,7 +151,28 @@ static const size_t FACTORY_PEDALBOARDS_DIRlen = (FACTORY_PEDALBOARDS_DIR != NUL : 0; // some other cached values -static const char* const HOME = getenv("HOME"); +static const char* getHOME() +{ +#ifdef _WIN32 + WCHAR wpath[MAX_PATH + 256]; + + if (SHGetSpecialFolderPathW(nullptr, wpath, CSIDL_MYDOCUMENTS, FALSE)) + { + static CHAR apath[MAX_PATH + 256]; + + if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, apath, MAX_PATH + 256, nullptr, nullptr)) + return apath; + } +#else + if (const char* const home = getenv("HOME")) + return home; + if (struct passwd* const pwd = getpwuid(getuid())) + return pwd->pw_dir; +#endif + return ""; +} + +static const char* const HOME = getHOME(); static size_t HOMElen = strlen(HOME); // configuration