<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![unstable(issue = "0", feature = "windows_stdio")]

use prelude::v1::*;
use io::prelude::*;

use io::{self, Cursor};
use ptr;
use str;
use sync::Mutex;
use sys::c;
use sys::cvt;
use sys::handle::Handle;
use sys_common::io::read_to_end_uninitialized;

pub struct NoClose(Option<Handle>);

pub enum Output {
    Console(NoClose),
    Pipe(NoClose),
}

pub struct Stdin {
    handle: Output,
    utf8: Mutex<io::Cursor<Vec<u8>>>,
}
pub struct Stdout(Output);
pub struct Stderr(Output);

pub fn get(handle: c::DWORD) -> io::Result<Output> {
    let handle = unsafe { c::GetStdHandle(handle) };
    if handle == c::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else if handle.is_null() {
        Err(io::Error::new(io::ErrorKind::Other,
                           "no stdio handle available for this process"))
    } else {
        let ret = NoClose::new(handle);
        let mut out = 0;
        match unsafe { c::GetConsoleMode(handle, &mut out) } {
            0 => Ok(Output::Pipe(ret)),
            _ => Ok(Output::Console(ret)),
        }
    }
}

fn write(out: &Output, data: &[u8]) -> io::Result<usize> {
    let handle = match *out {
        Output::Console(ref c) => c.get().raw(),
        Output::Pipe(ref p) => return p.get().write(data),
    };
    // As with stdin on windows, stdout often can't handle writes of large
    // sizes. For an example, see #14940. For this reason, don't try to
    // write the entire output buffer on windows.
    //
    // For some other references, it appears that this problem has been
    // encountered by others [1] [2]. We choose the number 8K just because
    // libuv does the same.
    //
    // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
    // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
    const OUT_MAX: usize = 8192;
    let data_len;
    let utf16 = match str::from_utf8(data).ok() {
        Some(mut utf8) => {
            if utf8.len() > OUT_MAX {
                let mut new_len = OUT_MAX;
                while !utf8.is_char_boundary(new_len) {
                    new_len -= 1;
                }
                utf8 = &utf8[..new_len];
            }
            data_len = utf8.len();
            utf8.encode_utf16().collect::<Vec<u16>>()
        }
        None => return Err(invalid_encoding()),
    };
    let mut written = 0;
    try!(cvt(unsafe {
        c::WriteConsoleW(handle,
                         utf16.as_ptr() as c::LPCVOID,
                         utf16.len() as u32,
                         &mut written,
                         ptr::null_mut())
    }));

    // FIXME if this only partially writes the utf16 buffer then we need to
    //       figure out how many bytes of `data` were actually written
    assert_eq!(written as usize, utf16.len());
    Ok(data_len)
}

impl Stdin {
    pub fn new() -> io::Result<Stdin> {
        get(c::STD_INPUT_HANDLE).map(|handle| {
            Stdin {
                handle: handle,
                utf8: Mutex::new(Cursor::new(Vec::new())),
            }
        })
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let handle = match self.handle {
            Output::Console(ref c) => c.get().raw(),
            Output::Pipe(ref p) => return p.get().read(buf),
        };
        let mut utf8 = self.utf8.lock().unwrap();
        // Read more if the buffer is empty
        if utf8.position() as usize == utf8.get_ref().len() {
            let mut utf16 = vec![0u16; 0x1000];
            let mut num = 0;
            try!(cvt(unsafe {
                c::ReadConsoleW(handle,
                                utf16.as_mut_ptr() as c::LPVOID,
                                utf16.len() as u32,
                                &mut num,
                                ptr::null_mut())
            }));
            utf16.truncate(num as usize);
            // FIXME: what to do about this data that has already been read?
            let data = match String::from_utf16(&utf16) {
                Ok(utf8) => utf8.into_bytes(),
                Err(..) => return Err(invalid_encoding()),
            };
            *utf8 = Cursor::new(data);
        }

        // MemReader shouldn't error here since we just filled it
        utf8.read(buf)
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }
}

#[unstable(reason = "not public", issue = "0", feature = "fd_read")]
impl<'a> Read for &'a Stdin {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        unsafe { read_to_end_uninitialized(self, buf) }
    }
}

impl Stdout {
    pub fn new() -> io::Result<Stdout> {
        get(c::STD_OUTPUT_HANDLE).map(Stdout)
    }

    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
        write(&self.0, data)
    }
}

impl Stderr {
    pub fn new() -> io::Result<Stderr> {
        get(c::STD_ERROR_HANDLE).map(Stderr)
    }

    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
        write(&self.0, data)
    }
}

// FIXME: right now this raw stderr handle is used in a few places because
//        std::io::stderr_raw isn't exposed, but once that's exposed this impl
//        should go away
impl io::Write for Stderr {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        Stderr::write(self, data)
    }
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

impl NoClose {
    fn new(handle: c::HANDLE) -> NoClose {
        NoClose(Some(Handle::new(handle)))
    }

    fn get(&self) -> &Handle { self.0.as_ref().unwrap() }
}

impl Drop for NoClose {
    fn drop(&mut self) {
        self.0.take().unwrap().into_raw();
    }
}

impl Output {
    pub fn handle(&self) -> &Handle {
        let nc = match *self {
            Output::Console(ref c) => c,
            Output::Pipe(ref c) => c,
        };
        nc.0.as_ref().unwrap()
    }
}

fn invalid_encoding() -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode")
}
<commit_msg>Simplify Windows stdout/stderr<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![unstable(issue = "0", feature = "windows_stdio")]

use prelude::v1::*;
use io::prelude::*;

use io::{self, Cursor};
use ptr;
use str;
use sync::Mutex;
use sys::c;
use sys::cvt;
use sys::handle::Handle;
use sys_common::io::read_to_end_uninitialized;

pub struct NoClose(Option<Handle>);

pub enum Output {
    Console(NoClose),
    Pipe(NoClose),
}

pub struct Stdin {
    handle: Output,
    utf8: Mutex<io::Cursor<Vec<u8>>>,
}
pub struct Stdout(Output);
pub struct Stderr(Output);

pub fn get(handle: c::DWORD) -> io::Result<Output> {
    let handle = unsafe { c::GetStdHandle(handle) };
    if handle == c::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else if handle.is_null() {
        Err(io::Error::new(io::ErrorKind::Other,
                           "no stdio handle available for this process"))
    } else {
        let ret = NoClose::new(handle);
        let mut out = 0;
        match unsafe { c::GetConsoleMode(handle, &mut out) } {
            0 => Ok(Output::Pipe(ret)),
            _ => Ok(Output::Console(ret)),
        }
    }
}

fn write(out: &Output, data: &[u8]) -> io::Result<usize> {
    let handle = match *out {
        Output::Console(ref c) => c.get().raw(),
        Output::Pipe(ref p) => return p.get().write(data),
    };
    // As with stdin on windows, stdout often can't handle writes of large
    // sizes. For an example, see #14940. For this reason, don't try to
    // write the entire output buffer on windows.
    //
    // For some other references, it appears that this problem has been
    // encountered by others [1] [2]. We choose the number 8K just because
    // libuv does the same.
    //
    // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
    // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
    const OUT_MAX: usize = 8192;
    let (utf16, data_len) = match str::from_utf8(data).ok() {
        Some(mut utf8) => {
            if utf8.len() > OUT_MAX {
                let mut new_len = OUT_MAX;
                while !utf8.is_char_boundary(new_len) {
                    new_len -= 1;
                }
                utf8 = &utf8[..new_len];
            }
            (utf8.encode_utf16().collect::<Vec<u16>>(), utf8.len())
        }
        None => return Err(invalid_encoding()),
    };
    let mut written = 0;
    try!(cvt(unsafe {
        c::WriteConsoleW(handle,
                         utf16.as_ptr() as c::LPCVOID,
                         utf16.len() as u32,
                         &mut written,
                         ptr::null_mut())
    }));

    // FIXME if this only partially writes the utf16 buffer then we need to
    //       figure out how many bytes of `data` were actually written
    assert_eq!(written as usize, utf16.len());
    Ok(data_len)
}

impl Stdin {
    pub fn new() -> io::Result<Stdin> {
        get(c::STD_INPUT_HANDLE).map(|handle| {
            Stdin {
                handle: handle,
                utf8: Mutex::new(Cursor::new(Vec::new())),
            }
        })
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let handle = match self.handle {
            Output::Console(ref c) => c.get().raw(),
            Output::Pipe(ref p) => return p.get().read(buf),
        };
        let mut utf8 = self.utf8.lock().unwrap();
        // Read more if the buffer is empty
        if utf8.position() as usize == utf8.get_ref().len() {
            let mut utf16 = vec![0u16; 0x1000];
            let mut num = 0;
            try!(cvt(unsafe {
                c::ReadConsoleW(handle,
                                utf16.as_mut_ptr() as c::LPVOID,
                                utf16.len() as u32,
                                &mut num,
                                ptr::null_mut())
            }));
            utf16.truncate(num as usize);
            // FIXME: what to do about this data that has already been read?
            let data = match String::from_utf16(&utf16) {
                Ok(utf8) => utf8.into_bytes(),
                Err(..) => return Err(invalid_encoding()),
            };
            *utf8 = Cursor::new(data);
        }

        // MemReader shouldn't error here since we just filled it
        utf8.read(buf)
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }
}

