<commit_before>use pipeline::chain::{Chain, Linker};
use pipeline::{Stage};
use pipeline::context::PipelineContext;
use future::{Promise};
use void::Void;

pub struct Pipeline<S, Start> {
    list: Option<Start>,
    socket: S,
}

impl<S, Start: Chain<S> + Stage<S>> Pipeline<S, Start> {
    pub fn new(socket: S) -> Pipeline<S, Start> {
        Pipeline {
            list: None,
            socket: socket,
        }
    }

    pub fn add_stage<St>(self, stage: St) -> Pipeline<S, Linker<St, Start>>
    where St: Stage<S, ReadOutput=Start::ReadInput, WriteInput=Start::WriteOutput> {
            match self {
                Pipeline {
                    list: stages,
                    socket: s,
                } => {
                    let stages = prepend_stage(stage, stages);
                    Pipeline {
                        list: Some(stages),
                        socket: s,
                    }
                }
            }
        }
}

impl<S, Start: Chain<S, ReadInput=()>> Pipeline<S, Start> {
    /// Calls connected method and then writable.
    pub fn connected(&mut self) {
        let list = &mut self.list;
        let socket = &mut self.socket;

        list.as_mut().and_then(|chain| {
            do_connected_iteration(chain, socket);
            do_writable_iteration(chain, socket)
        });
    }

    pub fn readable(&mut self) {
        let list = &mut self.list;
        let socket = &mut self.socket;

        list.as_mut().and_then(|chain| {
            do_read_and_write_iteration(chain, (), socket)
        });
    }

    /// Signifies that the socket is now writable. This will call stages 'writable' method until a
    /// stage returns data to write or there are no more stages.
    pub fn writable(&mut self) {
        let list = &mut self.list;
        let socket = &mut self.socket;

        list.as_mut().and_then(|chain| {
            do_writable_iteration(chain, socket)
        });
    }
}

fn do_connected_iteration<S, S2, C>(chain: &mut C, socket: &mut S)
where C: Chain<S, Next=S2>,
S2: Chain<S, ReadInput=C::ReadOutput, WriteOutput=C::WriteInput> {
    chain.next_stage_mut().map(|c| {
        do_connected_iteration(c, socket);
    });

    let mut ctx = PipelineContext::<_, Void>::new(socket);
    chain.connected(&mut ctx);
}

fn do_writable_iteration<S, S2, C>(chain: &mut C, socket: &mut S)
-> Option<(C::WriteOutput, Promise<()>)>
    where C: Chain<S, Next=S2>,
          S2: Chain<S, ReadInput=C::ReadOutput, WriteOutput=C::WriteInput> {
              // Recurse first so that we call writable on last stage first and flow back from there
              let to_write = chain.next_stage_mut().and_then(|c| {
                  do_writable_iteration(c, socket)
              });

              // If there is nothing to write, check to see if the current stage has something,
              // else call write method on current stage without calling writable
              match to_write {
                  None => {
                      let mut ctx = PipelineContext::<_, Void>::new(socket);
                      chain.writable(&mut ctx)
                  },
                  Some((write, promise)) => {
                      let mut ctx = PipelineContext::<_, Void>::new(socket);
                      chain.write(&mut ctx, write, promise)
                  }
              }
          }

fn do_read_and_write_iteration<S, R, S2, C>(chain: &mut C, input: R, socket: &mut S)
-> Option<(C::WriteOutput, Promise<()>)>
    where C: Chain<S, Next=S2, ReadInput=R>,
          S2: Chain<S, ReadInput=C::ReadOutput, WriteOutput=C::WriteInput> {
    let (read_out, to_write) = {
        let mut ctx = PipelineContext::new(socket);
        let read = chain.read(&mut ctx, input);
        (read, ctx.into())
    };

    // Return early if read stage wants to write, this silently discards read_out var
    // TODO: Is there are better way to handle this case?
    if to_write.is_some() {
        return to_write
    }

    // Recurse until stage doesn't return Some(read_val) or there are no more stages
    read_out.and_then(|read_val| {
        chain.next_stage_mut().and_then(|c| {
            do_read_and_write_iteration(c, read_val, socket)
        })
    }).and_then(|(input, promise)| {
        // Do not let write stage write anything
        let mut ctx = PipelineContext::<_, Void>::new(socket);
        chain.write(&mut ctx, input, promise)
    })
}

