<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

//! Implementation of cross-process surfaces for Linux. This uses X pixmaps.

use platform::surface::NativeSurfaceMethods;
use texturegl::Texture;

use geom::size::Size2D;
use libc::{c_int, c_uint, c_void};
use opengles::glx::{GLXFBConfig, GLXDrawable};
use opengles::glx::{GLX_BIND_TO_TEXTURE_RGBA_EXT};
use opengles::glx::{GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT};
use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT};
use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXCreatePixmap, glXDestroyPixmap};
use opengles::glx::{glXGetProcAddress, glXChooseFBConfig};
use opengles::glx::{glXGetVisualFromFBConfig};
use opengles::glx::{GLX_RGBA_BIT, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_DOUBLEBUFFER};
use opengles::gl2::NO_ERROR;
use opengles::gl2;
use std::cast;
use std::c_str::CString;
use std::ptr;
use xlib::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen};
use xlib::xlib::{XDisplayString, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow};
use xlib::xlib::{XVisualInfo, ZPixmap};

/// The display and visual info. This is needed in order to upload on the painting side. This
/// holds a weak reference to the display and will not close it when done.
///
/// FIXME(pcwalton): Mark nonsendable and noncopyable.
pub struct NativePaintingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
}

impl NativePaintingGraphicsContext {
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
        // visual.
        let (compositor_visual_info, _) =
            NativeCompositingGraphicsContext::compositor_visual_info(metadata.display);

        NativePaintingGraphicsContext {
            display: metadata.display,
            visual_info: compositor_visual_info,
        }
    }
}

/// The display, visual info, and framebuffer configuration. This is needed in order to bind to a
/// texture on the compositor side. This holds only a *weak* reference to the display and does not
/// close it.
///
/// FIXME(pcwalton): Unchecked weak references are bad and can violate memory safety. This is hard
/// to fix because the Display is given to us by the native windowing system, but we should fix it
/// someday.
///
/// FIXME(pcwalton): Mark nonsendable.
pub struct NativeCompositingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
    framebuffer_configuration: Option<GLXFBConfig>,
}

impl NativeCompositingGraphicsContext {
    /// Chooses the compositor visual info using the same algorithm that the compositor uses.
    ///
    /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual.
    fn compositor_visual_info(display: *Display) -> (*XVisualInfo, Option<GLXFBConfig>) {
        unsafe {
            let glx_display = cast::transmute(display);

            // CONSIDER:
            // In skia, they compute the GLX_ALPHA_SIZE minimum and request
            // that as well.

            let fbconfig_attributes = [
                GLX_DOUBLEBUFFER, 0,
                GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
                GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
                GLX_RENDER_TYPE, GLX_RGBA_BIT,
                0
            ];

            let screen = XDefaultScreen(display);
            let mut configs = 0;
            let fbconfigs = glXChooseFBConfig(glx_display, screen,
                                              &fbconfig_attributes[0], &mut configs);
            if configs == 0 {
                fail!("Unable to locate a GLX FB configuration that supports RGBA.");
            }

            let fbconfig = *fbconfigs.offset(0);
            let vi = glXGetVisualFromFBConfig(glx_display, fbconfig);
            (cast::transmute(vi), Some(fbconfig))
        }
    }

    /// Creates a native graphics context from the given X display connection. This uses GLX. Only
    /// the compositor is allowed to call this.
    pub fn from_display(display: *Display) -> NativeCompositingGraphicsContext {
        let (visual_info, fbconfig) = NativeCompositingGraphicsContext::compositor_visual_info(display);

        NativeCompositingGraphicsContext {
            display: display,
            visual_info: visual_info,
            framebuffer_configuration: fbconfig,
        }
    }
}

/// The X display.
#[deriving(Clone)]
pub struct NativeGraphicsMetadata {
    pub display: *Display,
}

impl NativeGraphicsMetadata {
    /// Creates graphics metadata from a metadata descriptor.
    pub fn from_descriptor(descriptor: &NativeGraphicsMetadataDescriptor)
                           -> NativeGraphicsMetadata {
        // WARNING: We currently rely on the X display connection being the
        // same in both the Painting and Compositing contexts, as otherwise
        // the X Pixmap will not be sharable across them. Using this
        // method breaks that assumption.
        unsafe {
            let display = descriptor.display.with_c_str(|c_str| {
                    XOpenDisplay(c_str)
                });

            if display.is_null() {
                fail!("XOpenDisplay() failed!");
            }

            NativeGraphicsMetadata {
                display: display,
            }
        }
    }
}

/// A sendable form of the X display string.
#[deriving(Clone, Decodable, Encodable)]
pub struct NativeGraphicsMetadataDescriptor {
    display: ~str,
}

impl NativeGraphicsMetadataDescriptor {
    /// Creates a metadata descriptor from metadata.
    pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor {
        unsafe {
            let c_str = CString::new(XDisplayString(metadata.display), false);
            NativeGraphicsMetadataDescriptor {
                display: c_str.as_str().unwrap().to_str(),
            }
        }
    }
}

#[deriving(Eq)]
pub enum NativeSurfaceTransientData {
    NoTransientData,
    RenderTaskTransientData(*Display, *XVisualInfo),
}

#[deriving(Decodable, Encodable)]
pub struct NativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

    /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes.
    will_leak: bool,
}

impl Drop for NativeSurface {
    fn drop(&mut self) {
        if self.will_leak {
            fail!("You should have disposed of the pixmap properly with destroy()! This pixmap \
                   will leak!");
        }
    }
}

