<commit_before>extern crate schannel;

use self::schannel::cert_context::{CertContext, HashAlgorithm};
use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions};
use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred};
use self::schannel::tls_stream;
use std::error;
use std::fmt;
use std::io;
use std::str;

use {TlsAcceptorBuilder, TlsConnectorBuilder};

const SEC_E_NO_CREDENTIALS: u32 = 0x8009030E;

static PROTOCOLS: &'static [Protocol] = &[
    Protocol::Ssl3,
    Protocol::Tls10,
    Protocol::Tls11,
    Protocol::Tls12,
];

fn convert_protocols(min: Option<::Protocol>, max: Option<::Protocol>) -> &'static [Protocol] {
    let mut protocols = PROTOCOLS;
    if let Some(p) = max.and_then(|max| protocols.get(..max as usize)) {
        protocols = p;
    }
    if let Some(p) = min.and_then(|min| protocols.get(min as usize..)) {
        protocols = p;
    }
    protocols
}

pub struct Error(io::Error);

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        error::Error::source(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Error {
        Error(error)
    }
}

#[derive(Clone)]
pub struct Identity {
    cert: CertContext,
}

impl Identity {
    pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> {
        let store = PfxImportOptions::new().password(pass).import(buf)?;
        let mut identity = None;

        for cert in store.certs() {
            if cert
                .private_key()
                .silent(true)
                .compare_key(true)
                .acquire()
                .is_ok()
            {
                identity = Some(cert);
                break;
            }
        }

        let identity = match identity {
            Some(identity) => identity,
            None => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "No identity found in PKCS #12 archive",
                )
                .into());
            }
        };

        Ok(Identity { cert: identity })
    }
}

#[derive(Clone)]
pub struct Certificate(CertContext);

impl Certificate {
    pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = CertContext::new(buf)?;
        Ok(Certificate(cert))
    }

    pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> {
        match str::from_utf8(buf) {
            Ok(s) => {
                let cert = CertContext::from_pem(s)?;
                Ok(Certificate(cert))
            }
            Err(_) => Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "PEM representation contains non-UTF-8 bytes",
            )
            .into()),
        }
    }

    pub fn to_der(&self) -> Result<Vec<u8>, Error> {
        Ok(self.0.to_der().to_vec())
    }
}

pub struct MidHandshakeTlsStream<S>(tls_stream::MidHandshakeTlsStream<S>);

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S> MidHandshakeTlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S> MidHandshakeTlsStream<S>
where
    S: io::Read + io::Write,
{
    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self.0.handshake() {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub enum HandshakeError<S> {
    Failure(Error),
    WouldBlock(MidHandshakeTlsStream<S>),
}

impl<S> From<tls_stream::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: tls_stream::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            tls_stream::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            tls_stream::HandshakeError::Interrupted(s) => {
                HandshakeError::WouldBlock(MidHandshakeTlsStream(s))
            }
        }
    }
}

impl<S> From<io::Error> for HandshakeError<S> {
    fn from(e: io::Error) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

#[derive(Clone, Debug)]
pub struct TlsConnector {
    cert: Option<CertContext>,
    roots: CertStore,
    min_protocol: Option<::Protocol>,
    max_protocol: Option<::Protocol>,
    use_sni: bool,
    accept_invalid_hostnames: bool,
    accept_invalid_certs: bool,
    disable_built_in_roots: bool,
    alpn: Vec<String>,
}

impl TlsConnector {
    pub fn new(builder: &TlsConnectorBuilder) -> Result<TlsConnector, Error> {
        let cert = builder.identity.as_ref().map(|i| i.0.cert.clone());
        let mut roots = Memory::new()?.into_store();
        for cert in &builder.root_certificates {
            roots.add_cert(&(cert.0).0, CertAdd::ReplaceExisting)?;
        }

        Ok(TlsConnector {
            cert,
            roots,
            min_protocol: builder.min_protocol,
            max_protocol: builder.max_protocol,
            use_sni: builder.use_sni,
            accept_invalid_hostnames: builder.accept_invalid_hostnames,
            accept_invalid_certs: builder.accept_invalid_certs,
            disable_built_in_roots: builder.disable_built_in_roots,
            alpn: builder.alpn.clone(),
        })
    }

    pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = SchannelCred::builder();
        builder.enabled_protocols(convert_protocols(self.min_protocol, self.max_protocol));
        if let Some(cert) = self.cert.as_ref() {
            builder.cert(cert.clone());
        }
        let cred = builder.acquire(Direction::Outbound)?;
        let mut builder = tls_stream::Builder::new();
        builder
            .cert_store(self.roots.clone())
            .domain(domain)
            .use_sni(self.use_sni)
            .accept_invalid_hostnames(self.accept_invalid_hostnames);
        if self.accept_invalid_certs {
            builder.verify_callback(|_| Ok(()));
        } else if self.disable_built_in_roots {
            let roots_copy = self.roots.clone();
            builder.verify_callback(move |res| {
                if let Err(err) = res.result() {
                    // Propagate previous error encountered during normal cert validation.
                    return Err(err);
                }

                if let Some(chain) = res.chain() {
                    if chain
                        .certificates()
                        .any(|cert| roots_copy.certs().any(|root_cert| root_cert == cert))
                    {
                        return Ok(());
                    }
                }

                Err(io::Error::new(
                    io::ErrorKind::Other,
                    "unable to find any user-specified roots in the final cert chain",
                ))
            });
        }
        #[cfg(feature = "alpn")]
        {
            if !self.alpn.is_empty() {
                builder.request_application_protocols(
                    &self.alpn.iter().map(|s| s.as_bytes()).collect::<Vec<_>>(),
                );
            }
        }
        match builder.connect(cred, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

#[derive(Clone)]
pub struct TlsAcceptor {
    cert: CertContext,
    min_protocol: Option<::Protocol>,
    max_protocol: Option<::Protocol>,
}

impl TlsAcceptor {
    pub fn new(builder: &TlsAcceptorBuilder) -> Result<TlsAcceptor, Error> {
        Ok(TlsAcceptor {
            cert: builder.identity.0.cert.clone(),
            min_protocol: builder.min_protocol,
            max_protocol: builder.max_protocol,
        })
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = SchannelCred::builder();
        builder.enabled_protocols(convert_protocols(self.min_protocol, self.max_protocol));
        builder.cert(self.cert.clone());
        // FIXME we're probably missing the certificate chain?
        let cred = builder.acquire(Direction::Inbound)?;
        match tls_stream::Builder::new().accept(cred, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsStream<S>(tls_stream::TlsStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S> TlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(self.0.get_buf().len())
    }

    pub fn peer_certificate(&self) -> Result<Option<Certificate>, Error> {
        match self.0.peer_certificate() {
            Ok(cert) => Ok(Some(Certificate(cert))),
            Err(ref e) if e.raw_os_error() == Some(SEC_E_NO_CREDENTIALS as i32) => Ok(None),
            Err(e) => Err(Error(e)),
        }
    }

    #[cfg(feature = "alpn")]
    pub fn negotiated_alpn(&self) -> Result<Option<Vec<u8>>, Error> {
        Ok(self.0.negotiated_application_protocol()?)
    }

    pub fn tls_server_end_point(&self) -> Result<Option<Vec<u8>>, Error> {
        let cert = if self.0.is_server() {
            self.0.certificate()
        } else {
            self.0.peer_certificate()
        };

        let cert = match cert {
            Ok(cert) => cert,
            Err(ref e) if e.raw_os_error() == Some(SEC_E_NO_CREDENTIALS as i32) => return Ok(None),
            Err(e) => return Err(Error(e)),
        };

        let signature_algorithms = cert.sign_hash_algorithms()?;
        let hash = match signature_algorithms.rsplit('/').next().unwrap() {
            "MD5" | "SHA1" | "SHA256" => HashAlgorithm::sha256(),
            "SHA384" => HashAlgorithm::sha384(),
            "SHA512" => HashAlgorithm::sha512(),
            _ => return Ok(None),
        };

        let digest = cert.fingerprint(hash)?;
        Ok(Some(digest))
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        self.0.shutdown()?;
        Ok(())
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}
<commit_msg>fix schannel alpn<commit_after>extern crate schannel;

use self::schannel::cert_context::{CertContext, HashAlgorithm};
use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions};
use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred};
use self::schannel::tls_stream;
use std::error;
use std::fmt;
use std::io;
use std::str;

use {TlsAcceptorBuilder, TlsConnectorBuilder};

const SEC_E_NO_CREDENTIALS: u32 = 0x8009030E;

static PROTOCOLS: &'static [Protocol] = &[
    Protocol::Ssl3,
    Protocol::Tls10,
    Protocol::Tls11,
    Protocol::Tls12,
];

fn convert_protocols(min: Option<::Protocol>, max: Option<::Protocol>) -> &'static [Protocol] {
    let mut protocols = PROTOCOLS;
    if let Some(p) = max.and_then(|max| protocols.get(..max as usize)) {
        protocols = p;
    }
    if let Some(p) = min.and_then(|min| protocols.get(min as usize..)) {
        protocols = p;
    }
    protocols
}

pub struct Error(io::Error);

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        error::Error::source(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Error {
        Error(error)
    }
}

#[derive(Clone)]
pub struct Identity {
    cert: CertContext,
}

impl Identity {
    pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> {
        let store = PfxImportOptions::new().password(pass).import(buf)?;
        let mut identity = None;

        for cert in store.certs() {
            if cert
                .private_key()
                .silent(true)
                .compare_key(true)
                .acquire()
                .is_ok()
            {
                identity = Some(cert);
                break;
            }
        }

        let identity = match identity {
            Some(identity) => identity,
            None => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "No identity found in PKCS #12 archive",
                )
                .into());
            }
        };

        Ok(Identity { cert: identity })
    }
}

#[derive(Clone)]
pub struct Certificate(CertContext);