#[unstable(reason = "not public", issue = "0", feature = "fd_read")]
impl<'a> Read for &'a Stdin {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        unsafe { read_to_end_uninitialized(self, buf) }
    }
}

impl Stdout {
    pub fn new() -> io::Result<Stdout> {
        get(c::STD_OUTPUT_HANDLE).map(Stdout)
    }

    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
        write(&self.0, data)
    }
}

impl Stderr {
    pub fn new() -> io::Result<Stderr> {
        get(c::STD_ERROR_HANDLE).map(Stderr)
    }

    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
        write(&self.0, data)
    }
}

// FIXME: right now this raw stderr handle is used in a few places because
//        std::io::stderr_raw isn't exposed, but once that's exposed this impl
//        should go away
impl io::Write for Stderr {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        Stderr::write(self, data)
    }
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

impl NoClose {
    fn new(handle: c::HANDLE) -> NoClose {
        NoClose(Some(Handle::new(handle)))
    }

    fn get(&self) -> &Handle { self.0.as_ref().unwrap() }
}

impl Drop for NoClose {
    fn drop(&mut self) {
        self.0.take().unwrap().into_raw();
    }
}

impl Output {
    pub fn handle(&self) -> &Handle {
        let nc = match *self {
            Output::Console(ref c) => c,
            Output::Pipe(ref c) => c,
        };
        nc.0.as_ref().unwrap()
    }
}

fn invalid_encoding() -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode")
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, BTreeSet};

use crate::grammar::{Element, ElementTypes, Grammar, Production};
use crate::utils::{Name, TreeNode, TreeValue, Void};

#[derive(Clone)]
pub struct NullableInfo<A> {
  nullable_actions: BTreeSet<TreeNode<A, Void>>,
}

impl<A: Ord> NullableInfo<A> {
  pub fn new() -> Self {
    NullableInfo {
      nullable_actions: BTreeSet::new(),
    }
  }
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
struct ProdWithHead<'a, E: ElementTypes> {
  head: &'a E::NonTerm,
  prod: &'a Production<E>,
}

fn grammar_to_prods_with_heads<E: ElementTypes>(
  g: &Grammar<E>,
) -> Vec<ProdWithHead<E>> {
  let mut result = Vec::new();
  for (head, rule) in g.rule_set() {
    for prod in rule.prods() {
      result.push(ProdWithHead { head, prod });
    }
  }
  result
}

/// Calculate the nullable set of a grammar
///
/// The nullable set of a grammar is the set of non-terminals in that grammar
/// that can parse the empty terminal sequence.
pub fn calculate_nullables<E: ElementTypes>(
  g: &Grammar<E>,
) -> BTreeMap<E::NonTerm, NullableInfo<E::Action>> {
  let prods_with_heads = grammar_to_prods_with_heads(g);

  let mut nullable_nts: BTreeMap<E::NonTerm, NullableInfo<E::Action>> =
    BTreeMap::new();

  loop {
    let mut changed = false;

    for prod_with_head in &prods_with_heads {
      if is_prod_nullable(&nullable_nts, prod_with_head.prod) {
        let mut node_args_set: BTreeSet<
          BTreeMap<Name, TreeValue<E::Action, Void>>,
        > = BTreeSet::new();
        node_args_set.insert(BTreeMap::new());
        for prod_elem in prod_with_head.prod.prod_elements() {
          let nt = prod_elem.elem().as_nonterm();
          let nt_nullable_info = nullable_nts.get(nt).unwrap();
          if let Some(id) = prod_elem.id() {
            let mut new_node_args_set = BTreeSet::new();
            for old_node_arg in &node_args_set {
              for nullable_action in &nt_nullable_info.nullable_actions {
                let mut new_node_arg = old_node_arg.clone();
                new_node_arg.insert(
                  id.clone(),
                  TreeValue::Node(Box::new(nullable_action.clone())),
                );
                new_node_args_set.insert(new_node_arg);
              }
            }

            node_args_set = new_node_args_set;
          }
        }

        let new_nodes = node_args_set.into_iter().map(|node_args| {
          TreeNode::new(prod_with_head.prod.action().clone(), node_args)
        });

        let nullable_info = nullable_nts
          .entry(prod_with_head.head.clone())
          .or_insert_with(NullableInfo::new);
        for new_node in new_nodes {
          if nullable_info.nullable_actions.insert(new_node) {
            changed = true;
          }
        }
      }
    }

    if !changed {
      return nullable_nts;
    }
  }
}

pub fn is_prod_nullable<E: ElementTypes, V>(
  nullables: &BTreeMap<E::NonTerm, V>,
  prod: &Production<E>,
) -> bool {
  for elem in prod.elements_iter() {
    match elem {
      Element::Term(_) => return false,
      Element::NonTerm(nt) => {
        if !nullables.contains_key(nt) {
          return false;
        }
      }
    }
  }
  true
}
<commit_msg>Extract function to simplify calculate_nullables<commit_after>use std::collections::{BTreeMap, BTreeSet};

use crate::grammar::{Element, ElementTypes, Grammar, Production};
use crate::utils::{Name, TreeNode, TreeValue, Void};

#[derive(Clone)]
pub struct NullableInfo<A> {
  nullable_actions: BTreeSet<TreeNode<A, Void>>,
}

impl<A: Ord> NullableInfo<A> {
  pub fn new() -> Self {
    NullableInfo {
      nullable_actions: BTreeSet::new(),
    }
  }
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
struct ProdWithHead<'a, E: ElementTypes> {
  head: &'a E::NonTerm,
  prod: &'a Production<E>,
}

fn grammar_to_prods_with_heads<E: ElementTypes>(
  g: &Grammar<E>,
) -> Vec<ProdWithHead<E>> {
  let mut result = Vec::new();
  for (head, rule) in g.rule_set() {
    for prod in rule.prods() {
      result.push(ProdWithHead { head, prod });
    }
  }
  result
}

fn calculate_nullable_tree_nodes<E: ElementTypes>(
  nullables: &BTreeMap<E::NonTerm, NullableInfo<E::Action>>,
  prod: &Production<E>,
) -> BTreeSet<TreeNode<E::Action, Void>> {
  let mut node_args_set = BTreeSet::new();
  node_args_set.insert(BTreeMap::new());
  for prod_elem in prod.prod_elements() {
    let nt = prod_elem.elem().as_nonterm();
    let nt_nullable_info = nullables.get(nt).unwrap();
    if let Some(id) = prod_elem.id() {
      let mut new_node_args_set = BTreeSet::new();
      for old_node_arg in &node_args_set {
        for nullable_action in &nt_nullable_info.nullable_actions {
          let mut new_node_arg = old_node_arg.clone();
          new_node_arg.insert(
            id.clone(),
            TreeValue::Node(Box::new(nullable_action.clone())),
          );
          new_node_args_set.insert(new_node_arg);
        }
      }

      node_args_set = new_node_args_set;
    }
  }

  node_args_set
    .into_iter()
    .map(|node_args| TreeNode::new(prod.action().clone(), node_args))
    .collect()
}

/// Calculate the nullable set of a grammar
///
/// The nullable set of a grammar is the set of non-terminals in that grammar
/// that can parse the empty terminal sequence.
pub fn calculate_nullables<E: ElementTypes>(
  g: &Grammar<E>,
) -> BTreeMap<E::NonTerm, NullableInfo<E::Action>> {
  let prods_with_heads = grammar_to_prods_with_heads(g);

  let mut nullable_nts = BTreeMap::new();

  loop {
    let mut changed = false;

    for prod_with_head in &prods_with_heads {
      if is_prod_nullable(&nullable_nts, prod_with_head.prod) {
        let new_nodes =
          calculate_nullable_tree_nodes(&nullable_nts, prod_with_head.prod);

        let nullable_info = nullable_nts
          .entry(prod_with_head.head.clone())
          .or_insert_with(NullableInfo::new);
        for new_node in new_nodes {
          if nullable_info.nullable_actions.insert(new_node) {
            changed = true;
          }
        }
      }
    }

    if !changed {
      return nullable_nts;
    }
  }
}

