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