<commit_before>use std::collections::HashMap;
use std::fs::{File, OpenOptions, remove_file};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

pub struct FileHandle {
    file: File,
    flag: i32,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        // The first two bits of the flag correspond to the access mode of the file in linux. This
        // is done this way because `O_RDONLY` is zero in several platforms.
        let access_mode = flag & 0b11;

        if access_mode == this.eval_libc_i32("O_RDONLY")? {
            options.read(true);
        } else if access_mode == this.eval_libc_i32("O_WRONLY")? {
            options.write(true);
        } else if access_mode == this.eval_libc_i32("O_RDWR")? {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // This flag is a noop for now because `std` already sets it.
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag);
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file, flag });
            fh.low
        });

        this.consume_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        arg_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`fcntl` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;

        if cmd == this.eval_libc_i32("F_SETFD")? {
            // This does not affect the file itself. Certain flags might require changing the file
            // or the way it is accessed somehow.
            let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
            // The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag.
            let fd_cloexec = this.eval_libc_i32("FD_CLOEXEC")?;
            if let Some(FileHandle { flag: old_flag, .. }) =
                this.machine.file_handler.handles.get_mut(&fd)
            {
                // Check that the only difference between the old flag and the current flag is
                // exactly the `FD_CLOEXEC` value.
                if flag ^ *old_flag == fd_cloexec {
                    *old_flag = flag;
                } else {
                    throw_unsup_format!("Unsupported arg {:#x} for `F_SETFD`", flag);
                }
            }
            Ok(0)
        } else if cmd == this.eval_libc_i32("F_GETFD")? {
            this.get_handle_and(fd, |handle| Ok(handle.flag))
        } else {
            throw_unsup_format!("Unsupported command {:#x}", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`close` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        this.remove_handle_and(fd, |handle, this| {
            this.consume_result(handle.file.sync_all().map(|_| 0i32))
        })
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`read` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Reading zero bytes should not change `buf`
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf_scalar = this.read_scalar(buf_op)?.not_undef()?;

        // Remove the file handle to avoid borrowing issues
        this.remove_handle_and(fd, |mut handle, this| {
            // Don't use `?` to avoid returning before reinserting the handle
            let bytes = this.force_ptr(buf_scalar).and_then(|buf| {
                this.memory_mut()
                    .get_mut(buf.alloc_id)?
                    .get_bytes_mut(tcx, buf, Size::from_bytes(count))
                    .map(|buffer| handle.file.read(buffer))
            });
            // Reinsert the file handle
            this.machine.file_handler.handles.insert(fd, handle);
            this.consume_result(bytes?.map(|bytes| bytes as i64))
        })
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`write` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Writing zero bytes should not change `buf`
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;

        this.remove_handle_and(fd, |mut handle, this| {
            let bytes = this.memory().get(buf.alloc_id).and_then(|alloc| {
                alloc
                    .get_bytes(tcx, buf, Size::from_bytes(count))
                    .map(|bytes| handle.file.write(bytes).map(|bytes| bytes as i64))
            });
            this.machine.file_handler.handles.insert(fd, handle);
            this.consume_result(bytes?)
        })
    }

    fn unlink( &mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`write` not available when isolation is enabled")
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;

        let result = remove_file(path).map(|_| 0);

        this.consume_result(result)
    }

    /// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
    /// using the `f` closure.
    ///
    /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
    /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn get_handle_and<F, T: From<i32>>(&mut self, fd: i32, f: F) -> InterpResult<'tcx, T>
    where
        F: Fn(&FileHandle) -> InterpResult<'tcx, T>,
    {
        let this = self.eval_context_mut();
        if let Some(handle) = this.machine.file_handler.handles.get(&fd) {
            f(handle)
        } else {
            let ebadf = this.eval_libc("EBADF")?;
            this.set_last_error(ebadf)?;
            Ok((-1).into())
        }
    }

    /// Helper function that removes a `FileHandle` and allows to manipulate it using the `f`
    /// closure. This function is quite useful when you need to modify a `FileHandle` but you need
    /// to modify `MiriEvalContext` at the same time, so you can modify the handle and reinsert it
    /// using `f`.
    ///
    /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
    /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn remove_handle_and<F, T: From<i32>>(&mut self, fd: i32, mut f: F) -> InterpResult<'tcx, T>
    where
        F: FnMut(FileHandle, &mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'tcx, T>,
    {
        let this = self.eval_context_mut();
        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            f(handle, this)
        } else {
            let ebadf = this.eval_libc("EBADF")?;
            this.set_last_error(ebadf)?;
            Ok((-1).into())
        }
    }

    /// Helper function that consumes an `std::io::Result<T>` and returns an
    /// `InterpResult<'tcx,T>::Ok` instead. It is expected that the result can be converted to an
    /// OS error using `std::io::Error::raw_os_error`.
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn consume_result<T: From<i32>>(
        &mut self,
        result: std::io::Result<T>,
    ) -> InterpResult<'tcx, T> {
        match result {
            Ok(ok) => Ok(ok),
            Err(e) => {
                self.eval_context_mut().consume_io_error(e)?;
                Ok((-1).into())
            }
        }
    }
}
<commit_msg>Remove F_SETFD command<commit_after>use std::collections::HashMap;
use std::fs::{File, OpenOptions, remove_file};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

pub struct FileHandle {
    file: File,
    flag: i32,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        // The first two bits of the flag correspond to the access mode of the file in linux. This
        // is done this way because `O_RDONLY` is zero in several platforms.
        let access_mode = flag & 0b11;

        if access_mode == this.eval_libc_i32("O_RDONLY")? {
            options.read(true);
        } else if access_mode == this.eval_libc_i32("O_WRONLY")? {
            options.write(true);
        } else if access_mode == this.eval_libc_i32("O_RDWR")? {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // This flag is a noop for now because `std` already sets it.
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag);
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file, flag });
            fh.low
        });

        this.consume_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`fcntl` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor
        if cmd == this.eval_libc_i32("F_GETFD")? {
            this.get_handle_and(fd, |handle| Ok(handle.flag))
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`close` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        this.remove_handle_and(fd, |handle, this| {
            this.consume_result(handle.file.sync_all().map(|_| 0i32))
        })
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`read` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Reading zero bytes should not change `buf`
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf_scalar = this.read_scalar(buf_op)?.not_undef()?;

        // Remove the file handle to avoid borrowing issues
        this.remove_handle_and(fd, |mut handle, this| {
            // Don't use `?` to avoid returning before reinserting the handle
            let bytes = this.force_ptr(buf_scalar).and_then(|buf| {
                this.memory_mut()
                    .get_mut(buf.alloc_id)?
                    .get_bytes_mut(tcx, buf, Size::from_bytes(count))
                    .map(|buffer| handle.file.read(buffer))
            });
            // Reinsert the file handle
            this.machine.file_handler.handles.insert(fd, handle);
            this.consume_result(bytes?.map(|bytes| bytes as i64))
        })
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`write` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Writing zero bytes should not change `buf`
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;

        this.remove_handle_and(fd, |mut handle, this| {
            let bytes = this.memory().get(buf.alloc_id).and_then(|alloc| {
                alloc
                    .get_bytes(tcx, buf, Size::from_bytes(count))
                    .map(|bytes| handle.file.write(bytes).map(|bytes| bytes as i64))
            });
            this.machine.file_handler.handles.insert(fd, handle);
            this.consume_result(bytes?)
        })
    }

    fn unlink( &mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`write` not available when isolation is enabled")
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;

        let result = remove_file(path).map(|_| 0);

        this.consume_result(result)
    }

    /// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
    /// using the `f` closure.
    ///
    /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
    /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn get_handle_and<F, T: From<i32>>(&mut self, fd: i32, f: F) -> InterpResult<'tcx, T>
    where
        F: Fn(&FileHandle) -> InterpResult<'tcx, T>,
    {
        let this = self.eval_context_mut();
        if let Some(handle) = this.machine.file_handler.handles.get(&fd) {
            f(handle)
        } else {
            let ebadf = this.eval_libc("EBADF")?;
            this.set_last_error(ebadf)?;
            Ok((-1).into())
        }
    }

    /// Helper function that removes a `FileHandle` and allows to manipulate it using the `f`
    /// closure. This function is quite useful when you need to modify a `FileHandle` but you need
    /// to modify `MiriEvalContext` at the same time, so you can modify the handle and reinsert it
    /// using `f`.
    ///
    /// If the `fd` file descriptor does not correspond to a file, this functions returns `Ok(-1)`
    /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn remove_handle_and<F, T: From<i32>>(&mut self, fd: i32, mut f: F) -> InterpResult<'tcx, T>
    where
        F: FnMut(FileHandle, &mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'tcx, T>,
    {
        let this = self.eval_context_mut();
        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            f(handle, this)
        } else {
            let ebadf = this.eval_libc("EBADF")?;
            this.set_last_error(ebadf)?;
            Ok((-1).into())
        }
    }

    /// Helper function that consumes an `std::io::Result<T>` and returns an
    /// `InterpResult<'tcx,T>::Ok` instead. It is expected that the result can be converted to an
    /// OS error using `std::io::Error::raw_os_error`.
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn consume_result<T: From<i32>>(
        &mut self,
        result: std::io::Result<T>,
    ) -> InterpResult<'tcx, T> {
        match result {
            Ok(ok) => Ok(ok),
            Err(e) => {
                self.eval_context_mut().consume_io_error(e)?;
                Ok((-1).into())
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

pub struct FileHandle {
    file: File,
    flag: i32,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        if flag != this.eval_libc_i32("O_RDONLY")? && flag != this.eval_libc_i32("O_CLOEXEC")? {
            throw_unsup_format!("Unsupported flag {:#x}", flag);
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;
        let fd = File::open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file, flag });
            fh.low
        });

        this.consume_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        arg_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;

        if cmd == this.eval_libc_i32("F_SETFD")? {
            // This does not affect the file itself. Certain flags might require changing the file
            // or the way it is accessed somehow.
            let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
            // The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag.
            let fd_cloexec = this.eval_libc_i32("FD_CLOEXEC")?;
            if let Some(FileHandle { flag: old_flag, .. }) =
                this.machine.file_handler.handles.get_mut(&fd)
            {
                if flag ^ *old_flag == fd_cloexec {
                    *old_flag = flag;
                } else {
                    throw_unsup_format!("Unsupported arg {:#x} for `F_SETFD`", flag);
                }
            }
            Ok(0)
        } else if cmd == this.eval_libc_i32("F_GETFD")? {
            this.get_handle_and(fd, |handle| Ok(handle.flag))
        } else {
            throw_unsup_format!("Unsupported command {:#x}", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        this.remove_handle_and(
            fd,
            |handle, this| this.consume_result(handle.file.sync_all().map(|_| 0i32)),
        )
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;

        // Remove the file handle to avoid borrowing issues
        this.remove_handle_and(
            fd,
            |mut handle, this| {
                let bytes = handle
                    .file
                    .read(this.memory_mut().get_mut(buf.alloc_id)?.get_bytes_mut(
                        tcx,
                        buf,
                        Size::from_bytes(count),
                    )?)
                    .map(|bytes| bytes as i64);
                // Reinsert the file handle
                this.machine.file_handler.handles.insert(fd, handle);
                this.consume_result(bytes)
            },
        )
    }

    fn get_handle_and<F, T: From<i32>>(&mut self, fd: i32, f: F) -> InterpResult<'tcx, T>
    where
        F: Fn(&FileHandle) -> InterpResult<'tcx, T>,
    {
        let this = self.eval_context_mut();
        if let Some(handle) = this.machine.file_handler.handles.get(&fd) {
            f(handle)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok((-1).into())
        }
    }

    fn remove_handle_and<F, T: From<i32>>(&mut self, fd: i32, mut f: F) -> InterpResult<'tcx, T>
    where
        F: FnMut(FileHandle, &mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'tcx, T>,
    {
        let this = self.eval_context_mut();
        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            f(handle, this)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok((-1).into())
        }
    }

    fn consume_result<T: From<i32>>(&mut self, result: std::io::Result<T>) -> InterpResult<'tcx, T> {
        match result {
            Ok(ok) => Ok(ok),
            Err(e) => {
                self.eval_context_mut().machine.last_error = e.raw_os_error().unwrap() as u32;
                Ok((-1).into())
            }
        }
    }
}
<commit_msg>Add docs for helper functions<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