impl NativeSurface {
    pub fn from_pixmap(pixmap: Pixmap) -> NativeSurface {
        NativeSurface {
            pixmap: pixmap,
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, stride: i32)
           -> NativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);
            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       ((stride / size.width) * 8) as c_uint);
            NativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    fn bind_to_texture(&self,
                       native_context: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       _size: Size2D<int>) {
        unsafe {
            // Create the GLX pixmap.
            //
            // FIXME(pcwalton): RAII for exception safety?
            let pixmap_attributes = [
                GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
                0
            ];

            let glx_display = cast::transmute(native_context.display);

            let glx_pixmap = glXCreatePixmap(glx_display,
                                             native_context.framebuffer_configuration.expect(
                                                 "GLX 1.3 should have a framebuffer_configuration"),
                                             self.pixmap,
                                             &pixmap_attributes[0]);

            let glXBindTexImageEXT: extern "C" fn(*Display, GLXDrawable, c_int, *c_int) =
                cast::transmute(glXGetProcAddress(cast::transmute(&"glXBindTexImageEXT\x00"[0])));
            assert!(glXBindTexImageEXT as *c_void != ptr::null());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               cast::transmute(glx_pixmap),
                               GLX_FRONT_EXT,
                               ptr::null());
            assert_eq!(gl2::get_error(), NO_ERROR);

            // FIXME(pcwalton): Recycle these for speed?
            glXDestroyPixmap(glx_display, glx_pixmap);
        }
    }

    /// This may only be called on the painting side.
    fn upload(&self, graphics_context: &NativePaintingGraphicsContext, data: &[u8]) {
        unsafe {
            // Ensure that we're running on the render task. Take the display.
            let pixmap = self.pixmap;

            // Figure out the width, height, and depth of the pixmap.
            let mut root_window = 0;
            let mut x = 0;
            let mut y = 0;
            let mut width = 0;
            let mut height = 0;
            let mut border_width = 0;
            let mut depth = 0;
            let _ = XGetGeometry(graphics_context.display,
                                 cast::transmute(pixmap),
                                 &mut root_window,
                                 &mut x,
                                 &mut y,
                                 &mut width,
                                 &mut height,
                                 &mut border_width,
                                 &mut depth);

            // Create the image.
            let image = XCreateImage(graphics_context.display,
                                     (*graphics_context.visual_info).visual,
                                     depth,
                                     ZPixmap,
                                     0,
                                     cast::transmute(&data[0]),
                                     width as c_uint,
                                     height as c_uint,
                                     32,
                                     0);

            // Create the X graphics context.
            let gc = XCreateGC(graphics_context.display, pixmap, 0, ptr::null());

            // Draw the image.
            let _ = XPutImage(graphics_context.display,
                              pixmap,
                              gc,
                              image,
                              0,
                              0,
                              0,
                              0,
                              width,
                              height);
        }
    }

    fn get_id(&self) -> int {
        self.pixmap as int
    }

    fn destroy(&mut self, graphics_context: &NativePaintingGraphicsContext) {
        unsafe {
            assert!(self.pixmap != 0);
            XFreePixmap(graphics_context.display, self.pixmap);
            self.mark_wont_leak()
        }
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<commit_msg>Use GLXFBConfig's depth when creating an XPixmap for a GLXPixmap<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

//! Implementation of cross-process surfaces for Linux. This uses X pixmaps.

use platform::surface::NativeSurfaceMethods;
use texturegl::Texture;

use geom::size::Size2D;
use libc::{c_int, c_uint, c_void};
use opengles::glx::{GLXFBConfig, GLXDrawable};
use opengles::glx::{GLX_BIND_TO_TEXTURE_RGBA_EXT};
use opengles::glx::{GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT};
use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT};
use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXCreatePixmap, glXDestroyPixmap};
use opengles::glx::{glXGetProcAddress, glXChooseFBConfig};
use opengles::glx::{glXGetVisualFromFBConfig};
use opengles::glx::{GLX_RGBA_BIT, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_DOUBLEBUFFER};
use opengles::gl2::NO_ERROR;
use opengles::gl2;
use std::cast;
use std::c_str::CString;
use std::ptr;
use xlib::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen};
use xlib::xlib::{XDisplayString, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow};
use xlib::xlib::{XVisualInfo, ZPixmap};

/// The display and visual info. This is needed in order to upload on the painting side. This
/// holds a weak reference to the display and will not close it when done.
///
/// FIXME(pcwalton): Mark nonsendable and noncopyable.
pub struct NativePaintingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
}

impl NativePaintingGraphicsContext {
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
        // visual.
        let (compositor_visual_info, _) =
            NativeCompositingGraphicsContext::compositor_visual_info(metadata.display);

        NativePaintingGraphicsContext {
            display: metadata.display,
            visual_info: compositor_visual_info,
        }
    }
}

/// The display, visual info, and framebuffer configuration. This is needed in order to bind to a
/// texture on the compositor side. This holds only a *weak* reference to the display and does not
/// close it.
///
/// FIXME(pcwalton): Unchecked weak references are bad and can violate memory safety. This is hard
/// to fix because the Display is given to us by the native windowing system, but we should fix it
/// someday.
///
/// FIXME(pcwalton): Mark nonsendable.
pub struct NativeCompositingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
    framebuffer_configuration: Option<GLXFBConfig>,
}

impl NativeCompositingGraphicsContext {
    /// Chooses the compositor visual info using the same algorithm that the compositor uses.
    ///
    /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual.
    fn compositor_visual_info(display: *Display) -> (*XVisualInfo, Option<GLXFBConfig>) {
        unsafe {
            let glx_display = cast::transmute(display);

            // CONSIDER:
            // In skia, they compute the GLX_ALPHA_SIZE minimum and request
            // that as well.

            let fbconfig_attributes = [
                GLX_DOUBLEBUFFER, 0,
                GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
                GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
                GLX_RENDER_TYPE, GLX_RGBA_BIT,
                0
            ];

            let screen = XDefaultScreen(display);
            let mut configs = 0;
            let fbconfigs = glXChooseFBConfig(glx_display, screen,
                                              &fbconfig_attributes[0], &mut configs);
            if configs == 0 {
                fail!("Unable to locate a GLX FB configuration that supports RGBA.");
            }

            let fbconfig = *fbconfigs.offset(0);
            let vi = glXGetVisualFromFBConfig(glx_display, fbconfig);
            (cast::transmute(vi), Some(fbconfig))
        }
    }

    /// Creates a native graphics context from the given X display connection. This uses GLX. Only
    /// the compositor is allowed to call this.
    pub fn from_display(display: *Display) -> NativeCompositingGraphicsContext {
        let (visual_info, fbconfig) = NativeCompositingGraphicsContext::compositor_visual_info(display);

        NativeCompositingGraphicsContext {
            display: display,
            visual_info: visual_info,
            framebuffer_configuration: fbconfig,
        }
    }
}

/// The X display.
#[deriving(Clone)]
pub struct NativeGraphicsMetadata {
    pub display: *Display,
}

impl NativeGraphicsMetadata {
    /// Creates graphics metadata from a metadata descriptor.
    pub fn from_descriptor(descriptor: &NativeGraphicsMetadataDescriptor)
                           -> NativeGraphicsMetadata {
        // WARNING: We currently rely on the X display connection being the
        // same in both the Painting and Compositing contexts, as otherwise
        // the X Pixmap will not be sharable across them. Using this
        // method breaks that assumption.
        unsafe {
            let display = descriptor.display.with_c_str(|c_str| {
                    XOpenDisplay(c_str)
                });

            if display.is_null() {
                fail!("XOpenDisplay() failed!");
            }

            NativeGraphicsMetadata {
                display: display,
            }
        }
    }
}

/// A sendable form of the X display string.
#[deriving(Clone, Decodable, Encodable)]
pub struct NativeGraphicsMetadataDescriptor {
    display: ~str,
}

impl NativeGraphicsMetadataDescriptor {
    /// Creates a metadata descriptor from metadata.
    pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor {
        unsafe {
            let c_str = CString::new(XDisplayString(metadata.display), false);
            NativeGraphicsMetadataDescriptor {
                display: c_str.as_str().unwrap().to_str(),
            }
        }
    }
}

#[deriving(Eq)]
pub enum NativeSurfaceTransientData {
    NoTransientData,
    RenderTaskTransientData(*Display, *XVisualInfo),
}

#[deriving(Decodable, Encodable)]
pub struct NativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

    /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes.
    will_leak: bool,
}

impl Drop for NativeSurface {
    fn drop(&mut self) {
        if self.will_leak {
            fail!("You should have disposed of the pixmap properly with destroy()! This pixmap \
                   will leak!");
        }
    }
}

