wryview 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wryview-0.3.0 → wryview-0.3.2}/Cargo.lock +2 -1
- {wryview-0.3.0 → wryview-0.3.2}/Cargo.toml +4 -1
- {wryview-0.3.0 → wryview-0.3.2}/PKG-INFO +1 -1
- {wryview-0.3.0 → wryview-0.3.2}/pyproject.toml +1 -1
- {wryview-0.3.0 → wryview-0.3.2}/python/wryview/__init__.py +3 -3
- {wryview-0.3.0 → wryview-0.3.2}/src/lib.rs +103 -9
- {wryview-0.3.0 → wryview-0.3.2}/.github/workflows/publish-to-pypi.yml +0 -0
- {wryview-0.3.0 → wryview-0.3.2}/.gitignore +0 -0
- {wryview-0.3.0 → wryview-0.3.2}/.vscode/settings.json +0 -0
- {wryview-0.3.0 → wryview-0.3.2}/LICENSE +0 -0
- {wryview-0.3.0 → wryview-0.3.2}/README.md +0 -0
- {wryview-0.3.0 → wryview-0.3.2}/README_ZH.md +0 -0
- {wryview-0.3.0 → wryview-0.3.2}/python/wryview/_core.pyi +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "wryview"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Comprehensive Python binding for wry — embeddable cross-platform webview."
|
|
6
6
|
readme = "README.md"
|
|
@@ -14,6 +14,9 @@ pyo3 = { version = "0.28", features = ["extension-module"] }
|
|
|
14
14
|
wry = { version = "0.55", features = ["devtools"] }
|
|
15
15
|
raw-window-handle = "0.6"
|
|
16
16
|
|
|
17
|
+
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
|
18
|
+
gtk = "0.18"
|
|
19
|
+
|
|
17
20
|
[profile.release]
|
|
18
21
|
lto = true
|
|
19
22
|
codegen-units = 1
|
|
@@ -9,7 +9,7 @@ authors = [
|
|
|
9
9
|
]
|
|
10
10
|
description = "Comprehensive Python binding for wry — cross-platform embeddable webview, no event loop required."
|
|
11
11
|
readme = "README.md"
|
|
12
|
-
version = "0.3.
|
|
12
|
+
version = "0.3.2"
|
|
13
13
|
requires-python = ">=3.10"
|
|
14
14
|
license = "MPL-2.0"
|
|
15
15
|
license-files = ["LICENSE"]
|
|
@@ -26,7 +26,7 @@ Usage:
|
|
|
26
26
|
print(f"{c.name} = {c.value}")
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
from wryview._core import WebView, CookieDict
|
|
29
|
+
from wryview._core import WebView, CookieDict, pump_events, ensure_gtk_init
|
|
30
30
|
|
|
31
|
-
__version__ = "0.3.
|
|
32
|
-
__all__ = ["WebView", "CookieDict"]
|
|
31
|
+
__version__ = "0.3.2"
|
|
32
|
+
__all__ = ["WebView", "CookieDict", "pump_events", "ensure_gtk_init"]
|
|
@@ -110,15 +110,41 @@ impl WebView {
|
|
|
110
110
|
headers: Option<pyo3::Py<pyo3::PyAny>>,
|
|
111
111
|
as_child: bool,
|
|
112
112
|
) -> PyResult<Self> {
|
|
113
|
-
// ── Build native window handle
|
|
114
|
-
|
|
113
|
+
// ── Build native window handle (platform-specific) ───────────
|
|
114
|
+
#[cfg(target_os = "windows")]
|
|
115
|
+
let window_handle = {
|
|
116
|
+
use raw_window_handle::{RawWindowHandle, Win32WindowHandle};
|
|
117
|
+
let hwnd_nz = NonZero::new(parent_hwnd).ok_or_else(|| {
|
|
118
|
+
PyErr::new::<pyo3::exceptions::PyValueError, _>("parent_hwnd is null")
|
|
119
|
+
})?;
|
|
120
|
+
let win32 = Win32WindowHandle::new(hwnd_nz);
|
|
121
|
+
let raw = RawWindowHandle::Win32(win32);
|
|
122
|
+
unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) }
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
#[cfg(target_os = "macos")]
|
|
126
|
+
let window_handle = {
|
|
127
|
+
use raw_window_handle::{AppKitWindowHandle, RawWindowHandle};
|
|
128
|
+
use std::ptr::NonNull;
|
|
129
|
+
let ptr = parent_hwnd as *mut std::ffi::c_void;
|
|
130
|
+
let ns_view = NonNull::new(ptr).ok_or_else(|| {
|
|
131
|
+
PyErr::new::<pyo3::exceptions::PyValueError, _>("parent_hwnd is null")
|
|
132
|
+
})?;
|
|
133
|
+
let appkit = AppKitWindowHandle::new(ns_view);
|
|
134
|
+
let raw = RawWindowHandle::AppKit(appkit);
|
|
135
|
+
unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) }
|
|
136
|
+
};
|
|
115
137
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
138
|
+
#[cfg(all(unix, not(target_os = "macos")))]
|
|
139
|
+
let window_handle = {
|
|
140
|
+
use raw_window_handle::{RawWindowHandle, XlibWindowHandle};
|
|
141
|
+
if parent_hwnd == 0 {
|
|
142
|
+
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>("parent_hwnd is null"));
|
|
143
|
+
}
|
|
144
|
+
let xlib = XlibWindowHandle::new(parent_hwnd as u64);
|
|
145
|
+
let raw = RawWindowHandle::Xlib(xlib);
|
|
146
|
+
unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) }
|
|
147
|
+
};
|
|
122
148
|
|
|
123
149
|
// ── Callback slots ─────────────────────────────────────────────
|
|
124
150
|
let ipc_cb: Arc<Mutex<Option<pyo3::Py<pyo3::PyAny>>>> = Arc::new(Mutex::new(ipc_handler));
|
|
@@ -306,7 +332,13 @@ impl WebView {
|
|
|
306
332
|
for (k, v) in &resp_headers {
|
|
307
333
|
builder = builder.header(k.as_str(), v.as_str());
|
|
308
334
|
}
|
|
309
|
-
let
|
|
335
|
+
let response =
|
|
336
|
+
builder.body(std::borrow::Cow::Owned(resp_body)).unwrap();
|
|
337
|
+
Python::attach(|py| {
|
|
338
|
+
py.detach(move || {
|
|
339
|
+
let _ = r.respond(response);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
310
342
|
}
|
|
311
343
|
Ok::<_, pyo3::PyErr>(unsafe { Python::assume_attached() }.None())
|
|
312
344
|
}
|
|
@@ -371,6 +403,23 @@ impl WebView {
|
|
|
371
403
|
builder = builder.with_html(h);
|
|
372
404
|
}
|
|
373
405
|
|
|
406
|
+
// ── GTK init (Linux only, idempotent) ──────────────────────────
|
|
407
|
+
#[cfg(all(unix, not(target_os = "macos")))]
|
|
408
|
+
{
|
|
409
|
+
match gtk::init() {
|
|
410
|
+
Ok(()) => { /* GTK initialized */ }
|
|
411
|
+
Err(ref e) => {
|
|
412
|
+
// "already initialized" is harmless — gtk::init() is idempotent
|
|
413
|
+
if !gtk::is_initialized() {
|
|
414
|
+
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
|
|
415
|
+
format!("GTK init failed: {}. Is $DISPLAY set?", e),
|
|
416
|
+
));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ── Build the webview ──────────────────────────────────────────
|
|
374
423
|
let webview = if as_child {
|
|
375
424
|
builder.build_as_child(&window_handle)
|
|
376
425
|
} else {
|
|
@@ -751,11 +800,56 @@ impl CookieDict {
|
|
|
751
800
|
}
|
|
752
801
|
}
|
|
753
802
|
|
|
803
|
+
// ── Platform event pump ─────────────────────────────────────────────────
|
|
804
|
+
///
|
|
805
|
+
/// Pump platform-specific events (no-op on Windows/macOS).
|
|
806
|
+
///
|
|
807
|
+
/// On Linux, call this periodically (e.g. via ``QTimer`` at ~60 fps) to
|
|
808
|
+
/// keep WebKitGTK rendering when the host app runs a non-GTK event loop
|
|
809
|
+
/// such as Qt's xcb backend.
|
|
810
|
+
///
|
|
811
|
+
/// This is a module-level function — it does not belong to any single
|
|
812
|
+
/// ``WebView`` instance.
|
|
813
|
+
#[pyfunction]
|
|
814
|
+
fn pump_events() {
|
|
815
|
+
#[cfg(all(unix, not(target_os = "macos")))]
|
|
816
|
+
{
|
|
817
|
+
// Process pending GTK/GLib events without blocking.
|
|
818
|
+
// Equivalent to Python: while Gtk.events_pending(): Gtk.main_iteration_do(False)
|
|
819
|
+
while gtk::main_iteration_do(false) {
|
|
820
|
+
// keep pumping while events remain
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
// Windows / macOS: nothing needed — the native webview integrates with
|
|
824
|
+
// the same event loop that Qt uses (Windows message pump / NSRunLoop).
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/// Initialise GTK from Rust's perspective (no-op on Windows/macOS).
|
|
828
|
+
///
|
|
829
|
+
/// Python's ``gi.repository.Gtk.init()`` calls C's ``gtk_init()``, which
|
|
830
|
+
/// initialises GTK at the OS level. However the Rust ``gtk`` crate tracks
|
|
831
|
+
/// initialisation state in a *separate* static ``AtomicBool``, and the
|
|
832
|
+
/// ``webkit2gtk`` crate (which wry uses internally on Linux) checks that
|
|
833
|
+
/// Rust-level flag.
|
|
834
|
+
///
|
|
835
|
+
/// Call this **once** before creating the first ``WebView`` on Linux. It is
|
|
836
|
+
/// idempotent — safe to call after Python's ``Gtk.init()`` or multiple times.
|
|
837
|
+
#[pyfunction]
|
|
838
|
+
fn ensure_gtk_init() {
|
|
839
|
+
#[cfg(all(unix, not(target_os = "macos")))]
|
|
840
|
+
{
|
|
841
|
+
let _ = gtk::init(); // idempotent; sets the Rust IS_INITIALIZED atomic
|
|
842
|
+
}
|
|
843
|
+
// Windows / macOS: nothing needed
|
|
844
|
+
}
|
|
845
|
+
|
|
754
846
|
// ── Module ─────────────────────────────────────────────────────────────────
|
|
755
847
|
|
|
756
848
|
#[pymodule]
|
|
757
849
|
fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
758
850
|
m.add_class::<WebView>()?;
|
|
759
851
|
m.add_class::<CookieDict>()?;
|
|
852
|
+
m.add_function(wrap_pyfunction!(pump_events, m)?)?;
|
|
853
|
+
m.add_function(wrap_pyfunction!(ensure_gtk_init, m)?)?;
|
|
760
854
|
Ok(())
|
|
761
855
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|