grafix_toolbox/kit/opengl/context/
sdl2_impl.rs1use 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}