<commit_before>//! The `SubotaiHash` is the element around which the Subotai network organizes itself.
//! It's 160 bits long, and it serves as an ID number to identify nodes, as a key to
//! identify storage entries, and optionally as a stored value.
//!
//! This module exposes utilities to create and inspect `SubotaiHash` structures. A
//! useful method is `sha1`, which allows you to create a sha-1 hash from some data,
//! which can then be used as a key for a storage entry.
use rand::{thread_rng, Rng};
use itertools;
use std::ops::BitXor;
use std::fmt;
use std::fmt::Write;
use std::cmp::{PartialOrd, Ordering};
use sha1;

pub const HASH_SIZE : usize = 160;
pub const HASH_SIZE_BYTES : usize = HASH_SIZE / 8;

/// Light wrapper over a little endian `HASH_SIZE` bit hash.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SubotaiHash {
   pub raw : [u8; HASH_SIZE_BYTES],
}

impl SubotaiHash {
   /// Generates a blank hash (every bit set to 0).
   pub fn blank() -> SubotaiHash {
      SubotaiHash { raw : [0; HASH_SIZE_BYTES] }
   }

   /// Generates a random hash via kernel supplied entropy.
   pub fn random() -> SubotaiHash {
      let mut hash = SubotaiHash::blank();
      thread_rng().fill_bytes(&mut hash.raw);
      hash
   }

   /// Generates a SHA-1 hash from a string.
   pub fn sha1(data: &str) -> SubotaiHash {
      let mut m = sha1::Sha1::new();
      m.reset();
      m.update(data.as_bytes());
      SubotaiHash {
         raw: m.digest().bytes(),
      }
   }

   /// Provides an iterator through the indices
   /// of each of its "0" bits.
   pub fn zeroes(&self) -> Zeroes {
      Zeroes {
         hash  : self,
         index : 0,
         rev   : HASH_SIZE
      }
   }

   /// Provides an iterator through the indices
   /// of each of its "1" bits.
   pub fn ones(&self) -> Ones {
      Ones {
         hash  : self,
         index : 0,
         rev   : HASH_SIZE
      }
   }

   /// Provides a consuming iterator through the
   /// indices of each of its "0" bits.
   pub fn into_zeroes(self) -> IntoZeroes {
      IntoZeroes {
         hash  : self,
         index : 0,
         rev   : HASH_SIZE
      }
   }

   /// Provides a consuming iterator through the
   /// indices of each of its "1" bits.
   pub fn into_ones(self) -> IntoOnes {
      IntoOnes {
         hash  : self,
         index : 0,
         rev   : HASH_SIZE
      }
   }

   /// Computes the bit index of the highest "1". Returns None for a blank hash.
   pub fn height(&self) -> Option<usize> {
      let last_nonzero_byte = self.raw.iter().enumerate().rev().find(|&pair| *pair.1 != 0);
      if let Some((index, byte)) = last_nonzero_byte {
         for bit in (0..8).rev() {
            if (byte & (1 << bit)) != 0 {
               return Some((8 * index + bit) as usize)
            }
         }
      }
      None
   }

   /// Flips a bit in the hash.
   pub fn flip_bit(&mut self, position : usize) {
      if position >= HASH_SIZE { return; }
      let byte = &mut self.raw[position / 8];
      *byte ^= 1 << (position % 8);
   }

   /// Creates a random hash at a given XOR distance from another (height of their XOR value).
   pub fn random_at_distance(reference: &SubotaiHash, distance: usize) -> SubotaiHash {
      let mut random_hash = SubotaiHash::random();
      let distance_ones = (&random_hash ^ reference).into_ones();
      for index in distance_ones.rev() {
         random_hash.flip_bit(index);
         if let Some(height) = (&random_hash ^ reference).height() {
            if height == distance {
               return random_hash;
            } else if height < distance {
               random_hash.flip_bit(distance);
               return random_hash;
            }
         }
      }

      random_hash
   }
}

impl fmt::Display for SubotaiHash {
   fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
      let mut leftpad_over = false;
      let mut hex = String::new();
      hex.push_str("0x[");
      for byte in self.raw.iter().rev() {
         if *byte > 0u8 {
            leftpad_over = true;
         }

         if leftpad_over {
            write!(&mut hex, "{:01$X}", byte, 2).unwrap();
         }
      }
      hex.push_str("]");
      write!(f, "{}", hex)
   }
}

/// Iterator through the indices of each '0' in a hash.
pub struct Zeroes<'a> {
   hash  : &'a SubotaiHash,
   index : usize,
   rev   : usize
}

/// Iterator through the indices of each '1' in a hash.
pub struct Ones<'a> {
   hash  : &'a SubotaiHash,
   index : usize,
   rev   : usize,
}

/// Consuming iterator through the indices of each '0' in a hash.
pub struct IntoZeroes {
   hash  : SubotaiHash,
   index : usize,
   rev   : usize
}

/// Consuming iterator through the indices of each '1' in a hash.
pub struct IntoOnes {
   hash  : SubotaiHash,
   index : usize,
   rev   : usize,
}

impl<'a> Iterator for Zeroes<'a> {
   type Item = usize;

   fn next(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_index = self.hash.raw[self.index / 8] & (1 << (self.index % 8));
         self.index += 1;
         if value_at_index == 0 {
            return Some(self.index - 1);
         }
      }
      None
   }
}

impl<'a> Iterator for Ones<'a> {
   type Item = usize;

   fn next(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_index = self.hash.raw[self.index / 8] & (1 << (self.index % 8));
         self.index += 1;
         if value_at_index != 0 {
            return Some(self.index - 1);
         }
      }
      None
   }
}

impl<'a> DoubleEndedIterator for Zeroes<'a> {
   fn next_back(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_rev = self.hash.raw[(self.rev-1) / 8] & (1 << ((self.rev-1) % 8));
         self.rev -= 1;
         if value_at_rev == 0 {
            return Some(self.rev);
         }
      }
      None
   }
}

impl<'a> DoubleEndedIterator for Ones<'a> {
   fn next_back(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_rev = self.hash.raw[(self.rev-1) / 8] & (1 << ((self.rev-1) % 8));
         self.rev -= 1;
         if value_at_rev != 0 {
            return Some(self.rev);
         }
      }
      None
   }
}

impl Iterator for IntoZeroes {
   type Item = usize;

   fn next(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_index = self.hash.raw[self.index / 8] & (1 << (self.index % 8));
         self.index += 1;
         if value_at_index == 0 {
            return Some(self.index - 1);
         }
      }
      None
   }
}

impl Iterator for IntoOnes {
   type Item = usize;

   fn next(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_index = self.hash.raw[self.index / 8] & (1 << (self.index % 8));
         self.index += 1;
         if value_at_index != 0 {
            return Some(self.index - 1);
         }
      }
      None
   }
}

impl DoubleEndedIterator for IntoZeroes {
   fn next_back(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_rev = self.hash.raw[(self.rev-1) / 8] & (1 << ((self.rev-1) % 8));
         self.rev -= 1;
         if value_at_rev == 0 {
            return Some(self.rev);
         }
      }
      None
   }
}

impl DoubleEndedIterator for IntoOnes {
   fn next_back(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_rev = self.hash.raw[(self.rev-1) / 8] & (1 << ((self.rev-1) % 8));
         self.rev -= 1;
         if value_at_rev != 0 {
            return Some(self.rev);
         }
      }
      None
   }
}

impl PartialOrd for SubotaiHash {
   fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
      for (a,b) in self.raw.iter().rev().zip(other.raw.iter().rev()) {
         match a.cmp(b) {
            Ordering::Less => return Some(Ordering::Less),
            Ordering::Greater => return Some(Ordering::Greater),
            Ordering::Equal => ()
         }
      }
      None
   }
}

impl Ord for SubotaiHash {
   fn cmp(&self, other: &Self) -> Ordering {
      match self.partial_cmp(other) {
         Some(order) => order,
         None => Ordering::Equal
      }
   }
}

impl<'a, 'b> BitXor<&'b SubotaiHash> for &'a SubotaiHash {
   type Output = SubotaiHash;

   fn bitxor (self, rhs: &'b SubotaiHash) -> SubotaiHash {
      let mut result = SubotaiHash::blank();
      for (d, a, b) in itertools::multizip((&mut result.raw, &self.raw, &rhs.raw)) {
         *d = a^b;
      }
      result
   }
}

impl BitXor for SubotaiHash {
   type Output = SubotaiHash;

   fn bitxor (mut self, rhs: Self) -> SubotaiHash {
      for (a, b) in self.raw.iter_mut().zip(rhs.raw.iter()) {
         *a ^= *b;
      }
      self
   }
}

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

    #[test]
    fn random_generation() {
       assert!(SubotaiHash::random() != SubotaiHash::random());
    }

    #[test]
    fn xor() {
       let alpha = SubotaiHash::random();
       let beta = SubotaiHash {
          raw: alpha.raw,
       };

       let reference_xor = &alpha ^ β
       let value_xor = alpha ^ beta;

       for (a, b) in reference_xor.raw.iter().zip(value_xor.raw.iter()) {
          assert_eq!(*a, 0x00);
          assert_eq!(*b, 0x00);
       }
    }

    #[test]
    fn computing_height() {
       let mut test_hash = SubotaiHash::blank();
       assert!(test_hash.height().is_none());

       // First bit
       test_hash.raw[0] = 1;
       assert_eq!(test_hash.height(), Some(0));

       // Fourth bit (index 3)
       test_hash.raw[0] = test_hash.raw[0] | (1 << 3);
       assert_eq!(test_hash.height(), Some(3));

       // Last bit (index 159)
       test_hash.raw[19] = 1 << 7;
       assert_eq!(test_hash.height(), Some(159));
    }

    #[test]
    fn bit_flipping() {
       let mut test_hash = SubotaiHash::blank();
       test_hash.flip_bit(9);
       assert_eq!(test_hash.raw[1], 2);
       test_hash.flip_bit(9);
       assert_eq!(test_hash.raw[1], 0);
    }

    #[test]
    fn iterating_over_ones() {
       let mut test_hash = SubotaiHash::blank();
       let bits = vec![5usize,20,40];

       for bit in &bits {
          test_hash.flip_bit(*bit);
       }

       for (actual, expected) in test_hash.ones().zip(bits) {
          assert_eq!(actual, expected);
       }
    }

   #[test]
   fn random_at_a_distance() {
      let test_hash = SubotaiHash::random();
      let distance = 30usize;
      let new_hash = SubotaiHash::random_at_distance(&test_hash, distance);
      let distance_hash = test_hash ^ new_hash;
      assert_eq!(distance, (distance_hash).height().unwrap());
   }
}
<commit_msg>Documentation<commit_after>//! The `SubotaiHash` is the element around which the Subotai network organizes itself.
//! It's 160 bits long, and it serves as an ID number to identify nodes, as a key to
//! identify storage entries, and optionally as a stored value.
//!
//! This module exposes utilities to create and inspect `SubotaiHash` structures. A
//! useful method is `sha1`, which allows you to create a sha-1 hash from some data,
//! which can then be used as a key for a storage entry.
use rand::{thread_rng, Rng};
use itertools;
use std::ops::BitXor;
use std::fmt;
use std::fmt::Write;
use std::cmp::{PartialOrd, Ordering};
use sha1;

