<commit_before>use field_bit::FieldBit;
use std;
use x86intrin::*;

#[derive(Clone, Copy, Debug)]
pub struct FieldBit256 {
    m: m256i,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LowHigh {
    LOW,
    HIGH,
}

impl FieldBit256 {
    pub fn new(m: m256i) -> FieldBit256 {
        FieldBit256 {
            m: m,
        }
    }

    pub fn uninitialized() -> FieldBit256 {
        unsafe { std::mem::uninitialized::<FieldBit256>() }
    }

    pub fn empty() -> FieldBit256 {
        FieldBit256 {
            m: mm256_setzero_si256()
        }
    }

    pub fn from_low_high(low: FieldBit, high: FieldBit) -> FieldBit256 {
        let m = mm256_inserti128_si256(mm256_castsi128_si256(low.as_m128i()), high.as_m128i(), 1);
        FieldBit256 {
            m: m
        }
    }

    pub fn get(&self, lowhigh: LowHigh, x: usize, y: usize) -> bool {
        debug_assert!(FieldBit256::check_in_range(x, y));
        mm256_testz_si256(FieldBit256::onebit(lowhigh, x, y), self.m) == 0
    }

    pub fn get_low(&self, x: usize, y: usize) -> bool {
        self.low().get(x, y)
    }

    pub fn get_high(&self, x: usize, y: usize) -> bool {
        self.high().get(x, y)
    }

    pub fn low(&self) -> FieldBit {
        FieldBit::new(mm256_castsi256_si128(self.m))
    }

    pub fn high(&self) -> FieldBit {
        FieldBit::new(mm256_extracti128_si256(self.m, 1))
    }

    pub fn set_low(&mut self, x: usize, y: usize) {
        self.m = self.m | FieldBit256::onebit(LowHigh::LOW, x, y)
    }

    pub fn set_high(&mut self, x: usize, y: usize) {
        self.m = self.m | FieldBit256::onebit(LowHigh::HIGH, x, y)
    }

    pub fn set_all(&mut self, fb: FieldBit256) {
        self.m = self.m | fb.m
    }

    pub fn expand(&self, mask: FieldBit256) -> FieldBit256 {
        let mut seed = self.m;

        loop {
            let mut expanded = seed;
            expanded = mm256_slli_epi16(seed, 1) | expanded;
            expanded = mm256_srli_epi16(seed, 1) | expanded;
            expanded = mm256_slli_si256(seed, 2) | expanded;
            expanded = mm256_srli_si256(seed, 2) | expanded;
            expanded = mask.m & expanded;

            if mm256_testc_si256(seed, expanded) != 0 { // seed == expanded
                return FieldBit256::new(expanded);
            }
            seed = expanded;
        }
    }

    pub fn expand1(&self, mask: FieldBit256) -> FieldBit256 {
        let v1 = mm256_slli_si256(self.m, 2);
        let v2 = mm256_srli_si256(self.m, 2);
        let v3 = mm256_slli_epi16(self.m, 1);
        let v4 = mm256_srli_epi16(self.m, 1);
        FieldBit256::new(((self.m | v1) | (v2 | v3) | v4) & mask.m)
    }

    pub fn find_vanishing_bits(&self, vanishing: &mut FieldBit256) -> bool {
        let m = self.m;
        let u = mm256_srli_epi16(m, 1) & m;
        let d = mm256_slli_epi16(m, 1) & m;
        let l = mm256_slli_si256(m, 2) & m;
        let r = mm256_srli_si256(m, 2) & m;

        let ud_and = u & d;
        let lr_and = l & r;
        let ud_or = u | d;
        let lr_or = l | r;

        let twos = lr_and | ud_and | (ud_or & lr_or);
        let two_d = mm256_slli_epi16(twos, 1) & twos;
        let two_l = mm256_slli_si256(twos, 2) & twos;
        let threes = (ud_and & lr_or) | (lr_and & ud_or);
        let t = two_d | two_l | threes;

        if mm256_testz_si256(t, t) != 0 {
            *vanishing = FieldBit256::empty();
            return false;
        }

        let two_u = mm256_srli_epi16(twos, 1) & twos;
        let two_r = mm256_srli_si256(twos, 2) & twos;
        *vanishing = FieldBit256::new(t | two_u | two_r).expand1(*self);
        return true;
    }

    pub fn popcount_low_high(&self) -> (usize, usize) {
        (self.low().popcount(), self.high().popcount())
    }

    fn check_in_range(x: usize, y: usize) -> bool {
        x < 8 && y < 16
    }

    fn onebit(lowhigh: LowHigh, x: usize, y: usize) -> m256i {
        debug_assert!(FieldBit256::check_in_range(x, y));

        // TODO(mayah): Maybe we have more good solution.

        let shift = ((x << 4) | y) & 0x3F;
        let zero = mm256_setzero_si256();
        if lowhigh == LowHigh::LOW {
            if x < 4 {
                return mm256_insert_epi64(zero, 1 << shift, 0)
            } else {
                return mm256_insert_epi64(zero, 1 << shift, 1)
            }
        } else {
            if x < 4 {
                return mm256_insert_epi64(zero, 1 << shift, 2)
            } else {
                return mm256_insert_epi64(zero, 1 << shift, 3)
            }
        }
    }
}

impl std::ops::BitOr for FieldBit256 {
    type Output = FieldBit256;

    fn bitor(self, rhs: FieldBit256) -> FieldBit256 {
        FieldBit256::new(self.m | rhs.m)
    }
}

impl std::ops::BitAnd for FieldBit256 {
    type Output = FieldBit256;

    fn bitand(self, rhs: FieldBit256) -> FieldBit256 {
        FieldBit256::new(self.m & rhs.m)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use field;
    use field_bit::FieldBit;

    #[test]
    fn test_constructor() {
        let fb256 = FieldBit256::empty();

        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert!(!fb256.get(LowHigh::LOW, x, y));
                assert!(!fb256.get(LowHigh::HIGH, x, y));
            }
        }
    }

    #[test]
    fn test_from_low_high() {
        let mut low = FieldBit::empty();
        let mut high = FieldBit::empty();
        low.set(1, 3);
        low.set(4, 8);
        high.set(2, 4);
        high.set(5, 9);

        let fb256 = FieldBit256::from_low_high(low, high);
        assert!(fb256.get(LowHigh::LOW, 1, 3));
        assert!(fb256.get(LowHigh::LOW, 4, 8));
        assert!(fb256.get(LowHigh::HIGH, 2, 4));
        assert!(fb256.get(LowHigh::HIGH, 5, 9));

        assert!(!fb256.get(LowHigh::HIGH, 1, 3));
        assert!(!fb256.get(LowHigh::HIGH, 4, 8));
        assert!(!fb256.get(LowHigh::LOW, 2, 4));
        assert!(!fb256.get(LowHigh::LOW, 5, 9));

        assert_eq!(low, fb256.low());
        assert_eq!(high, fb256.high());
    }

    #[test]
    fn test_expand() {
        let mask_high = FieldBit::from_str(concat!(
            "......",
            "11..11",
            "11..11",
            "......",
            "111111"));
        let mask_low = FieldBit::from_str(concat!(
            "111111",
            ".....1",
            "111111",
            "1.....",
            "111111"));

        let expected_high = FieldBit::from_str(concat!(
            "111111"));
        let expected_low = mask_low;

        let mask = FieldBit256::from_low_high(mask_low, mask_high);

        let mut bit = FieldBit256::empty();
        bit.set_high(3, 1);
        bit.set_low(6, 1);

        let expanded = bit.expand(mask);
        assert_eq!(expected_high, expanded.high());
        assert_eq!(expected_low, expanded.low());
    }

    #[test]
    fn test_find_vanishing_bits_1() {
        let fb256 = {
            let f = FieldBit::from_str(concat!(
                ".1....",
                "11..1.",
                ".1.111",
                "1...1.",
                "11.111",
                "1...1."));
            FieldBit256::from_low_high(f, f)
        };

        let mut vanishing = FieldBit256::uninitialized();
        assert!(fb256.find_vanishing_bits(&mut vanishing));

        for x in 1 .. field::WIDTH + 1 {
            for y in 1 .. field::HEIGHT + 1 {
                assert_eq!(vanishing.get_low(x, y), fb256.get_low(x, y), "x={}, y={}", x, y);
                assert_eq!(vanishing.get_high(x, y), fb256.get_high(x, y), "x={}, y={}", x, y);
            }
        }
    }

    #[test]
    fn test_find_vanishing_bits_2() {
        let fb256 = {
            let f = FieldBit::from_str(concat!(
                ".....1",
                ".111.1",
                ".....1",
                ".1.11."));
            FieldBit256::from_low_high(f, f)
        };

        let mut vanishing = FieldBit256::uninitialized();
        assert!(!fb256.find_vanishing_bits(&mut vanishing));

        for x in 1 .. field::WIDTH + 1 {
            for y in 1 .. field::HEIGHT + 1 {
                assert!(!vanishing.get_low(x, y));
                assert!(!vanishing.get_high(x, y));
            }
        }
    }

    #[test]
    fn test_iterate_bit_with_masking() {
        let mut bf = FieldBit::empty();
        bf.set(1, 2);
        bf.set(2, 3);
        bf.set(3, 4);
        bf.set(4, 5);
        bf.set(5, 6);
        bf.set(6, 7);

        assert_eq!(6, bf.popcount());

        let mut count = 0;
        let mut iterated = FieldBit::empty();
        bf.iterate_bit_with_masking(|x: FieldBit| -> FieldBit {
            iterated.set_all(x);
            assert_eq!(1, x.popcount());
            count += 1;

            x
        });

        assert_eq!(6, count);
        assert_eq!(bf, iterated);
    }
}
<commit_msg>:shirt: Remove LowHigh from FieldBit256<commit_after>use field_bit::FieldBit;
use std;
use x86intrin::*;

#[derive(Clone, Copy, Debug)]
pub struct FieldBit256 {
    m: m256i,
}

impl FieldBit256 {
    pub fn new(m: m256i) -> FieldBit256 {
        FieldBit256 {
            m: m,
        }
    }

    pub fn uninitialized() -> FieldBit256 {
        unsafe { std::mem::uninitialized::<FieldBit256>() }
    }

    pub fn empty() -> FieldBit256 {
        FieldBit256 {
            m: mm256_setzero_si256()
        }
    }

    pub fn from_low_high(low: FieldBit, high: FieldBit) -> FieldBit256 {
        let m = mm256_inserti128_si256(mm256_castsi128_si256(low.as_m128i()), high.as_m128i(), 1);
        FieldBit256 {
            m: m
        }
    }

    #[allow(dead_code)]
    pub fn get_low(&self, x: usize, y: usize) -> bool {
        self.low().get(x, y)
    }

