 .swf 就能跑:Ruffle 桌面端從文件落到渲染的完整鏈路)
拖一個(gè) .swf 就能跑Ruffle 桌面端從文件落到渲染的完整鏈路【免費(fèi)下載鏈接】ruffleA Flash Player emulator written in Rust項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ru/ruffle把鼠標(biāo)指針按在任意一個(gè).swf文件上拖到 Ruffle 窗口里松手——這是 Flash 模擬器 Ruffle 桌面版最省事的一個(gè)動(dòng)作。它把「用戶手里一個(gè)路徑」變成「窗口里正在播放的一段動(dòng)畫」中間沒有對話框、沒有格式確認(rèn)。這篇文章就盯住這條鏈路事件從哪來數(shù)據(jù)穿過哪幾層畫面怎么落上去。桌面端三塊積木各自管什么整個(gè)桌面殼子大致分三塊。App/MainWindow負(fù)責(zé)接操作系統(tǒng)事件、維持一個(gè) tokio 運(yùn)行時(shí)GuiController管 wgpu 渲染目標(biāo)和 egui 畫的那條菜單欄PlayerController則罩住真正的PlayerSWF 的解析、AVM 虛擬機(jī)、幀循環(huán)都躲在它后面。拖放這件事前兩塊各出一只手事件在MainWindow里被截獲創(chuàng)建動(dòng)作交給GuiController而「能不能播」的判定其實(shí)落在第三塊的取流階段。從 DroppedFile 事件到 create_moviewinit 把操作系統(tǒng)層面的拖放壓平成一個(gè)事件沒有進(jìn)入/懸停的中間態(tài)只有「文件已經(jīng)落地」這一個(gè)瞬間WindowEvent::DroppedFile(file) { if let Some(content_descriptor) ContentDescriptor::new_local(file, None) { self.gui.create_movie( mut self.player, LaunchOptions::from(self.preferences), content_descriptor, ); } }這一段在 app.rs 的window_event里。file是一個(gè)PathBufContentDescriptor::new_local做的事很輕——用Url::from_file_path把本地路徑包成file://URL同時(shí)留一個(gè)空的root_content_pathcontent.rs。注意這里沒有任何擴(kuò)展名檢查拖進(jìn)來是什么就包什么合法性判定被刻意推遲了。create_movie在 controller.rs 里先close_movie把舊播放器整個(gè)銷毀再M(fèi)ovieView::new造一個(gè)新的渲染目標(biāo)然后調(diào)player.create。也就是說每次拖放都會(huì)把上一個(gè)播放的 SWF 從內(nèi)存里連根拔掉而不是疊加。SWF 頭如何反過來決定窗口尺寸player.create在 player.rs 里走 builder 模式把渲染器、導(dǎo)航器、UI 后端全掛上最后調(diào)fetch_root_movie并塞進(jìn)去一個(gè)on_metadata回調(diào)。這個(gè)回調(diào)在 core 真正讀到 SWF 文件頭時(shí)觸發(fā)把HeaderExt含舞臺尺寸通過RuffleEvent::OnMetadata拋回事件循環(huán)。MainWindow::on_metadata收到它之后才做窗口布局拿stage_size()算出目標(biāo)邏輯尺寸套上菜單欄高度Size::clamp到屏幕上限然后request_inner_size。如果 X11 上報(bào)的當(dāng)前尺寸還和請求值對不上狀態(tài)機(jī)就停在WaitingForResize等到下一次Resized事件確認(rèn)到位才翻成Loaded。這個(gè)小小的三態(tài)枚舉Loading / WaitingForResize / Loaded保證了 SWF 里那些能觀察視口尺寸的代碼不會(huì)在窗口還沒定型時(shí)讀到錯(cuò)的值。兩層畫面怎么合成到一塊屏幕真正上屏?xí)r一幀被拆成兩段 draw。GuiController::render先從 wgpu 拿到 surface 紋理開一個(gè) render pass先調(diào)movie_view.render把 SWF 內(nèi)容從它的離屏紋理貼到屏幕上緊接著egui_renderer.render把菜單欄、按鈕那些 egui 三角形疊上去。SWF 自己渲染進(jìn)的是MovieView持有的RenderTarget紋理egui 走的是另一套 tessellate 管線兩者互不干擾最后在同一個(gè) pass 里前后合成。這就是為什么菜單可以被拖拽、縮放時(shí)獨(dú)立重繪而不會(huì)把下面的 SWF 畫面也一起重算。校驗(yàn)為什么不做在拖放入口一個(gè)容易踩的坑是在DroppedFile那里就檢查.swf后綴、讀 magic 字節(jié)不合格直接彈錯(cuò)。Ruffle 沒這么做。文件對話框那條路picker.rs確實(shí)按swf/spl/ruf分了過濾器可拖放這條路完全不過濾ContentDescriptor::new_local對任何路徑都返回Some。真正的合法性檢查下沉到了 core 取流時(shí)讀文件頭的那一步——magic 不對fetch_root_movie自然失敗錯(cuò)誤沿通知通道彈回 UI。把信任邊界壓到最深處的好處是拖一個(gè).txt進(jìn)來不會(huì) panic只是播不起來而且本地文件、bundle、遠(yuǎn)程 URL 三條入口共享同一套判定不用在入口各寫一份。本地跑一條命令cargo run -p ruffle-desktop -- /path/to/movie.swf把文件當(dāng)參數(shù)傳進(jìn)去或直接啟動(dòng)后拖進(jìn)去效果一致。拖放這條路目前只認(rèn)單個(gè)本地文件多文件拖入、bundle 目錄拖入都還沒接上——這塊大概率是下一個(gè)要補(bǔ)的口子值得留意?!久赓M(fèi)下載鏈接】ruffleA Flash Player emulator written in Rust項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ru/ruffle創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考