Skip to content

Commit

Permalink
fix crash on macos. working examples
Browse files Browse the repository at this point in the history
  • Loading branch information
thewh1teagle committed Dec 9, 2024
1 parent 200509c commit 192eb22
Show file tree
Hide file tree
Showing 12 changed files with 653 additions and 124 deletions.
500 changes: 490 additions & 10 deletions Cargo.lock

Large diffs are not rendered by default.

42 changes: 27 additions & 15 deletions examples/dist/index.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Hello world from Python + Tauri</h1>
<button>Invoke command</button>
<a href="second.html">
<button>Navigate to second page</button>
</a>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Demo application showcasing integration of web front-end with Python backend">
<title>Tauric Demo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>
<h1>Welcome to Tauric</h1>
</header>
<main>
<nav>
<a href="second.html" id="secondPageLink">Go to Second Page</a>
</nav>
<section>
<button onclick="greet()" id="callPythonButton">
Call Python Backend
</button>
</section>
</main>

<script>
document.querySelector("button").addEventListener("click", () => {
invoke("command", { args: { hello: "world1" } });
});
async function greet() {
const {message} = await invoke('Hello from JavaScript!')
alert(message)
}
</script>
</body>
</body>
</html>
17 changes: 8 additions & 9 deletions examples/dist/second.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
Second page
</head>
<body>
<a href="javascript:void(0);" onclick="history.back()">Go back</a>