    #[allow(dead_code)]
    pub fn get_high(&self, x: usize, y: usize) -> bool {
        self.high().get(x, y)
    }

    pub fn low(&self) -> FieldBit {
        FieldBit::new(mm256_castsi256_si128(self.m))
    }

    pub fn high(&self) -> FieldBit {
        FieldBit::new(mm256_extracti128_si256(self.m, 1))
    }

    #[allow(dead_code)]
    pub fn set_low(&mut self, x: usize, y: usize) {
        self.m = self.m | FieldBit256::onebit_low(x, y)
    }

    #[allow(dead_code)]
    pub fn set_high(&mut self, x: usize, y: usize) {
        self.m = self.m | FieldBit256::onebit_high(x, y)
    }

    pub fn set_all(&mut self, fb: FieldBit256) {
        self.m = self.m | fb.m
    }

    #[allow(dead_code)]
    pub fn expand(&self, mask: FieldBit256) -> FieldBit256 {
        let mut seed = self.m;

        loop {
            let mut expanded = seed;
            expanded = mm256_slli_epi16(seed, 1) | expanded;
            expanded = mm256_srli_epi16(seed, 1) | expanded;
            expanded = mm256_slli_si256(seed, 2) | expanded;
            expanded = mm256_srli_si256(seed, 2) | expanded;
            expanded = mask.m & expanded;

            if mm256_testc_si256(seed, expanded) != 0 { // seed == expanded
                return FieldBit256::new(expanded);
            }
            seed = expanded;
        }
    }

    pub fn expand1(&self, mask: FieldBit256) -> FieldBit256 {
        let v1 = mm256_slli_si256(self.m, 2);
        let v2 = mm256_srli_si256(self.m, 2);
        let v3 = mm256_slli_epi16(self.m, 1);
        let v4 = mm256_srli_epi16(self.m, 1);
        FieldBit256::new(((self.m | v1) | (v2 | v3) | v4) & mask.m)
    }

    pub fn find_vanishing_bits(&self, vanishing: &mut FieldBit256) -> bool {
        let m = self.m;
        let u = mm256_srli_epi16(m, 1) & m;
        let d = mm256_slli_epi16(m, 1) & m;
        let l = mm256_slli_si256(m, 2) & m;
        let r = mm256_srli_si256(m, 2) & m;

        let ud_and = u & d;
        let lr_and = l & r;
        let ud_or = u | d;
        let lr_or = l | r;

        let twos = lr_and | ud_and | (ud_or & lr_or);
        let two_d = mm256_slli_epi16(twos, 1) & twos;
        let two_l = mm256_slli_si256(twos, 2) & twos;
        let threes = (ud_and & lr_or) | (lr_and & ud_or);
        let t = two_d | two_l | threes;

        if mm256_testz_si256(t, t) != 0 {
            *vanishing = FieldBit256::empty();
            return false;
        }

        let two_u = mm256_srli_epi16(twos, 1) & twos;
        let two_r = mm256_srli_si256(twos, 2) & twos;
        *vanishing = FieldBit256::new(t | two_u | two_r).expand1(*self);
        return true;
    }

    pub fn popcount_low_high(&self) -> (usize, usize) {
        (self.low().popcount(), self.high().popcount())
    }

    fn check_in_range(x: usize, y: usize) -> bool {
        x < 8 && y < 16
    }

    fn onebit_low(x: usize, y: usize) -> m256i {
        debug_assert!(FieldBit256::check_in_range(x, y));

        let shift = ((x << 4) | y) & 0x3F;
        let zero = mm256_setzero_si256();
        if x < 4 {
            return mm256_insert_epi64(zero, 1 << shift, 0)
        } else {
            return mm256_insert_epi64(zero, 1 << shift, 1)
        }
    }

    fn onebit_high(x: usize, y: usize) -> m256i {
        debug_assert!(FieldBit256::check_in_range(x, y));

        let shift = ((x << 4) | y) & 0x3F;
        let zero = mm256_setzero_si256();
        if x < 4 {
            return mm256_insert_epi64(zero, 1 << shift, 2)
        } else {
            return mm256_insert_epi64(zero, 1 << shift, 3)
        }
    }
}

impl std::ops::BitOr for FieldBit256 {
    type Output = FieldBit256;

    fn bitor(self, rhs: FieldBit256) -> FieldBit256 {
        FieldBit256::new(self.m | rhs.m)
    }
}

impl std::ops::BitAnd for FieldBit256 {
    type Output = FieldBit256;

    fn bitand(self, rhs: FieldBit256) -> FieldBit256 {
        FieldBit256::new(self.m & rhs.m)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use field;
    use field_bit::FieldBit;

    #[test]
    fn test_constructor() {
        let fb256 = FieldBit256::empty();

        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert!(!fb256.get_low(x, y));
                assert!(!fb256.get_high(x, y));
            }
        }
    }

    #[test]
    fn test_from_low_high() {
        let mut low = FieldBit::empty();
        let mut high = FieldBit::empty();
        low.set(1, 3);
        low.set(4, 8);
        high.set(2, 4);
        high.set(5, 9);

        let fb256 = FieldBit256::from_low_high(low, high);
        assert!(fb256.get_low(1, 3));
        assert!(fb256.get_low(4, 8));
        assert!(fb256.get_high(2, 4));
        assert!(fb256.get_high(5, 9));

        assert!(!fb256.get_high(1, 3));
        assert!(!fb256.get_high(4, 8));
        assert!(!fb256.get_low(2, 4));
        assert!(!fb256.get_low(5, 9));

        assert_eq!(low, fb256.low());
        assert_eq!(high, fb256.high());
    }

    #[test]
    fn test_expand() {
        let mask_high = FieldBit::from_str(concat!(
            "......",
            "11..11",
            "11..11",
            "......",
            "111111"));
        let mask_low = FieldBit::from_str(concat!(
            "111111",
            ".....1",
            "111111",
            "1.....",
            "111111"));

        let expected_high = FieldBit::from_str(concat!(
            "111111"));
        let expected_low = mask_low;

        let mask = FieldBit256::from_low_high(mask_low, mask_high);

        let mut bit = FieldBit256::empty();
        bit.set_high(3, 1);
        bit.set_low(6, 1);

        let expanded = bit.expand(mask);
        assert_eq!(expected_high, expanded.high());
        assert_eq!(expected_low, expanded.low());
    }

    #[test]
    fn test_find_vanishing_bits_1() {
        let fb256 = {
            let f = FieldBit::from_str(concat!(
                ".1....",
                "11..1.",
                ".1.111",
                "1...1.",
                "11.111",
                "1...1."));
            FieldBit256::from_low_high(f, f)
        };

        let mut vanishing = FieldBit256::uninitialized();
        assert!(fb256.find_vanishing_bits(&mut vanishing));

        for x in 1 .. field::WIDTH + 1 {
            for y in 1 .. field::HEIGHT + 1 {
                assert_eq!(vanishing.get_low(x, y), fb256.get_low(x, y), "x={}, y={}", x, y);
                assert_eq!(vanishing.get_high(x, y), fb256.get_high(x, y), "x={}, y={}", x, y);
            }
        }
    }

    #[test]
    fn test_find_vanishing_bits_2() {
        let fb256 = {
            let f = FieldBit::from_str(concat!(
                ".....1",
                ".111.1",
                ".....1",
                ".1.11."));
            FieldBit256::from_low_high(f, f)
        };

        let mut vanishing = FieldBit256::uninitialized();
        assert!(!fb256.find_vanishing_bits(&mut vanishing));

        for x in 1 .. field::WIDTH + 1 {
            for y in 1 .. field::HEIGHT + 1 {
                assert!(!vanishing.get_low(x, y));
                assert!(!vanishing.get_high(x, y));
            }
        }
    }

    #[test]
    fn test_iterate_bit_with_masking() {
        let mut bf = FieldBit::empty();
        bf.set(1, 2);
        bf.set(2, 3);
        bf.set(3, 4);
        bf.set(4, 5);
        bf.set(5, 6);
        bf.set(6, 7);

        assert_eq!(6, bf.popcount());

        let mut count = 0;
        let mut iterated = FieldBit::empty();
        bf.iterate_bit_with_masking(|x: FieldBit| -> FieldBit {
            iterated.set_all(x);
            assert_eq!(1, x.popcount());
            count += 1;

            x
        });

        assert_eq!(6, count);
        assert_eq!(bf, iterated);
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::time::{Instant, Duration};
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    WindowedContext,
    PossiblyCurrent,
    dpi::LogicalSize,
    window::{WindowBuilder, Fullscreen},
    event::{
        Event as GlutinEvent,
        StartCause,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop},
};
use tokio::{
    sync::{mpsc, Mutex},
    runtime::{Runtime, Handle},
};

use crate::Event;
use crate::ipc_protocol::ServerConnection;

use super::{
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
    event_loop_notifier::{EventLoopNotifier, MainThreadAction},
};

/// The maximum rendering FPS allowed
///
/// Rendering is intentionally throttled to avoid too much contention over the display list. If
/// multiple turtles are used or if many lines are drawn quickly, we may get >= 1 redraw request
/// per *millisecond* this is far too many redraws. Limiting to this rate helps avoid that.
const MAX_RENDERING_FPS: u64 = 60;