pub fn is_prod_nullable<E: ElementTypes, V>(
  nullables: &BTreeMap<E::NonTerm, V>,
  prod: &Production<E>,
) -> bool {
  for elem in prod.elements_iter() {
    match elem {
      Element::Term(_) => return false,
      Element::NonTerm(nt) => {
        if !nullables.contains_key(nt) {
          return false;
        }
      }
    }
  }
  true
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;

mod http;
mod nupnp;
mod api;

use adapters::philips_hue::api::structs::*;
use adapters::philips_hue::api::HueLight;
use controller::Controller;
use events::*;
use iron::{ Request, Response, IronResult };
use iron::headers::{ ContentType, AccessControlAllowOrigin };
use iron::status::Status;
use iron::method::Method;
use router::Router;
use service::{ Service, ServiceAdapter, ServiceProperties };
use std::time::Duration;
use std::thread;
use uuid::Uuid;
use std::io::Read;

pub struct PhilipsHueAdapter<T> {
    name: String,
    controller: T,
}

impl<T: Controller> PhilipsHueAdapter<T> {
    pub fn new(controller: T) -> Self {
        debug!("Creating Philips Hue adapter");
        PhilipsHueAdapter { name: "PhilipsHueAdapter".to_owned(),
                       controller: controller,
                     }
    }
}

impl<T: Controller> ServiceAdapter for PhilipsHueAdapter<T> {
    fn get_name(&self) -> String {
        self.name.clone()
    }

    fn start(&self) {
        let mut id = 0;
        let controller = self.controller.clone();

        thread::spawn(move || {
            controller.send_event(
                EventData::AdapterStart {
                    name: "Philips Hue Service Adapter".to_owned()
                }).unwrap();

            let nupnp_hubs = nupnp::query();
            debug!("nUPnP reported Philips Hue bridges: {:?}", nupnp_hubs);

            let available_hubs = nupnp_hubs
                .iter()
                .filter(|hub| hub.is_available())
                .collect::<Vec<_>>();

            let paired_hubs = available_hubs
                .iter()
                .filter(|hub| {
                    info!("Philips Hue bridge {} at {}", hub.id, hub.ip);

                    // Querying the settings fails when not paired with the bridge
                    if hub.is_paired() {
                        true
                    } else {
                        info!("Push pairing button on Philips Hue Bridge ID {}", hub.id);

                        // Try pairing for 120 seconds.
                        for _ in 0..120 {
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                        "{{\"adapter\": \"philips_hue\", \"message\": \"NeedsPairing\", \"hub\": \"{}\"}}",
                                        hub.id)
                                }).unwrap();
                            if hub.try_pairing() {
                                break;
                            }
                            thread::sleep(Duration::from_millis(1000));
                        }

                        if hub.is_paired() {
                            info!("Paired with Philips Hue Bridge ID {}", hub.id);
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                         "{{\"adapter\": \"philips_hue\", \"message\": \"PairingSuccess\", \"hub\": \"{}\"}}",
                                         hub.id)
                                 }).unwrap();
                            true
                        } else {
                            warn!("Pairing timeout with Philips Hue Bridge ID {}", hub.id);
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                         "{{\"adapter\": \"philips_hue\", \"message\": \"PairingTimeout\", \"hub\": \"{}\"}}",
                                         hub.id)
                                 }).unwrap();
                            false
                        }
                    }
                })
                .collect::<Vec<_>>();

            for hub in &paired_hubs {
                hub.is_available();
                // Extract and log some info
                let setting = hub.get_settings();
                let hs = HueHubSettings::new(&setting).unwrap(); // TODO: no unwrap
                info!(
                    "Connected to Philips Hue bridge model {}, ID {}, software version {}, IP address {}",
                    hs.config.modelid, hs.config.bridgeid, hs.config.swversion,
                    hs.config.ipaddress);

                let lights = hub.get_lights();
                for light in lights {
                    debug!("Spawning service for {:?}", light);
                    id += 1;
                    let service = HueLightService::new(controller.clone(), id, light);
                    let service_id = service.get_properties().id;
                    service.start();
                    controller.add_service(Box::new(service));
                    controller.send_event(EventData::ServiceStart { id: service_id }).unwrap();
                }
            }
        });
    }

    fn stop(&self) {
        info!("Stopping Philips Hue adapter");
    }

}

struct HueLightService<T> {
    controller: T,
    properties: ServiceProperties,
    light: HueLight,
}

impl<T: Controller> HueLightService<T> {
    fn new(controller: T, id: u32, light: HueLight) -> Self {
        debug!("Creating HueLightService {} for HueLight {:?}", id, light);
        let service_id = Uuid::new_v4().to_simple_string();
        HueLightService {
            controller: controller.clone(),
            properties: ServiceProperties {
                id: service_id.clone(),
                name: "philips hue service".to_owned(),
                description: "Service for Philips Hue Light".to_owned(),
                http_url: controller.get_http_root_for_service(service_id.clone()),
                ws_url: controller.get_ws_root_for_service(service_id)
            },
            light: light
        }
    }

    fn handle_get_request(&self, cmd: &str) -> IronResult<Response> {
        match cmd {
            "state" => {
                // TODO: Every light.get_*() call produces a
                // get request to the API. Fix requires major
                // internal design change.
                let status = self.light.get_settings();
                let light_state = self.light.get_state();
                let json = format!(
                    "{{\"type\": \"{}\", \"available\": {}, \"on\": {}, \"hue\": {}, \"sat\": {}, \"val\": {}}}",
                    "device/light/colorlight",
                    status.state.reachable, status.state.on,
                    light_state.hue, light_state.sat, light_state.val);
                let mut response = Response::with(json);
                response.status = Some(Status::Ok);
                response.headers.set(ContentType::json());
                response.headers.set(AccessControlAllowOrigin::Any);
                Ok(response)
            },
            _ => {
                error_response()
            }
        }
    }

    fn handle_put_request(&self, cmd: &str, body: String) -> IronResult<Response> {
        match cmd {
            "state" => {
                debug!("Request body for state command: {}", body);
                let state_cmd: Option<StateCmd> = parse_json(&body);
                debug!("Parsed state command: {:?}", state_cmd);
                match state_cmd {
                    Some(value) => {
                        let mut light_state = self.light.get_state();
                        if let Some(on) = value.on { light_state.on = on; }
                        if let Some(hue) = value.hue { light_state.hue = hue; }
                        if let Some(sat) = value.sat { light_state.sat = sat; }
                        if let Some(val) = value.val { light_state.val = val; }
                        self.light.set_state(light_state);
                        success_response()
                    },
                    None => {
                        warn!("Invalid parameters in state command: {}", cmd);
                        error_response()
                    }
                }
            },
            _ => {
                warn!("Invalid command to Hue Light service: {}", cmd);
                error_response()
            }
        }
    }
}

impl<T: Controller> Service for HueLightService<T> {
    fn get_properties(&self) -> ServiceProperties {
        self.properties.clone()
    }

    // Starts the service, it will just spawn a thread and send messages once
    // in a while.
    fn start(&self) {
        let props = self.properties.clone();
        let controller = self.controller.clone();
        let light = self.light.clone();
        thread::spawn(move || {
            controller.send_event(
                EventData::ServiceStart { id: props.id.to_string() }).unwrap();
            info!("Service {} started for Philips Hue light \"{}\" on bridge {}",
                props.id, light.hue_id, light.hub_id);
            loop {
                // TODO: Monitor and manage state changes.
                thread::sleep(Duration::from_millis(60000));
            }
        });
    }

    fn stop(&self) {
        debug!("Service {} stopped for Philips Hue light \"{}\" on bridge {}",
            self.properties.id, self.light.hue_id, self.light.hub_id);
    }

    // Processes a http request.
    fn process_request(&self, req: &mut Request) -> IronResult<Response> {
        let cmd = req.extensions.get::<Router>().unwrap().find("command").unwrap_or("");
        debug!("Got command {} via {:?}", cmd, req);
        match req.method {
            Method::Get => {
                self.handle_get_request(cmd)
            },
            Method::Put => {
                let mut body = String::new();
                req.body.read_to_string(&mut body).unwrap();
                self.handle_put_request(cmd, body)
            },
            _ => {
                error_response()
            }
        }
    }
}

fn success_response() -> IronResult<Response> {
    let mut response = Response::with("{\"result\": \"success\"}".to_owned());
    response.status = Some(Status::Ok);
    response.headers.set(ContentType::json());
    response.headers.set(AccessControlAllowOrigin::Any);
    Ok(response)
}

fn error_response() ->  IronResult<Response> {
    let mut response = Response::with("{\"result\": \"error\"}".to_owned());
    response.status = Some(Status::MethodNotAllowed);
    response.headers.set(ContentType::json());
    response.headers.set(AccessControlAllowOrigin::Any);
    Ok(response)
}

#[derive(Serialize, Deserialize, Debug)]
struct StateCmd {
    on: Option<bool>,
    hue: Option<f32>,
    sat: Option<f32>,
    val: Option<f32>,
}
<commit_msg>Remove useless thread spawning<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;

mod http;
mod nupnp;
mod api;

use adapters::philips_hue::api::structs::*;
use adapters::philips_hue::api::HueLight;
use controller::Controller;
use events::*;
use iron::{ Request, Response, IronResult };
use iron::headers::{ ContentType, AccessControlAllowOrigin };
use iron::status::Status;
use iron::method::Method;
use router::Router;
use service::{ Service, ServiceAdapter, ServiceProperties };
use std::time::Duration;
use std::thread;
use uuid::Uuid;
use std::io::Read;

pub struct PhilipsHueAdapter<T> {
    name: String,
    controller: T,
}

impl<T: Controller> PhilipsHueAdapter<T> {
    pub fn new(controller: T) -> Self {
        debug!("Creating Philips Hue adapter");
        PhilipsHueAdapter { name: "PhilipsHueAdapter".to_owned(),
                       controller: controller,
                     }
    }
}

impl<T: Controller> ServiceAdapter for PhilipsHueAdapter<T> {
    fn get_name(&self) -> String {
        self.name.clone()
    }

