feat: 添加 Live2D 渲染插件及相关依赖,优化构建和启动脚本
This commit is contained in:
@@ -40,7 +40,7 @@ pub mod egl {
|
||||
// ============ X11 ============
|
||||
|
||||
pub mod x11 {
|
||||
use std::os::raw::{c_int, c_long, c_uint, c_void};
|
||||
use std::os::raw::{c_int, c_long, c_void};
|
||||
|
||||
pub type Display = c_void;
|
||||
pub type Window = c_long;
|
||||
@@ -68,6 +68,27 @@ pub mod x11 {
|
||||
pub const CWEventMask: c_long = 1 << 11;
|
||||
pub const ExposureMask: c_long = 1 << 15;
|
||||
pub const StructureNotifyMask: c_long = 1 << 17;
|
||||
pub const SubstructureNotifyMask: c_long = 1 << 19;
|
||||
pub const SubstructureRedirectMask: c_long = 1 << 20;
|
||||
|
||||
/// XEvent union(简化为最大 192 字节的字节数组)
|
||||
#[repr(C)]
|
||||
pub struct XEvent {
|
||||
pub data: [std::os::raw::c_char; 192],
|
||||
}
|
||||
|
||||
/// XClientMessageEvent 布局(用于 _NET_WM_STATE 客户端消息)
|
||||
#[repr(C)]
|
||||
pub struct XClientMessageEvent {
|
||||
pub ftype: c_long, // type (ClientMessage = 33)
|
||||
pub serial: c_long, // serial
|
||||
pub send_event: c_int, // send_event
|
||||
pub display: *mut c_void, // display
|
||||
pub window: c_long, // window
|
||||
pub message_type: c_long, // message_type atom
|
||||
pub format: c_int, // format (32)
|
||||
pub data_l: [c_long; 5], // data.l
|
||||
}
|
||||
}
|
||||
|
||||
// ============ GLES2 ============
|
||||
@@ -291,6 +312,33 @@ pub struct Gl {
|
||||
pub x_store_name:
|
||||
unsafe extern "C" fn(disp: *mut x11::Display, w: x11::Window, name: *const c_char) -> c_int,
|
||||
pub x_flush: unsafe extern "C" fn(disp: *mut x11::Display) -> c_int,
|
||||
pub x_default_root_window: unsafe extern "C" fn(disp: *mut x11::Display) -> x11::Window,
|
||||
pub x_sync: unsafe extern "C" fn(disp: *mut x11::Display, discard: c_int) -> c_int,
|
||||
pub x_raise_window: unsafe extern "C" fn(disp: *mut x11::Display, w: x11::Window) -> c_int,
|
||||
pub x_display_width: unsafe extern "C" fn(disp: *mut x11::Display, screen: c_int) -> c_int,
|
||||
pub x_display_height: unsafe extern "C" fn(disp: *mut x11::Display, screen: c_int) -> c_int,
|
||||
pub x_intern_atom: unsafe extern "C" fn(
|
||||
disp: *mut x11::Display,
|
||||
atom_name: *const c_char,
|
||||
only_if_exists: c_int,
|
||||
) -> c_long,
|
||||
pub x_change_property: unsafe extern "C" fn(
|
||||
disp: *mut x11::Display,
|
||||
w: x11::Window,
|
||||
property: c_long,
|
||||
ftype: c_long,
|
||||
format: c_int,
|
||||
mode: c_int,
|
||||
data: *const c_void,
|
||||
nelements: c_int,
|
||||
) -> c_int,
|
||||
pub x_send_event: unsafe extern "C" fn(
|
||||
disp: *mut x11::Display,
|
||||
w: x11::Window,
|
||||
propagate: c_int,
|
||||
event_mask: c_long,
|
||||
event_send: *mut x11::XEvent,
|
||||
) -> c_int,
|
||||
}
|
||||
|
||||
impl Gl {
|
||||
@@ -349,6 +397,14 @@ impl Gl {
|
||||
x_map_window: *lib_x11.get(b"XMapWindow\0")?,
|
||||
x_store_name: *lib_x11.get(b"XStoreName\0")?,
|
||||
x_flush: *lib_x11.get(b"XFlush\0")?,
|
||||
x_default_root_window: *lib_x11.get(b"XDefaultRootWindow\0")?,
|
||||
x_sync: *lib_x11.get(b"XSync\0")?,
|
||||
x_raise_window: *lib_x11.get(b"XRaiseWindow\0")?,
|
||||
x_display_width: *lib_x11.get(b"XDisplayWidth\0")?,
|
||||
x_display_height: *lib_x11.get(b"XDisplayHeight\0")?,
|
||||
x_intern_atom: *lib_x11.get(b"XInternAtom\0")?,
|
||||
x_change_property: *lib_x11.get(b"XChangeProperty\0")?,
|
||||
x_send_event: *lib_x11.get(b"XSendEvent\0")?,
|
||||
|
||||
_lib_egl: lib_egl,
|
||||
_lib_gl: lib_gl,
|
||||
@@ -364,6 +420,7 @@ pub struct RenderContext {
|
||||
pub gl: Gl,
|
||||
pub display: egl::EGLDisplay,
|
||||
pub surface: egl::EGLSurface,
|
||||
pub context: egl::EGLContext,
|
||||
pub x_display: *mut x11::Display,
|
||||
pub x_window: x11::Window,
|
||||
pub program: GLuint,
|
||||
@@ -395,13 +452,10 @@ fn compile_shader(gl: &Gl, shader_type: GLenum, src: &str) -> Result<GLuint> {
|
||||
let mut status = 0;
|
||||
(gl.gl_get_shaderiv)(shader, gl::GL_COMPILE_STATUS, &mut status);
|
||||
if status == 0 {
|
||||
let mut log = vec![0i8; 1024];
|
||||
(gl.gl_get_shader_info_log)(shader, 1024, std::ptr::null_mut(), log.as_mut_ptr());
|
||||
let log_str = String::from_utf8_lossy(std::slice::from_raw_parts(
|
||||
log.as_ptr() as *const u8,
|
||||
1024,
|
||||
));
|
||||
return Err(anyhow!("着色器编译失败: {}", log_str));
|
||||
let mut log: Vec<u8> = vec![0u8; 1024];
|
||||
(gl.gl_get_shader_info_log)(shader, 1024, std::ptr::null_mut(), log.as_mut_ptr() as *mut c_char);
|
||||
let log_str = String::from_utf8_lossy(&log);
|
||||
return Err(anyhow!("shader compile failed: {}", log_str));
|
||||
}
|
||||
Ok(shader)
|
||||
}
|
||||
@@ -414,8 +468,20 @@ impl RenderContext {
|
||||
// X11 窗口
|
||||
let x_display = unsafe { (gl.x_open_display)(std::ptr::null()) };
|
||||
if x_display.is_null() {
|
||||
return Err(anyhow!("XOpenDisplay 失败"));
|
||||
return Err(anyhow!("XOpenDisplay failed"));
|
||||
}
|
||||
let root_window = unsafe { (gl.x_default_root_window)(x_display) };
|
||||
|
||||
// 使用屏幕实际分辨率,确保全屏覆盖
|
||||
let screen = 0; // XDefaultScreen(display),通常 0
|
||||
let screen_w = unsafe { (gl.x_display_width)(x_display, screen) };
|
||||
let screen_h = unsafe { (gl.x_display_height)(x_display, screen) };
|
||||
// 用传入的 width/height 创建窗口(配置值),不用屏幕分辨率
|
||||
// (EGL surface 尺寸需与窗口匹配,大窗口可能导致 GPU 限制问题)
|
||||
let win_w = width;
|
||||
let win_h = height;
|
||||
println!("[Live2D] 窗口尺寸: {}x{} (屏幕 {}x{})", win_w, win_h, screen_w, screen_h);
|
||||
|
||||
let mut attrs = x11::XSetWindowAttributes {
|
||||
background_pixmap: 0,
|
||||
background_pixel: 0,
|
||||
@@ -429,34 +495,66 @@ impl RenderContext {
|
||||
save_under: 0,
|
||||
event_mask: x11::ExposureMask | x11::StructureNotifyMask,
|
||||
do_not_propagate_mask: 0,
|
||||
override_redirect: 1,
|
||||
override_redirect: 0,
|
||||
colormap: 0,
|
||||
cursor: 0,
|
||||
};
|
||||
let x_window = unsafe {
|
||||
(gl.x_create_window)(
|
||||
x_display,
|
||||
root_window,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
width as c_uint,
|
||||
height as c_uint,
|
||||
win_w as c_uint,
|
||||
win_h as c_uint,
|
||||
0,
|
||||
24,
|
||||
1,
|
||||
std::ptr::null_mut(),
|
||||
x11::CWOverrideRedirect | x11::CWEventMask,
|
||||
x11::CWEventMask,
|
||||
&mut attrs,
|
||||
)
|
||||
};
|
||||
if x_window == 0 {
|
||||
return Err(anyhow!("XCreateWindow 失败"));
|
||||
return Err(anyhow!("XCreateWindow failed"));
|
||||
}
|
||||
unsafe {
|
||||
let title = std::ffi::CString::new("ShowenV2 Live2D")?;
|
||||
(gl.x_store_name)(x_display, x_window, title.as_ptr());
|
||||
|
||||
// 让 kwin 把窗口设为全屏(_NET_WM_STATE_FULLSCREEN)
|
||||
// 通过发送 ClientMessage 事件给 root window
|
||||
let net_wm_state = (gl.x_intern_atom)(x_display, b"_NET_WM_STATE\0".as_ptr() as *const c_char, 1);
|
||||
let net_wm_state_fs = (gl.x_intern_atom)(x_display, b"_NET_WM_STATE_FULLSCREEN\0".as_ptr() as *const c_char, 1);
|
||||
let net_wm_state_above = (gl.x_intern_atom)(x_display, b"_NET_WM_STATE_ABOVE\0".as_ptr() as *const c_char, 1);
|
||||
|
||||
// 先 map 窗口,kwin 才会处理
|
||||
(gl.x_map_window)(x_display, x_window);
|
||||
(gl.x_flush)(x_display);
|
||||
(gl.x_sync)(x_display, 0);
|
||||
|
||||
// 发送 _NET_WM_STATE 客户端消息(两个状态:FULLSCREEN + ABOVE)
|
||||
let mut event: x11::XClientMessageEvent = std::mem::zeroed();
|
||||
event.ftype = 33; // ClientMessage
|
||||
event.display = x_display as *mut c_void;
|
||||
event.window = x_window;
|
||||
event.message_type = net_wm_state;
|
||||
event.format = 32;
|
||||
event.data_l[0] = 1; // _NET_WM_STATE_ADD
|
||||
event.data_l[1] = net_wm_state_fs;
|
||||
event.data_l[2] = net_wm_state_above;
|
||||
event.data_l[3] = 1; // source indication: application
|
||||
|
||||
let event_mask = x11::SubstructureNotifyMask | x11::SubstructureRedirectMask;
|
||||
(gl.x_send_event)(
|
||||
x_display,
|
||||
root_window,
|
||||
0,
|
||||
event_mask,
|
||||
&mut event as *mut x11::XClientMessageEvent as *mut x11::XEvent,
|
||||
);
|
||||
(gl.x_sync)(x_display, 0);
|
||||
|
||||
println!("[Live2D] 已请求 kwin 全屏置顶");
|
||||
}
|
||||
|
||||
// EGL
|
||||
@@ -503,7 +601,7 @@ impl RenderContext {
|
||||
}
|
||||
|
||||
let ctx_attrs = [egl::EGL_CONTEXT_CLIENT_VERSION, 2, egl::EGL_NONE];
|
||||
let context = unsafe { (gl.egl_create_context)(display, config, std::ptr::null(), ctx_attrs.as_ptr()) };
|
||||
let context = unsafe { (gl.egl_create_context)(display, config, std::ptr::null_mut(), ctx_attrs.as_ptr()) };
|
||||
if context.is_null() {
|
||||
return Err(anyhow!("eglCreateContext 失败"));
|
||||
}
|
||||
@@ -513,7 +611,7 @@ impl RenderContext {
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(gl.gl_viewport)(0, 0, width, height);
|
||||
(gl.gl_viewport)(0, 0, win_w, win_h);
|
||||
(gl.gl_clear_color)(0.0, 0.0, 0.0, 1.0);
|
||||
(gl.gl_enable)(gl::GL_BLEND);
|
||||
(gl.gl_blend_func)(gl::GL_SRC_ALPHA, gl::GL_ONE_MINUS_SRC_ALPHA);
|
||||
@@ -529,13 +627,10 @@ impl RenderContext {
|
||||
let mut status = 0;
|
||||
(gl.gl_get_programiv)(p, gl::GL_LINK_STATUS, &mut status);
|
||||
if status == 0 {
|
||||
let mut log = vec![0i8; 1024];
|
||||
(gl.gl_get_program_info_log)(p, 1024, std::ptr::null_mut(), log.as_mut_ptr());
|
||||
let log_str = String::from_utf8_lossy(std::slice::from_raw_parts(
|
||||
log.as_ptr() as *const u8,
|
||||
1024,
|
||||
));
|
||||
return Err(anyhow!("着色器链接失败: {}", log_str));
|
||||
let mut log: Vec<u8> = vec![0u8; 1024];
|
||||
(gl.gl_get_program_info_log)(p, 1024, std::ptr::null_mut(), log.as_mut_ptr() as *mut c_char);
|
||||
let log_str = String::from_utf8_lossy(&log);
|
||||
return Err(anyhow!("shader link failed: {}", log_str));
|
||||
}
|
||||
p
|
||||
};
|
||||
@@ -568,6 +663,7 @@ impl RenderContext {
|
||||
gl,
|
||||
display,
|
||||
surface,
|
||||
context,
|
||||
x_display,
|
||||
x_window,
|
||||
program,
|
||||
@@ -620,4 +716,26 @@ impl RenderContext {
|
||||
pub fn clear(&self) {
|
||||
unsafe { (self.gl.gl_clear)(gl::GL_COLOR_BUFFER_BIT); }
|
||||
}
|
||||
|
||||
/// 将窗口提升到 X11 堆叠顺序的最顶层
|
||||
pub fn raise_window(&self) {
|
||||
unsafe {
|
||||
(self.gl.x_raise_window)(self.x_display, self.x_window);
|
||||
}
|
||||
}
|
||||
|
||||
/// 将 EGL 上下文绑定到当前线程(EGL 上下文是线程绑定的)
|
||||
pub fn make_current(&self) -> Result<()> {
|
||||
if unsafe { (self.gl.egl_make_current)(self.display, self.surface, self.surface, self.context) } == 0 {
|
||||
return Err(anyhow!("eglMakeCurrent 失败(子线程)"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 释放当前线程的 EGL 上下文绑定,允许其他线程绑定
|
||||
pub fn release_current(&self) {
|
||||
unsafe {
|
||||
(self.gl.egl_make_current)(self.display, std::ptr::null_mut(), std::ptr::null_mut(), std::ptr::null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user