// 1,000,000 us in 1 s
const MICROS_PER_SEC: u64 = 1_000_000;

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The runtime for driving async code
    let runtime = Runtime::new()
        .expect("unable to spawn tokio runtime to run turtle server process");

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();
    // Create the proxy that will be given to the thread managing IPC
    let event_loop_proxy = event_loop.create_proxy();
    let event_loop_notifier = Arc::new(EventLoopNotifier::new(event_loop_proxy));

    // A channel for transferring events
    let (events_sender, events_receiver) = mpsc::unbounded_channel();
    // Put the events_receiver in an Option so we can call `take()` in the event loop. Required
    // because borrow checker cannot verify that `Init` event is only fired once.
    let mut events_receiver = Some(events_receiver);

    let window_builder = {
        let drawing = runtime.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size, gl_context.window().scale_factor());

    // For rate limiting rendering
    let min_render_delay = Duration::from_micros(MICROS_PER_SEC / MAX_RENDERING_FPS);
    // Subtracting the delay so we do an initial render right away
    let mut last_render = Instant::now() - min_render_delay;
    event_loop.run(move |event, _, control_flow| match event {
        GlutinEvent::NewEvents(StartCause::Init) => {
            // Spawn the actual server thread(s) that will handle incoming IPC messages and
            // asynchronous update the shared state
            //
            // Note that putting this code here instead of before the event loop causes the
            // `Turtle::new()`, etc. methods not to return before the window opens. Those methods
            // can't return because the connection handshake cannot complete before the thread used
            // for IPC is spawned.
            let handle = runtime.handle().clone();
            spawn_async_server(
                handle,
                app.clone(),
                display_list.clone(),
                event_loop_notifier.clone(),
                events_receiver.take().expect("bug: init event should only occur once"),
            );
        },

        GlutinEvent::NewEvents(StartCause::ResumeTimeReached {..}) => {
            // A render was delayed in the `RedrawRequested` so let's try to do it again now that
            // we have resumed
            gl_context.window().request_redraw();
        },

        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {event, ..} => {
            let scale_factor = renderer.scale_factor();
            match event {
                WindowEvent::Resized(size) => {
                    let size = size.to_logical(scale_factor);
                    let handle = runtime.handle();
                    let mut drawing = handle.block_on(app.drawing_mut());
                    drawing.width = size.width;
                    drawing.height = size.height;
                    //TODO: No idea if this next line is necessary or not
                    gl_context.window().request_redraw();
                },

                //TODO: There are currently no events for updating is_maximized, so that property
                // should not be relied on. https://github.com/rust-windowing/glutin/issues/1298

                _ => {},
            }

            //TODO: There is no guarantee that sending this event here will actually allow a client
            // to receive it. After all, if the window closes and this process exits, there will be
            // no way to handle subsequent `NextEvent` requests.
            if let Some(event) = Event::from_window_event(event, scale_factor) {
                events_sender.send(event)
                    .expect("bug: server IPC thread should stay alive as long as server main thread");
            }
        },

        // Window events are currently sufficient for the turtle event API
        GlutinEvent::DeviceEvent {..} => {},

        GlutinEvent::UserEvent(MainThreadAction::Redraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetTitle(title)) => {
            gl_context.window().set_title(&title);
        },

        GlutinEvent::UserEvent(MainThreadAction::SetSize(size)) => {
            gl_context.window().set_inner_size(size);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetIsMaximized(is_maximized)) => {
            gl_context.window().set_maximized(is_maximized);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetIsFullscreen(is_fullscreen)) => {
            gl_context.window().set_fullscreen(if is_fullscreen {
                Some(Fullscreen::Borderless(gl_context.window().current_monitor()))
            } else { None });

            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            // Check if we just rendered
            let last_render_delay = last_render.elapsed();
            if last_render_delay < min_render_delay {
                let remaining = min_render_delay - last_render_delay;
                *control_flow = ControlFlow::WaitUntil(Instant::now() + remaining);
                return;
            }

            let handle = runtime.handle();
            handle.block_on(redraw(&app, &display_list, &gl_context, &mut renderer));
            *control_flow = ControlFlow::Wait;
            last_render = Instant::now();
        },

        _ => {},
    });
}

async fn redraw(
    app: &App,
    display_list: &Mutex<DisplayList>,
    gl_context: &WindowedContext<PossiblyCurrent>,
    renderer: &mut Renderer,
) {
    let drawing = {
        // Hold the drawing lock for as little time as possible
        let drawing = app.drawing_mut().await;
        drawing.clone()
    };

    // Locking the turtles before the display list to be consistent with all of the request
    // handlers. Inconsistent lock ordering can cause deadlock.
    let mut turtles = Vec::new();
    for id in app.turtle_ids().await {
        turtles.push(app.turtle(id).await);
    }
    // Very important to have all the data locked before rendering. Do not want renderer to have
    // to figure out how to lock.
    let mut locked_turtles = Vec::with_capacity(turtles.len());
    for turtle in &turtles {
        locked_turtles.push(turtle.lock().await);
    }
    // Renderer only needs (read-only) access to TurtleState
    // Doing this also decouples renderer code from runtime by not having to
    // know about the `MutexGuard` type in tokio
    let turtle_states = locked_turtles.iter().map(|t| &t.state);

    let display_list = display_list.lock().await;

    let draw_size = gl_context.window().inner_size();
    renderer.render(draw_size, &display_list, &drawing, turtle_states);
    gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        //
        // It's easy for a user to accidentally cause this panic if they call `Turtle::new()` in a
        // new thread. This message is meant to point them to the solution: `turtle::start()`
        panic!("Windows can only be created on the main thread. \
                Make sure you have called `turtle::start()` at the beginning of your program. \
                See: <https://docs.rs/turtle/*/turtle/fn.start.html>");
    }
}

fn spawn_async_server(
    handle: Handle,
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: Arc<EventLoopNotifier>,
    events_receiver: mpsc::UnboundedReceiver<Event>,
) {
    // Spawn root task
    handle.spawn(async {
        let conn = ServerConnection::connect_stdin().await
            .expect("unable to establish turtle server connection");
        super::serve(conn, app, display_list, event_loop, events_receiver).await;
    });
}
<commit_msg>IPC thread may exit before main thread finishes if main process finishes before window is closed<commit_after>use std::thread;
use std::time::{Instant, Duration};
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    WindowedContext,
    PossiblyCurrent,
    dpi::LogicalSize,
    window::{WindowBuilder, Fullscreen},
    event::{
        Event as GlutinEvent,
        StartCause,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop},
};
use tokio::{
    sync::{mpsc, Mutex},
    runtime::{Runtime, Handle},
};

use crate::Event;
use crate::ipc_protocol::ServerConnection;

use super::{
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
    event_loop_notifier::{EventLoopNotifier, MainThreadAction},
};

/// The maximum rendering FPS allowed
///
/// Rendering is intentionally throttled to avoid too much contention over the display list. If
/// multiple turtles are used or if many lines are drawn quickly, we may get >= 1 redraw request
/// per *millisecond* this is far too many redraws. Limiting to this rate helps avoid that.
const MAX_RENDERING_FPS: u64 = 60;

// 1,000,000 us in 1 s
const MICROS_PER_SEC: u64 = 1_000_000;

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The runtime for driving async code
    let runtime = Runtime::new()
        .expect("unable to spawn tokio runtime to run turtle server process");

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();
    // Create the proxy that will be given to the thread managing IPC
    let event_loop_proxy = event_loop.create_proxy();
    let event_loop_notifier = Arc::new(EventLoopNotifier::new(event_loop_proxy));

    // A channel for transferring events
    let (events_sender, events_receiver) = mpsc::unbounded_channel();
    // Put the events_receiver in an Option so we can call `take()` in the event loop. Required
    // because borrow checker cannot verify that `Init` event is only fired once.
    let mut events_receiver = Some(events_receiver);

    let window_builder = {
        let drawing = runtime.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size, gl_context.window().scale_factor());

    // For rate limiting rendering
    let min_render_delay = Duration::from_micros(MICROS_PER_SEC / MAX_RENDERING_FPS);
    // Subtracting the delay so we do an initial render right away
    let mut last_render = Instant::now() - min_render_delay;
    event_loop.run(move |event, _, control_flow| match event {
        GlutinEvent::NewEvents(StartCause::Init) => {
            // Spawn the actual server thread(s) that will handle incoming IPC messages and
            // asynchronous update the shared state
            //
            // Note that putting this code here instead of before the event loop causes the
            // `Turtle::new()`, etc. methods not to return before the window opens. Those methods
            // can't return because the connection handshake cannot complete before the thread used
            // for IPC is spawned.
            let handle = runtime.handle().clone();
            spawn_async_server(
                handle,
                app.clone(),
                display_list.clone(),
                event_loop_notifier.clone(),
                events_receiver.take().expect("bug: init event should only occur once"),
            );
        },

        GlutinEvent::NewEvents(StartCause::ResumeTimeReached {..}) => {
            // A render was delayed in the `RedrawRequested` so let's try to do it again now that
            // we have resumed
            gl_context.window().request_redraw();
        },

        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {event, ..} => {
            let scale_factor = renderer.scale_factor();
            match event {
                WindowEvent::Resized(size) => {
                    let size = size.to_logical(scale_factor);
                    let handle = runtime.handle();
                    let mut drawing = handle.block_on(app.drawing_mut());
                    drawing.width = size.width;
                    drawing.height = size.height;
                    //TODO: No idea if this next line is necessary or not
                    gl_context.window().request_redraw();
                },

                //TODO: There are currently no events for updating is_maximized, so that property
                // should not be relied on. https://github.com/rust-windowing/glutin/issues/1298

                _ => {},
            }

            //TODO: There is no guarantee that sending this event here will actually allow a client
            // to receive it. After all, if the window closes and this process exits, there will be
            // no way to handle subsequent `NextEvent` requests.
            if let Some(event) = Event::from_window_event(event, scale_factor) {
                match events_sender.send(event) {
                    Ok(()) => {},
                    // Sending may fail if the IPC thread has ended due to a disconnection when the
                    // main process ends.
                    Err(_) => {},
                }
            }
        },

        // Window events are currently sufficient for the turtle event API
        GlutinEvent::DeviceEvent {..} => {},

        GlutinEvent::UserEvent(MainThreadAction::Redraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetTitle(title)) => {
            gl_context.window().set_title(&title);
        },

        GlutinEvent::UserEvent(MainThreadAction::SetSize(size)) => {
            gl_context.window().set_inner_size(size);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetIsMaximized(is_maximized)) => {
            gl_context.window().set_maximized(is_maximized);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetIsFullscreen(is_fullscreen)) => {
            gl_context.window().set_fullscreen(if is_fullscreen {
                Some(Fullscreen::Borderless(gl_context.window().current_monitor()))
            } else { None });

            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            // Check if we just rendered
            let last_render_delay = last_render.elapsed();
            if last_render_delay < min_render_delay {
                let remaining = min_render_delay - last_render_delay;
                *control_flow = ControlFlow::WaitUntil(Instant::now() + remaining);
                return;
            }

            let handle = runtime.handle();
            handle.block_on(redraw(&app, &display_list, &gl_context, &mut renderer));
            *control_flow = ControlFlow::Wait;
            last_render = Instant::now();
        },

        _ => {},
    });
}