pub struct FileHandle {
    file: File,
    flag: i32,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        if flag != this.eval_libc_i32("O_RDONLY")? && flag != this.eval_libc_i32("O_CLOEXEC")? {
            throw_unsup_format!("Unsupported flag {:#x}", flag);
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?;
        let fd = File::open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file, flag });
            fh.low
        });

        this.consume_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        arg_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;

        if cmd == this.eval_libc_i32("F_SETFD")? {
            // This does not affect the file itself. Certain flags might require changing the file
            // or the way it is accessed somehow.
            let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
            // The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag.
            let fd_cloexec = this.eval_libc_i32("FD_CLOEXEC")?;
            if let Some(FileHandle { flag: old_flag, .. }) =
                this.machine.file_handler.handles.get_mut(&fd)
            {
                if flag ^ *old_flag == fd_cloexec {
                    *old_flag = flag;
                } else {
                    throw_unsup_format!("Unsupported arg {:#x} for `F_SETFD`", flag);
                }
            }
            Ok(0)
        } else if cmd == this.eval_libc_i32("F_GETFD")? {
            this.get_handle_and(fd, |handle| Ok(handle.flag))
        } else {
            throw_unsup_format!("Unsupported command {:#x}", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        this.remove_handle_and(
            fd,
            |handle, this| this.consume_result(handle.file.sync_all().map(|_| 0i32)),
        )
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;

        // Remove the file handle to avoid borrowing issues
        this.remove_handle_and(
            fd,
            |mut handle, this| {
                let bytes = handle
                    .file
                    .read(this.memory_mut().get_mut(buf.alloc_id)?.get_bytes_mut(
                        tcx,
                        buf,
                        Size::from_bytes(count),
                    )?)
                    .map(|bytes| bytes as i64);
                // Reinsert the file handle
                this.machine.file_handler.handles.insert(fd, handle);
                this.consume_result(bytes)
            },
        )
    }

    /// Helper function that gets a `FileHandle` immutable reference and allows to manipulate it
    /// using `f`.
    ///
    /// If the `fd` file descriptor does not corresponds to a file, this functions returns `Ok(-1)`
    /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn get_handle_and<F, T: From<i32>>(&mut self, fd: i32, f: F) -> InterpResult<'tcx, T>
    where
        F: Fn(&FileHandle) -> InterpResult<'tcx, T>,
    {
        let this = self.eval_context_mut();
        if let Some(handle) = this.machine.file_handler.handles.get(&fd) {
            f(handle)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok((-1).into())
        }
    }

    /// Helper function that removes a `FileHandle` and allows to manipulate it using the `f`
    /// closure. This function is quite useful when you need to modify a `FileHandle` but you need
    /// to modify `MiriEvalContext` at the same time, so you can modify the handle and reinsert it
    /// using `f`.
    ///
    /// If the `fd` file descriptor does not corresponds to a file, this functions returns `Ok(-1)`
    /// and sets `Evaluator::last_error` to `libc::EBADF` (invalid file descriptor).
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn remove_handle_and<F, T: From<i32>>(&mut self, fd: i32, mut f: F) -> InterpResult<'tcx, T>
    where
        F: FnMut(FileHandle, &mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'tcx, T>,
    {
        let this = self.eval_context_mut();
        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            f(handle, this)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok((-1).into())
        }
    }

    /// Helper function that consumes an `std::io::Result<T>` and returns an
    /// `InterpResult<'tcx,T>::Ok` instead. It is expected that the result can be converted to an
    /// OS error using `std::io::Error::raw_os_error`.
    ///
    /// This function uses `T: From<i32>` instead of `i32` directly because some IO related
    /// functions return different integer types (like `read`, that returns an `i64`)
    fn consume_result<T: From<i32>>(&mut self, result: std::io::Result<T>) -> InterpResult<'tcx, T> {
        match result {
            Ok(ok) => Ok(ok),
            Err(e) => {
                self.eval_context_mut().machine.last_error = e.raw_os_error().unwrap() as u32;
                Ok((-1).into())
            }
        }
    }
}
<|endoftext|>


<commit_before>use gl;
use glutin;
use native::NativeTaskBuilder;
use std::sync::atomic::{AtomicUint, Relaxed};
use std::sync::{Arc, Mutex};
use std::task::TaskBuilder;

enum Message {
    EndFrame,
    Execute(proc(&gl::Gl, &mut GLState):Send),
}

pub struct Context {
    commands: Mutex<Sender<Message>>,
    events: Mutex<Receiver<glutin::Event>>,

    /// Dimensions of the frame buffer.
    dimensions: Arc<(AtomicUint, AtomicUint)>,
}

/// Represents the current OpenGL state.
/// The current state is passed to each function and can be freely updated.
pub struct GLState {
    /// Whether GL_BLEND is enabled
    pub enabled_blend: bool,

    /// Whether GL_CULL_FACE is enabled
    pub enabled_cull_face: bool,

    /// Whether GL_DEPTH_TEST is enabled
    pub enabled_depth_test: bool,

    /// Whether GL_DITHER is enabled
    pub enabled_dither: bool,

    /// Whether GL_POLYGON_OFFSET_FILL is enabled
    pub enabled_polygon_offset_fill: bool,

    /// Whether GL_SAMPLE_ALPHA_TO_COVERAGE is enabled
    pub enabled_sample_alpha_to_coverage: bool,

    /// Whether GL_SAMPLE_COVERAGE is enabled
    pub enabled_sample_coverage: bool,

    /// Whether GL_SCISSOR_TEST is enabled
    pub enabled_scissor_test: bool,

    /// Whether GL_STENCIL_TEST is enabled
    pub enabled_stencil_test: bool,

    // The latest value passed to `glUseProgram`.
    pub program: gl::types::GLuint,

    // The latest value passed to `glClearColor`.
    pub clear_color: (gl::types::GLclampf, gl::types::GLclampf,
                      gl::types::GLclampf, gl::types::GLclampf),

    // The latest value passed to `glClearDepthf`.
    pub clear_depth: gl::types::GLclampf,

    // The latest value passed to `glClearStencil`.
    pub clear_stencil: gl::types::GLint,