fn prepend_stage<S, St, C>(stage: St, chain: Option<C>) -> Linker<St, C>
where St: Stage<S>,
C: Chain<S> + Stage<S, ReadInput=St::ReadOutput, WriteOutput=St::WriteInput>
{
    match chain {
        Some(c) => {
            let mut linker = Linker::new(stage);
            linker.add_stage(c);
            linker
        },
        None => {
            Linker::new(stage)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ferrous::dsl::*;
    use pipeline::{Stage, ReadOnlyStage, WriteOnlyStage, WriteStage, ReadStage};
    use pipeline::chain::End;
    use test_helpers::{FakeReadStage, FakeReadWriteStage, FakePassthroughStage, FakeWriteStage, FakeBaseStage};
    use std::io::{self, Write, Read};
    use std::sync::{Arc, Mutex};

    struct Stub;

    #[test]
    fn test_pipeline_add_read_stage() {
        let pipeline = Pipeline::<_, End<io::Result<()>, u8>>::new(Stub).
            add_stage(ReadOnlyStage::<_, u8>::new(FakeReadStage::new()));
    }

    #[test]
    fn test_pipeline_add_write_stage() {
        let pipeline = Pipeline::<_, End<u8, u8>>::new(Stub).
            add_stage(WriteOnlyStage::<u8, _>::new(FakeWriteStage::new()));
    }

    #[test]
    fn test_pipeline_writable() {
        let (_send, _recv, stage) = FakeBaseStage::new();

        let last_stage = Arc::new(Mutex::new(FakeReadWriteStage::new()));

        let mut pipeline = Pipeline::<_, End<Vec<u8>, Vec<u8>>>::new(Stub).
            add_stage(last_stage.clone()).
            add_stage(FakePassthroughStage::<Vec<u8>, Vec<u8>>::new()).
            add_stage(stage);

        // FakeReadWriteStage will send a vec to be written
        pipeline.writable();

        expect(&last_stage.lock().unwrap().get_writable_future()).to(be_ok());
    }

    #[test]
    fn test_pipeline_connected() {
        let (_send, recv, stage) = FakeBaseStage::new();
        let read = vec!(3,3,3);

        let last_stage = Arc::new(Mutex::new(FakeReadWriteStage::new()));

        let mut pipeline = Pipeline::<_, End<Vec<u8>, Vec<u8>>>::new(Stub).
            add_stage(last_stage.clone()).
            add_stage(FakePassthroughStage::<Vec<u8>, Vec<u8>>::new()).
            add_stage(stage);
        // 2. Initiate a connect for pipeline
        pipeline.connected();
        // 3. Last read stage should write vector
        // 5. Assert that write was received
        let written = recv.try_recv().unwrap();

        expect(&written).to(equal(&read));
        expect(&last_stage.lock().unwrap().get_writable_future()).to(be_ok());
    }

    #[test]
    fn test_pipeline_read_write_cycle() {
        // 1. Multiple stage pipeline
        let (send, recv, stage) = FakeBaseStage::new();
        let read = vec!(1,2,3,4,5);
        send.send(read.clone()).unwrap();

        let last_stage = Arc::new(Mutex::new(FakeReadWriteStage::new()));

        let mut pipeline = Pipeline::<_, End<Vec<u8>, Vec<u8>>>::new(Stub).
            add_stage(last_stage.clone()).
            add_stage(FakePassthroughStage::<Vec<u8>, Vec<u8>>::new()).
            add_stage(stage);
        // 2. Initiate a read for pipeline
        pipeline.readable();
        // 3. Last read stage should write back
        // 5. Assert that write was received
        let written = recv.try_recv().unwrap();

        expect(&written).to(equal(&read));
        expect(&last_stage.lock().unwrap().get_future()).to(be_ok());
    }
}
<commit_msg>Add closed method to Pipeline<commit_after>use pipeline::chain::{Chain, Linker};
use pipeline::{Stage};
use pipeline::context::PipelineContext;
use future::{Promise};
use void::Void;

pub struct Pipeline<S, Start> {
    list: Option<Start>,
    socket: S,
}

impl<S, Start: Chain<S> + Stage<S>> Pipeline<S, Start> {
    pub fn new(socket: S) -> Pipeline<S, Start> {
        Pipeline {
            list: None,
            socket: socket,
        }
    }

    pub fn add_stage<St>(self, stage: St) -> Pipeline<S, Linker<St, Start>>
    where St: Stage<S, ReadOutput=Start::ReadInput, WriteInput=Start::WriteOutput> {
            match self {
                Pipeline {
                    list: stages,
                    socket: s,
                } => {
                    let stages = prepend_stage(stage, stages);
                    Pipeline {
                        list: Some(stages),
                        socket: s,
                    }
                }
            }
        }
}

impl<S, Start: Chain<S, ReadInput=()>> Pipeline<S, Start> {
    /// Calls connected method and then writable.
    pub fn connected(&mut self) {
        let list = &mut self.list;
        let socket = &mut self.socket;

        list.as_mut().and_then(|chain| {
            do_connected_iteration(chain, socket);
            do_writable_iteration(chain, socket)
        });
    }

    pub fn closed(&mut self) {
        let list = &mut self.list;
        let socket = &mut self.socket;

        list.as_mut().map(|chain| {
            do_closed_iteration(chain, socket);
        });
    }

    pub fn readable(&mut self) {
        let list = &mut self.list;
        let socket = &mut self.socket;

        list.as_mut().and_then(|chain| {
            do_read_and_write_iteration(chain, (), socket)
        });
    }

    /// Signifies that the socket is now writable. This will call stages 'writable' method until a
    /// stage returns data to write or there are no more stages.
    pub fn writable(&mut self) {
        let list = &mut self.list;
        let socket = &mut self.socket;

        list.as_mut().and_then(|chain| {
            do_writable_iteration(chain, socket)
        });
    }
}

fn do_closed_iteration<S, S2, C>(chain: &mut C, socket: &mut S)
where C: Chain<S, Next=S2>,
S2: Chain<S, ReadInput=C::ReadOutput, WriteOutput=C::WriteInput> {
    chain.next_stage_mut().map(|c| {
        do_closed_iteration(c, socket);
    });

    let mut ctx = PipelineContext::<_, Void>::new(socket);
    chain.closed(&mut ctx);
}

fn do_connected_iteration<S, S2, C>(chain: &mut C, socket: &mut S)
where C: Chain<S, Next=S2>,
S2: Chain<S, ReadInput=C::ReadOutput, WriteOutput=C::WriteInput> {
    chain.next_stage_mut().map(|c| {
        do_connected_iteration(c, socket);
    });

    let mut ctx = PipelineContext::<_, Void>::new(socket);
    chain.connected(&mut ctx);
}

fn do_writable_iteration<S, S2, C>(chain: &mut C, socket: &mut S)
-> Option<(C::WriteOutput, Promise<()>)>
    where C: Chain<S, Next=S2>,
          S2: Chain<S, ReadInput=C::ReadOutput, WriteOutput=C::WriteInput> {
              // Recurse first so that we call writable on last stage first and flow back from there
              let to_write = chain.next_stage_mut().and_then(|c| {
                  do_writable_iteration(c, socket)
              });

              // If there is nothing to write, check to see if the current stage has something,
              // else call write method on current stage without calling writable
              match to_write {
                  None => {
                      let mut ctx = PipelineContext::<_, Void>::new(socket);
                      chain.writable(&mut ctx)
                  },
                  Some((write, promise)) => {
                      let mut ctx = PipelineContext::<_, Void>::new(socket);
                      chain.write(&mut ctx, write, promise)
                  }
              }
          }

fn do_read_and_write_iteration<S, R, S2, C>(chain: &mut C, input: R, socket: &mut S)
-> Option<(C::WriteOutput, Promise<()>)>
    where C: Chain<S, Next=S2, ReadInput=R>,
          S2: Chain<S, ReadInput=C::ReadOutput, WriteOutput=C::WriteInput> {
    let (read_out, to_write) = {
        let mut ctx = PipelineContext::new(socket);
        let read = chain.read(&mut ctx, input);
        (read, ctx.into())
    };

    // Return early if read stage wants to write, this silently discards read_out var
    // TODO: Is there are better way to handle this case?
    if to_write.is_some() {
        return to_write
    }

    // Recurse until stage doesn't return Some(read_val) or there are no more stages
    read_out.and_then(|read_val| {
        chain.next_stage_mut().and_then(|c| {
            do_read_and_write_iteration(c, read_val, socket)
        })
    }).and_then(|(input, promise)| {
        // Do not let write stage write anything
        let mut ctx = PipelineContext::<_, Void>::new(socket);
        chain.write(&mut ctx, input, promise)
    })
}

fn prepend_stage<S, St, C>(stage: St, chain: Option<C>) -> Linker<St, C>
where St: Stage<S>,
C: Chain<S> + Stage<S, ReadInput=St::ReadOutput, WriteOutput=St::WriteInput>
{
    match chain {
        Some(c) => {
            let mut linker = Linker::new(stage);
            linker.add_stage(c);
            linker
        },
        None => {
            Linker::new(stage)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ferrous::dsl::*;
    use pipeline::{Stage, ReadOnlyStage, WriteOnlyStage, WriteStage, ReadStage};
    use pipeline::chain::End;
    use test_helpers::{FakeReadStage, FakeReadWriteStage, FakePassthroughStage, FakeWriteStage, FakeBaseStage};
    use std::io::{self, Write, Read};
    use std::sync::{Arc, Mutex};

    struct Stub;

    #[test]
    fn test_pipeline_add_read_stage() {
        let pipeline = Pipeline::<_, End<io::Result<()>, u8>>::new(Stub).
            add_stage(ReadOnlyStage::<_, u8>::new(FakeReadStage::new()));
    }

    #[test]
    fn test_pipeline_add_write_stage() {
        let pipeline = Pipeline::<_, End<u8, u8>>::new(Stub).
            add_stage(WriteOnlyStage::<u8, _>::new(FakeWriteStage::new()));
    }

    #[test]
    fn test_pipeline_writable() {
        let (_send, _recv, stage) = FakeBaseStage::new();

        let last_stage = Arc::new(Mutex::new(FakeReadWriteStage::new()));

        let mut pipeline = Pipeline::<_, End<Vec<u8>, Vec<u8>>>::new(Stub).
            add_stage(last_stage.clone()).
            add_stage(FakePassthroughStage::<Vec<u8>, Vec<u8>>::new()).
            add_stage(stage);

        // FakeReadWriteStage will send a vec to be written
        pipeline.writable();

        expect(&last_stage.lock().unwrap().get_writable_future()).to(be_ok());
    }

    #[test]
    fn test_pipeline_connected() {
        let (_send, recv, stage) = FakeBaseStage::new();
        let read = vec!(3,3,3);

        let last_stage = Arc::new(Mutex::new(FakeReadWriteStage::new()));

        let mut pipeline = Pipeline::<_, End<Vec<u8>, Vec<u8>>>::new(Stub).
            add_stage(last_stage.clone()).
            add_stage(FakePassthroughStage::<Vec<u8>, Vec<u8>>::new()).
            add_stage(stage);
        // 2. Initiate a connect for pipeline
        pipeline.connected();
        // 3. Last read stage should write vector
        // 5. Assert that write was received
        let written = recv.try_recv().unwrap();

        expect(&written).to(equal(&read));
        expect(&last_stage.lock().unwrap().get_writable_future()).to(be_ok());
    }

    #[test]
    fn test_pipeline_closed() {
        let (_send, _recv, stage) = FakeBaseStage::new();

        let last_stage = Arc::new(Mutex::new(FakeReadWriteStage::new()));

        let mut pipeline = Pipeline::<_, End<Vec<u8>, Vec<u8>>>::new(Stub).
            add_stage(last_stage.clone()).
            add_stage(FakePassthroughStage::<Vec<u8>, Vec<u8>>::new()).
            add_stage(stage);

        pipeline.closed();

        assert!(&last_stage.lock().unwrap().closed);
    }

    #[test]
    fn test_pipeline_read_write_cycle() {
        // 1. Multiple stage pipeline
        let (send, recv, stage) = FakeBaseStage::new();
        let read = vec!(1,2,3,4,5);
        send.send(read.clone()).unwrap();

        let last_stage = Arc::new(Mutex::new(FakeReadWriteStage::new()));

        let mut pipeline = Pipeline::<_, End<Vec<u8>, Vec<u8>>>::new(Stub).
            add_stage(last_stage.clone()).
            add_stage(FakePassthroughStage::<Vec<u8>, Vec<u8>>::new()).
            add_stage(stage);
        // 2. Initiate a read for pipeline
        pipeline.readable();
        // 3. Last read stage should write back
        // 5. Assert that write was received
        let written = recv.try_recv().unwrap();

        expect(&written).to(equal(&read));
        expect(&last_stage.lock().unwrap().get_future()).to(be_ok());
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![experimental]

use registry::*;
use super::ty;
use std::io::Writer;

pub struct GlobalGenerator<'a, W: 'a> {
    ns: Ns,
    writer: &'a mut W,
    registry: &'a Registry,
}

impl<'a, W: Writer> GlobalGenerator<'a, W> {
    fn new<'a>(writer: &'a mut W, registry: &'a Registry, ns: Ns) -> GlobalGenerator<'a, W> {
        GlobalGenerator {
            ns: ns,
            writer: writer,
            registry: registry,
        }
    }

    #[allow(unused_must_use)]
    fn write_str(&mut self, s: &str) {
        self.writer.write(s.as_bytes());
    }

    fn write_line(&mut self, s: &str) {
        self.write_str(s);
        self.write_str("\n");
    }

    fn write_enum(&mut self, enm: &Enum) {
        self.write_line(super::gen_enum_item(enm, "types::").as_slice());
    }

    fn write_enums(&mut self) {
        for e in self.registry.enum_iter() {
            self.write_enum(e);
        }
    }

    fn write_header(&mut self) {
        self.write_line("mod __gl_imports {");
        self.write_line("    extern crate libc;");
        self.write_line("    pub use std::mem;");
        self.write_line("}");
    }

    fn write_type_aliases(&mut self) {
        self.write_line("#[stable]");
        self.write_line("pub mod types {");
        let aliases = super::gen_type_aliases(&self.ns);
        self.write_line(aliases.as_slice());
        self.write_line("}");
    }

    fn write_fnptr_struct_def(&mut self) {
        self.write_line("pub struct FnPtr {");
        self.write_line("    f: *const __gl_imports::libc::c_void,");
        self.write_line("    is_loaded: bool,");
        self.write_line("}");
        self.write_line("");
        self.write_line("impl FnPtr {");
        self.write_line("    pub fn new(ptr: *const __gl_imports::libc::c_void, failing_fn: *const __gl_imports::libc::c_void) -> FnPtr {");
        self.write_line("        if ptr.is_null() {");
        self.write_line("            FnPtr { f: failing_fn, is_loaded: false }");
        self.write_line("        } else {");
        self.write_line("            FnPtr { f: ptr, is_loaded: true }");
        self.write_line("        }");
        self.write_line("    }");
        self.write_line("}");
    }

    fn write_failing_fns(&mut self) {
        self.write_line("mod failing {");

        self.write_line("use super::types;");
        self.write_line("use super::__gl_imports;");

        for c in self.registry.cmd_iter() {
            self.write_line(format!(
                "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                pub extern \"system\" fn {name}({params}){return_suffix} {{ \
                    fail!(\"`{name}` was not loaded\") \
                }}",
                name = c.proto.ident,
                params = super::gen_param_list(c, true),
                return_suffix = super::gen_return_suffix(c)
            ).as_slice());
        }

        self.write_line("}");
    }

    fn write_fns(&mut self) {
        for c in self.registry.cmd_iter() {
            self.write_line(
                if c.is_safe {
                    format!(
                        "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                        #[inline] #[unstable] pub fn {name}({params}){return_suffix} {{ \
                            unsafe {{ \
                                __gl_imports::mem::transmute::<_, extern \"system\" fn({types}){return_suffix}>\
                                    (storage::{name}.f)({idents}) \
                            }} \
                        }}",
                        name = c.proto.ident,
                        params = super::gen_param_list(c, true),
                        types = super::gen_param_ty_list(c),
                        return_suffix = super::gen_return_suffix(c),
                        idents = super::gen_param_ident_list(c),
                    )
                } else {
                    format!(
                        "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                        #[inline] #[unstable] pub unsafe fn {name}({typed_params}){return_suffix} {{ \
                            __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) {return_suffix}>\
                                (storage::{name}.f)({idents}) \
                        }}",
                        name = c.proto.ident,
                        typed_params = super::gen_param_list(c, true),
                        return_suffix = super::gen_return_suffix(c),
                        idents = super::gen_param_ident_list(c),
                    )
                }.as_slice()
            );
        }
    }

    fn write_ptrs(&mut self) {
        self.write_line("mod storage {");
        self.write_line("#![allow(non_snake_case)]");
        self.write_line("use super::__gl_imports::libc;");
        self.write_line("use super::failing;");
        self.write_line("use super::FnPtr;");

        for c in self.registry.cmd_iter() {
            self.write_line(format!(
                "pub static mut {name}: FnPtr = FnPtr {{ \
                    f: failing::{name} as *const libc::c_void, \
                    is_loaded: false \
                }};",
                name = c.proto.ident,
            ).as_slice());
        };

        self.write_line("}");
    }

    fn write_fn_mods(&mut self) {
        for c in self.registry.cmd_iter() {
            let ns = self.ns;
            self.write_line(format!(
                "#[unstable]
                #[allow(non_snake_case)]
                pub mod {0} {{
                    use super::{{failing, storage}};
                    use super::FnPtr;

                    #[inline]
                    #[allow(dead_code)]
                    pub fn is_loaded() -> bool {{
                        unsafe {{ storage::{0}.is_loaded }}
                    }}

                    #[allow(dead_code)]
                    pub fn load_with(loadfn: |symbol: &str| -> *const super::__gl_imports::libc::c_void) {{
                        unsafe {{
                            storage::{0} = FnPtr::new(loadfn(\"{1}\"),
                                failing::{0} as *const super::__gl_imports::libc::c_void)
                        }}
                    }}
                }}",
                c.proto.ident, super::gen_symbol_name(&ns, c)).as_slice());
        }
        // for c in self.registry.cmd_iter() {
        //     self.write_line(format!(
        //         "pub mod {name} {{ \
        //             #[inline] \
        //             pub fn is_loaded() -> bool {{ \
        //                 unsafe {{ ::storage::{name}.is_loaded }} \
        //             }} \
        //         }}",
        //         name = c.proto.ident,
        //     ).as_slice());
        // }
    }

    fn write_load_fn(&mut self) {
        self.write_line("/// Load each OpenGL symbol using a custom load function. This allows for the");
        self.write_line("/// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.");
        self.write_line("///");
        self.write_line("/// ~~~ignore");
        self.write_line("/// gl::load_with(|s| glfw.get_proc_address(s));");
        self.write_line("/// ~~~");
        self.write_line("#[unstable]");
        self.write_line("#[allow(dead_code)]");
        self.write_line("pub fn load_with(loadfn: |symbol: &str| -> *const __gl_imports::libc::c_void) {");

        for c in self.registry.cmd_iter() {
            self.write_line(format!("{}::load_with(|s| loadfn(s));", c.proto.ident).as_slice());
        }

        self.write_line("}");
    }

    pub fn write(writer: &mut W, registry: &Registry, ns: Ns) {
        let mut gen = GlobalGenerator::new(writer, registry, ns);

        // header with imports
        gen.write_header();
        gen.write_line("");

        // type aliases
        gen.write_type_aliases();
        gen.write_line("");

        // enums definitions
        gen.write_enums();
        gen.write_line("");

        // safe and unsafe OpenGl functions
        gen.write_fns();
        gen.write_line("");

        // FnPtr struct def
        gen.write_fnptr_struct_def();
        gen.write_line("");

        // static muts for storing function pointers
        gen.write_ptrs();
        gen.write_line("");

        // functions for querying the status of individual function pointers
        gen.write_fn_mods();
        gen.write_line("");

        // failing functions to assign to the function pointers
        gen.write_failing_fns();
        gen.write_line("");

        // loader function
        gen.write_load_fn();
        gen.write_line("");
    }
}
<commit_msg>Now using global functions in global generator<commit_after>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![experimental]

use registry::*;
use super::ty;

pub struct GlobalGenerator;

impl GlobalGenerator {
    pub fn write<W: Writer>(writer: &mut W, registry: &Registry, ns: Ns) {
        writeln!(writer, "{}", write_header());
        writeln!(writer, "{}", write_type_aliases(&ns));
        writeln!(writer, "{}", write_enums(registry));
        writeln!(writer, "{}", write_fns(registry));
        writeln!(writer, "{}", write_fnptr_struct_def());
        writeln!(writer, "{}", write_ptrs(registry));
        writeln!(writer, "{}", write_fn_mods(registry, &ns));
        writeln!(writer, "{}", write_failing_fns(registry));
        writeln!(writer, "{}", write_load_fn(registry));
    }
}

fn write_enum(enm: &Enum) -> String {
    super::gen_enum_item(enm, "types::")
}

fn write_enums(registry: &Registry) -> String {
    registry.enum_iter().map(|e| write_enum(e)).collect::<Vec<String>>().connect("\n")
}

fn write_header() -> String {
    format!(
        "mod __gl_imports {{
            extern crate libc;
            pub use std::mem;
        }}"
    )
}

fn write_type_aliases(ns: &Ns) -> String {
    format!(
        "#[stable]
        pub mod types {{
            {}
        }}
        ",

        super::gen_type_aliases(ns)
    )
}

fn write_fnptr_struct_def() -> String {
    format!(
        "pub struct FnPtr {{
            f: *const __gl_imports::libc::c_void,
            is_loaded: bool,
        }}

        impl FnPtr {{
            pub fn new(ptr: *const __gl_imports::libc::c_void, failing_fn: *const __gl_imports::libc::c_void) -> FnPtr {{
                if ptr.is_null() {{
                    FnPtr {{ f: failing_fn, is_loaded: false }}
                }} else {{
                    FnPtr {{ f: ptr, is_loaded: true }}
                }}
            }}
        }}"
    )
}

fn write_failing_fns(registry: &Registry) -> String {
    format!(
        "mod failing {{
            use super::types;
            use super::__gl_imports;

            {functions}
        }}",

        functions = registry.cmd_iter().map(|c| {
            format!(
                "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                pub extern \"system\" fn {name}({params}){return_suffix} {{ \
                    fail!(\"`{name}` was not loaded\") \
                }}",
                name = c.proto.ident,
                params = super::gen_param_list(c, true),
                return_suffix = super::gen_return_suffix(c)
            )
        }).collect::<Vec<String>>().connect("\n")
    )
}

fn write_fns(registry: &Registry) -> String {
    registry.cmd_iter().map(|c| {
        if c.is_safe {
            format!(
                "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                #[inline] #[unstable] pub fn {name}({params}){return_suffix} {{ \
                    unsafe {{ \
                        __gl_imports::mem::transmute::<_, extern \"system\" fn({types}){return_suffix}>\
                            (storage::{name}.f)({idents}) \
                    }} \
                }}",
                name = c.proto.ident,
                params = super::gen_param_list(c, true),
                types = super::gen_param_ty_list(c),
                return_suffix = super::gen_return_suffix(c),
                idents = super::gen_param_ident_list(c),
            )
        } else {
            format!(
                "#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]
                #[inline] #[unstable] pub unsafe fn {name}({typed_params}){return_suffix} {{ \
                    __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) {return_suffix}>\
                        (storage::{name}.f)({idents}) \
                }}",
                name = c.proto.ident,
                typed_params = super::gen_param_list(c, true),
                return_suffix = super::gen_return_suffix(c),
                idents = super::gen_param_ident_list(c),
            )
        }
    }).collect::<Vec<String>>().connect("\n")
}