impl NativeSurface {
    pub fn from_pixmap(pixmap: Pixmap) -> NativeSurface {
        NativeSurface {
            pixmap: pixmap,
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, stride: i32)
           -> NativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);
            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       (*native_context.visual_info).depth as c_uint);
            NativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    fn bind_to_texture(&self,
                       native_context: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       _size: Size2D<int>) {
        unsafe {
            // Create the GLX pixmap.
            //
            // FIXME(pcwalton): RAII for exception safety?
            let pixmap_attributes = [
                GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
                0
            ];

            let glx_display = cast::transmute(native_context.display);

            let glx_pixmap = glXCreatePixmap(glx_display,
                                             native_context.framebuffer_configuration.expect(
                                                 "GLX 1.3 should have a framebuffer_configuration"),
                                             self.pixmap,
                                             &pixmap_attributes[0]);

            let glXBindTexImageEXT: extern "C" fn(*Display, GLXDrawable, c_int, *c_int) =
                cast::transmute(glXGetProcAddress(cast::transmute(&"glXBindTexImageEXT\x00"[0])));
            assert!(glXBindTexImageEXT as *c_void != ptr::null());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               cast::transmute(glx_pixmap),
                               GLX_FRONT_EXT,
                               ptr::null());
            assert_eq!(gl2::get_error(), NO_ERROR);

            // FIXME(pcwalton): Recycle these for speed?
            glXDestroyPixmap(glx_display, glx_pixmap);
        }
    }

    /// This may only be called on the painting side.
    fn upload(&self, graphics_context: &NativePaintingGraphicsContext, data: &[u8]) {
        unsafe {
            // Ensure that we're running on the render task. Take the display.
            let pixmap = self.pixmap;

            // Figure out the width, height, and depth of the pixmap.
            let mut root_window = 0;
            let mut x = 0;
            let mut y = 0;
            let mut width = 0;
            let mut height = 0;
            let mut border_width = 0;
            let mut depth = 0;
            let _ = XGetGeometry(graphics_context.display,
                                 cast::transmute(pixmap),
                                 &mut root_window,
                                 &mut x,
                                 &mut y,
                                 &mut width,
                                 &mut height,
                                 &mut border_width,
                                 &mut depth);

            // Create the image.
            let image = XCreateImage(graphics_context.display,
                                     (*graphics_context.visual_info).visual,
                                     depth,
                                     ZPixmap,
                                     0,
                                     cast::transmute(&data[0]),
                                     width as c_uint,
                                     height as c_uint,
                                     32,
                                     0);

            // Create the X graphics context.
            let gc = XCreateGC(graphics_context.display, pixmap, 0, ptr::null());

            // Draw the image.
            let _ = XPutImage(graphics_context.display,
                              pixmap,
                              gc,
                              image,
                              0,
                              0,
                              0,
                              0,
                              width,
                              height);
        }
    }

    fn get_id(&self) -> int {
        self.pixmap as int
    }

    fn destroy(&mut self, graphics_context: &NativePaintingGraphicsContext) {
        unsafe {
            assert!(self.pixmap != 0);
            XFreePixmap(graphics_context.display, self.pixmap);
            self.mark_wont_leak()
        }
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<|endoftext|>


<commit_before>//! Things pertaining to the RFC 7230 `token` grammar rule.
//!
//! RFC 7230 grammar:
//!
//! ```abnf
//! token          = 1*tchar
//!
//! tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
//!                / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
//!                / DIGIT / ALPHA
//!                ; any VCHAR, except delimiters
//! ```
//!
//! Possibly of interest is that RFC 2616 actually defined things the other way
//! round, with the primary definition being that of what is now labelled the
//! delimiters but which was at that time known as *separators*:
//!
//! ```bnf
//! [REMEMBER THIS RULE IS OBSOLETE!]
//! separators: "(" | ")" | "<" | ">" | "@" | "," | ";" | ":"
//!           | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{"
//!           | "}" | SP | HT
//! ```

use std::borrow::BorrowFrom;
use std::fmt;
use std::str;
use self::Token::{Owned, Slice};

/// tchar: a token character; any VCHAR, except delimiters.
#[inline]
pub fn is_tchar(o: u8) -> bool {
    o == b'!' || o == b'#' || o == b'$' || o == b'%' || o == b'&' || o == b'\'' ||
    o == b'*' || o == b'+' || o == b'-' || o == b'.' || o == b'^' || o == b'_' ||
    o == b'`' || o == b'|' || o == b'~' || (o >= b'0' && o <= b'9') ||
    (o >= b'A' && o <= b'Z') || (o >= b'a' && o <= b'z')
}

/// A type representing an RFC 7230 `token`.
///
/// This permits strict character set control in a way that a simple `Vec<u8>`
/// or `String` would not.
///
/// This may be either owned, corresponding to `String`/`Vec<u8>`, or a slice,
/// corresponding to `&str`/`&[u8]`.
#[deriving(Clone, Hash)]
pub enum Token<'a> {
    /// A token backed by a vector (`Vec<u8>`).
    #[doc(hidden)]
    Owned {
        #[doc(hidden)]
        _bytes: Vec<u8>,
    },
    /// A token backed by a slice (`&[u8]`).
    #[doc(hidden)]
    Slice {
        #[doc(hidden)]
        _bytes: &'a [u8],
    },
}

impl<'a> fmt::Show for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Owned { ref _bytes } => f.write(_bytes.as_slice()),
            Slice { ref _bytes } => f.write(*_bytes),
        }
    }
}

impl<'a> PartialOrd for Token<'a> {
    #[inline]
    fn partial_cmp(&self, other: &Token<'a>) -> Option<Ordering> {
        self.as_bytes().partial_cmp(other.as_bytes())
    }
}

impl<'a> Ord for Token<'a> {
    #[inline]
    fn cmp(&self, other: &Token<'a>) -> Ordering {
        self.as_bytes().cmp(other.as_bytes())
    }
}

impl<'a> PartialEq for Token<'a> {
    #[inline]
    fn eq(&self, other: &Token<'a>) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl<'a> Eq for Token<'a> { }

impl<'a> Token<'a> {
    /// The number of bytes in the token.
    #[inline]
    pub fn len(&self) -> uint {
        self.as_bytes().len()
    }

    /// Whether the token is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl Token<'static> {
    /// Create a `Token` from a sequence of bytes.
    ///
    /// Returns `Err` with the original vector if not every byte in the vector
    /// is an RFC 7230 `tchar`.
    #[inline]
    pub fn from_vec(vec: Vec<u8>) -> Result<Token<'static>, Vec<u8>> {
        if vec.iter().all(|&c| is_tchar(c)) {
            Ok(Owned { _bytes: vec })
        } else {
            Err(vec)
        }
    }

    /// Create a `Token` from a sequence of bytes, without checking it.
    ///
    /// Be very careful calling this.
    #[inline]
    pub unsafe fn from_vec_nocheck(vec: Vec<u8>) -> Token<'static> {
        Owned { _bytes: vec }
    }
}

impl<'a> Token<'a> {
    /// Create a `Token` from a sequence of bytes.
    ///
    /// Returns `None` if not every byte in the slice is a RFC 7230 `tchar`.
    pub fn from_slice(slice: &[u8]) -> Option<Token> {
        if slice.iter().all(|&c| is_tchar(c)) {
            Some(Slice { _bytes: slice })
        } else {
            None
        }
    }