async fn redraw(
    app: &App,
    display_list: &Mutex<DisplayList>,
    gl_context: &WindowedContext<PossiblyCurrent>,
    renderer: &mut Renderer,
) {
    let drawing = {
        // Hold the drawing lock for as little time as possible
        let drawing = app.drawing_mut().await;
        drawing.clone()
    };

    // Locking the turtles before the display list to be consistent with all of the request
    // handlers. Inconsistent lock ordering can cause deadlock.
    let mut turtles = Vec::new();
    for id in app.turtle_ids().await {
        turtles.push(app.turtle(id).await);
    }
    // Very important to have all the data locked before rendering. Do not want renderer to have
    // to figure out how to lock.
    let mut locked_turtles = Vec::with_capacity(turtles.len());
    for turtle in &turtles {
        locked_turtles.push(turtle.lock().await);
    }
    // Renderer only needs (read-only) access to TurtleState
    // Doing this also decouples renderer code from runtime by not having to
    // know about the `MutexGuard` type in tokio
    let turtle_states = locked_turtles.iter().map(|t| &t.state);

    let display_list = display_list.lock().await;

    let draw_size = gl_context.window().inner_size();
    renderer.render(draw_size, &display_list, &drawing, turtle_states);
    gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        //
        // It's easy for a user to accidentally cause this panic if they call `Turtle::new()` in a
        // new thread. This message is meant to point them to the solution: `turtle::start()`
        panic!("Windows can only be created on the main thread. \
                Make sure you have called `turtle::start()` at the beginning of your program. \
                See: <https://docs.rs/turtle/*/turtle/fn.start.html>");
    }
}

fn spawn_async_server(
    handle: Handle,
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: Arc<EventLoopNotifier>,
    events_receiver: mpsc::UnboundedReceiver<Event>,
) {
    // Spawn root task
    handle.spawn(async {
        let conn = ServerConnection::connect_stdin().await
            .expect("unable to establish turtle server connection");
        super::serve(conn, app, display_list, event_loop, events_receiver).await;
    });
}
<|endoftext|>


<commit_before><commit_msg>identifiers: Make device_id! return a reference<commit_after><|endoftext|>


<commit_before>//! Backend using the pure-rust crossplatform crossterm library.
//!
//! Requires the `crossterm-backend` feature.

#![cfg(feature = "crossterm")]

use crate::vec::Vec2;
use crate::{backend, theme};
use crossterm::{
    cursor, execute, input, queue, terminal, AlternateScreen, AsyncReader,
    Attribute, Clear, ClearType, Color, Goto, InputEvent as CInputEvent,
    KeyEvent as CKeyEvent, MouseButton as CMouseButton,
    MouseEvent as CMouseEvent, SetAttr, SetBg, SetFg, Show, Terminal,
};

use crate::event::{Event, Key, MouseButton, MouseEvent};
use std::cell::{Cell, RefCell};
use std::io::{self, BufWriter, Stdout, Write};

/// Backend using crossterm
pub struct Backend {
    current_style: Cell<theme::ColorPair>,
    last_button: Option<MouseButton>,
    // reader to read user input async.
    async_reader: AsyncReader,
    _alternate_screen: AlternateScreen,
    stdout: RefCell<BufWriter<Stdout>>,
    terminal: Terminal,
}

impl Backend {
    /// Creates a new crossterm backend.
    pub fn init() -> Result<Box<dyn backend::Backend>, crossterm::ErrorKind>
    where
        Self: Sized,
    {
        let _alternate_screen = AlternateScreen::to_alternate(true)?;

        let input = input();
        let async_reader = input.read_async();
        input.enable_mouse_mode().unwrap();

        cursor().hide()?;

        Ok(Box::new(Backend {
            current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)),
            last_button: None,
            async_reader,
            _alternate_screen,
            stdout: RefCell::new(BufWriter::new(io::stdout())),
            terminal: terminal(),
        }))
    }

    fn apply_colors(&self, colors: theme::ColorPair) {
        with_color(colors.front, |c| {
            queue!(self.stdout.borrow_mut(), SetFg(*c))
        })
        .unwrap();
        with_color(colors.back, |c| {
            queue!(self.stdout.borrow_mut(), SetBg(*c))
        })
        .unwrap();
    }

    fn write<T>(&self, content: T)
    where
        T: std::fmt::Display,
    {
        write!(self.stdout.borrow_mut(), "{}", format!("{}", content))
            .unwrap();
    }

    fn set_attr(&self, attr: Attribute) {
        queue!(self.stdout.borrow_mut(), SetAttr(attr)).unwrap();
    }

    fn map_key(&mut self, event: CInputEvent) -> Event {
        match event {
            CInputEvent::Keyboard(key_event) => match key_event {
                CKeyEvent::Esc => Event::Key(Key::Esc),
                CKeyEvent::Backspace => Event::Key(Key::Backspace),
                CKeyEvent::Left => Event::Key(Key::Left),
                CKeyEvent::Right => Event::Key(Key::Right),
                CKeyEvent::Up => Event::Key(Key::Up),
                CKeyEvent::Down => Event::Key(Key::Down),
                CKeyEvent::Home => Event::Key(Key::Home),
                CKeyEvent::End => Event::Key(Key::End),
                CKeyEvent::PageUp => Event::Key(Key::PageUp),
                CKeyEvent::PageDown => Event::Key(Key::PageDown),
                CKeyEvent::Delete => Event::Key(Key::Del),
                CKeyEvent::Insert => Event::Key(Key::Ins),
                CKeyEvent::F(n) => Event::Key(Key::from_f(n)),
                CKeyEvent::Char('\n') => Event::Key(Key::Enter),
                CKeyEvent::Char('\t') => Event::Key(Key::Tab),
                CKeyEvent::Char(c) => Event::Char(c),
                CKeyEvent::Ctrl('c') => Event::Exit,
                CKeyEvent::Ctrl(c) => Event::CtrlChar(c),
                CKeyEvent::Alt(c) => Event::AltChar(c),
                _ => Event::Unknown(vec![]),
            },
            CInputEvent::Mouse(mouse_event) => match mouse_event {
                CMouseEvent::Press(btn, x, y) => {
                    let position = (x - 1, y - 1).into();

                    let event = match btn {
                        CMouseButton::Left => {
                            MouseEvent::Press(MouseButton::Left)
                        }
                        CMouseButton::Middle => {
                            MouseEvent::Press(MouseButton::Middle)
                        }
                        CMouseButton::Right => {
                            MouseEvent::Press(MouseButton::Right)
                        }
                        CMouseButton::WheelUp => MouseEvent::WheelUp,
                        CMouseButton::WheelDown => MouseEvent::WheelDown,
                    };

                    if let MouseEvent::Press(btn) = event {
                        self.last_button = Some(btn);
                    }

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                CMouseEvent::Release(x, y) if self.last_button.is_some() => {
                    let event = MouseEvent::Release(self.last_button.unwrap());
                    let position = (x - 1, y - 1).into();

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                CMouseEvent::Hold(x, y) if self.last_button.is_some() => {
                    let event = MouseEvent::Hold(self.last_button.unwrap());
                    let position = (x - 1, y - 1).into();

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                _ => {
                    log::warn!(
                        "Unknown mouse button event {:?}!",
                        mouse_event
                    );
                    Event::Unknown(vec![])
                }
            },
            _ => {
                log::warn!("Unknown mouse event {:?}!", event);
                Event::Unknown(vec![])
            }
        }
    }
}

impl backend::Backend for Backend {
    fn name(&self) -> &str {
        "crossterm"
    }

    fn poll_event(&mut self) -> Option<Event> {
        self.async_reader.next().map(|event| self.map_key(event))
    }

    fn finish(&mut self) {
        execute!(
            self.stdout.borrow_mut(),
            Goto(0, 0),
            Clear(ClearType::All),
            SetBg(Color::Reset),
            SetFg(Color::Reset),
            SetAttr(Attribute::Reset),
            Show
        )
        .unwrap();
        input().disable_mouse_mode().unwrap();
    }

    fn refresh(&mut self) {
        self.stdout.borrow_mut().flush().unwrap();
    }

    fn has_colors(&self) -> bool {
        // TODO: color support detection?
        true
    }

    fn screen_size(&self) -> Vec2 {
        let size = self.terminal.size().unwrap_or((0, 0));
        Vec2::from(size)
    }

    fn print_at(&self, pos: Vec2, text: &str) {
        queue!(self.stdout.borrow_mut(), Goto(pos.x as u16, pos.y as u16))
            .unwrap();
        self.write(text);
    }

    fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) {
        if repetitions > 0 {
            let mut out = self.stdout.borrow_mut();

            queue!(out, Goto(pos.x as u16, pos.y as u16)).unwrap();

            // as I (Timon) wrote this I figured out that calling `write_str` for unix was flushing the stdout.
            // Current work aground is writing bytes instead of a string to the terminal.
            out.write_all(text.as_bytes()).unwrap();

            let mut dupes_left = repetitions - 1;
            while dupes_left > 0 {
                out.write_all(text.as_bytes()).unwrap();
                dupes_left -= 1;
            }
        }
    }

    fn clear(&self, color: theme::Color) {
        self.apply_colors(theme::ColorPair {
            front: color,
            back: color,
        });

        queue!(self.stdout.borrow_mut(), Clear(ClearType::All)).unwrap();
    }

    fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
        let current_style = self.current_style.get();

        if current_style != color {
            self.apply_colors(color);
            self.current_style.set(color);
        }

        current_style
    }

    fn set_effect(&self, effect: theme::Effect) {
        match effect {
            theme::Effect::Simple => (),
            theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
            theme::Effect::Bold => self.set_attr(Attribute::Bold),
            theme::Effect::Italic => self.set_attr(Attribute::Italic),
            theme::Effect::Strikethrough => {
                self.set_attr(Attribute::CrossedOut)
            }
            theme::Effect::Underline => self.set_attr(Attribute::Underlined),
        }
    }

    fn unset_effect(&self, effect: theme::Effect) {
        match effect {
            theme::Effect::Simple => (),
            theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
            theme::Effect::Bold => self.set_attr(Attribute::NoBold),
            theme::Effect::Italic => self.set_attr(Attribute::NoItalic),
            theme::Effect::Strikethrough => {
                self.set_attr(Attribute::NotCrossedOut)
            }
            theme::Effect::Underline => self.set_attr(Attribute::Underlined),
        }
    }
}