fn write_ptrs(registry: &Registry) -> String {
    format!(
        "mod storage {{
            #![allow(non_snake_case)]
            use super::__gl_imports::libc;
            use super::failing;
            use super::FnPtr;

            {storages}
        }}",

        storages = registry.cmd_iter().map(|c| {
            format!(
                "pub static mut {name}: FnPtr = FnPtr {{ \
                    f: failing::{name} as *const libc::c_void, \
                    is_loaded: false \
                }};",
                name = c.proto.ident,
            )
        }).collect::<Vec<String>>().connect("\n")
    )
}

fn write_fn_mods(registry: &Registry, ns: &Ns) -> String {
    registry.cmd_iter().map(|c| {
        format!(
            "#[unstable]
            #[allow(non_snake_case)]
            pub mod {0} {{
                use super::{{failing, storage}};
                use super::FnPtr;

                #[inline]
                #[allow(dead_code)]
                pub fn is_loaded() -> bool {{
                    unsafe {{ storage::{0}.is_loaded }}
                }}

                #[allow(dead_code)]
                pub fn load_with(loadfn: |symbol: &str| -> *const super::__gl_imports::libc::c_void) {{
                    unsafe {{
                        storage::{0} = FnPtr::new(loadfn(\"{1}\"),
                            failing::{0} as *const super::__gl_imports::libc::c_void)
                    }}
                }}
            }}",
            c.proto.ident,
            super::gen_symbol_name(ns, c)
        )
    }).collect::<Vec<String>>().connect("\n")

    // TODO: this is a reliquate from an old code, I have no idea what it does
    // for c in self.registry.cmd_iter() {
    //     self.write_line(format!(
    //         "pub mod {name} {{ \
    //             #[inline] \
    //             pub fn is_loaded() -> bool {{ \
    //                 unsafe {{ ::storage::{name}.is_loaded }} \
    //             }} \
    //         }}",
    //         name = c.proto.ident,
    //     ).as_slice());
    // }
}

fn write_load_fn(registry: &Registry) -> String {
    format!(
        "/// Load each OpenGL symbol using a custom load function. This allows for the
        /// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
        /// ~~~ignore
        /// gl::load_with(|s| glfw.get_proc_address(s));
        /// ~~~
        #[unstable]
        #[allow(dead_code)]
        pub fn load_with(loadfn: |symbol: &str| -> *const __gl_imports::libc::c_void) {{
            {exprs}
        }}",

        exprs = registry.cmd_iter().map(|c| {
            format!("{}::load_with(|s| loadfn(s));", c.proto.ident)
        }).collect::<Vec<String>>().connect("\n")
    )
}
<|endoftext|>


<commit_before>use nn::{Module, InitModuleStruct, GetFieldStruct, ModDelegate, ModIntf, Parameter};
use nn::functional as F;
use autograd::Variable;
use std::marker::PhantomData;
use tensor::NumLimits;

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct LinearArgs<T: ::tensor::NumLimits> {
    in_features: usize,
    out_features: usize,
    #[builder(default="true")]
    bias: bool,
    #[builder(default="PhantomData")]
    phantom: PhantomData<T>,
}
impl<T: ::tensor::NumLimits> LinearArgsBuilder<T> {
    pub fn done(self) -> Linear<T> {
        let args = self.build().unwrap();
        Linear::new(args)
    }
}

#[derive(ModParse)]
pub struct Linear<T: NumLimits> {
    delegate: Module<T>,
    #[ignore]
    in_features: usize,
    #[ignore]
    out_features: usize,
    weight: Parameter<T>,
    bias: Option<Parameter<T>>,
}

impl<T: NumLimits> Linear<T> {
    pub fn build(in_features: usize, out_features: usize) -> LinearArgsBuilder<T> {
        LinearArgsBuilder::default()
            .in_features(in_features)
            .out_features(out_features)
    }
    pub fn new(args: LinearArgs<T>) -> Linear<T> {
        let bias = if args.bias {
            Some(Parameter::new((args.out_features)))
        } else {
            None
        };
        Linear {
                delegate: Module::new(),
                in_features: args.in_features,
                out_features: args.out_features,
                weight: Parameter::new((args.out_features, args.in_features)),
                bias: bias,
            }
            .init_module()
            /* XXX reset (initialize) parameters */
    }
}
impl_mod_delegate!(Linear);

impl<T: NumLimits> ModIntf<T> for Linear<T> {
    fn forward(&mut self, input: &mut Variable<T>) -> Variable<T> {
        let bias = if let Some(ref mut bias) = self.bias {
            Some(&mut bias.v)
        } else {
            None
        };
        F::linear(&input, &mut self.weight.v, bias)
    }
}
<commit_msg>initialize parameters<commit_after>use nn::{Module, InitModuleStruct, GetFieldStruct, ModDelegate, ModIntf, Parameter};
use nn::functional as F;
use autograd::Variable;
use std::marker::PhantomData;
use tensor::NumLimits;

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct LinearArgs<T: ::tensor::NumLimits> {
    in_features: usize,
    out_features: usize,
    #[builder(default="true")]
    bias: bool,
    #[builder(default="PhantomData")]
    phantom: PhantomData<T>,
}
impl<T: ::tensor::NumLimits> LinearArgsBuilder<T> {
    pub fn done(self) -> Linear<T> {
        let args = self.build().unwrap();
        Linear::new(args)
    }
}

#[derive(ModParse)]
pub struct Linear<T: NumLimits> {
    delegate: Module<T>,
    #[ignore]
    in_features: usize,
    #[ignore]
    out_features: usize,
    weight: Parameter<T>,
    bias: Option<Parameter<T>>,
}

impl<T: NumLimits> Linear<T> {
    pub fn build(in_features: usize, out_features: usize) -> LinearArgsBuilder<T> {
        LinearArgsBuilder::default()
            .in_features(in_features)
            .out_features(out_features)
    }
    fn reset_parameters(mut self) -> Self {
        let stdv : f64 = 1. / (self.in_features as f64).sqrt();
        self.weight.v.data().uniform_((-stdv, stdv));
        if let Some(ref mut bias) = self.bias {
            bias.v.data().uniform_((-stdv, stdv));
        }
        self
    }
    pub fn new(args: LinearArgs<T>) -> Linear<T> {
        let bias = if args.bias {
            Some(Parameter::new((args.out_features)))
        } else {
            None
        };
        Linear {
                delegate: Module::new(),
                in_features: args.in_features,
                out_features: args.out_features,
                weight: Parameter::new((args.out_features, args.in_features)),
                bias: bias,
            }
            .init_module()
            .reset_parameters()
    }
}
impl_mod_delegate!(Linear);

impl<T: NumLimits> ModIntf<T> for Linear<T> {
    fn forward(&mut self, input: &mut Variable<T>) -> Variable<T> {
        let bias = if let Some(ref mut bias) = self.bias {
            Some(&mut bias.v)
        } else {
            None
        };
        F::linear(&input, &mut self.weight.v, bias)
    }
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

use std::collections::HashMap;
use std::mem;
use std::ops::DerefMut;
use std::ptr::null_mut;
use std::sync::Mutex;

use windows::tools::KeyHandler;
use LoadAvg;
use ProcessorExt;

use ntapi::ntpoapi::PROCESSOR_POWER_INFORMATION;

use winapi::shared::minwindef::FALSE;
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::um::handleapi::CloseHandle;
use winapi::um::pdh::{
    PdhAddCounterW, PdhAddEnglishCounterA, PdhCloseQuery, PdhCollectQueryData,
    PdhCollectQueryDataEx, PdhGetFormattedCounterValue, PdhOpenQueryA, PdhRemoveCounter,
    PDH_FMT_COUNTERVALUE, PDH_FMT_DOUBLE, PDH_HCOUNTER, PDH_HQUERY,
};
use winapi::um::powerbase::CallNtPowerInformation;
use winapi::um::synchapi::CreateEventA;
use winapi::um::sysinfoapi::SYSTEM_INFO;
use winapi::um::winbase::{RegisterWaitForSingleObject, INFINITE};
use winapi::um::winnt::{ProcessorInformation, BOOLEAN, HANDLE, PVOID, WT_EXECUTEDEFAULT};

// This formula comes from linux's include/linux/sched/loadavg.h
// https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23
const LOADAVG_FACTOR_1F: f64 = 0.9200444146293232478931553241;
const LOADAVG_FACTOR_5F: f64 = 0.6592406302004437462547604110;
const LOADAVG_FACTOR_15F: f64 = 0.2865047968601901003248854266;
// The time interval in seconds between taking load counts, same as Linux
const SAMPLING_INTERVAL: usize = 5;

// maybe use a read/write lock instead?
static LOAD_AVG: once_cell::sync::Lazy<Mutex<Option<LoadAvg>>> =
    once_cell::sync::Lazy::new(|| unsafe { init_load_avg() });

pub(crate) fn get_load_average() -> LoadAvg {
    if let Ok(avg) = LOAD_AVG.lock() {
        if let Some(avg) = &*avg {
            return avg.clone();
        }
    }
    return LoadAvg::default();
}

unsafe extern "system" fn load_avg_callback(counter: PVOID, _: BOOLEAN) {
    let mut display_value: PDH_FMT_COUNTERVALUE = mem::MaybeUninit::uninit().assume_init();

    if PdhGetFormattedCounterValue(counter as _, PDH_FMT_DOUBLE, null_mut(), &mut display_value)
        != ERROR_SUCCESS as _
    {
        return;
    }
    if let Ok(mut avg) = LOAD_AVG.lock() {
        if let Some(avg) = avg.deref_mut() {
            let current_load = display_value.u.doubleValue();

            avg.one = avg.one * LOADAVG_FACTOR_1F + current_load * (1.0 - LOADAVG_FACTOR_1F);
            avg.five = avg.five * LOADAVG_FACTOR_5F + current_load * (1.0 - LOADAVG_FACTOR_5F);
            avg.fifteen =
                avg.fifteen * LOADAVG_FACTOR_15F + current_load * (1.0 - LOADAVG_FACTOR_15F);
        }
    }
}

unsafe fn init_load_avg() -> Mutex<Option<LoadAvg>> {
    // You can see the original implementation here: https://github.com/giampaolo/psutil
    let mut query = null_mut();

    if PdhOpenQueryA(null_mut(), 0, &mut query) != ERROR_SUCCESS as _ {
        return Mutex::new(None);
    }

    let mut counter: PDH_HCOUNTER = mem::zeroed();
    if PdhAddEnglishCounterA(
        query,
        b"\\System\\Processor Queue Length\0".as_ptr() as _,
        0,
        &mut counter,
    ) != ERROR_SUCCESS as _
    {
        PdhCloseQuery(query);
        return Mutex::new(None);
    }

    let event = CreateEventA(null_mut(), FALSE, FALSE, b"LoadUpdateEvent\0".as_ptr() as _);
    if event.is_null() {
        PdhCloseQuery(query);
        return Mutex::new(None);
    }

    if PdhCollectQueryDataEx(query, SAMPLING_INTERVAL as _, event) != ERROR_SUCCESS as _ {
        PdhCloseQuery(query);
        return Mutex::new(None);
    }

    let mut wait_handle = null_mut();
    if RegisterWaitForSingleObject(
        &mut wait_handle,
        event,
        Some(load_avg_callback),
        counter as _,
        INFINITE,
        WT_EXECUTEDEFAULT,
    ) == 0
    {
        PdhRemoveCounter(counter);
        PdhCloseQuery(query);
        Mutex::new(None)
    } else {
        Mutex::new(Some(LoadAvg::default()))
    }
}

struct InternalQuery {
    query: PDH_HQUERY,
    event: HANDLE,
    data: HashMap<String, PDH_HCOUNTER>,
}

unsafe impl Send for InternalQuery {}
unsafe impl Sync for InternalQuery {}

impl Drop for InternalQuery {
    fn drop(&mut self) {
        unsafe {
            for (_, counter) in self.data.iter() {
                PdhRemoveCounter(*counter);
            }

            if !self.event.is_null() {
                CloseHandle(self.event);
            }

            if !self.query.is_null() {
                PdhCloseQuery(self.query);
            }
        }
    }
}

pub struct Query {
    internal: InternalQuery,
}

impl Query {
    pub fn new() -> Option<Query> {
        let mut query = null_mut();
        unsafe {
            if PdhOpenQueryA(null_mut(), 0, &mut query) == ERROR_SUCCESS as i32 {
                let q = InternalQuery {
                    query,
                    event: null_mut(),
                    data: HashMap::new(),
                };
                Some(Query { internal: q })
            } else {
                None
            }
        }
    }

    pub fn get(&self, name: &String) -> Option<f32> {
        if let Some(ref counter) = self.internal.data.get(name) {
            unsafe {
                let mut display_value: PDH_FMT_COUNTERVALUE =
                    mem::MaybeUninit::uninit().assume_init();
                let counter: PDH_HCOUNTER = **counter;

                let ret = PdhGetFormattedCounterValue(
                    counter,
                    PDH_FMT_DOUBLE,
                    null_mut(),
                    &mut display_value,
                ) as u32;
                return if ret == ERROR_SUCCESS as _ {
                    let data = *display_value.u.doubleValue();
                    Some(data as f32)
                } else {
                    Some(0.)
                };
            }
        }
        None
    }

    pub fn add_counter(&mut self, name: &String, getter: Vec<u16>) -> bool {
        if self.internal.data.contains_key(name) {
            return false;
        }
        unsafe {
            let mut counter: PDH_HCOUNTER = ::std::mem::zeroed();
            let ret = PdhAddCounterW(self.internal.query, getter.as_ptr(), 0, &mut counter);
            if ret == ERROR_SUCCESS as _ {
                self.internal.data.insert(name.clone(), counter);
            } else {
                sysinfo_debug!("failed to add counter '{}': {:x}...", name, ret);
                return false;
            }
        }
        true
    }

    pub fn refresh(&self) {
        unsafe {
            if PdhCollectQueryData(self.internal.query) != ERROR_SUCCESS as _ {
                sysinfo_debug!("failed to refresh CPU data");
            }
        }
    }
}

/// Struct containing a processor information.
pub struct Processor {
    name: String,
    cpu_usage: f32,
    key_used: Option<KeyHandler>,
    vendor_id: String,
    brand: String,
    frequency: u64,
}

impl ProcessorExt for Processor {
    fn get_cpu_usage(&self) -> f32 {
        self.cpu_usage
    }

    fn get_name(&self) -> &str {
        &self.name
    }

    fn get_frequency(&self) -> u64 {
        self.frequency
    }

    fn get_vendor_id(&self) -> &str {
        &self.vendor_id
    }

    fn get_brand(&self) -> &str {
        &self.brand
    }
}

impl Processor {
    pub(crate) fn new_with_values(
        name: &str,
        vendor_id: String,
        brand: String,
        frequency: u64,
    ) -> Processor {
        Processor {
            name: name.to_owned(),
            cpu_usage: 0f32,
            key_used: None,
            vendor_id,
            brand,
            frequency,
        }
    }

    pub(crate) fn set_cpu_usage(&mut self, value: f32) {
        self.cpu_usage = value;
    }
}

fn get_vendor_id_not_great(info: &SYSTEM_INFO) -> String {
    use winapi::um::winnt;
    // https://docs.microsoft.com/fr-fr/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
    match unsafe { info.u.s() }.wProcessorArchitecture {
        winnt::PROCESSOR_ARCHITECTURE_INTEL => "Intel x86",
        winnt::PROCESSOR_ARCHITECTURE_MIPS => "MIPS",
        winnt::PROCESSOR_ARCHITECTURE_ALPHA => "RISC Alpha",
        winnt::PROCESSOR_ARCHITECTURE_PPC => "PPC",
        winnt::PROCESSOR_ARCHITECTURE_SHX => "SHX",
        winnt::PROCESSOR_ARCHITECTURE_ARM => "ARM",
        winnt::PROCESSOR_ARCHITECTURE_IA64 => "Intel Itanium-based x64",
        winnt::PROCESSOR_ARCHITECTURE_ALPHA64 => "RISC Alpha x64",
        winnt::PROCESSOR_ARCHITECTURE_MSIL => "MSIL",
        winnt::PROCESSOR_ARCHITECTURE_AMD64 => "(Intel or AMD) x64",
        winnt::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => "Intel Itanium-based x86",
        winnt::PROCESSOR_ARCHITECTURE_NEUTRAL => "unknown",
        winnt::PROCESSOR_ARCHITECTURE_ARM64 => "ARM x64",
        winnt::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => "ARM",
        winnt::PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 => "Intel Itanium-based x86",
        _ => "unknown",
    }
    .to_owned()
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) {
    #[cfg(target_arch = "x86")]
    use std::arch::x86::__cpuid;
    #[cfg(target_arch = "x86_64")]
    use std::arch::x86_64::__cpuid;

    fn add_u32(v: &mut Vec<u8>, i: u32) {
        let i = &i as *const u32 as *const u8;
        unsafe {
            v.push(*i);
            v.push(*i.offset(1));
            v.push(*i.offset(2));
            v.push(*i.offset(3));
        }
    }

    // First, we try to get the complete name.
    let res = unsafe { __cpuid(0x80000000) };
    let n_ex_ids = res.eax;
    let brand = if n_ex_ids >= 0x80000004 {
        let mut extdata = Vec::with_capacity(5);

        for i in 0x80000000..=n_ex_ids {
            extdata.push(unsafe { __cpuid(i) });
        }

        let mut out = Vec::with_capacity(4 * 4 * 3); // 4 * u32 * nb_entries
        for i in 2..5 {
            add_u32(&mut out, extdata[i].eax);
            add_u32(&mut out, extdata[i].ebx);
            add_u32(&mut out, extdata[i].ecx);
            add_u32(&mut out, extdata[i].edx);
        }
        let mut pos = 0;
        for e in out.iter() {
            if *e == 0 {
                break;
            }
            pos += 1;
        }
        match ::std::str::from_utf8(&out[..pos]) {
            Ok(s) => s.to_owned(),
            _ => String::new(),
        }
    } else {
        String::new()
    };

    // Failed to get full name, let's retry for the short version!
    let res = unsafe { __cpuid(0) };
    let mut x = Vec::with_capacity(16); // 3 * u32
    add_u32(&mut x, res.ebx);
    add_u32(&mut x, res.edx);
    add_u32(&mut x, res.ecx);
    let mut pos = 0;
    for e in x.iter() {
        if *e == 0 {
            break;
        }
        pos += 1;
    }
    let vendor_id = match ::std::str::from_utf8(&x[..pos]) {
        Ok(s) => s.to_owned(),
        Err(_) => get_vendor_id_not_great(info),
    };
    (vendor_id, brand)
}

#[cfg(all(not(target_arch = "x86_64"), not(target_arch = "x86")))]
pub fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) {
    (get_vendor_id_not_great(info), String::new())
}

pub fn get_key_used(p: &mut Processor) -> &mut Option<KeyHandler> {
    &mut p.key_used
}

// From https://stackoverflow.com/a/43813138:
//
// If your PC has 64 or fewer logical processors installed, the above code will work fine. However,
// if your PC has more than 64 logical processors installed, use GetActiveProcessorCount() or
// GetLogicalProcessorInformation() to determine the total number of logical processors installed.
pub fn get_frequencies(nb_processors: usize) -> Vec<u64> {
    let size = nb_processors * mem::size_of::<PROCESSOR_POWER_INFORMATION>();
    let mut infos: Vec<PROCESSOR_POWER_INFORMATION> = Vec::with_capacity(nb_processors);

    if unsafe {
        CallNtPowerInformation(
            ProcessorInformation,
            null_mut(),
            0,
            infos.as_mut_ptr() as _,
            size as _,
        )
    } == 0
    {
        unsafe {
            infos.set_len(nb_processors);
        }
        // infos.Number
        infos
            .into_iter()
            .map(|i| i.CurrentMhz as u64)
            .collect::<Vec<_>>()
    } else {
        vec![0; nb_processors]
    }
}
<commit_msg>Fix load average constants on windows<commit_after>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

use std::collections::HashMap;
use std::mem;
use std::ops::DerefMut;
use std::ptr::null_mut;
use std::sync::Mutex;

use windows::tools::KeyHandler;
use LoadAvg;
use ProcessorExt;

use ntapi::ntpoapi::PROCESSOR_POWER_INFORMATION;

use winapi::shared::minwindef::FALSE;
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::um::handleapi::CloseHandle;
use winapi::um::pdh::{
    PdhAddCounterW, PdhAddEnglishCounterA, PdhCloseQuery, PdhCollectQueryData,
    PdhCollectQueryDataEx, PdhGetFormattedCounterValue, PdhOpenQueryA, PdhRemoveCounter,
    PDH_FMT_COUNTERVALUE, PDH_FMT_DOUBLE, PDH_HCOUNTER, PDH_HQUERY,
};
use winapi::um::powerbase::CallNtPowerInformation;
use winapi::um::synchapi::CreateEventA;
use winapi::um::sysinfoapi::SYSTEM_INFO;
use winapi::um::winbase::{RegisterWaitForSingleObject, INFINITE};
use winapi::um::winnt::{ProcessorInformation, BOOLEAN, HANDLE, PVOID, WT_EXECUTEDEFAULT};

// This formula comes from linux's include/linux/sched/loadavg.h
// https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23
const LOADAVG_FACTOR_1F: f64 = 0.9200444146293232478931553241;
const LOADAVG_FACTOR_5F: f64 = 0.9834714538216174894737477501;
const LOADAVG_FACTOR_15F: f64 = 0.9944598480048967508795473394;
// The time interval in seconds between taking load counts, same as Linux
const SAMPLING_INTERVAL: usize = 5;

// maybe use a read/write lock instead?
static LOAD_AVG: once_cell::sync::Lazy<Mutex<Option<LoadAvg>>> =
    once_cell::sync::Lazy::new(|| unsafe { init_load_avg() });

pub(crate) fn get_load_average() -> LoadAvg {
    if let Ok(avg) = LOAD_AVG.lock() {
        if let Some(avg) = &*avg {
            return avg.clone();
        }
    }
    return LoadAvg::default();
}

unsafe extern "system" fn load_avg_callback(counter: PVOID, _: BOOLEAN) {
    let mut display_value: PDH_FMT_COUNTERVALUE = mem::MaybeUninit::uninit().assume_init();

    if PdhGetFormattedCounterValue(counter as _, PDH_FMT_DOUBLE, null_mut(), &mut display_value)
        != ERROR_SUCCESS as _
    {
        return;
    }
    if let Ok(mut avg) = LOAD_AVG.lock() {
        if let Some(avg) = avg.deref_mut() {
            let current_load = display_value.u.doubleValue();

            avg.one = avg.one * LOADAVG_FACTOR_1F + current_load * (1.0 - LOADAVG_FACTOR_1F);
            avg.five = avg.five * LOADAVG_FACTOR_5F + current_load * (1.0 - LOADAVG_FACTOR_5F);
            avg.fifteen =
                avg.fifteen * LOADAVG_FACTOR_15F + current_load * (1.0 - LOADAVG_FACTOR_15F);
        }
    }
}

unsafe fn init_load_avg() -> Mutex<Option<LoadAvg>> {
    // You can see the original implementation here: https://github.com/giampaolo/psutil
    let mut query = null_mut();

    if PdhOpenQueryA(null_mut(), 0, &mut query) != ERROR_SUCCESS as _ {
        return Mutex::new(None);
    }

    let mut counter: PDH_HCOUNTER = mem::zeroed();
    if PdhAddEnglishCounterA(
        query,
        b"\\System\\Processor Queue Length\0".as_ptr() as _,
        0,
        &mut counter,
    ) != ERROR_SUCCESS as _
    {
        PdhCloseQuery(query);
        return Mutex::new(None);
    }

    let event = CreateEventA(null_mut(), FALSE, FALSE, b"LoadUpdateEvent\0".as_ptr() as _);
    if event.is_null() {
        PdhCloseQuery(query);
        return Mutex::new(None);
    }

    if PdhCollectQueryDataEx(query, SAMPLING_INTERVAL as _, event) != ERROR_SUCCESS as _ {
        PdhCloseQuery(query);
        return Mutex::new(None);
    }

    let mut wait_handle = null_mut();
    if RegisterWaitForSingleObject(
        &mut wait_handle,
        event,
        Some(load_avg_callback),
        counter as _,
        INFINITE,
        WT_EXECUTEDEFAULT,
    ) == 0
    {
        PdhRemoveCounter(counter);
        PdhCloseQuery(query);
        Mutex::new(None)
    } else {
        Mutex::new(Some(LoadAvg::default()))
    }
}

struct InternalQuery {
    query: PDH_HQUERY,
    event: HANDLE,
    data: HashMap<String, PDH_HCOUNTER>,
}

unsafe impl Send for InternalQuery {}
unsafe impl Sync for InternalQuery {}

impl Drop for InternalQuery {
    fn drop(&mut self) {
        unsafe {
            for (_, counter) in self.data.iter() {
                PdhRemoveCounter(*counter);
            }

            if !self.event.is_null() {
                CloseHandle(self.event);
            }

            if !self.query.is_null() {
                PdhCloseQuery(self.query);
            }
        }
    }
}

pub struct Query {
    internal: InternalQuery,
}

impl Query {
    pub fn new() -> Option<Query> {
        let mut query = null_mut();
        unsafe {
            if PdhOpenQueryA(null_mut(), 0, &mut query) == ERROR_SUCCESS as i32 {
                let q = InternalQuery {
                    query,
                    event: null_mut(),
                    data: HashMap::new(),
                };
                Some(Query { internal: q })
            } else {
                None
            }
        }
    }

    pub fn get(&self, name: &String) -> Option<f32> {
        if let Some(ref counter) = self.internal.data.get(name) {
            unsafe {
                let mut display_value: PDH_FMT_COUNTERVALUE =
                    mem::MaybeUninit::uninit().assume_init();
                let counter: PDH_HCOUNTER = **counter;

                let ret = PdhGetFormattedCounterValue(
                    counter,
                    PDH_FMT_DOUBLE,
                    null_mut(),
                    &mut display_value,
                ) as u32;
                return if ret == ERROR_SUCCESS as _ {
                    let data = *display_value.u.doubleValue();
                    Some(data as f32)
                } else {
                    Some(0.)
                };
            }
        }
        None
    }

    pub fn add_counter(&mut self, name: &String, getter: Vec<u16>) -> bool {
        if self.internal.data.contains_key(name) {
            return false;
        }
        unsafe {
            let mut counter: PDH_HCOUNTER = ::std::mem::zeroed();
            let ret = PdhAddCounterW(self.internal.query, getter.as_ptr(), 0, &mut counter);
            if ret == ERROR_SUCCESS as _ {
                self.internal.data.insert(name.clone(), counter);
            } else {
                sysinfo_debug!("failed to add counter '{}': {:x}...", name, ret);
                return false;
            }
        }
        true
    }

    pub fn refresh(&self) {
        unsafe {
            if PdhCollectQueryData(self.internal.query) != ERROR_SUCCESS as _ {
                sysinfo_debug!("failed to refresh CPU data");
            }
        }
    }
}

/// Struct containing a processor information.
pub struct Processor {
    name: String,
    cpu_usage: f32,
    key_used: Option<KeyHandler>,
    vendor_id: String,
    brand: String,
    frequency: u64,
}

impl ProcessorExt for Processor {
    fn get_cpu_usage(&self) -> f32 {
        self.cpu_usage
    }

    fn get_name(&self) -> &str {
        &self.name
    }

    fn get_frequency(&self) -> u64 {
        self.frequency
    }

    fn get_vendor_id(&self) -> &str {
        &self.vendor_id
    }

    fn get_brand(&self) -> &str {
        &self.brand
    }
}

impl Processor {
    pub(crate) fn new_with_values(
        name: &str,
        vendor_id: String,
        brand: String,
        frequency: u64,
    ) -> Processor {
        Processor {
            name: name.to_owned(),
            cpu_usage: 0f32,
            key_used: None,
            vendor_id,
            brand,
            frequency,
        }
    }

    pub(crate) fn set_cpu_usage(&mut self, value: f32) {
        self.cpu_usage = value;
    }
}

fn get_vendor_id_not_great(info: &SYSTEM_INFO) -> String {
    use winapi::um::winnt;
    // https://docs.microsoft.com/fr-fr/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
    match unsafe { info.u.s() }.wProcessorArchitecture {
        winnt::PROCESSOR_ARCHITECTURE_INTEL => "Intel x86",
        winnt::PROCESSOR_ARCHITECTURE_MIPS => "MIPS",
        winnt::PROCESSOR_ARCHITECTURE_ALPHA => "RISC Alpha",
        winnt::PROCESSOR_ARCHITECTURE_PPC => "PPC",
        winnt::PROCESSOR_ARCHITECTURE_SHX => "SHX",
        winnt::PROCESSOR_ARCHITECTURE_ARM => "ARM",
        winnt::PROCESSOR_ARCHITECTURE_IA64 => "Intel Itanium-based x64",
        winnt::PROCESSOR_ARCHITECTURE_ALPHA64 => "RISC Alpha x64",
        winnt::PROCESSOR_ARCHITECTURE_MSIL => "MSIL",
        winnt::PROCESSOR_ARCHITECTURE_AMD64 => "(Intel or AMD) x64",
        winnt::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => "Intel Itanium-based x86",
        winnt::PROCESSOR_ARCHITECTURE_NEUTRAL => "unknown",
        winnt::PROCESSOR_ARCHITECTURE_ARM64 => "ARM x64",
        winnt::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => "ARM",
        winnt::PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 => "Intel Itanium-based x86",
        _ => "unknown",
    }
    .to_owned()
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) {
    #[cfg(target_arch = "x86")]
    use std::arch::x86::__cpuid;
    #[cfg(target_arch = "x86_64")]
    use std::arch::x86_64::__cpuid;

    fn add_u32(v: &mut Vec<u8>, i: u32) {
        let i = &i as *const u32 as *const u8;
        unsafe {
            v.push(*i);
            v.push(*i.offset(1));
            v.push(*i.offset(2));
            v.push(*i.offset(3));
        }
    }

    // First, we try to get the complete name.
    let res = unsafe { __cpuid(0x80000000) };
    let n_ex_ids = res.eax;
    let brand = if n_ex_ids >= 0x80000004 {
        let mut extdata = Vec::with_capacity(5);

        for i in 0x80000000..=n_ex_ids {
            extdata.push(unsafe { __cpuid(i) });
        }

        let mut out = Vec::with_capacity(4 * 4 * 3); // 4 * u32 * nb_entries
        for i in 2..5 {
            add_u32(&mut out, extdata[i].eax);
            add_u32(&mut out, extdata[i].ebx);
            add_u32(&mut out, extdata[i].ecx);
            add_u32(&mut out, extdata[i].edx);
        }
        let mut pos = 0;
        for e in out.iter() {
            if *e == 0 {
                break;
            }
            pos += 1;
        }
        match ::std::str::from_utf8(&out[..pos]) {
            Ok(s) => s.to_owned(),
            _ => String::new(),
        }
    } else {
        String::new()
    };

    // Failed to get full name, let's retry for the short version!
    let res = unsafe { __cpuid(0) };
    let mut x = Vec::with_capacity(16); // 3 * u32
    add_u32(&mut x, res.ebx);
    add_u32(&mut x, res.edx);
    add_u32(&mut x, res.ecx);
    let mut pos = 0;
    for e in x.iter() {
        if *e == 0 {
            break;
        }
        pos += 1;
    }
    let vendor_id = match ::std::str::from_utf8(&x[..pos]) {
        Ok(s) => s.to_owned(),
        Err(_) => get_vendor_id_not_great(info),
    };
    (vendor_id, brand)
}

#[cfg(all(not(target_arch = "x86_64"), not(target_arch = "x86")))]
pub fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) {
    (get_vendor_id_not_great(info), String::new())
}

