#![allow(clippy::too_many_arguments)]
use sdl2::event::Event;
use sdl2::image::InitFlag;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Canvas, TextureCreator};
use sdl2::surface::Surface;
use sdl2::video::GLProfile;
use crate::input_sdl2::InputState;
use crate::sdl2_ffi::FontFFI;
use crate::ColorRydit;
pub struct Sdl2Backend {
pub context: sdl2::Sdl,
pub video_subsystem: sdl2::VideoSubsystem,
pub canvas: Canvas<sdl2::video::Window>,
pub event_pump: sdl2::EventPump,
pub input: InputState,
pub width: i32,
pub height: i32,
pub title: String,
pub gl_context: Option<sdl2::video::GLContext>,
pub texture_creator: TextureCreator<sdl2::video::WindowContext>,
pub font: Option<FontFFI>,
}
impl Sdl2Backend {
pub fn new(title: &str, width: i32, height: i32) -> Result<Self, String> {
let context = sdl2::init().map_err(|e| e.to_string())?;
let video_subsystem = context.video().map_err(|e| e.to_string())?;
let gl_attr = video_subsystem.gl_attr();
gl_attr.set_context_profile(GLProfile::Core);
gl_attr.set_context_version(3, 3);
gl_attr.set_double_buffer(true);
gl_attr.set_multisample_samples(4);
let window = video_subsystem
.window(title, width as u32, height as u32)
.position_centered()
.opengl()
.resizable()
.build()
.map_err(|e| e.to_string())?;
let gl_context = window.gl_create_context().map_err(|e| e.to_string())?;
window
.gl_make_current(&gl_context)
.map_err(|e| e.to_string())?;
gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
let _image_context =
sdl2::image::init(InitFlag::PNG | InitFlag::JPG).map_err(|e| e.to_string())?;
let canvas = window
.into_canvas()
.present_vsync() .build()
.map_err(|e| e.to_string())?;
let event_pump = context.event_pump().map_err(|e| e.to_string())?;
let texture_creator = canvas.texture_creator();
let font = match FontFFI::init() {
Ok(_) => {
println!("[SDL2-BACKEND]: SDL2_ttf inicializado");
None }
Err(e) => {
println!("[SDL2-BACKEND]: SDL2_ttf no disponible: {}", e);
None
}
};
println!("[SDL2-BACKEND]: Ventana creada {}x{}", width, height);
println!("[SDL2-BACKEND]: OpenGL contexto 3.3 Core");
println!("[SDL2-BACKEND]: VSync activado");
println!("[SDL2-BACKEND]: SDL2_image inicializado (PNG, JPG)");
Ok(Self {
context,
video_subsystem,
canvas,
event_pump,
input: InputState::new(),
width,
height,
title: title.to_string(),
gl_context: Some(gl_context),
texture_creator,
font,
})
}
pub fn procesar_eventos(&mut self) -> bool {
self.input.limpiar_frame();
let mut should_close = false;
for event in self.event_pump.poll_iter() {
match event {
Event::Quit { .. } => {
should_close = true;
}
Event::KeyDown {
keycode: Some(keycode),
repeat: false,
..
} => {
self.input.teclas.insert(keycode, true);
self.input.teclas_pressionadas_frame.push(keycode);
if keycode == Keycode::Escape {
should_close = true;
}
}
Event::KeyUp {
keycode: Some(keycode),
..
} => {
self.input.teclas.insert(keycode, false);
}
Event::Window {
win_event: sdl2::event::WindowEvent::Resized(w, h),
..
} => {
self.width = w;
self.height = h;
let viewport = Rect::new(0, 0, w as u32, h as u32);
self.canvas.set_viewport(viewport);
}
_ => {}
}
}
should_close
}
pub fn begin_draw(&mut self) {
self.canvas.set_draw_color(Color::RGB(0, 0, 0));
self.canvas.clear();
self.canvas.present(); }
pub fn clear_background(&mut self, color: ColorRydit) {
let (r, g, b) = color.to_rgb();
self.canvas.set_draw_color(Color::RGB(r, g, b));
self.canvas.clear();
}
pub fn end_draw(&mut self) {
self.canvas.present();
}
pub fn should_close(&self) -> bool {
false }
pub fn is_key_pressed(&self, nombre: &str) -> bool {
self.input.is_key_pressed(nombre)
}
pub fn is_key_just_pressed(&self, nombre: &str) -> bool {
self.input.is_key_just_pressed(nombre)
}
pub fn draw_rect(&mut self, x: i32, y: i32, w: i32, h: i32, r: u8, g: u8, b: u8) {
self.canvas.set_draw_color(Color::RGB(r, g, b));
let rect = sdl2::rect::Rect::new(x, y, w as u32, h as u32);
self.canvas.fill_rect(rect).unwrap();
}
pub fn draw_rect_color(&mut self, x: i32, y: i32, w: i32, h: i32, color: ColorRydit) {
let (r, g, b) = color.to_rgb();
self.draw_rect(x, y, w, h, r, g, b);
}
pub fn draw_circle(&mut self, cx: i32, cy: i32, radius: i32, r: u8, g: u8, b: u8) {
let diameter = radius * 2;
self.canvas.set_draw_color(Color::RGB(r, g, b));
let rect = Rect::new(cx - radius, cy - radius, diameter as u32, diameter as u32);
let _ = self.canvas.fill_rect(rect); }
pub fn load_font(&mut self, path: &str, size: i32) -> Result<(), String> {
let font = FontFFI::load(path, size)?;
self.font = Some(font);
println!("[SDL2-BACKEND]: Fuente cargada: {} ({}px)", path, size);
Ok(())
}
pub fn draw_text(&mut self, text: &str, x: i32, y: i32, _size: u16, r: u8, g: u8, b: u8) {
let font = match &self.font {
Some(f) => f,
None => {
let width = text.len() as i32 * 10;
self.draw_rect(x, y, width, 16, r, g, b);
return;
}
};
let surface_ptr = match font.render_text_blended(text, r, g, b) {
Ok(s) => s,
Err(e) => {
eprintln!("[SDL2-BACKEND]: Error renderizando texto: {}", e);
return;
}
};
unsafe {
let sdl_surface = Surface::from_ll(surface_ptr as *mut sdl2::sys::SDL_Surface);
let texture = match self.texture_creator.create_texture_from_surface(&sdl_surface) {
Ok(t) => t,
Err(e) => {
eprintln!("[SDL2-BACKEND]: Error creando textura: {}", e);
return;
}
};
let query = texture.query();
let dst_rect = Rect::new(x, y, query.width, query.height);
let _ = self.canvas.copy(&texture, None, dst_rect);
}
}
pub fn draw_text_color(&mut self, text: &str, x: i32, y: i32, size: u16, color: ColorRydit) {
let (r, g, b) = color.to_rgb();
self.draw_text(text, x, y, size, r, g, b);
}
pub fn get_target_fps(&self) -> i32 {
60
}
pub fn get_fps(&self) -> i32 {
60
}
}
pub struct TextureManager;
impl TextureManager {
pub fn new() -> Self {
Self
}
pub fn load_surface(_path: &str) -> Result<sdl2::surface::Surface<'static>, String> {
Err("SDL2_image linking pendiente - v0.10.8".to_string())
}
pub fn draw_texture_from_surface(
_surface: &sdl2::surface::Surface,
_canvas: &mut Canvas<sdl2::video::Window>,
_x: i32,
_y: i32,
_width: u32,
_height: u32,
) -> Result<(), String> {
Err("SDL2_image linking pendiente - v0.10.8".to_string())
}
pub fn draw_texture(
_path: &str,
_canvas: &mut Canvas<sdl2::video::Window>,
_x: i32,
_y: i32,
_width: u32,
_height: u32,
) -> Result<(), String> {
Err("SDL2_image linking pendiente - v0.10.8".to_string())
}
pub fn count(&self) -> usize {
0
}
}
impl Default for TextureManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_texture_manager() {
let manager = TextureManager::new();
assert_eq!(manager.count(), 0);
}
#[test]
fn test_input_state() {
let input = InputState::new();
assert!(!input.alguna_tecla_presionada());
}
}