impl Certificate {
    pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = CertContext::new(buf)?;
        Ok(Certificate(cert))
    }

    pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> {
        match str::from_utf8(buf) {
            Ok(s) => {
                let cert = CertContext::from_pem(s)?;
                Ok(Certificate(cert))
            }
            Err(_) => Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "PEM representation contains non-UTF-8 bytes",
            )
            .into()),
        }
    }

    pub fn to_der(&self) -> Result<Vec<u8>, Error> {
        Ok(self.0.to_der().to_vec())
    }
}

pub struct MidHandshakeTlsStream<S>(tls_stream::MidHandshakeTlsStream<S>);

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S> MidHandshakeTlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S> MidHandshakeTlsStream<S>
where
    S: io::Read + io::Write,
{
    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self.0.handshake() {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub enum HandshakeError<S> {
    Failure(Error),
    WouldBlock(MidHandshakeTlsStream<S>),
}

impl<S> From<tls_stream::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: tls_stream::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            tls_stream::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            tls_stream::HandshakeError::Interrupted(s) => {
                HandshakeError::WouldBlock(MidHandshakeTlsStream(s))
            }
        }
    }
}

impl<S> From<io::Error> for HandshakeError<S> {
    fn from(e: io::Error) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

#[derive(Clone, Debug)]
pub struct TlsConnector {
    cert: Option<CertContext>,
    roots: CertStore,
    min_protocol: Option<::Protocol>,
    max_protocol: Option<::Protocol>,
    use_sni: bool,
    accept_invalid_hostnames: bool,
    accept_invalid_certs: bool,
    disable_built_in_roots: bool,
    #[cfg(feature = "alpn")]
    alpn: Vec<String>,
}

impl TlsConnector {
    pub fn new(builder: &TlsConnectorBuilder) -> Result<TlsConnector, Error> {
        let cert = builder.identity.as_ref().map(|i| i.0.cert.clone());
        let mut roots = Memory::new()?.into_store();
        for cert in &builder.root_certificates {
            roots.add_cert(&(cert.0).0, CertAdd::ReplaceExisting)?;
        }

        Ok(TlsConnector {
            cert,
            roots,
            min_protocol: builder.min_protocol,
            max_protocol: builder.max_protocol,
            use_sni: builder.use_sni,
            accept_invalid_hostnames: builder.accept_invalid_hostnames,
            accept_invalid_certs: builder.accept_invalid_certs,
            disable_built_in_roots: builder.disable_built_in_roots,
            #[cfg(feature = "alpn")]
            alpn: builder.alpn.clone(),
        })
    }

    pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = SchannelCred::builder();
        builder.enabled_protocols(convert_protocols(self.min_protocol, self.max_protocol));
        if let Some(cert) = self.cert.as_ref() {
            builder.cert(cert.clone());
        }
        let cred = builder.acquire(Direction::Outbound)?;
        let mut builder = tls_stream::Builder::new();
        builder
            .cert_store(self.roots.clone())
            .domain(domain)
            .use_sni(self.use_sni)
            .accept_invalid_hostnames(self.accept_invalid_hostnames);
        if self.accept_invalid_certs {
            builder.verify_callback(|_| Ok(()));
        } else if self.disable_built_in_roots {
            let roots_copy = self.roots.clone();
            builder.verify_callback(move |res| {
                if let Err(err) = res.result() {
                    // Propagate previous error encountered during normal cert validation.
                    return Err(err);
                }

                if let Some(chain) = res.chain() {
                    if chain
                        .certificates()
                        .any(|cert| roots_copy.certs().any(|root_cert| root_cert == cert))
                    {
                        return Ok(());
                    }
                }

                Err(io::Error::new(
                    io::ErrorKind::Other,
                    "unable to find any user-specified roots in the final cert chain",
                ))
            });
        }
        #[cfg(feature = "alpn")]
        {
            if !self.alpn.is_empty() {
                builder.request_application_protocols(
                    &self.alpn.iter().map(|s| s.as_bytes()).collect::<Vec<_>>(),
                );
            }
        }
        match builder.connect(cred, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

#[derive(Clone)]
pub struct TlsAcceptor {
    cert: CertContext,
    min_protocol: Option<::Protocol>,
    max_protocol: Option<::Protocol>,
}

impl TlsAcceptor {
    pub fn new(builder: &TlsAcceptorBuilder) -> Result<TlsAcceptor, Error> {
        Ok(TlsAcceptor {
            cert: builder.identity.0.cert.clone(),
            min_protocol: builder.min_protocol,
            max_protocol: builder.max_protocol,
        })
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut builder = SchannelCred::builder();
        builder.enabled_protocols(convert_protocols(self.min_protocol, self.max_protocol));
        builder.cert(self.cert.clone());
        // FIXME we're probably missing the certificate chain?
        let cred = builder.acquire(Direction::Inbound)?;
        match tls_stream::Builder::new().accept(cred, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsStream<S>(tls_stream::TlsStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S> TlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(self.0.get_buf().len())
    }

    pub fn peer_certificate(&self) -> Result<Option<Certificate>, Error> {
        match self.0.peer_certificate() {
            Ok(cert) => Ok(Some(Certificate(cert))),
            Err(ref e) if e.raw_os_error() == Some(SEC_E_NO_CREDENTIALS as i32) => Ok(None),
            Err(e) => Err(Error(e)),
        }
    }

    #[cfg(feature = "alpn")]
    pub fn negotiated_alpn(&self) -> Result<Option<Vec<u8>>, Error> {
        Ok(self.0.negotiated_application_protocol()?)
    }

    pub fn tls_server_end_point(&self) -> Result<Option<Vec<u8>>, Error> {
        let cert = if self.0.is_server() {
            self.0.certificate()
        } else {
            self.0.peer_certificate()
        };

        let cert = match cert {
            Ok(cert) => cert,
            Err(ref e) if e.raw_os_error() == Some(SEC_E_NO_CREDENTIALS as i32) => return Ok(None),
            Err(e) => return Err(Error(e)),
        };

        let signature_algorithms = cert.sign_hash_algorithms()?;
        let hash = match signature_algorithms.rsplit('/').next().unwrap() {
            "MD5" | "SHA1" | "SHA256" => HashAlgorithm::sha256(),
            "SHA384" => HashAlgorithm::sha384(),
            "SHA512" => HashAlgorithm::sha512(),
            _ => return Ok(None),
        };

        let digest = cert.fingerprint(hash)?;
        Ok(Some(digest))
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        self.0.shutdown()?;
        Ok(())
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals, non_camel_case_types)]

extern crate libc;

use std::ffi::CString;

pub type UInt32 = libc::c_uint;
pub type SInt16 = libc::c_short;
pub type SInt32 = libc::c_int;

pub type FourCharCode = UInt32;
pub type OSType = FourCharCode;
pub type OSErr = SInt16;

pub const gestaltSystemVersion: libc::c_uint = 1937339254;
pub const gestaltSystemVersionMajor: libc::c_uint = 1937339185;
pub const gestaltSystemVersionMinor: libc::c_uint = 1937339186;
pub const gestaltSystemVersionBugFix: libc::c_uint = 1937339187;

pub type CFRef = *mut libc::c_void;

pub type CFIndex = libc::c_long;
pub type CFTimeInterval = f64;

pub type CFMutableArrayRef = CFRef;
pub type CFURLRef = CFRef;
pub type CFErrorRef = CFRef;
pub type CFStringRef = CFRef;
pub type CFRunLoopRef = CFRef;

pub const NULL: CFRef = 0 as CFRef;
pub const NULL_REF_PTR: *mut CFRef = 0 as *mut CFRef;

pub type CFURLPathStyle = libc::c_uint;

pub const kCFAllocatorDefault: CFRef = NULL;
pub const kCFURLPOSIXPathStyle: CFURLPathStyle = 0;
pub const kCFURLHFSPathStyle: CFURLPathStyle = 1;
pub const kCFURLWindowsPathStyle: CFURLPathStyle = 2;

pub const kCFStringEncodingUTF8: u32 = 0x08000100;
pub type CFStringEncoding = u32;

pub const kCFCompareEqualTo: i32 = 0;
pub type CFComparisonResult = i32;

// MacOS uses Case Insensitive path
pub const kCFCompareCaseInsensitive: u32 = 1;
pub type CFStringCompareFlags = u32;

// CFStringEncoding
pub type kCFStringEncoding = u32;
pub const UTF8: kCFStringEncoding = 0x08000100;

#[repr(C)]
pub struct CFArrayCallBacks {
  version: CFIndex,
  retain: CFRef,
  release: CFRef,
  cp: CFRef,
  equal: CFRef,
}
//impl Clone for CFArrayCallBacks { }

#[link(name = "CoreServices", kind = "framework")]
extern "C" {
    pub static kCFTypeArrayCallBacks: CFArrayCallBacks;
    pub static kCFRunLoopDefaultMode: CFStringRef;

    pub fn Gestalt(selector: OSType, response: *const SInt32) -> OSErr;
    pub fn CFRelease(res: CFRef);
    pub fn CFShow(res: CFRef);

    pub fn CFRunLoopRun();
    pub fn CFRunLoopStop(run_loop: CFRunLoopRef);
    pub fn CFRunLoopGetCurrent() -> CFRunLoopRef;

    pub fn CFArrayCreateMutable(allocator: CFRef, capacity: CFIndex, callbacks: *const CFArrayCallBacks) -> CFMutableArrayRef;
    pub fn CFArrayInsertValueAtIndex(arr: CFMutableArrayRef, position: CFIndex, element: CFRef);
    pub fn CFArrayAppendValue(aff: CFMutableArrayRef, element: CFRef);
    pub fn CFArrayGetCount(arr: CFMutableArrayRef) -> CFIndex;
    pub fn CFArrayGetValueAtIndex(arr: CFMutableArrayRef, index: CFIndex) -> CFRef;

    pub fn CFURLCreateFileReferenceURL(allocator: CFRef, url: CFURLRef, err: CFRef) -> CFURLRef;
    pub fn CFURLCreateFilePathURL(allocator: CFRef, url: CFURLRef, err: CFRef) ->CFURLRef;
    pub fn CFURLCreateFromFileSystemRepresentation(allocator: CFRef, path: *const libc::c_char, len: CFIndex, is_directory: bool) -> CFURLRef;
    pub fn CFURLCopyAbsoluteURL(res: CFURLRef) -> CFURLRef;
    pub fn CFURLCopyLastPathComponent(res: CFURLRef) -> CFStringRef;
    pub fn CFURLCreateCopyDeletingLastPathComponent(allocator: CFRef, url: CFURLRef) -> CFURLRef;
    pub fn CFURLCreateCopyAppendingPathComponent(allocation: CFRef, url: CFURLRef, path: CFStringRef, is_directory: bool) -> CFURLRef;
    pub fn CFURLCopyFileSystemPath(anUrl: CFURLRef, path_style: CFURLPathStyle) -> CFStringRef;

    pub fn CFURLResourceIsReachable(res: CFURLRef, err: *mut CFErrorRef) -> bool;

    pub fn CFShowStr (str: CFStringRef);
    pub fn CFStringGetCStringPtr(theString: CFStringRef, encoding: CFStringEncoding) -> *const libc::c_char;
    pub fn CFStringCreateWithCString (alloc: CFRef, source: *const libc::c_char, encoding: kCFStringEncoding) -> CFStringRef;

    pub fn CFStringCompare(theString1: CFStringRef, theString2: CFStringRef, compareOptions: CFStringCompareFlags) -> CFComparisonResult;
    pub fn CFArrayRemoveValueAtIndex(theArray: CFMutableArrayRef, idx: CFIndex);

}

pub fn system_version_major() -> SInt32 {
  unsafe {
    let ret: SInt32 = 0;
    let err = Gestalt(gestaltSystemVersionMajor, &ret);
    if err != 0 {
      panic!("Gestalt call failed with error {} for gestaltSystemVersionMajor", err);
    }
    return ret;
  }

}

pub fn system_version_minor() -> SInt32 {
  unsafe {
    let ret: SInt32 = 0;
    let err = Gestalt(gestaltSystemVersionMinor, &ret);
    if err != 0 {
      panic!("Gestalt call failed with error {} for gestaltSystemVersionMinor", err);
    }
    return ret;
  }

}

pub fn system_version_bugfix() -> SInt32 {
  unsafe {
    let ret: SInt32 = 0;
    let err = Gestalt(gestaltSystemVersionBugFix, &ret);
    if err != 0 {
      panic!("Gestalt call failed with error {} for gestaltSystemVersionBugFix", err);
    }
    return ret;
  }

}

pub unsafe fn str_path_to_cfstring_ref(source: &str) -> CFStringRef {
  let c_path = CString::new(source).unwrap();
  let c_len = libc::strlen(c_path.as_ptr());
  let mut url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, c_path.as_ptr(), c_len as i64, false);
  let mut placeholder = CFURLCopyAbsoluteURL(url);
  CFRelease(url);

  let imaginary: CFRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);

  while !CFURLResourceIsReachable(placeholder, NULL_REF_PTR) {
    let child = CFURLCopyLastPathComponent(placeholder);
    CFArrayInsertValueAtIndex(imaginary, 0, child);
    CFRelease(child);

    url = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, placeholder);
    CFRelease(placeholder);
    placeholder = url;
  }

  url = CFURLCreateFileReferenceURL(kCFAllocatorDefault, placeholder, kCFAllocatorDefault);
  CFRelease(placeholder);
  placeholder = CFURLCreateFilePathURL(kCFAllocatorDefault, url, kCFAllocatorDefault);
  CFRelease(url);

  if imaginary != kCFAllocatorDefault {
    let mut count =  0;
    while count < CFArrayGetCount(imaginary) {
      let component = CFArrayGetValueAtIndex(imaginary, count);
      url = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, placeholder, component, false);
      CFRelease(placeholder);
      placeholder = url;
      count = count + 1;
    }
    CFRelease(imaginary);
  }