    fn start(&self) {
        let mut id = 0;
        let controller = self.controller.clone();

        thread::spawn(move || {
            controller.send_event(
                EventData::AdapterStart {
                    name: "Philips Hue Service Adapter".to_owned()
                }).unwrap();

            let nupnp_hubs = nupnp::query();
            debug!("nUPnP reported Philips Hue bridges: {:?}", nupnp_hubs);

            let available_hubs = nupnp_hubs
                .iter()
                .filter(|hub| hub.is_available())
                .collect::<Vec<_>>();

            let paired_hubs = available_hubs
                .iter()
                .filter(|hub| {
                    info!("Philips Hue bridge {} at {}", hub.id, hub.ip);

                    // Querying the settings fails when not paired with the bridge
                    if hub.is_paired() {
                        true
                    } else {
                        info!("Push pairing button on Philips Hue Bridge ID {}", hub.id);

                        // Try pairing for 120 seconds.
                        for _ in 0..120 {
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                        "{{\"adapter\": \"philips_hue\", \"message\": \"NeedsPairing\", \"hub\": \"{}\"}}",
                                        hub.id)
                                }).unwrap();
                            if hub.try_pairing() {
                                break;
                            }
                            thread::sleep(Duration::from_millis(1000));
                        }

                        if hub.is_paired() {
                            info!("Paired with Philips Hue Bridge ID {}", hub.id);
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                         "{{\"adapter\": \"philips_hue\", \"message\": \"PairingSuccess\", \"hub\": \"{}\"}}",
                                         hub.id)
                                 }).unwrap();
                            true
                        } else {
                            warn!("Pairing timeout with Philips Hue Bridge ID {}", hub.id);
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                         "{{\"adapter\": \"philips_hue\", \"message\": \"PairingTimeout\", \"hub\": \"{}\"}}",
                                         hub.id)
                                 }).unwrap();
                            false
                        }
                    }
                })
                .collect::<Vec<_>>();

            for hub in &paired_hubs {
                hub.is_available();
                // Extract and log some info
                let setting = hub.get_settings();
                let hs = HueHubSettings::new(&setting).unwrap(); // TODO: no unwrap
                info!(
                    "Connected to Philips Hue bridge model {}, ID {}, software version {}, IP address {}",
                    hs.config.modelid, hs.config.bridgeid, hs.config.swversion,
                    hs.config.ipaddress);

                let lights = hub.get_lights();
                for light in lights {
                    debug!("Spawning service for {:?}", light);
                    id += 1;
                    let service = HueLightService::new(controller.clone(), id, light);
                    let service_id = service.get_properties().id;
                    service.start();
                    controller.add_service(Box::new(service));
                    controller.send_event(EventData::ServiceStart { id: service_id }).unwrap();
                }
            }
        });
    }

    fn stop(&self) {
        info!("Stopping Philips Hue adapter");
    }

}

struct HueLightService<T> {
    controller: T,
    properties: ServiceProperties,
    light: HueLight,
}

impl<T: Controller> HueLightService<T> {
    fn new(controller: T, id: u32, light: HueLight) -> Self {
        debug!("Creating HueLightService {} for HueLight {:?}", id, light);
        let service_id = Uuid::new_v4().to_simple_string();
        HueLightService {
            controller: controller.clone(),
            properties: ServiceProperties {
                id: service_id.clone(),
                name: "philips hue service".to_owned(),
                description: "Service for Philips Hue Light".to_owned(),
                http_url: controller.get_http_root_for_service(service_id.clone()),
                ws_url: controller.get_ws_root_for_service(service_id)
            },
            light: light
        }
    }

    fn handle_get_request(&self, cmd: &str) -> IronResult<Response> {
        match cmd {
            "state" => {
                // TODO: Every light.get_*() call produces a
                // get request to the API. Fix requires major
                // internal design change.
                let status = self.light.get_settings();
                let light_state = self.light.get_state();
                let json = format!(
                    "{{\"type\": \"{}\", \"available\": {}, \"on\": {}, \"hue\": {}, \"sat\": {}, \"val\": {}}}",
                    "device/light/colorlight",
                    status.state.reachable, status.state.on,
                    light_state.hue, light_state.sat, light_state.val);
                let mut response = Response::with(json);
                response.status = Some(Status::Ok);
                response.headers.set(ContentType::json());
                response.headers.set(AccessControlAllowOrigin::Any);
                Ok(response)
            },
            _ => {
                error_response()
            }
        }
    }

    fn handle_put_request(&self, cmd: &str, body: String) -> IronResult<Response> {
        match cmd {
            "state" => {
                debug!("Request body for state command: {}", body);
                let state_cmd: Option<StateCmd> = parse_json(&body);
                debug!("Parsed state command: {:?}", state_cmd);
                match state_cmd {
                    Some(value) => {
                        let mut light_state = self.light.get_state();
                        if let Some(on) = value.on { light_state.on = on; }
                        if let Some(hue) = value.hue { light_state.hue = hue; }
                        if let Some(sat) = value.sat { light_state.sat = sat; }
                        if let Some(val) = value.val { light_state.val = val; }
                        self.light.set_state(light_state);
                        success_response()
                    },
                    None => {
                        warn!("Invalid parameters in state command: {}", cmd);
                        error_response()
                    }
                }
            },
            _ => {
                warn!("Invalid command to Hue Light service: {}", cmd);
                error_response()
            }
        }
    }
}

impl<T: Controller> Service for HueLightService<T> {
    fn get_properties(&self) -> ServiceProperties {
        self.properties.clone()
    }

    // Starts the service, it will just spawn a thread and send messages once
    // in a while.
    fn start(&self) {
        self.controller.send_event(
                EventData::ServiceStart { id: self.properties.id.to_string() }).unwrap();
        info!("Service {} started for Philips Hue light \"{}\" on bridge {}",
            self.properties.id, self.light.hue_id, self.light.hub_id);
    }

    fn stop(&self) {
        debug!("Service {} stopped for Philips Hue light \"{}\" on bridge {}",
            self.properties.id, self.light.hue_id, self.light.hub_id);
    }

    // Processes a http request.
    fn process_request(&self, req: &mut Request) -> IronResult<Response> {
        let cmd = req.extensions.get::<Router>().unwrap().find("command").unwrap_or("");
        debug!("Got command {} via {:?}", cmd, req);
        match req.method {
            Method::Get => {
                self.handle_get_request(cmd)
            },
            Method::Put => {
                let mut body = String::new();
                req.body.read_to_string(&mut body).unwrap();
                self.handle_put_request(cmd, body)
            },
            _ => {
                error_response()
            }
        }
    }
}

fn success_response() -> IronResult<Response> {
    let mut response = Response::with("{\"result\": \"success\"}".to_owned());
    response.status = Some(Status::Ok);
    response.headers.set(ContentType::json());
    response.headers.set(AccessControlAllowOrigin::Any);
    Ok(response)
}

fn error_response() ->  IronResult<Response> {
    let mut response = Response::with("{\"result\": \"error\"}".to_owned());
    response.status = Some(Status::MethodNotAllowed);
    response.headers.set(ContentType::json());
    response.headers.set(AccessControlAllowOrigin::Any);
    Ok(response)
}

#[derive(Serialize, Deserialize, Debug)]
struct StateCmd {
    on: Option<bool>,
    hue: Option<f32>,
    sat: Option<f32>,
    val: Option<f32>,
}
<|endoftext|>


<commit_before>// Copyright (c) 2020-2022, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use num_derive::*;

use crate::partition::BlockSize;
use crate::serialize::{Deserialize, Serialize};

use std::fmt;

// NOTE: Add Structures at the end.
/// Contains the speed settings.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SpeedSettings {
  /// Enables inter-frames to have multiple reference frames.
  ///
  /// Enabled is slower.
  pub multiref: bool,

  /// Enables fast deblocking filter.
  pub fast_deblock: bool,

  /// The number of lookahead frames to be used for temporal RDO.
  ///
  /// Higher is slower.
  pub rdo_lookahead_frames: usize,

  /// Which scene detection mode to use. Standard is slower, but best.
  pub scene_detection_mode: SceneDetectionSpeed,

  /// Enables CDEF.
  pub cdef: bool,

  /// Enables LRF.
  pub lrf: bool,

  /// Enable searching loop restoration units when no transforms have been coded
  /// restoration unit.
  pub lru_on_skip: bool,

  /// The amount of search done for self guided restoration.
  pub sgr_complexity: SGRComplexityLevel,

  /// Search level for segmentation.
  ///
  /// Full search is at least twice as slow.
  pub segmentation: SegmentationLevel,

  // NOTE: put enums and basic type fields above
  /// Speed settings related to partition decision
  pub partition: PartitionSpeedSettings,

  /// Speed settings related to transform size and type decision
  pub transform: TransformSpeedSettings,

  /// Speed settings related to intra prediction mode selection
  pub prediction: PredictionSpeedSettings,

  /// Speed settings related to motion estimation and motion vector selection
  pub motion: MotionSpeedSettings,
}