pub const HASH_SIZE : usize = 160;
pub const HASH_SIZE_BYTES : usize = HASH_SIZE / 8;

/// Subotai hash, a light wrapper over a little endian `HASH_SIZE` bit hash.
/// It can be generated randomly or via sha-1 of a given string.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SubotaiHash {
   pub raw : [u8; HASH_SIZE_BYTES],
}

impl SubotaiHash {
   /// Generates a blank hash (every bit set to 0).
   pub fn blank() -> SubotaiHash {
      SubotaiHash { raw : [0; HASH_SIZE_BYTES] }
   }

   /// Generates a random hash via kernel supplied entropy.
   pub fn random() -> SubotaiHash {
      let mut hash = SubotaiHash::blank();
      thread_rng().fill_bytes(&mut hash.raw);
      hash
   }

   /// Generates a SHA-1 hash from a string.
   pub fn sha1(data: &str) -> SubotaiHash {
      let mut m = sha1::Sha1::new();
      m.reset();
      m.update(data.as_bytes());
      SubotaiHash {
         raw: m.digest().bytes(),
      }
   }

   /// Creates a random hash at a given XOR distance from another (height of their XOR value).
   pub fn random_at_distance(reference: &SubotaiHash, distance: usize) -> SubotaiHash {
      let mut random_hash = SubotaiHash::random();
      let distance_ones = (&random_hash ^ reference).into_ones();
      for index in distance_ones.rev() {
         random_hash.flip_bit(index);
         if let Some(height) = (&random_hash ^ reference).height() {
            if height == distance {
               return random_hash;
            } else if height < distance {
               random_hash.flip_bit(distance);
               return random_hash;
            }
         }
      }

      random_hash
   }

   /// Provides an iterator through the indices
   /// of each of its "0" bits.
   pub fn zeroes(&self) -> Zeroes {
      Zeroes {
         hash  : self,
         index : 0,
         rev   : HASH_SIZE
      }
   }

   /// Provides an iterator through the indices
   /// of each of its "1" bits.
   pub fn ones(&self) -> Ones {
      Ones {
         hash  : self,
         index : 0,
         rev   : HASH_SIZE
      }
   }

   /// Provides a consuming iterator through the
   /// indices of each of its "0" bits.
   pub fn into_zeroes(self) -> IntoZeroes {
      IntoZeroes {
         hash  : self,
         index : 0,
         rev   : HASH_SIZE
      }
   }

   /// Provides a consuming iterator through the
   /// indices of each of its "1" bits.
   pub fn into_ones(self) -> IntoOnes {
      IntoOnes {
         hash  : self,
         index : 0,
         rev   : HASH_SIZE
      }
   }

   /// Computes the bit index of the highest "1". Returns None for a blank hash.
   pub fn height(&self) -> Option<usize> {
      let last_nonzero_byte = self.raw.iter().enumerate().rev().find(|&pair| *pair.1 != 0);
      if let Some((index, byte)) = last_nonzero_byte {
         for bit in (0..8).rev() {
            if (byte & (1 << bit)) != 0 {
               return Some((8 * index + bit) as usize)
            }
         }
      }
      None
   }

   /// Flips a bit in the hash.
   pub fn flip_bit(&mut self, position : usize) {
      if position >= HASH_SIZE { return; }
      let byte = &mut self.raw[position / 8];
      *byte ^= 1 << (position % 8);
   }
}

impl fmt::Display for SubotaiHash {
   fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
      let mut leftpad_over = false;
      let mut hex = String::new();
      hex.push_str("0x[");
      for byte in self.raw.iter().rev() {
         if *byte > 0u8 {
            leftpad_over = true;
         }

         if leftpad_over {
            write!(&mut hex, "{:01$X}", byte, 2).unwrap();
         }
      }
      hex.push_str("]");
      write!(f, "{}", hex)
   }
}

/// Iterator through the indices of each '0' in a hash.
pub struct Zeroes<'a> {
   hash  : &'a SubotaiHash,
   index : usize,
   rev   : usize
}

/// Iterator through the indices of each '1' in a hash.
pub struct Ones<'a> {
   hash  : &'a SubotaiHash,
   index : usize,
   rev   : usize,
}

/// Consuming iterator through the indices of each '0' in a hash.
pub struct IntoZeroes {
   hash  : SubotaiHash,
   index : usize,
   rev   : usize
}

/// Consuming iterator through the indices of each '1' in a hash.
pub struct IntoOnes {
   hash  : SubotaiHash,
   index : usize,
   rev   : usize,
}

impl<'a> Iterator for Zeroes<'a> {
   type Item = usize;

   fn next(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_index = self.hash.raw[self.index / 8] & (1 << (self.index % 8));
         self.index += 1;
         if value_at_index == 0 {
            return Some(self.index - 1);
         }
      }
      None
   }
}

impl<'a> Iterator for Ones<'a> {
   type Item = usize;

   fn next(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_index = self.hash.raw[self.index / 8] & (1 << (self.index % 8));
         self.index += 1;
         if value_at_index != 0 {
            return Some(self.index - 1);
         }
      }
      None
   }
}

impl<'a> DoubleEndedIterator for Zeroes<'a> {
   fn next_back(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_rev = self.hash.raw[(self.rev-1) / 8] & (1 << ((self.rev-1) % 8));
         self.rev -= 1;
         if value_at_rev == 0 {
            return Some(self.rev);
         }
      }
      None
   }
}

impl<'a> DoubleEndedIterator for Ones<'a> {
   fn next_back(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_rev = self.hash.raw[(self.rev-1) / 8] & (1 << ((self.rev-1) % 8));
         self.rev -= 1;
         if value_at_rev != 0 {
            return Some(self.rev);
         }
      }
      None
   }
}

impl Iterator for IntoZeroes {
   type Item = usize;

   fn next(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_index = self.hash.raw[self.index / 8] & (1 << (self.index % 8));
         self.index += 1;
         if value_at_index == 0 {
            return Some(self.index - 1);
         }
      }
      None
   }
}

impl Iterator for IntoOnes {
   type Item = usize;

   fn next(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_index = self.hash.raw[self.index / 8] & (1 << (self.index % 8));
         self.index += 1;
         if value_at_index != 0 {
            return Some(self.index - 1);
         }
      }
      None
   }
}

impl DoubleEndedIterator for IntoZeroes {
   fn next_back(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_rev = self.hash.raw[(self.rev-1) / 8] & (1 << ((self.rev-1) % 8));
         self.rev -= 1;
         if value_at_rev == 0 {
            return Some(self.rev);
         }
      }
      None
   }
}

impl DoubleEndedIterator for IntoOnes {
   fn next_back(&mut self) -> Option<usize> {
      while self.index < self.rev {
         let value_at_rev = self.hash.raw[(self.rev-1) / 8] & (1 << ((self.rev-1) % 8));
         self.rev -= 1;
         if value_at_rev != 0 {
            return Some(self.rev);
         }
      }
      None
   }
}

impl PartialOrd for SubotaiHash {
   fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
      for (a,b) in self.raw.iter().rev().zip(other.raw.iter().rev()) {
         match a.cmp(b) {
            Ordering::Less => return Some(Ordering::Less),
            Ordering::Greater => return Some(Ordering::Greater),
            Ordering::Equal => ()
         }
      }
      None
   }
}

impl Ord for SubotaiHash {
   fn cmp(&self, other: &Self) -> Ordering {
      match self.partial_cmp(other) {
         Some(order) => order,
         None => Ordering::Equal
      }
   }
}

impl<'a, 'b> BitXor<&'b SubotaiHash> for &'a SubotaiHash {
   type Output = SubotaiHash;

   fn bitxor (self, rhs: &'b SubotaiHash) -> SubotaiHash {
      let mut result = SubotaiHash::blank();
      for (d, a, b) in itertools::multizip((&mut result.raw, &self.raw, &rhs.raw)) {
         *d = a^b;
      }
      result
   }
}

impl BitXor for SubotaiHash {
   type Output = SubotaiHash;

   fn bitxor (mut self, rhs: Self) -> SubotaiHash {
      for (a, b) in self.raw.iter_mut().zip(rhs.raw.iter()) {
         *a ^= *b;
      }
      self
   }
}

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

    #[test]
    fn random_generation() {
       assert!(SubotaiHash::random() != SubotaiHash::random());
    }

    #[test]
    fn xor() {
       let alpha = SubotaiHash::random();
       let beta = SubotaiHash {
          raw: alpha.raw,
       };

       let reference_xor = &alpha ^ β
       let value_xor = alpha ^ beta;

       for (a, b) in reference_xor.raw.iter().zip(value_xor.raw.iter()) {
          assert_eq!(*a, 0x00);
          assert_eq!(*b, 0x00);
       }
    }

    #[test]
    fn computing_height() {
       let mut test_hash = SubotaiHash::blank();
       assert!(test_hash.height().is_none());

       // First bit
       test_hash.raw[0] = 1;
       assert_eq!(test_hash.height(), Some(0));

       // Fourth bit (index 3)
       test_hash.raw[0] = test_hash.raw[0] | (1 << 3);
       assert_eq!(test_hash.height(), Some(3));

       // Last bit (index 159)
       test_hash.raw[19] = 1 << 7;
       assert_eq!(test_hash.height(), Some(159));
    }

    #[test]
    fn bit_flipping() {
       let mut test_hash = SubotaiHash::blank();
       test_hash.flip_bit(9);
       assert_eq!(test_hash.raw[1], 2);
       test_hash.flip_bit(9);
       assert_eq!(test_hash.raw[1], 0);
    }

    #[test]
    fn iterating_over_ones() {
       let mut test_hash = SubotaiHash::blank();
       let bits = vec![5usize,20,40];

       for bit in &bits {
          test_hash.flip_bit(*bit);
       }

       for (actual, expected) in test_hash.ones().zip(bits) {
          assert_eq!(actual, expected);
       }
    }

   #[test]
   fn random_at_a_distance() {
      let test_hash = SubotaiHash::random();
      let distance = 30usize;
      let new_hash = SubotaiHash::random_at_distance(&test_hash, distance);
      let distance_hash = test_hash ^ new_hash;
      assert_eq!(distance, (distance_hash).height().unwrap());
   }
}
<|endoftext|>