  let cf_path = CFURLCopyFileSystemPath(placeholder, kCFURLPOSIXPathStyle);
  CFRelease(placeholder);
  cf_path
}
<commit_msg>fix cast on i686-apple-darwin<commit_after>#![allow(non_upper_case_globals, non_camel_case_types)]

extern crate libc;

use std::ffi::CString;

pub type UInt32 = libc::c_uint;
pub type SInt16 = libc::c_short;
pub type SInt32 = libc::c_int;

pub type FourCharCode = UInt32;
pub type OSType = FourCharCode;
pub type OSErr = SInt16;

pub const gestaltSystemVersion: libc::c_uint = 1937339254;
pub const gestaltSystemVersionMajor: libc::c_uint = 1937339185;
pub const gestaltSystemVersionMinor: libc::c_uint = 1937339186;
pub const gestaltSystemVersionBugFix: libc::c_uint = 1937339187;

pub type CFRef = *mut libc::c_void;

pub type CFIndex = libc::c_long;
pub type CFTimeInterval = f64;

pub type CFMutableArrayRef = CFRef;
pub type CFURLRef = CFRef;
pub type CFErrorRef = CFRef;
pub type CFStringRef = CFRef;
pub type CFRunLoopRef = CFRef;

pub const NULL: CFRef = 0 as CFRef;
pub const NULL_REF_PTR: *mut CFRef = 0 as *mut CFRef;

pub type CFURLPathStyle = libc::c_uint;

pub const kCFAllocatorDefault: CFRef = NULL;
pub const kCFURLPOSIXPathStyle: CFURLPathStyle = 0;
pub const kCFURLHFSPathStyle: CFURLPathStyle = 1;
pub const kCFURLWindowsPathStyle: CFURLPathStyle = 2;

pub const kCFStringEncodingUTF8: u32 = 0x08000100;
pub type CFStringEncoding = u32;

pub const kCFCompareEqualTo: i32 = 0;
pub type CFComparisonResult = i32;

// MacOS uses Case Insensitive path
pub const kCFCompareCaseInsensitive: u32 = 1;
pub type CFStringCompareFlags = u32;

// CFStringEncoding
pub type kCFStringEncoding = u32;
pub const UTF8: kCFStringEncoding = 0x08000100;

#[repr(C)]
pub struct CFArrayCallBacks {
  version: CFIndex,
  retain: CFRef,
  release: CFRef,
  cp: CFRef,
  equal: CFRef,
}
//impl Clone for CFArrayCallBacks { }

#[link(name = "CoreServices", kind = "framework")]
extern "C" {
    pub static kCFTypeArrayCallBacks: CFArrayCallBacks;
    pub static kCFRunLoopDefaultMode: CFStringRef;

    pub fn Gestalt(selector: OSType, response: *const SInt32) -> OSErr;
    pub fn CFRelease(res: CFRef);
    pub fn CFShow(res: CFRef);

    pub fn CFRunLoopRun();
    pub fn CFRunLoopStop(run_loop: CFRunLoopRef);
    pub fn CFRunLoopGetCurrent() -> CFRunLoopRef;

    pub fn CFArrayCreateMutable(allocator: CFRef, capacity: CFIndex, callbacks: *const CFArrayCallBacks) -> CFMutableArrayRef;
    pub fn CFArrayInsertValueAtIndex(arr: CFMutableArrayRef, position: CFIndex, element: CFRef);
    pub fn CFArrayAppendValue(aff: CFMutableArrayRef, element: CFRef);
    pub fn CFArrayGetCount(arr: CFMutableArrayRef) -> CFIndex;
    pub fn CFArrayGetValueAtIndex(arr: CFMutableArrayRef, index: CFIndex) -> CFRef;

    pub fn CFURLCreateFileReferenceURL(allocator: CFRef, url: CFURLRef, err: CFRef) -> CFURLRef;
    pub fn CFURLCreateFilePathURL(allocator: CFRef, url: CFURLRef, err: CFRef) ->CFURLRef;
    pub fn CFURLCreateFromFileSystemRepresentation(allocator: CFRef, path: *const libc::c_char, len: CFIndex, is_directory: bool) -> CFURLRef;
    pub fn CFURLCopyAbsoluteURL(res: CFURLRef) -> CFURLRef;
    pub fn CFURLCopyLastPathComponent(res: CFURLRef) -> CFStringRef;
    pub fn CFURLCreateCopyDeletingLastPathComponent(allocator: CFRef, url: CFURLRef) -> CFURLRef;
    pub fn CFURLCreateCopyAppendingPathComponent(allocation: CFRef, url: CFURLRef, path: CFStringRef, is_directory: bool) -> CFURLRef;
    pub fn CFURLCopyFileSystemPath(anUrl: CFURLRef, path_style: CFURLPathStyle) -> CFStringRef;

    pub fn CFURLResourceIsReachable(res: CFURLRef, err: *mut CFErrorRef) -> bool;

    pub fn CFShowStr (str: CFStringRef);
    pub fn CFStringGetCStringPtr(theString: CFStringRef, encoding: CFStringEncoding) -> *const libc::c_char;
    pub fn CFStringCreateWithCString (alloc: CFRef, source: *const libc::c_char, encoding: kCFStringEncoding) -> CFStringRef;

    pub fn CFStringCompare(theString1: CFStringRef, theString2: CFStringRef, compareOptions: CFStringCompareFlags) -> CFComparisonResult;
    pub fn CFArrayRemoveValueAtIndex(theArray: CFMutableArrayRef, idx: CFIndex);

}

pub fn system_version_major() -> SInt32 {
  unsafe {
    let ret: SInt32 = 0;
    let err = Gestalt(gestaltSystemVersionMajor, &ret);
    if err != 0 {
      panic!("Gestalt call failed with error {} for gestaltSystemVersionMajor", err);
    }
    return ret;
  }

}

pub fn system_version_minor() -> SInt32 {
  unsafe {
    let ret: SInt32 = 0;
    let err = Gestalt(gestaltSystemVersionMinor, &ret);
    if err != 0 {
      panic!("Gestalt call failed with error {} for gestaltSystemVersionMinor", err);
    }
    return ret;
  }

}

pub fn system_version_bugfix() -> SInt32 {
  unsafe {
    let ret: SInt32 = 0;
    let err = Gestalt(gestaltSystemVersionBugFix, &ret);
    if err != 0 {
      panic!("Gestalt call failed with error {} for gestaltSystemVersionBugFix", err);
    }
    return ret;
  }

}

pub unsafe fn str_path_to_cfstring_ref(source: &str) -> CFStringRef {
  let c_path = CString::new(source).unwrap();
  let c_len = libc::strlen(c_path.as_ptr());
  let mut url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, c_path.as_ptr(), c_len as CFIndex, false);
  let mut placeholder = CFURLCopyAbsoluteURL(url);
  CFRelease(url);