impl Default for SpeedSettings {
  /// The default settings are equivalent to speed 0
  fn default() -> Self {
    SpeedSettings {
      multiref: true,
      fast_deblock: false,
      rdo_lookahead_frames: 40,
      scene_detection_mode: SceneDetectionSpeed::Standard,
      cdef: true,
      lrf: true,
      lru_on_skip: true,
      sgr_complexity: SGRComplexityLevel::Full,
      segmentation: SegmentationLevel::Full,
      partition: PartitionSpeedSettings {
        encode_bottomup: true,
        non_square_partition_threshold: BlockSize::BLOCK_4X4,
        partition_range: PartitionRange::new(
          BlockSize::BLOCK_4X4,
          BlockSize::BLOCK_64X64,
        ),
      },
      transform: TransformSpeedSettings {
        reduced_tx_set: false,
        // TX domain distortion is always faster, with no significant quality change,
        // although it will be ignored when Tune == Psychovisual.
        tx_domain_distortion: true,
        tx_domain_rate: false,
        rdo_tx_decision: true,
        enable_inter_tx_split: false,
      },
      prediction: PredictionSpeedSettings {
        prediction_modes: PredictionModesSetting::ComplexAll,
        fine_directional_intra: true,
      },
      motion: MotionSpeedSettings {
        include_near_mvs: true,
        use_satd_subpel: true,
        me_allow_full_search: true,
      },
    }
  }
}

impl SpeedSettings {
  /// Set the speed setting according to a numeric speed preset.
  pub fn from_preset(speed: usize) -> Self {
    // The default settings are equivalent to speed 0
    let mut settings = SpeedSettings::default();

    if speed >= 1 {
      settings.lru_on_skip = false;
      settings.segmentation = SegmentationLevel::Simple;
    }

    if speed >= 2 {
      settings.partition.non_square_partition_threshold =
        BlockSize::BLOCK_16X16;
    }

    if speed >= 3 {
      settings.rdo_lookahead_frames = 30;

      settings.partition.partition_range =
        PartitionRange::new(BlockSize::BLOCK_8X8, BlockSize::BLOCK_64X64);
      settings.partition.non_square_partition_threshold =
        BlockSize::BLOCK_64X64;

      settings.prediction.prediction_modes =
        PredictionModesSetting::ComplexKeyframes;
    }

    if speed >= 4 {
      settings.partition.encode_bottomup = false;
    }

    if speed >= 5 {
      settings.sgr_complexity = SGRComplexityLevel::Reduced;
      settings.motion.include_near_mvs = false;
    }

    if speed >= 6 {
      settings.rdo_lookahead_frames = 20;

      settings.transform.rdo_tx_decision = false;
      settings.transform.reduced_tx_set = true;

      settings.motion.me_allow_full_search = false;
    }

    if speed >= 7 {
      settings.prediction.prediction_modes = PredictionModesSetting::Simple;
    }

    if speed >= 8 {
      // Multiref is enabled automatically if low_latency is false.
      //
      // If low_latency is true, enabling multiref allows using multiple
      // backwards references. low_latency false enables both forward and
      // backwards references.
      settings.multiref = false;
      settings.fast_deblock = true;
    }

    if speed >= 9 {
      settings.rdo_lookahead_frames = 10;

      // 8x8 is fast enough to use until very high speed levels,
      // because 8x8 with reduced TX set is faster but with equivalent
      // or better quality compared to 16x16 (to which reduced TX set does not apply).
      settings.partition.partition_range =
        PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_64X64);

      // FIXME: With unknown reasons, inter_tx_split does not work if reduced_tx_set is false
      settings.transform.enable_inter_tx_split = true;
    }

    if speed >= 10 {
      settings.scene_detection_mode = SceneDetectionSpeed::Fast;
      settings.lrf = false;

      settings.partition.partition_range =
        PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_32X32);

      settings.motion.use_satd_subpel = false;
    }

    settings
  }
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
/// Speed settings related to transform size and type decision
pub struct TransformSpeedSettings {
  /// Enables reduced transform set.
  ///
  /// Enabled is faster.
  pub reduced_tx_set: bool,

  /// Enables using transform-domain distortion instead of pixel-domain.
  ///
  /// Enabled is faster.
  pub tx_domain_distortion: bool,

  /// Enables using transform-domain rate estimation.
  ///
  /// Enabled is faster.
  pub tx_domain_rate: bool,

  /// Enables searching transform size and type with RDO.
  ///
  /// Enabled is slower.
  pub rdo_tx_decision: bool,

  /// Enable tx split for inter mode block.
  pub enable_inter_tx_split: bool,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
/// Speed settings related to partition decision
pub struct PartitionSpeedSettings {
  /// Enables bottom-up encoding, rather than top-down.
  ///
  /// Enabled is slower.
  pub encode_bottomup: bool,

  /// Allow non-square partition type outside of frame borders
  /// on any blocks above this size.
  pub non_square_partition_threshold: BlockSize,

  /// Range of partition sizes that can be used. Larger ranges are slower.
  ///
  /// Must be based on square block sizes, so e.g. 8×4 isn't allowed here.
  pub partition_range: PartitionRange,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
/// Speed settings related to motion estimation and motion vector selection
pub struct MotionSpeedSettings {
  /// Use SATD instead of SAD for subpixel search.
  ///
  /// Enabled is slower.
  pub use_satd_subpel: bool,

  /// Enables searching near motion vectors during RDO.
  ///
  /// Enabled is slower.
  pub include_near_mvs: bool,

  /// Enable full search in some parts of motion estimation. Allowing full
  /// search is slower.
  pub me_allow_full_search: bool,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
/// Speed settings related to intra prediction mode selection
pub struct PredictionSpeedSettings {
  /// Prediction modes to search.
  ///
  /// Complex settings are slower.
  pub prediction_modes: PredictionModesSetting,

  /// Use fine directional intra prediction
  pub fine_directional_intra: bool,
}

/// Range of block sizes to use.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PartitionRange {
  pub(crate) min: BlockSize,
  pub(crate) max: BlockSize,
}

impl PartitionRange {
  /// Creates a new partition range with min and max partition sizes.
  ///
  /// # Panics
  ///
  /// - Panics if `max` is larger than `min`.
  /// - Panics if either `min` or `max` are not square.
  pub fn new(min: BlockSize, max: BlockSize) -> Self {
    assert!(max >= min);
    // Topdown search checks the min block size for PARTITION_SPLIT only, so
    // the min block size must be square.
    assert!(min.is_sqr());
    // Rectangular max partition sizes have not been tested.
    assert!(max.is_sqr());

    Self { min, max }
  }
}

#[cfg(test)]
impl Default for PartitionRange {
  fn default() -> Self {
    PartitionRange::new(BlockSize::BLOCK_4X4, BlockSize::BLOCK_64X64)
  }
}

/// Prediction modes to search.
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum SceneDetectionSpeed {
  /// Fastest scene detection using pixel-wise comparison
  Fast,
  /// Scene detection using motion vectors and cost estimates
  Standard,
  /// Completely disable scene detection and only place keyframes
  /// at fixed intervals.
  None,
}

impl fmt::Display for SceneDetectionSpeed {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        SceneDetectionSpeed::Fast => "Fast",
        SceneDetectionSpeed::Standard => "Standard",
        SceneDetectionSpeed::None => "None",
      }
    )
  }
}

/// Prediction modes to search.
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum PredictionModesSetting {
  /// Only simple prediction modes.
  Simple,
  /// Search all prediction modes on key frames and simple modes on other
  /// frames.
  ComplexKeyframes,
  /// Search all prediction modes on all frames.
  ComplexAll,
}

impl fmt::Display for PredictionModesSetting {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        PredictionModesSetting::Simple => "Simple",
        PredictionModesSetting::ComplexKeyframes => "Complex-KFs",
        PredictionModesSetting::ComplexAll => "Complex-All",
      }
    )
  }
}

#[cfg(test)]
impl Default for PredictionModesSetting {
  fn default() -> Self {
    PredictionModesSetting::Simple
  }
}

/// Search level for self guided restoration
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum SGRComplexityLevel {
  /// Search all sgr parameters
  Full,
  /// Search a reduced set of sgr parameters
  Reduced,
}

impl fmt::Display for SGRComplexityLevel {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        SGRComplexityLevel::Full => "Full",
        SGRComplexityLevel::Reduced => "Reduced",
      }
    )
  }
}

/// Search level for segmentation
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum SegmentationLevel {
  /// No segmentation is signalled.
  Disabled,
  /// Segmentation index is derived from source statistics.
  Simple,
  /// Search all segmentation indices.
  Full,
}

impl fmt::Display for SegmentationLevel {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        SegmentationLevel::Disabled => "Disabled",
        SegmentationLevel::Simple => "Simple",
        SegmentationLevel::Full => "Full",
      }
    )
  }
}
<commit_msg>Move complex pred modes for keyframes to s2<commit_after>// Copyright (c) 2020-2022, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use num_derive::*;

use crate::partition::BlockSize;
use crate::serialize::{Deserialize, Serialize};

use std::fmt;