<commit_before>use crate::{
    backend::renderer::{utils::draw_surface_tree, Frame, ImportAll, Renderer, Texture},
    desktop::{utils::*, PopupManager, Space},
    utils::{Logical, Point, Rectangle},
    wayland::{
        compositor::with_states,
        output::Output,
        shell::xdg::{SurfaceCachedState, ToplevelSurface},
    },
};
use std::{
    cell::Cell,
    hash::{Hash, Hasher},
    rc::Rc,
};
use wayland_commons::user_data::UserDataMap;
use wayland_protocols::xdg_shell::server::xdg_toplevel;
use wayland_server::protocol::wl_surface;

crate::utils::ids::id_gen!(next_window_id, WINDOW_ID, WINDOW_IDS);

/// Abstraction around different toplevel kinds
#[derive(Debug, Clone, PartialEq)]
pub enum Kind {
    /// xdg-shell [`ToplevelSurface`]
    Xdg(ToplevelSurface),
    /// XWayland surface (TODO)
    #[cfg(feature = "xwayland")]
    X11(X11Surface),
}

/// Xwayland surface
#[derive(Debug, Clone)]
#[cfg(feature = "xwayland")]
pub struct X11Surface {
    /// underlying wl_surface
    pub surface: wl_surface::WlSurface,
}

#[cfg(feature = "xwayland")]
impl std::cmp::PartialEq for X11Surface {
    fn eq(&self, other: &Self) -> bool {
        self.alive() && other.alive() && self.surface == other.surface
    }
}

#[cfg(feature = "xwayland")]
impl X11Surface {
    /// Checks if the surface is still alive.
    pub fn alive(&self) -> bool {
        self.surface.as_ref().is_alive()
    }

    /// Returns the underlying [`WlSurface`](wl_surface::WlSurface), if still any.
    pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
        if self.alive() {
            Some(&self.surface)
        } else {
            None
        }
    }
}

impl Kind {
    /// Checks if the surface is still alive.
    pub fn alive(&self) -> bool {
        match *self {
            Kind::Xdg(ref t) => t.alive(),
            #[cfg(feature = "xwayland")]
            Kind::X11(ref t) => t.alive(),
        }
    }

    /// Returns the underlying [`WlSurface`](wl_surface::WlSurface), if still any.
    pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
        match *self {
            Kind::Xdg(ref t) => t.get_surface(),
            #[cfg(feature = "xwayland")]
            Kind::X11(ref t) => t.get_surface(),
        }
    }
}

#[derive(Debug)]
pub(super) struct WindowInner {
    pub(super) id: usize,
    toplevel: Kind,
    bbox: Cell<Rectangle<i32, Logical>>,
    user_data: UserDataMap,
}

impl Drop for WindowInner {
    fn drop(&mut self) {
        WINDOW_IDS.lock().unwrap().remove(&self.id);
    }
}

/// Represents a single application window
#[derive(Debug, Clone)]
pub struct Window(pub(super) Rc<WindowInner>);

impl PartialEq for Window {
    fn eq(&self, other: &Self) -> bool {
        self.0.id == other.0.id
    }
}

impl Eq for Window {}

impl Hash for Window {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.id.hash(state);
    }
}

bitflags::bitflags! {
    /// Defines the surface types that can be
    /// queried with [`Window::surface_under`]
    pub struct WindowSurfaceType: u32 {
        /// Include the toplevel surface
        const TOPLEVEL = 1;
        /// Include all subsurfaces
        const SUBSURFACE = 2;
        /// Include all popup surfaces
        const POPUP = 4;
        /// Query all surfaces
        const ALL = Self::TOPLEVEL.bits | Self::SUBSURFACE.bits | Self::POPUP.bits;
    }
}

impl Window {
    /// Construct a new [`Window`] from a given compatible toplevel surface
    pub fn new(toplevel: Kind) -> Window {
        let id = next_window_id();

        Window(Rc::new(WindowInner {
            id,
            toplevel,
            bbox: Cell::new(Rectangle::from_loc_and_size((0, 0), (0, 0))),
            user_data: UserDataMap::new(),
        }))
    }

    /// Returns the geometry of this window.
    pub fn geometry(&self) -> Rectangle<i32, Logical> {
        // It's the set geometry with the full bounding box as the fallback.
        with_states(self.0.toplevel.get_surface().unwrap(), |states| {
            states.cached_state.current::<SurfaceCachedState>().geometry
        })
        .unwrap()
        .unwrap_or_else(|| self.bbox())
    }

    /// Returns a bounding box over this window and its children.
    pub fn bbox(&self) -> Rectangle<i32, Logical> {
        if self.0.toplevel.get_surface().is_some() {
            self.0.bbox.get()
        } else {
            Rectangle::from_loc_and_size((0, 0), (0, 0))
        }
    }

    /// Returns a bounding box over this window and children including popups.
    ///
    /// Note: You need to use a [`PopupManager`] to track popups, otherwise the bounding box
    /// will not include the popups.
    pub fn bbox_with_popups(&self) -> Rectangle<i32, Logical> {
        let mut bounding_box = self.bbox();
        if let Some(surface) = self.0.toplevel.get_surface() {
            for (popup, location) in PopupManager::popups_for_surface(surface)
                .ok()
                .into_iter()
                .flatten()
            {
                if let Some(surface) = popup.get_surface() {
                    let offset = self.geometry().loc + location - popup.geometry().loc;
                    bounding_box = bounding_box.merge(bbox_from_surface_tree(surface, offset));
                }
            }
        }
        bounding_box
    }

    /// Activate/Deactivate this window
    pub fn set_activated(&self, active: bool) -> bool {
        match self.0.toplevel {
            Kind::Xdg(ref t) => t
                .with_pending_state(|state| {
                    if active {
                        state.states.set(xdg_toplevel::State::Activated)
                    } else {
                        state.states.unset(xdg_toplevel::State::Activated)
                    }
                })
                .unwrap_or(false),
            #[cfg(feature = "xwayland")]
            Kind::X11(ref _t) => false,
        }
    }

    /// Commit any changes to this window
    pub fn configure(&self) {
        match self.0.toplevel {
            Kind::Xdg(ref t) => t.send_configure(),
            #[cfg(feature = "xwayland")]
            Kind::X11(ref _t) => unimplemented!(),
        }
    }

    /// Sends the frame callback to all the subsurfaces in this
    /// window that requested it
    pub fn send_frame(&self, time: u32) {
        if let Some(surface) = self.0.toplevel.get_surface() {
            send_frames_surface_tree(surface, time);
            for (popup, _) in PopupManager::popups_for_surface(surface)
                .ok()
                .into_iter()
                .flatten()
            {
                if let Some(surface) = popup.get_surface() {
                    send_frames_surface_tree(surface, time);
                }
            }
        }
    }

    /// Updates internal values
    ///
    /// Needs to be called whenever the toplevel surface or any unsynchronized subsurfaces of this window are updated
    /// to correctly update the bounding box of this window.
    pub fn refresh(&self) {
        if let Some(surface) = self.0.toplevel.get_surface() {
            self.0.bbox.set(bbox_from_surface_tree(surface, (0, 0)));
        }
    }

    /// Finds the topmost surface under this point if any and returns it together with the location of this
    /// surface.
    ///
    /// - `point` should be relative to (0,0) of the window.
    pub fn surface_under<P: Into<Point<f64, Logical>>>(
        &self,
        point: P,
        surface_type: WindowSurfaceType,
    ) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)> {
        let point = point.into();
        if let Some(surface) = self.0.toplevel.get_surface() {
            if surface_type.contains(WindowSurfaceType::POPUP) {
                for (popup, location) in PopupManager::popups_for_surface(surface)
                    .ok()
                    .into_iter()
                    .flatten()
                {
                    let offset = self.geometry().loc + location - popup.geometry().loc;
                    if let Some(result) = popup
                        .get_surface()
                        .and_then(|surface| under_from_surface_tree(surface, point, offset, surface_type))
                    {
                        return Some(result);
                    }
                }
            }

            under_from_surface_tree(surface, point, (0, 0), surface_type)
        } else {
            None
        }
    }

    /// Damage of all the surfaces of this window.
    ///
    /// If `for_values` is `Some(_)` it will only return the damage on the
    /// first call for a given [`Space`] and [`Output`], if the buffer hasn't changed.
    /// Subsequent calls will return an empty vector until the buffer is updated again.
    pub fn accumulated_damage(&self, for_values: Option<(&Space, &Output)>) -> Vec<Rectangle<i32, Logical>> {
        let mut damage = Vec::new();
        if let Some(surface) = self.0.toplevel.get_surface() {
            damage.extend(
                damage_from_surface_tree(surface, (0, 0), for_values)
                    .into_iter()
                    .flat_map(|rect| rect.intersection(self.bbox())),
            );
        }
        damage
    }

    /// Returns the underlying toplevel
    pub fn toplevel(&self) -> &Kind {
        &self.0.toplevel
    }

    /// Returns a [`UserDataMap`] to allow associating arbitrary data with this window.
    pub fn user_data(&self) -> &UserDataMap {
        &self.0.user_data
    }
}