    /// The latest buffer bound to `GL_ARRAY_BUFFER`.
    pub array_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_ELEMENT_ARRAY_BUFFER`.
    pub element_array_buffer_binding: Option<gl::types::GLuint>,

    /// The latest framebuffer bound to `GL_READ_FRAMEBUFFER`.
    pub read_framebuffer: Option<gl::types::GLuint>,

    /// The latest framebuffer bound to `GL_DRAW_FRAMEBUFFER`.
    pub draw_framebuffer: Option<gl::types::GLuint>,

    /// The latest values passed to `glBlendFunc`.
    pub blend_func: (gl::types::GLenum, gl::types::GLenum),

    /// The latest value passed to `glDepthFunc`.
    pub depth_func: gl::types::GLenum,

    /// The latest values passed to `glViewport`.
    pub viewport: (gl::types::GLint, gl::types::GLint, gl::types::GLsizei, gl::types::GLsizei),

    /// The latest value passed to `glLineWidth`.
    pub line_width: gl::types::GLfloat,

    /// The latest value passed to `glCullFace`.
    pub cull_face: gl::types::GLenum,
}

impl GLState {
    /// Builds the `GLState` corresponding to the default GL values.
    fn new_defaults(viewport: (gl::types::GLint, gl::types::GLint, gl::types::GLsizei, gl::types::GLsizei)) -> GLState {
        GLState {
            enabled_blend: false,
            enabled_cull_face: false,
            enabled_depth_test: false,
            enabled_dither: false,
            enabled_polygon_offset_fill: false,
            enabled_sample_alpha_to_coverage: false,
            enabled_sample_coverage: false,
            enabled_scissor_test: false,
            enabled_stencil_test: false,

            program: 0,
            clear_color: (0.0, 0.0, 0.0, 0.0),
            clear_depth: 1.0,
            clear_stencil: 0,
            array_buffer_binding: None,
            element_array_buffer_binding: None,
            read_framebuffer: None,
            draw_framebuffer: None,
            depth_func: gl::LESS,
            blend_func: (0, 0),     // no default specified
            viewport: viewport,
            line_width: 1.0,
            cull_face: gl::BACK,
        }
    }
}

impl Context {
    pub fn new_from_window(window: glutin::Window) -> Context {
        let (tx_events, rx_events) = channel();
        let (tx_commands, rx_commands) = channel();

        let dimensions = Arc::new((AtomicUint::new(800), AtomicUint::new(600)));

        let context = Context {
            commands: Mutex::new(tx_commands),
            events: Mutex::new(rx_events),
            dimensions: dimensions.clone(),
        };

        TaskBuilder::new().native().spawn(proc() {
            unsafe { window.make_current(); }

            let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));

            // building the GLState and modifying to GL state to match it
            let mut gl_state = {
                let viewport = {
                    let dim = window.get_inner_size().unwrap();
                    dimensions.0.store(dim.0, Relaxed);
                    dimensions.1.store(dim.1, Relaxed);
                    (0, 0, dim.0 as gl::types::GLsizei, dim.1 as gl::types::GLsizei)
                };

                GLState::new_defaults(viewport)
            };

            'main: loop {
                // processing commands
                loop {
                    match rx_commands.recv_opt() {
                        Ok(EndFrame) => break,
                        Ok(Execute(cmd)) => cmd(&gl, &mut gl_state),
                        Err(_) => break 'main
                    }
                }

                // swapping
                window.swap_buffers();

                // getting events
                for event in window.poll_events() {
                    // calling `glViewport` if the window has been resized
                    if let &glutin::Resized(width, height) = &event {
                        dimensions.0.store(width, Relaxed);
                        dimensions.1.store(height, Relaxed);

                        if gl_state.viewport != (0, 0, width as gl::types::GLsizei,
                                                 height as gl::types::GLsizei)
                        {
                            gl.Viewport(0, 0, width as gl::types::GLsizei,
                                height as gl::types::GLsizei);
                            gl_state.viewport = (0, 0, width as gl::types::GLsizei,
                                height as gl::types::GLsizei);
                        }
                    }

                    // sending the event outside
                    if tx_events.send_opt(event.clone()).is_err() {
                        break 'main;
                    }
                }
            }
        });

        context
    }

    pub fn new_from_headless(window: glutin::HeadlessContext) -> Context {
        let (_, rx_events) = channel();
        let (tx_commands, rx_commands) = channel();

        // TODO: fixme
        let dimensions = Arc::new((AtomicUint::new(800), AtomicUint::new(600)));

        let context = Context {
            commands: Mutex::new(tx_commands),
            events: Mutex::new(rx_events),
            dimensions: dimensions,
        };

        TaskBuilder::new().native().spawn(proc() {
            unsafe { window.make_current(); }

            let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));

            // building the GLState
            let mut gl_state = GLState::new_defaults((0, 0, 0, 0));    // FIXME:

            loop {
                match rx_commands.recv_opt() {
                    Ok(Execute(cmd)) => cmd(&gl, &mut gl_state),
                    Ok(EndFrame) => (),     // ignoring buffer swapping
                    Err(_) => break
                }
            }
        });

        context
    }

    pub fn get_framebuffer_dimensions(&self) -> (uint, uint) {
        (
            self.dimensions.0.load(Relaxed),
            self.dimensions.1.load(Relaxed),
        )
    }

    pub fn exec(&self, f: proc(&gl::Gl, &mut GLState): Send) {
        self.commands.lock().send(Execute(f));
    }

    pub fn swap_buffers(&self) {
        self.commands.lock().send(EndFrame);
    }

    pub fn recv(&self) -> Vec<glutin::Event> {
        let events = self.events.lock();

        let mut result = Vec::new();
        loop {
            match events.try_recv() {
                Ok(ev) => result.push(ev),
                Err(_) => break
            }
        }
        result
    }
}
<commit_msg>Add call to glFlush when swapping buffers<commit_after>use gl;
use glutin;
use native::NativeTaskBuilder;
use std::sync::atomic::{AtomicUint, Relaxed};
use std::sync::{Arc, Mutex};
use std::task::TaskBuilder;

enum Message {
    EndFrame,
    Execute(proc(&gl::Gl, &mut GLState):Send),
}

pub struct Context {
    commands: Mutex<Sender<Message>>,
    events: Mutex<Receiver<glutin::Event>>,

    /// Dimensions of the frame buffer.
    dimensions: Arc<(AtomicUint, AtomicUint)>,
}

/// Represents the current OpenGL state.
/// The current state is passed to each function and can be freely updated.
pub struct GLState {
    /// Whether GL_BLEND is enabled
    pub enabled_blend: bool,

    /// Whether GL_CULL_FACE is enabled
    pub enabled_cull_face: bool,

    /// Whether GL_DEPTH_TEST is enabled
    pub enabled_depth_test: bool,

    /// Whether GL_DITHER is enabled
    pub enabled_dither: bool,

    /// Whether GL_POLYGON_OFFSET_FILL is enabled
    pub enabled_polygon_offset_fill: bool,

    /// Whether GL_SAMPLE_ALPHA_TO_COVERAGE is enabled
    pub enabled_sample_alpha_to_coverage: bool,

    /// Whether GL_SAMPLE_COVERAGE is enabled
    pub enabled_sample_coverage: bool,

    /// Whether GL_SCISSOR_TEST is enabled
    pub enabled_scissor_test: bool,

    /// Whether GL_STENCIL_TEST is enabled
    pub enabled_stencil_test: bool,

    // The latest value passed to `glUseProgram`.
    pub program: gl::types::GLuint,

    // The latest value passed to `glClearColor`.
    pub clear_color: (gl::types::GLclampf, gl::types::GLclampf,
                      gl::types::GLclampf, gl::types::GLclampf),

    // The latest value passed to `glClearDepthf`.
    pub clear_depth: gl::types::GLclampf,

    // The latest value passed to `glClearStencil`.
    pub clear_stencil: gl::types::GLint,

    /// The latest buffer bound to `GL_ARRAY_BUFFER`.
    pub array_buffer_binding: Option<gl::types::GLuint>,

    /// The latest buffer bound to `GL_ELEMENT_ARRAY_BUFFER`.
    pub element_array_buffer_binding: Option<gl::types::GLuint>,

    /// The latest framebuffer bound to `GL_READ_FRAMEBUFFER`.
    pub read_framebuffer: Option<gl::types::GLuint>,

    /// The latest framebuffer bound to `GL_DRAW_FRAMEBUFFER`.
    pub draw_framebuffer: Option<gl::types::GLuint>,

    /// The latest values passed to `glBlendFunc`.
    pub blend_func: (gl::types::GLenum, gl::types::GLenum),

    /// The latest value passed to `glDepthFunc`.
    pub depth_func: gl::types::GLenum,

    /// The latest values passed to `glViewport`.
    pub viewport: (gl::types::GLint, gl::types::GLint, gl::types::GLsizei, gl::types::GLsizei),

    /// The latest value passed to `glLineWidth`.
    pub line_width: gl::types::GLfloat,

    /// The latest value passed to `glCullFace`.
    pub cull_face: gl::types::GLenum,
}

impl GLState {
    /// Builds the `GLState` corresponding to the default GL values.
    fn new_defaults(viewport: (gl::types::GLint, gl::types::GLint, gl::types::GLsizei, gl::types::GLsizei)) -> GLState {
        GLState {
            enabled_blend: false,
            enabled_cull_face: false,
            enabled_depth_test: false,
            enabled_dither: false,
            enabled_polygon_offset_fill: false,
            enabled_sample_alpha_to_coverage: false,
            enabled_sample_coverage: false,
            enabled_scissor_test: false,
            enabled_stencil_test: false,

            program: 0,
            clear_color: (0.0, 0.0, 0.0, 0.0),
            clear_depth: 1.0,
            clear_stencil: 0,
            array_buffer_binding: None,
            element_array_buffer_binding: None,
            read_framebuffer: None,
            draw_framebuffer: None,
            depth_func: gl::LESS,
            blend_func: (0, 0),     // no default specified
            viewport: viewport,
            line_width: 1.0,
            cull_face: gl::BACK,
        }
    }
}

impl Context {
    pub fn new_from_window(window: glutin::Window) -> Context {
        let (tx_events, rx_events) = channel();
        let (tx_commands, rx_commands) = channel();

        let dimensions = Arc::new((AtomicUint::new(800), AtomicUint::new(600)));

        let context = Context {
            commands: Mutex::new(tx_commands),
            events: Mutex::new(rx_events),
            dimensions: dimensions.clone(),
        };

        TaskBuilder::new().native().spawn(proc() {
            unsafe { window.make_current(); }

            let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));

            // building the GLState and modifying to GL state to match it
            let mut gl_state = {
                let viewport = {
                    let dim = window.get_inner_size().unwrap();
                    dimensions.0.store(dim.0, Relaxed);
                    dimensions.1.store(dim.1, Relaxed);
                    (0, 0, dim.0 as gl::types::GLsizei, dim.1 as gl::types::GLsizei)
                };

                GLState::new_defaults(viewport)
            };

            'main: loop {
                // processing commands
                loop {
                    match rx_commands.recv_opt() {
                        Ok(EndFrame) => break,
                        Ok(Execute(cmd)) => cmd(&gl, &mut gl_state),
                        Err(_) => break 'main
                    }
                }

                // this is necessary on Windows 8, or nothing is being displayed
                gl.Flush();

                // swapping
                window.swap_buffers();

                // getting events
                for event in window.poll_events() {
                    // calling `glViewport` if the window has been resized
                    if let &glutin::Resized(width, height) = &event {
                        dimensions.0.store(width, Relaxed);
                        dimensions.1.store(height, Relaxed);

                        if gl_state.viewport != (0, 0, width as gl::types::GLsizei,
                                                 height as gl::types::GLsizei)
                        {
                            gl.Viewport(0, 0, width as gl::types::GLsizei,
                                height as gl::types::GLsizei);
                            gl_state.viewport = (0, 0, width as gl::types::GLsizei,
                                height as gl::types::GLsizei);
                        }
                    }

                    // sending the event outside
                    if tx_events.send_opt(event.clone()).is_err() {
                        break 'main;
                    }
                }
            }
        });

        context
    }

    pub fn new_from_headless(window: glutin::HeadlessContext) -> Context {
        let (_, rx_events) = channel();
        let (tx_commands, rx_commands) = channel();

        // TODO: fixme
        let dimensions = Arc::new((AtomicUint::new(800), AtomicUint::new(600)));

        let context = Context {
            commands: Mutex::new(tx_commands),
            events: Mutex::new(rx_events),
            dimensions: dimensions,
        };

        TaskBuilder::new().native().spawn(proc() {
            unsafe { window.make_current(); }

            let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));

            // building the GLState
            let mut gl_state = GLState::new_defaults((0, 0, 0, 0));    // FIXME:

            loop {
                match rx_commands.recv_opt() {
                    Ok(Execute(cmd)) => cmd(&gl, &mut gl_state),
                    Ok(EndFrame) => (),     // ignoring buffer swapping
                    Err(_) => break
                }
            }
        });

        context
    }

    pub fn get_framebuffer_dimensions(&self) -> (uint, uint) {
        (
            self.dimensions.0.load(Relaxed),
            self.dimensions.1.load(Relaxed),
        )
    }

    pub fn exec(&self, f: proc(&gl::Gl, &mut GLState): Send) {
        self.commands.lock().send(Execute(f));
    }

    pub fn swap_buffers(&self) {
        self.commands.lock().send(EndFrame);
    }

    pub fn recv(&self) -> Vec<glutin::Event> {
        let events = self.events.lock();

        let mut result = Vec::new();
        loop {
            match events.try_recv() {
                Ok(ev) => result.push(ev),
                Err(_) => break
            }
        }
        result
    }
}
<|endoftext|>


<commit_before>use crate::{ffi, AsRaw, Device, Event, FromRaw};
use std::{
    ffi::{CStr, CString},
    io::{Error as IoError, Result as IoResult},
    iter::Iterator,
    mem,
    os::unix::io::{AsRawFd, RawFd},
    path::Path,
    rc::Rc,
};
#[cfg(feature = "udev")]
use udev::ffi as udev;

/// libinput does not open file descriptors to devices directly,
/// instead `open_restricted` and `close_restricted` are called for
/// each path that must be opened.
///
/// Implementations are of course permitted to just use `open` and
/// `close` respectively, but doing so would require root permissions
/// to open devices. This interface provides an api agnostic way to
/// use ConsoleKit or similar endpoints to open devices without
/// direct priviledge escalation.
pub trait LibinputInterface {
    /// Open the device at the given path with the flags provided and
    /// return the fd.
    ///
    /// ## Paramater
    /// - `path` - The device path to open
    /// - `flags` Flags as defined by open(2)
    ///
    /// ## Returns
    /// The file descriptor, or a negative errno on failure.
    fn open_restricted(&mut self, path: &Path, flags: i32) -> Result<RawFd, i32>;

    /// Close the file descriptor.
    ///
    /// ## Parameter
    /// `fd` - The file descriptor to close
    fn close_restricted(&mut self, fd: RawFd);
}

unsafe extern "C" fn open_restricted<I: LibinputInterface + 'static>(
    path: *const libc::c_char,
    flags: libc::c_int,
    user_data: *mut libc::c_void,
) -> libc::c_int {
    use std::borrow::Cow;

    if let Some(interface) = (user_data as *mut I).as_mut() {
        let path_str = CStr::from_ptr(path).to_string_lossy();
        let res = match path_str {
            Cow::Borrowed(string) => interface.open_restricted(Path::new(string), flags),
            Cow::Owned(string) => interface.open_restricted(Path::new(&string), flags),
        };
        match res {
            Ok(fd) => fd,
            Err(errno) => {
                if errno > 0 {
                    -errno
                } else {
                    errno
                }
            }
        }
    } else {
        -1
    }
}

unsafe extern "C" fn close_restricted<I: LibinputInterface + 'static>(
    fd: libc::c_int,
    user_data: *mut libc::c_void,
) {
    if let Some(interface) = (user_data as *mut I).as_mut() {
        interface.close_restricted(fd)
    }
}

/// Libinput context
///
/// Contexts can be used to track input devices and receive events from them.
/// You can use either `new_from_udev` to create a context tracking all devices on a specific seat,
/// or use `new_from_path` to track input devices manually.
///
/// Either way you then have to use `dispatch()` and `next()` (provided by the `Iterator` trait) to
/// receive events.
pub struct Libinput {
    ffi: *mut ffi::libinput,
    _interface: Option<Rc<Box<dyn LibinputInterface + 'static>>>,
}

impl ::std::fmt::Debug for Libinput {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "Libinput @{:p}", self.as_raw())
    }
}

impl AsRaw<ffi::libinput> for Libinput {
    fn as_raw(&self) -> *const ffi::libinput {
        self.ffi as *const _
    }
}

impl Clone for Libinput {
    fn clone(&self) -> Self {
        Libinput {
            ffi: unsafe { ffi::libinput_ref(self.as_raw_mut()) },
            _interface: self._interface.clone(),
        }
    }
}

impl Drop for Libinput {
    fn drop(&mut self) {
        unsafe {
            ffi::libinput_unref(self.ffi);
        }
    }
}

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

impl Eq for Libinput {}

impl ::std::hash::Hash for Libinput {
    fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
        self.as_raw().hash(state);
    }
}

impl Iterator for Libinput {
    type Item = Event;
    fn next(&mut self) -> Option<Self::Item> {
        let ptr = unsafe { ffi::libinput_get_event(self.as_raw_mut()) };
        if ptr.is_null() {
            None
        } else {
            unsafe { Some(Event::from_raw(ptr, self)) }
        }
    }
}

impl Libinput {
    /// Create a new libinput context using a udev context.
    ///
    /// This context is inactive until `udev_assign_seat` is called.
    ///
    /// ## Arguments
    ///
    /// - interface - A `LibinputInterface` providing functions to open and close devices.
    /// - userdata - Optionally some userdata attached to the newly created context (see [`Userdata`](./trait.Userdata.html))
    /// - udev_context - Raw pointer to a valid udev context.
    ///
    /// # Safety
    ///
    /// This function is unsafe, because there is no way to verify that `udev_context` is indeed a valid udev context or even points to valid memory.
    #[cfg(feature = "udev")]
    pub fn new_with_udev<I: LibinputInterface + 'static>(interface: I) -> Libinput {
        let mut boxed_userdata = Box::new(interface);
        let boxed_interface = Box::new(ffi::libinput_interface {
            open_restricted: Some(open_restricted::<I>),
            close_restricted: Some(close_restricted::<I>),
        });

        let context = unsafe {
            let udev = udev::udev_new();
            let libinput = ffi::libinput_udev_create_context(
                &*boxed_interface as *const _,
                &mut *boxed_userdata as *mut I as *mut libc::c_void,
                udev as *mut input_sys::udev,
            );
            udev::udev_unref(udev);
            Libinput {
                ffi: libinput,
                _interface: Some(Rc::new(
                    boxed_userdata as Box<dyn LibinputInterface + 'static>,
                )),
            }
        };

        mem::forget(boxed_interface);

        context
    }

    /// Create a new libinput context that requires the caller to manually add or remove devices.
    ///
    /// The returned context is active, but will not yield any events
    /// until at least one device is added.
    ///
    /// Devices can be added and removed by calling `path_add_device` and `path_remove_device` respectively.
    ///
    /// ## Arguments
    ///
    /// - interface - A `LibinputInterface` providing functions to open and close devices.
    /// - userdata - Optionally some userdata attached to the newly created context (see [`Userdata`](./trait.Userdata.html))
    ///
    pub fn new_from_path<I: 'static + LibinputInterface>(interface: I) -> Libinput {
        let mut boxed_userdata = Box::new(interface);
        let boxed_interface = Box::new(ffi::libinput_interface {
            open_restricted: Some(open_restricted::<I>),
            close_restricted: Some(close_restricted::<I>),
        });

        let context = Libinput {
            ffi: unsafe {
                ffi::libinput_path_create_context(
                    &*boxed_interface as *const _,
                    &mut *boxed_userdata as *mut I as *mut libc::c_void,
                )
            },
            _interface: Some(Rc::new(
                boxed_userdata as Box<dyn LibinputInterface + 'static>,
            )),
        };

        mem::forget(boxed_interface);

        context
    }

    /// Add a device to a libinput context initialized with
    /// `new_from_context`.
    ///
    /// If successful, the device will be added to the internal list
    /// and re-opened on `resume`. The device can be removed with
    /// `path_remove_device()`.
    ///
    /// If the device was successfully initialized, it is returned.
    ///
    /// ## Warning
    ///
    /// It is an application bug to call this function on a context
    /// initialized with `new_from_udev`.
    pub fn path_add_device(&mut self, path: &str) -> Option<Device> {
        let path = CString::new(path).expect("Device Path contained a null-byte");
        unsafe {
            let ptr = ffi::libinput_path_add_device(self.as_raw_mut(), path.as_ptr());
            if ptr.is_null() {
                None
            } else {
                Some(Device::from_raw(ptr, self))
            }
        }
    }

    /// Remove a device from a libinput context initialized with
    /// `new_from_path` and added to such a context with
    /// `path_add_device`.
    ///
    /// Events already processed from this input device are kept in
    /// the queue, the `DeviceRemovedEvent` event marks the end of
    /// events for this device.
    ///
    /// ## Warning
    ///
    /// It is an application bug to call this function on a context
    /// initialized with `new_from_udev`.
    pub fn path_remove_device(&mut self, device: Device) {
        unsafe { ffi::libinput_path_remove_device(device.as_raw_mut()) }
    }

    /// Assign a seat to this libinput context.
    ///
    /// New devices or the removal of existing devices will appear as
    /// events during `dispatch`.
    ///
    /// `udev_assign_seat` succeeds even if no input devices are
    /// currently available on this seat, or if devices are available
    /// but fail to open in `LibinputInterface::open_restricted`.
    ///
    /// Devices that do not have the minimum capabilities to be
    /// recognized as pointer, keyboard or touch device are ignored. /// Such devices and those that failed to open ignored until the
    /// next call to `resume`.
    ///
    /// ## Warning
    ///
    /// This function may only be called once per context.
    #[cfg(feature = "udev")]
    pub fn udev_assign_seat(&mut self, seat_id: &str) -> Result<(), ()> {
        let id = CString::new(seat_id).expect("Seat Id contained a null-byte");
        unsafe {
            match ffi::libinput_udev_assign_seat(self.as_raw_mut(), id.as_ptr()) {
                0 => Ok(()),
                -1 => Err(()),
                _ => unreachable!(),
            }
        }
    }

    ffi_func!(
    /// Suspend monitoring for new devices and close existing
    /// devices.
    ///
    /// This closes all open devices and terminates libinput but
    /// does keep the context valid to be resumed with `resume`.
    pub fn suspend, ffi::libinput_suspend, ());

    /// Resume a suspended libinput context.
    ///
    /// This re-enables device monitoring and adds existing devices.
    pub fn resume(&mut self) -> Result<(), ()> {
        unsafe {
            match ffi::libinput_resume(self.as_raw_mut()) {
                0 => Ok(()),
                -1 => Err(()),
                _ => unreachable!(),
            }
        }
    }

    /// Main event dispatchment function.
    ///
    /// Reads events of the file descriptors and processes them
    /// internally. Use `next` or any other function provided by the
    /// `Iterator` trait to retrieve the events until `None` is
    /// returned.
    ///
    /// Dispatching does not necessarily queue libinput events. This
    /// function should be called immediately once data is available
    /// on the file descriptor returned by `fd`. libinput has a number
    /// of timing-sensitive features (e.g. tap-to-click), any delay in
    /// calling `dispatch` may prevent these features from working
    /// correctly.
    pub fn dispatch(&mut self) -> IoResult<()> {
        unsafe {
            match ffi::libinput_dispatch(self.as_raw_mut()) {
                0 => Ok(()),
                x if x < 0 => Err(IoError::from_raw_os_error(x)),
                _ => unreachable!(),
            }
        }
    }

    /// libinput keeps a single file descriptor for all events.
    ///
    /// Call into `dispatch` if any events become available on this fd.
    ///
    /// The most simple variant to check for available bytes is to use
    /// `nix::poll`:
    ///
    /// ```
    /// # extern crate libc;
    /// # extern crate nix;
    /// #
    /// # use std::fs::{File, OpenOptions};
    /// # use std::os::unix::{fs::OpenOptionsExt, io::{RawFd, FromRawFd, IntoRawFd, AsRawFd}};
    /// # use std::path::Path;
    /// # use libc::{O_RDONLY, O_RDWR, O_WRONLY};
    /// #
    /// use input::{Libinput, LibinputInterface};
    /// use nix::poll::{poll, PollFlags, PollFd};
    ///
    /// # struct Interface;
    /// #
    /// # impl LibinputInterface for Interface {
    /// #     fn open_restricted(&mut self, path: &Path, flags: i32) -> Result<RawFd, i32> {
    /// #         OpenOptions::new()
    /// #             .custom_flags(flags)
    /// #             .read((flags & O_RDONLY != 0) | (flags & O_RDWR != 0))
    /// #             .write((flags & O_WRONLY != 0) | (flags & O_RDWR != 0))
    /// #             .open(path)
    /// #             .map(|file| file.into_raw_fd())
    /// #             .map_err(|err| err.raw_os_error().unwrap())
    /// #     }
    /// #     fn close_restricted(&mut self, fd: RawFd) {
    /// #         unsafe {
    /// #             File::from_raw_fd(fd);
    /// #         }
    /// #     }
    /// # }
    /// #
    /// # // Preventing infinite execution (in particular on CI)
    /// # std::thread::spawn(|| {
    /// #     std::thread::sleep(std::time::Duration::from_secs(5));
    /// #     std::process::exit(0);
    /// # });
    /// #
    /// let mut input = Libinput::new_with_udev(Interface);
    /// input.udev_assign_seat("seat0").unwrap();
    ///
    /// let pollfd = PollFd::new(input.as_raw_fd(), PollFlags::POLLIN);
    /// while poll(&mut [pollfd], -1).is_ok() {
    ///     input.dispatch().unwrap();
    ///     for event in &mut input {
    ///         // do some processing...
    ///     }
    /// }
    /// ```
    ///
    /// For more complex operations you may wish to use other approches
    /// as event loops e.g. in the `wayland-server` or the `tokio`
    /// crates to wait for data to become available on this file
    /// descriptor.
    ///
    /// # Safety
    ///
    /// See [`AsRawFd`]
    #[deprecated(since = "0.4.1", note = "Use the provided AsRawFd implementation")]
    pub unsafe fn fd(&self) -> RawFd {
        ffi::libinput_get_fd(self.as_raw_mut())
    }