  let imaginary: CFRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);

  while !CFURLResourceIsReachable(placeholder, NULL_REF_PTR) {
    let child = CFURLCopyLastPathComponent(placeholder);
    CFArrayInsertValueAtIndex(imaginary, 0, child);
    CFRelease(child);

    url = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, placeholder);
    CFRelease(placeholder);
    placeholder = url;
  }

  url = CFURLCreateFileReferenceURL(kCFAllocatorDefault, placeholder, kCFAllocatorDefault);
  CFRelease(placeholder);
  placeholder = CFURLCreateFilePathURL(kCFAllocatorDefault, url, kCFAllocatorDefault);
  CFRelease(url);

  if imaginary != kCFAllocatorDefault {
    let mut count =  0;
    while count < CFArrayGetCount(imaginary) {
      let component = CFArrayGetValueAtIndex(imaginary, count);
      url = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, placeholder, component, false);
      CFRelease(placeholder);
      placeholder = url;
      count = count + 1;
    }
    CFRelease(imaginary);
  }

  let cf_path = CFURLCopyFileSystemPath(placeholder, kCFURLPOSIXPathStyle);
  CFRelease(placeholder);
  cf_path
}
<|endoftext|>


<commit_before>//! Common traits and types for egl rendering
//!
//! Large parts of this module are taken from
//! [glutin src/api/egl](https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/src/api/egl/)
//!
//! It therefore falls under
//! [glutin's Apache 2.0 license](https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/LICENSE)
//!
//! Wayland specific EGL functionality - EGL based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer)s.
//!
//! The types of this module can be used to initialize hardware acceleration rendering
//! based on EGL for clients as it may enabled usage of `EGLImage` based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer)s.
//!
//! To use it bind any backend implementing the [`EGLGraphicsBackend`](::backend::egl::EGLGraphicsBackend) trait, that shall do the
//! rendering (so pick a fast one), to the [`wayland_server::Display`] of your compositor.
//! Note only one backend may be bound to any [`Display`](wayland_server::Display) at any time.
//!
//! You may then use the resulting [`EGLDisplay`](::backend::egl::EGLDisplay) to receive [`EGLImages`](::backend::egl::EGLImages)
//! of an EGL-based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer) for rendering.

/*
#[cfg(feature = "renderer_gl")]
use crate::backend::graphics::{
    gl::{ffi as gl_ffi, GLGraphicsBackend},
    SwapBuffersError as GraphicsSwapBuffersError,
};
*/
use std::fmt;

pub mod context;
pub use self::context::EGLContext;
mod error;
pub use self::error::*;
use crate::backend::SwapBuffersError as GraphicsSwapBuffersError;

use nix::libc::c_void;

#[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)]
pub mod ffi;
use self::ffi::egl::types::EGLImage;

pub mod display;
pub mod native;
pub mod surface;
pub use self::display::EGLDisplay;
pub use self::surface::EGLSurface;

use self::display::EGLDisplayHandle;
use std::ffi::CString;
use std::sync::Arc;

/// Error that can happen on optional EGL features
#[derive(Debug, Clone, PartialEq)]
pub struct EglExtensionNotSupportedError(&'static [&'static str]);

impl fmt::Display for EglExtensionNotSupportedError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
        write!(
            formatter,
            "None of the following EGL extensions is supported by the underlying EGL implementation,
                     at least one is required: {:?}",
            self.0
        )
    }
}

impl ::std::error::Error for EglExtensionNotSupportedError {}

/// Returns the address of an OpenGL function.
///
/// Result is independent of displays and does not guarantee an extension is actually supported at runtime.
pub unsafe fn get_proc_address(symbol: &str) -> *const c_void {
    unsafe {
        let addr = CString::new(symbol.as_bytes()).unwrap();
        let addr = addr.as_ptr();
        ffi::egl::GetProcAddress(addr) as *const _
    }
}

/// Error that can occur when accessing an EGL buffer
#[cfg(feature = "wayland_frontend")]
#[derive(thiserror::Error)]
pub enum BufferAccessError {
    /// The corresponding Context is not alive anymore
    #[error("The corresponding context was lost")]
    ContextLost,
    /// This buffer is not managed by the EGL buffer
    #[error("This buffer is not managed by EGL. Err: {0:}")]
    NotManaged(#[source] EGLError),
    /// Failed to create `EGLImages` from the buffer
    #[error("Failed to create EGLImages from the buffer. Err: {0:}")]
    EGLImageCreationFailed(#[source] EGLError),
    /// The required EGL extension is not supported by the underlying EGL implementation
    #[error("{0}")]
    EglExtensionNotSupported(#[from] EglExtensionNotSupportedError),
    /// We currently do not support multi-planar buffers
    #[error("Multi-planar formats (like {0:?}) are unsupported")]
    UnsupportedMultiPlanarFormat(Format),
}

#[cfg(feature = "wayland_frontend")]
impl fmt::Debug for BufferAccessError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"),
            BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"),
            BufferAccessError::EGLImageCreationFailed(_) => {
                write!(formatter, "BufferAccessError::EGLImageCreationFailed")
            }
            BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err),
            BufferAccessError::UnsupportedMultiPlanarFormat(ref fmt) =>
                write!(formatter, "BufferAccessError::UnsupportedMultiPlanerFormat({:?})", fmt),
        }
    }
}