    /// Create a `Token` from a sequence of bytes, without checking it.
    ///
    /// Be very careful calling this.
    pub unsafe fn from_slice_nocheck(slice: &[u8]) -> Token {
        Slice { _bytes: slice }
    }

    /// Make a copy of the token, based around a slice of `self`.
    ///
    /// This is practically a free operation.
    #[inline]
    pub fn slice(&self) -> Token {
        Slice { _bytes: self.as_bytes() }
    }

    /// Change a slice token into an owned token.
    ///
    /// An owned token will be unchanged.
    #[inline]
    pub fn into_owned(self) -> Token<'static> {
        match self {
            Owned { _bytes } => Owned { _bytes: _bytes },
            Slice { _bytes } => Owned { _bytes: _bytes.to_vec() },
        }
    }

    /// Get a string slice of the contents of the token.
    #[inline]
    pub fn as_str(&self) -> &str {
        // `token` is a subset of ASCII, so this cannot produce invalid data.
        unsafe {
            str::raw::from_utf8(self.as_bytes())
        }
    }

    /// Get a slice of the bytes in the token.
    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        match *self {
            Owned { ref _bytes } => _bytes.as_slice(),
            Slice { ref _bytes } => _bytes.as_slice(),
        }
    }
}

impl<'a> Deref<str> for Token<'a> {
    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl<'a> BorrowFrom<Token<'a>> for [u8] {
    fn borrow_from<'b>(owned: &'b Token<'a>) -> &'b [u8] { owned.as_bytes() }
}

impl<'a> BorrowFrom<Token<'a>> for str {
    fn borrow_from<'b>(owned: &'b Token<'a>) -> &'b str { owned.as_str() }
}
<commit_msg>Update to avoid a deprecation warning.<commit_after>//! Things pertaining to the RFC 7230 `token` grammar rule.
//!
//! RFC 7230 grammar:
//!
//! ```abnf
//! token          = 1*tchar
//!
//! tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
//!                / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
//!                / DIGIT / ALPHA
//!                ; any VCHAR, except delimiters
//! ```
//!
//! Possibly of interest is that RFC 2616 actually defined things the other way
//! round, with the primary definition being that of what is now labelled the
//! delimiters but which was at that time known as *separators*:
//!
//! ```bnf
//! [REMEMBER THIS RULE IS OBSOLETE!]
//! separators: "(" | ")" | "<" | ">" | "@" | "," | ";" | ":"
//!           | "\" | <"> | "/" | "[" | "]" | "?" | "=" | "{"
//!           | "}" | SP | HT
//! ```

use std::borrow::BorrowFrom;
use std::fmt;
use std::str;
use self::Token::{Owned, Slice};

/// tchar: a token character; any VCHAR, except delimiters.
#[inline]
pub fn is_tchar(o: u8) -> bool {
    o == b'!' || o == b'#' || o == b'$' || o == b'%' || o == b'&' || o == b'\'' ||
    o == b'*' || o == b'+' || o == b'-' || o == b'.' || o == b'^' || o == b'_' ||
    o == b'`' || o == b'|' || o == b'~' || (o >= b'0' && o <= b'9') ||
    (o >= b'A' && o <= b'Z') || (o >= b'a' && o <= b'z')
}

/// A type representing an RFC 7230 `token`.
///
/// This permits strict character set control in a way that a simple `Vec<u8>`
/// or `String` would not.
///
/// This may be either owned, corresponding to `String`/`Vec<u8>`, or a slice,
/// corresponding to `&str`/`&[u8]`.
#[deriving(Clone, Hash)]
pub enum Token<'a> {
    /// A token backed by a vector (`Vec<u8>`).
    #[doc(hidden)]
    Owned {
        #[doc(hidden)]
        _bytes: Vec<u8>,
    },
    /// A token backed by a slice (`&[u8]`).
    #[doc(hidden)]
    Slice {
        #[doc(hidden)]
        _bytes: &'a [u8],
    },
}

impl<'a> fmt::Show for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Owned { ref _bytes } => f.write(_bytes.as_slice()),
            Slice { ref _bytes } => f.write(*_bytes),
        }
    }
}

impl<'a> PartialOrd for Token<'a> {
    #[inline]
    fn partial_cmp(&self, other: &Token<'a>) -> Option<Ordering> {
        self.as_bytes().partial_cmp(other.as_bytes())
    }
}

impl<'a> Ord for Token<'a> {
    #[inline]
    fn cmp(&self, other: &Token<'a>) -> Ordering {
        self.as_bytes().cmp(other.as_bytes())
    }
}

impl<'a> PartialEq for Token<'a> {
    #[inline]
    fn eq(&self, other: &Token<'a>) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl<'a> Eq for Token<'a> { }

impl<'a> Token<'a> {
    /// The number of bytes in the token.
    #[inline]
    pub fn len(&self) -> uint {
        self.as_bytes().len()
    }

    /// Whether the token is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl Token<'static> {
    /// Create a `Token` from a sequence of bytes.
    ///
    /// Returns `Err` with the original vector if not every byte in the vector
    /// is an RFC 7230 `tchar`.
    #[inline]
    pub fn from_vec(vec: Vec<u8>) -> Result<Token<'static>, Vec<u8>> {
        if vec.iter().all(|&c| is_tchar(c)) {
            Ok(Owned { _bytes: vec })
        } else {
            Err(vec)
        }
    }

    /// Create a `Token` from a sequence of bytes, without checking it.
    ///
    /// Be very careful calling this.
    #[inline]
    pub unsafe fn from_vec_nocheck(vec: Vec<u8>) -> Token<'static> {
        Owned { _bytes: vec }
    }
}

impl<'a> Token<'a> {
    /// Create a `Token` from a sequence of bytes.
    ///
    /// Returns `None` if not every byte in the slice is a RFC 7230 `tchar`.
    pub fn from_slice(slice: &[u8]) -> Option<Token> {
        if slice.iter().all(|&c| is_tchar(c)) {
            Some(Slice { _bytes: slice })
        } else {
            None
        }
    }

    /// Create a `Token` from a sequence of bytes, without checking it.
    ///
    /// Be very careful calling this.
    pub unsafe fn from_slice_nocheck(slice: &[u8]) -> Token {
        Slice { _bytes: slice }
    }

    /// Make a copy of the token, based around a slice of `self`.
    ///
    /// This is practically a free operation.
    #[inline]
    pub fn slice(&self) -> Token {
        Slice { _bytes: self.as_bytes() }
    }

    /// Change a slice token into an owned token.
    ///
    /// An owned token will be unchanged.
    #[inline]
    pub fn into_owned(self) -> Token<'static> {
        match self {
            Owned { _bytes } => Owned { _bytes: _bytes },
            Slice { _bytes } => Owned { _bytes: _bytes.to_vec() },
        }
    }

    /// Get a string slice of the contents of the token.
    #[inline]
    pub fn as_str(&self) -> &str {
        // `token` is a subset of ASCII, so this cannot produce invalid data.
        unsafe {
            str::from_utf8_unchecked(self.as_bytes())
        }
    }

    /// Get a slice of the bytes in the token.
    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        match *self {
            Owned { ref _bytes } => _bytes.as_slice(),
            Slice { ref _bytes } => _bytes.as_slice(),
        }
    }
}