    /// Create a new instance of this type from a raw pointer.
    ///
    /// ## Warning
    ///
    /// If you make use of [`Userdata`](./trait.Userdata.html) make sure you use the correct types
    /// to allow receiving the set userdata. When dealing with raw pointers initialized by other
    /// libraries this must be done extra carefully to select a correct representation.
    ///
    /// If unsure using `()` is always a safe option..
    ///
    /// # Safety
    ///
    /// If the pointer is pointing to a different struct, invalid memory or `NULL` the returned
    /// struct may panic on use or cause other undefined behavior.
    pub unsafe fn from_raw(ffi: *mut ffi::libinput) -> Self {
        Libinput {
            ffi: ffi::libinput_ref(ffi),
            _interface: None,
        }
    }
}

impl AsRawFd for Libinput {
    fn as_raw_fd(&self) -> RawFd {
        unsafe { ffi::libinput_get_fd(self.as_raw_mut()) }
    }
}
<commit_msg>Fixed double wrapping of interface<commit_after>use crate::{ffi, AsRaw, Device, Event, FromRaw};
use std::{
    ffi::{CStr, CString},
    io::{Error as IoError, Result as IoResult},
    iter::Iterator,
    os::unix::io::{AsRawFd, RawFd},
    path::Path,
    rc::Rc,
};
#[cfg(feature = "udev")]
use udev::ffi as udev;

