@pipelab/asset-tauri 1.0.0-beta.0

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.
Files changed (33) hide show
  1. package/LICENSE +110 -0
  2. package/package.json +18 -0
  3. package/template/README.md +7 -0
  4. package/template/index.html +45 -0
  5. package/template/package.json +23 -0
  6. package/template/pnpm-lock.yaml +715 -0
  7. package/template/src-tauri/Cargo.lock +11191 -0
  8. package/template/src-tauri/Cargo.toml +40 -0
  9. package/template/src-tauri/build.rs +3 -0
  10. package/template/src-tauri/capabilities/default.json +7 -0
  11. package/template/src-tauri/icons/128x128.png +0 -0
  12. package/template/src-tauri/icons/128x128@2x.png +0 -0
  13. package/template/src-tauri/icons/32x32.png +0 -0
  14. package/template/src-tauri/icons/Square107x107Logo.png +0 -0
  15. package/template/src-tauri/icons/Square142x142Logo.png +0 -0
  16. package/template/src-tauri/icons/Square150x150Logo.png +0 -0
  17. package/template/src-tauri/icons/Square284x284Logo.png +0 -0
  18. package/template/src-tauri/icons/Square30x30Logo.png +0 -0
  19. package/template/src-tauri/icons/Square310x310Logo.png +0 -0
  20. package/template/src-tauri/icons/Square44x44Logo.png +0 -0
  21. package/template/src-tauri/icons/Square71x71Logo.png +0 -0
  22. package/template/src-tauri/icons/Square89x89Logo.png +0 -0
  23. package/template/src-tauri/icons/StoreLogo.png +0 -0
  24. package/template/src-tauri/icons/icon.icns +0 -0
  25. package/template/src-tauri/icons/icon.ico +0 -0
  26. package/template/src-tauri/icons/icon.png +0 -0
  27. package/template/src-tauri/src/lib copy 2.rs +438 -0
  28. package/template/src-tauri/src/lib copy.rs +449 -0
  29. package/template/src-tauri/src/lib.rs +616 -0
  30. package/template/src-tauri/src/lib_webview_screenshot.rs +430 -0
  31. package/template/src-tauri/src/main.rs +6 -0
  32. package/template/src-tauri/tauri.conf.json +36 -0
  33. package/template/tsconfig.json +23 -0