impl<'a> Deref<str> for Token<'a> {
    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl<'a> BorrowFrom<Token<'a>> for [u8] {
    fn borrow_from<'b>(owned: &'b Token<'a>) -> &'b [u8] { owned.as_bytes() }
}

impl<'a> BorrowFrom<Token<'a>> for str {
    fn borrow_from<'b>(owned: &'b Token<'a>) -> &'b str { owned.as_str() }
}
<|endoftext|>


<commit_before>use rustwlc::WlcOutput;
use petgraph::graph::NodeIndex;
use uuid::Uuid;
use super::super::{LayoutTree, TreeError, FocusError};
use ::layout::core::container::{Container, ContainerType, Layout, Handle};
use ::layout::core::borders::Borders;
use ::render::Renderable;
use ::debug_enabled;

// TODO This module needs to be updated like the other modules...
// Need to add some errors for this (such as when trying to move a non-container/view,
// or when trying to grab a workspace whos name already exists)
//
// Also the code is generally pretty crap, cause it's pretty old (mid-2016)

impl LayoutTree {
    /// Gets a workspace by name or creates it
    fn get_or_make_workspace(&mut self, name: &str) -> NodeIndex {
        let active_index = self.tree.follow_path_until(self.tree.root_ix(),
                                                      ContainerType::Output)
            .expect("get_or_make_wksp: Couldn't get output");
        let workspace_ix = self.tree.workspace_ix_by_name(name).unwrap_or_else(|| {
            let root_ix = self.init_workspace(name.to_string(), active_index);
            self.tree.parent_of(root_ix)
                .expect("Workspace was not properly initialized with a root container")
        });
        self.validate();
        workspace_ix
    }

    /// Initializes a workspace and gets the index of the root container
    pub fn init_workspace(&mut self, name: String, output_ix: NodeIndex)
                      -> NodeIndex {
        let geometry = self.tree.get(output_ix)
            .expect("init_workspace: invalid output").get_geometry()
            .expect("init_workspace: no geometry for output");
        let worksp = Container::new_workspace(name.to_string(), geometry);
        let output_handle = match self.tree[output_ix].get_handle() {
            Ok(Handle::Output(output)) => output,
            Err(err) => panic!("Could not get handle from output: {:#?}", err),
            _ => unreachable!()
        };

        trace!("Adding workspace {:?}", worksp);
        let worksp_ix = self.tree.add_child(output_ix, worksp, false);
        let borders = Borders::new(geometry, output_handle);
        let container = Container::new_container(geometry,
                                                 output_handle,
                                                 borders);
        let container_ix = self.tree.add_child(worksp_ix, container, false);
        self.tree.set_ancestor_paths_active(container_ix);
        self.validate();
        container_ix
    }

    /// Switch to the specified workspace
    pub fn switch_to_workspace(&mut self, name: &str) {
        let maybe_active_ix = self.active_container
            .or_else(|| {
                let new_active = self.tree.follow_path(self.tree.root_ix());
                match self.tree[new_active].get_type() {
                    ContainerType::View | ContainerType::Container => {
                        Some(new_active)
                    },
                    // else try and get the root container
                    _ => self.tree.descendant_of_type(new_active, ContainerType::Container).ok()
                }
            });
        if maybe_active_ix.is_none() {
            warn!("{:#?}", self);
            warn!("No active container, cannot switch");
            return;
        }
        let active_ix = maybe_active_ix.unwrap();
        // Get the old (current) workspace
        let old_worksp_ix: NodeIndex;
        if let Ok(index) = self.tree.ancestor_of_type(active_ix, ContainerType::Workspace) {
            old_worksp_ix = index;
            trace!("Switching to workspace {}", name);
        } else {
            match self.tree[active_ix].get_type() {
                ContainerType::Workspace => {
                    old_worksp_ix = active_ix;
                    trace!("Switching to workspace {}", name);
                },
                _ => {
                    warn!("Could not find old workspace, could not set invisible");
                    return;
                }
            }
        }
        // Get the new workspace, or create one if it doesn't work
        let mut workspace_ix = self.get_or_make_workspace(name);
        if old_worksp_ix == workspace_ix {
            return;
        }
        {
            // Update the border colors
            let container = &mut self.tree[active_ix];
            container.clear_border_color()
                .expect("Could not clear old active border color");
            container.draw_borders().expect("Could not draw borders");
        }
        let old_worksp_parent_ix = self.tree.parent_of(old_worksp_ix)
            .expect("Old workspace had no parent");
        let new_worksp_parent_ix = self.tree.parent_of(workspace_ix)
            .expect("New workspace had no parent");
        // Only set the old one to be invisible if new and old share output.
        if new_worksp_parent_ix == old_worksp_parent_ix {
            // Set the old one to invisible
            self.set_container_visibility(old_worksp_ix, false);
        } else {
            // Set all views on the target output to be invisible,
            // to clear the old workspace visibilty out.
            self.set_container_visibility(new_worksp_parent_ix, false);
        }
        // Set the new one to visible
        self.container_visibilty_wrapper(workspace_ix, true);
        // Focus on the new output
        match self.tree[new_worksp_parent_ix] {
            Container::Output { handle, .. } => {
                WlcOutput::focus(Some(handle))
            },
            _ => unreachable!()
        }
        // Delete the old workspace if it has no views on it
        self.active_container = None;
        if self.tree.descendant_of_type(old_worksp_ix, ContainerType::View).is_err() {
            let siblings = self.tree.children_of(old_worksp_parent_ix);
            // Only remove if it's **NOT** the only workspace on the output.
            // AND if the new workspace is on the same output.
            if siblings.len() > 1 && old_worksp_parent_ix == new_worksp_parent_ix {
                trace!("Removing workspace: {:?}", self.tree[old_worksp_ix].get_name()
                    .expect("Workspace had no name"));
                if let Err(err) = self.remove_workspace(old_worksp_ix) {
                    warn!("Tried to remove empty workspace {:#?}, error: {:?}",
                        old_worksp_ix, err);
                    info!("{:#?}", self);
                    panic!("Could not remove old workspace");
                }
            }
        }
        workspace_ix = self.tree.workspace_ix_by_name(name)
            .expect("Workspace we just made was deleted!");
        let active_ix = self.tree.follow_path(workspace_ix);
        match self.tree[active_ix].get_type() {
            ContainerType::View  => {
                match self.tree[active_ix] {
                    Container::View { id, ..} => {
                        self.focus_on(id).unwrap_or_else(|_| {
                            warn!("Could not focus on {:?}", id);
                        });
                    },
                    _ => unreachable!()
                }
                // TODO Propogate this when this is refactored
                match self.set_active_node(active_ix) {
                    Err(TreeError::Focus(
                        FocusError::BlockedByFullscreen(_, focus_id))) => {
                        // If blocked, didn't get a chance to set it
                        self.active_container = self.tree.lookup_id(focus_id);
                        Ok(())
                    },
                    other => other
                }.expect("Could not set new active node");
                self.tree.set_ancestor_paths_active(active_ix);
                self.layout(workspace_ix);
                self.validate();
                self.validate_path();
                return;
            },
            _ => {
                self.active_container = self.tree
                    .descendant_of_type(active_ix, ContainerType::View)
                    .or_else(|_| self.tree.descendant_of_type(active_ix,
                                                              ContainerType::Container)).ok();
                match self.tree[self.active_container.expect("Workspace had NO children!")] {
                    Container::View { .. } => {
                        self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                    },
                    Container::Container { .. } => {
                        self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                    }
                    _ => unreachable!()
                };
            }
        }
        trace!("Focusing on next container");
        self.focus_on_next_container(workspace_ix);
        self.layout(workspace_ix);
        self.validate();
        self.validate_path();
    }