/// libinput does not open file descriptors to devices directly,
/// instead `open_restricted` and `close_restricted` are called for
/// each path that must be opened.
///
/// Implementations are of course permitted to just use `open` and
/// `close` respectively, but doing so would require root permissions
/// to open devices. This interface provides an api agnostic way to
/// use ConsoleKit or similar endpoints to open devices without
/// direct priviledge escalation.
pub trait LibinputInterface {
    /// Open the device at the given path with the flags provided and
    /// return the fd.
    ///
    /// ## Paramater
    /// - `path` - The device path to open
    /// - `flags` Flags as defined by open(2)
    ///
    /// ## Returns
    /// The file descriptor, or a negative errno on failure.
    fn open_restricted(&mut self, path: &Path, flags: i32) -> Result<RawFd, i32>;

    /// Close the file descriptor.
    ///
    /// ## Parameter
    /// `fd` - The file descriptor to close
    fn close_restricted(&mut self, fd: RawFd);
}

unsafe extern "C" fn open_restricted<I: LibinputInterface + 'static>(
    path: *const libc::c_char,
    flags: libc::c_int,
    user_data: *mut libc::c_void,
) -> libc::c_int {
    use std::borrow::Cow;

    if let Some(interface) = (user_data as *mut I).as_mut() {
        let path_str = CStr::from_ptr(path).to_string_lossy();
        let res = match path_str {
            Cow::Borrowed(string) => interface.open_restricted(Path::new(string), flags),
            Cow::Owned(string) => interface.open_restricted(Path::new(&string), flags),
        };
        match res {
            Ok(fd) => fd,
            Err(errno) => {
                if errno > 0 {
                    -errno
                } else {
                    errno
                }
            }
        }
    } else {
        -1
    }
}

unsafe extern "C" fn close_restricted<I: LibinputInterface + 'static>(
    fd: libc::c_int,
    user_data: *mut libc::c_void,
) {
    if let Some(interface) = (user_data as *mut I).as_mut() {
        interface.close_restricted(fd)
    }
}

/// Libinput context
///
/// Contexts can be used to track input devices and receive events from them.
/// You can use either `new_from_udev` to create a context tracking all devices on a specific seat,
/// or use `new_from_path` to track input devices manually.
///
/// Either way you then have to use `dispatch()` and `next()` (provided by the `Iterator` trait) to
/// receive events.
pub struct Libinput {
    ffi: *mut ffi::libinput,
    _interface: Option<Rc<dyn LibinputInterface + 'static>>,
}