/// Error that can happen when swapping buffers.
#[derive(Debug, thiserror::Error)]
pub enum SwapBuffersError {
    /// EGL error during `eglSwapBuffers`
    #[error("{0:}")]
    EGLSwapBuffers(#[source] EGLError),
    /// EGL error during surface creation
    #[error("{0:}")]
    EGLCreateSurface(#[source] EGLError),
}

impl std::convert::From<SwapBuffersError> for GraphicsSwapBuffersError {
    fn from(value: SwapBuffersError) -> Self {
        match value {
            // bad surface is answered with a surface recreation in `swap_buffers`
            x @ SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface) => {
                GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
            }
            // the rest is either never happening or are unrecoverable
            x @ SwapBuffersError::EGLSwapBuffers(_) => GraphicsSwapBuffersError::ContextLost(Box::new(x)),
            x @ SwapBuffersError::EGLCreateSurface(_) => {
                GraphicsSwapBuffersError::ContextLost(Box::new(x))
            }
        }
    }
}

/// Error that can happen when making a context (and surface) current on the active thread.
#[derive(thiserror::Error, Debug)]
#[error("`eglMakeCurrent` failed: {0}")]
pub struct MakeCurrentError(#[from] EGLError);

impl From<MakeCurrentError> for GraphicsSwapBuffersError {
    fn from(err: MakeCurrentError) -> GraphicsSwapBuffersError {
        match err {
            /*
            From khronos docs:
                If draw or read are not compatible with context, then an EGL_BAD_MATCH error is generated.
                If context is current to some other thread, or if either draw or read are bound to contexts in another thread, an EGL_BAD_ACCESS error is generated.
                If binding context would exceed the number of current contexts of that client API type supported by the implementation, an EGL_BAD_ACCESS error is generated.
                If either draw or read are pbuffers created with eglCreatePbufferFromClientBuffer, and the underlying bound client API buffers are in use by the client API that created them, an EGL_BAD_ACCESS error is generated.

            Except for the first case all of these recoverable. This conversation is mostly used in winit & EglSurface, where compatible context and surfaces are build.
            */
            x @ MakeCurrentError(EGLError::BadAccess) => {
                GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
            }
            // BadSurface would result in a recreation in `eglSwapBuffers` -> recoverable
            x @ MakeCurrentError(EGLError::BadSurface) => {
                GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
            }
            /*
            From khronos docs:
                If the previous context of the calling thread has unflushed commands, and the previous surface is no longer valid, an EGL_BAD_CURRENT_SURFACE error is generated.

            This does not consern this or future `makeCurrent`-calls.
            */
            x @ MakeCurrentError(EGLError::BadCurrentSurface) => {
                GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
            }
            // the rest is either never happening or are unrecoverable
            x => GraphicsSwapBuffersError::ContextLost(Box::new(x)),
        }
    }
}

/// Error that might happen when binding an `EGLImage` to a GL texture
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum TextureCreationError {
    /// The given plane index is out of bounds
    #[error("This buffer is not managed by EGL")]
    PlaneIndexOutOfBounds,
    /// The OpenGL context has been lost and needs to be recreated.
    ///
    /// All the objects associated to it (textures, buffers, programs, etc.)
    /// need to be recreated from scratch.
    ///
    /// Operations will have no effect. Functions that read textures, buffers, etc.
    /// from OpenGL will return uninitialized data instead.
    ///
    /// A context loss usually happens on mobile devices when the user puts the
    /// application on sleep and wakes it up later. However any OpenGL implementation
    /// can theoretically lose the context at any time.
    #[error("The context has been lost, it needs to be recreated")]
    ContextLost,
    /// Required OpenGL Extension for texture creation is missing
    #[error("Required OpenGL Extension for texture creation is missing: {0}")]
    GLExtensionNotSupported(&'static str),
    /// Failed to bind the `EGLImage` to the given texture
    ///
    /// The given argument is the GL error code
    #[error("Failed to create EGLImages from the buffer (GL error code {0:x}")]
    TextureBindingFailed(u32),
}

/// Texture format types
#[repr(i32)]
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub enum Format {
    /// RGB format
    RGB = ffi::egl::TEXTURE_RGB as i32,
    /// RGB + alpha channel format
    RGBA = ffi::egl::TEXTURE_RGBA as i32,
    /// External format
    External = ffi::egl::TEXTURE_EXTERNAL_WL,
    /// 2-plane Y and UV format
    Y_UV = ffi::egl::TEXTURE_Y_UV_WL,
    /// 3-plane Y, U and V format
    Y_U_V = ffi::egl::TEXTURE_Y_U_V_WL,
    /// 2-plane Y and XUXV format
    Y_XUXV = ffi::egl::TEXTURE_Y_XUXV_WL,
}

impl Format {
    /// Amount of planes this format uses
    pub fn num_planes(&self) -> usize {
        match *self {
            Format::RGB | Format::RGBA | Format::External => 1,
            Format::Y_UV | Format::Y_XUXV => 2,
            Format::Y_U_V => 3,
        }
    }
}

/// Images of the EGL-based [`WlBuffer`].
#[cfg(feature = "wayland_frontend")]
pub struct EGLBuffer {
    display: Arc<EGLDisplayHandle>,
    /// Width in pixels
    pub width: u32,
    /// Height in pixels
    pub height: u32,
    /// If the y-axis is inverted or not
    pub y_inverted: bool,
    /// Format of these images
    pub format: Format,
    images: Vec<EGLImage>,
}

// Gles2 does not implement debug, so we have to impl Debug manually
#[cfg(feature = "wayland_frontend")]
impl fmt::Debug for EGLImages {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Point")
            .field("display", &self.display)
            .field("width", &self.width)
            .field("height", &self.height)
            .field("y_inverted", &self.y_inverted)
            .field("format", &self.format)
            .field("images", &self.images)
            .finish()
    }
}

#[cfg(feature = "wayland_frontend")]
impl EGLBuffer {
    /// Amount of planes of these `EGLImages`
    pub fn num_planes(&self) -> usize {
        self.format.num_planes()
    }

    /*
    /// Bind plane to an OpenGL texture id
    ///
    /// This does only temporarily modify the OpenGL state any changes are reverted before returning.
    /// The given `GLGraphicsBackend` must be the one belonging to the `tex_id` and will be the current
    /// context (and surface if applicable) after this function returns.
    ///
    /// # Safety
    ///
    /// The given `tex_id` needs to be a valid GL texture in the given context otherwise undefined behavior might occur.
    #[cfg(feature = "renderer_gl")]
    pub unsafe fn bind_to_texture(
        &self,
        plane: usize,
        tex_id: c_uint,
        backend: &dyn GLGraphicsBackend,
    ) -> ::std::result::Result<(), TextureCreationError> {
        // receive the list of extensions for *this* context
        backend
            .make_current()
            .map_err(|_| TextureCreationError::ContextLost)?;

        let egl_to_texture_support = {
            // the list of gl extensions supported by the context
            let data = CStr::from_ptr(self.gl.GetString(gl_ffi::EXTENSIONS) as *const _)
                .to_bytes()
                .to_vec();
            let list = String::from_utf8(data).unwrap();
            list.split(' ')
                .any(|s| s == "GL_OES_EGL_image" || s == "GL_OES_EGL_image_base")
        };
        if !egl_to_texture_support {
            return Err(TextureCreationError::GLExtensionNotSupported("GL_OES_EGL_image"));
        }

        let mut old_tex_id: i32 = 0;
        self.gl.GetIntegerv(gl_ffi::TEXTURE_BINDING_2D, &mut old_tex_id);
        self.gl.BindTexture(gl_ffi::TEXTURE_2D, tex_id);
        self.gl.EGLImageTargetTexture2DOES(
            gl_ffi::TEXTURE_2D,
            *self
                .images
                .get(plane)
                .ok_or(TextureCreationError::PlaneIndexOutOfBounds)?,
        );
        let res = match ffi::egl::GetError() as u32 {
            ffi::egl::SUCCESS => Ok(()),
            err => Err(TextureCreationError::TextureBindingFailed(err)),
        };
        self.gl.BindTexture(gl_ffi::TEXTURE_2D, old_tex_id as u32);
        res
    }
    */
}

#[cfg(feature = "wayland_frontend")]
impl Drop for EGLBuffer {
    fn drop(&mut self) {
        for image in self.images.drain(..) {
            // ignore result on drop
            unsafe {
                ffi::egl::DestroyImageKHR(**self.display, image);
            }
        }
    }
}
<commit_msg>egl: EGLBuffer add accessor for EGLImages, remove old code<commit_after>//! Common traits and types for egl rendering
//!
//! Large parts of this module are taken from
//! [glutin src/api/egl](https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/src/api/egl/)
//!
//! It therefore falls under
//! [glutin's Apache 2.0 license](https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/LICENSE)
//!
//! Wayland specific EGL functionality - EGL based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer)s.
//!
//! The types of this module can be used to initialize hardware acceleration rendering
//! based on EGL for clients as it may enabled usage of `EGLImage` based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer)s.
//!
//! To use it bind any backend implementing the [`EGLGraphicsBackend`](::backend::egl::EGLGraphicsBackend) trait, that shall do the
//! rendering (so pick a fast one), to the [`wayland_server::Display`] of your compositor.
//! Note only one backend may be bound to any [`Display`](wayland_server::Display) at any time.
//!
//! You may then use the resulting [`EGLDisplay`](::backend::egl::EGLDisplay) to receive [`EGLImages`](::backend::egl::EGLImages)
//! of an EGL-based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer) for rendering.

/*
#[cfg(feature = "renderer_gl")]
use crate::backend::graphics::{
    gl::{ffi as gl_ffi, GLGraphicsBackend},
    SwapBuffersError as GraphicsSwapBuffersError,
};
*/
use std::fmt;

pub mod context;
pub use self::context::EGLContext;
mod error;
pub use self::error::*;
use crate::backend::SwapBuffersError as GraphicsSwapBuffersError;

use nix::libc::c_void;

#[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)]
pub mod ffi;
use self::ffi::egl::types::EGLImage;

pub mod display;
pub mod native;
pub mod surface;
pub use self::display::EGLDisplay;
pub use self::surface::EGLSurface;

use self::display::EGLDisplayHandle;
use std::ffi::CString;
use std::sync::Arc;

/// Error that can happen on optional EGL features
#[derive(Debug, Clone, PartialEq)]
pub struct EglExtensionNotSupportedError(&'static [&'static str]);

impl fmt::Display for EglExtensionNotSupportedError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
        write!(
            formatter,
            "None of the following EGL extensions is supported by the underlying EGL implementation,
                     at least one is required: {:?}",
            self.0
        )
    }
}

impl ::std::error::Error for EglExtensionNotSupportedError {}

/// Returns the address of an OpenGL function.
///
/// Result is independent of displays and does not guarantee an extension is actually supported at runtime.
pub unsafe fn get_proc_address(symbol: &str) -> *const c_void {
    unsafe {
        let addr = CString::new(symbol.as_bytes()).unwrap();
        let addr = addr.as_ptr();
        ffi::egl::GetProcAddress(addr) as *const _
    }
}

/// Error that can occur when accessing an EGL buffer
#[cfg(feature = "wayland_frontend")]
#[derive(thiserror::Error)]
pub enum BufferAccessError {
    /// The corresponding Context is not alive anymore
    #[error("The corresponding context was lost")]
    ContextLost,
    /// This buffer is not managed by the EGL buffer
    #[error("This buffer is not managed by EGL. Err: {0:}")]
    NotManaged(#[source] EGLError),
    /// Failed to create `EGLImages` from the buffer
    #[error("Failed to create EGLImages from the buffer. Err: {0:}")]
    EGLImageCreationFailed(#[source] EGLError),
    /// The required EGL extension is not supported by the underlying EGL implementation
    #[error("{0}")]
    EglExtensionNotSupported(#[from] EglExtensionNotSupportedError),
    /// We currently do not support multi-planar buffers
    #[error("Multi-planar formats (like {0:?}) are unsupported")]
    UnsupportedMultiPlanarFormat(Format),
}

#[cfg(feature = "wayland_frontend")]
impl fmt::Debug for BufferAccessError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"),
            BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"),
            BufferAccessError::EGLImageCreationFailed(_) => {
                write!(formatter, "BufferAccessError::EGLImageCreationFailed")
            }
            BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err),
            BufferAccessError::UnsupportedMultiPlanarFormat(ref fmt) =>
                write!(formatter, "BufferAccessError::UnsupportedMultiPlanerFormat({:?})", fmt),
        }
    }
}