/// Renders a given [`Window`] using a provided renderer and frame.
///
/// - `scale` needs to be equivalent to the fractional scale the rendered result should have.
/// - `location` is the position the window should be drawn at.
/// - `damage` is the set of regions of the window that should be drawn.
///
/// Note: This function will render nothing, if you are not using
/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
/// to let smithay handle buffer management.
pub fn draw_window<R, E, F, T, P>(
    renderer: &mut R,
    frame: &mut F,
    window: &Window,
    scale: f64,
    location: P,
    damage: &[Rectangle<i32, Logical>],
    log: &slog::Logger,
) -> Result<(), R::Error>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error,
    T: Texture + 'static,
    P: Into<Point<i32, Logical>>,
{
    let location = location.into();
    if let Some(surface) = window.toplevel().get_surface() {
        draw_surface_tree(renderer, frame, surface, scale, location, damage, log)?;
        for (popup, p_location) in PopupManager::popups_for_surface(surface)
            .ok()
            .into_iter()
            .flatten()
        {
            if let Some(surface) = popup.get_surface() {
                let offset = window.geometry().loc + p_location - popup.geometry().loc;
                let damage = damage
                    .iter()
                    .cloned()
                    .map(|mut geo| {
                        geo.loc -= offset;
                        geo
                    })
                    .collect::<Vec<_>>();
                draw_surface_tree(renderer, frame, surface, scale, location + offset, &damage, log)?;
            }
        }
    }
    Ok(())
}
<commit_msg>desktop: Fix crash when accessing (dead) popups of dead windows<commit_after>use crate::{
    backend::renderer::{utils::draw_surface_tree, Frame, ImportAll, Renderer, Texture},
    desktop::{utils::*, PopupManager, Space},
    utils::{Logical, Point, Rectangle},
    wayland::{
        compositor::with_states,
        output::Output,
        shell::xdg::{SurfaceCachedState, ToplevelSurface},
    },
};
use std::{
    cell::Cell,
    hash::{Hash, Hasher},
    rc::Rc,
};
use wayland_commons::user_data::UserDataMap;
use wayland_protocols::xdg_shell::server::xdg_toplevel;
use wayland_server::protocol::wl_surface;

crate::utils::ids::id_gen!(next_window_id, WINDOW_ID, WINDOW_IDS);

/// Abstraction around different toplevel kinds
#[derive(Debug, Clone, PartialEq)]
pub enum Kind {
    /// xdg-shell [`ToplevelSurface`]
    Xdg(ToplevelSurface),
    /// XWayland surface (TODO)
    #[cfg(feature = "xwayland")]
    X11(X11Surface),
}

/// Xwayland surface
#[derive(Debug, Clone)]
#[cfg(feature = "xwayland")]
pub struct X11Surface {
    /// underlying wl_surface
    pub surface: wl_surface::WlSurface,
}

#[cfg(feature = "xwayland")]
impl std::cmp::PartialEq for X11Surface {
    fn eq(&self, other: &Self) -> bool {
        self.alive() && other.alive() && self.surface == other.surface
    }
}

#[cfg(feature = "xwayland")]
impl X11Surface {
    /// Checks if the surface is still alive.
    pub fn alive(&self) -> bool {
        self.surface.as_ref().is_alive()
    }

    /// Returns the underlying [`WlSurface`](wl_surface::WlSurface), if still any.
    pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
        if self.alive() {
            Some(&self.surface)
        } else {
            None
        }
    }
}

impl Kind {
    /// Checks if the surface is still alive.
    pub fn alive(&self) -> bool {
        match *self {
            Kind::Xdg(ref t) => t.alive(),
            #[cfg(feature = "xwayland")]
            Kind::X11(ref t) => t.alive(),
        }
    }

    /// Returns the underlying [`WlSurface`](wl_surface::WlSurface), if still any.
    pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
        match *self {
            Kind::Xdg(ref t) => t.get_surface(),
            #[cfg(feature = "xwayland")]
            Kind::X11(ref t) => t.get_surface(),
        }
    }
}

#[derive(Debug)]
pub(super) struct WindowInner {
    pub(super) id: usize,
    toplevel: Kind,
    bbox: Cell<Rectangle<i32, Logical>>,
    user_data: UserDataMap,
}

impl Drop for WindowInner {
    fn drop(&mut self) {
        WINDOW_IDS.lock().unwrap().remove(&self.id);
    }
}

/// Represents a single application window
#[derive(Debug, Clone)]
pub struct Window(pub(super) Rc<WindowInner>);

impl PartialEq for Window {
    fn eq(&self, other: &Self) -> bool {
        self.0.id == other.0.id
    }
}

impl Eq for Window {}

impl Hash for Window {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.id.hash(state);
    }
}

bitflags::bitflags! {
    /// Defines the surface types that can be
    /// queried with [`Window::surface_under`]
    pub struct WindowSurfaceType: u32 {
        /// Include the toplevel surface
        const TOPLEVEL = 1;
        /// Include all subsurfaces
        const SUBSURFACE = 2;
        /// Include all popup surfaces
        const POPUP = 4;
        /// Query all surfaces
        const ALL = Self::TOPLEVEL.bits | Self::SUBSURFACE.bits | Self::POPUP.bits;
    }
}

impl Window {
    /// Construct a new [`Window`] from a given compatible toplevel surface
    pub fn new(toplevel: Kind) -> Window {
        let id = next_window_id();

        Window(Rc::new(WindowInner {
            id,
            toplevel,
            bbox: Cell::new(Rectangle::from_loc_and_size((0, 0), (0, 0))),
            user_data: UserDataMap::new(),
        }))
    }

    /// Returns the geometry of this window.
    pub fn geometry(&self) -> Rectangle<i32, Logical> {
        let surface = match self.0.toplevel.get_surface() {
            Some(surface) => surface,
            None => return Rectangle::from_loc_and_size((0, 0), (0, 0)),
        };
        // It's the set geometry with the full bounding box as the fallback.
        with_states(surface, |states| {
            states.cached_state.current::<SurfaceCachedState>().geometry
        })
        .unwrap()
        .unwrap_or_else(|| self.bbox())
    }

    /// Returns a bounding box over this window and its children.
    pub fn bbox(&self) -> Rectangle<i32, Logical> {
        if self.0.toplevel.get_surface().is_some() {
            self.0.bbox.get()
        } else {
            Rectangle::from_loc_and_size((0, 0), (0, 0))
        }
    }

    /// Returns a bounding box over this window and children including popups.
    ///
    /// Note: You need to use a [`PopupManager`] to track popups, otherwise the bounding box
    /// will not include the popups.
    pub fn bbox_with_popups(&self) -> Rectangle<i32, Logical> {
        let mut bounding_box = self.bbox();
        if let Some(surface) = self.0.toplevel.get_surface() {
            for (popup, location) in PopupManager::popups_for_surface(surface)
                .ok()
                .into_iter()
                .flatten()
            {
                if let Some(surface) = popup.get_surface() {
                    let offset = self.geometry().loc + location - popup.geometry().loc;
                    bounding_box = bounding_box.merge(bbox_from_surface_tree(surface, offset));
                }
            }
        }
        bounding_box
    }

    /// Activate/Deactivate this window
    pub fn set_activated(&self, active: bool) -> bool {
        match self.0.toplevel {
            Kind::Xdg(ref t) => t
                .with_pending_state(|state| {
                    if active {
                        state.states.set(xdg_toplevel::State::Activated)
                    } else {
                        state.states.unset(xdg_toplevel::State::Activated)
                    }
                })
                .unwrap_or(false),
            #[cfg(feature = "xwayland")]
            Kind::X11(ref _t) => false,
        }
    }

    /// Commit any changes to this window
    pub fn configure(&self) {
        match self.0.toplevel {
            Kind::Xdg(ref t) => t.send_configure(),
            #[cfg(feature = "xwayland")]
            Kind::X11(ref _t) => unimplemented!(),
        }
    }

    /// Sends the frame callback to all the subsurfaces in this
    /// window that requested it
    pub fn send_frame(&self, time: u32) {
        if let Some(surface) = self.0.toplevel.get_surface() {
            send_frames_surface_tree(surface, time);
            for (popup, _) in PopupManager::popups_for_surface(surface)
                .ok()
                .into_iter()
                .flatten()
            {
                if let Some(surface) = popup.get_surface() {
                    send_frames_surface_tree(surface, time);
                }
            }
        }
    }

    /// Updates internal values
    ///
    /// Needs to be called whenever the toplevel surface or any unsynchronized subsurfaces of this window are updated
    /// to correctly update the bounding box of this window.
    pub fn refresh(&self) {
        if let Some(surface) = self.0.toplevel.get_surface() {
            self.0.bbox.set(bbox_from_surface_tree(surface, (0, 0)));
        }
    }

    /// Finds the topmost surface under this point if any and returns it together with the location of this
    /// surface.
    ///
    /// - `point` should be relative to (0,0) of the window.
    pub fn surface_under<P: Into<Point<f64, Logical>>>(
        &self,
        point: P,
        surface_type: WindowSurfaceType,
    ) -> Option<(wl_surface::WlSurface, Point<i32, Logical>)> {
        let point = point.into();
        if let Some(surface) = self.0.toplevel.get_surface() {
            if surface_type.contains(WindowSurfaceType::POPUP) {
                for (popup, location) in PopupManager::popups_for_surface(surface)
                    .ok()
                    .into_iter()
                    .flatten()
                {
                    let offset = self.geometry().loc + location - popup.geometry().loc;
                    if let Some(result) = popup
                        .get_surface()
                        .and_then(|surface| under_from_surface_tree(surface, point, offset, surface_type))
                    {
                        return Some(result);
                    }
                }
            }

            under_from_surface_tree(surface, point, (0, 0), surface_type)
        } else {
            None
        }
    }

    /// Damage of all the surfaces of this window.
    ///
    /// If `for_values` is `Some(_)` it will only return the damage on the
    /// first call for a given [`Space`] and [`Output`], if the buffer hasn't changed.
    /// Subsequent calls will return an empty vector until the buffer is updated again.
    pub fn accumulated_damage(&self, for_values: Option<(&Space, &Output)>) -> Vec<Rectangle<i32, Logical>> {
        let mut damage = Vec::new();
        if let Some(surface) = self.0.toplevel.get_surface() {
            damage.extend(
                damage_from_surface_tree(surface, (0, 0), for_values)
                    .into_iter()
                    .flat_map(|rect| rect.intersection(self.bbox())),
            );
        }
        damage
    }

    /// Returns the underlying toplevel
    pub fn toplevel(&self) -> &Kind {
        &self.0.toplevel
    }

    /// Returns a [`UserDataMap`] to allow associating arbitrary data with this window.
    pub fn user_data(&self) -> &UserDataMap {
        &self.0.user_data
    }
}

