azul_glutin/lib.rs
1//! The purpose of this library is to provide an OpenGL [`Context`] on as many
2//! platforms as possible.
3//!
4//! # Building a [`WindowedContext<T>`]
5//!
6//! A [`WindowedContext<T>`] is composed of a [`Window`] and an OpenGL
7//! [`Context`].
8//!
9//! Due to some operating-system-specific quirks, glutin prefers control over
10//! the order of creation of the [`Context`] and [`Window`]. Here is an example
11//! of building a [`WindowedContext<T>`]:
12//!
13//! ```no_run
14//! # fn main() {
15//! let el = glutin::event_loop::EventLoop::new();
16//! let wb = glutin::window::WindowBuilder::new()
17//! .with_title("Hello world!")
18//! .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0));
19//! let windowed_context = glutin::ContextBuilder::new()
20//! .build_windowed(wb, &el)
21//! .unwrap();
22//! # }
23//! ```
24//!
25//! You can, of course, create a [`RawContext<T>`] separately from an existing
26//! window, however that may result in an suboptimal configuration of the window
27//! on some platforms. In that case use the unsafe platform-specific
28//! [`RawContextExt`] available on unix operating systems and Windows.
29//!
30//! You can also produce headless [`Context`]s via the
31//! [`ContextBuilder::build_headless`] function.
32//!
33//! [`Window`]: window/struct.Window.html
34//! [`Context`]: struct.Context.html
35//! [`WindowedContext<T>`]: type.WindowedContext.html
36//! [`RawContext<T>`]: type.RawContext.html
37#![cfg_attr(
38 target_os = "windows",
39 doc = "\
40[`RawContextExt`]: os/windows/trait.RawContextExt.html
41"
42)]
43#![cfg_attr(
44 not(any(
45 target_os = "linux",
46 target_os = "dragonfly",
47 target_os = "freebsd",
48 target_os = "netbsd",
49 target_os = "windows",
50 target_os = "openbsd",
51 )),
52 doc = "\
53[`RawContextExt`]: os/index.html
54"
55)]
56#![cfg_attr(
57 any(
58 target_os = "linux",
59 target_os = "dragonfly",
60 target_os = "freebsd",
61 target_os = "netbsd",
62 target_os = "openbsd",
63 ),
64 doc = "\
65[`RawContextExt`]: os/unix/trait.RawContextExt.html
66"
67)]
68#![deny(
69 missing_debug_implementations,
70 //missing_docs,
71)]
72
73#[cfg(any(
74 target_os = "windows",
75 target_os = "linux",
76 target_os = "android",
77 target_os = "dragonfly",
78 target_os = "freebsd",
79 target_os = "netbsd",
80 target_os = "openbsd",
81))]
82#[macro_use]
83extern crate lazy_static;
84#[cfg(any(target_os = "macos", target_os = "ios"))]
85#[macro_use]
86extern crate objc;
87#[cfg(any(
88 target_os = "linux",
89 target_os = "dragonfly",
90 target_os = "freebsd",
91 target_os = "netbsd",
92 target_os = "openbsd",
93))]
94#[macro_use]
95extern crate log;
96
97pub mod platform;
98
99mod api;
100mod context;
101mod platform_impl;
102mod windowed;
103
104pub use crate::context::*;
105pub use crate::windowed::*;
106pub use winit::*;
107
108use winit::error::OsError;
109
110use std::io;
111
112/// An object that allows you to build [`Context`]s, [`RawContext<T>`]s and
113/// [`WindowedContext<T>`]s.
114///
115/// One notable limitation of the Wayland backend when it comes to shared
116/// [`Context`]s is that both contexts must use the same events loop.
117///
118/// [`Context`]: struct.Context.html
119/// [`WindowedContext<T>`]: type.WindowedContext.html
120/// [`RawContext<T>`]: type.RawContext.html
121#[derive(Debug, Clone)]
122pub struct ContextBuilder<'a, T: ContextCurrentState> {
123 /// The attributes to use to create the context.
124 pub gl_attr: GlAttributes<&'a Context<T>>,
125 /// The pixel format requirements
126 pub pf_reqs: PixelFormatRequirements,
127}
128
129impl<'a> ContextBuilder<'a, NotCurrent> {
130 /// Initializes a new `ContextBuilder` with default values.
131 pub fn new() -> Self {
132 ContextBuilder {
133 pf_reqs: std::default::Default::default(),
134 gl_attr: std::default::Default::default(),
135 }
136 }
137}
138
139impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> {
140 /// Sets how the backend should choose the OpenGL API and version.
141 #[inline]
142 pub fn with_gl(mut self, request: GlRequest) -> Self {
143 self.gl_attr.version = request;
144 self
145 }
146
147 /// Sets the desired OpenGL [`Context`] profile.
148 ///
149 /// [`Context`]: struct.Context.html
150 #[inline]
151 pub fn with_gl_profile(mut self, profile: GlProfile) -> Self {
152 self.gl_attr.profile = Some(profile);
153 self
154 }
155
156 /// Sets the *debug* flag for the OpenGL [`Context`].
157 ///
158 /// The default value for this flag is `cfg!(debug_assertions)`, which means
159 /// that it's enabled when you run `cargo build` and disabled when you run
160 /// `cargo build --release`.
161 ///
162 /// [`Context`]: struct.Context.html
163 #[inline]
164 pub fn with_gl_debug_flag(mut self, flag: bool) -> Self {
165 self.gl_attr.debug = flag;
166 self
167 }
168
169 /// Sets the robustness of the OpenGL [`Context`]. See the docs of
170 /// [`Robustness`].
171 ///
172 /// [`Context`]: struct.Context.html
173 /// [`Robustness`]: enum.Robustness.html
174 #[inline]
175 pub fn with_gl_robustness(mut self, robustness: Robustness) -> Self {
176 self.gl_attr.robustness = robustness;
177 self
178 }
179
180 /// Requests that the window has vsync enabled.
181 ///
182 /// By default, vsync is not enabled.
183 #[inline]
184 pub fn with_vsync(mut self, vsync: bool) -> Self {
185 self.gl_attr.vsync = vsync;
186 self
187 }
188
189 /// Share the display lists with the given [`Context`].
190 ///
191 /// [`Context`]: struct.Context.html
192 #[inline]
193 pub fn with_shared_lists<T2: ContextCurrentState>(
194 self,
195 other: &'a Context<T2>,
196 ) -> ContextBuilder<'a, T2> {
197 ContextBuilder { gl_attr: self.gl_attr.set_sharing(Some(other)), pf_reqs: self.pf_reqs }
198 }
199
200 /// Sets the multisampling level to request. A value of `0` indicates that
201 /// multisampling must not be enabled.
202 ///
203 /// # Panic
204 ///
205 /// Will panic if `samples` is not a power of two.
206 #[inline]
207 pub fn with_multisampling(mut self, samples: u16) -> Self {
208 self.pf_reqs.multisampling = match samples {
209 0 => None,
210 _ => {
211 assert!(samples.is_power_of_two());
212 Some(samples)
213 }
214 };
215 self
216 }
217
218 /// Sets the number of bits in the depth buffer.
219 #[inline]
220 pub fn with_depth_buffer(mut self, bits: u8) -> Self {
221 self.pf_reqs.depth_bits = Some(bits);
222 self
223 }
224
225 /// Sets the number of bits in the stencil buffer.
226 #[inline]
227 pub fn with_stencil_buffer(mut self, bits: u8) -> Self {
228 self.pf_reqs.stencil_bits = Some(bits);
229 self
230 }
231
232 /// Sets the number of bits in the color buffer.
233 #[inline]
234 pub fn with_pixel_format(mut self, color_bits: u8, alpha_bits: u8) -> Self {
235 self.pf_reqs.color_bits = Some(color_bits);
236 self.pf_reqs.alpha_bits = Some(alpha_bits);
237 self
238 }
239
240 /// Request the backend to be stereoscopic.
241 #[inline]
242 pub fn with_stereoscopy(mut self) -> Self {
243 self.pf_reqs.stereoscopy = true;
244 self
245 }
246
247 /// Sets whether sRGB should be enabled on the window.
248 ///
249 /// The default value is `true`.
250 #[inline]
251 pub fn with_srgb(mut self, srgb_enabled: bool) -> Self {
252 self.pf_reqs.srgb = srgb_enabled;
253 self
254 }
255
256 /// Sets whether double buffering should be enabled.
257 ///
258 /// The default value is `None`.
259 ///
260 /// ## Platform-specific
261 ///
262 /// This option will be taken into account on the following platforms:
263 ///
264 /// * MacOS
265 /// * Unix operating systems using GLX with X
266 /// * Windows using WGL
267 #[inline]
268 pub fn with_double_buffer(mut self, double_buffer: Option<bool>) -> Self {
269 self.pf_reqs.double_buffer = double_buffer;
270 self
271 }
272
273 /// Sets whether hardware acceleration is required.
274 ///
275 /// The default value is `Some(true)`
276 ///
277 /// ## Platform-specific
278 ///
279 /// This option will be taken into account on the following platforms:
280 ///
281 /// * MacOS
282 /// * Unix operating systems using EGL with either X or Wayland
283 /// * Windows using EGL or WGL
284 /// * Android using EGL
285 #[inline]
286 pub fn with_hardware_acceleration(mut self, acceleration: Option<bool>) -> Self {
287 self.pf_reqs.hardware_accelerated = acceleration;
288 self
289 }
290}
291
292/// Error that can happen while creating a window or a headless renderer.
293#[derive(Debug)]
294pub enum CreationError {
295 OsError(String),
296 NotSupported(String),
297 NoBackendAvailable(Box<dyn std::error::Error + Send + Sync>),
298 RobustnessNotSupported,
299 OpenGlVersionNotSupported,
300 NoAvailablePixelFormat,
301 PlatformSpecific(String),
302 Window(OsError),
303 /// We received multiple errors, instead of one.
304 CreationErrors(Vec<Box<CreationError>>),
305}
306
307impl CreationError {
308 #[cfg(any(
309 target_os = "linux",
310 target_os = "dragonfly",
311 target_os = "freebsd",
312 target_os = "netbsd",
313 target_os = "openbsd",
314 ))]
315 #[cfg(feature = "x11")]
316 pub(crate) fn append(self, err: CreationError) -> Self {
317 match self {
318 CreationError::CreationErrors(mut errs) => {
319 errs.push(Box::new(err));
320 CreationError::CreationErrors(errs)
321 }
322 _ => CreationError::CreationErrors(vec![Box::new(err), Box::new(self)]),
323 }
324 }
325
326 fn to_string(&self) -> &str {
327 match *self {
328 CreationError::OsError(ref text) | CreationError::NotSupported(ref text) => &text,
329 CreationError::NoBackendAvailable(_) => "No backend is available",
330 CreationError::RobustnessNotSupported => {
331 "You requested robustness, but it is not supported."
332 }
333 CreationError::OpenGlVersionNotSupported => {
334 "The requested OpenGL version is not supported."
335 }
336 CreationError::NoAvailablePixelFormat => {
337 "Couldn't find any pixel format that matches the criteria."
338 }
339 CreationError::PlatformSpecific(ref text) => &text,
340 CreationError::Window(ref err) => std::error::Error::description(err),
341 CreationError::CreationErrors(_) => "Received multiple errors.",
342 }
343 }
344}
345
346impl std::fmt::Display for CreationError {
347 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
348 formatter.write_str(self.to_string())?;
349
350 if let CreationError::CreationErrors(ref es) = *self {
351 use std::fmt::Debug;
352 write!(formatter, " Errors: `")?;
353 es.fmt(formatter)?;
354 write!(formatter, "`")?;
355 }
356
357 if let Some(err) = std::error::Error::source(self) {
358 write!(formatter, ": {}", err)?;
359 }
360 Ok(())
361 }
362}
363
364impl std::error::Error for CreationError {
365 fn description(&self) -> &str {
366 self.to_string()
367 }
368
369 fn cause(&self) -> Option<&dyn std::error::Error> {
370 match *self {
371 CreationError::NoBackendAvailable(ref err) => Some(&**err),
372 CreationError::Window(ref err) => Some(err),
373 _ => None,
374 }
375 }
376}
377
378impl From<OsError> for CreationError {
379 fn from(err: OsError) -> Self {
380 CreationError::Window(err)
381 }
382}
383
384/// Error that can happen when manipulating an OpenGL [`Context`].
385///
386/// [`Context`]: struct.Context.html
387#[derive(Debug)]
388pub enum ContextError {
389 /// General platform error.
390 OsError(String),
391 IoError(io::Error),
392 ContextLost,
393 FunctionUnavailable,
394}
395
396impl ContextError {
397 fn to_string(&self) -> &str {
398 use std::error::Error;
399 match *self {
400 ContextError::OsError(ref string) => string,
401 ContextError::IoError(ref err) => err.description(),
402 ContextError::ContextLost => "Context lost",
403 ContextError::FunctionUnavailable => "Function unavailable",
404 }
405 }
406}
407
408impl std::fmt::Display for ContextError {
409 fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
410 formatter.write_str(self.to_string())
411 }
412}
413
414impl std::error::Error for ContextError {
415 fn description(&self) -> &str {
416 self.to_string()
417 }
418}
419
420/// All APIs related to OpenGL that you can possibly get while using glutin.
421#[derive(Debug, Clone, Copy, PartialEq, Eq)]
422pub enum Api {
423 /// The classical OpenGL. Available on Windows, Unix operating systems,
424 /// OS/X.
425 OpenGl,
426 /// OpenGL embedded system. Available on Unix operating systems, Android.
427 OpenGlEs,
428 /// OpenGL for the web. Very similar to OpenGL ES.
429 WebGl,
430}
431
432/// Describes the requested OpenGL [`Context`] profiles.
433///
434/// [`Context`]: struct.Context.html
435#[derive(Debug, Clone, Copy, PartialEq, Eq)]
436pub enum GlProfile {
437 /// Include all the immediate more functions and definitions.
438 Compatibility,
439 /// Include all the future-compatible functions and definitions.
440 Core,
441}
442
443/// Describes the OpenGL API and version that are being requested when a context
444/// is created.
445#[derive(Debug, Copy, Clone)]
446pub enum GlRequest {
447 /// Request the latest version of the "best" API of this platform.
448 ///
449 /// On desktop, will try OpenGL.
450 Latest,
451
452 /// Request a specific version of a specific API.
453 ///
454 /// Example: `GlRequest::Specific(Api::OpenGl, (3, 3))`.
455 Specific(Api, (u8, u8)),
456
457 /// If OpenGL is available, create an OpenGL [`Context`] with the specified
458 /// `opengl_version`. Else if OpenGL ES or WebGL is available, create a
459 /// context with the specified `opengles_version`.
460 ///
461 /// [`Context`]: struct.Context.html
462 GlThenGles {
463 /// The version to use for OpenGL.
464 opengl_version: (u8, u8),
465 /// The version to use for OpenGL ES.
466 opengles_version: (u8, u8),
467 },
468}
469
470impl GlRequest {
471 /// Extract the desktop GL version, if any.
472 pub fn to_gl_version(&self) -> Option<(u8, u8)> {
473 match self {
474 &GlRequest::Specific(Api::OpenGl, opengl_version) => Some(opengl_version),
475 &GlRequest::GlThenGles { opengl_version, .. } => Some(opengl_version),
476 _ => None,
477 }
478 }
479}
480
481/// The minimum core profile GL context. Useful for getting the minimum
482/// required GL version while still running on OSX, which often forbids
483/// the compatibility profile features.
484pub static GL_CORE: GlRequest = GlRequest::Specific(Api::OpenGl, (3, 2));
485
486/// Specifies the tolerance of the OpenGL [`Context`] to faults. If you accept
487/// raw OpenGL commands and/or raw shader code from an untrusted source, you
488/// should definitely care about this.
489///
490/// [`Context`]: struct.Context.html
491#[derive(Debug, Copy, Clone, PartialEq, Eq)]
492pub enum Robustness {
493 /// Not everything is checked. Your application can crash if you do
494 /// something wrong with your shaders.
495 NotRobust,
496
497 /// The driver doesn't check anything. This option is very dangerous.
498 /// Please know what you're doing before using it. See the
499 /// `GL_KHR_no_error` extension.
500 ///
501 /// Since this option is purely an optimization, no error will be returned
502 /// if the backend doesn't support it. Instead it will automatically
503 /// fall back to [`NotRobust`].
504 ///
505 /// [`NotRobust`]: enum.Robustness.html#variant.NotRobust
506 NoError,
507
508 /// Everything is checked to avoid any crash. The driver will attempt to
509 /// avoid any problem, but if a problem occurs the behavior is
510 /// implementation-defined. You are just guaranteed not to get a crash.
511 RobustNoResetNotification,
512
513 /// Same as [`RobustNoResetNotification`] but the context creation doesn't
514 /// fail if it's not supported.
515 ///
516 /// [`RobustNoResetNotification`]:
517 /// enum.Robustness.html#variant.RobustNoResetNotification
518 TryRobustNoResetNotification,
519
520 /// Everything is checked to avoid any crash. If a problem occurs, the
521 /// context will enter a "context lost" state. It must then be
522 /// recreated. For the moment, glutin doesn't provide a way to recreate
523 /// a context with the same window :-/
524 RobustLoseContextOnReset,
525
526 /// Same as [`RobustLoseContextOnReset`] but the context creation doesn't
527 /// fail if it's not supported.
528 ///
529 /// [`RobustLoseContextOnReset`]:
530 /// enum.Robustness.html#variant.RobustLoseContextOnReset
531 TryRobustLoseContextOnReset,
532}
533
534/// The behavior of the driver when you change the current context.
535#[derive(Debug, Copy, Clone, PartialEq, Eq)]
536pub enum ReleaseBehavior {
537 /// Doesn't do anything. Most notably doesn't flush.
538 None,
539
540 /// Flushes the context that was previously current as if `glFlush` was
541 /// called.
542 Flush,
543}
544
545/// Describes a possible format.
546#[allow(missing_docs)]
547#[derive(Debug, Clone)]
548pub struct PixelFormat {
549 pub hardware_accelerated: bool,
550 /// The number of color bits. Does not include alpha bits.
551 pub color_bits: u8,
552 pub alpha_bits: u8,
553 pub depth_bits: u8,
554 pub stencil_bits: u8,
555 pub stereoscopy: bool,
556 pub double_buffer: bool,
557 /// `None` if multisampling is disabled, otherwise `Some(N)` where `N` is
558 /// the multisampling level.
559 pub multisampling: Option<u16>,
560 pub srgb: bool,
561}
562
563/// Describes how the backend should choose a pixel format.
564// TODO: swap method? (swap, copy)
565#[derive(Clone, Debug)]
566pub struct PixelFormatRequirements {
567 /// If true, only hardware-accelerated formats will be considered. If
568 /// false, only software renderers. `None` means "don't care". Default
569 /// is `Some(true)`.
570 pub hardware_accelerated: Option<bool>,
571
572 /// Minimum number of bits for the color buffer, excluding alpha. `None`
573 /// means "don't care". The default is `Some(24)`.
574 pub color_bits: Option<u8>,
575
576 /// If true, the color buffer must be in a floating point format. Default
577 /// is `false`.
578 ///
579 /// Using floating points allows you to write values outside of the `[0.0,
580 /// 1.0]` range.
581 pub float_color_buffer: bool,
582
583 /// Minimum number of bits for the alpha in the color buffer. `None` means
584 /// "don't care". The default is `Some(8)`.
585 pub alpha_bits: Option<u8>,
586
587 /// Minimum number of bits for the depth buffer. `None` means "don't care".
588 /// The default value is `Some(24)`.
589 pub depth_bits: Option<u8>,
590
591 /// Minimum number of stencil bits. `None` means "don't care".
592 /// The default value is `Some(8)`.
593 pub stencil_bits: Option<u8>,
594
595 /// If true, only double-buffered formats will be considered. If false,
596 /// only single-buffer formats. `None` means "don't care". The default
597 /// is `Some(true)`.
598 pub double_buffer: Option<bool>,
599
600 /// Contains the minimum number of samples per pixel in the color, depth
601 /// and stencil buffers. `None` means "don't care". Default is `None`.
602 /// A value of `Some(0)` indicates that multisampling must not be enabled.
603 pub multisampling: Option<u16>,
604
605 /// If true, only stereoscopic formats will be considered. If false, only
606 /// non-stereoscopic formats. The default is `false`.
607 pub stereoscopy: bool,
608
609 /// If true, only sRGB-capable formats will be considered. If false, don't
610 /// care. The default is `true`.
611 pub srgb: bool,
612
613 /// The behavior when changing the current context. Default is `Flush`.
614 pub release_behavior: ReleaseBehavior,
615
616 /// X11 only: set internally to insure a certain visual xid is used when
617 /// choosing the fbconfig.
618 pub(crate) x11_visual_xid: Option<std::os::raw::c_ulong>,
619}
620
621impl Default for PixelFormatRequirements {
622 #[inline]
623 fn default() -> PixelFormatRequirements {
624 PixelFormatRequirements {
625 hardware_accelerated: Some(true),
626 color_bits: Some(24),
627 float_color_buffer: false,
628 alpha_bits: Some(8),
629 depth_bits: Some(24),
630 stencil_bits: Some(8),
631 double_buffer: None,
632 multisampling: None,
633 stereoscopy: false,
634 srgb: true,
635 release_behavior: ReleaseBehavior::Flush,
636 x11_visual_xid: None,
637 }
638 }
639}
640
641/// Attributes to use when creating an OpenGL [`Context`].
642///
643/// [`Context`]: struct.Context.html
644#[derive(Clone, Debug)]
645pub struct GlAttributes<S> {
646 /// An existing context with which some OpenGL objects get shared.
647 ///
648 /// The default is `None`.
649 pub sharing: Option<S>,
650
651 /// Version to try create. See [`GlRequest`] for more infos.
652 ///
653 /// The default is [`Latest`].
654 ///
655 /// [`Latest`]: enum.GlRequest.html#variant.Latest
656 /// [`GlRequest`]: enum.GlRequest.html
657 pub version: GlRequest,
658
659 /// OpenGL profile to use.
660 ///
661 /// The default is `None`.
662 pub profile: Option<GlProfile>,
663
664 /// Whether to enable the `debug` flag of the context.
665 ///
666 /// Debug contexts are usually slower but give better error reporting.
667 ///
668 /// The default is `true` in debug mode and `false` in release mode.
669 pub debug: bool,
670
671 /// How the OpenGL [`Context`] should detect errors.
672 ///
673 /// The default is `NotRobust` because this is what is typically expected
674 /// when you create an OpenGL [`Context`]. However for safety you should
675 /// consider [`TryRobustLoseContextOnReset`].
676 ///
677 /// [`Context`]: struct.Context.html
678 /// [`TryRobustLoseContextOnReset`]:
679 /// enum.Robustness.html#variant.TryRobustLoseContextOnReset
680 pub robustness: Robustness,
681
682 /// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will
683 /// block until the screen refreshes. This is typically used to prevent
684 /// screen tearing.
685 ///
686 /// The default is `false`.
687 pub vsync: bool,
688}
689
690impl<S> GlAttributes<S> {
691 /// Turns the `sharing` parameter into another type by calling a closure.
692 #[inline]
693 pub fn map_sharing<F, T>(self, f: F) -> GlAttributes<T>
694 where
695 F: FnOnce(S) -> T,
696 {
697 GlAttributes {
698 sharing: self.sharing.map(f),
699 version: self.version,
700 profile: self.profile,
701 debug: self.debug,
702 robustness: self.robustness,
703 vsync: self.vsync,
704 }
705 }
706
707 /// Turns the `sharing` parameter into another type.
708 #[inline]
709 fn set_sharing<T>(self, sharing: Option<T>) -> GlAttributes<T> {
710 GlAttributes {
711 sharing,
712 version: self.version,
713 profile: self.profile,
714 debug: self.debug,
715 robustness: self.robustness,
716 vsync: self.vsync,
717 }
718 }
719}
720
721impl<S> Default for GlAttributes<S> {
722 #[inline]
723 fn default() -> GlAttributes<S> {
724 GlAttributes {
725 sharing: None,
726 version: GlRequest::Latest,
727 profile: None,
728 debug: cfg!(debug_assertions),
729 robustness: Robustness::NotRobust,
730 vsync: false,
731 }
732 }
733}
734
735// Rectangles to submit as buffer damage.
736#[derive(Debug, Clone, Copy, PartialEq, Eq)]
737pub struct Rect {
738 pub x: u32,
739 pub y: u32,
740 pub width: u32,
741 pub height: u32,
742}