    /// Moves the active container to a new workspace.
    pub fn send_active_to_workspace(&mut self, name: &str) {
        if let Some(active_ix) = self.active_container {
            let id = self.tree[active_ix].get_id();
            self.send_to_workspace(id, name);
        }
    }
    /// Moves a container to a new workspace
    pub fn send_to_workspace(&mut self, id: Uuid, name: &str) {
        let node_ix = self.tree.lookup_id(id);
        // Ensure focus
        // TODO Need to not make it default, but need to add tests to make
        // sure that doesn't cause a regression.
        if let Some(active_ix) = node_ix.or(self.active_container) {
            let curr_work_ix = self.active_ix_of(ContainerType::Workspace)
                .expect("send_active: Not currently in a workspace!");
            if active_ix == self.tree.children_of(curr_work_ix)[0] {
                warn!("Tried to move the root container of a workspace, aborting move");
                return;
            }
            let next_work_ix = self.get_or_make_workspace(name);

            // Check if the workspaces are the same
            if next_work_ix == curr_work_ix {
                trace!("Attempted to move a view to the same workspace {}!", name);
                return;
            }
            self.set_container_visibility(curr_work_ix, false);
            let new_output_ix = self.tree.parent_of(next_work_ix)
                .expect("Target workspace had no parent");
            match self.tree[new_output_ix] {
                Container::Output { handle: output_handle, .. } => {
                    fn set_output_recurse(this: &mut LayoutTree,
                                          node_ix: NodeIndex,
                                          output_handle: WlcOutput) {
                        match this.tree[node_ix].get_type() {
                            ContainerType::View => {
                                this.tree[node_ix].update_border_output(output_handle)
                                    .expect("Could not update border output for view");
                            },
                            ContainerType::Container => {
                                this.tree[node_ix].update_border_output(output_handle)
                                    .expect("Could not update border output for view");
                                for child_ix in this.tree.children_of(node_ix) {
                                    set_output_recurse(this, child_ix, output_handle)
                                }
                            },
                            _ => unreachable!()
                        }
                    }
                    set_output_recurse(self, active_ix, output_handle);
                },
                _ => unreachable!()
            }

            // Save the parent of this view for focusing
            let maybe_active_parent = self.tree.parent_of(active_ix);

            // Get the root container of the next workspace
            let next_work_children = self.tree.children_of(next_work_ix);
            if cfg!(debug_assertions) || !debug_enabled() {
                assert!(next_work_children.len() == 1,
                        "Next workspace has multiple roots!");
            }
            let next_work_root_ix = next_work_children[0];

            // Move the container
            info!("Moving container {:?} to workspace {}",
                self.get_active_container(), name);
            self.tree.move_node(active_ix, next_work_root_ix);

            // If different outputs, show it on the new output.
            let cur_output_ix = self.tree.parent_of(curr_work_ix)
                .expect("Couldn't get parent of current work index");
            if new_output_ix != cur_output_ix {
                self.container_visibilty_wrapper(new_output_ix, true);
            }

            // If it's a fullscreen app, then update the fullscreen lists
            self.transfer_fullscreen(curr_work_ix, next_work_ix, id);

            // Update the active container
            if let Ok(parent_ix) = maybe_active_parent {
                self.tree.set_ancestor_paths_active(parent_ix);
                let ctype = self.tree.node_type(parent_ix).unwrap_or(ContainerType::Root);
                if ctype == ContainerType::Container {
                    self.focus_on_next_container(parent_ix);
                } else {
                    trace!("Send to container invalidated a NodeIndex: {:?} to {:?}",
                    parent_ix, ctype);
                }
                if self.tree.can_remove_empty_parent(parent_ix) {
                    if let Err(err) = self.remove_view_or_container(parent_ix) {
                        error!("{:#?}\nCould not remove {:#?} from tree {:#?}", err, parent_ix, self);
                        panic!("Could not remove empty parent!");
                    }
                }
            }
            else {
                self.focus_on_next_container(curr_work_ix);
            }
            self.container_visibilty_wrapper(curr_work_ix, true);
            if !self.tree[active_ix].floating() {
                self.normalize_container(active_ix).ok();
            }
        }
        let root_ix = self.tree.root_ix();
        self.layout(root_ix);
        self.validate();
        self.validate_path();
    }

    /// Transfers a fullscreen app from this workspace to another.
    fn transfer_fullscreen(&mut self, cur_work_ix: NodeIndex, next_work_ix: NodeIndex,
                           fullscreen_id: Uuid) {
        if let Some(fullscreen_ids) = self.tree[cur_work_ix].fullscreen_c() {
            if !fullscreen_ids.iter().any(|id| *id == fullscreen_id) {
                return;
            }
        } else {
            return;
        }
        self.tree[cur_work_ix].update_fullscreen_c(fullscreen_id, false)
            .expect("cur_work_ix was not a workspace");
        self.tree[next_work_ix].update_fullscreen_c(fullscreen_id, true)
            .expect("next_work_ix was not a workspace");
    }

    /// Wrapper around `set_container_visibility`, so that tabbed/stacked
    /// is handled correctly (i.e, it's visibilty checks are skipped).
    fn container_visibilty_wrapper(&mut self, node_ix: NodeIndex, val: bool) {
        let mut set = false;
        match self.tree[node_ix] {
            Container::Container { layout, .. } => {
                match layout {
                    Layout::Tabbed | Layout::Stacked => {
                        if val {
                            set = true;
                        }
                    },
                    _ => {}
                }
            },
            _ => {}
        }
        if !set {
            self.tree[node_ix].set_visibility(val);
            for child in self.tree.children_of(node_ix) {
                self.container_visibilty_wrapper(child, val)
            }
        } else {
            self.tree.next_active_node(node_ix)
                .map(|node| self.container_visibilty_wrapper(node, val));
        }
    }

    /// Gets the current workspace we are focused on
    ///
    /// If a workspace is not found, this is considered a hard error and
    /// it will panic.
    #[allow(dead_code)]
    pub fn current_workspace(&self) -> Result<&str, TreeError> {
        let active_ix = self.active_container
            .ok_or(TreeError::NoActiveContainer)?;
        let workspace_ix = self.tree.ancestor_of_type(active_ix,
                                                      ContainerType::Workspace)?;
        Ok(self.tree[workspace_ix].get_name()
           .expect("workspace_ix didn't point to a workspace!"))
    }
}

#[cfg(test)]
mod tests {
    use ::layout::core::tree::tests::basic_tree;