/// Error that can happen when swapping buffers.
#[derive(Debug, thiserror::Error)]
pub enum SwapBuffersError {
    /// EGL error during `eglSwapBuffers`
    #[error("{0:}")]
    EGLSwapBuffers(#[source] EGLError),
    /// EGL error during surface creation
    #[error("{0:}")]
    EGLCreateSurface(#[source] EGLError),
}

impl std::convert::From<SwapBuffersError> for GraphicsSwapBuffersError {
    fn from(value: SwapBuffersError) -> Self {
        match value {
            // bad surface is answered with a surface recreation in `swap_buffers`
            x @ SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface) => {
                GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
            }
            // the rest is either never happening or are unrecoverable
            x @ SwapBuffersError::EGLSwapBuffers(_) => GraphicsSwapBuffersError::ContextLost(Box::new(x)),
            x @ SwapBuffersError::EGLCreateSurface(_) => {
                GraphicsSwapBuffersError::ContextLost(Box::new(x))
            }
        }
    }
}

/// Error that can happen when making a context (and surface) current on the active thread.
#[derive(thiserror::Error, Debug)]
#[error("`eglMakeCurrent` failed: {0}")]
pub struct MakeCurrentError(#[from] EGLError);

impl From<MakeCurrentError> for GraphicsSwapBuffersError {
    fn from(err: MakeCurrentError) -> GraphicsSwapBuffersError {
        match err {
            /*
            From khronos docs:
                If draw or read are not compatible with context, then an EGL_BAD_MATCH error is generated.
                If context is current to some other thread, or if either draw or read are bound to contexts in another thread, an EGL_BAD_ACCESS error is generated.
                If binding context would exceed the number of current contexts of that client API type supported by the implementation, an EGL_BAD_ACCESS error is generated.
                If either draw or read are pbuffers created with eglCreatePbufferFromClientBuffer, and the underlying bound client API buffers are in use by the client API that created them, an EGL_BAD_ACCESS error is generated.

            Except for the first case all of these recoverable. This conversation is mostly used in winit & EglSurface, where compatible context and surfaces are build.
            */
            x @ MakeCurrentError(EGLError::BadAccess) => {
                GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
            }
            // BadSurface would result in a recreation in `eglSwapBuffers` -> recoverable
            x @ MakeCurrentError(EGLError::BadSurface) => {
                GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
            }
            /*
            From khronos docs:
                If the previous context of the calling thread has unflushed commands, and the previous surface is no longer valid, an EGL_BAD_CURRENT_SURFACE error is generated.

            This does not consern this or future `makeCurrent`-calls.
            */
            x @ MakeCurrentError(EGLError::BadCurrentSurface) => {
                GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
            }
            // the rest is either never happening or are unrecoverable
            x => GraphicsSwapBuffersError::ContextLost(Box::new(x)),
        }
    }
}

/// Error that might happen when binding an `EGLImage` to a GL texture
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum TextureCreationError {
    /// The given plane index is out of bounds
    #[error("This buffer is not managed by EGL")]
    PlaneIndexOutOfBounds,
    /// The OpenGL context has been lost and needs to be recreated.
    ///
    /// All the objects associated to it (textures, buffers, programs, etc.)
    /// need to be recreated from scratch.
    ///
    /// Operations will have no effect. Functions that read textures, buffers, etc.
    /// from OpenGL will return uninitialized data instead.
    ///
    /// A context loss usually happens on mobile devices when the user puts the
    /// application on sleep and wakes it up later. However any OpenGL implementation
    /// can theoretically lose the context at any time.
    #[error("The context has been lost, it needs to be recreated")]
    ContextLost,
    /// Required OpenGL Extension for texture creation is missing
    #[error("Required OpenGL Extension for texture creation is missing: {0}")]
    GLExtensionNotSupported(&'static str),
    /// Failed to bind the `EGLImage` to the given texture
    ///
    /// The given argument is the GL error code
    #[error("Failed to create EGLImages from the buffer (GL error code {0:x}")]
    TextureBindingFailed(u32),
}

/// Texture format types
#[repr(i32)]
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub enum Format {
    /// RGB format
    RGB = ffi::egl::TEXTURE_RGB as i32,
    /// RGB + alpha channel format
    RGBA = ffi::egl::TEXTURE_RGBA as i32,
    /// External format
    External = ffi::egl::TEXTURE_EXTERNAL_WL,
    /// 2-plane Y and UV format
    Y_UV = ffi::egl::TEXTURE_Y_UV_WL,
    /// 3-plane Y, U and V format
    Y_U_V = ffi::egl::TEXTURE_Y_U_V_WL,
    /// 2-plane Y and XUXV format
    Y_XUXV = ffi::egl::TEXTURE_Y_XUXV_WL,
}

impl Format {
    /// Amount of planes this format uses
    pub fn num_planes(&self) -> usize {
        match *self {
            Format::RGB | Format::RGBA | Format::External => 1,
            Format::Y_UV | Format::Y_XUXV => 2,
            Format::Y_U_V => 3,
        }
    }
}

/// Images of the EGL-based [`WlBuffer`].
#[cfg(feature = "wayland_frontend")]
pub struct EGLBuffer {
    display: Arc<EGLDisplayHandle>,
    /// Width in pixels
    pub width: u32,
    /// Height in pixels
    pub height: u32,
    /// If the y-axis is inverted or not
    pub y_inverted: bool,
    /// Format of these images
    pub format: Format,
    images: Vec<EGLImage>,
}

// Gles2 does not implement debug, so we have to impl Debug manually
#[cfg(feature = "wayland_frontend")]
impl fmt::Debug for EGLImages {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Point")
            .field("display", &self.display)
            .field("width", &self.width)
            .field("height", &self.height)
            .field("y_inverted", &self.y_inverted)
            .field("format", &self.format)
            .field("images", &self.images)
            .finish()
    }
}

#[cfg(feature = "wayland_frontend")]
impl EGLBuffer {
    /// Amount of planes of these `EGLImages`
    pub fn num_planes(&self) -> usize {
        self.format.num_planes()
    }

    pub fn image(&self, plane: usize) -> Option<EGLImage> {
        if plane > self.format.num_planes() {
            None
        } else {
            Some(self.images[plane])
        }
    }
}

#[cfg(feature = "wayland_frontend")]
impl Drop for EGLBuffer {
    fn drop(&mut self) {
        for image in self.images.drain(..) {
            // ignore result on drop
            unsafe {
                ffi::egl::DestroyImageKHR(**self.display, image);
            }
        }
    }
}
<|endoftext|>


<commit_before>use log::debug;
use std::fmt;
use std::io;
use std::io::Write;

use super::Backend;
use crate::buffer::Cell;
use crate::layout::Rect;
use crate::style;

pub struct TermionBackend<W>
where
    W: Write,
{
    stdout: W,
}

impl<W> TermionBackend<W>
where
    W: Write,
{
    pub fn new(stdout: W) -> TermionBackend<W> {
        TermionBackend { stdout }
    }
}

impl<W> Write for TermionBackend<W>
where
    W: Write,
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }
}

impl<W> Backend for TermionBackend<W>
where
    W: Write,
{
    /// Clears the entire screen and move the cursor to the top left of the screen
    fn clear(&mut self) -> io::Result<()> {
        write!(self.stdout, "{}", termion::clear::All)?;
        write!(self.stdout, "{}", termion::cursor::Goto(1, 1))?;
        self.stdout.flush()
    }

    /// Hides cursor
    fn hide_cursor(&mut self) -> io::Result<()> {
        write!(self.stdout, "{}", termion::cursor::Hide)?;
        self.stdout.flush()
    }

    /// Shows cursor
    fn show_cursor(&mut self) -> io::Result<()> {
        write!(self.stdout, "{}", termion::cursor::Show)?;
        self.stdout.flush()
    }

    /// Gets cursor position (0-based index)
    fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
        termion::cursor::DetectCursorPos::cursor_pos(&mut self.stdout).map(|(x, y)| (x - 1, y - 1))
    }

    /// Sets cursor position (0-based index)
    fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
        write!(self.stdout, "{}", termion::cursor::Goto(x + 1, y + 1))?;
        self.stdout.flush()
    }

    fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
    where
        I: Iterator<Item = (u16, u16, &'a Cell)>,
    {
        use std::fmt::Write;

        let mut string = String::with_capacity(content.size_hint().0 * 3);
        let mut style = style::Style::default();
        let mut last_y = 0;
        let mut last_x = 0;
        let mut inst = 0;
        for (x, y, cell) in content {
            if y != last_y || x != last_x + 1 || inst == 0 {
                write!(string, "{}", termion::cursor::Goto(x + 1, y + 1)).unwrap();
                inst += 1;
            }
            last_x = x;
            last_y = y;
            if cell.style.modifier != style.modifier {
                write!(
                    string,
                    "{}",
                    ModifierDiff {
                        from: style.modifier,
                        to: cell.style.modifier
                    }
                )
                .unwrap();
                style.modifier = cell.style.modifier;
                inst += 1;
            }
            if cell.style.fg != style.fg {
                write!(string, "{}", Fg(cell.style.fg)).unwrap();
                style.fg = cell.style.fg;
                inst += 1;
            }
            if cell.style.bg != style.bg {
                write!(string, "{}", Bg(cell.style.bg)).unwrap();
                style.bg = cell.style.bg;
                inst += 1;
            }
            string.push_str(&cell.symbol);
            inst += 1;
        }
        debug!("{} instructions outputed.", inst);
        write!(
            self.stdout,
            "{}{}{}{}",
            string,
            Fg(style::Color::Reset),
            Bg(style::Color::Reset),
            termion::style::Reset,
        )
    }

    /// Return the size of the terminal
    fn size(&self) -> io::Result<Rect> {
        let terminal = r#try!(termion::terminal_size());
        Ok(Rect::new(0, 0, terminal.0, terminal.1))
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }
}

struct Fg(style::Color);

struct Bg(style::Color);

struct ModifierDiff {
    from: style::Modifier,
    to: style::Modifier,
}