// NOTE: Add Structures at the end.
/// Contains the speed settings.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SpeedSettings {
  /// Enables inter-frames to have multiple reference frames.
  ///
  /// Enabled is slower.
  pub multiref: bool,

  /// Enables fast deblocking filter.
  pub fast_deblock: bool,

  /// The number of lookahead frames to be used for temporal RDO.
  ///
  /// Higher is slower.
  pub rdo_lookahead_frames: usize,

  /// Which scene detection mode to use. Standard is slower, but best.
  pub scene_detection_mode: SceneDetectionSpeed,

  /// Enables CDEF.
  pub cdef: bool,

  /// Enables LRF.
  pub lrf: bool,

  /// Enable searching loop restoration units when no transforms have been coded
  /// restoration unit.
  pub lru_on_skip: bool,

  /// The amount of search done for self guided restoration.
  pub sgr_complexity: SGRComplexityLevel,

  /// Search level for segmentation.
  ///
  /// Full search is at least twice as slow.
  pub segmentation: SegmentationLevel,

  // NOTE: put enums and basic type fields above
  /// Speed settings related to partition decision
  pub partition: PartitionSpeedSettings,

  /// Speed settings related to transform size and type decision
  pub transform: TransformSpeedSettings,

  /// Speed settings related to intra prediction mode selection
  pub prediction: PredictionSpeedSettings,

  /// Speed settings related to motion estimation and motion vector selection
  pub motion: MotionSpeedSettings,
}

impl Default for SpeedSettings {
  /// The default settings are equivalent to speed 0
  fn default() -> Self {
    SpeedSettings {
      multiref: true,
      fast_deblock: false,
      rdo_lookahead_frames: 40,
      scene_detection_mode: SceneDetectionSpeed::Standard,
      cdef: true,
      lrf: true,
      lru_on_skip: true,
      sgr_complexity: SGRComplexityLevel::Full,
      segmentation: SegmentationLevel::Full,
      partition: PartitionSpeedSettings {
        encode_bottomup: true,
        non_square_partition_threshold: BlockSize::BLOCK_4X4,
        partition_range: PartitionRange::new(
          BlockSize::BLOCK_4X4,
          BlockSize::BLOCK_64X64,
        ),
      },
      transform: TransformSpeedSettings {
        reduced_tx_set: false,
        // TX domain distortion is always faster, with no significant quality change,
        // although it will be ignored when Tune == Psychovisual.
        tx_domain_distortion: true,
        tx_domain_rate: false,
        rdo_tx_decision: true,
        enable_inter_tx_split: false,
      },
      prediction: PredictionSpeedSettings {
        prediction_modes: PredictionModesSetting::ComplexAll,
        fine_directional_intra: true,
      },
      motion: MotionSpeedSettings {
        include_near_mvs: true,
        use_satd_subpel: true,
        me_allow_full_search: true,
      },
    }
  }
}

impl SpeedSettings {
  /// Set the speed setting according to a numeric speed preset.
  pub fn from_preset(speed: usize) -> Self {
    // The default settings are equivalent to speed 0
    let mut settings = SpeedSettings::default();

    if speed >= 1 {
      settings.lru_on_skip = false;
      settings.segmentation = SegmentationLevel::Simple;
    }

    if speed >= 2 {
      settings.partition.non_square_partition_threshold =
        BlockSize::BLOCK_16X16;

      settings.prediction.prediction_modes =
        PredictionModesSetting::ComplexKeyframes;
    }

    if speed >= 3 {
      settings.rdo_lookahead_frames = 30;

      settings.partition.partition_range =
        PartitionRange::new(BlockSize::BLOCK_8X8, BlockSize::BLOCK_64X64);
      settings.partition.non_square_partition_threshold =
        BlockSize::BLOCK_64X64;
    }

    if speed >= 4 {
      settings.partition.encode_bottomup = false;
    }

    if speed >= 5 {
      settings.sgr_complexity = SGRComplexityLevel::Reduced;
      settings.motion.include_near_mvs = false;
    }

    if speed >= 6 {
      settings.rdo_lookahead_frames = 20;

      settings.transform.rdo_tx_decision = false;
      settings.transform.reduced_tx_set = true;

      settings.motion.me_allow_full_search = false;
    }

    if speed >= 7 {
      settings.prediction.prediction_modes = PredictionModesSetting::Simple;
    }

    if speed >= 8 {
      // Multiref is enabled automatically if low_latency is false.
      //
      // If low_latency is true, enabling multiref allows using multiple
      // backwards references. low_latency false enables both forward and
      // backwards references.
      settings.multiref = false;
      settings.fast_deblock = true;
    }

    if speed >= 9 {
      settings.rdo_lookahead_frames = 10;

      // 8x8 is fast enough to use until very high speed levels,
      // because 8x8 with reduced TX set is faster but with equivalent
      // or better quality compared to 16x16 (to which reduced TX set does not apply).
      settings.partition.partition_range =
        PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_64X64);

      // FIXME: With unknown reasons, inter_tx_split does not work if reduced_tx_set is false
      settings.transform.enable_inter_tx_split = true;
    }

    if speed >= 10 {
      settings.scene_detection_mode = SceneDetectionSpeed::Fast;
      settings.lrf = false;

      settings.partition.partition_range =
        PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_32X32);

      settings.motion.use_satd_subpel = false;
    }

    settings
  }
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
/// Speed settings related to transform size and type decision
pub struct TransformSpeedSettings {
  /// Enables reduced transform set.
  ///
  /// Enabled is faster.
  pub reduced_tx_set: bool,

  /// Enables using transform-domain distortion instead of pixel-domain.
  ///
  /// Enabled is faster.
  pub tx_domain_distortion: bool,

  /// Enables using transform-domain rate estimation.
  ///
  /// Enabled is faster.
  pub tx_domain_rate: bool,

  /// Enables searching transform size and type with RDO.
  ///
  /// Enabled is slower.
  pub rdo_tx_decision: bool,

  /// Enable tx split for inter mode block.
  pub enable_inter_tx_split: bool,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
/// Speed settings related to partition decision
pub struct PartitionSpeedSettings {
  /// Enables bottom-up encoding, rather than top-down.
  ///
  /// Enabled is slower.
  pub encode_bottomup: bool,

  /// Allow non-square partition type outside of frame borders
  /// on any blocks above this size.
  pub non_square_partition_threshold: BlockSize,

  /// Range of partition sizes that can be used. Larger ranges are slower.
  ///
  /// Must be based on square block sizes, so e.g. 8×4 isn't allowed here.
  pub partition_range: PartitionRange,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
/// Speed settings related to motion estimation and motion vector selection
pub struct MotionSpeedSettings {
  /// Use SATD instead of SAD for subpixel search.
  ///
  /// Enabled is slower.
  pub use_satd_subpel: bool,

  /// Enables searching near motion vectors during RDO.
  ///
  /// Enabled is slower.
  pub include_near_mvs: bool,

  /// Enable full search in some parts of motion estimation. Allowing full
  /// search is slower.
  pub me_allow_full_search: bool,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
/// Speed settings related to intra prediction mode selection
pub struct PredictionSpeedSettings {
  /// Prediction modes to search.
  ///
  /// Complex settings are slower.
  pub prediction_modes: PredictionModesSetting,

  /// Use fine directional intra prediction
  pub fine_directional_intra: bool,
}

/// Range of block sizes to use.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PartitionRange {
  pub(crate) min: BlockSize,
  pub(crate) max: BlockSize,
}

impl PartitionRange {
  /// Creates a new partition range with min and max partition sizes.
  ///
  /// # Panics
  ///
  /// - Panics if `max` is larger than `min`.
  /// - Panics if either `min` or `max` are not square.
  pub fn new(min: BlockSize, max: BlockSize) -> Self {
    assert!(max >= min);
    // Topdown search checks the min block size for PARTITION_SPLIT only, so
    // the min block size must be square.
    assert!(min.is_sqr());
    // Rectangular max partition sizes have not been tested.
    assert!(max.is_sqr());

    Self { min, max }
  }
}

#[cfg(test)]
impl Default for PartitionRange {
  fn default() -> Self {
    PartitionRange::new(BlockSize::BLOCK_4X4, BlockSize::BLOCK_64X64)
  }
}

/// Prediction modes to search.
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum SceneDetectionSpeed {
  /// Fastest scene detection using pixel-wise comparison
  Fast,
  /// Scene detection using motion vectors and cost estimates
  Standard,
  /// Completely disable scene detection and only place keyframes
  /// at fixed intervals.
  None,
}

impl fmt::Display for SceneDetectionSpeed {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        SceneDetectionSpeed::Fast => "Fast",
        SceneDetectionSpeed::Standard => "Standard",
        SceneDetectionSpeed::None => "None",
      }
    )
  }
}

/// Prediction modes to search.
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum PredictionModesSetting {
  /// Only simple prediction modes.
  Simple,
  /// Search all prediction modes on key frames and simple modes on other
  /// frames.
  ComplexKeyframes,
  /// Search all prediction modes on all frames.
  ComplexAll,
}

impl fmt::Display for PredictionModesSetting {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        PredictionModesSetting::Simple => "Simple",
        PredictionModesSetting::ComplexKeyframes => "Complex-KFs",
        PredictionModesSetting::ComplexAll => "Complex-All",
      }
    )
  }
}

#[cfg(test)]
impl Default for PredictionModesSetting {
  fn default() -> Self {
    PredictionModesSetting::Simple
  }
}

/// Search level for self guided restoration
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum SGRComplexityLevel {
  /// Search all sgr parameters
  Full,
  /// Search a reduced set of sgr parameters
  Reduced,
}

impl fmt::Display for SGRComplexityLevel {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        SGRComplexityLevel::Full => "Full",
        SGRComplexityLevel::Reduced => "Reduced",
      }
    )
  }
}