    #[test]
    pub fn switch_empty_workspaces() {
        let mut tree = basic_tree();
        tree.switch_to_workspace("5");
        tree.switch_to_workspace("4");
        tree.switch_to_workspace("5");
        tree.switch_to_workspace("4");
        tree.switch_to_workspace("2");
    }
}
<commit_msg>Only show on new output if workspace is vis<commit_after>use rustwlc::WlcOutput;
use petgraph::graph::NodeIndex;
use uuid::Uuid;
use super::super::{LayoutTree, TreeError, FocusError};
use ::layout::core::container::{Container, ContainerType, Layout, Handle};
use ::layout::core::borders::Borders;
use ::render::Renderable;
use ::debug_enabled;

// TODO This module needs to be updated like the other modules...
// Need to add some errors for this (such as when trying to move a non-container/view,
// or when trying to grab a workspace whos name already exists)
//
// Also the code is generally pretty crap, cause it's pretty old (mid-2016)

impl LayoutTree {
    /// Gets a workspace by name or creates it
    fn get_or_make_workspace(&mut self, name: &str) -> NodeIndex {
        let active_index = self.tree.follow_path_until(self.tree.root_ix(),
                                                      ContainerType::Output)
            .expect("get_or_make_wksp: Couldn't get output");
        let workspace_ix = self.tree.workspace_ix_by_name(name).unwrap_or_else(|| {
            let root_ix = self.init_workspace(name.to_string(), active_index);
            self.tree.parent_of(root_ix)
                .expect("Workspace was not properly initialized with a root container")
        });
        self.validate();
        workspace_ix
    }

    /// Initializes a workspace and gets the index of the root container
    pub fn init_workspace(&mut self, name: String, output_ix: NodeIndex)
                      -> NodeIndex {
        let geometry = self.tree.get(output_ix)
            .expect("init_workspace: invalid output").get_geometry()
            .expect("init_workspace: no geometry for output");
        let worksp = Container::new_workspace(name.to_string(), geometry);
        let output_handle = match self.tree[output_ix].get_handle() {
            Ok(Handle::Output(output)) => output,
            Err(err) => panic!("Could not get handle from output: {:#?}", err),
            _ => unreachable!()
        };

        trace!("Adding workspace {:?}", worksp);
        let worksp_ix = self.tree.add_child(output_ix, worksp, false);
        let borders = Borders::new(geometry, output_handle);
        let container = Container::new_container(geometry,
                                                 output_handle,
                                                 borders);
        let container_ix = self.tree.add_child(worksp_ix, container, false);
        self.tree.set_ancestor_paths_active(container_ix);
        self.validate();
        container_ix
    }

    /// Switch to the specified workspace
    pub fn switch_to_workspace(&mut self, name: &str) {
        let maybe_active_ix = self.active_container
            .or_else(|| {
                let new_active = self.tree.follow_path(self.tree.root_ix());
                match self.tree[new_active].get_type() {
                    ContainerType::View | ContainerType::Container => {
                        Some(new_active)
                    },
                    // else try and get the root container
                    _ => self.tree.descendant_of_type(new_active, ContainerType::Container).ok()
                }
            });
        if maybe_active_ix.is_none() {
            warn!("{:#?}", self);
            warn!("No active container, cannot switch");
            return;
        }
        let active_ix = maybe_active_ix.unwrap();
        // Get the old (current) workspace
        let old_worksp_ix: NodeIndex;
        if let Ok(index) = self.tree.ancestor_of_type(active_ix, ContainerType::Workspace) {
            old_worksp_ix = index;
            trace!("Switching to workspace {}", name);
        } else {
            match self.tree[active_ix].get_type() {
                ContainerType::Workspace => {
                    old_worksp_ix = active_ix;
                    trace!("Switching to workspace {}", name);
                },
                _ => {
                    warn!("Could not find old workspace, could not set invisible");
                    return;
                }
            }
        }
        // Get the new workspace, or create one if it doesn't work
        let mut workspace_ix = self.get_or_make_workspace(name);
        if old_worksp_ix == workspace_ix {
            return;
        }
        {
            // Update the border colors
            let container = &mut self.tree[active_ix];
            container.clear_border_color()
                .expect("Could not clear old active border color");
            container.draw_borders().expect("Could not draw borders");
        }
        let old_worksp_parent_ix = self.tree.parent_of(old_worksp_ix)
            .expect("Old workspace had no parent");
        let new_worksp_parent_ix = self.tree.parent_of(workspace_ix)
            .expect("New workspace had no parent");
        // Only set the old one to be invisible if new and old share output.
        if new_worksp_parent_ix == old_worksp_parent_ix {
            // Set the old one to invisible
            self.set_container_visibility(old_worksp_ix, false);
        } else {
            // Set all views on the target output to be invisible,
            // to clear the old workspace visibilty out.
            self.set_container_visibility(new_worksp_parent_ix, false);
        }
        // Set the new one to visible
        self.container_visibilty_wrapper(workspace_ix, true);
        // Focus on the new output
        match self.tree[new_worksp_parent_ix] {
            Container::Output { handle, .. } => {
                WlcOutput::focus(Some(handle))
            },
            _ => unreachable!()
        }
        // Delete the old workspace if it has no views on it
        self.active_container = None;
        if self.tree.descendant_of_type(old_worksp_ix, ContainerType::View).is_err() {
            let siblings = self.tree.children_of(old_worksp_parent_ix);
            // Only remove if it's **NOT** the only workspace on the output.
            // AND if the new workspace is on the same output.
            if siblings.len() > 1 && old_worksp_parent_ix == new_worksp_parent_ix {
                trace!("Removing workspace: {:?}", self.tree[old_worksp_ix].get_name()
                    .expect("Workspace had no name"));
                if let Err(err) = self.remove_workspace(old_worksp_ix) {
                    warn!("Tried to remove empty workspace {:#?}, error: {:?}",
                        old_worksp_ix, err);
                    info!("{:#?}", self);
                    panic!("Could not remove old workspace");
                }
            }
        }
        workspace_ix = self.tree.workspace_ix_by_name(name)
            .expect("Workspace we just made was deleted!");
        let active_ix = self.tree.follow_path(workspace_ix);
        match self.tree[active_ix].get_type() {
            ContainerType::View  => {
                match self.tree[active_ix] {
                    Container::View { id, ..} => {
                        self.focus_on(id).unwrap_or_else(|_| {
                            warn!("Could not focus on {:?}", id);
                        });
                    },
                    _ => unreachable!()
                }
                // TODO Propogate this when this is refactored
                match self.set_active_node(active_ix) {
                    Err(TreeError::Focus(
                        FocusError::BlockedByFullscreen(_, focus_id))) => {
                        // If blocked, didn't get a chance to set it
                        self.active_container = self.tree.lookup_id(focus_id);
                        Ok(())
                    },
                    other => other
                }.expect("Could not set new active node");
                self.tree.set_ancestor_paths_active(active_ix);
                self.layout(workspace_ix);
                self.validate();
                self.validate_path();
                return;
            },
            _ => {
                self.active_container = self.tree
                    .descendant_of_type(active_ix, ContainerType::View)
                    .or_else(|_| self.tree.descendant_of_type(active_ix,
                                                              ContainerType::Container)).ok();
                match self.tree[self.active_container.expect("Workspace had NO children!")] {
                    Container::View { .. } => {
                        self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                    },
                    Container::Container { .. } => {
                        self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                    }
                    _ => unreachable!()
                };
            }
        }
        trace!("Focusing on next container");
        self.focus_on_next_container(workspace_ix);
        self.layout(workspace_ix);
        self.validate();
        self.validate_path();
    }