impl ::std::fmt::Debug for Libinput {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "Libinput @{:p}", self.as_raw())
    }
}

impl AsRaw<ffi::libinput> for Libinput {
    fn as_raw(&self) -> *const ffi::libinput {
        self.ffi as *const _
    }
}

impl Clone for Libinput {
    fn clone(&self) -> Self {
        Libinput {
            ffi: unsafe { ffi::libinput_ref(self.as_raw_mut()) },
            _interface: self._interface.clone(),
        }
    }
}

impl Drop for Libinput {
    fn drop(&mut self) {
        unsafe {
            ffi::libinput_unref(self.ffi);
        }
    }
}

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

impl Eq for Libinput {}

impl ::std::hash::Hash for Libinput {
    fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
        self.as_raw().hash(state);
    }
}

impl Iterator for Libinput {
    type Item = Event;
    fn next(&mut self) -> Option<Self::Item> {
        let ptr = unsafe { ffi::libinput_get_event(self.as_raw_mut()) };
        if ptr.is_null() {
            None
        } else {
            unsafe { Some(Event::from_raw(ptr, self)) }
        }
    }
}

impl Libinput {
    /// Create a new libinput context using a udev context.
    ///
    /// This context is inactive until `udev_assign_seat` is called.
    ///
    /// ## Arguments
    ///
    /// - interface - A `LibinputInterface` providing functions to open and close devices.
    /// - userdata - Optionally some userdata attached to the newly created context (see [`Userdata`](./trait.Userdata.html))
    /// - udev_context - Raw pointer to a valid udev context.
    ///
    /// # Safety
    ///
    /// This function is unsafe, because there is no way to verify that `udev_context` is indeed a valid udev context or even points to valid memory.
    #[cfg(feature = "udev")]
    pub fn new_with_udev<I: LibinputInterface + 'static>(interface: I) -> Libinput {
        let boxed_userdata = Rc::new(interface);
        let boxed_interface = Box::new(ffi::libinput_interface {
            open_restricted: Some(open_restricted::<I>),
            close_restricted: Some(close_restricted::<I>),
        });

        let context = unsafe {
            let udev = udev::udev_new();
            let libinput = ffi::libinput_udev_create_context(
                Box::into_raw(boxed_interface),
                Rc::as_ptr(&boxed_userdata) as *mut _,
                udev as *mut input_sys::udev,
            );
            udev::udev_unref(udev);
            Libinput {
                ffi: libinput,
                _interface: Some(boxed_userdata as Rc<dyn LibinputInterface>),
            }
        };

        context
    }

    /// Create a new libinput context that requires the caller to manually add or remove devices.
    ///
    /// The returned context is active, but will not yield any events
    /// until at least one device is added.
    ///
    /// Devices can be added and removed by calling `path_add_device` and `path_remove_device` respectively.
    ///
    /// ## Arguments
    ///
    /// - interface - A `LibinputInterface` providing functions to open and close devices.
    /// - userdata - Optionally some userdata attached to the newly created context (see [`Userdata`](./trait.Userdata.html))
    ///
    pub fn new_from_path<I: 'static + LibinputInterface>(interface: I) -> Libinput {
        let boxed_userdata = Rc::new(interface);
        let boxed_interface = Box::new(ffi::libinput_interface {
            open_restricted: Some(open_restricted::<I>),
            close_restricted: Some(close_restricted::<I>),
        });

        let context = Libinput {
            ffi: unsafe {
                ffi::libinput_path_create_context(
                    Box::into_raw(boxed_interface),
                    Rc::as_ptr(&boxed_userdata) as *mut _,
                )
            },
            _interface: Some(boxed_userdata as Rc<dyn LibinputInterface>),
        };

        context
    }

    /// Add a device to a libinput context initialized with
    /// `new_from_context`.
    ///
    /// If successful, the device will be added to the internal list
    /// and re-opened on `resume`. The device can be removed with
    /// `path_remove_device()`.
    ///
    /// If the device was successfully initialized, it is returned.
    ///
    /// ## Warning
    ///
    /// It is an application bug to call this function on a context
    /// initialized with `new_from_udev`.
    pub fn path_add_device(&mut self, path: &str) -> Option<Device> {
        let path = CString::new(path).expect("Device Path contained a null-byte");
        unsafe {
            let ptr = ffi::libinput_path_add_device(self.as_raw_mut(), path.as_ptr());
            if ptr.is_null() {
                None
            } else {
                Some(Device::from_raw(ptr, self))
            }
        }
    }

    /// Remove a device from a libinput context initialized with
    /// `new_from_path` and added to such a context with
    /// `path_add_device`.
    ///
    /// Events already processed from this input device are kept in
    /// the queue, the `DeviceRemovedEvent` event marks the end of
    /// events for this device.
    ///
    /// ## Warning
    ///
    /// It is an application bug to call this function on a context
    /// initialized with `new_from_udev`.
    pub fn path_remove_device(&mut self, device: Device) {
        unsafe { ffi::libinput_path_remove_device(device.as_raw_mut()) }
    }

    /// Assign a seat to this libinput context.
    ///
    /// New devices or the removal of existing devices will appear as
    /// events during `dispatch`.
    ///
    /// `udev_assign_seat` succeeds even if no input devices are
    /// currently available on this seat, or if devices are available
    /// but fail to open in `LibinputInterface::open_restricted`.
    ///
    /// Devices that do not have the minimum capabilities to be
    /// recognized as pointer, keyboard or touch device are ignored. /// Such devices and those that failed to open ignored until the
    /// next call to `resume`.
    ///
    /// ## Warning
    ///
    /// This function may only be called once per context.
    #[cfg(feature = "udev")]
    pub fn udev_assign_seat(&mut self, seat_id: &str) -> Result<(), ()> {
        let id = CString::new(seat_id).expect("Seat Id contained a null-byte");
        unsafe {
            match ffi::libinput_udev_assign_seat(self.as_raw_mut(), id.as_ptr()) {
                0 => Ok(()),
                -1 => Err(()),
                _ => unreachable!(),
            }
        }
    }

    ffi_func!(
    /// Suspend monitoring for new devices and close existing
    /// devices.
    ///
    /// This closes all open devices and terminates libinput but
    /// does keep the context valid to be resumed with `resume`.
    pub fn suspend, ffi::libinput_suspend, ());

    /// Resume a suspended libinput context.
    ///
    /// This re-enables device monitoring and adds existing devices.
    pub fn resume(&mut self) -> Result<(), ()> {
        unsafe {
            match ffi::libinput_resume(self.as_raw_mut()) {
                0 => Ok(()),
                -1 => Err(()),
                _ => unreachable!(),
            }
        }
    }

    /// Main event dispatchment function.
    ///
    /// Reads events of the file descriptors and processes them
    /// internally. Use `next` or any other function provided by the
    /// `Iterator` trait to retrieve the events until `None` is
    /// returned.
    ///
    /// Dispatching does not necessarily queue libinput events. This
    /// function should be called immediately once data is available
    /// on the file descriptor returned by `fd`. libinput has a number
    /// of timing-sensitive features (e.g. tap-to-click), any delay in
    /// calling `dispatch` may prevent these features from working
    /// correctly.
    pub fn dispatch(&mut self) -> IoResult<()> {
        unsafe {
            match ffi::libinput_dispatch(self.as_raw_mut()) {
                0 => Ok(()),
                x if x < 0 => Err(IoError::from_raw_os_error(x)),
                _ => unreachable!(),
            }
        }
    }

    /// libinput keeps a single file descriptor for all events.
    ///
    /// Call into `dispatch` if any events become available on this fd.
    ///
    /// The most simple variant to check for available bytes is to use
    /// `nix::poll`:
    ///
    /// ```
    /// # extern crate libc;
    /// # extern crate nix;
    /// #
    /// # use std::fs::{File, OpenOptions};
    /// # use std::os::unix::{fs::OpenOptionsExt, io::{RawFd, FromRawFd, IntoRawFd, AsRawFd}};
    /// # use std::path::Path;
    /// # use libc::{O_RDONLY, O_RDWR, O_WRONLY};
    /// #
    /// use input::{Libinput, LibinputInterface};
    /// use nix::poll::{poll, PollFlags, PollFd};
    ///
    /// # struct Interface;
    /// #
    /// # impl LibinputInterface for Interface {
    /// #     fn open_restricted(&mut self, path: &Path, flags: i32) -> Result<RawFd, i32> {
    /// #         OpenOptions::new()
    /// #             .custom_flags(flags)
    /// #             .read((flags & O_RDONLY != 0) | (flags & O_RDWR != 0))
    /// #             .write((flags & O_WRONLY != 0) | (flags & O_RDWR != 0))
    /// #             .open(path)
    /// #             .map(|file| file.into_raw_fd())
    /// #             .map_err(|err| err.raw_os_error().unwrap())
    /// #     }
    /// #     fn close_restricted(&mut self, fd: RawFd) {
    /// #         unsafe {
    /// #             File::from_raw_fd(fd);
    /// #         }
    /// #     }
    /// # }
    /// #
    /// # // Preventing infinite execution (in particular on CI)
    /// # std::thread::spawn(|| {
    /// #     std::thread::sleep(std::time::Duration::from_secs(5));
    /// #     std::process::exit(0);
    /// # });
    /// #
    /// let mut input = Libinput::new_with_udev(Interface);
    /// input.udev_assign_seat("seat0").unwrap();
    ///
    /// let pollfd = PollFd::new(input.as_raw_fd(), PollFlags::POLLIN);
    /// while poll(&mut [pollfd], -1).is_ok() {
    ///     input.dispatch().unwrap();
    ///     for event in &mut input {
    ///         // do some processing...
    ///     }
    /// }
    /// ```
    ///
    /// For more complex operations you may wish to use other approches
    /// as event loops e.g. in the `wayland-server` or the `tokio`
    /// crates to wait for data to become available on this file
    /// descriptor.
    ///
    /// # Safety
    ///
    /// See [`AsRawFd`]
    #[deprecated(since = "0.4.1", note = "Use the provided AsRawFd implementation")]
    pub unsafe fn fd(&self) -> RawFd {
        ffi::libinput_get_fd(self.as_raw_mut())
    }

    /// Create a new instance of this type from a raw pointer.
    ///
    /// ## Warning
    ///
    /// If you make use of [`Userdata`](./trait.Userdata.html) make sure you use the correct types
    /// to allow receiving the set userdata. When dealing with raw pointers initialized by other
    /// libraries this must be done extra carefully to select a correct representation.
    ///
    /// If unsure using `()` is always a safe option..
    ///
    /// # Safety
    ///
    /// If the pointer is pointing to a different struct, invalid memory or `NULL` the returned
    /// struct may panic on use or cause other undefined behavior.
    pub unsafe fn from_raw(ffi: *mut ffi::libinput) -> Self {
        Libinput {
            ffi: ffi::libinput_ref(ffi),
            _interface: None,
        }
    }
}