@@ -0,0 +1,449 @@
1
+ use futures_util::StreamExt;
2
+ use std::env;
3
+ use std::error::Error;
4
+ use std::net::SocketAddr;
5
+ use std::sync::Arc;
6
+ use std::{borrow::Cow, sync::Mutex, time::Instant};
7
+ use steamworks::AppId;
8
+ use steamworks::Client;
9
+ use steamworks::FriendFlags;
10
+ use steamworks::PersonaStateChange;
11
+ use tauri::WebviewWindow;
12
+ use tauri::{async_runtime, Emitter, Manager, RunEvent, WindowEvent};
13
+ use tokio::sync::mpsc::{Receiver, Sender};
14
+ use tokio::sync::oneshot;
15
+ use warp::Filter;
16
+
17
+ pub struct WgpuState<'win> {
18
+ pub queue: wgpu::Queue,
19
+ pub device: wgpu::Device,
20
+ pub surface: wgpu::Surface<'win>,
21
+ pub render_pipeline: wgpu::RenderPipeline,
22
+ pub config: Mutex<wgpu::SurfaceConfiguration>,
23
+ }
24
+
25
+ impl WgpuState<'_> {
26
+ pub async fn new(window: WebviewWindow) -> Self {
27
+ let size = window.inner_size().unwrap();
28
+ let instance = wgpu::Instance::default();
29
+ let surface = instance.create_surface(window).unwrap();
30
+ let adapter = instance
31
+ .request_adapter(&wgpu::RequestAdapterOptions {
32
+ power_preference: wgpu::PowerPreference::default(),
33
+ force_fallback_adapter: false,
34
+ compatible_surface: Some(&surface),
35
+ })
36
+ .await
37
+ .expect("Failed to find an appropriate adapter");
38
+
39
+ let (device, queue) = adapter
40
+ .request_device(
41
+ &wgpu::DeviceDescriptor {
42
+ label: None,
43
+ required_features: wgpu::Features::empty(),
44
+ required_limits: wgpu::Limits::downlevel_webgl2_defaults()
45
+ .using_resolution(adapter.limits()),
46
+ },
47
+ None,
48
+ )
49
+ .await
50
+ .expect("Failed to create device");
51
+
52
+ let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
53
+ label: None,
54
+ source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(
55
+ r#"
56
+ @vertex
57
+ fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {
58
+ var position: vec4<f32>;
59
+ switch (vertex_index) {
60
+ case 0u: {
61
+ position = vec4<f32>(-1.0, -1.0, 0.0, 1.0); // Bottom-left
62
+ }
63
+ case 1u: {
64
+ position = vec4<f32>( 1.0, -1.0, 0.0, 1.0); // Bottom-right
65
+ }
66
+ case 2u: {
67
+ position = vec4<f32>( 0.0, 1.0, 0.0, 1.0); // Top-center
68
+ }
69
+ default: {
70
+ position = vec4<f32>(0.0, 0.0, 0.0, 1.0); // Default case (should not be reached)
71
+ }
72
+ }
73
+ return position;
74
+ }
75
+
76
+ @fragment
77
+ fn fs_main() -> @location(0) vec4<f32> {
78
+ // Output a solid color
79
+ return vec4<f32>(0.0, 1.0, 0.0, 0.5); // Green
80
+ }
81
+
82
+ "#,
83
+ )),
84
+ });
85
+
86
+ let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
87
+ label: None,
88
+ push_constant_ranges: &[],
89
+ bind_group_layouts: &[],
90
+ });
91
+
92
+ let swapchain_capabilities = surface.get_capabilities(&adapter);
93
+ println!("swapchain_capabilities {:?}", swapchain_capabilities);
94
+ let swapchain_format = swapchain_capabilities.formats[0];
95
+
96
+ let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
97
+ label: None,
98
+ layout: Some(&pipeline_layout),
99
+ vertex: wgpu::VertexState {
100
+ module: &shader,
101
+ entry_point: "vs_main",
102
+ buffers: &[],
103
+ compilation_options: wgpu::PipelineCompilationOptions::default(),
104
+ },
105
+ fragment: Some(wgpu::FragmentState {
106
+ module: &shader,
107
+ entry_point: "fs_main",
108
+ targets: &[Some(wgpu::ColorTargetState {
109
+ format: swapchain_format,
110
+ blend: Some(wgpu::BlendState::ALPHA_BLENDING),
111
+ write_mask: wgpu::ColorWrites::ALL,
112
+ })],
113
+ compilation_options: wgpu::PipelineCompilationOptions::default(),
114
+ }),
115
+ primitive: wgpu::PrimitiveState::default(),
116
+ depth_stencil: None,
117
+ multisample: wgpu::MultisampleState::default(),
118
+ multiview: None,
119
+ });
120
+
121
+ let alpha_mode = if swapchain_capabilities
122
+ .alpha_modes
123
+ .contains(&wgpu::CompositeAlphaMode::PreMultiplied)
124
+ {
125
+ wgpu::CompositeAlphaMode::PreMultiplied
126
+ } else {
127
+ swapchain_capabilities.alpha_modes[0]
128
+ };
129
+
130
+ let config = wgpu::SurfaceConfiguration {
131
+ width: size.width,
132
+ height: size.height,
133
+ format: swapchain_format,
134
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
135
+ present_mode: wgpu::PresentMode::Fifo,
136
+ alpha_mode,
137
+ view_formats: vec![],
138
+ desired_maximum_frame_latency: 2,
139
+ };
140
+
141
+ surface.configure(&device, &config);
142
+
143
+ Self {
144
+ device,
145
+ queue,
146
+ surface,
147
+ render_pipeline,
148
+ config: Mutex::new(config),
149
+ }
150
+ }
151
+ }
152
+
153
+ #[tauri::command]
154
+ fn showOverlay() {
155
+ println!("Showing overlay");
156
+ match Client::init_app(480) {
157
+ Ok((client, _single)) => {
158
+ println!("Client created");
159
+ client.friends().activate_game_overlay("hey");
160
+ println!("Overlay shown");
161
+ }
162
+ Err(e) => eprintln!("Failed to initialize Steam client: {:?}", e),
163
+ }
164
+ }
165
+
166
+ async fn websocket_server(tx: Sender<String>, mut rx: Receiver<String>) {
167
+ let addr = SocketAddr::from(([127, 0, 0, 1], 31753));
168
+
169
+ // WebSocket upgrade
170
+ let ws_route = warp::path::end()
171
+ .and(warp::ws())
172
+ .map(move |ws: warp::ws::Ws| {
173
+ let tx = tx.clone();
174
+ ws.on_upgrade(move |websocket| async move {
175
+ let (mut ws_tx, mut ws_rx) = websocket.split();
176
+
177
+ // Forward messages to the sender
178
+ while let Some(result) = ws_rx.next().await {
179
+ if let Ok(msg) = result {
180
+ if let Ok(text) = msg.to_str() {
181
+ println!("Received WebSocket message: {}", text);
182
+ if tx.send(text.to_string()).await.is_err() {
183
+ eprintln!("Failed to send message to channel");
184
+ break;
185
+ }
186
+ }
187
+ }
188
+ }
189
+ })
190
+ });
191
+
192
+ let routes = ws_route;
193
+
194
+ // Spawn HTTP server
195
+ tokio::spawn(warp::serve(routes).run(addr));
196
+ println!("WebSocket server running on ws://{}", addr);
197
+
198
+ // Process messages from the receiver
199
+ while let Some(message) = rx.recv().await {
200
+ println!("Processing message: {}", message);
201
+ // Handle messages as needed
202
+ }
203
+ }
204
+
205
+ fn setup_wgpu_overlay(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
206
+ println!("Webgpu rendering");
207
+
208
+ let window = app.get_webview_window("main").unwrap();
209
+ let size = window.inner_size()?;
210
+
211
+ // Create a WgpuState (containing the device, instance, adapter etc.)
212
+ // And store it in the state
213
+ let wgpu_state = async_runtime::block_on(WgpuState::new(window));
214
+ app.manage(Arc::new(wgpu_state));
215
+
216
+ let app_handle = app.app_handle().clone();
217
+
218
+ async_runtime::spawn(async move {
219
+ let wgpu_state = app_handle.state::<Arc<WgpuState>>();
220
+
221
+ while true {
222
+ let t = Instant::now();
223
+
224
+ let output = match wgpu_state.surface.get_current_texture() {
225
+ Ok(output) => output,
226
+ Err(wgpu::SurfaceError::Lost) => {
227
+ println!("Surface lost, recreating surface...");
228
+ continue;
229
+ }
230
+ Err(wgpu::SurfaceError::OutOfMemory) => {
231
+ eprintln!("Out of memory error");
232
+ continue;
233
+ }
234
+ Err(e) => {
235
+ eprintln!("Failed to acquire next swap chain texture: {:?}", e);
236
+ continue;
237
+ }
238
+ };
239
+ // let view = output
240
+ // .texture
241
+ // .create_view(&wgpu::TextureViewDescriptor::default());
242
+
243
+ // let mut encoder = wgpu_state
244
+ // .device
245
+ // .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
246
+ // {
247
+ // let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
248
+ // label: None,
249
+ // color_attachments: &[Some(wgpu::RenderPassColorAttachment {
250
+ // view: &view,
251
+ // resolve_target: None,
252
+ // ops: wgpu::Operations {
253
+ // load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
254
+ // store: wgpu::StoreOp::Store,
255
+ // },
256
+ // })],
257
+ // depth_stencil_attachment: None,
258
+ // timestamp_writes: None,
259
+ // occlusion_query_set: None,
260
+ // });
261
+ // rpass.set_pipeline(&wgpu_state.render_pipeline);
262
+ // rpass.draw(0..3, 0..1);
263
+ // }
264
+
265
+ // wgpu_state.queue.submit(Some(encoder.finish()));
266
+
267
+ // Read the texture data before calling present
268
+ let width = output.texture.size().width as usize;
269
+ let height = output.texture.size().height as usize;
270
+ let bytes_per_pixel = 4; // RGBA8 format
271
+ let unaligned_bytes_per_row = width * bytes_per_pixel;
272
+ let aligned_bytes_per_row = ((unaligned_bytes_per_row + 255) / 256) * 256;
273
+ let buffer_size = aligned_bytes_per_row * output.texture.size().height as usize;
274
+
275
+ println!(
276
+ "Texture size: width={}, height={}",
277
+ output.texture.size().width,
278
+ output.texture.size().height
279
+ );
280
+
281
+ let buffer_desc = wgpu::BufferDescriptor {
282
+ label: Some("Staging Buffer"),
283
+ size: buffer_size as wgpu::BufferAddress,
284
+ usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
285
+ mapped_at_creation: false,
286
+ };
287
+ let staging_buffer = wgpu_state.device.create_buffer(&buffer_desc);
288
+
289
+ let mut encoder = wgpu_state
290
+ .device
291
+ .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
292
+ encoder.copy_texture_to_buffer(
293
+ wgpu::ImageCopyTexture {
294
+ texture: &output.texture,
295
+ mip_level: 0,
296
+ origin: wgpu::Origin3d::ZERO,
297
+ aspect: wgpu::TextureAspect::All,
298
+ },
299
+ wgpu::ImageCopyBuffer {
300
+ buffer: &staging_buffer,
301
+ layout: wgpu::ImageDataLayout {
302
+ offset: 0,
303
+ bytes_per_row: Some(aligned_bytes_per_row as u32),
304
+ rows_per_image: Some(output.texture.size().height),
305
+ },
306
+ },
307
+ wgpu::Extent3d {
308
+ width: output.texture.size().width,
309
+ height: output.texture.size().height,
310
+ depth_or_array_layers: 1,
311
+ },
312
+ );
313
+ wgpu_state.queue.submit(Some(encoder.finish()));
314
+
315
+ /** Export frame */
316
+ // Map the buffer to read the data
317
+ // println!("buffer {:?}", staging_buffer);
318
+
319
+ // Map the staging buffer to read the data
320
+ let buffer_slice = staging_buffer.slice(..);
321
+ let (sender, receiver) = futures::channel::oneshot::channel();
322
+ buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
323
+ sender.send(result).unwrap();
324
+ });
325
+ wgpu_state.device.poll(wgpu::Maintain::Wait);
326
+
327
+ receiver.await;
328
+
329
+ let unaligned_width = output.texture.size().width as usize;
330
+ let height = output.texture.size().height as usize;
331
+
332
+ let data = buffer_slice.get_mapped_range();
333
+ let image_data = data.to_vec(); // Copy buffer data to a Vec<u8>.
334
+ drop(data); // Release the buffer mapping.
335
+
336
+ // println!("image_data {:?}", image_data);
337
+
338
+ let mut reconstructed_data = Vec::with_capacity(unaligned_bytes_per_row * height);
339
+
340
+ for row in 0..height {
341
+ let start = row * aligned_bytes_per_row;
342
+ let end = start + unaligned_bytes_per_row; // Only take the valid portion
343
+ reconstructed_data.extend_from_slice(&image_data[start..end]);
344
+ }
345
+
346
+ assert_eq!(reconstructed_data.len(), unaligned_bytes_per_row * height);
347
+ println!("Reconstructed data size: {}", reconstructed_data.len());
348
+
349
+ // println!(
350
+ // "image_data {:?}, expected {:?}",
351
+ // image_data.len(),
352
+ // aligned_bytes_per_row * height
353
+ // );
354
+ // println!(
355
+ // "reconstructed_data {:?}, expected {:?}",
356
+ // reconstructed_data.len(),
357
+ // aligned_bytes_per_row * height
358
+ // );
359
+ // println!("Expected 1920000");
360
+
361
+ // Send the data to the webview
362
+ let window = app_handle.get_webview_window("main").unwrap();
363
+ if let Err(e) = window.emit("frame-data", reconstructed_data) {
364
+ eprintln!("Failed to emit data: {:?}", e);
365
+ }
366
+
367
+ /** End */
368
+ // Now call present
369
+ output.present();
370
+
371
+ println!("Frame rendered in: {}ms", t.elapsed().as_millis());
372
+ }
373
+ });
374
+
375
+ Ok(())
376
+ }
377
+
378
+ async fn setup_app<'a>(app: &'a mut tauri::App) -> Result<(), Box<dyn Error>> {
379
+ // Setup HTTP + WebSocket server
380
+ let (tx, rx) = tokio::sync::mpsc::channel::<String>(32);
381
+
382
+ tokio::spawn(async move {
383
+ websocket_server(tx, rx).await;
384
+ });
385
+
386
+ Ok(())
387
+ }
388
+
389
+ #[cfg_attr(mobile, tauri::mobile_entry_point)]
390
+ pub fn run() {
391
+ let init = match Client::init_app(480) {
392
+ Ok(val) => {
393
+ let (client, single) = val;
394
+ let _cb = client.register_callback(|p: PersonaStateChange| {
395
+ println!("Got callback: {:?}", p);
396
+ });
397
+
398
+ let utils = client.utils();
399
+ println!("Utils:");
400
+ println!("AppId: {:?}", utils.app_id());
401
+ println!("UI Language: {}", utils.ui_language());
402
+
403
+ let apps = client.apps();
404
+ println!("Apps");
405
+ println!("IsInstalled(480): {}", apps.is_app_installed(AppId(480)));
406
+ println!("InstallDir(480): {}", apps.app_install_dir(AppId(480)));
407
+ println!("BuildId: {}", apps.app_build_id());
408
+ println!("AppOwner: {:?}", apps.app_owner());
409
+ println!("Langs: {:?}", apps.available_game_languages());
410
+ println!("Lang: {}", apps.current_game_language());
411
+ println!("Beta: {:?}", apps.current_beta_name());
412
+
413
+ let friends = client.friends();
414
+ println!("Friends");
415
+ let list = friends.get_friends(FriendFlags::IMMEDIATE);
416
+ println!("{:?}", list);
417
+ for f in &list {
418
+ println!("Friend: {:?} - {}({:?})", f.id(), f.name(), f.state());
419
+ friends.request_user_information(f.id(), true);
420
+ }
421
+ }
422
+ Err(err) => {
423
+ println!("Error {}", err);
424
+ }
425
+ };
426
+
427
+ tauri::Builder::default()
428
+ .plugin(tauri_plugin_fs::init())
429
+ .setup(move |app| {
430
+ println!("setup");
431
+ let _ = setup_wgpu_overlay(app);
432
+ setup_app(app);
433
+ Ok(())
434
+ })
435
+ .invoke_handler(tauri::generate_handler![showOverlay])
436
+ .build(tauri::generate_context!())
437
+ .expect("error while running tauri application")
438
+ .run(|app_handle, event| match event {
439
+ RunEvent::WindowEvent {
440
+ label: _,
441
+ event: WindowEvent::Resized(size),
442
+ ..
443
+ } => {
444
+ //
445
+ }
446
+ RunEvent::MainEventsCleared => {}
447
+ _ => (),
448
+ });
449
+ }