fn with_color<F, R>(clr: theme::Color, f: F) -> R
where
    F: FnOnce(&Color) -> R,
{
    match clr {
        theme::Color::Dark(theme::BaseColor::Black) => f(&Color::Black),
        theme::Color::Dark(theme::BaseColor::Red) => f(&Color::DarkRed),
        theme::Color::Dark(theme::BaseColor::Green) => f(&Color::DarkGreen),
        theme::Color::Dark(theme::BaseColor::Yellow) => f(&Color::DarkYellow),
        theme::Color::Dark(theme::BaseColor::Blue) => f(&Color::DarkBlue),
        theme::Color::Dark(theme::BaseColor::Magenta) => {
            f(&Color::DarkMagenta)
        }
        theme::Color::Dark(theme::BaseColor::Cyan) => f(&Color::DarkCyan),
        theme::Color::Dark(theme::BaseColor::White) => f(&Color::Grey),

        theme::Color::Light(theme::BaseColor::Black) => f(&Color::Grey),
        theme::Color::Light(theme::BaseColor::Red) => f(&Color::Red),
        theme::Color::Light(theme::BaseColor::Green) => f(&Color::Green),
        theme::Color::Light(theme::BaseColor::Yellow) => f(&Color::Yellow),
        theme::Color::Light(theme::BaseColor::Blue) => f(&Color::Blue),
        theme::Color::Light(theme::BaseColor::Magenta) => f(&Color::Magenta),
        theme::Color::Light(theme::BaseColor::Cyan) => f(&Color::Cyan),
        theme::Color::Light(theme::BaseColor::White) => f(&Color::White),

        theme::Color::Rgb(r, g, b) => f(&Color::Rgb { r, g, b }),
        theme::Color::RgbLowRes(r, g, b) => {
            debug_assert!(r <= 5,
                          "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
                          r);
            debug_assert!(g <= 5,
                          "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
                          g);
            debug_assert!(b <= 5,
                          "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
                          b);

            f(&Color::AnsiValue(16 + 36 * r + 6 * g + b))
        }

        theme::Color::TerminalDefault => {
            unimplemented!(
                "I have to take a look at how reset has to work out"
            );
        }
    }
}
<commit_msg>Make enter and tab key work again in crossterm backend (#395)<commit_after>//! Backend using the pure-rust crossplatform crossterm library.
//!
//! Requires the `crossterm-backend` feature.

#![cfg(feature = "crossterm")]

use crate::vec::Vec2;
use crate::{backend, theme};
use crossterm::{
    cursor, execute, input, queue, terminal, AlternateScreen, AsyncReader,
    Attribute, Clear, ClearType, Color, Goto, InputEvent as CInputEvent,
    KeyEvent as CKeyEvent, MouseButton as CMouseButton,
    MouseEvent as CMouseEvent, SetAttr, SetBg, SetFg, Show, Terminal,
};

use crate::event::{Event, Key, MouseButton, MouseEvent};
use std::cell::{Cell, RefCell};
use std::io::{self, BufWriter, Stdout, Write};

/// Backend using crossterm
pub struct Backend {
    current_style: Cell<theme::ColorPair>,
    last_button: Option<MouseButton>,
    // reader to read user input async.
    async_reader: AsyncReader,
    _alternate_screen: AlternateScreen,
    stdout: RefCell<BufWriter<Stdout>>,
    terminal: Terminal,
}

impl Backend {
    /// Creates a new crossterm backend.
    pub fn init() -> Result<Box<dyn backend::Backend>, crossterm::ErrorKind>
    where
        Self: Sized,
    {
        let _alternate_screen = AlternateScreen::to_alternate(true)?;

        let input = input();
        let async_reader = input.read_async();
        input.enable_mouse_mode().unwrap();

        cursor().hide()?;

        Ok(Box::new(Backend {
            current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)),
            last_button: None,
            async_reader,
            _alternate_screen,
            stdout: RefCell::new(BufWriter::new(io::stdout())),
            terminal: terminal(),
        }))
    }

    fn apply_colors(&self, colors: theme::ColorPair) {
        with_color(colors.front, |c| {
            queue!(self.stdout.borrow_mut(), SetFg(*c))
        })
        .unwrap();
        with_color(colors.back, |c| {
            queue!(self.stdout.borrow_mut(), SetBg(*c))
        })
        .unwrap();
    }

    fn write<T>(&self, content: T)
    where
        T: std::fmt::Display,
    {
        write!(self.stdout.borrow_mut(), "{}", format!("{}", content))
            .unwrap();
    }

    fn set_attr(&self, attr: Attribute) {
        queue!(self.stdout.borrow_mut(), SetAttr(attr)).unwrap();
    }

    fn map_key(&mut self, event: CInputEvent) -> Event {
        match event {
            CInputEvent::Keyboard(key_event) => match key_event {
                CKeyEvent::Esc => Event::Key(Key::Esc),
                CKeyEvent::Backspace => Event::Key(Key::Backspace),
                CKeyEvent::Left => Event::Key(Key::Left),
                CKeyEvent::Right => Event::Key(Key::Right),
                CKeyEvent::Up => Event::Key(Key::Up),
                CKeyEvent::Down => Event::Key(Key::Down),
                CKeyEvent::Home => Event::Key(Key::Home),
                CKeyEvent::End => Event::Key(Key::End),
                CKeyEvent::PageUp => Event::Key(Key::PageUp),
                CKeyEvent::PageDown => Event::Key(Key::PageDown),
                CKeyEvent::Delete => Event::Key(Key::Del),
                CKeyEvent::Insert => Event::Key(Key::Ins),
                CKeyEvent::Enter => Event::Key(Key::Enter),
                CKeyEvent::Tab => Event::Key(Key::Tab),
                CKeyEvent::F(n) => Event::Key(Key::from_f(n)),
                CKeyEvent::Char(c) => Event::Char(c),
                CKeyEvent::Ctrl('c') => Event::Exit,
                CKeyEvent::Ctrl(c) => Event::CtrlChar(c),
                CKeyEvent::Alt(c) => Event::AltChar(c),
                _ => Event::Unknown(vec![]),
            },
            CInputEvent::Mouse(mouse_event) => match mouse_event {
                CMouseEvent::Press(btn, x, y) => {
                    let position = (x - 1, y - 1).into();

                    let event = match btn {
                        CMouseButton::Left => {
                            MouseEvent::Press(MouseButton::Left)
                        }
                        CMouseButton::Middle => {
                            MouseEvent::Press(MouseButton::Middle)
                        }
                        CMouseButton::Right => {
                            MouseEvent::Press(MouseButton::Right)
                        }
                        CMouseButton::WheelUp => MouseEvent::WheelUp,
                        CMouseButton::WheelDown => MouseEvent::WheelDown,
                    };

                    if let MouseEvent::Press(btn) = event {
                        self.last_button = Some(btn);
                    }

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                CMouseEvent::Release(x, y) if self.last_button.is_some() => {
                    let event = MouseEvent::Release(self.last_button.unwrap());
                    let position = (x - 1, y - 1).into();

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                CMouseEvent::Hold(x, y) if self.last_button.is_some() => {
                    let event = MouseEvent::Hold(self.last_button.unwrap());
                    let position = (x - 1, y - 1).into();

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                _ => {
                    log::warn!(
                        "Unknown mouse button event {:?}!",
                        mouse_event
                    );
                    Event::Unknown(vec![])
                }
            },
            _ => {
                log::warn!("Unknown mouse event {:?}!", event);
                Event::Unknown(vec![])
            }
        }
    }
}

impl backend::Backend for Backend {
    fn name(&self) -> &str {
        "crossterm"
    }

    fn poll_event(&mut self) -> Option<Event> {
        self.async_reader.next().map(|event| self.map_key(event))
    }

    fn finish(&mut self) {
        execute!(
            self.stdout.borrow_mut(),
            Goto(0, 0),
            Clear(ClearType::All),
            SetBg(Color::Reset),
            SetFg(Color::Reset),
            SetAttr(Attribute::Reset),
            Show
        )
        .unwrap();
        input().disable_mouse_mode().unwrap();
    }

    fn refresh(&mut self) {
        self.stdout.borrow_mut().flush().unwrap();
    }

    fn has_colors(&self) -> bool {
        // TODO: color support detection?
        true
    }

    fn screen_size(&self) -> Vec2 {
        let size = self.terminal.size().unwrap_or((0, 0));
        Vec2::from(size)
    }

    fn print_at(&self, pos: Vec2, text: &str) {
        queue!(self.stdout.borrow_mut(), Goto(pos.x as u16, pos.y as u16))
            .unwrap();
        self.write(text);
    }

    fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) {
        if repetitions > 0 {
            let mut out = self.stdout.borrow_mut();

            queue!(out, Goto(pos.x as u16, pos.y as u16)).unwrap();

            // as I (Timon) wrote this I figured out that calling `write_str` for unix was flushing the stdout.
            // Current work aground is writing bytes instead of a string to the terminal.
            out.write_all(text.as_bytes()).unwrap();

            let mut dupes_left = repetitions - 1;
            while dupes_left > 0 {
                out.write_all(text.as_bytes()).unwrap();
                dupes_left -= 1;
            }
        }
    }

    fn clear(&self, color: theme::Color) {
        self.apply_colors(theme::ColorPair {
            front: color,
            back: color,
        });

        queue!(self.stdout.borrow_mut(), Clear(ClearType::All)).unwrap();
    }

    fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
        let current_style = self.current_style.get();

        if current_style != color {
            self.apply_colors(color);
            self.current_style.set(color);
        }

        current_style
    }

    fn set_effect(&self, effect: theme::Effect) {
        match effect {
            theme::Effect::Simple => (),
            theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
            theme::Effect::Bold => self.set_attr(Attribute::Bold),
            theme::Effect::Italic => self.set_attr(Attribute::Italic),
            theme::Effect::Strikethrough => {
                self.set_attr(Attribute::CrossedOut)
            }
            theme::Effect::Underline => self.set_attr(Attribute::Underlined),
        }
    }

    fn unset_effect(&self, effect: theme::Effect) {
        match effect {
            theme::Effect::Simple => (),
            theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
            theme::Effect::Bold => self.set_attr(Attribute::NoBold),
            theme::Effect::Italic => self.set_attr(Attribute::NoItalic),
            theme::Effect::Strikethrough => {
                self.set_attr(Attribute::NotCrossedOut)
            }
            theme::Effect::Underline => self.set_attr(Attribute::Underlined),
        }
    }
}

fn with_color<F, R>(clr: theme::Color, f: F) -> R
where
    F: FnOnce(&Color) -> R,
{
    match clr {
        theme::Color::Dark(theme::BaseColor::Black) => f(&Color::Black),
        theme::Color::Dark(theme::BaseColor::Red) => f(&Color::DarkRed),
        theme::Color::Dark(theme::BaseColor::Green) => f(&Color::DarkGreen),
        theme::Color::Dark(theme::BaseColor::Yellow) => f(&Color::DarkYellow),
        theme::Color::Dark(theme::BaseColor::Blue) => f(&Color::DarkBlue),
        theme::Color::Dark(theme::BaseColor::Magenta) => {
            f(&Color::DarkMagenta)
        }
        theme::Color::Dark(theme::BaseColor::Cyan) => f(&Color::DarkCyan),
        theme::Color::Dark(theme::BaseColor::White) => f(&Color::Grey),

        theme::Color::Light(theme::BaseColor::Black) => f(&Color::Grey),
        theme::Color::Light(theme::BaseColor::Red) => f(&Color::Red),
        theme::Color::Light(theme::BaseColor::Green) => f(&Color::Green),
        theme::Color::Light(theme::BaseColor::Yellow) => f(&Color::Yellow),
        theme::Color::Light(theme::BaseColor::Blue) => f(&Color::Blue),
        theme::Color::Light(theme::BaseColor::Magenta) => f(&Color::Magenta),
        theme::Color::Light(theme::BaseColor::Cyan) => f(&Color::Cyan),
        theme::Color::Light(theme::BaseColor::White) => f(&Color::White),

        theme::Color::Rgb(r, g, b) => f(&Color::Rgb { r, g, b }),
        theme::Color::RgbLowRes(r, g, b) => {
            debug_assert!(r <= 5,
                          "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
                          r);
            debug_assert!(g <= 5,
                          "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
                          g);
            debug_assert!(b <= 5,
                          "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
                          b);

            f(&Color::AnsiValue(16 + 36 * r + 6 * g + b))
        }

        theme::Color::TerminalDefault => {
            unimplemented!(
                "I have to take a look at how reset has to work out"
            );
        }
    }
}
<|endoftext|>


