sovereignty over app lifecycle
This commit is contained in:
668
src-tauri/Cargo.lock
generated
668
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -18,8 +18,16 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri = { version = "2", features = ["macos-private-api", "unstable"] }
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] }
|
||||
tauri-plugin-global-shortcut = "2"
|
||||
tauri-plugin-positioner = "2"
|
||||
reqwest = { version = "0.12.23", features = ["json", "native-tls"] }
|
||||
ts-rs = "11.0.1"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-global-shortcut = "2"
|
||||
tauri-plugin-positioner = "2"
|
||||
|
||||
41
src-tauri/src/app.rs
Normal file
41
src-tauri/src/app.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_positioner::WindowExt;
|
||||
|
||||
use crate::{
|
||||
get_app_handle,
|
||||
services::overlay::{overlay_fullscreen, SCENE_WINDOW_LABEL},
|
||||
};
|
||||
|
||||
pub async fn start_fdoll() {
|
||||
initialize_session().await;
|
||||
}
|
||||
|
||||
pub async fn initialize_session() {
|
||||
let webview_window = tauri::WebviewWindowBuilder::new(
|
||||
get_app_handle(),
|
||||
SCENE_WINDOW_LABEL,
|
||||
tauri::WebviewUrl::App("/scene".into()),
|
||||
)
|
||||
.title("Friendolls Scene")
|
||||
.inner_size(600.0, 500.0)
|
||||
.resizable(false)
|
||||
.decorations(false)
|
||||
.transparent(true)
|
||||
.shadow(false)
|
||||
.visible(true)
|
||||
.skip_taskbar(true)
|
||||
.always_on_top(true)
|
||||
.visible_on_all_workspaces(true)
|
||||
.build()
|
||||
.expect("Failed to display scene screen");
|
||||
|
||||
webview_window
|
||||
.move_window(tauri_plugin_positioner::Position::Center)
|
||||
.unwrap();
|
||||
|
||||
let window = get_app_handle().get_window(webview_window.label()).unwrap();
|
||||
overlay_fullscreen(&window).unwrap();
|
||||
window.set_ignore_cursor_events(true).unwrap();
|
||||
|
||||
println!("Scene window initialized.");
|
||||
}
|
||||
4
src-tauri/src/core/mod.rs
Normal file
4
src-tauri/src/core/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod models;
|
||||
pub mod services;
|
||||
pub mod state;
|
||||
pub mod utilities;
|
||||
8
src-tauri/src/core/models/app_config.rs
Normal file
8
src-tauri/src/core/models/app_config.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[ts(export)]
|
||||
pub struct AppConfig {
|
||||
pub api_base_url: Option<String>,
|
||||
}
|
||||
1
src-tauri/src/core/models/mod.rs
Normal file
1
src-tauri/src/core/models/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod app_config;
|
||||
1
src-tauri/src/core/services/mod.rs
Normal file
1
src-tauri/src/core/services/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
26
src-tauri/src/core/state.rs
Normal file
26
src-tauri/src/core/state.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
// in app-core/src/state.rs
|
||||
use crate::{core::models::app_config::AppConfig, lock_w};
|
||||
use reqwest::Client;
|
||||
use std::sync::{Arc, LazyLock, RwLock};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AppState {
|
||||
pub app_config: Option<AppConfig>,
|
||||
pub http_client: Client,
|
||||
}
|
||||
|
||||
// Global application state
|
||||
// FDOLL = Multiplayer Todo App
|
||||
// Read / write this state via the `lock_r!` / `lock_w!` macros from `fdoll-core::utilities`
|
||||
pub static FDOLL: LazyLock<Arc<RwLock<AppState>>> =
|
||||
LazyLock::new(|| Arc::new(RwLock::new(AppState::default())));
|
||||
|
||||
pub fn init_fdoll_state() {
|
||||
{
|
||||
let mut guard = lock_w!(FDOLL);
|
||||
guard.app_config = Some(AppConfig {
|
||||
api_base_url: Some("http://sandbox:3000".to_string()),
|
||||
});
|
||||
guard.http_client = reqwest::Client::new();
|
||||
}
|
||||
}
|
||||
29
src-tauri/src/core/utilities.rs
Normal file
29
src-tauri/src/core/utilities.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
#[macro_export]
|
||||
macro_rules! lock_r {
|
||||
($rwlock:expr) => {{
|
||||
match $rwlock.read() {
|
||||
Ok(guard) => guard,
|
||||
Err(_) => panic!(
|
||||
"Failed to acquire read lock on {} at {}:{}",
|
||||
stringify!($rwlock),
|
||||
file!(),
|
||||
line!()
|
||||
),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! lock_w {
|
||||
($rwlock:expr) => {{
|
||||
match $rwlock.write() {
|
||||
Ok(guard) => guard,
|
||||
Err(_) => panic!(
|
||||
"Failed to acquire write lock on {} at {}:{}",
|
||||
stringify!($rwlock),
|
||||
file!(),
|
||||
line!()
|
||||
),
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -1,8 +1,50 @@
|
||||
static APP_HANDLE: std::sync::OnceLock<tauri::AppHandle<tauri::Wry>> = std::sync::OnceLock::new();
|
||||
|
||||
mod app;
|
||||
mod core;
|
||||
mod services;
|
||||
|
||||
/// Tauri app handle
|
||||
pub fn get_app_handle<'a>() -> &'a tauri::AppHandle<tauri::Wry> {
|
||||
APP_HANDLE
|
||||
.get()
|
||||
.expect("get_app_handle called but app is still not initialized")
|
||||
}
|
||||
|
||||
fn setup_fdoll() -> Result<(), tauri::Error> {
|
||||
core::state::init_fdoll_state();
|
||||
tokio::spawn(async move { app::start_fdoll().await });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_app_events(event: tauri::RunEvent) {
|
||||
match event {
|
||||
tauri::RunEvent::ExitRequested { api, code, .. } => {
|
||||
if code.is_none() {
|
||||
api.prevent_exit();
|
||||
} else {
|
||||
println!("exit code: {:?}", code);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_positioner::init())
|
||||
// .plugin(tauri_plugin_global_shortcut::Builder::new().build())
|
||||
.invoke_handler(tauri::generate_handler![])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
.setup(|app| {
|
||||
APP_HANDLE
|
||||
.set(app.handle().to_owned())
|
||||
.expect("Failed to init app handle.");
|
||||
setup_fdoll().expect("Failed to setup app.");
|
||||
Ok(())
|
||||
})
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while running tauri application")
|
||||
.run(|_, event| register_app_events(event));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
friendolls_desktop_lib::run()
|
||||
}
|
||||
|
||||
1
src-tauri/src/services/mod.rs
Normal file
1
src-tauri/src/services/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod overlay;
|
||||
25
src-tauri/src/services/overlay.rs
Normal file
25
src-tauri/src/services/overlay.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use crate::get_app_handle;
|
||||
|
||||
pub static SCENE_WINDOW_LABEL: &str = "scene";
|
||||
|
||||
pub fn overlay_fullscreen(window: &tauri::Window) -> Result<(), tauri::Error> {
|
||||
// Get the primary monitor
|
||||
let monitor = get_app_handle().primary_monitor()?.unwrap();
|
||||
|
||||
// Get the work area (usable space, excluding menu bar/dock/notch)
|
||||
let work_area = monitor.work_area();
|
||||
|
||||
// Set window position to top-left of the work area
|
||||
window.set_position(tauri::PhysicalPosition {
|
||||
x: work_area.position.x,
|
||||
y: work_area.position.y,
|
||||
})?;
|
||||
|
||||
// Set window size to match work area size
|
||||
window.set_size(tauri::PhysicalSize {
|
||||
width: work_area.size.width,
|
||||
height: work_area.size.height,
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -10,16 +10,11 @@
|
||||
"frontendDist": "../build"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "friendolls-desktop",
|
||||
"width": 800,
|
||||
"height": 600
|
||||
}
|
||||
],
|
||||
"windows": [],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"macOSPrivateApi": true
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "daisyui";
|
||||
|
||||
|
||||
@plugin "daisyui/theme" {
|
||||
name: "proper";
|
||||
default: true;
|
||||
@@ -37,8 +36,6 @@
|
||||
--noise: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@plugin "daisyui/theme" {
|
||||
name: "properdark";
|
||||
default: false;
|
||||
@@ -73,3 +70,7 @@
|
||||
--depth: 1;
|
||||
--noise: 1;
|
||||
}
|
||||
|
||||
:root {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
7
src/routes/+layout.svelte
Normal file
7
src/routes/+layout.svelte
Normal file
@@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div class="size-full bg-transparent">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -3,3 +3,4 @@
|
||||
// See: https://svelte.dev/docs/kit/single-page-apps
|
||||
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
|
||||
export const ssr = false;
|
||||
import "../app.css";
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<script lang="ts">
|
||||
import "../app.css";
|
||||
</script>
|
||||
|
||||
<main class="card-body">
|
||||
<button class="btn btn-primary">Hello TailwindCSS!</button>
|
||||
</main>
|
||||
|
||||
10
src/routes/scene/+page.svelte
Normal file
10
src/routes/scene/+page.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="w-svw h-svh p-4">
|
||||
<div
|
||||
class="size-max mx-auto bg-base-100 border-base-200 border px-4 py-3 rounded-xl"
|
||||
>
|
||||
<div class="flex flex-col text-center">
|
||||
<p class="text-xl">Friendolls</p>
|
||||
<p class="text-sm opacity-50">Scene Screen</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user