Skip to main content

grafix_toolbox/kit/opengl/context/
sdl2_impl.rs

1use super::{event::*, window, *};
2use sdl2::video::*;
3
4pub struct CtxDrop(Window, GLContext);
5unsafe impl Send for CtxDrop {}
6
7pub struct WindowImpl {
8	mods: Mod,
9	ctx: GLContext,
10	window: Window,
11	events: sdl2::EventPump,
12	info: FrameInfo,
13}
14impl WindowImpl {
15	pub fn get(args: impl window::WINSize, title: &str) -> Res<Self> {
16		use WindowPos::Positioned as P;
17
18		let sdl = sdl2::init().explain_err(|| "SLD2 init failed")?;
19		let video = sdl.video().explain_err(|| "SDL2 video failed")?;
20
21		let gl = video.gl_attr();
22		gl.set_accelerated_visual(true);
23		gl.set_context_profile(GLProfile::Core);
24		let (maj, min, _) = GL::unigl::GL_VERSION;
25		gl.set_context_version(maj as u8, min as u8);
26		gl.set_context_flags()
27			.forward_compatible()
28			.pipe(|a| if GL::unigl::IS_DEBUG { a.debug() } else { a })
29			.set();
30		gl.set_multisample_buffers(1);
31		gl.set_multisample_samples(4);
32
33		let (x, y, w, h) = args.get();
34		let window = video
35			.window(title, w, h)
36			.opengl()
37			.resizable()
38			.build()
39			.explain_err(|| "Cannot create SDL2 window")?
40			.tap(|w| w.set_position(P(x), P(y)));
41
42		video.text_input().start();
43
44		let ctx = window.gl_create_context().explain_err(|| "SDL2 context failed")?;
45
46		gl.set_share_with_current_context(true);
47
48		load_gl(|s| video.gl_get_proc_address(s) as *const _);
49
50		let events = sdl.event_pump().unwrap();
51
52		let info = FrameInfo::new((w, h));
53		Self { mods: Mod::empty(), ctx, window, events, info }
54			.tap(|w| {
55				crate::kit::policies::task::InitGLRuntime(Some(w));
56				sdl.pipe(Box).pipe(Box::leak);
57			})
58			.pipe(Ok)
59	}
60}
61impl window::Window for WindowImpl {
62	fn info(&self) -> &FrameInfo {
63		&self.info
64	}
65	fn clipboard(&self) -> String {
66		self.window.subsystem().clipboard().clipboard_text().unwrap_or_default()
67	}
68	fn set_clipboard(&mut self, s: &str) {
69		self.window.subsystem().clipboard().set_clipboard_text(&s[..s.len().min(60_000)]).warn()
70	}
71	fn set_vsync(&mut self, e: bool) {
72		use SwapInterval::*;
73		self.window.subsystem().gl_set_swap_interval(if e { VSync } else { Immediate }).warn()
74	}
75	fn resize(&mut self, size: uVec2) {
76		let Self { window, info, .. } = self;
77		*info = FrameInfo::new(size);
78		let (w, h) = vec2(size);
79		window.set_size(w, h).warn();
80	}
81	fn gl_ctx_maker(&mut self) -> impl SendS + FnOnce() -> CtxDrop {
82		let Self { ctx, window, .. } = self;
83
84		let w = window
85			.subsystem()
86			.window("offhand", 1, 1)
87			.opengl()
88			.borderless()
89			.hidden()
90			.build()
91			.explain_err(|| "Cannot create offhand SDL2 window")
92			.fail();
93		let c = w.gl_create_context().explain_err(|| "SDL2 gl failed").fail();
94		let ctxdrop = CtxDrop(w, c);
95
96		window.gl_make_current(ctx).fail();
97
98		move || {
99			let CtxDrop(window, ctx) = &ctxdrop;
100			window.gl_make_current(ctx).fail();
101			*GL::macro_uses::gl_was_initialized() = true;
102			ctxdrop
103		}
104	}
105	fn poll_events(&mut self) -> Vec<Event> {
106		use sdl2::{event::Event::*, event::WindowEvent::Resized, keyboard::Mod as M};
107		use {Click as C, Event as E, Key as K};
108
109		let Self { mods, events, info, .. } = self;
110		let modkey = |k| match k {
111			K::LShift | K::RShift => Mod::SHIFT,
112			K::LCtrl | K::RCtrl => Mod::CTRL,
113			K::LAlt | K::RAlt => Mod::ALT,
114			K::LGui | K::RGui => Mod::WIN,
115			_ => Mod::empty(),
116		};
117		let getmod = |m: M| {
118			let i = |c| m.intersects(c);
119			Mod::SHIFT.or_def(i(M::LSHIFTMOD | M::RSHIFTMOD))
120				| Mod::CTRL.or_def(i(M::LCTRLMOD | M::RCTRLMOD))
121				| Mod::ALT.or_def(i(M::LALTMOD | M::RALTMOD))
122				| Mod::WIN.or_def(i(M::LGUIMOD | M::RGUIMOD))
123		};
124
125		events
126			.poll_iter()
127			.flat_map(|event| match event {
128				MouseMotion { mousestate: m, x, y, .. } => {
129					let ((x, y), s @ (w, h)) = (Vec2(2).mul((x, y)), Vec2(info.size));
130					let at = (x - w, h - y).div(s.min_comp());
131
132					let m = Mod::LEFT.or_def(m.left()) | Mod::RIGHT.or_def(m.right()) | Mod::MID.or_def(m.middle());
133					let m = m | *mods;
134
135					vec![E::MouseMove { at, m }]
136				}
137				MouseButtonDown { mouse_btn: b, .. } | MouseButtonUp { mouse_btn: b, .. } => {
138					use sdl2::mouse::MouseButton as B;
139					let button = match b {
140						B::Left => C::Left,
141						B::Right => C::Right,
142						B::Middle => C::Middle,
143						_ => {
144							WARN!("Excessive buttons on mouse");
145							C::Middle
146						}
147					};
148
149					let m = matches!(event, MouseButtonDown { .. });
150					let m = if m { Mod::PRESS } else { Mod::RELEASE };
151					let m = m | *mods;
152
153					vec![E::MouseButton { button, m }]
154				}
155				KeyDown { keycode: Some(key), keymod: m, .. } => {
156					*mods = mods.union(modkey(key));
157					vec![E::Keyboard { key, m: Mod::PRESS | getmod(m) }]
158				}
159				KeyUp { keycode: Some(key), keymod: m, .. } => {
160					*mods = mods.difference(modkey(key));
161					vec![E::Keyboard { key, m: Mod::RELEASE | getmod(m) }]
162				}
163				MouseWheel { precise_x: x, precise_y: y, .. } => vec![E::Scroll { at: (-x, y), m: *mods }],
164				TextInput { text, .. } => text.chars().map(|ch| E::Char { ch }).collect(),
165				Window { win_event: Resized(w, h), .. } => {
166					*info = FrameInfo::new(vec2((w, h)));
167					vec![]
168				}
169				e => {
170					DEBUG!("Registered event not covered {e:?}");
171					vec![]
172				}
173			})
174			.collect()
175	}
176	fn swap(&mut self) {
177		self.window.gl_swap_window();
178	}
179}