impl AsRawFd for Libinput {
    fn as_raw_fd(&self) -> RawFd {
        unsafe { ffi::libinput_get_fd(self.as_raw_mut()) }
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use super::*;

#[derive(Debug, Clone)]
#[doc(hidden)]
pub struct Context {
    // TODO file from config should be in here
    config: RunningConfig,
    /// Periodically flushes dirty data. We keep this in an
    /// Arc separate from the PageCache below to separate
    /// "high-level" references from Db, Tree etc... from
    /// "low-level" references like background threads.
    /// When the last high-level reference is dropped, it
    /// should trigger all background threads to clean
    /// up synchronously.
    #[cfg(any(windows, target_os = "linux", target_os = "macos"))]
    pub(crate) flusher: Arc<Mutex<Option<flusher::Flusher>>>,
    #[doc(hidden)]
    pub pagecache: Arc<PageCache>,
}

impl std::ops::Deref for Context {
    type Target = RunningConfig;

    fn deref(&self) -> &RunningConfig {
        &self.config
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        loop {
            match self.pagecache.flush() {
                Ok(0) => return,
                Ok(_) => continue,
                Err(e) => {
                    error!(
                        "failed to flush underlying \
                         pagecache during drop: {:?}",
                        e
                    );
                    return;
                }
            }
        }
    }
}

impl Context {
    pub(crate) fn start(config: RunningConfig) -> Result<Self> {
        trace!("starting context");

        let pagecache = Arc::new(PageCache::start(config.clone())?);

        Ok(Self {
            config,
            pagecache,
            #[cfg(any(windows, target_os = "linux", target_os = "macos"))]
            flusher: Arc::new(parking_lot::Mutex::new(None)),
        })
    }

    /// Returns `true` if the database was
    /// recovered from a previous process.
    /// Note that database state is only
    /// guaranteed to be present up to the
    /// last call to `flush`! Otherwise state
    /// is synced to disk periodically if the
    /// `sync_every_ms` configuration option
    /// is set to `Some(number_of_ms_between_syncs)`
    /// or if the IO buffer gets filled to
    /// capacity before being rotated.
    pub fn was_recovered(&self) -> bool {
        self.pagecache.was_recovered()
    }

    /// Generate a monotonic ID. Not guaranteed to be
    /// contiguous. Written to disk every `idgen_persist_interval`
    /// operations, followed by a blocking flush. During recovery, we
    /// take the last recovered generated ID and add 2x
    /// the `idgen_persist_interval` to it. While persisting, if the
    /// previous persisted counter wasn't synced to disk yet, we will do
    /// a blocking flush to fsync the latest counter, ensuring
    /// that we will never give out the same counter twice.
    pub fn generate_id(&self) -> Result<u64> {
        self.pagecache.generate_id()
    }

    pub(crate) fn pin_log(&self, guard: &Guard) -> Result<RecoveryGuard<'_>> {
        self.pagecache.pin_log(guard)
    }
}
<commit_msg>Enable the async flusher for the BSDs<commit_after>use std::sync::Arc;

use super::*;

#[derive(Debug, Clone)]
#[doc(hidden)]
pub struct Context {
    // TODO file from config should be in here
    config: RunningConfig,
    /// Periodically flushes dirty data. We keep this in an
    /// Arc separate from the PageCache below to separate
    /// "high-level" references from Db, Tree etc... from
    /// "low-level" references like background threads.
    /// When the last high-level reference is dropped, it
    /// should trigger all background threads to clean
    /// up synchronously.
    #[cfg(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    ))]
    pub(crate) flusher: Arc<Mutex<Option<flusher::Flusher>>>,
    #[doc(hidden)]
    pub pagecache: Arc<PageCache>,
}

impl std::ops::Deref for Context {
    type Target = RunningConfig;

    fn deref(&self) -> &RunningConfig {
        &self.config
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        loop {
            match self.pagecache.flush() {
                Ok(0) => return,
                Ok(_) => continue,
                Err(e) => {
                    error!(
                        "failed to flush underlying \
                         pagecache during drop: {:?}",
                        e
                    );
                    return;
                }
            }
        }
    }
}

impl Context {
    pub(crate) fn start(config: RunningConfig) -> Result<Self> {
        trace!("starting context");

        let pagecache = Arc::new(PageCache::start(config.clone())?);

        Ok(Self {
            config,
            pagecache,
            #[cfg(any(windows, target_os = "linux", target_os = "macos"))]
            flusher: Arc::new(parking_lot::Mutex::new(None)),
        })
    }

    /// Returns `true` if the database was
    /// recovered from a previous process.
    /// Note that database state is only
    /// guaranteed to be present up to the
    /// last call to `flush`! Otherwise state
    /// is synced to disk periodically if the
    /// `sync_every_ms` configuration option
    /// is set to `Some(number_of_ms_between_syncs)`
    /// or if the IO buffer gets filled to
    /// capacity before being rotated.
    pub fn was_recovered(&self) -> bool {
        self.pagecache.was_recovered()
    }

    /// Generate a monotonic ID. Not guaranteed to be
    /// contiguous. Written to disk every `idgen_persist_interval`
    /// operations, followed by a blocking flush. During recovery, we
    /// take the last recovered generated ID and add 2x
    /// the `idgen_persist_interval` to it. While persisting, if the
    /// previous persisted counter wasn't synced to disk yet, we will do
    /// a blocking flush to fsync the latest counter, ensuring
    /// that we will never give out the same counter twice.
    pub fn generate_id(&self) -> Result<u64> {
        self.pagecache.generate_id()
    }

    pub(crate) fn pin_log(&self, guard: &Guard) -> Result<RecoveryGuard<'_>> {
        self.pagecache.pin_log(guard)
    }
}
<|endoftext|>


<commit_before>#![allow(missing_docs)]
//! These are inner traits to support methods overloading for the `Client`

use error::Error;
use output::Output;
use playlist::Playlist;
use song::{self, Id, Song};
use std::collections::BTreeMap;
use std::ops::{Range, RangeFrom, RangeFull, RangeTo};

use time::Duration;

#[doc(hidden)]
pub trait FromMap: Sized {
    fn from_map(map: BTreeMap<String, String>) -> Result<Self, Error>;
}

#[doc(hidden)]
pub trait FromIter: Sized {
    fn from_iter<I: Iterator<Item = Result<(String, String), Error>>>(iter: I) -> Result<Self, Error>;
}

impl<T: FromIter> FromMap for T {
    fn from_map(map: BTreeMap<String, String>) -> Result<Self, Error> {
        FromIter::from_iter(map.into_iter().map(Ok))
    }
}

// Playlist name polymorphisms {{{
pub trait ToPlaylistName {
    fn to_name(&self) -> &str;
}

impl ToPlaylistName for Playlist {
    fn to_name(&self) -> &str {
        &*self.name
    }
}

impl<'a> ToPlaylistName for &'a Playlist {
    fn to_name(&self) -> &str {
        &*self.name
    }
}

impl<'a> ToPlaylistName for &'a String {
    fn to_name(&self) -> &str {
        &*self
    }
}

impl<'a> ToPlaylistName for &'a str {
    fn to_name(&self) -> &str {
        *self
    }
}

impl ToPlaylistName for str {
    fn to_name(&self) -> &str {
        self
    }
}

impl ToPlaylistName for String {
    fn to_name(&self) -> &str {
        &*self
    }
}
// }}}

// Seconds polymorphisms {{{
pub trait ToSeconds {
    fn to_seconds(self) -> f64;
}

impl ToSeconds for i64 {
    fn to_seconds(self) -> f64 {
        self as f64
    }
}

impl ToSeconds for f64 {
    fn to_seconds(self) -> f64 {
        self
    }
}

impl ToSeconds for Duration {
    fn to_seconds(self) -> f64 {
        self.num_milliseconds() as f64 / 1000.0
    }
}
// }}}

// Queue place polymorphisms {{{

pub trait IsId {
    fn is_id() -> bool {
        false
    }
}

pub trait ToQueueRangeOrPlace: IsId {
    fn to_range(self) -> String;
}

pub trait ToQueueRange {
    fn to_range(self) -> String;
}

impl<T: ToQueuePlace> ToQueueRangeOrPlace for T {
    fn to_range(self) -> String {
        format!("{}", self.to_place())
    }
}

impl ToQueueRange for Range<u32> {
    fn to_range(self) -> String {
        format!("{}:{}", self.start, self.end)
    }
}

impl ToQueueRangeOrPlace for Range<u32> {
    fn to_range(self) -> String {
        ToQueueRange::to_range(self)
    }
}