<commit_before>use super::super::backend::Backend;
use super::super::buckets::Buckets;
use std::net::{Ipv4Addr, SocketAddrV4, TcpStream};
use std::str::FromStr;
use std::fmt::Write;
use std::io::Write as IoWrite;
use time;

#[derive(Debug)]
pub struct Graphite {
    addr: SocketAddrV4,
    last_flush_time: u64,
    last_flush_length: u64
}

impl Graphite {
    /// Create a Graphite formatter
    ///
    /// # Examples
    ///
    /// ```
    /// let graph = Graphite::new(host, port);
    /// ```
    pub fn new(host: &str, port: u16) -> Graphite {
        let ip = Ipv4Addr::from_str(&host).unwrap();
        let addr = SocketAddrV4::new(ip, port);
        Graphite {
            addr: addr,
            last_flush_time: 0,
            last_flush_length: 0,
        }
    }
}

impl Backend for Graphite {
    fn flush_buckets(&mut self, buckets: &Buckets) {
        let start = time::get_time().sec;
        let mut stats = String::new();

        write!(
            stats,
            "{} {} {}\n",
            "statsd.bad_messages",
            buckets.bad_messages(),
            start).unwrap();
        write!(
            stats,
            "{} {} {}\n",
            "statsd.total_messages",
            buckets.total_messages(),
            start).unwrap();

        for (key, value) in buckets.counters().iter() {
            write!(
                stats,
                "{} {} {} \n",
                key,
                value,
                start).unwrap();
        }

        for (key, value) in buckets.gauges().iter() {
            write!(
                stats,
                "{} {} {} \n",
                key,
                value,
                start).unwrap();
        }

        // The raw timer data is not sent to graphite.
        for (key, value) in buckets.timer_data().iter() {
            write!(
                stats,
                "{} {} {} \n",
                key,
                value,
                start).unwrap();
        }

        let mut stream = TcpStream::connect(self.addr).unwrap();
        let _ = stream.write(stats.as_bytes());
    }
}
<commit_msg>Start adding tests for graphite formatting.<commit_after>use super::super::backend::Backend;
use super::super::buckets::Buckets;
use std::net::{Ipv4Addr, SocketAddrV4, TcpStream};
use std::str::FromStr;
use std::fmt::Write;
use std::io::Write as IoWrite;
use time;

#[derive(Debug)]
pub struct Graphite {
    addr: SocketAddrV4,
    last_flush_time: u64,
    last_flush_length: u64
}

impl Graphite {
    /// Create a Graphite formatter
    ///
    /// # Examples
    ///
    /// ```
    /// let graph = Graphite::new(host, port);
    /// ```
    pub fn new(host: &str, port: u16) -> Graphite {
        let ip = Ipv4Addr::from_str(&host).unwrap();
        let addr = SocketAddrV4::new(ip, port);
        Graphite {
            addr: addr,
            last_flush_time: 0,
            last_flush_length: 0,
        }
    }

    /// Convert the buckets into a String that
    /// can be sent to graphite's newline API
    pub fn format_stats(&self, buckets: &Buckets) -> String {
        let start = time::get_time().sec;
        let mut stats = String::new();

        write!(
            stats,
            "{} {} {}\n",
            "statsd.bad_messages",
            buckets.bad_messages(),
            start).unwrap();
        write!(
            stats,
            "{} {} {}\n",
            "statsd.total_messages",
            buckets.total_messages(),
            start).unwrap();

        for (key, value) in buckets.counters().iter() {
            write!(
                stats,
                "{} {} {} \n",
                key,
                value,
                start).unwrap();
        }

        for (key, value) in buckets.gauges().iter() {
            write!(
                stats,
                "{} {} {} \n",
                key,
                value,
                start).unwrap();
        }

        // The raw timer data is not sent to graphite.
        for (key, value) in buckets.timer_data().iter() {
            write!(
                stats,
                "{} {} {} \n",
                key,
                value,
                start).unwrap();
        }
        stats
    }
}

impl Backend for Graphite {
    fn flush_buckets(&mut self, buckets: &Buckets) {
        let stats = self.format_stats(&buckets);

        let mut stream = TcpStream::connect(self.addr).unwrap();
        let _ = stream.write(stats.as_bytes());
    }
}

#[cfg(test)]
mod test {
    use super::super::super::metric::{Metric, MetricKind};
    use super::super::super::buckets::Buckets;
    use super::super::super::metric_processor::process;
    use super::*;

    fn make_buckets() -> Buckets {
        let mut buckets = Buckets::new();
        let m1 = Metric::new("test.counter", 1.0, MetricKind::Counter(1.0));
        let m2 = Metric::new("test.gauge", 3.211, MetricKind::Gauge);

        let m3 = Metric::new("test.timer", 12.101, MetricKind::Timer);
        let m4 = Metric::new("test.timer", 1.101, MetricKind::Timer);
        let m5 = Metric::new("test.timer", 3.101, MetricKind::Timer);
        buckets.add(&m1);
        buckets.add(&m2);
        buckets.add(&m3);
        buckets.add(&m4);
        buckets.add(&m5);
        buckets
    }

    #[test]
    fn test_format_buckets_no_timers() {
        let buckets = make_buckets();
        let graphite = Graphite::new("127.0.0.1", 2003);
        let result = graphite.format_stats(&buckets);
        let mut lines: Vec<&str> = result.lines().collect();

        assert_eq!(4, lines.len());
        assert!(lines[0].contains("statsd.bad_messages 0"));
        assert!(lines[1].contains("statsd.total_messages 5"));
        assert!(lines[2].contains("test.counter 1"));
        assert!(lines[3].contains("test.gauge 3.211"));
    }
}
<|endoftext|>


<commit_before>extern crate find_folder;

use screen::*;
use ::object;
use piston_window;
use gfx_device_gl;

pub struct BattleScreen {
    player: object::Object,
    up: bool,
    down: bool,
    left: bool,
    right: bool,
}

// TODO: Remove
fn load_sprite(window: &piston_window::PistonWindow, name: &str) -> piston_window::Texture<gfx_device_gl::Resources> {
    // TODO: Create and use default sprite in case of failure during loading?
    let assets = find_folder::Search::ParentsThenKids(3, 3).for_folder("assets").unwrap();
    let sprite = assets.join(name);
    piston_window::Texture::from_path(&mut *window.factory.borrow_mut(), &sprite, piston_window::Flip::None, &piston_window::TextureSettings::new()).unwrap()
}

impl BattleScreen {
    pub fn new() -> BattleScreen {
        BattleScreen{ player: load_sprite(&window, "skeleton.png"), }
    }
}

impl Screen for BattleScreen {
    fn on_input(&mut self, input: &piston_window::Input) -> InputResults {
        use piston_window::{Input, Button, Key};

        let result = Vec::new();

        match input {
            &Input::Press(button) => {
                match button {
                    Button::Keyboard(Key::Up) => { self.up = true; }
                    Button::Keyboard(Key::Down) => { self.down = true; }
                    Button::Keyboard(Key::Left) => { self.left = true; }
                    Button::Keyboard(Key::Right) => { self.right = true; }
                    _ => {}
                }
            }
            &Input::Release(button) => {
                match button {
                    Button::Keyboard(Key::Up) => { self.up = false; }
                    Button::Keyboard(Key::Down) => { self.down = false; }
                    Button::Keyboard(Key::Left) => { self.left = false; }
                    Button::Keyboard(Key::Right) => { self.right = false; }
                    _ => {}
                }
            }
            _ => {}
        }

        result
    }

    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &piston_window::PistonWindow) {
	window.draw_2d(|context, graphics| {
	    piston_window::clear([0.0, 0.0, 0.0, 1.0], graphics);

            let center = context.transform.trans((args.width / 2) as f64, (args.height / 2) as f64);
            self.player.render(graphics, &center);
	});
    }

    fn on_update(&mut self, args: &piston_window::UpdateArgs) {
	if self.up   { self.player.mov(0.0, -150.0 * args.dt); }
        if self.down { self.player.mov(0.0, 150.0 * args.dt); }
        if self.left { self.player.mov(-150.0 * args.dt, 0.0); }
        if self.right { self.player.mov(150.0 * args.dt, 0.0); }
    }
}
<commit_msg>TODO note<commit_after>extern crate find_folder;

use screen::*;
use ::object;
use piston_window;
use gfx_device_gl;

pub struct BattleScreen {
    player: object::Object,
    up: bool,
    down: bool,
    left: bool,
    right: bool,
}

// TODO: Replace by sprite loader?
// TODO: Remove
fn load_sprite(window: &piston_window::PistonWindow, name: &str) -> piston_window::Texture<gfx_device_gl::Resources> {
    // TODO: Create and use default sprite in case of failure during loading?
    let assets = find_folder::Search::ParentsThenKids(3, 3).for_folder("assets").unwrap();
    let sprite = assets.join(name);
    piston_window::Texture::from_path(&mut *window.factory.borrow_mut(), &sprite, piston_window::Flip::None, &piston_window::TextureSettings::new()).unwrap()
}

impl BattleScreen {
    pub fn new() -> BattleScreen {
        BattleScreen{ player: load_sprite(&window, "skeleton.png"), }
    }
}

