opengl/es renderer
This commit is contained in:
parent
8271920178
commit
a08ea7a86b
10
Cargo.toml
10
Cargo.toml
|
@ -23,11 +23,12 @@ panic = 'abort'
|
|||
opt-level = 3
|
||||
|
||||
[features]
|
||||
default = ["scripting", "backend-sdl", "ogg-playback", "netplay", "exe"]
|
||||
default = ["scripting", "backend-sdl", "render-opengl", "ogg-playback", "exe"]
|
||||
ogg-playback = ["lewton"]
|
||||
backend-sdl = ["sdl2", "sdl2-sys"]
|
||||
#backend-sokol = ["sokol"]
|
||||
backend-glutin = ["winit", "glutin"]
|
||||
backend-glutin = ["winit", "glutin", "render-opengl"]
|
||||
render-opengl = []
|
||||
scripting = ["lua-ffi"]
|
||||
netplay = []
|
||||
editor = []
|
||||
|
@ -45,7 +46,7 @@ chrono = "0.4"
|
|||
cpal = { git = "https://github.com/doukutsu-rs/cpal.git", rev = "e027550be0b93b7e2912c7de28a4944a7d04e070" }
|
||||
directories = "3"
|
||||
funty = "=1.1.0" # https://github.com/bitvecto-rs/bitvec/issues/105
|
||||
glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "a34ee3c99b3c999b638ca2bae53cf96df2b94c04", optional = true }
|
||||
glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "a34ee3c99b3c999b638ca2bae53cf96df2b94c04", optional = true, default_features = false, features = ["x11"] }
|
||||
imgui = "0.7.0"
|
||||
image = { version = "0.23", default-features = false, features = ["png", "bmp"] }
|
||||
itertools = "0.10"
|
||||
|
@ -54,7 +55,6 @@ lewton = { version = "0.10.2", optional = true }
|
|||
libc = { version = "0.2", optional = true }
|
||||
log = "0.4"
|
||||
lua-ffi = { git = "https://github.com/doukutsu-rs/lua-ffi.git", rev = "1ef3caf772d72068297ddf75df06fd2ef8c1daab", optional = true }
|
||||
lru = "0.6.0"
|
||||
num-derive = "0.3.2"
|
||||
num-traits = "0.2.12"
|
||||
paste = "1.0.0"
|
||||
|
@ -70,7 +70,7 @@ strum_macros = "0.20"
|
|||
# remove and replace when drain_filter is in stable
|
||||
vec_mut_scan = "0.4"
|
||||
webbrowser = "0.5.5"
|
||||
winit = { version = "0.24", optional = true }
|
||||
winit = { version = "0.24", optional = true, default_features = false, features = ["x11"] }
|
||||
|
||||
#[build-dependencies]
|
||||
#gl_generator = { version = "0.14.0", optional = true }
|
||||
|
|
|
@ -17,6 +17,8 @@ pub trait BackendEventLoop {
|
|||
}
|
||||
|
||||
pub trait BackendRenderer {
|
||||
fn renderer_name(&self) -> String;
|
||||
|
||||
fn clear(&mut self, color: Color);
|
||||
|
||||
fn present(&mut self) -> GameResult;
|
||||
|
@ -55,7 +57,7 @@ pub trait BackendTexture {
|
|||
pub fn init_backend() -> GameResult<Box<dyn Backend>> {
|
||||
#[cfg(all(feature = "backend-glutin"))]
|
||||
{
|
||||
return crate::framework::backend_opengl::GlutinBackend::new();
|
||||
return crate::framework::backend_glutin::GlutinBackend::new();
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend-sokol")]
|
||||
|
|
|
@ -0,0 +1,518 @@
|
|||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::ffi::c_void;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use glutin::{Api, ContextBuilder, GlProfile, GlRequest, PossiblyCurrent, WindowedContext};
|
||||
use glutin::event::{ElementState, Event, TouchPhase, VirtualKeyCode, WindowEvent};
|
||||
use glutin::event_loop::{ControlFlow, EventLoop};
|
||||
use glutin::window::WindowBuilder;
|
||||
use imgui::{DrawCmdParams, DrawData, DrawIdx, DrawVert};
|
||||
|
||||
use crate::{Game, GAME_SUSPENDED};
|
||||
use crate::common::Rect;
|
||||
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::gl;
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::framework::render_opengl::{GLContext, OpenGLRenderer};
|
||||
use crate::input::touch_controls::TouchPoint;
|
||||
|
||||
pub struct GlutinBackend;
|
||||
|
||||
impl GlutinBackend {
|
||||
pub fn new() -> GameResult<Box<dyn Backend>> {
|
||||
Ok(Box::new(GlutinBackend))
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for GlutinBackend {
|
||||
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> {
|
||||
#[cfg(target_os = "android")]
|
||||
loop {
|
||||
match ndk_glue::native_window().as_ref() {
|
||||
Some(_) => {
|
||||
log::info!("NativeWindow Found: {:?}", ndk_glue::native_window());
|
||||
break;
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Box::new(GlutinEventLoop { refs: Rc::new(UnsafeCell::new(None)) }))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlutinEventLoop {
|
||||
refs: Rc<UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>>,
|
||||
}
|
||||
|
||||
impl GlutinEventLoop {
|
||||
fn get_context(&self, event_loop: &EventLoop<()>) -> &mut WindowedContext<PossiblyCurrent> {
|
||||
let mut refs = unsafe { &mut *self.refs.get() };
|
||||
|
||||
if refs.is_none() {
|
||||
let mut window = WindowBuilder::new();
|
||||
let windowed_context = ContextBuilder::new();
|
||||
let windowed_context = windowed_context.with_gl(GlRequest::Specific(Api::OpenGl, (3, 0)));
|
||||
#[cfg(target_os = "android")]
|
||||
let windowed_context = windowed_context.with_gl(GlRequest::Specific(Api::OpenGlEs, (2, 0)));
|
||||
|
||||
let windowed_context = windowed_context.with_gl_profile(GlProfile::Core)
|
||||
.with_gl_debug_flag(false)
|
||||
.with_pixel_format(24, 8)
|
||||
.with_vsync(true);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use glutin::platform::windows::WindowBuilderExtWindows;
|
||||
window = window.with_drag_and_drop(false);
|
||||
}
|
||||
|
||||
window = window.with_title("doukutsu-rs");
|
||||
|
||||
let windowed_context = windowed_context.build_windowed(window, event_loop).unwrap();
|
||||
|
||||
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if let Some(nwin) = ndk_glue::native_window().as_ref() {
|
||||
unsafe {
|
||||
windowed_context.surface_created(nwin.ptr().as_ptr() as *mut std::ffi::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
refs.replace(windowed_context);
|
||||
}
|
||||
|
||||
refs.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
fn request_android_redraw() {
|
||||
match ndk_glue::native_window().as_ref() {
|
||||
Some(native_window) => {
|
||||
let a_native_window: *mut ndk_sys::ANativeWindow = native_window.ptr().as_ptr();
|
||||
let a_native_activity: *mut ndk_sys::ANativeActivity = ndk_glue::native_activity().ptr().as_ptr();
|
||||
unsafe {
|
||||
match (*(*a_native_activity).callbacks).onNativeWindowRedrawNeeded {
|
||||
Some(callback) => callback(a_native_activity, a_native_window),
|
||||
None => (),
|
||||
};
|
||||
};
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
fn get_insets() -> GameResult<(f32, f32, f32, f32)> {
|
||||
unsafe {
|
||||
let vm_ptr = ndk_glue::native_activity().vm();
|
||||
let vm = unsafe { jni::JavaVM::from_raw(vm_ptr) }?;
|
||||
let vm_env = vm.attach_current_thread()?;
|
||||
|
||||
//let class = vm_env.find_class("io/github/doukutsu_rs/MainActivity")?;
|
||||
let class = vm_env.new_global_ref(ndk_glue::native_activity().activity())?;
|
||||
let field = vm_env.get_field(class.as_obj(), "displayInsets", "[I")?.to_jni().l as jni::sys::jintArray;
|
||||
|
||||
let mut elements = [0; 4];
|
||||
vm_env.get_int_array_region(field, 0, &mut elements)?;
|
||||
|
||||
Ok((elements[0] as f32, elements[1] as f32, elements[2] as f32, elements[3] as f32))
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendEventLoop for GlutinEventLoop {
|
||||
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
|
||||
let event_loop = EventLoop::new();
|
||||
let state_ref = unsafe { &mut *game.state.get() };
|
||||
let window: &'static mut WindowedContext<PossiblyCurrent> =
|
||||
unsafe { std::mem::transmute(self.get_context(&event_loop)) };
|
||||
|
||||
{
|
||||
let size = window.window().inner_size();
|
||||
ctx.screen_size = (size.width.max(1) as f32, size.height.max(1) as f32);
|
||||
state_ref.handle_resize(ctx).unwrap();
|
||||
}
|
||||
|
||||
// it won't ever return
|
||||
let (game, ctx): (&'static mut Game, &'static mut Context) =
|
||||
unsafe { (std::mem::transmute(game), std::mem::transmute(ctx)) };
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, window_id }
|
||||
if window_id == window.window().id() =>
|
||||
{
|
||||
state_ref.shutdown();
|
||||
}
|
||||
Event::Resumed => {
|
||||
println!("resumed!");
|
||||
{
|
||||
let mut mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
*mutex = false;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if let Some(nwin) = ndk_glue::native_window().as_ref() {
|
||||
state_ref.graphics_reset();
|
||||
unsafe {
|
||||
window.surface_created(nwin.ptr().as_ptr() as *mut std::ffi::c_void);
|
||||
request_android_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Suspended => {
|
||||
println!("suspended!");
|
||||
{
|
||||
let mut mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
*mutex = true;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
unsafe {
|
||||
window.surface_destroyed();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event: WindowEvent::Resized(size), window_id }
|
||||
if window_id == window.window().id() =>
|
||||
{
|
||||
if let Some(renderer) = ctx.renderer.as_ref() {
|
||||
if let Ok(imgui) = renderer.imgui() {
|
||||
imgui.io_mut().display_size = [size.width as f32, size.height as f32];
|
||||
}
|
||||
|
||||
ctx.screen_size = (size.width as f32, size.height as f32);
|
||||
state_ref.handle_resize(ctx).unwrap();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event: WindowEvent::Touch(touch), window_id }
|
||||
if window_id == window.window().id() =>
|
||||
{
|
||||
let mut controls = &mut state_ref.touch_controls;
|
||||
let scale = state_ref.scale as f64;
|
||||
|
||||
match touch.phase {
|
||||
TouchPhase::Started | TouchPhase::Moved => {
|
||||
if let Some(point) = controls.points.iter_mut().find(|p| p.id == touch.id) {
|
||||
point.last_position = point.position;
|
||||
point.position = (touch.location.x / scale, touch.location.y / scale);
|
||||
} else {
|
||||
controls.touch_id_counter = controls.touch_id_counter.wrapping_add(1);
|
||||
|
||||
let point = TouchPoint {
|
||||
id: touch.id,
|
||||
touch_id: controls.touch_id_counter,
|
||||
position: (touch.location.x / scale, touch.location.y / scale),
|
||||
last_position: (0.0, 0.0),
|
||||
};
|
||||
controls.points.push(point);
|
||||
|
||||
if touch.phase == TouchPhase::Started {
|
||||
controls.clicks.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
TouchPhase::Ended | TouchPhase::Cancelled => {
|
||||
controls.points.retain(|p| p.id != touch.id);
|
||||
controls.clicks.retain(|p| p.id != touch.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput { input, .. }, window_id }
|
||||
if window_id == window.window().id() =>
|
||||
{
|
||||
if let Some(keycode) = input.virtual_keycode {
|
||||
if let Some(drs_scan) = conv_keycode(keycode) {
|
||||
let key_state = match input.state {
|
||||
ElementState::Pressed => true,
|
||||
ElementState::Released => false,
|
||||
};
|
||||
|
||||
ctx.keyboard_context.set_key(drs_scan, key_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(id) if id == window.window().id() => {
|
||||
{
|
||||
let mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
if *mutex {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
{
|
||||
if let Err(err) = game.draw(ctx) {
|
||||
log::error!("Failed to draw frame: {}", err);
|
||||
}
|
||||
|
||||
window.window().request_redraw();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
request_android_redraw();
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
if state_ref.shutdown {
|
||||
log::info!("Shutting down...");
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
if *mutex {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
game.update(ctx).unwrap();
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
match get_insets() {
|
||||
Ok(insets) => {
|
||||
ctx.screen_insets = insets;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to update insets: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = game.draw(ctx) {
|
||||
log::error!("Failed to draw frame: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
if state_ref.next_scene.is_some() {
|
||||
mem::swap(&mut game.scene, &mut state_ref.next_scene);
|
||||
state_ref.next_scene = None;
|
||||
game.scene.as_mut().unwrap().init(state_ref, ctx).unwrap();
|
||||
game.loops = 0;
|
||||
state_ref.frame_time = 0.0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
let mut imgui = imgui::Context::create();
|
||||
imgui.io_mut().display_size = [640.0, 480.0];
|
||||
|
||||
let refs = self.refs.clone();
|
||||
let user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
|
||||
unsafe fn get_proc_address(user_data: &mut *mut c_void, name: &str) -> *const c_void {
|
||||
let refs = Rc::from_raw(*user_data as *mut UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>);
|
||||
|
||||
let result = {
|
||||
let refs = &mut *refs.get();
|
||||
|
||||
if let Some(refs) = refs {
|
||||
refs.get_proc_address(name)
|
||||
} else {
|
||||
std::ptr::null()
|
||||
}
|
||||
};
|
||||
|
||||
*user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
unsafe fn swap_buffers(user_data: &mut *mut c_void) {
|
||||
let refs = Rc::from_raw(*user_data as *mut UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>);
|
||||
|
||||
{
|
||||
let refs = &mut *refs.get();
|
||||
|
||||
if let Some(refs) = refs {
|
||||
refs.swap_buffers();
|
||||
}
|
||||
}
|
||||
|
||||
*user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
}
|
||||
|
||||
let gl_context = GLContext { get_proc_address, swap_buffers, user_data };
|
||||
|
||||
Ok(Box::new(OpenGLRenderer::new(gl_context, UnsafeCell::new(imgui))))
|
||||
}
|
||||
}
|
||||
|
||||
fn conv_keycode(code: VirtualKeyCode) -> Option<ScanCode> {
|
||||
match code {
|
||||
VirtualKeyCode::Key1 => Some(ScanCode::Key1),
|
||||
VirtualKeyCode::Key2 => Some(ScanCode::Key2),
|
||||
VirtualKeyCode::Key3 => Some(ScanCode::Key3),
|
||||
VirtualKeyCode::Key4 => Some(ScanCode::Key4),
|
||||
VirtualKeyCode::Key5 => Some(ScanCode::Key5),
|
||||
VirtualKeyCode::Key6 => Some(ScanCode::Key6),
|
||||
VirtualKeyCode::Key7 => Some(ScanCode::Key7),
|
||||
VirtualKeyCode::Key8 => Some(ScanCode::Key8),
|
||||
VirtualKeyCode::Key9 => Some(ScanCode::Key9),
|
||||
VirtualKeyCode::Key0 => Some(ScanCode::Key0),
|
||||
VirtualKeyCode::A => Some(ScanCode::A),
|
||||
VirtualKeyCode::B => Some(ScanCode::B),
|
||||
VirtualKeyCode::C => Some(ScanCode::C),
|
||||
VirtualKeyCode::D => Some(ScanCode::D),
|
||||
VirtualKeyCode::E => Some(ScanCode::E),
|
||||
VirtualKeyCode::F => Some(ScanCode::F),
|
||||
VirtualKeyCode::G => Some(ScanCode::G),
|
||||
VirtualKeyCode::H => Some(ScanCode::H),
|
||||
VirtualKeyCode::I => Some(ScanCode::I),
|
||||
VirtualKeyCode::J => Some(ScanCode::J),
|
||||
VirtualKeyCode::K => Some(ScanCode::K),
|
||||
VirtualKeyCode::L => Some(ScanCode::L),
|
||||
VirtualKeyCode::M => Some(ScanCode::M),
|
||||
VirtualKeyCode::N => Some(ScanCode::N),
|
||||
VirtualKeyCode::O => Some(ScanCode::O),
|
||||
VirtualKeyCode::P => Some(ScanCode::P),
|
||||
VirtualKeyCode::Q => Some(ScanCode::Q),
|
||||
VirtualKeyCode::R => Some(ScanCode::R),
|
||||
VirtualKeyCode::S => Some(ScanCode::S),
|
||||
VirtualKeyCode::T => Some(ScanCode::T),
|
||||
VirtualKeyCode::U => Some(ScanCode::U),
|
||||
VirtualKeyCode::V => Some(ScanCode::V),
|
||||
VirtualKeyCode::W => Some(ScanCode::W),
|
||||
VirtualKeyCode::X => Some(ScanCode::X),
|
||||
VirtualKeyCode::Y => Some(ScanCode::Y),
|
||||
VirtualKeyCode::Z => Some(ScanCode::Z),
|
||||
VirtualKeyCode::Escape => Some(ScanCode::Escape),
|
||||
VirtualKeyCode::F1 => Some(ScanCode::F1),
|
||||
VirtualKeyCode::F2 => Some(ScanCode::F2),
|
||||
VirtualKeyCode::F3 => Some(ScanCode::F3),
|
||||
VirtualKeyCode::F4 => Some(ScanCode::F4),
|
||||
VirtualKeyCode::F5 => Some(ScanCode::F5),
|
||||
VirtualKeyCode::F6 => Some(ScanCode::F6),
|
||||
VirtualKeyCode::F7 => Some(ScanCode::F7),
|
||||
VirtualKeyCode::F8 => Some(ScanCode::F8),
|
||||
VirtualKeyCode::F9 => Some(ScanCode::F9),
|
||||
VirtualKeyCode::F10 => Some(ScanCode::F10),
|
||||
VirtualKeyCode::F11 => Some(ScanCode::F11),
|
||||
VirtualKeyCode::F12 => Some(ScanCode::F12),
|
||||
VirtualKeyCode::F13 => Some(ScanCode::F13),
|
||||
VirtualKeyCode::F14 => Some(ScanCode::F14),
|
||||
VirtualKeyCode::F15 => Some(ScanCode::F15),
|
||||
VirtualKeyCode::F16 => Some(ScanCode::F16),
|
||||
VirtualKeyCode::F17 => Some(ScanCode::F17),
|
||||
VirtualKeyCode::F18 => Some(ScanCode::F18),
|
||||
VirtualKeyCode::F19 => Some(ScanCode::F19),
|
||||
VirtualKeyCode::F20 => Some(ScanCode::F20),
|
||||
VirtualKeyCode::F21 => Some(ScanCode::F21),
|
||||
VirtualKeyCode::F22 => Some(ScanCode::F22),
|
||||
VirtualKeyCode::F23 => Some(ScanCode::F23),
|
||||
VirtualKeyCode::F24 => Some(ScanCode::F24),
|
||||
VirtualKeyCode::Snapshot => Some(ScanCode::Snapshot),
|
||||
VirtualKeyCode::Scroll => Some(ScanCode::Scroll),
|
||||
VirtualKeyCode::Pause => Some(ScanCode::Pause),
|
||||
VirtualKeyCode::Insert => Some(ScanCode::Insert),
|
||||
VirtualKeyCode::Home => Some(ScanCode::Home),
|
||||
VirtualKeyCode::Delete => Some(ScanCode::Delete),
|
||||
VirtualKeyCode::End => Some(ScanCode::End),
|
||||
VirtualKeyCode::PageDown => Some(ScanCode::PageDown),
|
||||
VirtualKeyCode::PageUp => Some(ScanCode::PageUp),
|
||||
VirtualKeyCode::Left => Some(ScanCode::Left),
|
||||
VirtualKeyCode::Up => Some(ScanCode::Up),
|
||||
VirtualKeyCode::Right => Some(ScanCode::Right),
|
||||
VirtualKeyCode::Down => Some(ScanCode::Down),
|
||||
VirtualKeyCode::Back => Some(ScanCode::Back),
|
||||
VirtualKeyCode::Return => Some(ScanCode::Return),
|
||||
VirtualKeyCode::Space => Some(ScanCode::Space),
|
||||
VirtualKeyCode::Compose => Some(ScanCode::Compose),
|
||||
VirtualKeyCode::Caret => Some(ScanCode::Caret),
|
||||
VirtualKeyCode::Numlock => Some(ScanCode::Numlock),
|
||||
VirtualKeyCode::Numpad0 => Some(ScanCode::Numpad0),
|
||||
VirtualKeyCode::Numpad1 => Some(ScanCode::Numpad1),
|
||||
VirtualKeyCode::Numpad2 => Some(ScanCode::Numpad2),
|
||||
VirtualKeyCode::Numpad3 => Some(ScanCode::Numpad3),
|
||||
VirtualKeyCode::Numpad4 => Some(ScanCode::Numpad4),
|
||||
VirtualKeyCode::Numpad5 => Some(ScanCode::Numpad5),
|
||||
VirtualKeyCode::Numpad6 => Some(ScanCode::Numpad6),
|
||||
VirtualKeyCode::Numpad7 => Some(ScanCode::Numpad7),
|
||||
VirtualKeyCode::Numpad8 => Some(ScanCode::Numpad8),
|
||||
VirtualKeyCode::Numpad9 => Some(ScanCode::Numpad9),
|
||||
VirtualKeyCode::NumpadAdd => Some(ScanCode::NumpadAdd),
|
||||
VirtualKeyCode::NumpadDivide => Some(ScanCode::NumpadDivide),
|
||||
VirtualKeyCode::NumpadDecimal => Some(ScanCode::NumpadDecimal),
|
||||
VirtualKeyCode::NumpadComma => Some(ScanCode::NumpadComma),
|
||||
VirtualKeyCode::NumpadEnter => Some(ScanCode::NumpadEnter),
|
||||
VirtualKeyCode::NumpadEquals => Some(ScanCode::NumpadEquals),
|
||||
VirtualKeyCode::NumpadMultiply => Some(ScanCode::NumpadMultiply),
|
||||
VirtualKeyCode::NumpadSubtract => Some(ScanCode::NumpadSubtract),
|
||||
VirtualKeyCode::AbntC1 => Some(ScanCode::AbntC1),
|
||||
VirtualKeyCode::AbntC2 => Some(ScanCode::AbntC2),
|
||||
VirtualKeyCode::Apostrophe => Some(ScanCode::Apostrophe),
|
||||
VirtualKeyCode::Apps => Some(ScanCode::Apps),
|
||||
VirtualKeyCode::Asterisk => Some(ScanCode::Asterisk),
|
||||
VirtualKeyCode::At => Some(ScanCode::At),
|
||||
VirtualKeyCode::Ax => Some(ScanCode::Ax),
|
||||
VirtualKeyCode::Backslash => Some(ScanCode::Backslash),
|
||||
VirtualKeyCode::Calculator => Some(ScanCode::Calculator),
|
||||
VirtualKeyCode::Capital => Some(ScanCode::Capital),
|
||||
VirtualKeyCode::Colon => Some(ScanCode::Colon),
|
||||
VirtualKeyCode::Comma => Some(ScanCode::Comma),
|
||||
VirtualKeyCode::Convert => Some(ScanCode::Convert),
|
||||
VirtualKeyCode::Equals => Some(ScanCode::Equals),
|
||||
VirtualKeyCode::Grave => Some(ScanCode::Grave),
|
||||
VirtualKeyCode::Kana => Some(ScanCode::Kana),
|
||||
VirtualKeyCode::Kanji => Some(ScanCode::Kanji),
|
||||
VirtualKeyCode::LAlt => Some(ScanCode::LAlt),
|
||||
VirtualKeyCode::LBracket => Some(ScanCode::LBracket),
|
||||
VirtualKeyCode::LControl => Some(ScanCode::LControl),
|
||||
VirtualKeyCode::LShift => Some(ScanCode::LShift),
|
||||
VirtualKeyCode::LWin => Some(ScanCode::LWin),
|
||||
VirtualKeyCode::Mail => Some(ScanCode::Mail),
|
||||
VirtualKeyCode::MediaSelect => Some(ScanCode::MediaSelect),
|
||||
VirtualKeyCode::MediaStop => Some(ScanCode::MediaStop),
|
||||
VirtualKeyCode::Minus => Some(ScanCode::Minus),
|
||||
VirtualKeyCode::Mute => Some(ScanCode::Mute),
|
||||
VirtualKeyCode::MyComputer => Some(ScanCode::MyComputer),
|
||||
VirtualKeyCode::NavigateForward => Some(ScanCode::NavigateForward),
|
||||
VirtualKeyCode::NavigateBackward => Some(ScanCode::NavigateBackward),
|
||||
VirtualKeyCode::NextTrack => Some(ScanCode::NextTrack),
|
||||
VirtualKeyCode::NoConvert => Some(ScanCode::NoConvert),
|
||||
VirtualKeyCode::OEM102 => Some(ScanCode::OEM102),
|
||||
VirtualKeyCode::Period => Some(ScanCode::Period),
|
||||
VirtualKeyCode::PlayPause => Some(ScanCode::PlayPause),
|
||||
VirtualKeyCode::Plus => Some(ScanCode::Plus),
|
||||
VirtualKeyCode::Power => Some(ScanCode::Power),
|
||||
VirtualKeyCode::PrevTrack => Some(ScanCode::PrevTrack),
|
||||
VirtualKeyCode::RAlt => Some(ScanCode::RAlt),
|
||||
VirtualKeyCode::RBracket => Some(ScanCode::RBracket),
|
||||
VirtualKeyCode::RControl => Some(ScanCode::RControl),
|
||||
VirtualKeyCode::RShift => Some(ScanCode::RShift),
|
||||
VirtualKeyCode::RWin => Some(ScanCode::RWin),
|
||||
VirtualKeyCode::Semicolon => Some(ScanCode::Semicolon),
|
||||
VirtualKeyCode::Slash => Some(ScanCode::Slash),
|
||||
VirtualKeyCode::Sleep => Some(ScanCode::Sleep),
|
||||
VirtualKeyCode::Stop => Some(ScanCode::Stop),
|
||||
VirtualKeyCode::Sysrq => Some(ScanCode::Sysrq),
|
||||
VirtualKeyCode::Tab => Some(ScanCode::Tab),
|
||||
VirtualKeyCode::Underline => Some(ScanCode::Underline),
|
||||
VirtualKeyCode::Unlabeled => Some(ScanCode::Unlabeled),
|
||||
VirtualKeyCode::VolumeDown => Some(ScanCode::VolumeDown),
|
||||
VirtualKeyCode::VolumeUp => Some(ScanCode::VolumeUp),
|
||||
VirtualKeyCode::Wake => Some(ScanCode::Wake),
|
||||
VirtualKeyCode::WebBack => Some(ScanCode::WebBack),
|
||||
VirtualKeyCode::WebFavorites => Some(ScanCode::WebFavorites),
|
||||
VirtualKeyCode::WebForward => Some(ScanCode::WebForward),
|
||||
VirtualKeyCode::WebHome => Some(ScanCode::WebHome),
|
||||
VirtualKeyCode::WebRefresh => Some(ScanCode::WebRefresh),
|
||||
VirtualKeyCode::WebSearch => Some(ScanCode::WebSearch),
|
||||
VirtualKeyCode::WebStop => Some(ScanCode::WebStop),
|
||||
VirtualKeyCode::Yen => Some(ScanCode::Yen),
|
||||
VirtualKeyCode::Copy => Some(ScanCode::Copy),
|
||||
VirtualKeyCode::Paste => Some(ScanCode::Paste),
|
||||
VirtualKeyCode::Cut => Some(ScanCode::Cut),
|
||||
}
|
||||
}
|
|
@ -82,6 +82,10 @@ impl BackendTexture for NullTexture {
|
|||
pub struct NullRenderer(RefCell<imgui::Context>);
|
||||
|
||||
impl BackendRenderer for NullRenderer {
|
||||
fn renderer_name(&self) -> String {
|
||||
"Null".to_owned()
|
||||
}
|
||||
|
||||
fn clear(&mut self, _color: Color) {
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use core::mem;
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::rc::Rc;
|
||||
|
||||
use imgui::internal::RawWrapper;
|
||||
|
@ -11,14 +12,15 @@ use sdl2::mouse::{Cursor, SystemCursor};
|
|||
use sdl2::pixels::PixelFormatEnum;
|
||||
use sdl2::render::{Texture, TextureCreator, WindowCanvas};
|
||||
use sdl2::video::WindowContext;
|
||||
use sdl2::{keyboard, pixels, EventPump, Sdl};
|
||||
use sdl2::{keyboard, pixels, EventPump, Sdl, VideoSubsystem};
|
||||
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameError, GameResult};
|
||||
use crate::framework::graphics::{BlendMode};
|
||||
use crate::framework::graphics::BlendMode;
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::framework::render_opengl::{GLContext, OpenGLRenderer};
|
||||
use crate::framework::ui::init_imgui;
|
||||
use crate::Game;
|
||||
use crate::GAME_SUSPENDED;
|
||||
|
@ -46,29 +48,49 @@ impl Backend for SDL2Backend {
|
|||
struct SDL2EventLoop {
|
||||
event_pump: EventPump,
|
||||
refs: Rc<RefCell<SDL2Context>>,
|
||||
opengl_available: RefCell<bool>,
|
||||
}
|
||||
|
||||
struct SDL2Context {
|
||||
video: VideoSubsystem,
|
||||
canvas: WindowCanvas,
|
||||
texture_creator: TextureCreator<WindowContext>,
|
||||
gl_context: Option<sdl2::video::GLContext>,
|
||||
blend_mode: sdl2::render::BlendMode,
|
||||
}
|
||||
|
||||
impl SDL2EventLoop {
|
||||
pub fn new(sdl: &Sdl) -> GameResult<Box<dyn BackendEventLoop>> {
|
||||
sdl2::hint::set("SDL_HINT_RENDER_DRIVER", "opengles2");
|
||||
|
||||
let event_pump = sdl.event_pump().map_err(|e| GameError::WindowError(e))?;
|
||||
let video = sdl.video().map_err(|e| GameError::WindowError(e))?;
|
||||
let window =
|
||||
video.window("Cave Story (doukutsu-rs)", 640, 480).position_centered().resizable().build().map_err(|e| GameError::WindowError(e.to_string()))?;
|
||||
let window = video
|
||||
.window("Cave Story (doukutsu-rs)", 640, 480)
|
||||
.position_centered()
|
||||
.resizable()
|
||||
.build()
|
||||
.map_err(|e| GameError::WindowError(e.to_string()))?;
|
||||
|
||||
let canvas = window.into_canvas().accelerated().present_vsync().build().map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
let canvas = window
|
||||
.into_canvas()
|
||||
.accelerated()
|
||||
.present_vsync()
|
||||
.build()
|
||||
.map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
|
||||
let texture_creator = canvas.texture_creator();
|
||||
|
||||
let event_loop =
|
||||
SDL2EventLoop { event_pump, refs: Rc::new(RefCell::new(SDL2Context { canvas, texture_creator, blend_mode: sdl2::render::BlendMode::Blend })) };
|
||||
let opengl_available = if let Ok(v) = std::env::var("CAVESTORY_NO_OPENGL") { v != "1" } else { true };
|
||||
let event_loop = SDL2EventLoop {
|
||||
event_pump,
|
||||
refs: Rc::new(RefCell::new(SDL2Context {
|
||||
video,
|
||||
canvas,
|
||||
texture_creator,
|
||||
gl_context: None,
|
||||
blend_mode: sdl2::render::BlendMode::Blend,
|
||||
})),
|
||||
opengl_available: RefCell::new(opengl_available),
|
||||
};
|
||||
|
||||
Ok(Box::new(event_loop))
|
||||
}
|
||||
|
@ -169,12 +191,71 @@ impl BackendEventLoop for SDL2EventLoop {
|
|||
state.frame_time = 0.0;
|
||||
}
|
||||
|
||||
imgui_sdl2.prepare_frame(imgui.io_mut(), self.refs.borrow().canvas.window(), &self.event_pump.mouse_state());
|
||||
imgui_sdl2.prepare_frame(
|
||||
imgui.io_mut(),
|
||||
self.refs.borrow().canvas.window(),
|
||||
&self.event_pump.mouse_state(),
|
||||
);
|
||||
game.draw(ctx).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
#[cfg(feature = "render-opengl")]
|
||||
{
|
||||
let mut refs = self.refs.borrow_mut();
|
||||
match refs.canvas.window().gl_create_context() {
|
||||
Ok(gl_ctx) => {
|
||||
refs.canvas.window().gl_make_current(&gl_ctx);
|
||||
refs.gl_context = Some(gl_ctx);
|
||||
}
|
||||
Err(err) => {
|
||||
*self.opengl_available.borrow_mut() = false;
|
||||
log::error!("Failed to initialize OpenGL context, falling back to SDL2 renderer: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "render-opengl")]
|
||||
if *self.opengl_available.borrow() {
|
||||
let mut imgui = init_imgui()?;
|
||||
|
||||
let refs = self.refs.clone();
|
||||
|
||||
let user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
|
||||
unsafe fn get_proc_address(user_data: &mut *mut c_void, name: &str) -> *const c_void {
|
||||
let refs = Rc::from_raw(*user_data as *mut RefCell<SDL2Context>);
|
||||
|
||||
let result = {
|
||||
let refs = &mut *refs.as_ptr();
|
||||
refs.video.gl_get_proc_address(name) as *const _
|
||||
};
|
||||
|
||||
log::info!("gl proc {} -> {:?}", name, result);
|
||||
|
||||
*user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
unsafe fn swap_buffers(user_data: &mut *mut c_void) {
|
||||
let refs = Rc::from_raw(*user_data as *mut RefCell<SDL2Context>);
|
||||
|
||||
{
|
||||
let refs = &mut *refs.as_ptr();
|
||||
|
||||
refs.canvas.window().gl_swap_window();
|
||||
}
|
||||
|
||||
*user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
}
|
||||
|
||||
let gl_context = GLContext { get_proc_address, swap_buffers, user_data };
|
||||
|
||||
return Ok(Box::new(OpenGLRenderer::new(gl_context, UnsafeCell::new(imgui))));
|
||||
}
|
||||
|
||||
SDL2Renderer::new(self.refs.clone())
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +305,13 @@ impl SDL2Renderer {
|
|||
|
||||
imgui_textures.insert(
|
||||
id,
|
||||
SDL2Texture { refs: refs.clone(), texture: Some(texture), width: font_tex.width as u16, height: font_tex.height as u16, commands: vec![] },
|
||||
SDL2Texture {
|
||||
refs: refs.clone(),
|
||||
texture: Some(texture),
|
||||
width: font_tex.width as u16,
|
||||
height: font_tex.height as u16,
|
||||
commands: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -233,7 +320,12 @@ impl SDL2Renderer {
|
|||
ImguiSdl2::new(&mut imgui, refs.canvas.window())
|
||||
};
|
||||
|
||||
Ok(Box::new(SDL2Renderer { refs, imgui: Rc::new(RefCell::new(imgui)), imgui_event: Rc::new(RefCell::new(imgui_sdl2)), imgui_textures }))
|
||||
Ok(Box::new(SDL2Renderer {
|
||||
refs,
|
||||
imgui: Rc::new(RefCell::new(imgui)),
|
||||
imgui_event: Rc::new(RefCell::new(imgui_sdl2)),
|
||||
imgui_textures,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,7 +334,10 @@ fn to_sdl(color: Color) -> pixels::Color {
|
|||
pixels::Color::RGBA(r, g, b, a)
|
||||
}
|
||||
|
||||
unsafe fn set_raw_target(renderer: *mut sdl2::sys::SDL_Renderer, raw_texture: *mut sdl2::sys::SDL_Texture) -> GameResult {
|
||||
unsafe fn set_raw_target(
|
||||
renderer: *mut sdl2::sys::SDL_Renderer,
|
||||
raw_texture: *mut sdl2::sys::SDL_Texture,
|
||||
) -> GameResult {
|
||||
if sdl2::sys::SDL_SetRenderTarget(renderer, raw_texture) == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -271,6 +366,10 @@ fn max3(x: f32, y: f32, z: f32) -> f32 {
|
|||
}
|
||||
|
||||
impl BackendRenderer for SDL2Renderer {
|
||||
fn renderer_name(&self) -> String {
|
||||
"SDL2_Renderer".to_owned()
|
||||
}
|
||||
|
||||
fn clear(&mut self, color: Color) {
|
||||
let mut refs = self.refs.borrow_mut();
|
||||
|
||||
|
@ -366,7 +465,12 @@ impl BackendRenderer for SDL2Renderer {
|
|||
|
||||
refs.canvas.set_draw_color(pixels::Color::RGBA(r, g, b, a));
|
||||
refs.canvas
|
||||
.fill_rect(sdl2::rect::Rect::new(rect.left as i32, rect.top as i32, rect.width() as u32, rect.height() as u32))
|
||||
.fill_rect(sdl2::rect::Rect::new(
|
||||
rect.left as i32,
|
||||
rect.top as i32,
|
||||
rect.width() as u32,
|
||||
rect.height() as u32,
|
||||
))
|
||||
.map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
|
@ -383,15 +487,30 @@ impl BackendRenderer for SDL2Renderer {
|
|||
0 => {} // no-op
|
||||
1 => {
|
||||
refs.canvas
|
||||
.draw_rect(sdl2::rect::Rect::new(rect.left as i32, rect.top as i32, rect.width() as u32, rect.height() as u32))
|
||||
.draw_rect(sdl2::rect::Rect::new(
|
||||
rect.left as i32,
|
||||
rect.top as i32,
|
||||
rect.width() as u32,
|
||||
rect.height() as u32,
|
||||
))
|
||||
.map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
}
|
||||
_ => {
|
||||
let rects = [
|
||||
sdl2::rect::Rect::new(rect.left as i32, rect.top as i32, rect.width() as u32, line_width as u32),
|
||||
sdl2::rect::Rect::new(rect.left as i32, rect.bottom as i32 - line_width as i32, rect.width() as u32, line_width as u32),
|
||||
sdl2::rect::Rect::new(
|
||||
rect.left as i32,
|
||||
rect.bottom as i32 - line_width as i32,
|
||||
rect.width() as u32,
|
||||
line_width as u32,
|
||||
),
|
||||
sdl2::rect::Rect::new(rect.left as i32, rect.top as i32, line_width as u32, rect.height() as u32),
|
||||
sdl2::rect::Rect::new(rect.right as i32 - line_width as i32, rect.top as i32, line_width as u32, rect.height() as u32),
|
||||
sdl2::rect::Rect::new(
|
||||
rect.right as i32 - line_width as i32,
|
||||
rect.top as i32,
|
||||
line_width as u32,
|
||||
rect.height() as u32,
|
||||
),
|
||||
];
|
||||
|
||||
refs.canvas.fill_rects(&rects).map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
|
@ -433,9 +552,12 @@ impl BackendRenderer for SDL2Renderer {
|
|||
continue;
|
||||
}
|
||||
|
||||
let v1 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i] as usize];
|
||||
let v2 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 1] as usize];
|
||||
let v3 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 2] as usize];
|
||||
let v1 = draw_list.vtx_buffer()
|
||||
[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i] as usize];
|
||||
let v2 = draw_list.vtx_buffer()
|
||||
[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 1] as usize];
|
||||
let v3 = draw_list.vtx_buffer()
|
||||
[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 2] as usize];
|
||||
|
||||
vert_x[0] = (v1.pos[0] - 0.5) as i16;
|
||||
vert_y[0] = (v1.pos[1] - 0.5) as i16;
|
||||
|
@ -446,9 +568,12 @@ impl BackendRenderer for SDL2Renderer {
|
|||
|
||||
#[allow(clippy::float_cmp)]
|
||||
if i < count - 3 {
|
||||
let v4 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 3] as usize];
|
||||
let v5 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 4] as usize];
|
||||
let v6 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 5] as usize];
|
||||
let v4 = draw_list.vtx_buffer()
|
||||
[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 3] as usize];
|
||||
let v5 = draw_list.vtx_buffer()
|
||||
[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 4] as usize];
|
||||
let v6 = draw_list.vtx_buffer()
|
||||
[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 5] as usize];
|
||||
|
||||
min[0] = min3(v1.pos[0], v2.pos[0], v3.pos[0]);
|
||||
min[1] = min3(v1.pos[1], v2.pos[1], v3.pos[1]);
|
||||
|
@ -484,13 +609,20 @@ impl BackendRenderer for SDL2Renderer {
|
|||
((tex_pos[2] - tex_pos[0]) * surf.width as f32) as u32,
|
||||
((tex_pos[3] - tex_pos[1]) * surf.height as f32) as u32,
|
||||
);
|
||||
let dest = sdl2::rect::Rect::new(min[0] as i32, min[1] as i32, (max[0] - min[0]) as u32, (max[1] - min[1]) as u32);
|
||||
let dest = sdl2::rect::Rect::new(
|
||||
min[0] as i32,
|
||||
min[1] as i32,
|
||||
(max[0] - min[0]) as u32,
|
||||
(max[1] - min[1]) as u32,
|
||||
);
|
||||
|
||||
let tex = surf.texture.as_mut().unwrap();
|
||||
tex.set_color_mod(v1.col[0], v1.col[1], v1.col[2]);
|
||||
tex.set_alpha_mod(v1.col[3]);
|
||||
|
||||
refs.canvas.copy(tex, src, dest).map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
refs.canvas
|
||||
.copy(tex, src, dest)
|
||||
.map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
} else {
|
||||
/*sdl2::sys::gfx::primitives::filledPolygonRGBA(
|
||||
refs.canvas.raw(),
|
||||
|
@ -842,7 +974,9 @@ impl ImguiSdl2 {
|
|||
|
||||
pub fn ignore_event(&self, event: &Event) -> bool {
|
||||
match *event {
|
||||
Event::KeyDown { .. } | Event::KeyUp { .. } | Event::TextEditing { .. } | Event::TextInput { .. } => self.ignore_keyboard,
|
||||
Event::KeyDown { .. } | Event::KeyUp { .. } | Event::TextEditing { .. } | Event::TextInput { .. } => {
|
||||
self.ignore_keyboard
|
||||
}
|
||||
Event::MouseMotion { .. }
|
||||
| Event::MouseButtonDown { .. }
|
||||
| Event::MouseButtonUp { .. }
|
||||
|
@ -910,7 +1044,12 @@ impl ImguiSdl2 {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn prepare_frame(&mut self, io: &mut imgui::Io, window: &sdl2::video::Window, mouse_state: &sdl2::mouse::MouseState) {
|
||||
pub fn prepare_frame(
|
||||
&mut self,
|
||||
io: &mut imgui::Io,
|
||||
window: &sdl2::video::Window,
|
||||
mouse_state: &sdl2::mouse::MouseState,
|
||||
) {
|
||||
let mouse_util = window.subsystem().sdl().mouse();
|
||||
|
||||
let (win_w, win_h) = window.size();
|
||||
|
|
|
@ -1,358 +0,0 @@
|
|||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use std::ops::DerefMut;
|
||||
use std::time::Duration;
|
||||
|
||||
use imgui::{DrawData, Ui};
|
||||
use ndk::input_queue::InputQueue;
|
||||
use sokol::app::{SApp, SAppDesc, SAppEvent, SAppEventType, SAppKeycode};
|
||||
use sokol::gfx::{sg_isvalid, sg_query_backend, sg_shutdown};
|
||||
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics::BlendMode;
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::Game;
|
||||
|
||||
pub struct SokolBackend;
|
||||
|
||||
impl SokolBackend {
|
||||
pub fn new() -> GameResult<Box<dyn Backend>> {
|
||||
Ok(Box::new(SokolBackend))
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for SokolBackend {
|
||||
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> {
|
||||
Ok(Box::new(SokolEventLoop))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SokolEventLoop;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
extern "C" {
|
||||
fn sapp_android_on_create(
|
||||
activity: *mut ndk_sys::ANativeActivity,
|
||||
window: *mut ndk_sys::ANativeWindow,
|
||||
input_queue: *mut ndk_sys::AInputQueue,
|
||||
);
|
||||
}
|
||||
|
||||
impl BackendEventLoop for SokolEventLoop {
|
||||
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
|
||||
#[cfg(target_os = "android")]
|
||||
unsafe {
|
||||
let activity = ndk_glue::native_activity().ptr().as_ptr();
|
||||
let window = match ndk_glue::native_window().as_ref() {
|
||||
None => std::ptr::null_mut(),
|
||||
Some(p) => p.ptr().as_ptr(),
|
||||
};
|
||||
let input_queue = match ndk_glue::input_queue().as_ref() {
|
||||
None => std::ptr::null_mut(),
|
||||
Some(p) => p.ptr().as_ptr(),
|
||||
};
|
||||
|
||||
println!("activity = {:?} window = {:?} input_queue = {:?}", activity, window, input_queue);
|
||||
|
||||
sapp_android_on_create(activity, window, input_queue);
|
||||
}
|
||||
|
||||
struct Callbacks<'a, 'b> {
|
||||
ctx: &'a mut Context,
|
||||
game: &'b mut Game,
|
||||
};
|
||||
|
||||
impl<'a, 'b> SApp for Callbacks<'a, 'b> {
|
||||
fn sapp_init(&mut self) {
|
||||
let state_ref = unsafe { &mut *self.game.state.get() };
|
||||
|
||||
self.ctx.screen_size = (640.0, 480.0);
|
||||
state_ref.handle_resize(self.ctx).unwrap();
|
||||
}
|
||||
|
||||
fn sapp_frame(&mut self) {
|
||||
let state_ref = unsafe { &mut *self.game.state.get() };
|
||||
|
||||
self.game.update(self.ctx).unwrap();
|
||||
|
||||
// todo: not really supported on iOS/consoles
|
||||
if state_ref.shutdown {
|
||||
log::info!("Shutting down...");
|
||||
std::process::exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if state_ref.next_scene.is_some() {
|
||||
mem::swap(&mut self.game.scene, &mut state_ref.next_scene);
|
||||
state_ref.next_scene = None;
|
||||
self.game.scene.as_mut().unwrap().init(state_ref, self.ctx).unwrap();
|
||||
self.game.loops = 0;
|
||||
state_ref.frame_time = 0.0;
|
||||
}
|
||||
|
||||
self.game.draw(self.ctx).unwrap();
|
||||
}
|
||||
|
||||
fn sapp_cleanup(&mut self) {
|
||||
if sg_isvalid() {
|
||||
sg_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
fn sapp_event(&mut self, event: SAppEvent) {
|
||||
let state_ref = unsafe { &mut *self.game.state.get() };
|
||||
println!("event: {:?}", event.event_type);
|
||||
|
||||
match event.event_type {
|
||||
SAppEventType::Invalid => {}
|
||||
SAppEventType::KeyDown => {
|
||||
if let Some(drs_scan) = conv_scancode(event.key_code) {
|
||||
state_ref.process_debug_keys(drs_scan);
|
||||
self.ctx.keyboard_context.set_key(drs_scan, true);
|
||||
}
|
||||
}
|
||||
SAppEventType::KeyUp => {
|
||||
if let Some(drs_scan) = conv_scancode(event.key_code) {
|
||||
self.ctx.keyboard_context.set_key(drs_scan, false);
|
||||
}
|
||||
}
|
||||
SAppEventType::Char => {}
|
||||
SAppEventType::MouseDown => {}
|
||||
SAppEventType::MouseUp => {}
|
||||
SAppEventType::MouseScroll => {}
|
||||
SAppEventType::MouseMove => {}
|
||||
SAppEventType::MouseEnter => {}
|
||||
SAppEventType::MouseLeave => {}
|
||||
SAppEventType::TouchesBegan => {}
|
||||
SAppEventType::TouchesMoved => {}
|
||||
SAppEventType::TouchesEnded => {}
|
||||
SAppEventType::TouchesCancelled => {}
|
||||
SAppEventType::Resized => {}
|
||||
SAppEventType::Iconified => {}
|
||||
SAppEventType::Restored => {}
|
||||
SAppEventType::Suspended => {}
|
||||
SAppEventType::Resumed => {}
|
||||
SAppEventType::UpdateCursor => {}
|
||||
SAppEventType::QuitRequested => {
|
||||
state_ref.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sokol::app::sapp_run(
|
||||
Callbacks { ctx: unsafe { std::mem::transmute(ctx) }, game: unsafe { std::mem::transmute(game) } },
|
||||
SAppDesc {
|
||||
width: 640,
|
||||
height: 480,
|
||||
window_title: "doukutsu-rs".to_string(),
|
||||
ios_keyboard_resizes_canvas: false,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_millis(10))
|
||||
}
|
||||
}
|
||||
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
let mut imgui = imgui::Context::create();
|
||||
imgui.io_mut().display_size = [640.0, 480.0];
|
||||
imgui.fonts().build_alpha8_texture();
|
||||
|
||||
log::info!("Using Sokol backend: {:?}", sg_query_backend());
|
||||
|
||||
Ok(Box::new(SokolRenderer(RefCell::new(imgui))))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NullTexture(u16, u16);
|
||||
|
||||
impl BackendTexture for NullTexture {
|
||||
fn dimensions(&self) -> (u16, u16) {
|
||||
(self.0, self.1)
|
||||
}
|
||||
|
||||
fn add(&mut self, command: SpriteBatchCommand) {}
|
||||
|
||||
fn clear(&mut self) {}
|
||||
|
||||
fn draw(&mut self) -> GameResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SokolRenderer(RefCell<imgui::Context>);
|
||||
|
||||
impl BackendRenderer for SokolRenderer {
|
||||
fn clear(&mut self, color: Color) {}
|
||||
|
||||
fn present(&mut self) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult<Box<dyn BackendTexture>> {
|
||||
Ok(Box::new(NullTexture(width, height)))
|
||||
}
|
||||
|
||||
fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult<Box<dyn BackendTexture>> {
|
||||
Ok(Box::new(NullTexture(width, height)))
|
||||
}
|
||||
|
||||
fn set_blend_mode(&mut self, blend: BlendMode) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_render_target(&mut self, texture: Option<&Box<dyn BackendTexture>>) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_rect(&mut self, rect: Rect<isize>, color: Color) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_outline_rect(&mut self, rect: Rect<isize>, line_width: usize, color: Color) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn imgui(&self) -> GameResult<&mut imgui::Context> {
|
||||
unsafe { Ok(&mut *self.0.as_ptr()) }
|
||||
}
|
||||
|
||||
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_frame<'ui>(&self, ui: &Ui<'ui>) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn conv_scancode(code: SAppKeycode) -> Option<ScanCode> {
|
||||
match code {
|
||||
SAppKeycode::KeySpace => Some(ScanCode::Space),
|
||||
SAppKeycode::KeyApostrophe => Some(ScanCode::Apostrophe),
|
||||
SAppKeycode::KeyComma => Some(ScanCode::Comma),
|
||||
SAppKeycode::KeyMinus => Some(ScanCode::Minus),
|
||||
SAppKeycode::KeyPeriod => Some(ScanCode::Period),
|
||||
SAppKeycode::KeySlash => Some(ScanCode::Slash),
|
||||
SAppKeycode::Key0 => Some(ScanCode::Key0),
|
||||
SAppKeycode::Key1 => Some(ScanCode::Key1),
|
||||
SAppKeycode::Key2 => Some(ScanCode::Key2),
|
||||
SAppKeycode::Key3 => Some(ScanCode::Key3),
|
||||
SAppKeycode::Key4 => Some(ScanCode::Key4),
|
||||
SAppKeycode::Key5 => Some(ScanCode::Key5),
|
||||
SAppKeycode::Key6 => Some(ScanCode::Key6),
|
||||
SAppKeycode::Key7 => Some(ScanCode::Key7),
|
||||
SAppKeycode::Key8 => Some(ScanCode::Key8),
|
||||
SAppKeycode::Key9 => Some(ScanCode::Key9),
|
||||
SAppKeycode::KeySemicolon => Some(ScanCode::Semicolon),
|
||||
SAppKeycode::KeyEqual => Some(ScanCode::Equals),
|
||||
SAppKeycode::KeyA => Some(ScanCode::A),
|
||||
SAppKeycode::KeyB => Some(ScanCode::B),
|
||||
SAppKeycode::KeyC => Some(ScanCode::C),
|
||||
SAppKeycode::KeyD => Some(ScanCode::D),
|
||||
SAppKeycode::KeyE => Some(ScanCode::E),
|
||||
SAppKeycode::KeyF => Some(ScanCode::F),
|
||||
SAppKeycode::KeyG => Some(ScanCode::G),
|
||||
SAppKeycode::KeyH => Some(ScanCode::H),
|
||||
SAppKeycode::KeyI => Some(ScanCode::I),
|
||||
SAppKeycode::KeyJ => Some(ScanCode::J),
|
||||
SAppKeycode::KeyK => Some(ScanCode::K),
|
||||
SAppKeycode::KeyL => Some(ScanCode::L),
|
||||
SAppKeycode::KeyM => Some(ScanCode::M),
|
||||
SAppKeycode::KeyN => Some(ScanCode::N),
|
||||
SAppKeycode::KeyO => Some(ScanCode::O),
|
||||
SAppKeycode::KeyP => Some(ScanCode::P),
|
||||
SAppKeycode::KeyQ => Some(ScanCode::Q),
|
||||
SAppKeycode::KeyR => Some(ScanCode::R),
|
||||
SAppKeycode::KeyS => Some(ScanCode::S),
|
||||
SAppKeycode::KeyT => Some(ScanCode::T),
|
||||
SAppKeycode::KeyU => Some(ScanCode::U),
|
||||
SAppKeycode::KeyV => Some(ScanCode::V),
|
||||
SAppKeycode::KeyW => Some(ScanCode::W),
|
||||
SAppKeycode::KeyX => Some(ScanCode::X),
|
||||
SAppKeycode::KeyY => Some(ScanCode::Y),
|
||||
SAppKeycode::KeyZ => Some(ScanCode::Z),
|
||||
SAppKeycode::KeyLeftBracket => Some(ScanCode::LBracket),
|
||||
SAppKeycode::KeyBackslash => Some(ScanCode::Backslash),
|
||||
SAppKeycode::KeyRightBracket => Some(ScanCode::RBracket),
|
||||
SAppKeycode::KeyGraveAccent => Some(ScanCode::Grave),
|
||||
SAppKeycode::KeyWorld1 => Some(ScanCode::AbntC1),
|
||||
SAppKeycode::KeyWorld2 => Some(ScanCode::AbntC2),
|
||||
SAppKeycode::KeyEscape => Some(ScanCode::Escape),
|
||||
SAppKeycode::KeyEnter => Some(ScanCode::Return),
|
||||
SAppKeycode::KeyTab => Some(ScanCode::Tab),
|
||||
SAppKeycode::KeyBackspace => Some(ScanCode::Backspace),
|
||||
SAppKeycode::KeyInsert => Some(ScanCode::Insert),
|
||||
SAppKeycode::KeyDelete => Some(ScanCode::Delete),
|
||||
SAppKeycode::KeyRight => Some(ScanCode::Right),
|
||||
SAppKeycode::KeyLeft => Some(ScanCode::Left),
|
||||
SAppKeycode::KeyDown => Some(ScanCode::Down),
|
||||
SAppKeycode::KeyUp => Some(ScanCode::Up),
|
||||
SAppKeycode::KeyPageUp => Some(ScanCode::PageUp),
|
||||
SAppKeycode::KeyPageDown => Some(ScanCode::PageDown),
|
||||
SAppKeycode::KeyHome => Some(ScanCode::Home),
|
||||
SAppKeycode::KeyEnd => Some(ScanCode::End),
|
||||
SAppKeycode::KeyCapsLock => Some(ScanCode::Capslock),
|
||||
SAppKeycode::KeyScrollLock => Some(ScanCode::Scrolllock),
|
||||
SAppKeycode::KeyNumLock => Some(ScanCode::Numlock),
|
||||
SAppKeycode::KeyPrintScreen => Some(ScanCode::Sysrq),
|
||||
SAppKeycode::KeyPause => Some(ScanCode::Pause),
|
||||
SAppKeycode::KeyF1 => Some(ScanCode::F1),
|
||||
SAppKeycode::KeyF2 => Some(ScanCode::F2),
|
||||
SAppKeycode::KeyF3 => Some(ScanCode::F3),
|
||||
SAppKeycode::KeyF4 => Some(ScanCode::F4),
|
||||
SAppKeycode::KeyF5 => Some(ScanCode::F5),
|
||||
SAppKeycode::KeyF6 => Some(ScanCode::F6),
|
||||
SAppKeycode::KeyF7 => Some(ScanCode::F7),
|
||||
SAppKeycode::KeyF8 => Some(ScanCode::F8),
|
||||
SAppKeycode::KeyF9 => Some(ScanCode::F9),
|
||||
SAppKeycode::KeyF10 => Some(ScanCode::F10),
|
||||
SAppKeycode::KeyF11 => Some(ScanCode::F11),
|
||||
SAppKeycode::KeyF12 => Some(ScanCode::F12),
|
||||
SAppKeycode::KeyF13 => Some(ScanCode::F13),
|
||||
SAppKeycode::KeyF14 => Some(ScanCode::F14),
|
||||
SAppKeycode::KeyF15 => Some(ScanCode::F15),
|
||||
SAppKeycode::KeyF16 => Some(ScanCode::F16),
|
||||
SAppKeycode::KeyF17 => Some(ScanCode::F17),
|
||||
SAppKeycode::KeyF18 => Some(ScanCode::F18),
|
||||
SAppKeycode::KeyF19 => Some(ScanCode::F19),
|
||||
SAppKeycode::KeyF20 => Some(ScanCode::F20),
|
||||
SAppKeycode::KeyF21 => Some(ScanCode::F21),
|
||||
SAppKeycode::KeyF22 => Some(ScanCode::F22),
|
||||
SAppKeycode::KeyF23 => Some(ScanCode::F23),
|
||||
SAppKeycode::KeyF24 => Some(ScanCode::F24),
|
||||
SAppKeycode::KeyKP0 => Some(ScanCode::Numpad0),
|
||||
SAppKeycode::KeyKP1 => Some(ScanCode::Numpad1),
|
||||
SAppKeycode::KeyKP2 => Some(ScanCode::Numpad2),
|
||||
SAppKeycode::KeyKP3 => Some(ScanCode::Numpad3),
|
||||
SAppKeycode::KeyKP4 => Some(ScanCode::Numpad4),
|
||||
SAppKeycode::KeyKP5 => Some(ScanCode::Numpad5),
|
||||
SAppKeycode::KeyKP6 => Some(ScanCode::Numpad6),
|
||||
SAppKeycode::KeyKP7 => Some(ScanCode::Numpad7),
|
||||
SAppKeycode::KeyKP8 => Some(ScanCode::Numpad8),
|
||||
SAppKeycode::KeyKP9 => Some(ScanCode::Numpad9),
|
||||
SAppKeycode::KeyKPDecimal => Some(ScanCode::NumpadDecimal),
|
||||
SAppKeycode::KeyKPDivide => Some(ScanCode::NumpadDivide),
|
||||
SAppKeycode::KeyKPMultiply => Some(ScanCode::NumpadMultiply),
|
||||
SAppKeycode::KeyKPSubtract => Some(ScanCode::NumpadSubtract),
|
||||
SAppKeycode::KeyKPAdd => Some(ScanCode::NumpadAdd),
|
||||
SAppKeycode::KeyKPEnter => Some(ScanCode::NumpadEnter),
|
||||
SAppKeycode::KeyKPEqual => Some(ScanCode::NumpadEquals),
|
||||
SAppKeycode::KeyLeftShift => Some(ScanCode::LShift),
|
||||
SAppKeycode::KeyLeftControl => Some(ScanCode::LControl),
|
||||
SAppKeycode::KeyLeftAlt => Some(ScanCode::LAlt),
|
||||
SAppKeycode::KeyLeftSuper => Some(ScanCode::LWin),
|
||||
SAppKeycode::KeyRightShift => Some(ScanCode::RShift),
|
||||
SAppKeycode::KeyRightControl => Some(ScanCode::RControl),
|
||||
SAppKeycode::KeyRightAlt => Some(ScanCode::RAlt),
|
||||
SAppKeycode::KeyRightSuper => Some(ScanCode::RWin),
|
||||
SAppKeycode::KeyMenu => Some(ScanCode::Menu),
|
||||
_ => None,
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
pub mod backend;
|
||||
pub mod backend_null;
|
||||
#[cfg(feature = "backend-glutin")]
|
||||
pub mod backend_opengl;
|
||||
#[cfg(feature = "backend-glutin")]
|
||||
pub mod backend_glutin;
|
||||
#[cfg(feature = "render-opengl")]
|
||||
mod gl;
|
||||
#[cfg(feature = "backend-sdl")]
|
||||
pub mod backend_sdl2;
|
||||
|
@ -13,5 +13,7 @@ pub mod error;
|
|||
pub mod filesystem;
|
||||
pub mod graphics;
|
||||
pub mod keyboard;
|
||||
#[cfg(feature = "render-opengl")]
|
||||
pub mod render_opengl;
|
||||
pub mod ui;
|
||||
pub mod vfs;
|
||||
|
|
|
@ -1,325 +1,27 @@
|
|||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use glutin::event::{Event, TouchPhase, WindowEvent, ElementState, VirtualKeyCode};
|
||||
use glutin::event_loop::{ControlFlow, EventLoop};
|
||||
use glutin::window::WindowBuilder;
|
||||
use glutin::{Api, ContextBuilder, GlProfile, GlRequest, PossiblyCurrent, WindowedContext};
|
||||
use imgui::{DrawCmd, DrawCmdParams, DrawData, DrawIdx, DrawVert};
|
||||
|
||||
use gl::types::*;
|
||||
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::backend::{BackendRenderer, BackendTexture, SpriteBatchCommand};
|
||||
use crate::framework::error::GameError::RenderError;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::gl;
|
||||
use crate::framework::gl::types::*;
|
||||
use crate::framework::graphics::BlendMode;
|
||||
use crate::input::touch_controls::TouchPoint;
|
||||
use crate::{Game, GAME_SUSPENDED};
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
pub struct GlutinBackend;
|
||||
|
||||
impl GlutinBackend {
|
||||
pub fn new() -> GameResult<Box<dyn Backend>> {
|
||||
Ok(Box::new(GlutinBackend))
|
||||
}
|
||||
pub struct GLContext {
|
||||
pub get_proc_address: unsafe fn(user_data: &mut *mut c_void, name: &str) -> *const c_void,
|
||||
pub swap_buffers: unsafe fn(user_data: &mut *mut c_void),
|
||||
pub user_data: *mut c_void,
|
||||
}
|
||||
|
||||
impl Backend for GlutinBackend {
|
||||
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> {
|
||||
#[cfg(target_os = "android")]
|
||||
loop {
|
||||
match ndk_glue::native_window().as_ref() {
|
||||
Some(_) => {
|
||||
log::info!("NativeWindow Found: {:?}", ndk_glue::native_window());
|
||||
break;
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Box::new(GlutinEventLoop { refs: Rc::new(UnsafeCell::new(None)) }))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlutinEventLoop {
|
||||
refs: Rc<UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>>,
|
||||
}
|
||||
|
||||
impl GlutinEventLoop {
|
||||
fn get_context(&self, event_loop: &EventLoop<()>) -> &mut WindowedContext<PossiblyCurrent> {
|
||||
let mut refs = unsafe { &mut *self.refs.get() };
|
||||
|
||||
if refs.is_none() {
|
||||
let mut window = WindowBuilder::new();
|
||||
let windowed_context = ContextBuilder::new()
|
||||
.with_gl(GlRequest::Specific(Api::OpenGlEs, (2, 0)))
|
||||
.with_gl_profile(GlProfile::Core)
|
||||
.with_gl_debug_flag(false)
|
||||
.with_pixel_format(24, 8)
|
||||
.with_vsync(true);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use glutin::platform::windows::WindowBuilderExtWindows;
|
||||
window = window.with_drag_and_drop(false);
|
||||
}
|
||||
|
||||
window = window.with_title("doukutsu-rs");
|
||||
|
||||
let windowed_context = windowed_context.build_windowed(window, event_loop)
|
||||
.unwrap();
|
||||
|
||||
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if let Some(nwin) = ndk_glue::native_window().as_ref() {
|
||||
unsafe {
|
||||
windowed_context.surface_created(nwin.ptr().as_ptr() as *mut std::ffi::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
refs.replace(windowed_context);
|
||||
}
|
||||
|
||||
refs.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
fn request_android_redraw() {
|
||||
match ndk_glue::native_window().as_ref() {
|
||||
Some(native_window) => {
|
||||
let a_native_window: *mut ndk_sys::ANativeWindow = native_window.ptr().as_ptr();
|
||||
let a_native_activity: *mut ndk_sys::ANativeActivity = ndk_glue::native_activity().ptr().as_ptr();
|
||||
unsafe {
|
||||
match (*(*a_native_activity).callbacks).onNativeWindowRedrawNeeded {
|
||||
Some(callback) => callback(a_native_activity, a_native_window),
|
||||
None => (),
|
||||
};
|
||||
};
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
fn get_insets() -> GameResult<(f32, f32, f32, f32)> {
|
||||
unsafe {
|
||||
let vm_ptr = ndk_glue::native_activity().vm();
|
||||
let vm = unsafe { jni::JavaVM::from_raw(vm_ptr) }?;
|
||||
let vm_env = vm.attach_current_thread()?;
|
||||
|
||||
//let class = vm_env.find_class("io/github/doukutsu_rs/MainActivity")?;
|
||||
let class = vm_env.new_global_ref(ndk_glue::native_activity().activity())?;
|
||||
let field = vm_env.get_field(class.as_obj(), "displayInsets", "[I")?.to_jni().l as jni::sys::jintArray;
|
||||
|
||||
let mut elements = [0; 4];
|
||||
vm_env.get_int_array_region(field, 0, &mut elements)?;
|
||||
|
||||
Ok((elements[0] as f32, elements[1] as f32, elements[2] as f32, elements[3] as f32))
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendEventLoop for GlutinEventLoop {
|
||||
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
|
||||
let event_loop = EventLoop::new();
|
||||
let state_ref = unsafe { &mut *game.state.get() };
|
||||
let window: &'static mut WindowedContext<PossiblyCurrent> =
|
||||
unsafe { std::mem::transmute(self.get_context(&event_loop)) };
|
||||
|
||||
{
|
||||
let size = window.window().inner_size();
|
||||
ctx.screen_size = (size.width.max(1) as f32, size.height.max(1) as f32);
|
||||
state_ref.handle_resize(ctx).unwrap();
|
||||
}
|
||||
|
||||
// it won't ever return
|
||||
let (game, ctx): (&'static mut Game, &'static mut Context) =
|
||||
unsafe { (std::mem::transmute(game), std::mem::transmute(ctx)) };
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, window_id }
|
||||
if window_id == window.window().id() =>
|
||||
{
|
||||
state_ref.shutdown();
|
||||
}
|
||||
Event::Resumed => {
|
||||
println!("resumed!");
|
||||
{
|
||||
let mut mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
*mutex = false;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if let Some(nwin) = ndk_glue::native_window().as_ref() {
|
||||
state_ref.graphics_reset();
|
||||
unsafe {
|
||||
window.surface_created(nwin.ptr().as_ptr() as *mut std::ffi::c_void);
|
||||
request_android_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Suspended => {
|
||||
println!("suspended!");
|
||||
{
|
||||
let mut mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
*mutex = true;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
unsafe {
|
||||
window.surface_destroyed();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event: WindowEvent::Resized(size), window_id }
|
||||
if window_id == window.window().id() =>
|
||||
{
|
||||
if let Some(renderer) = ctx.renderer.as_ref() {
|
||||
if let Ok(imgui) = renderer.imgui() {
|
||||
imgui.io_mut().display_size = [size.width as f32, size.height as f32];
|
||||
}
|
||||
|
||||
ctx.screen_size = (size.width as f32, size.height as f32);
|
||||
state_ref.handle_resize(ctx).unwrap();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event: WindowEvent::Touch(touch), window_id }
|
||||
if window_id == window.window().id() =>
|
||||
{
|
||||
let mut controls = &mut state_ref.touch_controls;
|
||||
let scale = state_ref.scale as f64;
|
||||
|
||||
match touch.phase {
|
||||
TouchPhase::Started | TouchPhase::Moved => {
|
||||
if let Some(point) = controls.points.iter_mut().find(|p| p.id == touch.id) {
|
||||
point.last_position = point.position;
|
||||
point.position = (touch.location.x / scale, touch.location.y / scale);
|
||||
} else {
|
||||
controls.touch_id_counter = controls.touch_id_counter.wrapping_add(1);
|
||||
|
||||
let point = TouchPoint {
|
||||
id: touch.id,
|
||||
touch_id: controls.touch_id_counter,
|
||||
position: (touch.location.x / scale, touch.location.y / scale),
|
||||
last_position: (0.0, 0.0),
|
||||
};
|
||||
controls.points.push(point);
|
||||
|
||||
if touch.phase == TouchPhase::Started {
|
||||
controls.clicks.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
TouchPhase::Ended | TouchPhase::Cancelled => {
|
||||
controls.points.retain(|p| p.id != touch.id);
|
||||
controls.clicks.retain(|p| p.id != touch.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput { input, .. }, window_id }
|
||||
if window_id == window.window().id() => {
|
||||
|
||||
if let Some(keycode) = input.virtual_keycode {
|
||||
if let Some(drs_scan) = conv_keycode(keycode) {
|
||||
let key_state = match input.state {
|
||||
ElementState::Pressed => true,
|
||||
ElementState::Released => false,
|
||||
};
|
||||
|
||||
ctx.keyboard_context.set_key(drs_scan, key_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(id) if id == window.window().id() => {
|
||||
{
|
||||
let mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
if *mutex {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
{
|
||||
if let Err(err) = game.draw(ctx) {
|
||||
log::error!("Failed to draw frame: {}", err);
|
||||
}
|
||||
|
||||
window.window().request_redraw();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
request_android_redraw();
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
if state_ref.shutdown {
|
||||
log::info!("Shutting down...");
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
if *mutex {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
game.update(ctx).unwrap();
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
match get_insets() {
|
||||
Ok(insets) => {
|
||||
ctx.screen_insets = insets;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to update insets: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = game.draw(ctx) {
|
||||
log::error!("Failed to draw frame: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
if state_ref.next_scene.is_some() {
|
||||
mem::swap(&mut game.scene, &mut state_ref.next_scene);
|
||||
state_ref.next_scene = None;
|
||||
game.scene.as_mut().unwrap().init(state_ref, ctx).unwrap();
|
||||
game.loops = 0;
|
||||
state_ref.frame_time = 0.0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
let mut imgui = imgui::Context::create();
|
||||
imgui.io_mut().display_size = [640.0, 480.0];
|
||||
|
||||
Ok(Box::new(GlutinRenderer {
|
||||
refs: self.refs.clone(),
|
||||
imgui: UnsafeCell::new(imgui),
|
||||
imgui_data: ImguiData::new(),
|
||||
context_active: Arc::new(RefCell::new(true)),
|
||||
def_matrix: [[0.0; 4]; 4],
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlutinTexture {
|
||||
pub struct OpenGLTexture {
|
||||
width: u16,
|
||||
height: u16,
|
||||
texture_id: u32,
|
||||
|
@ -338,7 +40,7 @@ struct VertexData {
|
|||
color: (u8, u8, u8, u8),
|
||||
}
|
||||
|
||||
impl BackendTexture for GlutinTexture {
|
||||
impl BackendTexture for OpenGLTexture {
|
||||
fn dimensions(&self) -> (u16, u16) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
@ -537,7 +239,7 @@ impl BackendTexture for GlutinTexture {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for GlutinTexture {
|
||||
impl Drop for OpenGLTexture {
|
||||
fn drop(&mut self) {
|
||||
if *self.context_active.as_ref().borrow() {
|
||||
unsafe {
|
||||
|
@ -582,7 +284,7 @@ fn check_shader_compile_status(shader: u32, gl: &Gl) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
const IMGUI_SHADER_VERT: &str = r"
|
||||
const VERTEX_SHADER_BASIC: &str = r"
|
||||
#version 100
|
||||
|
||||
precision mediump float;
|
||||
|
@ -603,7 +305,7 @@ void main()
|
|||
|
||||
";
|
||||
|
||||
const IMGUI_SHADER_FRAG: &str = r"
|
||||
const FRAGMENT_SHADER_TEXTURED: &str = r"
|
||||
#version 100
|
||||
|
||||
precision mediump float;
|
||||
|
@ -619,6 +321,21 @@ void main()
|
|||
|
||||
";
|
||||
|
||||
const FRAGMENT_SHADER_COLOR: &str = r"
|
||||
#version 100
|
||||
|
||||
precision mediump float;
|
||||
|
||||
varying vec2 Frag_UV;
|
||||
varying vec4 Frag_Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = Frag_Color;
|
||||
}
|
||||
|
||||
";
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Locs {
|
||||
texture: GLint,
|
||||
|
@ -654,10 +371,10 @@ impl ImguiData {
|
|||
fn init(&mut self, imgui: &mut imgui::Context, gl: &Gl) {
|
||||
self.initialized = true;
|
||||
|
||||
let vert_sources = [IMGUI_SHADER_VERT.as_ptr() as *const GLchar];
|
||||
let frag_sources = [IMGUI_SHADER_FRAG.as_ptr() as *const GLchar];
|
||||
let vert_sources_len = [IMGUI_SHADER_VERT.len() as GLint - 1];
|
||||
let frag_sources_len = [IMGUI_SHADER_FRAG.len() as GLint - 1];
|
||||
let vert_sources = [VERTEX_SHADER_BASIC.as_ptr() as *const GLchar];
|
||||
let frag_sources = [FRAGMENT_SHADER_TEXTURED.as_ptr() as *const GLchar];
|
||||
let vert_sources_len = [VERTEX_SHADER_BASIC.len() as GLint - 1];
|
||||
let frag_sources_len = [FRAGMENT_SHADER_TEXTURED.len() as GLint - 1];
|
||||
|
||||
unsafe {
|
||||
self.program = gl.gl.CreateProgram();
|
||||
|
@ -726,31 +443,28 @@ impl ImguiData {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct GlutinRenderer {
|
||||
refs: Rc<UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>>,
|
||||
imgui: UnsafeCell<imgui::Context>,
|
||||
imgui_data: ImguiData,
|
||||
context_active: Arc<RefCell<bool>>,
|
||||
def_matrix: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
pub struct Gl {
|
||||
pub gl: gl::Gles2,
|
||||
}
|
||||
|
||||
static mut GL_PROC: Option<Gl> = None;
|
||||
|
||||
pub fn load_gl(gl_context: &glutin::Context<PossiblyCurrent>) -> &'static Gl {
|
||||
pub fn load_gl(gl_context: &mut GLContext) -> &'static Gl {
|
||||
unsafe {
|
||||
if let Some(gl) = GL_PROC.as_ref() {
|
||||
return gl;
|
||||
}
|
||||
|
||||
let gl = gl::Gles2::load_with(|ptr| gl_context.get_proc_address(ptr) as *const _);
|
||||
let gl = gl::Gles2::load_with(|ptr| (gl_context.get_proc_address)(&mut gl_context.user_data, ptr));
|
||||
|
||||
let version = unsafe {
|
||||
let data = CStr::from_ptr(gl.GetString(gl::VERSION) as *const _).to_bytes().to_vec();
|
||||
String::from_utf8(data).unwrap()
|
||||
let p = gl.GetString(gl::VERSION);
|
||||
if p.is_null() {
|
||||
"unknown".to_owned()
|
||||
} else {
|
||||
let data = CStr::from_ptr(p as *const _).to_bytes().to_vec();
|
||||
String::from_utf8(data).unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
log::info!("OpenGL version {}", version);
|
||||
|
@ -760,25 +474,41 @@ pub fn load_gl(gl_context: &glutin::Context<PossiblyCurrent>) -> &'static Gl {
|
|||
}
|
||||
}
|
||||
|
||||
impl GlutinRenderer {
|
||||
fn get_context(&mut self) -> Option<(&mut WindowedContext<PossiblyCurrent>, &'static Gl)> {
|
||||
let (refs, imgui) = unsafe { ((&mut *self.refs.get()).as_mut(), &mut *self.imgui.get()) };
|
||||
pub struct OpenGLRenderer {
|
||||
refs: GLContext,
|
||||
imgui: UnsafeCell<imgui::Context>,
|
||||
imgui_data: ImguiData,
|
||||
context_active: Arc<RefCell<bool>>,
|
||||
def_matrix: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
refs.map(|context| {
|
||||
let gl = load_gl(context);
|
||||
impl OpenGLRenderer {
|
||||
pub fn new(refs: GLContext, imgui: UnsafeCell<imgui::Context>) -> OpenGLRenderer {
|
||||
OpenGLRenderer {
|
||||
refs,
|
||||
imgui,
|
||||
imgui_data: ImguiData::new(),
|
||||
context_active: Arc::new(RefCell::new(true)),
|
||||
def_matrix: [[0.0; 4]; 4],
|
||||
}
|
||||
}
|
||||
|
||||
if !self.imgui_data.initialized {
|
||||
self.imgui_data.init(imgui, gl);
|
||||
}
|
||||
fn get_context(&mut self) -> Option<(&mut GLContext, &'static Gl)> {
|
||||
let imgui = unsafe { &mut *self.imgui.get() };
|
||||
|
||||
(context, gl)
|
||||
})
|
||||
let gl = load_gl(&mut self.refs);
|
||||
|
||||
if !self.imgui_data.initialized {
|
||||
self.imgui_data.init(imgui, gl);
|
||||
}
|
||||
|
||||
Some((&mut self.refs, gl))
|
||||
}
|
||||
}
|
||||
|
||||
fn field_offset<T, U, F: for<'a> FnOnce(&'a T) -> &'a U>(f: F) -> usize {
|
||||
unsafe {
|
||||
let instance = mem::zeroed::<T>();
|
||||
let instance = MaybeUninit::uninit().assume_init();
|
||||
|
||||
let offset = {
|
||||
let field: &U = f(&instance);
|
||||
|
@ -800,7 +530,11 @@ where
|
|||
val
|
||||
}
|
||||
|
||||
impl BackendRenderer for GlutinRenderer {
|
||||
impl BackendRenderer for OpenGLRenderer {
|
||||
fn renderer_name(&self) -> String {
|
||||
"OpenGL(ES)".to_owned()
|
||||
}
|
||||
|
||||
fn clear(&mut self, color: Color) {
|
||||
if let Some((_, gl)) = self.get_context() {
|
||||
unsafe {
|
||||
|
@ -812,7 +546,7 @@ impl BackendRenderer for GlutinRenderer {
|
|||
|
||||
fn present(&mut self) -> GameResult {
|
||||
{
|
||||
let mutex = GAME_SUSPENDED.lock().unwrap();
|
||||
let mutex = crate::GAME_SUSPENDED.lock().unwrap();
|
||||
if *mutex {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -821,9 +555,9 @@ impl BackendRenderer for GlutinRenderer {
|
|||
if let Some((context, gl)) = self.get_context() {
|
||||
unsafe {
|
||||
gl.gl.Finish();
|
||||
}
|
||||
|
||||
context.swap_buffers().map_err(|e| RenderError(e.to_string()))?;
|
||||
(context.swap_buffers)(&mut context.user_data);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -895,7 +629,7 @@ impl BackendRenderer for GlutinRenderer {
|
|||
|
||||
// todo error checking: glCheckFramebufferStatus()
|
||||
|
||||
Ok(Box::new(GlutinTexture {
|
||||
Ok(Box::new(OpenGLTexture {
|
||||
texture_id,
|
||||
framebuffer_id,
|
||||
width,
|
||||
|
@ -934,7 +668,7 @@ impl BackendRenderer for GlutinRenderer {
|
|||
|
||||
gl.gl.BindTexture(gl::TEXTURE_2D, current_texture_id);
|
||||
|
||||
Ok(Box::new(GlutinTexture {
|
||||
Ok(Box::new(OpenGLTexture {
|
||||
texture_id,
|
||||
framebuffer_id: 0,
|
||||
width,
|
||||
|
@ -977,7 +711,7 @@ impl BackendRenderer for GlutinRenderer {
|
|||
if let Some((_, gl)) = self.get_context() {
|
||||
unsafe {
|
||||
if let Some(texture) = texture {
|
||||
let gl_texture: &Box<GlutinTexture> = std::mem::transmute(texture);
|
||||
let gl_texture: &Box<OpenGLTexture> = std::mem::transmute(texture);
|
||||
|
||||
let matrix = [
|
||||
[2.0 / (gl_texture.width as f32), 0.0, 0.0, 0.0],
|
||||
|
@ -1201,6 +935,7 @@ impl BackendRenderer for GlutinRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
gl.gl.Disable(gl::SCISSOR_TEST);
|
||||
//gl.gl.DeleteVertexArrays(1, &vao);
|
||||
}
|
||||
}
|
||||
|
@ -1209,176 +944,8 @@ impl BackendRenderer for GlutinRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for GlutinRenderer {
|
||||
impl Drop for OpenGLRenderer {
|
||||
fn drop(&mut self) {
|
||||
*self.context_active.as_ref().borrow_mut() = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn conv_keycode(code: VirtualKeyCode) -> Option<ScanCode> {
|
||||
match code {
|
||||
VirtualKeyCode::Key1 => Some(ScanCode::Key1),
|
||||
VirtualKeyCode::Key2 => Some(ScanCode::Key2),
|
||||
VirtualKeyCode::Key3 => Some(ScanCode::Key3),
|
||||
VirtualKeyCode::Key4 => Some(ScanCode::Key4),
|
||||
VirtualKeyCode::Key5 => Some(ScanCode::Key5),
|
||||
VirtualKeyCode::Key6 => Some(ScanCode::Key6),
|
||||
VirtualKeyCode::Key7 => Some(ScanCode::Key7),
|
||||
VirtualKeyCode::Key8 => Some(ScanCode::Key8),
|
||||
VirtualKeyCode::Key9 => Some(ScanCode::Key9),
|
||||
VirtualKeyCode::Key0 => Some(ScanCode::Key0),
|
||||
VirtualKeyCode::A => Some(ScanCode::A),
|
||||
VirtualKeyCode::B => Some(ScanCode::B),
|
||||
VirtualKeyCode::C => Some(ScanCode::C),
|
||||
VirtualKeyCode::D => Some(ScanCode::D),
|
||||
VirtualKeyCode::E => Some(ScanCode::E),
|
||||
VirtualKeyCode::F => Some(ScanCode::F),
|
||||
VirtualKeyCode::G => Some(ScanCode::G),
|
||||
VirtualKeyCode::H => Some(ScanCode::H),
|
||||
VirtualKeyCode::I => Some(ScanCode::I),
|
||||
VirtualKeyCode::J => Some(ScanCode::J),
|
||||
VirtualKeyCode::K => Some(ScanCode::K),
|
||||
VirtualKeyCode::L => Some(ScanCode::L),
|
||||
VirtualKeyCode::M => Some(ScanCode::M),
|
||||
VirtualKeyCode::N => Some(ScanCode::N),
|
||||
VirtualKeyCode::O => Some(ScanCode::O),
|
||||
VirtualKeyCode::P => Some(ScanCode::P),
|
||||
VirtualKeyCode::Q => Some(ScanCode::Q),
|
||||
VirtualKeyCode::R => Some(ScanCode::R),
|
||||
VirtualKeyCode::S => Some(ScanCode::S),
|
||||
VirtualKeyCode::T => Some(ScanCode::T),
|
||||
VirtualKeyCode::U => Some(ScanCode::U),
|
||||
VirtualKeyCode::V => Some(ScanCode::V),
|
||||
VirtualKeyCode::W => Some(ScanCode::W),
|
||||
VirtualKeyCode::X => Some(ScanCode::X),
|
||||
VirtualKeyCode::Y => Some(ScanCode::Y),
|
||||
VirtualKeyCode::Z => Some(ScanCode::Z),
|
||||
VirtualKeyCode::Escape => Some(ScanCode::Escape),
|
||||
VirtualKeyCode::F1 => Some(ScanCode::F1),
|
||||
VirtualKeyCode::F2 => Some(ScanCode::F2),
|
||||
VirtualKeyCode::F3 => Some(ScanCode::F3),
|
||||
VirtualKeyCode::F4 => Some(ScanCode::F4),
|
||||
VirtualKeyCode::F5 => Some(ScanCode::F5),
|
||||
VirtualKeyCode::F6 => Some(ScanCode::F6),
|
||||
VirtualKeyCode::F7 => Some(ScanCode::F7),
|
||||
VirtualKeyCode::F8 => Some(ScanCode::F8),
|
||||
VirtualKeyCode::F9 => Some(ScanCode::F9),
|
||||
VirtualKeyCode::F10 => Some(ScanCode::F10),
|
||||
VirtualKeyCode::F11 => Some(ScanCode::F11),
|
||||
VirtualKeyCode::F12 => Some(ScanCode::F12),
|
||||
VirtualKeyCode::F13 => Some(ScanCode::F13),
|
||||
VirtualKeyCode::F14 => Some(ScanCode::F14),
|
||||
VirtualKeyCode::F15 => Some(ScanCode::F15),
|
||||
VirtualKeyCode::F16 => Some(ScanCode::F16),
|
||||
VirtualKeyCode::F17 => Some(ScanCode::F17),
|
||||
VirtualKeyCode::F18 => Some(ScanCode::F18),
|
||||
VirtualKeyCode::F19 => Some(ScanCode::F19),
|
||||
VirtualKeyCode::F20 => Some(ScanCode::F20),
|
||||
VirtualKeyCode::F21 => Some(ScanCode::F21),
|
||||
VirtualKeyCode::F22 => Some(ScanCode::F22),
|
||||
VirtualKeyCode::F23 => Some(ScanCode::F23),
|
||||
VirtualKeyCode::F24 => Some(ScanCode::F24),
|
||||
VirtualKeyCode::Snapshot => Some(ScanCode::Snapshot),
|
||||
VirtualKeyCode::Scroll => Some(ScanCode::Scroll),
|
||||
VirtualKeyCode::Pause => Some(ScanCode::Pause),
|
||||
VirtualKeyCode::Insert => Some(ScanCode::Insert),
|
||||
VirtualKeyCode::Home => Some(ScanCode::Home),
|
||||
VirtualKeyCode::Delete => Some(ScanCode::Delete),
|
||||
VirtualKeyCode::End => Some(ScanCode::End),
|
||||
VirtualKeyCode::PageDown => Some(ScanCode::PageDown),
|
||||
VirtualKeyCode::PageUp => Some(ScanCode::PageUp),
|
||||
VirtualKeyCode::Left => Some(ScanCode::Left),
|
||||
VirtualKeyCode::Up => Some(ScanCode::Up),
|
||||
VirtualKeyCode::Right => Some(ScanCode::Right),
|
||||
VirtualKeyCode::Down => Some(ScanCode::Down),
|
||||
VirtualKeyCode::Back => Some(ScanCode::Back),
|
||||
VirtualKeyCode::Return => Some(ScanCode::Return),
|
||||
VirtualKeyCode::Space => Some(ScanCode::Space),
|
||||
VirtualKeyCode::Compose => Some(ScanCode::Compose),
|
||||
VirtualKeyCode::Caret => Some(ScanCode::Caret),
|
||||
VirtualKeyCode::Numlock => Some(ScanCode::Numlock),
|
||||
VirtualKeyCode::Numpad0 => Some(ScanCode::Numpad0),
|
||||
VirtualKeyCode::Numpad1 => Some(ScanCode::Numpad1),
|
||||
VirtualKeyCode::Numpad2 => Some(ScanCode::Numpad2),
|
||||
VirtualKeyCode::Numpad3 => Some(ScanCode::Numpad3),
|
||||
VirtualKeyCode::Numpad4 => Some(ScanCode::Numpad4),
|
||||
VirtualKeyCode::Numpad5 => Some(ScanCode::Numpad5),
|
||||
VirtualKeyCode::Numpad6 => Some(ScanCode::Numpad6),
|
||||
VirtualKeyCode::Numpad7 => Some(ScanCode::Numpad7),
|
||||
VirtualKeyCode::Numpad8 => Some(ScanCode::Numpad8),
|
||||
VirtualKeyCode::Numpad9 => Some(ScanCode::Numpad9),
|
||||
VirtualKeyCode::NumpadAdd => Some(ScanCode::NumpadAdd),
|
||||
VirtualKeyCode::NumpadDivide => Some(ScanCode::NumpadDivide),
|
||||
VirtualKeyCode::NumpadDecimal => Some(ScanCode::NumpadDecimal),
|
||||
VirtualKeyCode::NumpadComma => Some(ScanCode::NumpadComma),
|
||||
VirtualKeyCode::NumpadEnter => Some(ScanCode::NumpadEnter),
|
||||
VirtualKeyCode::NumpadEquals => Some(ScanCode::NumpadEquals),
|
||||
VirtualKeyCode::NumpadMultiply => Some(ScanCode::NumpadMultiply),
|
||||
VirtualKeyCode::NumpadSubtract => Some(ScanCode::NumpadSubtract),
|
||||
VirtualKeyCode::AbntC1 => Some(ScanCode::AbntC1),
|
||||
VirtualKeyCode::AbntC2 => Some(ScanCode::AbntC2),
|
||||
VirtualKeyCode::Apostrophe => Some(ScanCode::Apostrophe),
|
||||
VirtualKeyCode::Apps => Some(ScanCode::Apps),
|
||||
VirtualKeyCode::Asterisk => Some(ScanCode::Asterisk),
|
||||
VirtualKeyCode::At => Some(ScanCode::At),
|
||||
VirtualKeyCode::Ax => Some(ScanCode::Ax),
|
||||
VirtualKeyCode::Backslash => Some(ScanCode::Backslash),
|
||||
VirtualKeyCode::Calculator => Some(ScanCode::Calculator),
|
||||
VirtualKeyCode::Capital => Some(ScanCode::Capital),
|
||||
VirtualKeyCode::Colon => Some(ScanCode::Colon),
|
||||
VirtualKeyCode::Comma => Some(ScanCode::Comma),
|
||||
VirtualKeyCode::Convert => Some(ScanCode::Convert),
|
||||
VirtualKeyCode::Equals => Some(ScanCode::Equals),
|
||||
VirtualKeyCode::Grave => Some(ScanCode::Grave),
|
||||
VirtualKeyCode::Kana => Some(ScanCode::Kana),
|
||||
VirtualKeyCode::Kanji => Some(ScanCode::Kanji),
|
||||
VirtualKeyCode::LAlt => Some(ScanCode::LAlt),
|
||||
VirtualKeyCode::LBracket => Some(ScanCode::LBracket),
|
||||
VirtualKeyCode::LControl => Some(ScanCode::LControl),
|
||||
VirtualKeyCode::LShift => Some(ScanCode::LShift),
|
||||
VirtualKeyCode::LWin => Some(ScanCode::LWin),
|
||||
VirtualKeyCode::Mail => Some(ScanCode::Mail),
|
||||
VirtualKeyCode::MediaSelect => Some(ScanCode::MediaSelect),
|
||||
VirtualKeyCode::MediaStop => Some(ScanCode::MediaStop),
|
||||
VirtualKeyCode::Minus => Some(ScanCode::Minus),
|
||||
VirtualKeyCode::Mute => Some(ScanCode::Mute),
|
||||
VirtualKeyCode::MyComputer => Some(ScanCode::MyComputer),
|
||||
VirtualKeyCode::NavigateForward => Some(ScanCode::NavigateForward),
|
||||
VirtualKeyCode::NavigateBackward => Some(ScanCode::NavigateBackward),
|
||||
VirtualKeyCode::NextTrack => Some(ScanCode::NextTrack),
|
||||
VirtualKeyCode::NoConvert => Some(ScanCode::NoConvert),
|
||||
VirtualKeyCode::OEM102 => Some(ScanCode::OEM102),
|
||||
VirtualKeyCode::Period => Some(ScanCode::Period),
|
||||
VirtualKeyCode::PlayPause => Some(ScanCode::PlayPause),
|
||||
VirtualKeyCode::Plus => Some(ScanCode::Plus),
|
||||
VirtualKeyCode::Power => Some(ScanCode::Power),
|
||||
VirtualKeyCode::PrevTrack => Some(ScanCode::PrevTrack),
|
||||
VirtualKeyCode::RAlt => Some(ScanCode::RAlt),
|
||||
VirtualKeyCode::RBracket => Some(ScanCode::RBracket),
|
||||
VirtualKeyCode::RControl => Some(ScanCode::RControl),
|
||||
VirtualKeyCode::RShift => Some(ScanCode::RShift),
|
||||
VirtualKeyCode::RWin => Some(ScanCode::RWin),
|
||||
VirtualKeyCode::Semicolon => Some(ScanCode::Semicolon),
|
||||
VirtualKeyCode::Slash => Some(ScanCode::Slash),
|
||||
VirtualKeyCode::Sleep => Some(ScanCode::Sleep),
|
||||
VirtualKeyCode::Stop => Some(ScanCode::Stop),
|
||||
VirtualKeyCode::Sysrq => Some(ScanCode::Sysrq),
|
||||
VirtualKeyCode::Tab => Some(ScanCode::Tab),
|
||||
VirtualKeyCode::Underline => Some(ScanCode::Underline),
|
||||
VirtualKeyCode::Unlabeled => Some(ScanCode::Unlabeled),
|
||||
VirtualKeyCode::VolumeDown => Some(ScanCode::VolumeDown),
|
||||
VirtualKeyCode::VolumeUp => Some(ScanCode::VolumeUp),
|
||||
VirtualKeyCode::Wake => Some(ScanCode::Wake),
|
||||
VirtualKeyCode::WebBack => Some(ScanCode::WebBack),
|
||||
VirtualKeyCode::WebFavorites => Some(ScanCode::WebFavorites),
|
||||
VirtualKeyCode::WebForward => Some(ScanCode::WebForward),
|
||||
VirtualKeyCode::WebHome => Some(ScanCode::WebHome),
|
||||
VirtualKeyCode::WebRefresh => Some(ScanCode::WebRefresh),
|
||||
VirtualKeyCode::WebSearch => Some(ScanCode::WebSearch),
|
||||
VirtualKeyCode::WebStop => Some(ScanCode::WebStop),
|
||||
VirtualKeyCode::Yen => Some(ScanCode::Yen),
|
||||
VirtualKeyCode::Copy => Some(ScanCode::Copy),
|
||||
VirtualKeyCode::Paste => Some(ScanCode::Paste),
|
||||
VirtualKeyCode::Cut => Some(ScanCode::Cut),
|
||||
}
|
||||
}
|
|
@ -121,8 +121,8 @@ impl Scene for TitleScene {
|
|||
} else {
|
||||
self.option_menu.push_entry(MenuEntry::Disabled("Seasonal textures".to_string()));
|
||||
}
|
||||
self.option_menu.push_entry(MenuEntry::Active("Join our Discord".to_string()));
|
||||
self.option_menu.push_entry(MenuEntry::Disabled(DISCORD_LINK.to_owned()));
|
||||
self.option_menu.push_entry(MenuEntry::Active(DISCORD_LINK.to_owned()));
|
||||
self.option_menu.push_entry(MenuEntry::Disabled(["Renderer: ", &ctx.renderer.as_ref().unwrap().renderer_name()].join("")));
|
||||
self.option_menu.push_entry(MenuEntry::Active("Back".to_string()));
|
||||
|
||||
self.save_select_menu.push_entry(MenuEntry::NewSave);
|
||||
|
|
Loading…
Reference in New Issue