impl ToQueueRange for RangeTo<u32> {
    fn to_range(self) -> String {
        format!(":{}", self.end)
    }
}

impl ToQueueRangeOrPlace for RangeTo<u32> {
    fn to_range(self) -> String {
        ToQueueRange::to_range(self)
    }
}

impl ToQueueRange for RangeFrom<u32> {
    fn to_range(self) -> String {
        format!("{}:", self.start)
    }
}

impl ToQueueRangeOrPlace for RangeFrom<u32> {
    fn to_range(self) -> String {
        ToQueueRange::to_range(self)
    }
}

impl ToQueueRange for RangeFull {
    fn to_range(self) -> String {
        String::new()
    }
}

impl ToQueueRangeOrPlace for RangeFull {
    fn to_range(self) -> String {
        ToQueueRange::to_range(self)
    }
}

pub trait ToQueuePlace: IsId {
    fn to_place(self) -> u32;
}

impl ToQueuePlace for Id {
    fn to_place(self) -> u32 {
        self.0
    }
}

impl ToQueuePlace for u32 {
    fn to_place(self) -> u32 {
        self
    }
}

impl IsId for u32 {}
impl IsId for Range<u32> {}
impl IsId for RangeTo<u32> {}
impl IsId for RangeFrom<u32> {}
impl IsId for RangeFull {}
impl IsId for Id {
    fn is_id() -> bool {
        true
    }
}

pub trait ToSongId {
    fn to_song_id(&self) -> Id;
}

impl ToSongId for Song {
    fn to_song_id(&self) -> Id {
        self.place.unwrap().id
    }
}

impl ToSongId for u32 {
    fn to_song_id(&self) -> Id {
        Id(*self)
    }
}

impl ToSongId for Id {
    fn to_song_id(&self) -> Id {
        *self
    }
}
// }}}

// Output id polymorphisms {{{
pub trait ToOutputId {
    fn to_output_id(self) -> u32;
}

impl ToOutputId for u32 {
    fn to_output_id(self) -> u32 {
        self
    }
}
impl ToOutputId for Output {
    fn to_output_id(self) -> u32 {
        self.id
    }
}
// }}}

// Song play range polymorphisms {{{
pub trait ToSongRange {
    fn to_range(self) -> song::Range;
}

impl ToSongRange for Range<Duration> {
    fn to_range(self) -> song::Range {
        song::Range(self.start, Some(self.end))
    }
}

impl ToSongRange for Range<u32> {
    fn to_range(self) -> song::Range {
        song::Range(Duration::seconds(self.start as i64), Some(Duration::seconds(self.end as i64)))
    }
}

impl ToSongRange for RangeFrom<Duration> {
    fn to_range(self) -> song::Range {
        song::Range(self.start, None)
    }
}

impl ToSongRange for RangeFrom<u32> {
    fn to_range(self) -> song::Range {
        song::Range(Duration::seconds(self.start as i64), None)
    }
}

impl ToSongRange for RangeTo<Duration> {
    fn to_range(self) -> song::Range {
        song::Range(Duration::zero(), Some(self.end))
    }
}

impl ToSongRange for RangeTo<u32> {
    fn to_range(self) -> song::Range {
        song::Range(Duration::zero(), Some(Duration::seconds(self.end as i64)))
    }
}

impl ToSongRange for RangeFull {
    fn to_range(self) -> song::Range {
        song::Range(Duration::zero(), None)
    }
}

impl ToSongRange for song::Range {
    fn to_range(self) -> song::Range {
        self
    }
}
// }}}
<commit_msg>Don't dereference just to borrow again.<commit_after>#![allow(missing_docs)]
//! These are inner traits to support methods overloading for the `Client`

use error::Error;
use output::Output;
use playlist::Playlist;
use song::{self, Id, Song};
use std::collections::BTreeMap;
use std::ops::{Range, RangeFrom, RangeFull, RangeTo};

use time::Duration;

#[doc(hidden)]
pub trait FromMap: Sized {
    fn from_map(map: BTreeMap<String, String>) -> Result<Self, Error>;
}

#[doc(hidden)]
pub trait FromIter: Sized {
    fn from_iter<I: Iterator<Item = Result<(String, String), Error>>>(iter: I) -> Result<Self, Error>;
}

impl<T: FromIter> FromMap for T {
    fn from_map(map: BTreeMap<String, String>) -> Result<Self, Error> {
        FromIter::from_iter(map.into_iter().map(Ok))
    }
}

// Playlist name polymorphisms {{{
pub trait ToPlaylistName {
    fn to_name(&self) -> &str;
}

impl ToPlaylistName for Playlist {
    fn to_name(&self) -> &str {
        &*self.name
    }
}

impl<'a> ToPlaylistName for &'a Playlist {
    fn to_name(&self) -> &str {
        &*self.name
    }
}

impl<'a> ToPlaylistName for &'a String {
    fn to_name(&self) -> &str {
        self
    }
}

impl<'a> ToPlaylistName for &'a str {
    fn to_name(&self) -> &str {
        *self
    }
}

impl ToPlaylistName for str {
    fn to_name(&self) -> &str {
        self
    }
}

impl ToPlaylistName for String {
    fn to_name(&self) -> &str {
        &*self
    }
}
// }}}

// Seconds polymorphisms {{{
pub trait ToSeconds {
    fn to_seconds(self) -> f64;
}

impl ToSeconds for i64 {
    fn to_seconds(self) -> f64 {
        self as f64
    }
}

impl ToSeconds for f64 {
    fn to_seconds(self) -> f64 {
        self
    }
}

impl ToSeconds for Duration {
    fn to_seconds(self) -> f64 {
        self.num_milliseconds() as f64 / 1000.0
    }
}
// }}}

// Queue place polymorphisms {{{

pub trait IsId {
    fn is_id() -> bool {
        false
    }
}

pub trait ToQueueRangeOrPlace: IsId {
    fn to_range(self) -> String;
}

pub trait ToQueueRange {
    fn to_range(self) -> String;
}

impl<T: ToQueuePlace> ToQueueRangeOrPlace for T {
    fn to_range(self) -> String {
        format!("{}", self.to_place())
    }
}

impl ToQueueRange for Range<u32> {
    fn to_range(self) -> String {
        format!("{}:{}", self.start, self.end)
    }
}

impl ToQueueRangeOrPlace for Range<u32> {
    fn to_range(self) -> String {
        ToQueueRange::to_range(self)
    }
}

impl ToQueueRange for RangeTo<u32> {
    fn to_range(self) -> String {
        format!(":{}", self.end)
    }
}

impl ToQueueRangeOrPlace for RangeTo<u32> {
    fn to_range(self) -> String {
        ToQueueRange::to_range(self)
    }
}

impl ToQueueRange for RangeFrom<u32> {
    fn to_range(self) -> String {
        format!("{}:", self.start)
    }
}

impl ToQueueRangeOrPlace for RangeFrom<u32> {
    fn to_range(self) -> String {
        ToQueueRange::to_range(self)
    }
}

impl ToQueueRange for RangeFull {
    fn to_range(self) -> String {
        String::new()
    }
}

impl ToQueueRangeOrPlace for RangeFull {
    fn to_range(self) -> String {
        ToQueueRange::to_range(self)
    }
}

pub trait ToQueuePlace: IsId {
    fn to_place(self) -> u32;
}

impl ToQueuePlace for Id {
    fn to_place(self) -> u32 {
        self.0
    }
}

impl ToQueuePlace for u32 {
    fn to_place(self) -> u32 {
        self
    }
}

impl IsId for u32 {}
impl IsId for Range<u32> {}
impl IsId for RangeTo<u32> {}
impl IsId for RangeFrom<u32> {}
impl IsId for RangeFull {}
impl IsId for Id {
    fn is_id() -> bool {
        true
    }
}

pub trait ToSongId {
    fn to_song_id(&self) -> Id;
}

impl ToSongId for Song {
    fn to_song_id(&self) -> Id {
        self.place.unwrap().id
    }
}

impl ToSongId for u32 {
    fn to_song_id(&self) -> Id {
        Id(*self)
    }
}

impl ToSongId for Id {
    fn to_song_id(&self) -> Id {
        *self
    }
}
// }}}

// Output id polymorphisms {{{
pub trait ToOutputId {
    fn to_output_id(self) -> u32;
}

impl ToOutputId for u32 {
    fn to_output_id(self) -> u32 {
        self
    }
}
impl ToOutputId for Output {
    fn to_output_id(self) -> u32 {
        self.id
    }
}
// }}}

// Song play range polymorphisms {{{
pub trait ToSongRange {
    fn to_range(self) -> song::Range;
}

impl ToSongRange for Range<Duration> {
    fn to_range(self) -> song::Range {
        song::Range(self.start, Some(self.end))
    }
}

impl ToSongRange for Range<u32> {
    fn to_range(self) -> song::Range {
        song::Range(Duration::seconds(self.start as i64), Some(Duration::seconds(self.end as i64)))
    }
}

impl ToSongRange for RangeFrom<Duration> {
    fn to_range(self) -> song::Range {
        song::Range(self.start, None)
    }
}

impl ToSongRange for RangeFrom<u32> {
    fn to_range(self) -> song::Range {
        song::Range(Duration::seconds(self.start as i64), None)
    }
}

impl ToSongRange for RangeTo<Duration> {
    fn to_range(self) -> song::Range {
        song::Range(Duration::zero(), Some(self.end))
    }
}

impl ToSongRange for RangeTo<u32> {
    fn to_range(self) -> song::Range {
        song::Range(Duration::zero(), Some(Duration::seconds(self.end as i64)))
    }
}

impl ToSongRange for RangeFull {
    fn to_range(self) -> song::Range {
        song::Range(Duration::zero(), None)
    }
}

impl ToSongRange for song::Range {
    fn to_range(self) -> song::Range {
        self
    }
}
// }}}
<|endoftext|>