pub fn get_key_used(p: &mut Processor) -> &mut Option<KeyHandler> {
    &mut p.key_used
}

// From https://stackoverflow.com/a/43813138:
//
// If your PC has 64 or fewer logical processors installed, the above code will work fine. However,
// if your PC has more than 64 logical processors installed, use GetActiveProcessorCount() or
// GetLogicalProcessorInformation() to determine the total number of logical processors installed.
pub fn get_frequencies(nb_processors: usize) -> Vec<u64> {
    let size = nb_processors * mem::size_of::<PROCESSOR_POWER_INFORMATION>();
    let mut infos: Vec<PROCESSOR_POWER_INFORMATION> = Vec::with_capacity(nb_processors);

    if unsafe {
        CallNtPowerInformation(
            ProcessorInformation,
            null_mut(),
            0,
            infos.as_mut_ptr() as _,
            size as _,
        )
    } == 0
    {
        unsafe {
            infos.set_len(nb_processors);
        }
        // infos.Number
        infos
            .into_iter()
            .map(|i| i.CurrentMhz as u64)
            .collect::<Vec<_>>()
    } else {
        vec![0; nb_processors]
    }
}
<|endoftext|>


<commit_before>use std::ffi::CStr;
use std::ptr;
use kernel32::{GlobalAlloc, GlobalLock, GlobalUnlock};
use user32::{CloseClipboard, EmptyClipboard, GetClipboardData, OpenClipboard, SetClipboardData};
use winapi::minwindef::{FALSE, HGLOBAL, UINT};
use {Clipboard, Item, Result};

