From ea5a2a9447954781245d8bd5cdfdd7f3c4ab24ff Mon Sep 17 00:00:00 2001 From: Qubasa Date: Sat, 4 Jan 2025 00:23:41 +0100 Subject: [PATCH] clan-app: changed webui to webview lib --- .../clan_app/deps/webview/__init__.py | 0 .../clan_app/deps/webview/_dummy_holder.pyx | 0 .../clan_app/deps/webview/_webview_ffi.py | 134 ++++++++++++++++++ .../clan-app/clan_app/deps/webview/webview.py | 92 ++++++++++++ pkgs/webview-wrapper-py/default.nix | 56 -------- pkgs/webview-wrapper/default.nix | 37 ++--- 6 files changed, 241 insertions(+), 78 deletions(-) create mode 100644 pkgs/clan-app/clan_app/deps/webview/__init__.py create mode 100644 pkgs/clan-app/clan_app/deps/webview/_dummy_holder.pyx create mode 100644 pkgs/clan-app/clan_app/deps/webview/_webview_ffi.py create mode 100644 pkgs/clan-app/clan_app/deps/webview/webview.py delete mode 100644 pkgs/webview-wrapper-py/default.nix diff --git a/pkgs/clan-app/clan_app/deps/webview/__init__.py b/pkgs/clan-app/clan_app/deps/webview/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pkgs/clan-app/clan_app/deps/webview/_dummy_holder.pyx b/pkgs/clan-app/clan_app/deps/webview/_dummy_holder.pyx new file mode 100644 index 000000000..e69de29bb diff --git a/pkgs/clan-app/clan_app/deps/webview/_webview_ffi.py b/pkgs/clan-app/clan_app/deps/webview/_webview_ffi.py new file mode 100644 index 000000000..9295f5816 --- /dev/null +++ b/pkgs/clan-app/clan_app/deps/webview/_webview_ffi.py @@ -0,0 +1,134 @@ +import ctypes +import sys +import os +import platform +import urllib.request +from pathlib import Path +from ctypes import c_int, c_char_p, c_void_p, CFUNCTYPE +import ctypes.util + +def _encode_c_string(s: str) -> bytes: + return s.encode("utf-8") + +def _get_webview_version(): + """Get webview version from environment variable or use default""" + return os.getenv("WEBVIEW_VERSION", "0.8.1") + +def _get_lib_names(): + """Get platform-specific library names.""" + system = platform.system().lower() + machine = platform.machine().lower() + + if system == "windows": + if machine == "amd64" or machine == "x86_64": + return ["webview.dll", "WebView2Loader.dll"] + elif machine == "arm64": + raise Exception("arm64 is not supported on Windows") + elif system == "darwin": + if machine == "arm64": + return ["libwebview.aarch64.dylib"] + else: + return ["libwebview.x86_64.dylib"] + else: # linux + return ["libwebview.so"] + +def _get_download_urls(): + """Get the appropriate download URLs based on the platform.""" + version = _get_webview_version() + return [f"https://github.com/webview/webview_deno/releases/download/{version}/{lib_name}" + for lib_name in _get_lib_names()] + +def _be_sure_libraries(): + """Ensure libraries exist and return paths.""" + if getattr(sys, 'frozen', False): + if hasattr(sys, '_MEIPASS'): + base_dir = Path(sys._MEIPASS) + else: + base_dir = Path(sys.executable).parent / '_internal' + else: + base_dir = Path(__file__).parent + + lib_dir = base_dir / "lib" + lib_names = _get_lib_names() + lib_paths = [lib_dir / lib_name for lib_name in lib_names] + + # Check if any library is missing + missing_libs = [path for path in lib_paths if not path.exists()] + if not missing_libs: + return lib_paths + + # Download missing libraries + download_urls = _get_download_urls() + system = platform.system().lower() + + lib_dir.mkdir(parents=True, exist_ok=True) + + for url, lib_path in zip(download_urls, lib_paths): + if lib_path.exists(): + continue + + print(f"Downloading library from {url}") + try: + req = urllib.request.Request( + url, + headers={'User-Agent': 'Mozilla/5.0'} + ) + with urllib.request.urlopen(req) as response, open(lib_path, 'wb') as out_file: + out_file.write(response.read()) + except Exception as e: + raise RuntimeError(f"Failed to download library: {e}") + + return lib_paths + +class _WebviewLibrary: + def __init__(self): + lib_names=_get_lib_names() + try: + library_path = ctypes.util.find_library(lib_names[0]) + if not library_path: + library_paths = _be_sure_libraries() + self.lib = ctypes.cdll.LoadLibrary(str(library_paths[0])) + except Exception as e: + print(f"Failed to load webview library: {e}") + raise + # Define FFI functions + self.webview_create = self.lib.webview_create + self.webview_create.argtypes = [c_int, c_void_p] + self.webview_create.restype = c_void_p + + self.webview_destroy = self.lib.webview_destroy + self.webview_destroy.argtypes = [c_void_p] + + self.webview_run = self.lib.webview_run + self.webview_run.argtypes = [c_void_p] + + self.webview_terminate = self.lib.webview_terminate + self.webview_terminate.argtypes = [c_void_p] + + self.webview_set_title = self.lib.webview_set_title + self.webview_set_title.argtypes = [c_void_p, c_char_p] + + self.webview_set_size = self.lib.webview_set_size + self.webview_set_size.argtypes = [c_void_p, c_int, c_int, c_int] + + self.webview_navigate = self.lib.webview_navigate + self.webview_navigate.argtypes = [c_void_p, c_char_p] + + self.webview_init = self.lib.webview_init + self.webview_init.argtypes = [c_void_p, c_char_p] + + self.webview_eval = self.lib.webview_eval + self.webview_eval.argtypes = [c_void_p, c_char_p] + + self.webview_bind = self.lib.webview_bind + self.webview_bind.argtypes = [c_void_p, c_char_p, c_void_p, c_void_p] + + self.webview_unbind = self.lib.webview_unbind + self.webview_unbind.argtypes = [c_void_p, c_char_p] + + self.webview_return = self.lib.webview_return + self.webview_return.argtypes = [c_void_p, c_char_p, c_int, c_char_p] + + self.CFUNCTYPE = CFUNCTYPE + +_webview_lib = _WebviewLibrary() diff --git a/pkgs/clan-app/clan_app/deps/webview/webview.py b/pkgs/clan-app/clan_app/deps/webview/webview.py new file mode 100644 index 000000000..2cebd315e --- /dev/null +++ b/pkgs/clan-app/clan_app/deps/webview/webview.py @@ -0,0 +1,92 @@ +from enum import IntEnum +from typing import Optional, Callable, Any +import json +import ctypes +from ._webview_ffi import _webview_lib, _encode_c_string + +class SizeHint(IntEnum): + NONE = 0 + MIN = 1 + MAX = 2 + FIXED = 3 + +class Size: + def __init__(self, width: int, height: int, hint: SizeHint): + self.width = width + self.height = height + self.hint = hint + +class Webview: + def __init__(self, debug: bool = False, size: Optional[Size] = None, window: Optional[int] = None): + self._handle = _webview_lib.webview_create(int(debug), window) + self._callbacks = {} + + if size: + self.size = size + + @property + def size(self) -> Size: + return self._size + + @size.setter + def size(self, value: Size): + _webview_lib.webview_set_size(self._handle, value.width, value.height, value.hint) + self._size = value + + @property + def title(self) -> str: + return self._title + + @title.setter + def title(self, value: str): + _webview_lib.webview_set_title(self._handle, _encode_c_string(value)) + self._title = value + + def destroy(self): + for name in list(self._callbacks.keys()): + self.unbind(name) + _webview_lib.webview_terminate(self._handle) + _webview_lib.webview_destroy(self._handle) + self._handle = None + + def navigate(self, url: str): + _webview_lib.webview_navigate(self._handle, _encode_c_string(url)) + + def run(self): + _webview_lib.webview_run(self._handle) + self.destroy() + + def bind(self, name: str, callback: Callable[..., Any]): + def wrapper(seq: bytes, req: bytes, arg: int): + args = json.loads(req.decode()) + try: + result = callback(*args) + success = True + except Exception as e: + result = str(e) + success = False + self.return_(seq.decode(), 0 if success else 1, json.dumps(result)) + + c_callback = _webview_lib.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p)(wrapper) + self._callbacks[name] = c_callback + _webview_lib.webview_bind(self._handle, _encode_c_string(name), c_callback, None) + + def unbind(self, name: str): + if name in self._callbacks: + _webview_lib.webview_unbind(self._handle, _encode_c_string(name)) + del self._callbacks[name] + + def return_(self, seq: str, status: int, result: str): + _webview_lib.webview_return(self._handle, _encode_c_string(seq), status, _encode_c_string(result)) + + def eval(self, source: str): + _webview_lib.webview_eval(self._handle, _encode_c_string(source)) + + def init(self, source: str): + _webview_lib.webview_init(self._handle, _encode_c_string(source)) + +if __name__ == "__main__": + wv = Webview() + wv.title = "Hello, World!" + wv.navigate("https://www.google.com") + wv.run() diff --git a/pkgs/webview-wrapper-py/default.nix b/pkgs/webview-wrapper-py/default.nix deleted file mode 100644 index 9d13ca739..000000000 --- a/pkgs/webview-wrapper-py/default.nix +++ /dev/null @@ -1,56 +0,0 @@ -{ lib -, buildPythonPackage -, fetchFromGitHub - -# build-system dependencies -, setuptools -, wheel -, webview-wrapper - -}: - -buildPythonPackage rec { - pname = "python-webui"; - version = "main"; - - src = fetchFromGitHub { - owner = "webui-dev"; - repo = "python-webui"; - rev = "fa961b5ee0752c9408ac01519097f5481a0fcecf"; # Replace with specific commit hash for reproducibility - # sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; # Replace with actual hash via nix-prefetch-git - }; - - sourceRoot = "PyPI/Package"; - - # Indicate this is a recent Python project with PEP 517 support (pyproject.toml) - pyproject = true; - - # Declare the build system (setuptools and wheel are common) - buildInputs = [ - setuptools - wheel - ]; - - # Declare required Python package dependencies - propagatedBuildInputs = [ - - ]; - - # Native inputs for testing, if tests are included - nativeCheckInputs = [ - - ]; - - # If tests don't work out of the box or need adjustments, patches can be applied here - postPatch = '' - # Example: Modify or patch some test files - echo "No postPatch modifications applied yet." - ''; - - meta = with lib; { - description = "A Python library for webui-dev"; - homepage = "https://github.com/webui-dev/python-webui"; - license = licenses.mit; - maintainers = [ maintainers.yourname ]; - }; -} \ No newline at end of file diff --git a/pkgs/webview-wrapper/default.nix b/pkgs/webview-wrapper/default.nix index 4adc04ed1..3951ebf45 100644 --- a/pkgs/webview-wrapper/default.nix +++ b/pkgs/webview-wrapper/default.nix @@ -1,39 +1,32 @@ { pkgs }: -pkgs.stdenv.mkDerivation rec { - pname = "webui"; +pkgs.stdenv.mkDerivation { + pname = "webview"; version = "nigthly"; src = pkgs.fetchFromGitHub { - owner = "webui-dev"; - repo = "python-webui"; - rev = "0ff3b1351b9e24be4463b1baf2c26966caeae74a"; # Use a specific commit sha or tag for reproducibility - sha256 = "sha256-xSOnCkW4iZkSSLKzk6r3hewC3bPJlV7L6aoGEchyEys="; # Replace with actual sha256 + owner = "webview"; + repo = "webview"; + rev = "83a4b4a5bbcb4b0ba2ca3ee226c2da1414719106"; + sha256 = "sha256-5R8kllvP2EBuDANIl07fxv/EcbPpYgeav8Wfz7Kt13c="; }; outputs = [ "out" "dev" ]; # Dependencies used during the build process, if any - buildInputs = [ - pkgs.gnumake + buildInputs = with pkgs; [ + gnumake + cmake + pkg-config + webkitgtk_6_0 + gtk4 ]; - # Commands to build and install the project - buildPhase = '' - make - ''; - - installPhase = '' - mkdir -p $out/lib - mkdir -p $out/include - cp -r dist/* $out/lib - cp -r include/* $out/include - ''; meta = with pkgs.lib; { - description = "Webui is a UI library for C/C++/Go/Rust to build portable desktop/web apps using WebView"; - homepage = "https://github.com/webui-dev/webui"; + description = "Tiny cross-platform webview library for C/C++. Uses WebKit (GTK/Cocoa) and Edge WebView2 (Windows)"; + homepage = "https://github.com/webview/webview"; license = licenses.mit; - platforms = platforms.linux ++ platforms.darwin; # Adjust if needed + platforms = platforms.linux ++ platforms.darwin; }; } \ No newline at end of file