impl fmt::Display for Fg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use termion::color::Color;
        match self.0 {
            style::Color::Reset => termion::color::Reset.write_fg(f),
            style::Color::Black => termion::color::Black.write_fg(f),
            style::Color::Red => termion::color::Red.write_fg(f),
            style::Color::Green => termion::color::Green.write_fg(f),
            style::Color::Yellow => termion::color::Yellow.write_fg(f),
            style::Color::Blue => termion::color::Blue.write_fg(f),
            style::Color::Magenta => termion::color::Magenta.write_fg(f),
            style::Color::Cyan => termion::color::Cyan.write_fg(f),
            style::Color::Gray => termion::color::White.write_fg(f),
            style::Color::DarkGray => termion::color::LightBlack.write_fg(f),
            style::Color::LightRed => termion::color::LightRed.write_fg(f),
            style::Color::LightGreen => termion::color::LightGreen.write_fg(f),
            style::Color::LightBlue => termion::color::LightBlue.write_fg(f),
            style::Color::LightYellow => termion::color::LightYellow.write_fg(f),
            style::Color::LightMagenta => termion::color::LightMagenta.write_fg(f),
            style::Color::LightCyan => termion::color::LightCyan.write_fg(f),
            style::Color::White => termion::color::LightWhite.write_fg(f),
            style::Color::Indexed(i) => termion::color::AnsiValue(i).write_fg(f),
            style::Color::Rgb(r, g, b) => termion::color::Rgb(r, g, b).write_fg(f),
        }
    }
}
impl fmt::Display for Bg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use termion::color::Color;
        match self.0 {
            style::Color::Reset => termion::color::Reset.write_bg(f),
            style::Color::Black => termion::color::Black.write_bg(f),
            style::Color::Red => termion::color::Red.write_bg(f),
            style::Color::Green => termion::color::Green.write_bg(f),
            style::Color::Yellow => termion::color::Yellow.write_bg(f),
            style::Color::Blue => termion::color::Blue.write_bg(f),
            style::Color::Magenta => termion::color::Magenta.write_bg(f),
            style::Color::Cyan => termion::color::Cyan.write_bg(f),
            style::Color::Gray => termion::color::White.write_bg(f),
            style::Color::DarkGray => termion::color::LightBlack.write_bg(f),
            style::Color::LightRed => termion::color::LightRed.write_bg(f),
            style::Color::LightGreen => termion::color::LightGreen.write_bg(f),
            style::Color::LightBlue => termion::color::LightBlue.write_bg(f),
            style::Color::LightYellow => termion::color::LightYellow.write_bg(f),
            style::Color::LightMagenta => termion::color::LightMagenta.write_bg(f),
            style::Color::LightCyan => termion::color::LightCyan.write_bg(f),
            style::Color::White => termion::color::LightWhite.write_bg(f),
            style::Color::Indexed(i) => termion::color::AnsiValue(i).write_bg(f),
            style::Color::Rgb(r, g, b) => termion::color::Rgb(r, g, b).write_bg(f),
        }
    }
}

impl fmt::Display for ModifierDiff {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let remove = self.from - self.to;
        if remove.contains(style::Modifier::REVERSED) {
            write!(f, "{}", termion::style::NoInvert)?;
        }
        if remove.contains(style::Modifier::BOLD) {
            write!(f, "{}", termion::style::NoBold)?;
        }
        if remove.contains(style::Modifier::ITALIC) {
            write!(f, "{}", termion::style::NoItalic)?;
        }
        if remove.contains(style::Modifier::UNDERLINED) {
            write!(f, "{}", termion::style::NoUnderline)?;
        }
        if remove.contains(style::Modifier::DIM) {
            write!(f, "{}", termion::style::NoFaint)?;
        }
        if remove.contains(style::Modifier::CROSSED_OUT) {
            write!(f, "{}", termion::style::NoCrossedOut)?;
        }
        if remove.contains(style::Modifier::SLOW_BLINK)
            || remove.contains(style::Modifier::RAPID_BLINK)
        {
            write!(f, "{}", termion::style::NoBlink)?;
        }

        let add = self.to - self.from;
        if add.contains(style::Modifier::REVERSED) {
            write!(f, "{}", termion::style::Invert)?;
        }
        if add.contains(style::Modifier::BOLD) {
            write!(f, "{}", termion::style::Bold)?;
        }
        if add.contains(style::Modifier::ITALIC) {
            write!(f, "{}", termion::style::Italic)?;
        }
        if add.contains(style::Modifier::UNDERLINED) {
            write!(f, "{}", termion::style::Underline)?;
        }
        if add.contains(style::Modifier::DIM) {
            write!(f, "{}", termion::style::Faint)?;
        }
        if add.contains(style::Modifier::CROSSED_OUT) {
            write!(f, "{}", termion::style::CrossedOut)?;
        }
        if add.contains(style::Modifier::SLOW_BLINK) || add.contains(style::Modifier::RAPID_BLINK) {
            write!(f, "{}", termion::style::Blink)?;
        }

        Ok(())
    }
}
<commit_msg>Add workarounds for weird termion escape code handling<commit_after>use log::debug;
use std::fmt;
use std::io;
use std::io::Write;

use super::Backend;
use crate::buffer::Cell;
use crate::layout::Rect;
use crate::style;

pub struct TermionBackend<W>
where
    W: Write,
{
    stdout: W,
}

impl<W> TermionBackend<W>
where
    W: Write,
{
    pub fn new(stdout: W) -> TermionBackend<W> {
        TermionBackend { stdout }
    }
}

impl<W> Write for TermionBackend<W>
where
    W: Write,
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }
}

impl<W> Backend for TermionBackend<W>
where
    W: Write,
{
    /// Clears the entire screen and move the cursor to the top left of the screen
    fn clear(&mut self) -> io::Result<()> {
        write!(self.stdout, "{}", termion::clear::All)?;
        write!(self.stdout, "{}", termion::cursor::Goto(1, 1))?;
        self.stdout.flush()
    }

    /// Hides cursor
    fn hide_cursor(&mut self) -> io::Result<()> {
        write!(self.stdout, "{}", termion::cursor::Hide)?;
        self.stdout.flush()
    }

    /// Shows cursor
    fn show_cursor(&mut self) -> io::Result<()> {
        write!(self.stdout, "{}", termion::cursor::Show)?;
        self.stdout.flush()
    }

    /// Gets cursor position (0-based index)
    fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
        termion::cursor::DetectCursorPos::cursor_pos(&mut self.stdout).map(|(x, y)| (x - 1, y - 1))
    }

    /// Sets cursor position (0-based index)
    fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
        write!(self.stdout, "{}", termion::cursor::Goto(x + 1, y + 1))?;
        self.stdout.flush()
    }

    fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
    where
        I: Iterator<Item = (u16, u16, &'a Cell)>,
    {
        use std::fmt::Write;

        let mut string = String::with_capacity(content.size_hint().0 * 3);
        let mut style = style::Style::default();
        let mut last_y = 0;
        let mut last_x = 0;
        let mut inst = 0;
        for (x, y, cell) in content {
            if y != last_y || x != last_x + 1 || inst == 0 {
                write!(string, "{}", termion::cursor::Goto(x + 1, y + 1)).unwrap();
                inst += 1;
            }
            last_x = x;
            last_y = y;
            if cell.style.modifier != style.modifier {
                write!(
                    string,
                    "{}",
                    ModifierDiff {
                        from: style.modifier,
                        to: cell.style.modifier
                    }
                )
                .unwrap();
                style.modifier = cell.style.modifier;
                inst += 1;
            }
            if cell.style.fg != style.fg {
                write!(string, "{}", Fg(cell.style.fg)).unwrap();
                style.fg = cell.style.fg;
                inst += 1;
            }
            if cell.style.bg != style.bg {
                write!(string, "{}", Bg(cell.style.bg)).unwrap();
                style.bg = cell.style.bg;
                inst += 1;
            }
            string.push_str(&cell.symbol);
            inst += 1;
        }
        debug!("{} instructions outputed.", inst);
        write!(
            self.stdout,
            "{}{}{}{}",
            string,
            Fg(style::Color::Reset),
            Bg(style::Color::Reset),
            termion::style::Reset,
        )
    }

    /// Return the size of the terminal
    fn size(&self) -> io::Result<Rect> {
        let terminal = r#try!(termion::terminal_size());
        Ok(Rect::new(0, 0, terminal.0, terminal.1))
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }
}

struct Fg(style::Color);

struct Bg(style::Color);

struct ModifierDiff {
    from: style::Modifier,
    to: style::Modifier,
}

impl fmt::Display for Fg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use termion::color::Color;
        match self.0 {
            style::Color::Reset => termion::color::Reset.write_fg(f),
            style::Color::Black => termion::color::Black.write_fg(f),
            style::Color::Red => termion::color::Red.write_fg(f),
            style::Color::Green => termion::color::Green.write_fg(f),
            style::Color::Yellow => termion::color::Yellow.write_fg(f),
            style::Color::Blue => termion::color::Blue.write_fg(f),
            style::Color::Magenta => termion::color::Magenta.write_fg(f),
            style::Color::Cyan => termion::color::Cyan.write_fg(f),
            style::Color::Gray => termion::color::White.write_fg(f),
            style::Color::DarkGray => termion::color::LightBlack.write_fg(f),
            style::Color::LightRed => termion::color::LightRed.write_fg(f),
            style::Color::LightGreen => termion::color::LightGreen.write_fg(f),
            style::Color::LightBlue => termion::color::LightBlue.write_fg(f),
            style::Color::LightYellow => termion::color::LightYellow.write_fg(f),
            style::Color::LightMagenta => termion::color::LightMagenta.write_fg(f),
            style::Color::LightCyan => termion::color::LightCyan.write_fg(f),
            style::Color::White => termion::color::LightWhite.write_fg(f),
            style::Color::Indexed(i) => termion::color::AnsiValue(i).write_fg(f),
            style::Color::Rgb(r, g, b) => termion::color::Rgb(r, g, b).write_fg(f),
        }
    }
}
impl fmt::Display for Bg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use termion::color::Color;
        match self.0 {
            style::Color::Reset => termion::color::Reset.write_bg(f),
            style::Color::Black => termion::color::Black.write_bg(f),
            style::Color::Red => termion::color::Red.write_bg(f),
            style::Color::Green => termion::color::Green.write_bg(f),
            style::Color::Yellow => termion::color::Yellow.write_bg(f),
            style::Color::Blue => termion::color::Blue.write_bg(f),
            style::Color::Magenta => termion::color::Magenta.write_bg(f),
            style::Color::Cyan => termion::color::Cyan.write_bg(f),
            style::Color::Gray => termion::color::White.write_bg(f),
            style::Color::DarkGray => termion::color::LightBlack.write_bg(f),
            style::Color::LightRed => termion::color::LightRed.write_bg(f),
            style::Color::LightGreen => termion::color::LightGreen.write_bg(f),
            style::Color::LightBlue => termion::color::LightBlue.write_bg(f),
            style::Color::LightYellow => termion::color::LightYellow.write_bg(f),
            style::Color::LightMagenta => termion::color::LightMagenta.write_bg(f),
            style::Color::LightCyan => termion::color::LightCyan.write_bg(f),
            style::Color::White => termion::color::LightWhite.write_bg(f),
            style::Color::Indexed(i) => termion::color::AnsiValue(i).write_bg(f),
            style::Color::Rgb(r, g, b) => termion::color::Rgb(r, g, b).write_bg(f),
        }
    }
}