/// Renders a given [`Window`] using a provided renderer and frame.
///
/// - `scale` needs to be equivalent to the fractional scale the rendered result should have.
/// - `location` is the position the window should be drawn at.
/// - `damage` is the set of regions of the window that should be drawn.
///
/// Note: This function will render nothing, if you are not using
/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
/// to let smithay handle buffer management.
pub fn draw_window<R, E, F, T, P>(
    renderer: &mut R,
    frame: &mut F,
    window: &Window,
    scale: f64,
    location: P,
    damage: &[Rectangle<i32, Logical>],
    log: &slog::Logger,
) -> Result<(), R::Error>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error,
    T: Texture + 'static,
    P: Into<Point<i32, Logical>>,
{
    let location = location.into();
    if let Some(surface) = window.toplevel().get_surface() {
        draw_surface_tree(renderer, frame, surface, scale, location, damage, log)?;
        for (popup, p_location) in PopupManager::popups_for_surface(surface)
            .ok()
            .into_iter()
            .flatten()
        {
            if let Some(surface) = popup.get_surface() {
                let offset = window.geometry().loc + p_location - popup.geometry().loc;
                let damage = damage
                    .iter()
                    .cloned()
                    .map(|mut geo| {
                        geo.loc -= offset;
                        geo
                    })
                    .collect::<Vec<_>>();
                draw_surface_tree(renderer, frame, surface, scale, location + offset, &damage, log)?;
            }
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>use gui::*;
use gui::div;

use std::cell::{Ref, RefCell};
use std::time::Instant;
use std::collections::HashMap;

struct CachedDiv {
    /// Node id of the cached div.
    id: it::NodeId,
    /// Visibility of the cached div.
    visibility: div::ComputedVisibility,
    /// The parent id and bbox of the cached div.
    parent: Option<(it::NodeId, Bbox)>,
}

pub struct View<'a> {
    bbox: Bbox,
    arena: it::Arena<div::SpaceDiv<'a>>,
    root_div: it::NodeId,
    children: Vec<it::NodeId>,
    cache: RefCell<Option<Vec<CachedDiv>>>,
    time_start: Instant,
    /// The scrollbars which need to be drawn per div.
    pending_scrollbars: RefCell<HashMap<it::NodeId, (Option<Bbox>, Option<Bbox>)>>,
}

impl<'a> View<'a> {
    pub fn new(bbox: Bbox) -> Self {
        let mut arena = it::Arena::new();
        let root_div = arena.new_node(
            div::SpaceDiv::full()
                .vertical()
                .vert_align(div::Alignment::Min)
                .build(),
        );
        Self {
            bbox,
            arena,
            root_div,
            children: Vec::new(),
            cache: RefCell::new(None),
            time_start: Instant::now(),
            pending_scrollbars: RefCell::new(HashMap::new()),
        }
    }

    pub fn without_bbox() -> Self {
        let zero = Point::new(0, 0);
        Self::new(Bbox::new(zero, zero))
    }

    pub fn add_div(&mut self, parent: Option<it::NodeId>, div: div::SpaceDiv<'a>) -> it::NodeId {
        let node = self.arena.new_node(div);
        parent
            .unwrap_or(self.root_div)
            .append(node, &mut self.arena);
        self.children.push(node);
        *self.cache.borrow_mut() = None;
        node
    }

    pub fn bbox(&self) -> &Bbox {
        &self.bbox
    }

    pub fn set_bbox(&mut self, bbox: Bbox) {
        if bbox != self.bbox {
            *self.cache.borrow_mut() = None;
        }

        self.bbox = bbox;
    }

    /// Returns the root space div of this view.
    pub fn space_div(&self) -> &div::SpaceDiv<'a> {
        &self.arena[self.root_div].data
    }

    /// Draw the entire frame.
    pub fn draw(&self, frame: &nanovg::Frame) {
        self.pending_scrollbars.borrow_mut().clear();

        if let Some(ref divs) = *self.divs() {
            for cached_div in divs.iter() {
                self.draw_div(
                    &self.arena[cached_div.id].data,
                    cached_div.visibility,
                    cached_div.parent,
                    frame,
                );
            }
        }

        self.draw_scrollbars(frame);
    }

    fn draw_scrollbars(&self, frame: &nanovg::Frame) {
        let draw_bbox = |bbox: Bbox| {
            frame.path(
                |path| {
                    let origin = (bbox.min.x as f32, bbox.min.y as f32);
                    let size = (bbox.size().x as f32, bbox.size().y as f32);
                    path.rect(origin, size);
                    path.fill(nanovg::FillStyle {
                        coloring_style: nanovg::ColoringStyle::Color(
                            Color::rgba(0.0, 1.0, 0.0, 0.4).into(),
                        ),
                        ..Default::default()
                    });
                },
                Default::default(),
            );
        };

        for &(hori_bbox, vert_bbox) in self.pending_scrollbars.borrow().values() {
            if let Some(hori_bbox) = hori_bbox {
                draw_bbox(hori_bbox);
            }
            if let Some(vert_bbox) = vert_bbox {
                draw_bbox(vert_bbox);
            }
        }
    }

    /// Draw a single div.
    fn draw_div(
        &self,
        div: &div::SpaceDiv,
        div_visibility: div::ComputedVisibility,
        parent_bbox: Option<(it::NodeId, Bbox)>,
        frame: &nanovg::Frame,
    ) {
        let (div_bbox, x, y) = match div_visibility {
            div::ComputedVisibility::Invisible => return,
            div::ComputedVisibility::Visible { bbox, x, y } => (bbox, x, y),
        };

        // Build clip bbox
        let clip = {
            let mut clip = div_bbox;
            if let Some((min_val, max_val)) = x.min_max() {
                clip.min.x = min_val;
                clip.max.x = max_val;
            }
            if let Some((min_val, max_val)) = y.min_max() {
                clip.min.y = min_val;
                clip.max.y = max_val;
            }
            clip
        };

        // Draw background color if we have one
        if let Some(color) = div.background_color {
            frame.path(
                |path| {
                    let origin = (clip.min.x as f32, clip.min.y as f32);
                    let size = (clip.size().x as f32, clip.size().y as f32);
                    path.rect(origin, size);
                    path.fill(nanovg::FillStyle {
                        coloring_style: nanovg::ColoringStyle::Color(color.into()),
                        ..Default::default()
                    });
                },
                Default::default(),
            );
        }

        // Generate scrollbars
        if let Some((parent, parent_bbox)) = parent_bbox {
            if let Some(sx) = x.scroll() {
                let width = max(parent_bbox.size().x - sx, 8);
                let height = 16;
                let origin = Point::new(
                    parent_bbox.min.x + div.scroll.get().x,
                    parent_bbox.max.y - height,
                );
                let size = Point::new(width, height);
                let bbox = Bbox::with_size(origin, size);
                let mut bars = self.pending_scrollbars.borrow_mut();
                bars.entry(parent).or_insert((None, None)).0 = Some(bbox);
            }
            if let Some(sy) = y.scroll() {
                let width = 16;
                let height = max(parent_bbox.size().y - sy, 8);
                let origin = Point::new(
                    parent_bbox.max.x - width,
                    parent_bbox.min.y + div.scroll.get().y,
                );
                let size = Point::new(width, height);
                let bbox = Bbox::with_size(origin, size);
                let mut bars = self.pending_scrollbars.borrow_mut();
                bars.entry(parent).or_insert((None, None)).1 = Some(bbox);
            }
        }

        let scrolled_div_bbox = {
            let mut bbox = div_bbox;
            let scroll = div.scroll.get();
            if let div::AxisOverflowBehaviour::Scroll { .. } = x {
                bbox.min.x -= scroll.x;
                bbox.max.x -= scroll.x;
            }
            if let div::AxisOverflowBehaviour::Scroll { .. } = y {
                bbox.min.y -= scroll.y;
                bbox.max.y -= scroll.y;
            }
            bbox
        };

        // Draw the widget
        if let Some(ref widget) = div.widget {
            let mut widget = widget.borrow_mut();
            widget.update();
            widget.draw(scrolled_div_bbox, clip, frame);
        }

        let max_scroll = {
            let x = x.scroll().unwrap_or(0);
            let y = y.scroll().unwrap_or(0);
            Point::new(x, y)
        };

        // Scroll right 1 pixel per frame for testing the scrolling.
        let delta = self.time_start.elapsed();
        let delta = (delta.as_secs() * 1000 + delta.subsec_millis() as u64) as f32 / 1000.0;
        let delta = ((delta.sin() * 0.5 + 0.5) * max_scroll.x as f32) as i32;
        div.scroll.set(point_min(Point::new(delta, 0), max_scroll));
    }

    /// Recursively visit all space divs of this view.
    fn visit_divs<F>(
        &self,
        id: it::NodeId,
        visibility: div::ComputedVisibility,
        parent: Option<(it::NodeId, Bbox)>,
        visitor: &mut F,
    ) where
        F: FnMut(CachedDiv),
    {
        if let div::ComputedVisibility::Visible { bbox, .. } = visibility {
            visitor(CachedDiv {
                id,
                visibility,
                parent,
            });

            let visible_children = self.arena[id].data.children(&self.arena, id, bbox);

            for (c, vis) in visible_children {
                self.visit_divs(c, vis, Some((id, bbox)), visitor);
            }
        }
    }

    fn divs(&self) -> Ref<Option<Vec<CachedDiv>>> {
        let is_none = { self.cache.borrow().is_none() };
        if is_none {
            // Build the cache
            let mut vec = self.cache.borrow_mut();
            let vec = vec.get_or_insert_with(|| Vec::with_capacity(32));
            vec.clear();
            self.visit_divs(
                self.root_div,
                div::ComputedVisibility::Visible {
                    bbox: self.bbox,
                    x: div::AxisOverflowBehaviour::Overflow,
                    y: div::AxisOverflowBehaviour::Overflow,
                },
                None,
                &mut |cached_div| vec.push(cached_div),
            );
        }
        self.cache.borrow()
    }
}
<commit_msg>Remove semantically incorrect variable<commit_after>use gui::*;
use gui::div;

use std::cell::{Ref, RefCell};
use std::time::Instant;
use std::collections::HashMap;

struct CachedDiv {
    /// Node id of the cached div.
    id: it::NodeId,
    /// Visibility of the cached div.
    visibility: div::ComputedVisibility,
    /// The parent id and bbox of the cached div.
    parent: Option<(it::NodeId, Bbox)>,
}

pub struct View<'a> {
    bbox: Bbox,
    arena: it::Arena<div::SpaceDiv<'a>>,
    root_div: it::NodeId,
    children: Vec<it::NodeId>,
    cache: RefCell<Option<Vec<CachedDiv>>>,
    time_start: Instant,
    /// The scrollbars which need to be drawn per div.
    pending_scrollbars: RefCell<HashMap<it::NodeId, (Option<Bbox>, Option<Bbox>)>>,
}