/// Search level for segmentation
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum SegmentationLevel {
  /// No segmentation is signalled.
  Disabled,
  /// Segmentation index is derived from source statistics.
  Simple,
  /// Search all segmentation indices.
  Full,
}

impl fmt::Display for SegmentationLevel {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        SegmentationLevel::Disabled => "Disabled",
        SegmentationLevel::Simple => "Simple",
        SegmentationLevel::Full => "Full",
      }
    )
  }
}
<|endoftext|>


<commit_before>use runtime::Runtime;
use std::error::Error;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::fmt::Display;
use std::path::Path;
use std::result::Result;

use storage::backend::{StorageBackend, StorageBackendError};
use self::command::ExecutableCommand;
mod command;

#[derive(Debug)]
pub struct ModuleError {
    desc: String,
}

impl ModuleError {
    fn mk(desc: &'static str) -> ModuleError {
        ModuleError {
            desc: desc.to_owned().to_string(),
        }
    }
}

impl Error for ModuleError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for ModuleError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "ModuleError: {}", self.description())
    }
}

pub type ModuleResult = Result<(), ModuleError>;

pub trait Module {

    fn new(rt : &Runtime) -> Self;
    fn callnames() -> &'static [&'static str];
    fn name(&self) -> &'static str;

    fn execute(&self, rt : &Runtime) -> ModuleResult;
    fn shutdown(&self, rt : &Runtime) -> ModuleResult;

    fn getCommandBuilder<T, F>() -> F
        where F: FnOnce(StorageBackend) -> T,
              T: ExecutableCommand;

}

<commit_msg>Fix: unused import<commit_after>use runtime::Runtime;
use std::error::Error;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::fmt::Display;
use std::result::Result;

use storage::backend::{StorageBackend, StorageBackendError};
use self::command::ExecutableCommand;
mod command;

#[derive(Debug)]
pub struct ModuleError {
    desc: String,
}

impl ModuleError {
    fn mk(desc: &'static str) -> ModuleError {
        ModuleError {
            desc: desc.to_owned().to_string(),
        }
    }
}

impl Error for ModuleError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for ModuleError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "ModuleError: {}", self.description())
    }
}

pub type ModuleResult = Result<(), ModuleError>;

pub trait Module {

    fn new(rt : &Runtime) -> Self;
    fn callnames() -> &'static [&'static str];
    fn name(&self) -> &'static str;

    fn execute(&self, rt : &Runtime) -> ModuleResult;
    fn shutdown(&self, rt : &Runtime) -> ModuleResult;

    fn getCommandBuilder<T, F>() -> F
        where F: FnOnce(StorageBackend) -> T,
              T: ExecutableCommand;

}

<|endoftext|>


<commit_before>//! Implementations of the default callbacks exposed by wlc.
//! These functions are the main entry points into Way Cooler from user action.
//! This is the default mode that Way Cooler is in at initilization
use rustwlc::*;
use rustwlc::wayland::wlc_view_get_wl_client;
use rustwlc::input::pointer;
use wayland_sys::server::wl_client;

use super::{Mode, Modes, Default, write_current_mode};
use ::layout::try_lock_tree;

use rustwlc::{ResizeEdge, WlcView, Geometry, Point};
use rustwlc::types::VIEW_ACTIVATED;

/// A lock screen program, that has been spawned by Way Cooler.
///
/// The PID is what is returned from spawning the subprocess.
///
/// The view is optional, because after spawning it we must find it in the
/// `view_created` callback.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LockScreen {
    /// Lists of clients that want to lock the screen.
    ///
    /// Multiple clients can attempt to lock a screen.
    /// The normal mode of operation is that they are the same client,
    /// but with different connections.
    ///
    ///The usize is the client id, and the view is the view it's associated with
    /// (if it has been spawned yet)
    ///
    /// The WlcOutput is the output the view should be associated with.
    pub clients: Vec<(usize, WlcOutput, Option<WlcView>)>,
    /// Flag that is set when a view is destroyed, either because the user
    /// closed it OR if the program failed to spawn.
    removed: bool
}

impl LockScreen {
    /// Creates a new Lock Screen from the PID
    ///
    /// The view is set to `None`, because we haven't found it yet in the
    /// `view_created` callback. This should be updated once it is found,
    /// so that we can start locking the screen.
    pub fn new(client: *mut wl_client, output: WlcOutput) -> Self {
        LockScreen {
            clients: vec![(client as _, output, None)],
            removed: false
        }
    }

    /// Adds the view to the `LockScreen` if it's PID matches the stored one.
    ///
    /// If there is already here, false is always returned.
    ///
    /// If the view's PID matches this PID, then it's automatically focused,
    /// and the geometry of it is set to the size of it's `WlcOutput`'s size.
    fn add_view_if_match(&mut self, view: WlcView) -> bool {
        let cur_client = unsafe {
            wlc_view_get_wl_client(view.0 as _) as _
        };
        warn!("Checkng view {:#?}", view);
        for client in &mut self.clients {
            if client.0 == cur_client && client.2 == None {
                error!("Found a match, setting to output {:#?}", client.1);
                view.set_output(client.1);
                client.2 = Some(view);
                let output = view.get_output();
                let resolution = output.get_resolution()
                    .expect("Output was invalid: had no resolution");
                let geo = Geometry {
                    origin: Point { x: 0, y: 0 },
                    size: resolution
                };
                view.set_geometry(ResizeEdge::empty(), geo);
                view.set_state(VIEW_ACTIVATED, true);
                view.focus();
                view.set_mask(1);
                view.bring_to_front();
                return true
            }
        }
        false
    }

    /// Removes the lockscreen if provided view matches the stored view.
    ///
    /// Does not compare the PID, because the passed in `WlcView` should be
    /// considered invalidated because this function is probably being called
    /// from `view_destroyed`.
    ///
    /// To protect against failing programs, the child handler should be
    /// checked to see if the program is still running.
    /// HOWEVER, this is blocked because this is still a nightly feature.
    /// See https://github.com/rust-lang/rust/issues/38903
    fn remove_if_match(&mut self, cur_view: WlcView) -> Option<WlcView> {
        for &mut (_, _, ref mut view) in &mut self.clients {
            if *view == Some(cur_view) {
                self.removed = true;
                // invalidate this view so we can't do anything to it.
                return view.take()
            }
        }
        None
    }

    fn eval_if_lockscreen<F, T>(&mut self, cur_view: WlcView, eval: F) -> T
        where F: Fn(WlcView) -> T,
              T: ::std::default::Default
    {
        for &(_, _, ref view) in &self.clients {
            if *view == Some(cur_view) {
                return eval(view.unwrap())
            }
        }
        T::default()
    }
}

#[allow(unused)]
impl Mode for LockScreen {
    fn output_render_post(&mut self, output: WlcOutput) {
        Default.output_render_post(output);
        // TODO Uncomment
        // we should allow this some other way
        /*// Don't allow screen scraping during lock screen
        let need_to_fetch = read_screen_scrape_lock();
        if *need_to_fetch {
            if let Ok(mut scraped_pixels) = scraped_pixels_lock() {
                let resolution = output.get_resolution()
                    .expect("Output had no resolution");
                // Give them zero-ed out pixels
                *scraped_pixels = vec![0; resolution.w as usize * resolution.h as usize * 4];
                sync_scrape();
            }
        }*/
    }

    fn view_created(&mut self, view: WlcView) -> bool {
        // this will focus and set the size if necessary.
        if self.add_view_if_match(view) {
            trace!("Adding lockscreen");
            true
        } else {
            false
        }
    }

    fn view_destroyed(&mut self, view: WlcView) {
        // Always remove the destroyed view from the tree.
        Default.view_destroyed(view);
        // View is automatically removed from the internal lock screen struct.
        if self.remove_if_match(view).is_some() {
            trace!("Removing lock screen");
            // Update the active container in the tree to the active path end.
            if let Ok(mut tree) = try_lock_tree() {
                tree.reset_focus()
                    .unwrap_or_else(|_| {
                        warn!("Could not reset focus\
                              after removing lock screen");
                    })
            }
        }
    }

    fn view_focused(&mut self, current: WlcView, focused: bool) {
        // Do nothing
    }

    fn view_request_state(&mut self, view: WlcView, state: ViewState, toggle: bool) {
        // Do nothing
    }

    fn view_request_move(&mut self, view: WlcView, _dest: Point) {
        // Do nothing
    }

    fn view_request_resize(&mut self, view: WlcView, edge: ResizeEdge, point: Point) {
        // Do nothing
    }

    fn on_keyboard_key(&mut self, view: WlcView, _time: u32, mods: KeyboardModifiers,
                            key: u32, state: KeyState) -> bool {
        !self.eval_if_lockscreen(view, |view| {
            view.focus();
            true
        })
    }

    fn on_pointer_button(&mut self, view: WlcView, _time: u32,
                            mods: KeyboardModifiers, button: u32,
                                state: ButtonState, point: Point) -> bool {
        self.eval_if_lockscreen(view, |view| {
            view.focus();
            true
        })
    }

