1 use super::*; 2 3 use std::marker::PhantomData; 4 use winit::event_loop::EventLoopWindowTarget; 5 use winit::window::{Window, WindowBuilder}; 6 7 /// Represents an OpenGL [`Context`] and the [`Window`] with which it is 8 /// associated. 9 /// 10 /// Please see [`ContextWrapper<T, Window>`]. 11 /// 12 /// # Example 13 /// 14 /// ```no_run 15 /// # fn main() { 16 /// let mut el = glutin::event_loop::EventLoop::new(); 17 /// let wb = glutin::window::WindowBuilder::new(); 18 /// let windowed_context = glutin::ContextBuilder::new() 19 /// .build_windowed(wb, &el) 20 /// .unwrap(); 21 /// 22 /// let windowed_context = unsafe { windowed_context.make_current().unwrap() }; 23 /// # } 24 /// ``` 25 /// 26 /// [`ContextWrapper<T, Window>`]: struct.ContextWrapper.html 27 /// [`Window`]: struct.Window.html 28 /// [`Context`]: struct.Context.html 29 pub type WindowedContext<T> = ContextWrapper<T, Window>; 30 31 /// Represents an OpenGL [`Context`] which has an underlying window that is 32 /// stored separately. 33 /// 34 /// This type can only be created via one of three ways: 35 /// 36 /// * [`platform::unix::RawContextExt`] 37 /// * [`platform::windows::RawContextExt`] 38 /// * [`WindowedContext<T>::split`] 39 /// 40 /// Please see [`ContextWrapper<T, ()>`]. 41 /// 42 /// [`ContextWrapper<T, ()>`]: struct.ContextWrapper.html 43 /// [`WindowedContext<T>::split`]: type.WindowedContext.html#method.split 44 /// [`Context`]: struct.Context.html 45 #[cfg_attr( 46 target_os = "windows", 47 doc = "\ 48 [`platform::windows::RawContextExt`]: os/windows/enum.RawHandle.html 49 " 50 )] 51 #[cfg_attr( 52 not(target_os = "windows",), 53 doc = "\ 54 [`platform::windows::RawContextExt`]: os/index.html 55 " 56 )] 57 #[cfg_attr( 58 not(any( 59 target_os = "linux", 60 target_os = "dragonfly", 61 target_os = "freebsd", 62 target_os = "netbsd", 63 target_os = "openbsd", 64 )), 65 doc = "\ 66 [`platform::unix::RawContextExt`]: os/index.html 67 " 68 )] 69 #[cfg_attr( 70 any( 71 target_os = "linux", 72 target_os = "dragonfly", 73 target_os = "freebsd", 74 target_os = "netbsd", 75 target_os = "openbsd", 76 ), 77 doc = "\ 78 [`platform::unix::RawContextExt`]: os/unix/enum.RawHandle.html 79 " 80 )] 81 pub type RawContext<T> = ContextWrapper<T, ()>; 82 83 /// A context which has an underlying window, which may or may not be stored 84 /// separately. 85 /// 86 /// If the window is stored separately, it is a [`RawContext<T>`]. Otherwise, 87 /// it is a [`WindowedContext<T>`]. 88 /// 89 /// [`WindowedContext<T>`]: type.WindowedContext.html 90 /// [`RawContext<T>`]: type.RawContext.html 91 /// [`Context`]: struct.Context.html 92 #[derive(Debug)] 93 pub struct ContextWrapper<T: ContextCurrentState, W> { 94 pub(crate) context: Context<T>, 95 pub(crate) window: W, 96 } 97 98 impl<T: ContextCurrentState> WindowedContext<T> { 99 /// Borrow the inner `W`. window(&self) -> &Window100 pub fn window(&self) -> &Window { 101 &self.window 102 } 103 104 /// Split the [`Window`] apart from the OpenGL [`Context`]. Should only be 105 /// used when intending to transfer the [`RawContext<T>`] to another 106 /// thread. 107 /// 108 /// Unsaftey: 109 /// - The OpenGL [`Context`] must be dropped before the [`Window`]. 110 /// 111 /// [`RawContext<T>`]: type.RawContext.html 112 /// [`Window`]: struct.Window.html 113 /// [`Context`]: struct.Context.html split(self) -> (RawContext<T>, Window)114 pub unsafe fn split(self) -> (RawContext<T>, Window) { 115 ( 116 RawContext { 117 context: self.context, 118 window: (), 119 }, 120 self.window, 121 ) 122 } 123 } 124 125 impl<W> ContextWrapper<PossiblyCurrent, W> { 126 /// Swaps the buffers in case of double or triple buffering. 127 /// 128 /// You should call this function every time you have finished rendering, or 129 /// the image may not be displayed on the screen. 130 /// 131 /// **Warning**: if you enabled vsync, this function will block until the 132 /// next time the screen is refreshed. However drivers can choose to 133 /// override your vsync settings, which means that you can't know in 134 /// advance whether `swap_buffers` will block or not. swap_buffers(&self) -> Result<(), ContextError>135 pub fn swap_buffers(&self) -> Result<(), ContextError> { 136 self.context.context.swap_buffers() 137 } 138 139 /// Swaps the buffers in case of double or triple buffering using specified 140 /// damage rects. 141 /// 142 /// You should call this function every time you have finished rendering, or 143 /// the image may not be displayed on the screen. 144 /// 145 /// **Warning**: if you enabled vsync, this function will block until the 146 /// next time the screen is refreshed. However drivers can choose to 147 /// override your vsync settings, which means that you can't know in 148 /// advance whether `swap_buffers` will block or not. swap_buffers_with_damage( &self, rects: &[Rect], ) -> Result<(), ContextError>149 pub fn swap_buffers_with_damage( 150 &self, 151 rects: &[Rect], 152 ) -> Result<(), ContextError> { 153 self.context.context.swap_buffers_with_damage(rects) 154 } 155 156 /// Returns whether or not swap_buffer_with_damage is available. If this 157 /// function returns false, any call to swap_buffers_with_damage will 158 /// return an error. swap_buffers_with_damage_supported(&self) -> bool159 pub fn swap_buffers_with_damage_supported(&self) -> bool { 160 self.context.context.swap_buffers_with_damage_supported() 161 } 162 163 /// Returns the pixel format of the main framebuffer of the context. get_pixel_format(&self) -> PixelFormat164 pub fn get_pixel_format(&self) -> PixelFormat { 165 self.context.context.get_pixel_format() 166 } 167 168 /// Resize the context. 169 /// 170 /// Some platforms (macOS, Wayland) require being manually updated when 171 /// their window or surface is resized. 172 /// 173 /// The easiest way of doing this is to take every [`Resized`] window event 174 /// that is received with a [`LogicalSize`] and convert it to a 175 /// [`PhysicalSize`] and pass it into this function. 176 /// 177 /// [`LogicalSize`]: dpi/struct.LogicalSize.html 178 /// [`PhysicalSize`]: dpi/struct.PhysicalSize.html 179 /// [`Resized`]: event/enum.WindowEvent.html#variant.Resized resize(&self, size: dpi::PhysicalSize<u32>)180 pub fn resize(&self, size: dpi::PhysicalSize<u32>) { 181 let (width, height) = size.into(); 182 self.context.context.resize(width, height); 183 } 184 } 185 186 impl<T: ContextCurrentState, W> ContextWrapper<T, W> { 187 /// Borrow the inner GL [`Context`]. 188 /// 189 /// [`Context`]: struct.Context.html context(&self) -> &Context<T>190 pub fn context(&self) -> &Context<T> { 191 &self.context 192 } 193 194 /// Sets this context as the current context. The previously current context 195 /// (if any) is no longer current. 196 /// 197 /// A failed call to `make_current` might make this, or no context 198 /// current. It could also keep the previous context current. What happens 199 /// varies by platform and error. 200 /// 201 /// To attempt to recover and get back into a know state, either: 202 /// 203 /// * attempt to use [`is_current`] to find the new current context; or 204 /// * call [`make_not_current`] on both the previously 205 /// current context and this context. 206 /// 207 /// # An higher level overview. 208 /// 209 /// In OpenGl, only a single context can be current in a thread at a time. 210 /// Making a new context current will make the old one not current. 211 /// Contexts can only be sent to different threads if they are not current. 212 /// 213 /// If you call `make_current` on some context, you should call 214 /// [`treat_as_not_current`] as soon as possible on the previously current 215 /// context. 216 /// 217 /// If you wish to move a currently current context to a different thread, 218 /// you should do one of two options: 219 /// 220 /// * Call `make_current` on another context, then call 221 /// [`treat_as_not_current`] on this context. 222 /// * Call [`make_not_current`] on this context. 223 /// 224 /// If you are aware of what context you intend to make current next, it is 225 /// preferable for performance reasons to call `make_current` on that 226 /// context, then [`treat_as_not_current`] on this context. 227 /// 228 /// If you are not aware of what context you intend to make current next, 229 /// consider waiting until you do. If you need this context not current 230 /// immediately (e.g. to transfer it to another thread), then call 231 /// [`make_not_current`] on this context. 232 /// 233 /// Please avoid calling [`make_not_current`] on one context only to call 234 /// `make_current` on another context before and/or after. This hurts 235 /// performance by requiring glutin to: 236 /// 237 /// * Check if this context is current; then 238 /// * If it is, change the current context from this context to none; then 239 /// * Change the current context from none to the new context. 240 /// 241 /// Instead prefer the method we mentioned above with `make_current` and 242 /// [`treat_as_not_current`]. 243 /// 244 /// [`make_not_current`]: struct.ContextWrapper.html#method.make_not_current 245 /// [`treat_as_not_current`]: 246 /// struct.ContextWrapper.html#method.treat_as_not_current 247 /// [`is_current`]: struct.ContextWrapper.html#method.is_current make_current( self, ) -> Result<ContextWrapper<PossiblyCurrent, W>, (Self, ContextError)>248 pub unsafe fn make_current( 249 self, 250 ) -> Result<ContextWrapper<PossiblyCurrent, W>, (Self, ContextError)> { 251 let window = self.window; 252 match self.context.make_current() { 253 Ok(context) => Ok(ContextWrapper { window, context }), 254 Err((context, err)) => { 255 Err((ContextWrapper { window, context }, err)) 256 } 257 } 258 } 259 260 /// If this context is current, makes this context not current. If this 261 /// context is not current however, this function does nothing. 262 /// 263 /// Please see [`make_current`]. 264 /// 265 /// [`make_current`]: struct.ContextWrapper.html#method.make_current make_not_current( self, ) -> Result<ContextWrapper<NotCurrent, W>, (Self, ContextError)>266 pub unsafe fn make_not_current( 267 self, 268 ) -> Result<ContextWrapper<NotCurrent, W>, (Self, ContextError)> { 269 let window = self.window; 270 match self.context.make_not_current() { 271 Ok(context) => Ok(ContextWrapper { window, context }), 272 Err((context, err)) => { 273 Err((ContextWrapper { window, context }, err)) 274 } 275 } 276 } 277 278 /// Treats this context as not current, even if it is current. We do no 279 /// checks to confirm that this is actually case. 280 /// 281 /// If unsure whether or not this context is current, please use 282 /// [`make_not_current`] which will do nothing if this context is not 283 /// current. 284 /// 285 /// Please see [`make_current`]. 286 /// 287 /// [`make_not_current`]: struct.ContextWrapper.html#method.make_not_current 288 /// [`make_current`]: struct.ContextWrapper.html#method.make_current treat_as_not_current(self) -> ContextWrapper<NotCurrent, W>289 pub unsafe fn treat_as_not_current(self) -> ContextWrapper<NotCurrent, W> { 290 ContextWrapper { 291 context: self.context.treat_as_not_current(), 292 window: self.window, 293 } 294 } 295 296 /// Treats this context as current, even if it is not current. We do no 297 /// checks to confirm that this is actually case. 298 /// 299 /// This function should only be used if you intend to track context 300 /// currency without the limited aid of glutin, and you wish to store 301 /// all the [`Context`]s as [`NotCurrent`]. 302 /// 303 /// Please see [`make_current`] for the prefered method of handling context 304 /// currency. 305 /// 306 /// [`make_current`]: struct.ContextWrapper.html#method.make_current 307 /// [`NotCurrent`]: enum.NotCurrent.html 308 /// [`Context`]: struct.Context.html treat_as_current(self) -> ContextWrapper<PossiblyCurrent, W>309 pub unsafe fn treat_as_current(self) -> ContextWrapper<PossiblyCurrent, W> { 310 ContextWrapper { 311 context: self.context.treat_as_current(), 312 window: self.window, 313 } 314 } 315 316 /// Returns true if this context is the current one in this thread. is_current(&self) -> bool317 pub fn is_current(&self) -> bool { 318 self.context.is_current() 319 } 320 321 /// Returns the OpenGL API being used. get_api(&self) -> Api322 pub fn get_api(&self) -> Api { 323 self.context.get_api() 324 } 325 } 326 327 impl<W> ContextWrapper<PossiblyCurrent, W> { 328 /// Returns the address of an OpenGL function. 329 #[inline] get_proc_address(&self, addr: &str) -> *const core::ffi::c_void330 pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void { 331 self.context.get_proc_address(addr) 332 } 333 } 334 335 impl<T: ContextCurrentState, W> std::ops::Deref for ContextWrapper<T, W> { 336 type Target = Context<T>; deref(&self) -> &Self::Target337 fn deref(&self) -> &Self::Target { 338 &self.context 339 } 340 } 341 342 impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> { 343 /// Builds the given window along with the associated GL context, returning 344 /// the pair as a [`WindowedContext<T>`]. 345 /// 346 /// Errors can occur in two scenarios: 347 /// - If the window could not be created (via permission denied, 348 /// incompatible system, out of memory, etc.). This should be very rare. 349 /// - If the OpenGL [`Context`] could not be created. This generally 350 /// happens 351 /// because the underlying platform doesn't support a requested feature. 352 /// 353 /// [`WindowedContext<T>`]: type.WindowedContext.html 354 /// [`Context`]: struct.Context.html build_windowed<TE>( self, wb: WindowBuilder, el: &EventLoopWindowTarget<TE>, ) -> Result<WindowedContext<NotCurrent>, CreationError>355 pub fn build_windowed<TE>( 356 self, 357 wb: WindowBuilder, 358 el: &EventLoopWindowTarget<TE>, 359 ) -> Result<WindowedContext<NotCurrent>, CreationError> { 360 let ContextBuilder { pf_reqs, gl_attr } = self; 361 let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); 362 platform_impl::Context::new_windowed(wb, el, &pf_reqs, &gl_attr).map( 363 |(window, context)| WindowedContext { 364 window, 365 context: Context { 366 context, 367 phantom: PhantomData, 368 }, 369 }, 370 ) 371 } 372 } 373