<button onclick="history.back()">back</button>
</body>
</html>
</body>
</html>
30 changes: 30 additions & 0 deletions examples/dist/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 20px;
background-color: #f9f9f9;
color: #333;
}
header {
text-align: center;
margin-bottom: 20px;
}
a, button {
display: inline-block;
margin: 10px 5px;
padding: 10px 15px;
text-decoration: none;
font-size: 16px;
color: #fff;
background-color: #007BFF;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
a:hover, button:hover {
background-color: #0056b3;
}
button {
font-weight: bold;
}
17 changes: 8 additions & 9 deletions examples/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,24 @@
"""
from tauripy import Tauri, create_command_callback, create_ready_callback
from pathlib import Path
from threading import Timer
import json

def command_callback(message: bytes) -> None:
print(f"Command: {message.decode('utf-8')}")

def on_ready(tauric: 'Tauri') -> None:
tauric.create_window("example_window", "tauric", "local://index.html")
def on_command(message: bytes):
print(f"Received: {message.decode('utf-8')}")
return json.dumps({'message': 'Hello from Python!'}).encode('utf-8')

def main() -> None:
tauric = Tauri("com.tauric.dev", "tauric")

command_callback_c = create_command_callback(command_callback)
ready_callback_c = create_ready_callback(lambda: on_ready(tauric))
command_callback_c = create_command_callback(on_command)

current_path = Path(__file__).parent
dist_path = current_path / 'dist'
tauric.mount_frontend(str(dist_path.absolute()))
tauric.on_command(command_callback_c)
tauric.on_ready(ready_callback_c)
tauric.run_app()
tauric.run(lambda: tauric.create_window("example_window", "tauric", "fs://index.html"))


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions libtauric/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ cbindgen = "=0.26.0"
ctrlc = "3.4.4"
serde_json = "1.0.120"
tauri = { version = "2", features = ["devtools"] }
tauri-plugin-dialog = "2"
tokio = { version = "1.39.1", features = ["fs"] }
26 changes: 14 additions & 12 deletions libtauric/capabilities/default.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
{
"$schema": "../gen/schemas/capabilities.json",
"identifier": "main",
"permissions": [],
"windows": [
"*"
],
"remote": {
"urls": [
"http://*",
"https://*"
]
}
"$schema": "../gen/schemas/capabilities.json",
"identifier": "main",
"permissions": [
"dialog:default"
],
"windows": [
"*"
],
"remote": {
"urls": [
"http://*",
"https://*"
]
}
}
62 changes: 34 additions & 28 deletions libtauric/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::{
ffi::{c_char, CStr, CString}, fs, path::PathBuf, str::FromStr, sync::Mutex
};
use tauri::{image::{self, Image}, utils::config::TrayIconConfig, AppHandle, Builder, Manager, Url, WebviewWindowBuilder};
use serde_json::Value;
use tauri::{utils::config::TrayIconConfig, AppHandle, Builder, Url, WebviewWindowBuilder};

type IPCCallbackFn = extern "C" fn(*const c_char);
type IPCCallbackFn = extern "C" fn(*const c_char) -> *const c_char;
type ReadyCallbackFn = extern "C" fn();

static APP_HANDLE: Mutex<Option<AppHandle>> = Mutex::new(None);
Expand All @@ -12,44 +13,53 @@ static READY_CALLBACK: Mutex<Option<ReadyCallbackFn>> = Mutex::new(None);
static FRONTEND_DIR: Mutex<Option<String>> = Mutex::new(None);

#[tauri::command]
fn command(args: serde_json::Value) -> Result<(), String> {
fn command(args: serde_json::Value) -> Result<Option<Value>, String> {
let args = serde_json::to_string_pretty(&args).unwrap();
let callback = IPC_CALLBACK.lock().unwrap().clone();

if let Some(callback) = callback {
// Convert the string to a C string
let c_str = CString::new(args).map_err(|e| e.to_string())?;
// Call the callback function
callback(c_str.as_ptr());
let resp = callback(c_str.as_ptr());

if !resp.is_null() {
let c_str_resp = unsafe { CStr::from_ptr(resp) };

let resp_str = c_str_resp.to_str().map_err(|e| e.to_string())?;

// Try to deserialize the string into serde_json::Value
let deserialized: Value = serde_json::from_str(resp_str).map_err(|e| e.to_string())?;

return Ok(Some(deserialized));
}
} else {
return Err("Please register command callback using register_commands()".into());
}
Ok(())
Ok(None)
}

#[no_mangle]
pub extern "C" fn on_command(callback: Option<extern "C" fn(*const c_char)>) {
pub extern "C" fn TauricOnCommand(callback: IPCCallbackFn) {
// Store the callback function in the global variable
*IPC_CALLBACK.lock().unwrap() = callback;
*IPC_CALLBACK.lock().unwrap() = Some(callback);
}

#[no_mangle]
pub extern "C" fn on_ready(callback: Option<extern "C" fn()>) {
pub extern "C" fn TauricOnReady(callback: Option<extern "C" fn()>) {
// Store the callback function in the global variable
println!("ready register");
*READY_CALLBACK.lock().unwrap() = callback;
}

#[no_mangle]
pub extern "C" fn mount_frontend(path: *const c_char) {
pub extern "C" fn TauricMountFrontend(path: *const c_char) {
// Store the callback function in the global variable
println!("mount frontend");
let path = unsafe { CStr::from_ptr(path).to_str().unwrap().to_owned() };
*FRONTEND_DIR.lock().unwrap() = Some(path);
}

#[no_mangle]
pub extern "C" fn create_window(label: *const c_char, title: *const c_char, url: *const c_char) {
pub extern "C" fn TauricCreateWindow(label: *const c_char, title: *const c_char, url: *const c_char) {
let label = unsafe {
assert!(!label.is_null());
CStr::from_ptr(label).to_str().unwrap().to_owned()
Expand All @@ -72,22 +82,22 @@ pub extern "C" fn create_window(label: *const c_char, title: *const c_char, url:
.title(title)
.inner_size(800.0, 600.0)
.visible(true)
.initialization_script("window.addEventListener('DOMContentLoaded', () => { window.invoke = window.__TAURI__.core.invoke; });")
.initialization_script("window.addEventListener('DOMContentLoaded', () => { window.invoke = (args) => window.__TAURI__.core.invoke('command', {args: args}); });")
.build()
.unwrap();
}

#[no_mangle]
pub extern "C" fn close() {
pub extern "C" fn TauricClose() {
if let Some(app_handle) = APP_HANDLE.lock().unwrap().clone() {
app_handle.exit(0);
}
}

#[no_mangle]
pub extern "C" fn run(identifier: *const c_char, product_name: *const c_char, icon_path: *const c_char) -> i32 {
pub extern "C" fn TauricRun(identifier: *const c_char, product_name: *const c_char, icon_path: *const c_char, on_ready: Option<extern "C" fn()>) -> i32 {
ctrlc::set_handler(move || {
close();
TauricClose();
})
.unwrap();
let mut context = tauri::generate_context!();
Expand All @@ -102,22 +112,21 @@ pub extern "C" fn run(identifier: *const c_char, product_name: *const c_char, ic
};
config.identifier = identifier;
config.product_name = Some(product_name);
config.app.with_global_tauri = true;
if !icon_path.is_null() {

let icon_path = unsafe {
CStr::from_ptr(icon_path).to_str().unwrap().to_owned()
};

config.bundle.icon = vec![icon_path.clone()];
println!("icons: {:?}", config.bundle.icon);
println!("custom icon {}", PathBuf::from(icon_path.clone()).exists());
config.app.tray_icon = Some(TrayIconConfig{icon_path: PathBuf::from(icon_path), ..Default::default()});
}


let result = Builder::default()
.register_uri_scheme_protocol("mounted", |app, request| {
println!("local request");
.plugin(tauri_plugin_dialog::init())
.register_uri_scheme_protocol("fs", |_app, request| {
let front_dir = FRONTEND_DIR.lock().unwrap();
let front_dir_opt = front_dir.as_ref().unwrap();
let mut request_path = request.uri().path();
Expand All @@ -127,34 +136,31 @@ pub extern "C" fn run(identifier: *const c_char, product_name: *const c_char, ic
request_path = request_path.strip_prefix("/").unwrap(); // Remove the leading '/'
}
let path = std::path::PathBuf::from(front_dir_opt).join(request_path);
println!("path is {}", path.display());

if path.exists() {
let content = fs::read(path).unwrap();
let result = tauri::http::Response::builder()
.status(200)
.body(content)
.unwrap();
println!("result is {:?}", result);
return result;
} else {
let result = tauri::http::Response::builder()
.status(404)
.body(Vec::new())
.unwrap();
println!("result is {:?}", result);
return result;
}
})
.setup(|app| {
.setup(move |app| {
let mut app_handle = APP_HANDLE.lock().unwrap();

*app_handle = Some(app.handle().clone());

tauri::async_runtime::spawn(async {
let ready_callback = READY_CALLBACK.lock().unwrap().clone();
if let Some(callback) = ready_callback {
println!("calling ready callback");
let on_ready = on_ready.clone();

tauri::async_runtime::spawn(async move {
if let Some(callback) = on_ready {
callback();
}
});
Expand Down
33 changes: 22 additions & 11 deletions libtauric/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
use std::{env, ffi::CString, ptr::null};
use std::{env, ffi::{c_char, CStr, CString}, path::PathBuf, ptr::null};

use serde_json::Value;
use tauric;

extern "C" fn ready_callback() {
println!("Ready callback called!");
#[no_mangle]
extern "C" fn OnReady() {
let label = CString::new("main").unwrap();
let title = CString::new("tauric").unwrap();
let url = CString::new("mounted://index.html").unwrap();
let cwd = CString::new(env::current_dir().unwrap().to_str().unwrap()).unwrap();
tauric::mount_frontend(cwd.as_ptr());
tauric::create_window(label.as_ptr(), title.as_ptr(),url.as_ptr());

let url = CString::new("fs://index.html").unwrap();
tauric::TauricCreateWindow(label.as_ptr(), title.as_ptr(),url.as_ptr());
}

#[no_mangle]
extern "C" fn OnCommand(message: *const c_char) -> *const c_char {
let message_c = unsafe { CStr::from_ptr(message) };
let message = message_c.to_str().unwrap();
let message: Value = serde_json::from_str(message).unwrap();
println!("Received: {:?}", message);
null()
}


fn main() {
// Register the callback function
tauric::on_ready(Some(ready_callback));
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let identifier = CString::new("com.tauric.dev").unwrap();
let product_name = CString::new("tauric").unwrap();
tauric::run(identifier.as_ptr(), product_name.as_ptr(), null());
let dist_dir = PathBuf::from(manifest_dir).join("../examples/dist").canonicalize().unwrap();
let dist_dir = CString::new(dist_dir.to_str().unwrap().to_owned()).unwrap();
tauric::TauricMountFrontend(dist_dir.as_ptr());
tauric::TauricOnCommand(OnCommand);
tauric::TauricRun(identifier.as_ptr(), product_name.as_ptr(), null(), Some(OnReady));
}
Loading

0 comments on commit 192eb22

Please sign in to comment.