bitflags! {
    pub flags GlobalAllocFlags: UINT {
        const GHND = GMEM_MOVEABLE | GMEM_ZEROINIT,
        const GMEM_FIXED = 0x0000,
        const GMEM_MOVEABLE = 0x0002,
        const GMEM_ZEROINIT = 0x0040,
        const GPTR = GMEM_FIXED | GMEM_ZEROINIT
    }
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ClipboardFormats {
    CF_BITMAP = 0x0002,
    CF_DIB = 0x0008,
    CF_DIBV5 = 0x0011,
    CF_DIF = 0x0005,
    CF_DSPBITMAP = 0x0082,
    CF_DSPENHMETAFILE = 0x008E,
    CF_DSPMETAFILEPICT = 0x0083,
    CF_DSPTEXT = 0x0081,
    CF_ENHMETAFILE = 0x0E,
    CF_GDIOBJFIRST = 0x0300,
    CF_GDIOBJLAST = 0x03FF,
    CF_HDROP = 0x000F,
    CF_LOCALE = 0x0010,
    CF_METAFILEPICT = 0x0003,
    CF_OEMTEXT = 0x0007,
    CF_OWNERDISPLAY = 0x0080,
    CF_PALETTE = 0x0009,
    CF_PENDATA = 0x000A,
    CF_PRIVATEFIRST = 0x0200,
    CF_PRIVATELAST = 0x02FF,
    CF_RIFF = 0x000B,
    CF_SYLK = 0x0004,
    CF_TEXT = 0x0001,
    CF_TIFF = 0x0006,
    CF_UNICODETEXT = 0x000C,
    CF_WAVE = 0x000D
}

pub trait ClipboardExt: Clipboard { }

#[derive(Debug)]
pub struct WindowsClipboard {
    _priv: ()
}

struct ClipboardGuard;
impl Default for ClipboardGuard {
    fn default() -> Self {
        let result = OpenClipboard(ptr::null_mut());
        assert_ne!(result, FALSE);
        ClipboardGuard
    }
}

impl Drop for ClipboardGuard {
    fn drop(&mut self) {
        CloseClipboard();
    }
}

struct GlobalLockGuard {
    data: HANDLE,
    ptr: *mut u8
}

impl GlobalLockGuard {
    fn new(data: HANDLE) -> Self {
        assert_ne!(data, ptr::null_mut());
        let ptr = GlobalLock(data) as *mut u8;
        assert_ne!(ptr, ptr::null_mut());
        GlobalLockGuard {
            data: data,
            ptr: ptr
        }
    }

    fn get(&self) -> *mut u8 {
        self.ptr
    }
}

impl Drop for GlobalLockGuard {
    fn drop(&mut self) {
        GlobalUnlock(self.data);
        self.ptr = ptr::null_mut();
    }
}

impl Clipboard for WindowsClipboard {
    fn get() -> Result<Self, Box<Error + Send + Sync>> {
        Ok(WindowsClipboard {
            _priv: ()
        })
    }

    fn copy(&mut self, item: &Item) -> Result<(), Box<Error + Send + Sync>> {
        unsafe {
            let text = match *item {
                Item::Text(ref t) => t,
                _ => return Ok(())
            };
            let _guard = ClipboardGuard::default();

            let clip_buf = GlobalLockHandle::new(GlobalAlloc(GMEM_MOVEABLE, text.len()));
            ptr::copy_nonoverlapping(text.as_ptr(), clip_buf.get(), text.len());

            let empty_result = EmptyClipboard();
            assert_ne!(empty_result, FALSE);
            SetClipboardData(ClipboardFormats::CF_UNICODETEXT as UINT, clip_buf);
            Ok(())
        }
    }

    fn copy_items(&mut self, items: &[Item]) -> Result<()> {
        unimplemented!();
    }

    fn get_paste_text(&self) -> Result<&str, Box<Error + Send + Sync>> {
        use std::slice;
        unsafe {
            let _guard = ClipboardGuard::default();
            let clip_buf = GlobalLockGuard::new(GetClipboardData(CF_UNICODETEXT));

            Ok(CStr::from_ptr(clip_buf.get()).to_str().unwrap_or(""))
        }
    }

    fn get_items(&self) -> &[Item] {
        unimplemented!();
    }
}

impl ClipboardExt for WindowsClipboard { }
<commit_msg>Don't export GlobalAllocFlags and ClipboardFormats<commit_after>use std::ffi::CStr;
use std::ptr;
use kernel32::{GlobalAlloc, GlobalLock, GlobalUnlock};
use user32::{CloseClipboard, EmptyClipboard, GetClipboardData, OpenClipboard, SetClipboardData};
use winapi::minwindef::{FALSE, HGLOBAL, UINT};
use {Clipboard, Item, Result};

bitflags! {
    flags GlobalAllocFlags: UINT {
        const GHND = GMEM_MOVEABLE | GMEM_ZEROINIT,
        const GMEM_FIXED = 0x0000,
        const GMEM_MOVEABLE = 0x0002,
        const GMEM_ZEROINIT = 0x0040,
        const GPTR = GMEM_FIXED | GMEM_ZEROINIT
    }
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum ClipboardFormats {
    CF_BITMAP = 0x0002,
    CF_DIB = 0x0008,
    CF_DIBV5 = 0x0011,
    CF_DIF = 0x0005,
    CF_DSPBITMAP = 0x0082,
    CF_DSPENHMETAFILE = 0x008E,
    CF_DSPMETAFILEPICT = 0x0083,
    CF_DSPTEXT = 0x0081,
    CF_ENHMETAFILE = 0x0E,
    CF_GDIOBJFIRST = 0x0300,
    CF_GDIOBJLAST = 0x03FF,
    CF_HDROP = 0x000F,
    CF_LOCALE = 0x0010,
    CF_METAFILEPICT = 0x0003,
    CF_OEMTEXT = 0x0007,
    CF_OWNERDISPLAY = 0x0080,
    CF_PALETTE = 0x0009,
    CF_PENDATA = 0x000A,
    CF_PRIVATEFIRST = 0x0200,
    CF_PRIVATELAST = 0x02FF,
    CF_RIFF = 0x000B,
    CF_SYLK = 0x0004,
    CF_TEXT = 0x0001,
    CF_TIFF = 0x0006,
    CF_UNICODETEXT = 0x000C,
    CF_WAVE = 0x000D
}

pub trait ClipboardExt: Clipboard { }

#[derive(Debug)]
pub struct WindowsClipboard {
    _priv: ()
}

struct ClipboardGuard;
impl Default for ClipboardGuard {
    fn default() -> Self {
        let result = OpenClipboard(ptr::null_mut());
        assert_ne!(result, FALSE);
        ClipboardGuard
    }
}

impl Drop for ClipboardGuard {
    fn drop(&mut self) {
        CloseClipboard();
    }
}

struct GlobalLockGuard {
    data: HANDLE,
    ptr: *mut u8
}

impl GlobalLockGuard {
    fn new(data: HANDLE) -> Self {
        assert_ne!(data, ptr::null_mut());
        let ptr = GlobalLock(data) as *mut u8;
        assert_ne!(ptr, ptr::null_mut());
        GlobalLockGuard {
            data: data,
            ptr: ptr
        }
    }

    fn get(&self) -> *mut u8 {
        self.ptr
    }
}

impl Drop for GlobalLockGuard {
    fn drop(&mut self) {
        GlobalUnlock(self.data);
        self.ptr = ptr::null_mut();
    }
}

impl Clipboard for WindowsClipboard {
    fn get() -> Result<Self, Box<Error + Send + Sync>> {
        Ok(WindowsClipboard {
            _priv: ()
        })
    }

    fn copy(&mut self, item: &Item) -> Result<(), Box<Error + Send + Sync>> {
        unsafe {
            let text = match *item {
                Item::Text(ref t) => t,
                _ => return Ok(())
            };
            let _guard = ClipboardGuard::default();

            let clip_buf = GlobalLockHandle::new(GlobalAlloc(GMEM_MOVEABLE, text.len()));
            ptr::copy_nonoverlapping(text.as_ptr(), clip_buf.get(), text.len());

            let empty_result = EmptyClipboard();
            assert_ne!(empty_result, FALSE);
            SetClipboardData(ClipboardFormats::CF_UNICODETEXT as UINT, clip_buf);
            Ok(())
        }
    }

    fn copy_items(&mut self, items: &[Item]) -> Result<()> {
        unimplemented!();
    }

    fn get_paste_text(&self) -> Result<&str, Box<Error + Send + Sync>> {
        use std::slice;
        unsafe {
            let _guard = ClipboardGuard::default();
            let clip_buf = GlobalLockGuard::new(GetClipboardData(CF_UNICODETEXT));

            Ok(CStr::from_ptr(clip_buf.get()).to_str().unwrap_or(""))
        }
    }

    fn get_items(&self) -> &[Item] {
        unimplemented!();
    }
}

impl ClipboardExt for WindowsClipboard { }
<|endoftext|>


<commit_before>use super::*;

struct session;

//TODO: implement
struct audio_buffer_stats;
struct audioformat;

pub trait Callbacks<T> {
    fn logged_in(s: &session, error: ::error::Error);
    fn logged_out(s: &session);
    fn metadata_updated(s: &session);
    fn connection_error(s: &session, error: ::error::Error);
    fn message_to_user(s: &session, message: String);
    fn notify_main_threaD(s: &session);
    fn music_delivery(s: &session, format: &audioformat, frames: &T, num_frames: isize) -> isize;
    fn play_token_lost(s: &session);
    fn log_message(s: &session, data: &str);
    fn end_of_track(s: &session);
    fn streaming_error(s: &session, error: ::error::Error);
    fn userinfo_updated(s: &session);
    fn start_playback(s: &session);
    fn stop_playback(s: &session);
    fn get_audio_buffer_stats(s: &session, stats: &audio_buffer_stats);
    fn offline_status_updated(s: &session);
    fn offline_error(s: &session, error: ::error::Error);
    fn credentials_blob_updated(s: &session, blob: &str);
    fn connectionstate_updated(s: &session);
    fn scrobble_error(s: &session, error: ::error::Error);
    fn private_session_mode_changed(session: &session, is_private: bool);
}
<commit_msg>Added documentation for session::callbacks<commit_after>use super::*;

struct session;

//TODO: implement
struct audio_buffer_stats;
struct audioformat;

pub trait Callbacks<T> {
    /// Called when login has been processed and was successful
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    error       One of the following errors, from error ERROR_OK
    ///                         ERROR_CLIENT_TOO_OLD ERROR_UNABLE_TO_CONTACT_SERVER
    ///                         ERROR_BAD_USERNAME_OR_PASSWORD ERROR_USER_BANNED ERROR_USER_NEEDS_PREMIUM
    ///                         ERROR_OTHER_TRANSIENT ERROR_OTHER_PERMANENT
    fn logged_in(s: &session, error: ::error::Error);

    /// Called when logout has been processed. Either called explicitly if you initialize a logout
    /// operation, or implicitly if there is a permanent connection error
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn logged_out(s: &session);

    /// Called whenever metadata has been updated
    ///
    /// If you have metadata cached outside of libspotify, you should purge your caches and fetch
    /// new versions.
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn metadata_updated(s: &session);

    /// Called when there is a connection error, and the library has problems reconnecting to the
    /// Spotify service. Could be called multiple times (as long as the problem is present)
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    error       One of the following errors, from error ERROR_OK
    ///                         ERROR_CLIENT_TOO_OLD ERROR_UNABLE_TO_CONTACT_SERVER
    ///                         ERROR_BAD_USERNAME_OR_PASSWORD ERROR_USER_BANNED ERROR_USER_NEEDS_PREMIUM
    ///                         ERROR_OTHER_TRANSIENT ERROR_OTHER_PERMANENT
    ///
    fn connection_error(s: &session, error: ::error::Error);

    /// Called when the access point wants to display a message to the user
    ///
    /// In the desktop client, these are shown in a blueish toolbar just below the search box.
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    message     String in UTF-8 format.
    fn message_to_user(s: &session, message: String);

    ///
    ///
    /// Called when processing needs to take place on the main thread.
    ///
    /// You need to call session_process_events() in the main thread to get libspotify to do
    /// more work. Failure to do so may cause request timeouts, or a lost connection.
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///
    /// Note:
    ///     This function is called from an internal session thread - you need to have proper
    ///     synchronization!
    ///
    fn notify_main_threaD(s: &session);

    /// Called when there is decompressed audio data available.
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    format  Audio format descriptor audioformat
    ///     [in]    frames  Points to raw PCM data as described by format
    ///     [in]    num_frames  Number of available samples in frames. If this is 0, a discontinuity has occurred (such as after a seek). The application should flush its audio fifos, etc.
    ///
    /// Returns:
    ///     Number of frames consumed. This value can be used to rate limit the output from the library if your output buffers are saturated. The library will retry delivery in about 100ms.
    ///
    /// Note:
    ///     This function is called from an internal session thread - you need to have proper synchronization!
    ///     This function must never block. If your output buffers are full you must return 0 to signal that the library should retry delivery in a short while.

    fn music_delivery(s: &session, format: &audioformat, frames: &T, num_frames: isize) -> isize;

    /// Music has been paused because an account only allows music to be played from one location
    /// simultaneously.
    ///
    /// Note:
    ///     When this callback is invoked the application should behave just as if the user pressed
    ///     the pause button. The application should also display a message to the user indicating
    ///     the playback has been paused because another application is playing using the same
    ///     account.
    ///     IT MUST NOT automatically resume playback but must instead wait for the user to
    ///     press play.
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn play_token_lost(s: &session);

    /// Logging callback.
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    data        Log data
    fn log_message(s: &session, data: &str);

    /// End of track. Called when the currently played track has reached its end.
    ///
    /// Note:
    ///     This function is invoked from the main thread
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn end_of_track(s: &session);

    /// Streaming error. Called when streaming cannot start or continue.
    ///
    /// Note:
    ///     This function is invoked from the main thread
    ///
    /// Parameters:
    ///    [in]    session      Session
    ///    [in]    error        One of the following errors, from error
    ///                         ERROR_NO_STREAM_AVAILABLE ERROR_OTHER_TRANSIENT ERROR_OTHER_PERMANENT
    fn streaming_error(s: &session, error: ::error::Error);

    /// Called after user info (anything related to user objects) have been updated.
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn userinfo_updated(s: &session);

    /// Called when audio playback should start
    ///
    /// Note:
    ///     For this to work correctly the application must also implement get_audio_buffer_stats()
    ///     This function is called from an internal session thread - you need to have proper
    ///     synchronization!
    ///     This function must never block.
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn start_playback(s: &session);

    /// Called when audio playback should stop
    ///
    /// Note:
    ///     For this to work correctly the application must also implement get_audio_buffer_stats()
    ///     This function is called from an internal session thread - you need to have proper
    ///     synchronization!
    ///     This function must never block.
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn stop_playback(s: &session);

    /// Called to query application about its audio buffer
    ///
    /// Note:
    ///     This function is called from an internal session thread - you need to have proper
    ///     synchronization!
    ///         This function must never block.
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [out]   stats       Stats struct to be filled by application
    fn get_audio_buffer_stats(s: &session, stats: &audio_buffer_stats);

    /// Called when offline synchronization status is updated
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn offline_status_updated(s: &session);

    /// Called when offline synchronization status is updated
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    error       Offline error. Will be ERROR_OK if the offline synchronization
    ///                         error state has cleared
    fn offline_error(s: &session, error: ::error::Error);

    /// Called when storable credentials have been updated, usually called when we have connected
    /// to the AP.
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    blob        Blob is a null-terminated string which contains an encrypted token
    ///                         that can be stored safely on disk instead of storing plaintext passwords.
    fn credentials_blob_updated(s: &session, blob: &str);

    /// Called when the connection state has updated - such as when logging in, going offline, etc.
    ///
    /// Parameters:
    ///     [in]    session     Session
    fn connectionstate_updated(s: &session);

    /// Called when there is a scrobble error event
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    error       Scrobble error. Currently ERROR_LASTFM_AUTH_ERROR.
    fn scrobble_error(s: &session, error: ::error::Error);

    /// Called when there is a change in the private session mode
    ///
    /// Parameters:
    ///     [in]    session     Session
    ///     [in]    isPrivate   True if in private session, false otherwhise
    fn private_session_mode_changed(session: &session, is_private: bool);
}
<|endoftext|>


<commit_before>#[crate_id = "snowmew-cube"];

#[feature(macro_rules)];
#[feature(globs)];

extern mod glfw;
extern mod gl;
extern mod snowmew;
extern mod render = "snowmew-render";
extern mod cgmath;
extern mod native;
extern mod extra;

use snowmew::core::Database;
use snowmew::shader::Shader;

use render::RenderManager;

use cgmath::quaternion::*;
use cgmath::transform::*;
use cgmath::vector::*;
use cgmath::matrix::*;
use cgmath::angle::{ToRad, deg};

use extra::time::precise_time_s;

static VS_SRC: &'static str =
"#version 400
uniform mat4 position;
uniform mat4 projection;
in vec3 pos;
out vec3 UV;
void main() {
    gl_Position = projection * position * vec4(pos, 1.);
    UV = vec3(pos.x, pos.y, pos.z);
}";

static FS_SRC: &'static str =
"#version 400
out vec4 colour;
in vec3 UV;\n \
void main() {
    colour = vec4(UV.x, UV.y, UV.z, 1);
}";

static VERTEX_DATA: [f32, ..24] = [
    -1., -1.,  1.,
    -1.,  1.,  1.,
     1., -1.,  1.,
     1.,  1.,  1.,
    -1., -1., -1.,
    -1.,  1., -1.,
     1., -1., -1.,
     1.,  1., -1.,
];

static INDEX_DATA: [u32, ..36] = [
    // top
    0, 2, 1,
    2, 3, 1,

    // bottom
    5, 7, 4,
    7, 6, 4,

    // right
    1, 3, 5,
    3, 7, 5,

    // left
    4, 6, 0,
    6, 2, 0,

    // front
    4, 0, 5,
    0, 1, 5,

    // back
    2, 6, 3,
    6, 7, 3,
];

#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, main)
}

/*
struct Cube
{
    shader: Shader,
    geometry: Geometry,
    lines_shader: Shader,
    mat: i32
}

impl Cube
{
    fn new() -> Cube
    {
        let shader = snowmew::shader::Shader::new(VS_SRC, FS_SRC, (gl::ONE, gl::ZERO));
        let geometry = snowmew::geometry::Geometry::triangles_adjacency(VERTEX_DATA,
            snowmew::geometry::to_triangles_adjacency(INDEX_DATA));
        let lines_shader = snowmew::shader::Shader::new_geo(LINE_VS_SRC, LINE_FS_SRC, LINE_GS_SRC, (gl::ONE, gl::ZERO));

        Cube {
            shader: shader,
            geometry: geometry,
            lines_shader: lines_shader,
            mat: shader.uniform(&"mat")
        }
    }
}

impl Object for Cube
{
    fn draw(&self, ren: &Database, ctx: &mut Context, frame: &FrameInfo, target: &mut DrawTarget)
    {
        let projection = cgmath::projection::perspective(
            cgmath::angle::deg(60f32), 16f32/9f32, 0.01f32, 25f32
        );

        let time = frame.time as f32;
        let transform = cgmath::matrix::Mat4::scale(1f32, 1f32, 1f32);
        let transform = cgmath::matrix::Mat3::from_angle_x(cgmath::angle::rad(time/2f32)).to_mat4().mul_m(&transform);
        let transform = cgmath::matrix::Mat3::from_angle_y(cgmath::angle::rad(time/8f32)).to_mat4().mul_m(&transform);
        let transform = cgmath::matrix::Mat3::from_angle_z(cgmath::angle::rad(time)).to_mat4().mul_m(&transform);
        let transform = cgmath::matrix::Mat4::translate(0f32, 0f32, -5f32).mul_m(&transform);
        let transform = projection.mul_m(&transform);

        target.draw(ctx,
                    &self.lines_shader,
                    &self.geometry,
                    &[(self.mat, &transform as &snowmew::coregl::Uniforms)],
                    &[]
        );

        target.draw(ctx,
                    &self.shader,
                    &self.geometry,
                    &[(self.mat, &transform as &snowmew::coregl::Uniforms)],
                    &[]
        );
    }
}

fn main() {
    do glfw::start {
        let width = 2560 as uint;
        let height = 1440 as uint;
        println(format!("{} {}", width, height));
        glfw::window_hint::context_version(4, 0);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(width as u32, height as u32, "OpenGL", glfw::Windowed).unwrap();
        window.make_context_current();
        glfw::set_swap_interval(0);

        gl::load_with(glfw::get_proc_address);

        let mut cube = Cube::new();

        let mut fb = snowmew::coregl::FrameBuffer {
            id: 0,
            width: width,
            height: height
        };

        gl::Enable(gl::SCISSOR_TEST);
        gl::Enable(gl::DEPTH_TEST);
        gl::Enable(gl::CULL_FACE);
        gl::Enable(gl::LINE_SMOOTH);
        gl::Enable(gl::BLEND);
        gl::CullFace(gl::BACK);

        gl::ClearColor(0.05, 0.05, 0.05, 1.);

        let mut count: uint = 0;
        let mut time = glfw::get_time();
        let mut time_last = time;

        let mut ctx = snowmew::render::Context::new();

        let mut db = snowmew::core::Database::new();

        while !window.should_close() {
            glfw::poll_events();
            time = glfw::get_time();p

            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

            let fi = FrameInfo {
                count: count,
                time: time,
                delta: time - time_last
            };

            //render.add
            fb.viewport(&mut ctx, (0, 0), (width, height), |dt, ctx| {
                cube.draw(&mut db, ctx, &fi, dt);
            });

            gl::Finish();
            let end_time = glfw::get_time();

            print(format!("Frame Budget %{:f}          \r", 100. * (end_time - time) / (1./120.)));
            window.swap_buffers();
            time_last = time;
            count += 1;
        }
    }
}
*/

fn main() {
    do glfw::start {
        let width = 1920 as uint;
        let height = 1080 as uint;
        println!("{} {}", width, height);
        glfw::window_hint::context_version(4, 0);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(width as u32, height as u32, "OpenGL", glfw::Windowed).unwrap();
        window.make_context_current();
        glfw::set_swap_interval(1);

        gl::load_with(glfw::get_proc_address);

        let mut db = Database::new();

        let shader = Shader::new(VS_SRC.into_owned(), FS_SRC.into_owned());
        let indexs = snowmew::geometry::to_triangles_adjacency(INDEX_DATA);
        let len = indexs.len();
        let vbo = snowmew::geometry::VertexBuffer::new(VERTEX_DATA.into_owned(), indexs);

        let assets = db.new_object(None, ~"asserts");
        let shader = db.add_shader(assets, ~"shader", shader);
        let vbo = db.add_vertex_buffer(assets, ~"vbo", vbo);
        let geometry = snowmew::geometry::Geometry::triangles_adjacency(vbo, 0, len);

        let geometry = db.add_geometry(assets, ~"geo", geometry);

        let camera = db.new_object(None, ~"camera");
        let scene = db.new_object(None, ~"scene");

        let size = 25;

        for y in range(-size, size) { for x in range(-size, size) {for z in range(-size, size) {
            let cube_id = db.new_object(Some(scene), format!("cube_{}_{}_{}", x, y, z));
            let x = (x*5) as f32;
            let y = (y*5) as f32;
            let z = (z*5) as f32;
            db.update_location(cube_id,
                Transform3D::new(0.5f32, Quat::from_euler(deg(15f32).to_rad(), deg(0f32).to_rad(), deg(0f32).to_rad()), Vec3::new(y, x, z)));
            db.set_draw(cube_id, geometry, shader);
        }}}

        db.update_location(camera,
            Transform3D::new(1f32,
                             Quat::from_euler(deg(0f32).to_rad(), deg(0f32).to_rad(), deg(0f32).to_rad()),
                             Vec3::new(0f32, 0f32, 0f32)));

        let mut ren = RenderManager::new(db.clone());
        ren.load();

        window.set_cursor_mode(glfw::CursorDisabled);

        glfw::poll_events();
        let (wx, wy) = window.get_size();
        let (wx, wy) = (wx as f64, wy as f64);
        window.set_cursor_pos(wx/2., wy/2.);

        let (mut rot_x, mut rot_y) = (0_f64, 0_f64);

        let mut pos = Vec3::new(0f32, 0f32, 0f32);

        while !window.should_close() {
            glfw::poll_events();
            let start = precise_time_s();

            let (x, y) = window.get_cursor_pos();
            let (wx, wy) = window.get_size();
            let (wx, wy) = (wx as f64, wy as f64);
            window.set_cursor_pos(wx/2., wy/2.);

            rot_x += (x - wx/2.) / 3.;
            rot_y += (y - wy/2.) / 3.;

            rot_y = rot_y.max(&-90.).min(&90.);

            let input_vec = Vec4::new(
                if window.get_key(glfw::KeyA) == glfw::Press {0.5f32} else {0f32} +
                if window.get_key(glfw::KeyD) == glfw::Press {-0.5f32} else {0f32},
                0f32,
                if window.get_key(glfw::KeyW) == glfw::Press {0.5f32} else {0f32} +
                if window.get_key(glfw::KeyS) == glfw::Press {-0.5f32} else {0f32},
                1f32
            );

            let rot =  Quat::from_axis_angle(&Vec3::new(0f32, 1f32, 0f32), deg(-rot_x as f32).to_rad()).mul_q(
                      &Quat::from_axis_angle(&Vec3::new(1f32, 0f32, 0f32), deg(-rot_y as f32).to_rad()));

            let trans = Transform3D::new(0f32,
                                 rot.normalize(),
                                 Vec3::new(0f32, 0f32, 0f32));
            let pos_v = trans.rotate().to_mat4().mul_v(&input_vec);
            let new_pos = Vec3::new(pos_v.x, pos_v.y, pos_v.z);
            pos = pos.add_v(&new_pos);

            let rot =  Quat::from_axis_angle(&Vec3::new(1f32, 0f32, 0f32), deg(rot_y as f32).to_rad()).mul_q(
                      &Quat::from_axis_angle(&Vec3::new(0f32, 1f32, 0f32), deg(rot_x as f32).to_rad())).normalize();

            let trans = Transform3D::new(0f32,
                                 rot.normalize(),
                                 pos);

            db.update_location(camera, trans);

            ren.update(db.clone());

            gl::ClearColor(0.05, 0.05, 0.05, 1.);
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
            ren.render(scene, camera);
            window.swap_buffers();
            let end = precise_time_s();

            let time = (end-start);

            print!("\rfps: {:0.2f} time: {:0.3f}ms, budget: {:0.2f}                 ",
                1./time, time*1000., time/(1./60.));
        }
    }
}<commit_msg>removed commented out code<commit_after>#[crate_id = "snowmew-cube"];

#[feature(macro_rules)];
#[feature(globs)];

extern mod glfw;
extern mod gl;
extern mod snowmew;
extern mod render = "snowmew-render";
extern mod cgmath;
extern mod native;
extern mod extra;

use snowmew::core::Database;
use snowmew::shader::Shader;

use render::RenderManager;

use cgmath::quaternion::*;
use cgmath::transform::*;
use cgmath::vector::*;
use cgmath::matrix::*;
use cgmath::angle::{ToRad, deg};

use extra::time::precise_time_s;

static VS_SRC: &'static str =
"#version 400
uniform mat4 position;
uniform mat4 projection;
in vec3 pos;
out vec3 UV;
void main() {
    gl_Position = projection * position * vec4(pos, 1.);
    UV = vec3(pos.x, pos.y, pos.z);
}";

static FS_SRC: &'static str =
"#version 400
out vec4 colour;
in vec3 UV;\n \
void main() {
    colour = vec4(UV.x, UV.y, UV.z, 1);
}";

static VERTEX_DATA: [f32, ..24] = [
    -1., -1.,  1.,
    -1.,  1.,  1.,
     1., -1.,  1.,
     1.,  1.,  1.,
    -1., -1., -1.,
    -1.,  1., -1.,
     1., -1., -1.,
     1.,  1., -1.,
];

static INDEX_DATA: [u32, ..36] = [
    // top
    0, 2, 1,
    2, 3, 1,

    // bottom
    5, 7, 4,
    7, 6, 4,

    // right
    1, 3, 5,
    3, 7, 5,

    // left
    4, 6, 0,
    6, 2, 0,

    // front
    4, 0, 5,
    0, 1, 5,

    // back
    2, 6, 3,
    6, 7, 3,
];

#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, main)
}