impl<'a> View<'a> {
    pub fn new(bbox: Bbox) -> Self {
        let mut arena = it::Arena::new();
        let root_div = arena.new_node(
            div::SpaceDiv::full()
                .vertical()
                .vert_align(div::Alignment::Min)
                .build(),
        );
        Self {
            bbox,
            arena,
            root_div,
            children: Vec::new(),
            cache: RefCell::new(None),
            time_start: Instant::now(),
            pending_scrollbars: RefCell::new(HashMap::new()),
        }
    }

    pub fn without_bbox() -> Self {
        let zero = Point::new(0, 0);
        Self::new(Bbox::new(zero, zero))
    }

    pub fn add_div(&mut self, parent: Option<it::NodeId>, div: div::SpaceDiv<'a>) -> it::NodeId {
        let node = self.arena.new_node(div);
        parent
            .unwrap_or(self.root_div)
            .append(node, &mut self.arena);
        self.children.push(node);
        *self.cache.borrow_mut() = None;
        node
    }

    pub fn bbox(&self) -> &Bbox {
        &self.bbox
    }

    pub fn set_bbox(&mut self, bbox: Bbox) {
        if bbox != self.bbox {
            *self.cache.borrow_mut() = None;
        }

        self.bbox = bbox;
    }

    /// Returns the root space div of this view.
    pub fn space_div(&self) -> &div::SpaceDiv<'a> {
        &self.arena[self.root_div].data
    }

    /// Draw the entire frame.
    pub fn draw(&self, frame: &nanovg::Frame) {
        self.pending_scrollbars.borrow_mut().clear();

        if let Some(ref divs) = *self.divs() {
            for cached_div in divs.iter() {
                self.draw_div(
                    &self.arena[cached_div.id].data,
                    cached_div.visibility,
                    cached_div.parent,
                    frame,
                );
            }
        }

        self.draw_scrollbars(frame);
    }

    fn draw_scrollbars(&self, frame: &nanovg::Frame) {
        let draw_bbox = |bbox: Bbox| {
            frame.path(
                |path| {
                    let origin = (bbox.min.x as f32, bbox.min.y as f32);
                    let size = (bbox.size().x as f32, bbox.size().y as f32);
                    path.rect(origin, size);
                    path.fill(nanovg::FillStyle {
                        coloring_style: nanovg::ColoringStyle::Color(
                            Color::rgba(0.0, 1.0, 0.0, 0.4).into(),
                        ),
                        ..Default::default()
                    });
                },
                Default::default(),
            );
        };

        for &(hori_bbox, vert_bbox) in self.pending_scrollbars.borrow().values() {
            if let Some(hori_bbox) = hori_bbox {
                draw_bbox(hori_bbox);
            }
            if let Some(vert_bbox) = vert_bbox {
                draw_bbox(vert_bbox);
            }
        }
    }

    /// Draw a single div.
    fn draw_div(
        &self,
        div: &div::SpaceDiv,
        div_visibility: div::ComputedVisibility,
        parent_bbox: Option<(it::NodeId, Bbox)>,
        frame: &nanovg::Frame,
    ) {
        let (div_bbox, x, y) = match div_visibility {
            div::ComputedVisibility::Invisible => return,
            div::ComputedVisibility::Visible { bbox, x, y } => (bbox, x, y),
        };

        // Build clip bbox
        let clip = {
            let mut clip = div_bbox;
            if let Some((min_val, max_val)) = x.min_max() {
                clip.min.x = min_val;
                clip.max.x = max_val;
            }
            if let Some((min_val, max_val)) = y.min_max() {
                clip.min.y = min_val;
                clip.max.y = max_val;
            }
            clip
        };

        // Draw background color if we have one
        if let Some(color) = div.background_color {
            frame.path(
                |path| {
                    let origin = (clip.min.x as f32, clip.min.y as f32);
                    let size = (clip.size().x as f32, clip.size().y as f32);
                    path.rect(origin, size);
                    path.fill(nanovg::FillStyle {
                        coloring_style: nanovg::ColoringStyle::Color(color.into()),
                        ..Default::default()
                    });
                },
                Default::default(),
            );
        }

        // Generate scrollbars
        if let Some((parent, parent_bbox)) = parent_bbox {
            if let Some(sx) = x.scroll() {
                let width = max(parent_bbox.size().x - sx, 8);
                let height = 16;
                let origin = Point::new(
                    parent_bbox.min.x + div.scroll.get().x,
                    parent_bbox.max.y - height,
                );
                let size = Point::new(width, height);
                let bbox = Bbox::with_size(origin, size);
                let mut bars = self.pending_scrollbars.borrow_mut();
                bars.entry(parent).or_insert((None, None)).0 = Some(bbox);
            }
            if let Some(sy) = y.scroll() {
                let width = 16;
                let height = max(parent_bbox.size().y - sy, 8);
                let origin = Point::new(
                    parent_bbox.max.x - width,
                    parent_bbox.min.y + div.scroll.get().y,
                );
                let size = Point::new(width, height);
                let bbox = Bbox::with_size(origin, size);
                let mut bars = self.pending_scrollbars.borrow_mut();
                bars.entry(parent).or_insert((None, None)).1 = Some(bbox);
            }
        }

        let scrolled_div_bbox = {
            let mut bbox = div_bbox;
            let scroll = div.scroll.get();
            if let div::AxisOverflowBehaviour::Scroll { .. } = x {
                bbox.min.x -= scroll.x;
                bbox.max.x -= scroll.x;
            }
            if let div::AxisOverflowBehaviour::Scroll { .. } = y {
                bbox.min.y -= scroll.y;
                bbox.max.y -= scroll.y;
            }
            bbox
        };

        // Draw the widget
        if let Some(ref widget) = div.widget {
            let mut widget = widget.borrow_mut();
            widget.update();
            widget.draw(scrolled_div_bbox, clip, frame);
        }

        let max_scroll = {
            let x = x.scroll().unwrap_or(0);
            let y = y.scroll().unwrap_or(0);
            Point::new(x, y)
        };

        // Scroll right 1 pixel per frame for testing the scrolling.
        let delta = self.time_start.elapsed();
        let delta = (delta.as_secs() * 1000 + delta.subsec_millis() as u64) as f32 / 1000.0;
        let delta = ((delta.sin() * 0.5 + 0.5) * max_scroll.x as f32) as i32;
        div.scroll.set(point_min(Point::new(delta, 0), max_scroll));
    }

    /// Recursively visit all space divs of this view.
    fn visit_divs<F>(
        &self,
        id: it::NodeId,
        visibility: div::ComputedVisibility,
        parent: Option<(it::NodeId, Bbox)>,
        visitor: &mut F,
    ) where
        F: FnMut(CachedDiv),
    {
        if let div::ComputedVisibility::Visible { bbox, .. } = visibility {
            visitor(CachedDiv {
                id,
                visibility,
                parent,
            });

            for (c, vis) in self.arena[id].data.children(&self.arena, id, bbox) {
                self.visit_divs(c, vis, Some((id, bbox)), visitor);
            }
        }
    }

    fn divs(&self) -> Ref<Option<Vec<CachedDiv>>> {
        let is_none = { self.cache.borrow().is_none() };
        if is_none {
            // Build the cache
            let mut vec = self.cache.borrow_mut();
            let vec = vec.get_or_insert_with(|| Vec::with_capacity(32));
            vec.clear();
            self.visit_divs(
                self.root_div,
                div::ComputedVisibility::Visible {
                    bbox: self.bbox,
                    x: div::AxisOverflowBehaviour::Overflow,
                    y: div::AxisOverflowBehaviour::Overflow,
                },
                None,
                &mut |cached_div| vec.push(cached_div),
            );
        }
        self.cache.borrow()
    }
}
<|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/. */

use WindowWrapper;
use base64;
use gleam::gl;
use image::ColorType;
use image::png::PNGEncoder;
use parse_function::parse_function;
use std::cmp;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::sync::mpsc::{channel, Receiver, Sender};
use webrender_traits::*;
use wrench::{Wrench, WrenchThing};
use yaml_frame_reader::YamlFrameReader;

pub enum ReftestOp {
    Equal,
    NotEqual,
}
pub struct Reftest {
    op: ReftestOp,
    test: PathBuf,
    reference: PathBuf,
    max_difference: usize,
    num_differences: usize,
}

struct ReftestImage {
    data: Vec<u8>,
    size: DeviceUintSize,
}
enum ReftestImageComparison {
    Equal,
    NotEqual { max_difference: usize, count_different: usize },
}

impl ReftestImage {
    fn compare(&self, other: &ReftestImage) -> ReftestImageComparison {
        assert!(self.size == other.size);
        assert!(self.data.len() == other.data.len());
        assert!(self.data.len() % 4 == 0);

        let mut count = 0;
        let mut max = 0;

        for (a, b) in self.data.chunks(4).zip(other.data.chunks(4)) {
            if a != b {
                let pixel_max = a.iter()
                                 .zip(b.iter())
                                 .map(|(x, y)| (*x as isize - *y as isize).abs() as usize)
                                 .max().unwrap();

                count += 1;
                max = cmp::max(max, pixel_max);
            }
        }

        if count != 0 {
            ReftestImageComparison::NotEqual {
                max_difference: max,
                count_different: count,
            }
        } else {
            ReftestImageComparison::Equal
        }
    }

    fn create_data_uri(mut self) -> String {
        let width = self.size.width;
        let height = self.size.height;

        // flip image vertically (texture is upside down)
        let orig_pixels = self.data.clone();
        let stride = width as usize * 4;
        for y in 0..height as usize {
            let dst_start = y * stride;
            let src_start = (height as usize - y - 1) * stride;
            let src_slice = &orig_pixels[src_start .. src_start + stride];
            (&mut self.data[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]);
        }

        let mut png: Vec<u8> = vec![];
        {
            let encoder = PNGEncoder::new(&mut png);
            encoder.encode(&self.data[..],
                            width,
                            height,
                            ColorType::RGBA(8)).expect("Unable to encode PNG!");
        }
        let png_base64 = base64::encode(&png);
        format!("data:image/png;base64,{}", png_base64)
    }
}

