Struct mini_gl_fb::breakout::GlutinBreakout[][src]

pub struct GlutinBreakout {
    pub context: WindowedContext<PossiblyCurrent>,
    pub fb: Framebuffer,
}

GlutinBreakout is useful when you are growing out of the basic input methods and synchronous nature of MiniGlFb, since it's more powerful than the the higher-level abstrations. You can obtain it by calling MiniGlFb::glutin_breakout().

Usage for multiple windows

The basic idea for managing multiple windows is to check each incoming event to determine which window it's for. In order to draw to multiple windows individually, you have to switch the context using make_current before updating the window.

Here's a basic implementation (there's a lot of boilerplate because we're not using the MiniGlFb API - it's closer to using winit directly):

use mini_gl_fb::GlutinBreakout;
use mini_gl_fb::glutin::window::{Window, WindowId};
use mini_gl_fb::glutin::event::{Event, WindowEvent, KeyboardInput, VirtualKeyCode, ElementState};
use mini_gl_fb::glutin::event_loop::{EventLoop, ControlFlow};
use mini_gl_fb::config;

struct TrackedWindow {
    pub breakout: GlutinBreakout,
    pub background: [u8; 4]
}

impl TrackedWindow {
    fn window(&self) -> &Window { self.breakout.context.window() }
    fn matches_id(&self, id: WindowId) -> bool { id == self.breakout.context.window().id() }

    pub fn handle_event(&mut self, event: &Event<()>) -> bool {
        match event {
            Event::WindowEvent { window_id: id, event, .. } if self.matches_id(*id) => {
                match event {
                    WindowEvent::CloseRequested |
                    WindowEvent::KeyboardInput {
                        input: KeyboardInput {
                            virtual_keycode: Some(VirtualKeyCode::Escape),
                            state: ElementState::Pressed,
                            ..
                        },
                        ..
                    } => return false,
                    WindowEvent::Resized(size) => {
                        self.breakout.fb.resize_viewport(size.width, size.height);
                        let size = size.to_logical(self.window().scale_factor());
                        self.breakout.fb.resize_buffer(size.width, size.height);
                    }
                    _ => {
                        // do other stuff?
                    }
                }
            }
            Event::RedrawRequested(id) if self.matches_id(*id) => {
                // If you don't do this, OpenGL will get confused and only draw to one window.
                unsafe { self.breakout.make_current().unwrap(); }

                let size = self.window().inner_size().to_logical::<f64>(self.window().scale_factor());

                // Unfortunately the performance of this is abysmal. Usually you should cache
                // your buffer and only update it when needed or when the window is resized.
                let pixels = size.width.floor() as usize * size.height.floor() as usize;
                self.breakout.fb.update_buffer(&vec![self.background; pixels]);
                self.breakout.context.swap_buffers();
            }
            _ => {}
        }

        true
    }
}

fn main() {
    let event_loop = EventLoop::new();
    let mut windows: Vec<Option<TrackedWindow>> = vec![];

    let config = config! {
        resizable: true
    };

    windows.push(Some(TrackedWindow {
        breakout: mini_gl_fb::get_fancy(config.clone(), &event_loop).glutin_breakout(),
        background: [224u8, 66, 26, 255]
    }));

    windows.push(Some(TrackedWindow {
        breakout: mini_gl_fb::get_fancy(config.clone(), &event_loop).glutin_breakout(),
        background: [26u8, 155, 224, 255]
    }));

    // run event loop
    event_loop.run(move |event, _, flow| {
        *flow = ControlFlow::Wait;

        for option in &mut windows {
            if let Some(window) = option {
                if !window.handle_event(&event) {
                    option.take();
                }
            }
        }

        windows.retain(Option::is_some);

        if windows.is_empty() {
            *flow = ControlFlow::Exit;
        }
    })
}

It's hard to come up with a generalized, flexible implementation of this, especially if you need to open more windows based on user input, or run tasks in other threads, etc. Basically, it's open for you to play with, but it's not functionality that MGlFb wants to include first-class just yet.

Fields

context: WindowedContext<PossiblyCurrent>

Contains the OpenGL context and its associated window. This is a glutin struct; go see their documentation on WindowedContext for more information.

fb: Framebuffer

Contains the Framebuffer for that context. Consult its documentation for information on how to use it.

Implementations

impl GlutinBreakout[src]

pub unsafe fn make_current(&mut self) -> Result<(), ContextError>[src]

Sets the current thread's OpenGL context to the one contained in this breakout.

Historically, MGlFb did not support multiple windows. It owned its own event loop and you weren't allowed to use the library with your own. However, as of version 0.8, you are now expected to bring your own event loop to all functions that involve one. This means that multiple windows are very possible, and even supported, as long as you're willing to route events yourself... and manage all the OpenGL contexts.

The problem with managing multiple OpenGL contexts from one thread is that the "current" context is set per-thread. That means you basically have to switch through them really quickly if you want to update multiple windows in "parallel". But how do you switch?

Glutin has you partially covered on this one - it has make_current. However, that method takes self and emits a new WindowedContext, and you can't really move self into that function without unsafe code.

Here is an unsafe function containing code that makes the context current, in-place. That way, you can switch contexts in one line of code, and focus on other stuff.

Usage

event_loop.run(move |event, _, flow| {
    match event {
        // ...
        Event::RedrawRequested(..) => {
            unsafe { breakout.make_current().unwrap(); }
            // ...
            breakout.fb.update_buffer(&your_buffer_here);
            breakout.context.swap_buffers();
        }
        // ...
    }
})

Trait Implementations

impl Debug for GlutinBreakout[src]

Auto Trait Implementations

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.