    /// Moves the active container to a new workspace.
    pub fn send_active_to_workspace(&mut self, name: &str) {
        if let Some(active_ix) = self.active_container {
            let id = self.tree[active_ix].get_id();
            self.send_to_workspace(id, name);
        }
    }
    /// Moves a container to a new workspace
    pub fn send_to_workspace(&mut self, id: Uuid, name: &str) {
        let node_ix = self.tree.lookup_id(id);
        // Ensure focus
        // TODO Need to not make it default, but need to add tests to make
        // sure that doesn't cause a regression.
        if let Some(active_ix) = node_ix.or(self.active_container) {
            let curr_work_ix = self.active_ix_of(ContainerType::Workspace)
                .expect("send_active: Not currently in a workspace!");
            if active_ix == self.tree.children_of(curr_work_ix)[0] {
                warn!("Tried to move the root container of a workspace, aborting move");
                return;
            }
            let next_work_ix = self.get_or_make_workspace(name);

            // Check if the workspaces are the same
            if next_work_ix == curr_work_ix {
                trace!("Attempted to move a view to the same workspace {}!", name);
                return;
            }
            self.set_container_visibility(curr_work_ix, false);
            let new_output_ix = self.tree.parent_of(next_work_ix)
                .expect("Target workspace had no parent");
            match self.tree[new_output_ix] {
                Container::Output { handle: output_handle, .. } => {
                    fn set_output_recurse(this: &mut LayoutTree,
                                          node_ix: NodeIndex,
                                          output_handle: WlcOutput) {
                        match this.tree[node_ix].get_type() {
                            ContainerType::View => {
                                this.tree[node_ix].update_border_output(output_handle)
                                    .expect("Could not update border output for view");
                            },
                            ContainerType::Container => {
                                this.tree[node_ix].update_border_output(output_handle)
                                    .expect("Could not update border output for view");
                                for child_ix in this.tree.children_of(node_ix) {
                                    set_output_recurse(this, child_ix, output_handle)
                                }
                            },
                            _ => unreachable!()
                        }
                    }
                    set_output_recurse(self, active_ix, output_handle);
                },
                _ => unreachable!()
            }

            // Save the parent of this view for focusing
            let maybe_active_parent = self.tree.parent_of(active_ix);

            // Get the root container of the next workspace
            let next_work_children = self.tree.children_of(next_work_ix);
            if cfg!(debug_assertions) || !debug_enabled() {
                assert!(next_work_children.len() == 1,
                        "Next workspace has multiple roots!");
            }
            let next_work_root_ix = next_work_children[0];

            // Move the container
            info!("Moving container {:?} to workspace {}",
                self.get_active_container(), name);
            self.tree.move_node(active_ix, next_work_root_ix);

            let cur_output_ix = self.tree.parent_of(curr_work_ix)
                .expect("Couldn't get parent of current work index");
            let next_is_vis = self.tree.on_path(next_work_root_ix);
            // If different outputs, show it on the new output if workspace is vis.
            if new_output_ix != cur_output_ix && next_is_vis {
                self.container_visibilty_wrapper(new_output_ix, true);
            }

            // If it's a fullscreen app, then update the fullscreen lists
            self.transfer_fullscreen(curr_work_ix, next_work_ix, id);

            // Update the active container
            if let Ok(parent_ix) = maybe_active_parent {
                self.tree.set_ancestor_paths_active(parent_ix);
                let ctype = self.tree.node_type(parent_ix).unwrap_or(ContainerType::Root);
                if ctype == ContainerType::Container {
                    self.focus_on_next_container(parent_ix);
                } else {
                    trace!("Send to container invalidated a NodeIndex: {:?} to {:?}",
                    parent_ix, ctype);
                }
                if self.tree.can_remove_empty_parent(parent_ix) {
                    if let Err(err) = self.remove_view_or_container(parent_ix) {
                        error!("{:#?}\nCould not remove {:#?} from tree {:#?}", err, parent_ix, self);
                        panic!("Could not remove empty parent!");
                    }
                }
            }
            else {
                self.focus_on_next_container(curr_work_ix);
            }
            self.container_visibilty_wrapper(curr_work_ix, true);
            if !self.tree[active_ix].floating() {
                self.normalize_container(active_ix).ok();
            }
        }
        let root_ix = self.tree.root_ix();
        self.layout(root_ix);
        self.validate();
        self.validate_path();
    }

    /// Transfers a fullscreen app from this workspace to another.
    fn transfer_fullscreen(&mut self, cur_work_ix: NodeIndex, next_work_ix: NodeIndex,
                           fullscreen_id: Uuid) {
        if let Some(fullscreen_ids) = self.tree[cur_work_ix].fullscreen_c() {
            if !fullscreen_ids.iter().any(|id| *id == fullscreen_id) {
                return;
            }
        } else {
            return;
        }
        self.tree[cur_work_ix].update_fullscreen_c(fullscreen_id, false)
            .expect("cur_work_ix was not a workspace");
        self.tree[next_work_ix].update_fullscreen_c(fullscreen_id, true)
            .expect("next_work_ix was not a workspace");
    }

    /// Wrapper around `set_container_visibility`, so that tabbed/stacked
    /// is handled correctly (i.e, it's visibilty checks are skipped).
    fn container_visibilty_wrapper(&mut self, node_ix: NodeIndex, val: bool) {
        let mut set = false;
        match self.tree[node_ix] {
            Container::Container { layout, .. } => {
                match layout {
                    Layout::Tabbed | Layout::Stacked => {
                        if val {
                            set = true;
                        }
                    },
                    _ => {}
                }
            },
            _ => {}
        }
        if !set {
            self.tree[node_ix].set_visibility(val);
            for child in self.tree.children_of(node_ix) {
                self.container_visibilty_wrapper(child, val)
            }
        } else {
            self.tree.next_active_node(node_ix)
                .map(|node| self.container_visibilty_wrapper(node, val));
        }
    }

    /// Gets the current workspace we are focused on
    ///
    /// If a workspace is not found, this is considered a hard error and
    /// it will panic.
    #[allow(dead_code)]
    pub fn current_workspace(&self) -> Result<&str, TreeError> {
        let active_ix = self.active_container
            .ok_or(TreeError::NoActiveContainer)?;
        let workspace_ix = self.tree.ancestor_of_type(active_ix,
                                                      ContainerType::Workspace)?;
        Ok(self.tree[workspace_ix].get_name()
           .expect("workspace_ix didn't point to a workspace!"))
    }
}

#[cfg(test)]
mod tests {
    use ::layout::core::tree::tests::basic_tree;

    #[test]
    pub fn switch_empty_workspaces() {
        let mut tree = basic_tree();
        tree.switch_to_workspace("5");
        tree.switch_to_workspace("4");
        tree.switch_to_workspace("5");
        tree.switch_to_workspace("4");
        tree.switch_to_workspace("2");
    }
}
<|endoftext|>