struct ReftestManifest {
    reftests: Vec<Reftest>,
}
impl ReftestManifest {
    fn new(manifest: &Path) -> ReftestManifest {
        let dir = manifest.parent().unwrap();
        let f = File::open(manifest).expect(&format!("couldn't open manifest: {}", manifest.display()));
        let file = BufReader::new(&f);

        let mut reftests = Vec::new();

        for line in file.lines() {
            let l = line.unwrap();

            // strip the comments
            let s = &l[0..l.find("#").unwrap_or(l.len())];
            let s = s.trim();
            if s.len() == 0 {
                continue;
            }

            let items: Vec<&str> = s.split_whitespace().collect();

            match items[0] {
                "include" => {
                    let include = dir.join(items[1]);

                    reftests.append(&mut ReftestManifest::new(include.as_path()).reftests);
                }
                item_str => {
                    // If the first item is "fuzzy(<val>,<count>)" the positions of the operator
                    // and paths in the array are offset.
                    // TODO: This is simple but not great because it does not support having spaces
                    // in the fuzzy syntax, like between the arguments.
                    let (max, count, offset) =  if item_str.starts_with("fuzzy(") {
                        let (_, args) = parse_function(item_str);
                        (args[0].parse().unwrap(),  args[1].parse().unwrap(), 1)
                    } else {
                        (0, 0, 0)
                    };
                    reftests.push(Reftest {
                        op: parse_operator(items[offset]).expect("unexpected match operator"),
                        test: dir.join(items[offset + 1]),
                        reference: dir.join(items[offset + 2]),
                        max_difference: max,
                        num_differences: count,
                    });
                }
            };
        }

        ReftestManifest {
            reftests: reftests
        }
    }

    fn find(&self, prefix: &Path) -> Vec<&Reftest> {
        self.reftests.iter().filter(|x| {
            x.test.starts_with(prefix) || x.reference.starts_with(prefix)
        }).collect()
    }
}

fn parse_operator(op_str: &str) -> Option<ReftestOp> {
    match op_str {
        "==" => Some(ReftestOp::Equal),
        "!=" => Some(ReftestOp::NotEqual),
        _ => None,
    }
}

pub struct ReftestHarness<'a> {
    wrench: &'a mut Wrench,
    window: &'a mut WindowWrapper,
    rx: Receiver<()>,
}
impl<'a> ReftestHarness<'a> {
    pub fn new(wrench: &'a mut Wrench,
               window: &'a mut WindowWrapper) -> ReftestHarness<'a>
    {
        // setup a notifier so we can wait for frames to be finished
        struct Notifier {
            tx: Sender<()>,
        };
        impl RenderNotifier for Notifier {
            fn new_frame_ready(&mut self) {
                self.tx.send(()).unwrap();
            }
            fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {}
        }
        let (tx, rx) = channel();
        wrench.renderer.set_render_notifier(Box::new(Notifier { tx: tx }));

        ReftestHarness {
            wrench: wrench,
            window: window,
            rx: rx,
        }
    }

    pub fn run(mut self, base_manifest: &Path, reftests: Option<&Path>) {
        let manifest = ReftestManifest::new(base_manifest);
        let reftests = manifest.find(reftests.unwrap_or(&PathBuf::new()));

        let mut total_passing = 0;
        let mut total_failing = 0;

        for t in reftests {
            if self.run_reftest(&t) {
                total_passing += 1;
            } else {
                total_failing += 1;
            }
        }

        println!("REFTEST INFO | {} passing, {} failing", total_passing, total_failing);

        if total_failing > 0 {
            // panic here so that we fail CI
            panic!();
        }
    }

    fn run_reftest(&mut self, t: &Reftest) -> bool {
        let name = match t.op {
            ReftestOp::Equal => format!("{} == {}", t.test.display(), t.reference.display()),
            ReftestOp::NotEqual => format!("{} != {}", t.test.display(), t.reference.display()),
        };

        println!("REFTEST {}", name);

        let test = self.render_yaml(t.test.as_path());
        let reference = self.render_yaml(t.reference.as_path());
        let comparison = test.compare(&reference);

        match (&t.op, comparison) {
            (&ReftestOp::Equal, ReftestImageComparison::Equal) => true,
            (&ReftestOp::Equal,
              ReftestImageComparison::NotEqual { max_difference, count_different }) => {
                if max_difference > t.max_difference || count_different > t.num_differences {
                    println!("{} | {} | {}: {}, {}: {}",
                             "REFTEST TEST-UNEXPECTED-FAIL", name,
                             "image comparison, max difference", max_difference,
                             "number of differing pixels", count_different);
                    println!("REFTEST   IMAGE 1 (TEST): {}", test.create_data_uri());
                    println!("REFTEST   IMAGE 2 (REFERENCE): {}", reference.create_data_uri());
                    println!("REFTEST TEST-END | {}", name);

                    false
                } else {
                    true
                }
            },
            (&ReftestOp::NotEqual, ReftestImageComparison::Equal) => {
                println!("REFTEST TEST-UNEXPECTED-FAIL | {} | image comparison", name);
                println!("REFTEST TEST-END | {}", name);

                false
            },
            (&ReftestOp::NotEqual, ReftestImageComparison::NotEqual { .. }) => true,
        }
    }

    fn render_yaml(&mut self, filename: &Path) -> ReftestImage {
        let mut reader = YamlFrameReader::new(filename);
        reader.do_frame(self.wrench);

        // wait for the frame
        self.rx.recv().unwrap();
        self.wrench.render();

        let size = self.window.get_inner_size_pixels();
        let pixels = self.window.gl().read_pixels(0,
                                                  0,
                                                  size.0 as gl::GLsizei,
                                                  size.1 as gl::GLsizei,
                                                  gl::RGBA,
                                                  gl::UNSIGNED_BYTE);
        self.window.swap_buffers();

        ReftestImage {
            data: pixels,
            size: DeviceUintSize::new(size.0, size.1)
        }
    }
}
<commit_msg>Image refrests logic for PNG<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/. */

use WindowWrapper;
use base64;
use gleam::gl;
use image::{load as load_piston_image, ColorType, ImageFormat};
use image::png::PNGEncoder;
use parse_function::parse_function;
use std::cmp;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::sync::mpsc::{channel, Receiver, Sender};
use webrender_traits::*;
use wrench::{Wrench, WrenchThing};
use yaml_frame_reader::YamlFrameReader;

pub enum ReftestOp {
    Equal,
    NotEqual,
}
pub struct Reftest {
    op: ReftestOp,
    test: PathBuf,
    reference: PathBuf,
    max_difference: usize,
    num_differences: usize,
}

struct ReftestImage {
    data: Vec<u8>,
    size: DeviceUintSize,
}
enum ReftestImageComparison {
    Equal,
    NotEqual { max_difference: usize, count_different: usize },
}

impl ReftestImage {
    fn compare(&self, other: &ReftestImage) -> ReftestImageComparison {
        assert!(self.size == other.size);
        assert!(self.data.len() == other.data.len());
        assert!(self.data.len() % 4 == 0);

        let mut count = 0;
        let mut max = 0;

        for (a, b) in self.data.chunks(4).zip(other.data.chunks(4)) {
            if a != b {
                let pixel_max = a.iter()
                                 .zip(b.iter())
                                 .map(|(x, y)| (*x as isize - *y as isize).abs() as usize)
                                 .max().unwrap();

                count += 1;
                max = cmp::max(max, pixel_max);
            }
        }

        if count != 0 {
            ReftestImageComparison::NotEqual {
                max_difference: max,
                count_different: count,
            }
        } else {
            ReftestImageComparison::Equal
        }
    }

    fn create_data_uri(mut self) -> String {
        let width = self.size.width;
        let height = self.size.height;

        // flip image vertically (texture is upside down)
        let orig_pixels = self.data.clone();
        let stride = width as usize * 4;
        for y in 0..height as usize {
            let dst_start = y * stride;
            let src_start = (height as usize - y - 1) * stride;
            let src_slice = &orig_pixels[src_start .. src_start + stride];
            (&mut self.data[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]);
        }

        let mut png: Vec<u8> = vec![];
        {
            let encoder = PNGEncoder::new(&mut png);
            encoder.encode(&self.data[..],
                            width,
                            height,
                            ColorType::RGBA(8)).expect("Unable to encode PNG!");
        }
        let png_base64 = base64::encode(&png);
        format!("data:image/png;base64,{}", png_base64)
    }
}

struct ReftestManifest {
    reftests: Vec<Reftest>,
}
impl ReftestManifest {
    fn new(manifest: &Path) -> ReftestManifest {
        let dir = manifest.parent().unwrap();
        let f = File::open(manifest).expect(&format!("couldn't open manifest: {}", manifest.display()));
        let file = BufReader::new(&f);

        let mut reftests = Vec::new();

        for line in file.lines() {
            let l = line.unwrap();

            // strip the comments
            let s = &l[0..l.find("#").unwrap_or(l.len())];
            let s = s.trim();
            if s.len() == 0 {
                continue;
            }

            let items: Vec<&str> = s.split_whitespace().collect();

            match items[0] {
                "include" => {
                    let include = dir.join(items[1]);

                    reftests.append(&mut ReftestManifest::new(include.as_path()).reftests);
                }
                item_str => {
                    // If the first item is "fuzzy(<val>,<count>)" the positions of the operator
                    // and paths in the array are offset.
                    // TODO: This is simple but not great because it does not support having spaces
                    // in the fuzzy syntax, like between the arguments.
                    let (max, count, offset) =  if item_str.starts_with("fuzzy(") {
                        let (_, args) = parse_function(item_str);
                        (args[0].parse().unwrap(),  args[1].parse().unwrap(), 1)
                    } else {
                        (0, 0, 0)
                    };
                    reftests.push(Reftest {
                        op: parse_operator(items[offset]).expect("unexpected match operator"),
                        test: dir.join(items[offset + 1]),
                        reference: dir.join(items[offset + 2]),
                        max_difference: max,
                        num_differences: count,
                    });
                }
            };
        }

        ReftestManifest {
            reftests: reftests
        }
    }