impl fmt::Display for ModifierDiff {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let remove = self.from - self.to;
        if remove.contains(style::Modifier::REVERSED) {
            write!(f, "{}", termion::style::NoInvert)?;
        }
        if remove.contains(style::Modifier::BOLD) {
            // XXX: the termion NoBold flag actually enables double-underline on ECMA-48 compliant
            // terminals, and NoFaint additionally disables bold... so we use this trick to get
            // the right semantics.
            write!(f, "{}", termion::style::NoFaint)?;

            if self.to.contains(style::Modifier::DIM) {
                write!(f, "{}", termion::style::Faint)?;
            }
        }
        if remove.contains(style::Modifier::ITALIC) {
            write!(f, "{}", termion::style::NoItalic)?;
        }
        if remove.contains(style::Modifier::UNDERLINED) {
            write!(f, "{}", termion::style::NoUnderline)?;
        }
        if remove.contains(style::Modifier::DIM) {
            write!(f, "{}", termion::style::NoFaint)?;

            // XXX: the NoFaint flag additionally disables bold as well, so we need to re-enable it
            // here if we want it.
            if self.to.contains(style::Modifier::BOLD) {
                write!(f, "{}", termion::style::Bold)?;
            }
        }
        if remove.contains(style::Modifier::CROSSED_OUT) {
            write!(f, "{}", termion::style::NoCrossedOut)?;
        }
        if remove.contains(style::Modifier::SLOW_BLINK)
            || remove.contains(style::Modifier::RAPID_BLINK)
        {
            write!(f, "{}", termion::style::NoBlink)?;
        }

        let add = self.to - self.from;
        if add.contains(style::Modifier::REVERSED) {
            write!(f, "{}", termion::style::Invert)?;
        }
        if add.contains(style::Modifier::BOLD) {
            write!(f, "{}", termion::style::Bold)?;
        }
        if add.contains(style::Modifier::ITALIC) {
            write!(f, "{}", termion::style::Italic)?;
        }
        if add.contains(style::Modifier::UNDERLINED) {
            write!(f, "{}", termion::style::Underline)?;
        }
        if add.contains(style::Modifier::DIM) {
            write!(f, "{}", termion::style::Faint)?;
        }
        if add.contains(style::Modifier::CROSSED_OUT) {
            write!(f, "{}", termion::style::CrossedOut)?;
        }
        if add.contains(style::Modifier::SLOW_BLINK) || add.contains(style::Modifier::RAPID_BLINK) {
            write!(f, "{}", termion::style::Blink)?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use bytes::{ Buf, BigEndian, BytesMut, IntoBuf };
use tokio::net::TcpStream;
use tokio::prelude::{ Async, AsyncRead, Poll, Stream };
use tokio::io;
use super::ClientMessage;

pub struct ClientCodec {
    stream: TcpStream,
    read_buffer: BytesMut,
//    write_buffer: BytesMut
}

impl ClientCodec {
    pub fn new(stream: TcpStream) -> Self {
        Self {
            stream,
            read_buffer: BytesMut::new(),
 //           write_buffer: BytesMut::new()
        }
    }

    fn read_next_packet(&mut self) -> Poll<(), io::Error> {
        loop {
            // This initial value of 1024 is totally just an educated guess. It should be reviewed at some later point.
            self.read_buffer.reserve(1024);

            let n = try_ready!(self.stream.read_buf(&mut self.read_buffer));

            println!("Got value from try_ready: {}", n);

            if n == 0 {
                return Ok(Async::Ready(()))
            }
        }
    }
}

impl Stream for ClientCodec {

    type Item = ClientMessage;
    type Error = io::Error;

    /// For each invocation of `poll` we try to read and retrieve a `ClientMessage`.
    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        let sock_closed = self.read_next_packet()?.is_ready();
        let mut read_bytes: usize = 0;

        println!("Read buffer: {:?}", self.read_buffer);

        const MSG_MNEMONIC_SIZE: usize = 2;
        const STR_LENGTH_SIZE: usize = 1;

        let mut buffer = self.read_buffer.clone();

        if buffer.len() >= MSG_MNEMONIC_SIZE {
            let msg_code = buffer.split_to(MSG_MNEMONIC_SIZE);

            read_bytes += MSG_MNEMONIC_SIZE;

            println!("Message code: #{:?}", msg_code);

            let result = match &msg_code[..] {
                b"he" => {
                    if buffer.len() >= STR_LENGTH_SIZE {
                        let msg_size = buffer.split_to(STR_LENGTH_SIZE);
                        read_bytes += STR_LENGTH_SIZE;

                        println!("Message size: {}", msg_size[0]);
                        let offset = msg_size[0] as usize - '0' as usize;

                        if buffer.len() >= offset {
                            let raw_name = buffer.split_to(offset);
                            read_bytes += offset;

                            println!("Found name: {:?}", raw_name);

                            Async::Ready(Some(ClientMessage::ClientHello { name: String::from_utf8(raw_name.as_ref().to_vec()).unwrap_or(String::from("Name error"))  }))
                        } else {
                            Async::NotReady
                        }
                    } else {
                        Async::NotReady
                    }
                },
                _ => Async::Ready(None)
            };

            match result {
                Async::Ready(_) => { self.read_buffer.split_to(read_bytes); },
                _ => ()
            }

            println!("Inner result: {:?}", result);

            return Ok(result)
        }

        if sock_closed {
            Ok(Async::Ready(None))
        } else {
            Ok(Async::NotReady)
        }
    }
}
<commit_msg>Began reorganising the client decoder code<commit_after>use bytes::{ Buf, BigEndian, BytesMut, IntoBuf };
use tokio::net::TcpStream;
use tokio::prelude::{ Async, AsyncRead, Poll, Stream };
use tokio::io;
use super::ClientMessage;

pub struct ClientCodec {
    stream: TcpStream,
    read_buffer: BytesMut,
//    write_buffer: BytesMut
}

impl ClientCodec {
    pub fn new(stream: TcpStream) -> Self {
        Self {
            stream,
            read_buffer: BytesMut::new(),
 //           write_buffer: BytesMut::new()
        }
    }

    fn read_next_packet(&mut self) -> Poll<(), io::Error> {
        loop {
            // This initial value of 1024 is totally just an educated guess. It should be reviewed at some later point.
            self.read_buffer.reserve(1024);

            let n = try_ready!(self.stream.read_buf(&mut self.read_buffer));

            println!("Got value from try_ready: {}", n);

            if n == 0 {
                return Ok(Async::Ready(()))
            }
        }
    }

    fn read_label(buffer: &mut BytesMut) -> Async<Option<String>> {
        const LABEL_LEN_SIZE: usize = 1;

        if buffer.len() >= LABEL_LEN_SIZE {
            let label_len = buffer.split_to(LABEL_LEN_SIZE)[0] as usize;

            if buffer.len() >= label_len {
                let raw_label = buffer.split_to(label_len);

                Async::Ready(Some(String::from_utf8(raw_label.as_ref().to_vec()).unwrap_or(String::from("[Label error]"))))
            } else {
                Async::NotReady
            }
        } else {
            Async::NotReady
        }
    }
}

impl Stream for ClientCodec {

    type Item = ClientMessage;
    type Error = io::Error;

    /// For each invocation of `poll` we try to read and retrieve a `ClientMessage`.
    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        let sock_closed = self.read_next_packet()?.is_ready();
        let mut read_bytes: usize = 0;

        println!("Read buffer: {:?}", self.read_buffer);

        const MSG_MNEMONIC_SIZE: usize = 2;
        const STR_LENGTH_SIZE: usize = 1;

        let mut buffer = self.read_buffer.clone();

        if buffer.len() >= MSG_MNEMONIC_SIZE {
            let msg_code = buffer.split_to(MSG_MNEMONIC_SIZE);

            read_bytes += MSG_MNEMONIC_SIZE;

            println!("Message code: #{:?}", msg_code);

            let result = match &msg_code[..] {
                b"la" => ClientCodec::read_label(&mut buffer).map(|label| label.map(|name| ClientMessage::ClientHello { name: name })),
                b"he" => {
                    if buffer.len() >= STR_LENGTH_SIZE {
                        let msg_size = buffer.split_to(STR_LENGTH_SIZE);
                        read_bytes += STR_LENGTH_SIZE;

                        println!("Message size: {}", msg_size[0]);
                        let offset = msg_size[0] as usize - '0' as usize;

                        if buffer.len() >= offset {
                            let raw_name = buffer.split_to(offset);
                            read_bytes += offset;

                            println!("Found name: {:?}", raw_name);

                            Async::Ready(Some(ClientMessage::ClientHello { name: String::from_utf8(raw_name.as_ref().to_vec()).unwrap_or(String::from("Name error"))  }))
                        } else {
                            Async::NotReady
                        }
                    } else {
                        Async::NotReady
                    }
                },
                _ => Async::Ready(None)
            };

            match result {
                Async::Ready(_) => { self.read_buffer.split_to(read_bytes); },
                _ => ()
            }

            println!("Inner result: {:?}", result);

            return Ok(result)
        }

        if sock_closed {
            Ok(Async::Ready(None))
        } else {
            Ok(Async::NotReady)
        }
    }
}
<|endoftext|>