fn main() {
    do glfw::start {
        let width = 1920 as uint;
        let height = 1080 as uint;
        println!("{} {}", width, height);
        glfw::window_hint::context_version(4, 0);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(width as u32, height as u32, "OpenGL", glfw::Windowed).unwrap();
        window.make_context_current();
        glfw::set_swap_interval(1);

        gl::load_with(glfw::get_proc_address);

        let mut db = Database::new();

        let shader = Shader::new(VS_SRC.into_owned(), FS_SRC.into_owned());
        let indexs = snowmew::geometry::to_triangles_adjacency(INDEX_DATA);
        let len = indexs.len();
        let vbo = snowmew::geometry::VertexBuffer::new(VERTEX_DATA.into_owned(), indexs);

        let assets = db.new_object(None, ~"asserts");
        let shader = db.add_shader(assets, ~"shader", shader);
        let vbo = db.add_vertex_buffer(assets, ~"vbo", vbo);
        let geometry = snowmew::geometry::Geometry::triangles_adjacency(vbo, 0, len);

        let geometry = db.add_geometry(assets, ~"geo", geometry);

        let camera = db.new_object(None, ~"camera");
        let scene = db.new_object(None, ~"scene");

        let size = 25;

        for y in range(-size, size) { for x in range(-size, size) {for z in range(-size, size) {
            let cube_id = db.new_object(Some(scene), format!("cube_{}_{}_{}", x, y, z));
            let x = (x*5) as f32;
            let y = (y*5) as f32;
            let z = (z*5) as f32;
            db.update_location(cube_id,
                Transform3D::new(0.5f32, Quat::from_euler(deg(15f32).to_rad(), deg(0f32).to_rad(), deg(0f32).to_rad()), Vec3::new(y, x, z)));
            db.set_draw(cube_id, geometry, shader);
        }}}

        db.update_location(camera,
            Transform3D::new(1f32,
                             Quat::from_euler(deg(0f32).to_rad(), deg(0f32).to_rad(), deg(0f32).to_rad()),
                             Vec3::new(0f32, 0f32, 0f32)));

        let mut ren = RenderManager::new(db.clone());
        ren.load();

        window.set_cursor_mode(glfw::CursorDisabled);

        glfw::poll_events();
        let (wx, wy) = window.get_size();
        let (wx, wy) = (wx as f64, wy as f64);
        window.set_cursor_pos(wx/2., wy/2.);

        let (mut rot_x, mut rot_y) = (0_f64, 0_f64);

        let mut pos = Vec3::new(0f32, 0f32, 0f32);

        while !window.should_close() {
            glfw::poll_events();
            let start = precise_time_s();

            let (x, y) = window.get_cursor_pos();
            let (wx, wy) = window.get_size();
            let (wx, wy) = (wx as f64, wy as f64);
            window.set_cursor_pos(wx/2., wy/2.);

            rot_x += (x - wx/2.) / 3.;
            rot_y += (y - wy/2.) / 3.;

            rot_y = rot_y.max(&-90.).min(&90.);

            let input_vec = Vec4::new(
                if window.get_key(glfw::KeyA) == glfw::Press {0.5f32} else {0f32} +
                if window.get_key(glfw::KeyD) == glfw::Press {-0.5f32} else {0f32},
                0f32,
                if window.get_key(glfw::KeyW) == glfw::Press {0.5f32} else {0f32} +
                if window.get_key(glfw::KeyS) == glfw::Press {-0.5f32} else {0f32},
                1f32
            );

            let rot =  Quat::from_axis_angle(&Vec3::new(0f32, 1f32, 0f32), deg(-rot_x as f32).to_rad()).mul_q(
                      &Quat::from_axis_angle(&Vec3::new(1f32, 0f32, 0f32), deg(-rot_y as f32).to_rad()));

            let trans = Transform3D::new(0f32,
                                 rot.normalize(),
                                 Vec3::new(0f32, 0f32, 0f32));
            let pos_v = trans.rotate().to_mat4().mul_v(&input_vec);
            let new_pos = Vec3::new(pos_v.x, pos_v.y, pos_v.z);
            pos = pos.add_v(&new_pos);

            let rot =  Quat::from_axis_angle(&Vec3::new(1f32, 0f32, 0f32), deg(rot_y as f32).to_rad()).mul_q(
                      &Quat::from_axis_angle(&Vec3::new(0f32, 1f32, 0f32), deg(rot_x as f32).to_rad())).normalize();

            let trans = Transform3D::new(0f32,
                                 rot.normalize(),
                                 pos);

            db.update_location(camera, trans);

            ren.update(db.clone());

            gl::ClearColor(0.05, 0.05, 0.05, 1.);
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
            ren.render(scene, camera);
            window.swap_buffers();
            let end = precise_time_s();

            let time = (end-start);

            print!("\rfps: {:0.2f} time: {:0.3f}ms, budget: {:0.2f}                 ",
                1./time, time*1000., time/(1./60.));
        }
    }
}<|endoftext|>