impl Screen for BattleScreen {
    fn on_input(&mut self, input: &piston_window::Input) -> InputResults {
        use piston_window::{Input, Button, Key};

        let result = Vec::new();

        match input {
            &Input::Press(button) => {
                match button {
                    Button::Keyboard(Key::Up) => { self.up = true; }
                    Button::Keyboard(Key::Down) => { self.down = true; }
                    Button::Keyboard(Key::Left) => { self.left = true; }
                    Button::Keyboard(Key::Right) => { self.right = true; }
                    _ => {}
                }
            }
            &Input::Release(button) => {
                match button {
                    Button::Keyboard(Key::Up) => { self.up = false; }
                    Button::Keyboard(Key::Down) => { self.down = false; }
                    Button::Keyboard(Key::Left) => { self.left = false; }
                    Button::Keyboard(Key::Right) => { self.right = false; }
                    _ => {}
                }
            }
            _ => {}
        }

        result
    }

    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &piston_window::PistonWindow) {
	window.draw_2d(|context, graphics| {
	    piston_window::clear([0.0, 0.0, 0.0, 1.0], graphics);

            let center = context.transform.trans((args.width / 2) as f64, (args.height / 2) as f64);
            self.player.render(graphics, &center);
	});
    }

    fn on_update(&mut self, args: &piston_window::UpdateArgs) {
	if self.up   { self.player.mov(0.0, -150.0 * args.dt); }
        if self.down { self.player.mov(0.0, 150.0 * args.dt); }
        if self.left { self.player.mov(-150.0 * args.dt, 0.0); }
        if self.right { self.player.mov(150.0 * args.dt, 0.0); }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod reply_markup;

use serde::de::Deserialize;
use serde::ser::{Serialize, Serializer};

use types::*;

pub use self::reply_markup::*;

pub trait Request: Serialize {
    type Response;
    type RawResponse: Deserialize;

    fn map(raw: Self::RawResponse) -> Self::Response;

    fn name() -> &'static str;
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ChatId<'a> {
    ById(Integer),
    ByUsername(&'a str),
}

impl<'a> ChatId<'a> {
    pub fn from_id(id: Integer) -> Self {
        ChatId::ById(id)
    }

    pub fn from_username(username: &'a str) -> Self {
        ChatId::ByUsername(username)
    }
}

impl<'a> Serialize for ChatId<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        match *self {
            ChatId::ById(id) => serializer.serialize_i64(id),
            ChatId::ByUsername(username) => serializer.serialize_str(username),
        }
    }
}

impl<'a> From<Integer> for ChatId<'a> {
    fn from(value: Integer) -> ChatId<'a> {
        ChatId::from_id(value)
    }
}

impl<'a, 'b> From<&'b Chat> for ChatId<'a> {
    fn from(value: &'b Chat) -> ChatId<'a> {
        ChatId::from_id(value.id())
    }
}

impl<'a, 'b> From<&'b ForwardFrom> for ChatId<'a> {
    fn from(value: &'b ForwardFrom) -> ChatId<'a> {
        let id = match *value {
            ForwardFrom::User {ref user, ..} => user.id,
            ForwardFrom::Channel {ref channel, ..} => channel.id,
        };
        ChatId::from_id(id)
    }
}

macro_rules! from_chat_type {
    ($chat: ident) => {
        impl<'a, 'b> From<&'b $chat> for ChatId<'a> {
            fn from(value: &'b $chat) -> ChatId<'a> {
                ChatId::from_id(value.id)
            }
        }
    };
}

from_chat_type!(User);
from_chat_type!(Group);
from_chat_type!(Supergroup);
from_chat_type!(Channel);

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct UserId {
    inner: Integer
}

impl<'a> Serialize for UserId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        serializer.serialize_i64(self.inner)
    }
}

impl From<Integer> for UserId {
    fn from(value: Integer) -> UserId {
        UserId { inner: value }
    }
}

impl<'b> From<&'b User> for UserId {
    fn from(value: &User) -> UserId {
        UserId { inner: value.id }
    }
}

impl<'b> From<&'b ChatMember> for UserId {
    fn from(value: &ChatMember) -> UserId {
        UserId { inner: value.user.id }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize)]
pub enum ParseMode {
    Markdown,
    #[serde(rename = "HTML")]
    Html
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct MessageId(pub Integer);

impl Serialize for MessageId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        serializer.serialize_i64(self.0)
    }
}

impl From<Integer> for MessageId {
    fn from(value: Integer) -> Self {
        MessageId(value)
    }
}

impl<'a> From<&'a Message> for MessageId {
    fn from(value: &Message) -> Self {
        MessageId(value.id)
    }
}
<commit_msg>docs: document some base request types<commit_after>#[macro_use]
pub mod reply_markup;

use serde::de::Deserialize;
use serde::ser::{Serialize, Serializer};

use types::*;

pub use self::reply_markup::*;

/// Request corresponds to the specific Telegram API method.
pub trait Request: Serialize {
    type Response;
    /// Directly mapped from Telegram API response.
    type RawResponse: Deserialize;

    /// Map `RawResponse` to `Response`, `id` usually.
    fn map(raw: Self::RawResponse) -> Self::Response;

    /// Name of the method.
    fn name() -> &'static str;
}

/// Unique identifier for the target chat or username of the
/// target channel (in the format @channelusername)
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ChatId<'a> {
    ById(Integer),
    ByUsername(&'a str),
}

impl<'a> ChatId<'a> {
    /// Create chat id from numeric identifier.
    pub fn from_id(id: Integer) -> Self {
        ChatId::ById(id)
    }

    /// Create chat id from channel username.
    pub fn from_username(username: &'a str) -> Self {
        ChatId::ByUsername(username)
    }
}

impl<'a> Serialize for ChatId<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        match *self {
            ChatId::ById(id) => serializer.serialize_i64(id),
            ChatId::ByUsername(username) => serializer.serialize_str(username),
        }
    }
}

impl<'a> From<Integer> for ChatId<'a> {
    fn from(value: Integer) -> ChatId<'a> {
        ChatId::from_id(value)
    }
}

impl<'a, 'b> From<&'b Chat> for ChatId<'a> {
    fn from(value: &'b Chat) -> ChatId<'a> {
        ChatId::from_id(value.id())
    }
}

impl<'a, 'b> From<&'b ForwardFrom> for ChatId<'a> {
    fn from(value: &'b ForwardFrom) -> ChatId<'a> {
        let id = match *value {
            ForwardFrom::User {ref user, ..} => user.id,
            ForwardFrom::Channel {ref channel, ..} => channel.id,
        };
        ChatId::from_id(id)
    }
}

macro_rules! from_chat_type {
    ($chat: ident) => {
        impl<'a, 'b> From<&'b $chat> for ChatId<'a> {
            fn from(value: &'b $chat) -> ChatId<'a> {
                ChatId::from_id(value.id)
            }
        }
    };
}

from_chat_type!(User);
from_chat_type!(Group);
from_chat_type!(Supergroup);
from_chat_type!(Channel);

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct UserId {
    inner: Integer
}

impl<'a> Serialize for UserId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        serializer.serialize_i64(self.inner)
    }
}

impl From<Integer> for UserId {
    fn from(value: Integer) -> UserId {
        UserId { inner: value }
    }
}

impl<'b> From<&'b User> for UserId {
    fn from(value: &User) -> UserId {
        UserId { inner: value.id }
    }
}

impl<'b> From<&'b ChatMember> for UserId {
    fn from(value: &ChatMember) -> UserId {
        UserId { inner: value.user.id }
    }
}

/// Strongly typed ParseMode.
/// See [documentation](https://core.telegram.org/bots/api#formatting-options) for details.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize)]
pub enum ParseMode {
    /// Use markdown formatting.
    Markdown,
    /// Use HTML formatting.
    #[serde(rename = "HTML")]
    Html
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct MessageId(pub Integer);

impl Serialize for MessageId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        serializer.serialize_i64(self.0)
    }
}

impl From<Integer> for MessageId {
    fn from(value: Integer) -> Self {
        MessageId(value)
    }
}

impl<'a> From<&'a Message> for MessageId {
    fn from(value: &Message) -> Self {
        MessageId(value.id)
    }
}
<|endoftext|>


<commit_before>use std::mem::size_of;

use crate::compiler;
use crate::compiler::fct::{JitBaselineFct, JitDescriptor, JitFct, LazyCompilationSite};
use crate::compiler::map::CodeDescriptor;
use crate::cpu::{Mem, FREG_PARAMS, REG_FP, REG_PARAMS, REG_RESULT, REG_SP, REG_THREAD, REG_TMP1};
use crate::exception::DoraToNativeInfo;
use crate::gc::Address;
use crate::masm::MacroAssembler;
use crate::mem;
use crate::object::Obj;
use crate::threads::ThreadLocalData;
use crate::ty::{MachineMode, TypeList};
use crate::vm::FctId;
use crate::vm::{get_vm, VM};

// This code generates the compiler stub, there should only be one instance
// of this function be used in Dora. It is necessary for lazy compilation, where
// functions are only compiled on their first invocation. The compiler can use
// the address of this stub for invocations of functions that have not been compiled
// yet. The stub compiles the function and patches the call site to invoke the
// now-compiled function directly on the next invocation. In the end the function is
// executed.

pub fn generate<'a, 'ast: 'a>(vm: &'a VM<'ast>) -> Address {
    let ngen = DoraCompileGen {
        vm,
        masm: MacroAssembler::new(),
        dbg: vm.args.flag_emit_debug_compile,
    };

    let jit_fct = ngen.generate();
    let addr = jit_fct.instruction_start();
    vm.insert_code_map(
        jit_fct.ptr_start(),
        jit_fct.ptr_end(),
        CodeDescriptor::CompileStub,
    );
    vm.jit_fcts.push(JitFct::Base(jit_fct));

    addr
}

struct DoraCompileGen<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    masm: MacroAssembler,
    dbg: bool,
}

impl<'a, 'ast> DoraCompileGen<'a, 'ast>
where
    'ast: 'a,
{
    pub fn generate(mut self) -> JitBaselineFct {
        let offset_dtn = 0;
        let offset_params = offset_dtn + size_of::<DoraToNativeInfo>() as i32;
        let offset_thread =
            offset_params + (FREG_PARAMS.len() + REG_PARAMS.len()) as i32 * mem::ptr_width();
        let framesize = mem::align_i32(offset_thread + mem::ptr_width(), 16) as i32;

        if self.dbg {
            self.masm.debug();
        }

        // the return address is the call-site we need to patch
        self.masm.prolog_size(framesize);

        // store params passed in registers on the stack
        self.store_params(offset_params);

        // prepare the native call
        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::last_offset()),
            REG_TMP1.into(),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::fp_offset()),
            REG_FP.into(),
        );

        self.masm.copy_pc(REG_TMP1);

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::pc_offset()),
            REG_TMP1.into(),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
            REG_SP.into(),
        );

        // invoke the compiler for the call site
        self.masm.load_mem(
            MachineMode::Ptr,
            REG_PARAMS[0].into(),
            Mem::Base(REG_FP, mem::ptr_width()),
        );
        self.masm.load_mem(
            MachineMode::Ptr,
            REG_PARAMS[1].into(),
            Mem::Base(REG_SP, offset_params),
        );
        self.masm.raw_call(compile_request as *const u8);

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::last_offset()),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
            REG_TMP1.into(),
        );

        // restore argument registers from the stack
        self.load_params(offset_params);

        // remove the stack frame
        self.masm.epilog_without_return();

        // jump to compiled function
        self.masm.jump_reg(REG_RESULT);

        self.masm
            .jit(self.vm, framesize, JitDescriptor::CompileStub, false)
    }

    fn store_params(&mut self, mut offset: i32) {
        for reg in &REG_PARAMS {
            self.masm
                .store_mem(MachineMode::Ptr, Mem::Base(REG_SP, offset), (*reg).into());
            offset += mem::ptr_width();
        }

        for reg in &FREG_PARAMS {
            self.masm.store_mem(
                MachineMode::Float64,
                Mem::Base(REG_SP, offset),
                (*reg).into(),
            );
            offset += mem::ptr_width();
        }
    }

    fn load_params(&mut self, mut offset: i32) {
        for reg in &REG_PARAMS {
            self.masm
                .load_mem(MachineMode::Ptr, (*reg).into(), Mem::Base(REG_SP, offset));
            offset += mem::ptr_width();
        }

        for reg in &FREG_PARAMS {
            self.masm.load_mem(
                MachineMode::Float64,
                (*reg).into(),
                Mem::Base(REG_SP, offset),
            );
            offset += mem::ptr_width();
        }
    }
}