    fn on_pointer_scroll(&mut self, view: WlcView, _time: u32,
                            _mods_ptr: KeyboardModifiers, _axis: ScrollAxis,
                            _heights: [f64; 2]) -> bool {
        self.eval_if_lockscreen(view, |view| {
            view.focus();
            true
        })
    }

    fn on_pointer_motion(&mut self, view: WlcView, _time: u32, point: Point) -> bool {
        !self.eval_if_lockscreen(view, |view| {
            view.focus();
            pointer::set_position(point);
            // false because default is false, but true is the base case here
            false
        })
    }
}

pub fn lock_screen(client: *mut wl_client, output: WlcOutput) {
    let mut mode = write_current_mode();
    warn!("Locking screen w/ mode {:#?}", mode);
    {
        match *mode {
            Modes::LockScreen(ref mut lock_mode) => {
                warn!("Found another view");
                lock_mode.clients.push((client as _, output, None));
                return
            },
            _ => {}
        }
    }
    *mode = Modes::LockScreen(LockScreen::new(client, output));
}

pub fn unlock_screen(cur_client: *mut wl_client) {
    let mode = {
        write_current_mode().clone()
    };
    match mode {
        Modes::LockScreen(ref lock_mode) => {
            let mut seen = false;
            for &(client, _, view) in &lock_mode.clients {
                if client == cur_client as _ {
                    seen = true;
                    break;
                }
                view.map(WlcView::close);
            }
            if !seen {
                return
            }
        },
        _ => {}
    }
    *write_current_mode() = Modes::Default(Default);
}
<commit_msg>Removed warnings<commit_after>//! Implementations of the default callbacks exposed by wlc.
//! These functions are the main entry points into Way Cooler from user action.
//! This is the default mode that Way Cooler is in at initilization
use rustwlc::*;
use rustwlc::wayland::wlc_view_get_wl_client;
use rustwlc::input::pointer;
use wayland_sys::server::wl_client;

use super::{Mode, Modes, Default, write_current_mode};
use ::layout::try_lock_tree;

use rustwlc::{ResizeEdge, WlcView, Geometry, Point};
use rustwlc::types::VIEW_ACTIVATED;

/// A lock screen program, that has been spawned by Way Cooler.
///
/// The PID is what is returned from spawning the subprocess.
///
/// The view is optional, because after spawning it we must find it in the
/// `view_created` callback.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LockScreen {
    /// Lists of clients that want to lock the screen.
    ///
    /// Multiple clients can attempt to lock a screen.
    /// The normal mode of operation is that they are the same client,
    /// but with different connections.
    ///
    ///The usize is the client id, and the view is the view it's associated with
    /// (if it has been spawned yet)
    ///
    /// The WlcOutput is the output the view should be associated with.
    pub clients: Vec<(usize, WlcOutput, Option<WlcView>)>,
    /// Flag that is set when a view is destroyed, either because the user
    /// closed it OR if the program failed to spawn.
    removed: bool
}

impl LockScreen {
    /// Creates a new Lock Screen from the PID
    ///
    /// The view is set to `None`, because we haven't found it yet in the
    /// `view_created` callback. This should be updated once it is found,
    /// so that we can start locking the screen.
    pub fn new(client: *mut wl_client, output: WlcOutput) -> Self {
        LockScreen {
            clients: vec![(client as _, output, None)],
            removed: false
        }
    }

    /// Adds the view to the `LockScreen` if it's PID matches the stored one.
    ///
    /// If there is already here, false is always returned.
    ///
    /// If the view's PID matches this PID, then it's automatically focused,
    /// and the geometry of it is set to the size of it's `WlcOutput`'s size.
    fn add_view_if_match(&mut self, view: WlcView) -> bool {
        let cur_client = unsafe {
            wlc_view_get_wl_client(view.0 as _) as _
        };
        for client in &mut self.clients {
            if client.0 == cur_client && client.2 == None {
                error!("Found a match, setting to output {:#?}", client.1);
                view.set_output(client.1);
                client.2 = Some(view);
                let output = view.get_output();
                let resolution = output.get_resolution()
                    .expect("Output was invalid: had no resolution");
                let geo = Geometry {
                    origin: Point { x: 0, y: 0 },
                    size: resolution
                };
                view.set_geometry(ResizeEdge::empty(), geo);
                view.set_state(VIEW_ACTIVATED, true);
                view.focus();
                view.set_mask(1);
                view.bring_to_front();
                return true
            }
        }
        false
    }

    /// Removes the lockscreen if provided view matches the stored view.
    ///
    /// Does not compare the PID, because the passed in `WlcView` should be
    /// considered invalidated because this function is probably being called
    /// from `view_destroyed`.
    ///
    /// To protect against failing programs, the child handler should be
    /// checked to see if the program is still running.
    /// HOWEVER, this is blocked because this is still a nightly feature.
    /// See https://github.com/rust-lang/rust/issues/38903
    fn remove_if_match(&mut self, cur_view: WlcView) -> Option<WlcView> {
        for &mut (_, _, ref mut view) in &mut self.clients {
            if *view == Some(cur_view) {
                self.removed = true;
                // invalidate this view so we can't do anything to it.
                return view.take()
            }
        }
        None
    }

    fn eval_if_lockscreen<F, T>(&mut self, cur_view: WlcView, eval: F) -> T
        where F: Fn(WlcView) -> T,
              T: ::std::default::Default
    {
        for &(_, _, ref view) in &self.clients {
            if *view == Some(cur_view) {
                return eval(view.unwrap())
            }
        }
        T::default()
    }
}

#[allow(unused)]
impl Mode for LockScreen {
    fn output_render_post(&mut self, output: WlcOutput) {
        Default.output_render_post(output);
        // TODO Uncomment
        // we should allow this some other way
        /*// Don't allow screen scraping during lock screen
        let need_to_fetch = read_screen_scrape_lock();
        if *need_to_fetch {
            if let Ok(mut scraped_pixels) = scraped_pixels_lock() {
                let resolution = output.get_resolution()
                    .expect("Output had no resolution");
                // Give them zero-ed out pixels
                *scraped_pixels = vec![0; resolution.w as usize * resolution.h as usize * 4];
                sync_scrape();
            }
        }*/
    }

    fn view_created(&mut self, view: WlcView) -> bool {
        // this will focus and set the size if necessary.
        if self.add_view_if_match(view) {
            trace!("Adding lockscreen");
            true
        } else {
            false
        }
    }

    fn view_destroyed(&mut self, view: WlcView) {
        // Always remove the destroyed view from the tree.
        Default.view_destroyed(view);
        // View is automatically removed from the internal lock screen struct.
        if self.remove_if_match(view).is_some() {
            trace!("Removing lock screen");
            // Update the active container in the tree to the active path end.
            if let Ok(mut tree) = try_lock_tree() {
                tree.reset_focus()
                    .unwrap_or_else(|_| {
                        warn!("Could not reset focus\
                              after removing lock screen");
                    })
            }
        }
    }

    fn view_focused(&mut self, current: WlcView, focused: bool) {
        // Do nothing
    }

    fn view_request_state(&mut self, view: WlcView, state: ViewState, toggle: bool) {
        // Do nothing
    }

    fn view_request_move(&mut self, view: WlcView, _dest: Point) {
        // Do nothing
    }

    fn view_request_resize(&mut self, view: WlcView, edge: ResizeEdge, point: Point) {
        // Do nothing
    }

    fn on_keyboard_key(&mut self, view: WlcView, _time: u32, mods: KeyboardModifiers,
                            key: u32, state: KeyState) -> bool {
        !self.eval_if_lockscreen(view, |view| {
            view.focus();
            true
        })
    }

    fn on_pointer_button(&mut self, view: WlcView, _time: u32,
                            mods: KeyboardModifiers, button: u32,
                                state: ButtonState, point: Point) -> bool {
        self.eval_if_lockscreen(view, |view| {
            view.focus();
            true
        })
    }

    fn on_pointer_scroll(&mut self, view: WlcView, _time: u32,
                            _mods_ptr: KeyboardModifiers, _axis: ScrollAxis,
                            _heights: [f64; 2]) -> bool {
        self.eval_if_lockscreen(view, |view| {
            view.focus();
            true
        })
    }

    fn on_pointer_motion(&mut self, view: WlcView, _time: u32, point: Point) -> bool {
        !self.eval_if_lockscreen(view, |view| {
            view.focus();
            pointer::set_position(point);
            // false because default is false, but true is the base case here
            false
        })
    }
}

pub fn lock_screen(client: *mut wl_client, output: WlcOutput) {
    let mut mode = write_current_mode();
    {
        match *mode {
            Modes::LockScreen(ref mut lock_mode) => {
                lock_mode.clients.push((client as _, output, None));
                return
            },
            _ => {}
        }
    }
    *mode = Modes::LockScreen(LockScreen::new(client, output));
}

pub fn unlock_screen(cur_client: *mut wl_client) {
    let mode = {
        write_current_mode().clone()
    };
    match mode {
        Modes::LockScreen(ref lock_mode) => {
            let mut seen = false;
            for &(client, _, view) in &lock_mode.clients {
                if client == cur_client as _ {
                    seen = true;
                    break;
                }
                view.map(WlcView::close);
            }
            if !seen {
                return
            }
        },
        _ => {}
    }
    *write_current_mode() = Modes::Default(Default);
}
<|endoftext|>