    fn find(&self, prefix: &Path) -> Vec<&Reftest> {
        self.reftests.iter().filter(|x| {
            x.test.starts_with(prefix) || x.reference.starts_with(prefix)
        }).collect()
    }
}

fn parse_operator(op_str: &str) -> Option<ReftestOp> {
    match op_str {
        "==" => Some(ReftestOp::Equal),
        "!=" => Some(ReftestOp::NotEqual),
        _ => None,
    }
}

pub struct ReftestHarness<'a> {
    wrench: &'a mut Wrench,
    window: &'a mut WindowWrapper,
    rx: Receiver<()>,
}
impl<'a> ReftestHarness<'a> {
    pub fn new(wrench: &'a mut Wrench,
               window: &'a mut WindowWrapper) -> ReftestHarness<'a>
    {
        // setup a notifier so we can wait for frames to be finished
        struct Notifier {
            tx: Sender<()>,
        };
        impl RenderNotifier for Notifier {
            fn new_frame_ready(&mut self) {
                self.tx.send(()).unwrap();
            }
            fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {}
        }
        let (tx, rx) = channel();
        wrench.renderer.set_render_notifier(Box::new(Notifier { tx: tx }));

        ReftestHarness {
            wrench: wrench,
            window: window,
            rx: rx,
        }
    }

    pub fn run(mut self, base_manifest: &Path, reftests: Option<&Path>) {
        let manifest = ReftestManifest::new(base_manifest);
        let reftests = manifest.find(reftests.unwrap_or(&PathBuf::new()));

        let mut total_passing = 0;
        let mut total_failing = 0;

        for t in reftests {
            if self.run_reftest(&t) {
                total_passing += 1;
            } else {
                total_failing += 1;
            }
        }

        println!("REFTEST INFO | {} passing, {} failing", total_passing, total_failing);

        if total_failing > 0 {
            // panic here so that we fail CI
            panic!();
        }
    }

    fn run_reftest(&mut self, t: &Reftest) -> bool {
        let name = match t.op {
            ReftestOp::Equal => format!("{} == {}", t.test.display(), t.reference.display()),
            ReftestOp::NotEqual => format!("{} != {}", t.test.display(), t.reference.display()),
        };

        println!("REFTEST {}", name);

        let test = self.render_yaml(t.test.as_path());
        let reference = if t.reference.ends_with(".png") {
            self.load_image(t.reference.as_path())
        } else {
            self.render_yaml(t.reference.as_path())
        };
        let comparison = test.compare(&reference);

        match (&t.op, comparison) {
            (&ReftestOp::Equal, ReftestImageComparison::Equal) => true,
            (&ReftestOp::Equal,
              ReftestImageComparison::NotEqual { max_difference, count_different }) => {
                if max_difference > t.max_difference || count_different > t.num_differences {
                    println!("{} | {} | {}: {}, {}: {}",
                             "REFTEST TEST-UNEXPECTED-FAIL", name,
                             "image comparison, max difference", max_difference,
                             "number of differing pixels", count_different);
                    println!("REFTEST   IMAGE 1 (TEST): {}", test.create_data_uri());
                    println!("REFTEST   IMAGE 2 (REFERENCE): {}", reference.create_data_uri());
                    println!("REFTEST TEST-END | {}", name);

                    false
                } else {
                    true
                }
            },
            (&ReftestOp::NotEqual, ReftestImageComparison::Equal) => {
                println!("REFTEST TEST-UNEXPECTED-FAIL | {} | image comparison", name);
                println!("REFTEST TEST-END | {}", name);

                false
            },
            (&ReftestOp::NotEqual, ReftestImageComparison::NotEqual { .. }) => true,
        }
    }

    fn load_image(&mut self, filename: &Path) -> ReftestImage {
        let file = BufReader::new(File::open(filename).unwrap());
        let img = load_piston_image(file, ImageFormat::PNG).unwrap().to_rgba();
        let size = img.dimensions();
        ReftestImage {
            data: img.into_raw(),
            size: DeviceUintSize::new(size.0, size.1)
        }
    }

    fn render_yaml(&mut self, filename: &Path) -> ReftestImage {
        let mut reader = YamlFrameReader::new(filename);
        reader.do_frame(self.wrench);

        // wait for the frame
        self.rx.recv().unwrap();
        self.wrench.render();

        let size = self.window.get_inner_size_pixels();
        let pixels = self.window.gl().read_pixels(0,
                                                  0,
                                                  size.0 as gl::GLsizei,
                                                  size.1 as gl::GLsizei,
                                                  gl::RGBA,
                                                  gl::UNSIGNED_BYTE);
        self.window.swap_buffers();

        ReftestImage {
            data: pixels,
            size: DeviceUintSize::new(size.0, size.1)
        }
    }
}
<|endoftext|>


<commit_before>// The code below is based on the fallback `memrchr` implementation from the respective crate.
//
// We use this mainly to skip repeated `/`. If there is only one slash, `memrnchr` performs the same
// as a naive version (e.g. `rposition`). However, it is much faster in pathological cases.

const LO_U64: u64 = 0x0101010101010101;
const HI_U64: u64 = 0x8080808080808080;

// use truncation
const LO_USIZE: usize = LO_U64 as usize;
const HI_USIZE: usize = HI_U64 as usize;

#[cfg(target_pointer_width = "32")]
const USIZE_BYTES: usize = 4;
#[cfg(target_pointer_width = "64")]
const USIZE_BYTES: usize = 8;

// Returns the byte offset of the last byte that is NOT equal to the given one.
#[inline(always)]
pub fn memrnchr(x: u8, text: &[u8]) -> Option<usize> {
  // Scan for a single byte value by reading two `usize` words at a time.
  //
  // Split `text` in three parts
  // - unaligned tail, after the last word aligned address in text
  // - body, scan by 2 words at a time
  // - the first remaining bytes, < 2 word size
  let len = text.len();
  let ptr = text.as_ptr();

  // search to an aligned boundary
  let end_align = (ptr as usize + len) & (USIZE_BYTES - 1);
  let mut offset;
  if end_align > 0 {
    offset = if end_align >= len { 0 } else { len - end_align };
    let pos = text[offset..].iter().rposition(|elt| *elt != x);
    if let Some(index) = pos {
      return Some(offset + index);
    }
  } else {
    offset = len;
  }

  // search the body of the text
  let repeated_x = repeat_byte(x);

  while offset >= 2 * USIZE_BYTES {
    debug_assert_eq!((ptr as usize + offset) % USIZE_BYTES, 0);
    unsafe {
      let u = *(ptr.offset(offset as isize - 2 * USIZE_BYTES as isize) as *const usize);
      let v = *(ptr.offset(offset as isize - USIZE_BYTES as isize) as *const usize);

      // break if there is a matching byte
      let zu = contains_zero_byte(u ^ repeated_x);
      let zv = contains_zero_byte(v ^ repeated_x);
      if !zu || !zv {
        break;
      }
    }
    offset -= 2 * USIZE_BYTES;
  }

  // find the byte before the point the body loop stopped
  text[..offset].iter().rposition(|elt| *elt != x)
}

/// Return `true` if `x` contains any zero byte.
///
/// From *Matters Computational*, J. Arndt
///
/// "The idea is to subtract one from each of the bytes and then look for
/// bytes where the borrow propagated all the way to the most significant
/// bit."
#[inline]
fn contains_zero_byte(x: usize) -> bool {
  x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0
}

#[cfg(target_pointer_width = "32")]
#[inline]
fn repeat_byte(b: u8) -> usize {
  let mut rep = (b as usize) << 8 | b as usize;
  rep = rep << 16 | rep;
  rep
}

#[cfg(target_pointer_width = "64")]
#[inline]
fn repeat_byte(b: u8) -> usize {
  let mut rep = (b as usize) << 8 | b as usize;
  rep = rep << 16 | rep;
  rep = rep << 32 | rep;
  rep
}
<commit_msg>Fix a bug in memrnchr<commit_after>// The code below is based on the fallback `memrchr` implementation from the respective crate.
//
// We use this mainly to skip repeated `/`. If there is only one slash, `memrnchr` performs the same
// as a naive version (e.g. `rposition`). However, it is much faster in pathological cases.

#[cfg(target_pointer_width = "32")]
const USIZE_BYTES: usize = 4;
#[cfg(target_pointer_width = "64")]
const USIZE_BYTES: usize = 8;

// Returns the byte offset of the last byte that is NOT equal to the given one.
#[inline(always)]
pub fn memrnchr(x: u8, text: &[u8]) -> Option<usize> {
  // Scan for a single byte value by reading two `usize` words at a time.
  //
  // Split `text` in three parts
  // - unaligned tail, after the last word aligned address in text
  // - body, scan by 2 words at a time
  // - the first remaining bytes, < 2 word size
  let len = text.len();
  let ptr = text.as_ptr();

  // search to an aligned boundary
  let end_align = (ptr as usize + len) & (USIZE_BYTES - 1);
  let mut offset;
  if end_align > 0 {
    offset = if end_align >= len { 0 } else { len - end_align };
    let pos = text[offset..].iter().rposition(|elt| *elt != x);
    if let Some(index) = pos {
      return Some(offset + index);
    }
  } else {
    offset = len;
  }

  // search the body of the text
  let repeated_x = repeat_byte(x);

  while offset >= 2 * USIZE_BYTES {
    debug_assert_eq!((ptr as usize + offset) % USIZE_BYTES, 0);
    unsafe {
      let u = *(ptr.offset(offset as isize - 2 * USIZE_BYTES as isize) as *const usize);
      let v = *(ptr.offset(offset as isize - USIZE_BYTES as isize) as *const usize);

      // break if there is no matching byte
      if u & repeated_x != usize::max_value() || v & repeated_x != usize::max_value() {
        break;
      }
    }
    offset -= 2 * USIZE_BYTES;
  }

  // find the byte before the point the body loop stopped
  text[..offset].iter().rposition(|elt| *elt != x)
}

#[cfg(target_pointer_width = "32")]
#[inline]
fn repeat_byte(b: u8) -> usize {
  let mut rep = (b as usize) << 8 | b as usize;
  rep = rep << 16 | rep;
  rep
}

#[cfg(target_pointer_width = "64")]
#[inline]
fn repeat_byte(b: u8) -> usize {
  let mut rep = (b as usize) << 8 | b as usize;
  rep = rep << 16 | rep;
  rep = rep << 32 | rep;
  rep
}
<|endoftext|>