fn compile_request(ra: usize, receiver: Address) -> Address {
    let vm = get_vm();

    let lazy_compilation_site = {
        let data = {
            let code_map = vm.code_map.lock();
            code_map.get(ra.into()).expect("return address not found")
        };

        let fct_id = match data {
            CodeDescriptor::DoraFct(fct_id) => fct_id,
            _ => panic!("expected function for code"),
        };

        let jit_fct = vm.jit_fcts.idx(fct_id);

        let offset = ra - jit_fct.instruction_start().to_usize();
        let jit_fct = jit_fct.to_base().expect("baseline expected");
        jit_fct
            .lazy_compilation
            .get(offset as u32)
            .expect("bailout info not found")
            .clone()
    };

    match lazy_compilation_site {
        LazyCompilationSite::Compile(fct_id, disp, ref cls_tps, ref fct_tps) => {
            patch_fct_call(vm, ra, fct_id, cls_tps, fct_tps, disp)
        }

        LazyCompilationSite::VirtCompile(vtable_index, ref cls_tps, ref fct_tps) => {
            patch_vtable_call(vm, receiver, vtable_index, cls_tps, fct_tps)
        }
    }
}

fn patch_vtable_call(
    vm: &VM,
    receiver: Address,
    vtable_index: u32,
    cls_tps: &TypeList,
    fct_tps: &TypeList,
) -> Address {
    let obj = unsafe { &mut *receiver.to_mut_ptr::<Obj>() };
    let vtable = obj.header().vtbl();
    let cls_id = vtable.class().cls_id.expect("no corresponding class");
    let cls = vm.classes.idx(cls_id);
    let cls = cls.read();

    let fct_id = cls.virtual_fcts[vtable_index as usize];
    let fct_ptr = compiler::generate(vm, fct_id, cls_tps, fct_tps);

    let methodtable = vtable.table_mut();
    methodtable[vtable_index as usize] = fct_ptr.to_usize();

    fct_ptr
}

fn patch_fct_call(
    vm: &VM,
    ra: usize,
    fct_id: FctId,
    cls_tps: &TypeList,
    fct_tps: &TypeList,
    disp: i32,
) -> Address {
    let fct_ptr = compiler::generate(vm, fct_id, cls_tps, fct_tps);
    let fct_addr: *mut usize = (ra as isize - disp as isize) as *mut _;

    // update function pointer in data segment
    unsafe {
        *fct_addr = fct_ptr.to_usize();
    }

    fct_ptr
}
<commit_msg>remove usage of to_base()<commit_after>use std::mem::size_of;

use crate::compiler;
use crate::compiler::fct::{JitBaselineFct, JitDescriptor, JitFct, LazyCompilationSite};
use crate::compiler::map::CodeDescriptor;
use crate::cpu::{Mem, FREG_PARAMS, REG_FP, REG_PARAMS, REG_RESULT, REG_SP, REG_THREAD, REG_TMP1};
use crate::exception::DoraToNativeInfo;
use crate::gc::Address;
use crate::masm::MacroAssembler;
use crate::mem;
use crate::object::Obj;
use crate::threads::ThreadLocalData;
use crate::ty::{MachineMode, TypeList};
use crate::vm::FctId;
use crate::vm::{get_vm, VM};

// This code generates the compiler stub, there should only be one instance
// of this function be used in Dora. It is necessary for lazy compilation, where
// functions are only compiled on their first invocation. The compiler can use
// the address of this stub for invocations of functions that have not been compiled
// yet. The stub compiles the function and patches the call site to invoke the
// now-compiled function directly on the next invocation. In the end the function is
// executed.

pub fn generate<'a, 'ast: 'a>(vm: &'a VM<'ast>) -> Address {
    let ngen = DoraCompileGen {
        vm,
        masm: MacroAssembler::new(),
        dbg: vm.args.flag_emit_debug_compile,
    };

    let jit_fct = ngen.generate();
    let addr = jit_fct.instruction_start();
    vm.insert_code_map(
        jit_fct.ptr_start(),
        jit_fct.ptr_end(),
        CodeDescriptor::CompileStub,
    );
    vm.jit_fcts.push(JitFct::Base(jit_fct));

    addr
}

struct DoraCompileGen<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    masm: MacroAssembler,
    dbg: bool,
}

impl<'a, 'ast> DoraCompileGen<'a, 'ast>
where
    'ast: 'a,
{
    pub fn generate(mut self) -> JitBaselineFct {
        let offset_dtn = 0;
        let offset_params = offset_dtn + size_of::<DoraToNativeInfo>() as i32;
        let offset_thread =
            offset_params + (FREG_PARAMS.len() + REG_PARAMS.len()) as i32 * mem::ptr_width();
        let framesize = mem::align_i32(offset_thread + mem::ptr_width(), 16) as i32;

        if self.dbg {
            self.masm.debug();
        }

        // the return address is the call-site we need to patch
        self.masm.prolog_size(framesize);

        // store params passed in registers on the stack
        self.store_params(offset_params);

        // prepare the native call
        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::last_offset()),
            REG_TMP1.into(),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::fp_offset()),
            REG_FP.into(),
        );

        self.masm.copy_pc(REG_TMP1);

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::pc_offset()),
            REG_TMP1.into(),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
            REG_SP.into(),
        );

        // invoke the compiler for the call site
        self.masm.load_mem(
            MachineMode::Ptr,
            REG_PARAMS[0].into(),
            Mem::Base(REG_FP, mem::ptr_width()),
        );
        self.masm.load_mem(
            MachineMode::Ptr,
            REG_PARAMS[1].into(),
            Mem::Base(REG_SP, offset_params),
        );
        self.masm.raw_call(compile_request as *const u8);

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::last_offset()),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
            REG_TMP1.into(),
        );

        // restore argument registers from the stack
        self.load_params(offset_params);

        // remove the stack frame
        self.masm.epilog_without_return();

        // jump to compiled function
        self.masm.jump_reg(REG_RESULT);

        self.masm
            .jit(self.vm, framesize, JitDescriptor::CompileStub, false)
    }

    fn store_params(&mut self, mut offset: i32) {
        for reg in &REG_PARAMS {
            self.masm
                .store_mem(MachineMode::Ptr, Mem::Base(REG_SP, offset), (*reg).into());
            offset += mem::ptr_width();
        }

        for reg in &FREG_PARAMS {
            self.masm.store_mem(
                MachineMode::Float64,
                Mem::Base(REG_SP, offset),
                (*reg).into(),
            );
            offset += mem::ptr_width();
        }
    }

    fn load_params(&mut self, mut offset: i32) {
        for reg in &REG_PARAMS {
            self.masm
                .load_mem(MachineMode::Ptr, (*reg).into(), Mem::Base(REG_SP, offset));
            offset += mem::ptr_width();
        }

        for reg in &FREG_PARAMS {
            self.masm.load_mem(
                MachineMode::Float64,
                (*reg).into(),
                Mem::Base(REG_SP, offset),
            );
            offset += mem::ptr_width();
        }
    }
}

fn compile_request(ra: usize, receiver: Address) -> Address {
    let vm = get_vm();

    let lazy_compilation_site = {
        let data = {
            let code_map = vm.code_map.lock();
            code_map.get(ra.into()).expect("return address not found")
        };

        let fct_id = match data {
            CodeDescriptor::DoraFct(fct_id) => fct_id,
            _ => panic!("expected function for code"),
        };

        let jit_fct = vm.jit_fcts.idx(fct_id);

        let offset = ra - jit_fct.instruction_start().to_usize();
        jit_fct
            .lazy_for_offset(offset as u32)
            .expect("lazy compilation site not found")
            .clone()
    };

    match lazy_compilation_site {
        LazyCompilationSite::Compile(fct_id, disp, ref cls_tps, ref fct_tps) => {
            patch_fct_call(vm, ra, fct_id, cls_tps, fct_tps, disp)
        }

        LazyCompilationSite::VirtCompile(vtable_index, ref cls_tps, ref fct_tps) => {
            patch_vtable_call(vm, receiver, vtable_index, cls_tps, fct_tps)
        }
    }
}

fn patch_vtable_call(
    vm: &VM,
    receiver: Address,
    vtable_index: u32,
    cls_tps: &TypeList,
    fct_tps: &TypeList,
) -> Address {
    let obj = unsafe { &mut *receiver.to_mut_ptr::<Obj>() };
    let vtable = obj.header().vtbl();
    let cls_id = vtable.class().cls_id.expect("no corresponding class");
    let cls = vm.classes.idx(cls_id);
    let cls = cls.read();

    let fct_id = cls.virtual_fcts[vtable_index as usize];
    let fct_ptr = compiler::generate(vm, fct_id, cls_tps, fct_tps);

    let methodtable = vtable.table_mut();
    methodtable[vtable_index as usize] = fct_ptr.to_usize();

    fct_ptr
}

fn patch_fct_call(
    vm: &VM,
    ra: usize,
    fct_id: FctId,
    cls_tps: &TypeList,
    fct_tps: &TypeList,
    disp: i32,
) -> Address {
    let fct_ptr = compiler::generate(vm, fct_id, cls_tps, fct_tps);
    let fct_addr: *mut usize = (ra as isize - disp as isize) as *mut _;

    // update function pointer in data segment
    unsafe {
        *fct_addr = fct_ptr.to_usize();
    }

    fct_ptr
}
<|endoftext|>