<commit_before>use basetypes::*;

pub struct BoardGeometry {
    grid: [u8; 120],
    piece_grid_deltas: [[i8; 8]; 5],
    pub piece_longrange: [bool; 5],
    pub attacks: [[u64; 64]; 5],
    pub blockers_and_beyond: [[u64; 64]; 5],
    pub squares_at_line: [[u64; 64]; 64],
    pub squares_between_including: [[u64; 64]; 64],
    pub squares_behind_blocker: [[u64; 64]; 64],
}

impl BoardGeometry {
    pub fn new() -> BoardGeometry {
        // We use 10x12 grid (8x8 with guarding markers, 2 at top and
        // bottom, 1 at the sides), so that we can detect out-of-board
        // movements. Each cell in the grid contains the corresponding
        // square number (from 0 to 63) or 0xff (the guarding marker).
        let mut grid = [0xffu8; 120];
        for i in 0..64 {
            grid[BoardGeometry::grid_index_from_square(i)] = i as u8;
        }

        // "piece_deltas" represent the change in the grid-index when
        // sliding a particular piece by one square in a particular
        // direction. We are not concerned with pawns here.
        let mut piece_grid_deltas = [[0i8; 8]; 5];
        piece_grid_deltas[QUEEN] = [-11, -10, -9, -1, 1, 9, 10, 11];
        piece_grid_deltas[ROOK] = [0, -10, 0, -1, 1, 0, 10, 0];
        piece_grid_deltas[BISHOP] = [-11, 0, -9, 0, 0, 9, 0, 11];
        piece_grid_deltas[KNIGHT] = [-21, -19, -12, -8, 8, 12, 19, 21];
        piece_grid_deltas[KING] = [-11, -10, -9, -1, 1, 9, 10, 11];

        // All pieces except knights and kings are long-range (They
        // can slide by more than one square). We are not concerned
        // with pawns here.
        let mut piece_longrange = [true; 5];
        piece_longrange[KNIGHT] = false;
        piece_longrange[KING] = false;

        let mut g = BoardGeometry {
            grid: grid,
            piece_grid_deltas: piece_grid_deltas,
            piece_longrange: piece_longrange,
            attacks: [[0u64; 64]; 5],
            blockers_and_beyond: [[0u64; 64]; 5],
            squares_at_line: [[0u64; 64]; 64],
            squares_between_including: [[0u64; 64]; 64],
            squares_behind_blocker: [[0u64; 64]; 64],
        };

        // "attacks" and "blockers_and_beyond" fields hold attack and
        // blockers bitsets for each piece on each possible square.
        // For example:
        //
        // g.attacks[QUEEN][D4]  g.blockers_and_beyond[QUEEN][D4]
        // . . . 1 . . . 1       . . . . . . . .
        // 1 . . 1 . . 1 .       . . . 1 . . 1 .
        // . 1 . 1 . 1 . .       . 1 . 1 . 1 . .
        // . . 1 1 1 . . .       . . 1 1 1 . . .
        // 1 1 1 Q 1 1 1 1       . 1 1 Q 1 1 1 .
        // . . 1 1 1 . . .       . . 1 1 1 . . .
        // . 1 . 1 . 1 . .       . 1 . 1 . 1 . .
        // 1 . . 1 . . 1 .       . . . . . . . .
        //
        // g.attacks[KNIGHT][D4] g.blockers_and_beyond[KNIGHT][D4]
        // . . . . . . . .       . . . . . . . .
        // . . . . . . . .       . . . . . . . .
        // . . 1 . 1 . . .       . . . . . . . .
        // . 1 . . . 1 . .       . . . . . . . .
        // . . . N . . . .       . . . N . . . .
        // . 1 . . . 1 . .       . . . . . . . .
        // . . 1 . 1 . . .       . . . . . . . .
        // . . . . . . . .       . . . . . . . .
        g.fill_attack_and_blockers_and_beyond_arrays();

        // The "squares_behind_blocker" field holds bitsets that
        // describe all squares hidden behind a blocker from the
        // attacker's position. For example:
        //
        // g.squares_behind_blocker[B2][F6]
        // . . . . . . . 1
        // . . . . . . 1 .
        // . . . . . B . .
        // . . . . . . . .
        // . . . . . . . .
        // . . . . . . . .
        // . A . . . . . .
        // . . . . . . . .
        //
        // The "squares_between_including" field holds bitsets that
        // describe all squares between an attacker an a blocker
        // including the attacker's and blocker's fields
        // themselves. For example:
        //
        // g.squares_between_including[B2][F6]
        // . . . . . . . .
        // . . . . . . . .
        // . . . . . 1 . .
        // . . . . 1 . . .
        // . . . 1 . . . .
        // . . 1 . . . . .
        // . 1 . . . . . .
        // . . . . . . . .
        g.fill_squares_between_including_and_squares_behind_blocker_arrays();

        // The "squares_at_line" field holds bitsets that describe all
        // squares lying at the line determined by the attacker and
        // the blocker. For example:
        //
        // g.squares_at_line[B2][F6]
        // . . . . . . . 1
        // . . . . . . 1 .
        // . . . . . 1 . .
        // . . . . 1 . . .
        // . . . 1 . . . .
        // . . 1 . . . . .
        // . 1 . . . . . .
        // 1 . . . . . . .
        g.fill_squares_at_line_array();

        g
    }

    fn grid_index(&self, i: Square) -> usize {
        Self::grid_index_from_square(i)
    }

    #[inline(always)]
    fn grid_index_from_square(i: Square) -> usize {
        ((i / 8) * 10 + (i % 8) + 21)
    }

    fn fill_attack_and_blockers_and_beyond_arrays(&mut self) {
        for piece_type in 0..5 {
            for square in 0..64 {
                let mut attack = 0u64;
                let mut blockers = 0u64;
                for move_direction in 0..8 {
                    let delta = self.piece_grid_deltas[piece_type][move_direction];
                    if delta != 0 {
                        let mut last_mask = 0u64;
                        let mut curr_grid_index = self.grid_index(square);
                        loop {
                            curr_grid_index = (curr_grid_index as i8 + delta) as usize;
                            let curr_square = self.grid[curr_grid_index] as Square;
                            if curr_square != 0xff {
                                last_mask = 1 << curr_square;
                                attack |= last_mask;
                                blockers |= last_mask;
                                if self.piece_longrange[piece_type] {
                                    continue;
                                }
                            }
                            blockers &= !last_mask;
                            break;
                        }
                    }
                }
                self.attacks[piece_type][square] = attack;
                self.blockers_and_beyond[piece_type][square] = blockers;
            }
        }
    }

    fn fill_squares_between_including_and_squares_behind_blocker_arrays(&mut self) {
        for attacker in 0..64 {
            for blocker in 0..64 {
                // Try to find a grid-index increment (delta) that
                // will generate all squares at the line. If the
                // attacker and the blocker happens not to lie at a
                // straight line, then and we simply proceed to the
                // next attacker/blocker pair.
                let rank_diff = rank(blocker) as i8 - rank(attacker) as i8;
                let file_diff = file(blocker) as i8 - file(attacker) as i8;
                let delta = match (rank_diff, file_diff) {
                    (0, 0) => continue,
                    (0, f) => f.signum(),
                    (r, 0) => 10 * r.signum(),
                    (r, f) if r == f => 10 * r.signum() + r.signum(),
                    (r, f) if r == -f => 10 * r.signum() - r.signum(),
                    _ => continue,
                };

                // Starting from the attacker's square update
                // "squares_between_including" until the blocker's
                // square is encountered, then switch to updating
                // "squares_behind_blocker" until the end of the board
                // is reached.
                let mut squares_between_including = 0u64;
                let mut squares_behind_blocker = 0u64;
                let mut curr_grid_index = self.grid_index(attacker);
                let mut blocker_encountered = false;
                loop {
                    let curr_square = self.grid[curr_grid_index] as Square;
                    match curr_square {
                        0xff => {
                            break;
                        }
                        x if x == blocker => {
                            squares_between_including |= 1 << curr_square;
                            blocker_encountered = true;
                        }
                        _ => {
                            if blocker_encountered {
                                squares_behind_blocker |= 1 << curr_square;
                            } else {
                                squares_between_including |= 1 << curr_square;
                            }
                        }
                    }
                    curr_grid_index = (curr_grid_index as i8 + delta) as usize;
                }
                assert!(blocker_encountered);
                self.squares_between_including[attacker][blocker] = squares_between_including;
                self.squares_behind_blocker[attacker][blocker] = squares_behind_blocker;
            }
        }
    }

    fn fill_squares_at_line_array(&mut self) {
        for a in 0..64 {
            for b in 0..64 {
                self.squares_at_line[a][b] = self.squares_between_including[a][b] |
                                             self.squares_behind_blocker[a][b] |
                                             self.squares_behind_blocker[b][a];
            }
        }
    }
}

// Attack (or blockers and beyond) array for all sliding pieces.
pub type SlidersArray = [[u64; 64]; 5];

// The StateInfo struct stores information needed to restore a Position
// object to its previous state when we retract a move. Whenever a move
// is made on the board (by calling Position::do_move), a StateInfo
// object must be passed as a parameter.

// struct StateInfo {
//   Key pawnKey, materialKey;
//   Value npMaterial[COLOR_NB];
//   int castlingRights, rule50, pliesFromNull;
//   Score psq;
//   Square epSquare;

//   Key key;
//   Bitboard checkersBB;
//   PieceType capturedType;
//   StateInfo* previous;
// };

pub type LinesArray = [[u64; 64]; 64];

// pub fn generate_behind_and_between_arrays() -> LinesArray {

// }

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

    #[test]
    fn test_attack_sets() {
        use basetypes::*;
        let g = BoardGeometry::new();
        assert_eq!(g.attacks[KING][A1], 0b11 << 8 | 0b10);
        assert_eq!(g.blockers_and_beyond[KING][A1], 0);
        assert_eq!(g.attacks[ROOK][A1],
                   0b11111110 | 1 << 8 | 1 << 16 | 1 << 24 | 1 << 32 | 1 << 40 | 1 << 48 | 1 << 56);
        assert_eq!(g.blockers_and_beyond[ROOK][A1],
                   0b01111110 | 1 << 8 | 1 << 16 | 1 << 24 | 1 << 32 | 1 << 40 | 1 << 48 | 0 << 56);
        assert_eq!(g.attacks[KING][D4], g.attacks[KING][E4] >> 1);
        assert_eq!(g.attacks[KING][D4], g.attacks[KING][D5] >> 8);
        assert_eq!(g.attacks[KNIGHT][D4], g.attacks[KNIGHT][D5] >> 8);
        assert_eq!(g.attacks[KNIGHT][D4] & g.attacks[KING][D5], 1 << C6 | 1 << E6);
        assert_eq!(g.attacks[ROOK][D4] | g.attacks[BISHOP][D4], g.attacks[QUEEN][D4]);
        assert_eq!(g.attacks[ROOK][D4] & g.attacks[BISHOP][D4], 0);
        assert_eq!(g.attacks[KING][D4] & g.attacks[QUEEN][D4], g.attacks[KING][D4]);
        assert_eq!(g.attacks[BISHOP][E1] & g.attacks[KNIGHT][H1], 1 << F2 | 1 << G3);
    }

    #[test]
    fn test_line_sets() {
        use basetypes::*;
        let g = BoardGeometry::new();
        assert_eq!(g.squares_at_line[B1][G1], 0b11111111);
        assert_eq!(g.squares_at_line[G8][B8], 0b11111111 << 56);
        assert_eq!(g.squares_between_including[B1][G1], 0b01111110);
        assert_eq!(g.squares_between_including[G8][B8], 0b01111110 << 56);
        assert_eq!(g.squares_behind_blocker[B1][G1], 1 << H1);
        assert_eq!(g.squares_behind_blocker[G8][B8], 1 << A8);
        assert_eq!(g.squares_behind_blocker[A1][G7], 1 << H8);
        assert_eq!(g.squares_behind_blocker[H1][B7], 1 << A8);
        assert_eq!(g.squares_behind_blocker[B7][G2], 1 << H1);
        assert_eq!(g.squares_behind_blocker[G7][B2], 1 << A1);
        assert_eq!(g.squares_behind_blocker[D7][D7], 0);
        assert_eq!(g.squares_behind_blocker[D7][F8], 0);
        assert_eq!(g.squares_between_including[A1][A4] | g.squares_behind_blocker[A1][A4],
                   g.squares_at_line[A1][A4]);
    }
}
<commit_msg>make piece_longrange private.<commit_after>use basetypes::*;

pub struct BoardGeometry {
    grid: [u8; 120],
    piece_grid_deltas: [[i8; 8]; 5],
    piece_longrange: [bool; 5],
    pub attacks: [[u64; 64]; 5],
    pub blockers_and_beyond: [[u64; 64]; 5],
    pub squares_at_line: [[u64; 64]; 64],
    pub squares_between_including: [[u64; 64]; 64],
    pub squares_behind_blocker: [[u64; 64]; 64],
}

impl BoardGeometry {
    pub fn new() -> BoardGeometry {
        // We use 10x12 grid (8x8 with guarding markers, 2 at top and
        // bottom, 1 at the sides), so that we can detect out-of-board
        // movements. Each cell in the grid contains the corresponding
        // square number (from 0 to 63) or 0xff (the guarding marker).
        let mut grid = [0xffu8; 120];
        for i in 0..64 {
            grid[BoardGeometry::grid_index_from_square(i)] = i as u8;
        }

        // "piece_deltas" represent the change in the grid-index when
        // sliding a particular piece by one square in a particular
        // direction. We are not concerned with pawns here.
        let mut piece_grid_deltas = [[0i8; 8]; 5];
        piece_grid_deltas[QUEEN] = [-11, -10, -9, -1, 1, 9, 10, 11];
        piece_grid_deltas[ROOK] = [0, -10, 0, -1, 1, 0, 10, 0];
        piece_grid_deltas[BISHOP] = [-11, 0, -9, 0, 0, 9, 0, 11];
        piece_grid_deltas[KNIGHT] = [-21, -19, -12, -8, 8, 12, 19, 21];
        piece_grid_deltas[KING] = [-11, -10, -9, -1, 1, 9, 10, 11];

        // All pieces except knights and kings are long-range (They
        // can slide by more than one square). We are not concerned
        // with pawns here.
        let mut piece_longrange = [true; 5];
        piece_longrange[KNIGHT] = false;
        piece_longrange[KING] = false;

        let mut g = BoardGeometry {
            grid: grid,
            piece_grid_deltas: piece_grid_deltas,
            piece_longrange: piece_longrange,
            attacks: [[0u64; 64]; 5],
            blockers_and_beyond: [[0u64; 64]; 5],
            squares_at_line: [[0u64; 64]; 64],
            squares_between_including: [[0u64; 64]; 64],
            squares_behind_blocker: [[0u64; 64]; 64],
        };

        // "attacks" and "blockers_and_beyond" fields hold attack and
        // blockers bitsets for each piece on each possible square.
        // For example:
        //
        // g.attacks[QUEEN][D4]  g.blockers_and_beyond[QUEEN][D4]
        // . . . 1 . . . 1       . . . . . . . .
        // 1 . . 1 . . 1 .       . . . 1 . . 1 .
        // . 1 . 1 . 1 . .       . 1 . 1 . 1 . .
        // . . 1 1 1 . . .       . . 1 1 1 . . .
        // 1 1 1 Q 1 1 1 1       . 1 1 Q 1 1 1 .
        // . . 1 1 1 . . .       . . 1 1 1 . . .
        // . 1 . 1 . 1 . .       . 1 . 1 . 1 . .
        // 1 . . 1 . . 1 .       . . . . . . . .
        //
        // g.attacks[KNIGHT][D4] g.blockers_and_beyond[KNIGHT][D4]
        // . . . . . . . .       . . . . . . . .
        // . . . . . . . .       . . . . . . . .
        // . . 1 . 1 . . .       . . . . . . . .
        // . 1 . . . 1 . .       . . . . . . . .
        // . . . N . . . .       . . . N . . . .
        // . 1 . . . 1 . .       . . . . . . . .
        // . . 1 . 1 . . .       . . . . . . . .
        // . . . . . . . .       . . . . . . . .
        g.fill_attack_and_blockers_and_beyond_arrays();

        // The "squares_behind_blocker" field holds bitsets that
        // describe all squares hidden behind a blocker from the
        // attacker's position. For example:
        //
        // g.squares_behind_blocker[B2][F6]
        // . . . . . . . 1
        // . . . . . . 1 .
        // . . . . . B . .
        // . . . . . . . .
        // . . . . . . . .
        // . . . . . . . .
        // . A . . . . . .
        // . . . . . . . .
        //
        // The "squares_between_including" field holds bitsets that
        // describe all squares between an attacker an a blocker
        // including the attacker's and blocker's fields
        // themselves. For example:
        //
        // g.squares_between_including[B2][F6]
        // . . . . . . . .
        // . . . . . . . .
        // . . . . . 1 . .
        // . . . . 1 . . .
        // . . . 1 . . . .
        // . . 1 . . . . .
        // . 1 . . . . . .
        // . . . . . . . .
        g.fill_squares_between_including_and_squares_behind_blocker_arrays();

        // The "squares_at_line" field holds bitsets that describe all
        // squares lying at the line determined by the attacker and
        // the blocker. For example:
        //
        // g.squares_at_line[B2][F6]
        // . . . . . . . 1
        // . . . . . . 1 .
        // . . . . . 1 . .
        // . . . . 1 . . .
        // . . . 1 . . . .
        // . . 1 . . . . .
        // . 1 . . . . . .
        // 1 . . . . . . .
        g.fill_squares_at_line_array();

        g
    }

    fn grid_index(&self, i: Square) -> usize {
        Self::grid_index_from_square(i)
    }

    #[inline(always)]
    fn grid_index_from_square(i: Square) -> usize {
        ((i / 8) * 10 + (i % 8) + 21)
    }

    fn fill_attack_and_blockers_and_beyond_arrays(&mut self) {
        for piece_type in 0..5 {
            for square in 0..64 {
                let mut attack = 0u64;
                let mut blockers = 0u64;
                for move_direction in 0..8 {
                    let delta = self.piece_grid_deltas[piece_type][move_direction];
                    if delta != 0 {
                        let mut last_mask = 0u64;
                        let mut curr_grid_index = self.grid_index(square);
                        loop {
                            curr_grid_index = (curr_grid_index as i8 + delta) as usize;
                            let curr_square = self.grid[curr_grid_index] as Square;
                            if curr_square != 0xff {
                                last_mask = 1 << curr_square;
                                attack |= last_mask;
                                blockers |= last_mask;
                                if self.piece_longrange[piece_type] {
                                    continue;
                                }
                            }
                            blockers &= !last_mask;
                            break;
                        }
                    }
                }
                self.attacks[piece_type][square] = attack;
                self.blockers_and_beyond[piece_type][square] = blockers;
            }
        }
    }

    fn fill_squares_between_including_and_squares_behind_blocker_arrays(&mut self) {
        for attacker in 0..64 {
            for blocker in 0..64 {
                // Try to find a grid-index increment (delta) that
                // will generate all squares at the line. If the
                // attacker and the blocker happens not to lie at a
                // straight line, then and we simply proceed to the
                // next attacker/blocker pair.
                let rank_diff = rank(blocker) as i8 - rank(attacker) as i8;
                let file_diff = file(blocker) as i8 - file(attacker) as i8;
                let delta = match (rank_diff, file_diff) {
                    (0, 0) => continue,
                    (0, f) => f.signum(),
                    (r, 0) => 10 * r.signum(),
                    (r, f) if r == f => 10 * r.signum() + r.signum(),
                    (r, f) if r == -f => 10 * r.signum() - r.signum(),
                    _ => continue,
                };

                // Starting from the attacker's square update
                // "squares_between_including" until the blocker's
                // square is encountered, then switch to updating
                // "squares_behind_blocker" until the end of the board
                // is reached.
                let mut squares_between_including = 0u64;
                let mut squares_behind_blocker = 0u64;
                let mut curr_grid_index = self.grid_index(attacker);
                let mut blocker_encountered = false;
                loop {
                    let curr_square = self.grid[curr_grid_index] as Square;
                    match curr_square {
                        0xff => {
                            break;
                        }
                        x if x == blocker => {
                            squares_between_including |= 1 << curr_square;
                            blocker_encountered = true;
                        }
                        _ => {
                            if blocker_encountered {
                                squares_behind_blocker |= 1 << curr_square;
                            } else {
                                squares_between_including |= 1 << curr_square;
                            }
                        }
                    }
                    curr_grid_index = (curr_grid_index as i8 + delta) as usize;
                }
                assert!(blocker_encountered);
                self.squares_between_including[attacker][blocker] = squares_between_including;
                self.squares_behind_blocker[attacker][blocker] = squares_behind_blocker;
            }
        }
    }

    fn fill_squares_at_line_array(&mut self) {
        for a in 0..64 {
            for b in 0..64 {
                self.squares_at_line[a][b] = self.squares_between_including[a][b] |
                                             self.squares_behind_blocker[a][b] |
                                             self.squares_behind_blocker[b][a];
            }
        }
    }
}

// The StateInfo struct stores information needed to restore a Position
// object to its previous state when we retract a move. Whenever a move
// is made on the board (by calling Position::do_move), a StateInfo
// object must be passed as a parameter.

// struct StateInfo {
//   Key pawnKey, materialKey;
//   Value npMaterial[COLOR_NB];
//   int castlingRights, rule50, pliesFromNull;
//   Score psq;
//   Square epSquare;

//   Key key;
//   Bitboard checkersBB;
//   PieceType capturedType;
//   StateInfo* previous;
// };

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

    #[test]
    fn test_attack_sets() {
        use basetypes::*;
        let g = BoardGeometry::new();
        assert_eq!(g.attacks[KING][A1], 0b11 << 8 | 0b10);
        assert_eq!(g.blockers_and_beyond[KING][A1], 0);
        assert_eq!(g.attacks[ROOK][A1],
                   0b11111110 | 1 << 8 | 1 << 16 | 1 << 24 | 1 << 32 | 1 << 40 | 1 << 48 | 1 << 56);
        assert_eq!(g.blockers_and_beyond[ROOK][A1],
                   0b01111110 | 1 << 8 | 1 << 16 | 1 << 24 | 1 << 32 | 1 << 40 | 1 << 48 | 0 << 56);
        assert_eq!(g.attacks[KING][D4], g.attacks[KING][E4] >> 1);
        assert_eq!(g.attacks[KING][D4], g.attacks[KING][D5] >> 8);
        assert_eq!(g.attacks[KNIGHT][D4], g.attacks[KNIGHT][D5] >> 8);
        assert_eq!(g.attacks[KNIGHT][D4] & g.attacks[KING][D5], 1 << C6 | 1 << E6);
        assert_eq!(g.attacks[ROOK][D4] | g.attacks[BISHOP][D4], g.attacks[QUEEN][D4]);
        assert_eq!(g.attacks[ROOK][D4] & g.attacks[BISHOP][D4], 0);
        assert_eq!(g.attacks[KING][D4] & g.attacks[QUEEN][D4], g.attacks[KING][D4]);
        assert_eq!(g.attacks[BISHOP][E1] & g.attacks[KNIGHT][H1], 1 << F2 | 1 << G3);
    }

    #[test]
    fn test_line_sets() {
        use basetypes::*;
        let g = BoardGeometry::new();
        assert_eq!(g.squares_at_line[B1][G1], 0b11111111);
        assert_eq!(g.squares_at_line[G8][B8], 0b11111111 << 56);
        assert_eq!(g.squares_between_including[B1][G1], 0b01111110);
        assert_eq!(g.squares_between_including[G8][B8], 0b01111110 << 56);
        assert_eq!(g.squares_behind_blocker[B1][G1], 1 << H1);
        assert_eq!(g.squares_behind_blocker[G8][B8], 1 << A8);
        assert_eq!(g.squares_behind_blocker[A1][G7], 1 << H8);
        assert_eq!(g.squares_behind_blocker[H1][B7], 1 << A8);
        assert_eq!(g.squares_behind_blocker[B7][G2], 1 << H1);
        assert_eq!(g.squares_behind_blocker[G7][B2], 1 << A1);
        assert_eq!(g.squares_behind_blocker[D7][D7], 0);
        assert_eq!(g.squares_behind_blocker[D7][F8], 0);
        assert_eq!(g.squares_between_including[A1][A4] | g.squares_behind_blocker[A1][A4],
                   g.squares_at_line[A1][A4]);
    }
}
<|endoftext|>


<commit_before>// Integration with Musashi
extern crate libc;

// Register enum copied from Musashi's m68k_register_t enum
#[repr(C)]
#[derive(Copy, Clone)]
#[allow(dead_code)]
pub enum Register {
	/* Real registers */
	D0,		/* Data registers */
	D1,
	D2,
	D3,
	D4,
	D5,
	D6,
	D7,
	A0,		/* Address registers */
	A1,
	A2,
	A3,
	A4,
	A5,
	A6,
	A7,
	PC,		/* Program Counter */
	SR,		/* Status Register */
	SP,		/* The current Stack Pointer (located in A7) */
	USP,		/* User Stack Pointer */
	ISP,		/* Interrupt Stack Pointer */
	MSP,		/* Master Stack Pointer */
	SFC,		/* Source Function Code */
	DFC,		/* Destination Function Code */
	VBR,		/* Vector Base Register */
	CACR,		/* Cache Control Register */
	CAAR,		/* Cache Address Register */

	/* Assumed registers */
	/* These are cheat registers which emulate the 1-longword prefetch
	 * present in the 68000 and 68010.
	 */
	PrefAddr,	/* Last prefetch address */
	PrefData,	/* Last prefetch data */

	/* Convenience registers */
	PPC,		/* Previous value in the program counter */
	IR,			/* Instruction register */
	CpuType	/* Type of CPU being run */
}

#[repr(C)]
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum CpuType
{
	Invalid,
	M68000,
	M68010,
	M68EC020,
	M68020,
	M68030,		/* Supported by disassembler ONLY */
	M68040		/* Supported by disassembler ONLY */
}

#[link(name = "musashi", kind = "static")]
extern {
	fn m68k_init();
	fn m68k_set_cpu_type(cputype: CpuType);
	fn m68k_pulse_reset();
	fn m68k_execute(num_cycles: i32) -> i32;
	fn m68k_get_reg(context: *mut libc::c_void, regnum: Register) -> u32;
	fn m68k_set_reg(regnum: Register, value: u32);
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum Operation {
	None,
    ReadByte(u32),
    ReadWord(u32),
    ReadLong(u32),
    WriteByte(u32, u32),
    WriteWord(u32, u32),
    WriteLong(u32, u32),
}
static mut musashi_memory:  [u8; 1024] = [0u8; 1024];
// as statics are not allowed to have destructors, allocate a
// big enough array to hold the small number of operations
// expected from executing a very limited number of opcodes
static mut musashi_opcount: usize = 0;
static mut musashi_ops: [Operation; 128] = [Operation::None; 128];

unsafe fn register_op(op: Operation) {
	if musashi_opcount < musashi_ops.len() {
		musashi_ops[musashi_opcount] = op;
		musashi_opcount += 1;
	}
}
// callbacks from Musashi
#[no_mangle]
pub extern fn cpu_read_byte(address: u32) -> u32 {
	let op = Operation::ReadByte(address);
	let address = address as usize;
	unsafe {
		register_op(op);
		musashi_memory[address] as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_word(address: u32) -> u32 {
	let op = Operation::ReadWord(address);
	let address = address as usize;
	unsafe {
		register_op(op);
		((musashi_memory[address+0] as u32) << 8
		|(musashi_memory[address+1] as u32) << 0) as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_long(address: u32) -> u32 {
	let op = Operation::ReadLong(address);
	let address = address as usize;
	unsafe {
		register_op(op);
		((musashi_memory[address+0] as u32) << 24
		|(musashi_memory[address+1] as u32) << 16
		|(musashi_memory[address+2] as u32) <<  8
		|(musashi_memory[address+3] as u32) <<  0) as u32
	}
}

#[no_mangle]
pub extern fn cpu_write_byte(address: u32, value: u32) {
	let op = Operation::WriteByte(address, value);
	let address = address as usize;
	unsafe {
		register_op(op);
		musashi_memory[address+0] = (value & 0xff) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_word(address: u32, value: u32) {
	let op = Operation::WriteWord(address, value);
	let address = address as usize;
	unsafe {
		register_op(op);
		musashi_memory[address+0] = (value & 0xff00 >> 8) as u8;
		musashi_memory[address+1] = (value & 0x00ff >> 0) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_long(address: u32, value: u32) {
	let op = Operation::WriteLong(address, value);
	let address = address as usize;
	unsafe {
		register_op(op);
		musashi_memory[address+0] = (value & 0xff000000 >> 24) as u8;
		musashi_memory[address+1] = (value & 0x00ff0000 >> 16) as u8;
		musashi_memory[address+2] = (value & 0x0000ff00 >>  8) as u8;
		musashi_memory[address+3] = (value & 0x000000ff >>  0) as u8;
	}
}

#[no_mangle]
pub extern fn cpu_pulse_reset() {panic!("pr")}
#[no_mangle]
pub extern fn cpu_long_branch() {}
#[no_mangle]
pub extern fn cpu_set_fc(fc: u32) {
	//println!("set_fc {}", fc)
}
#[no_mangle]
pub extern fn cpu_irq_ack(level: i32) -> i32 {panic!("ia")}
#[no_mangle]
pub extern fn cpu_instr_callback() {}

use std::ptr;

pub fn experimental_communication() {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(Register::D0, 123);
		println!("D0: {}", m68k_get_reg(ptr::null_mut(), Register::D0));
	}
}

pub fn roundtrip_register(reg: Register, value: u32) -> u32 {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(reg, value);
		m68k_get_reg(ptr::null_mut(), reg)
	}
}

use cpu::Core;

pub fn execute1(core: &mut Core) {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_pulse_reset();
		// Resetting opcount, because m68k_pulse_reset causes irrelevant
		// reads from 0x00000000 to set PC/SP, a jump to PC and
		// resetting of state. But we don't want to test those ops.
		musashi_opcount = 0;
		let regs = [Register::D0, Register::D1, Register::D2, Register::D3, Register::D4, Register::D5, Register::D6, Register::D7, Register::A0, Register::A1, Register::A2, Register::A3, Register::A4, Register::A5, Register::A6, Register::A7];
		m68k_set_reg(Register::PC, core.pc);
		m68k_set_reg(Register::SP, core.sp);
		m68k_set_reg(Register::SR, core.status_register());
		for (i, &reg) in regs.iter().enumerate() { m68k_set_reg(reg, core.dar[i]); }
		for (i,b) in core.mem.iter().enumerate() {
			musashi_memory[i] = *b;
		}
		m68k_execute(1);

		for (i, &reg) in regs.iter().enumerate() {
			core.dar[i] = m68k_get_reg(ptr::null_mut(), reg);
		}
		core.pc = m68k_get_reg(ptr::null_mut(), Register::PC);
		core.sp = m68k_get_reg(ptr::null_mut(), Register::SP);
		core.sr_to_flags(m68k_get_reg(ptr::null_mut(), Register::SR));
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use super::musashi_ops;
	use super::musashi_opcount;
	use super::Operation;
	use cpu::Core;

	fn get_ops() -> Vec<Operation> {
		let mut res: Vec<Operation> = vec![];
		unsafe {
			for i in 0..musashi_opcount {
				res.push(musashi_ops[i]);
			}
		}
		res
	}
	macro_rules! core_eq {
	    ($left:ident , $right:ident . $field:ident [ $index:expr ]) => ({
	        match (&($left.$field[$index]), &($right.$field[$index])) {
	            (left_val, right_val) => {
	                if !(*left_val == *right_val) {
	                    panic!("core incoherence: `{}[{}]` differs \
	                           ({}: `0x{:x}`, {}: `0x{:x}`)", stringify!($field), $index, stringify!($left), left_val, stringify!($right), right_val)
	                }
	            }
	        }
	    });
	    ($left:ident , $right:ident . $field:ident () ?) => ({
	        match (&($left.$field()), &($right.$field())) {
	            (left_val, right_val) => {
	                if !(*left_val == *right_val) {
	                    panic!("core incoherence: `{}()` differs \
	                           ({}: `{:?}`, {}: `{:?}`)", stringify!($field), stringify!($left), left_val, stringify!($right), right_val)
	                }
	            }
	        }
	    });
	    ($left:ident , $right:ident . $field:ident ()) => ({
	        match (&($left.$field()), &($right.$field())) {
	            (left_val, right_val) => {
	                if !(*left_val == *right_val) {
	                    panic!("core incoherence: `{}()` differs \
	                           ({}: `0x{:x}`, {}: `0x{:x}`)", stringify!($field), stringify!($left), left_val, stringify!($right), right_val)
	                }
	            }
	        }
	    });
	    ($left:ident , $right:ident . $field:ident) => ({
	        match (&($left.$field), &($right.$field)) {
	            (left_val, right_val) => {
	                if !(*left_val == *right_val) {
	                    panic!("core incoherence: `{}` differs \
	                           ({}: `0x{:x}`, {}: `0x{:x}`)", stringify!($field), stringify!($left), left_val, stringify!($right), right_val)
	                }
	            }
	        }
	    })
	}
	fn assert_cores_equal(musashi: &Core, r68k: &Core, pc: u32) {
		let ops = get_ops();
		assert_eq!(1, ops.len());
		assert_eq!(Operation::ReadLong(pc), ops[0]);

		core_eq!(musashi, r68k.pc);
		core_eq!(musashi, r68k.sp);
		for i in (0..16).rev() {
			core_eq!(musashi, r68k.dar[i]);
		}
		core_eq!(musashi, r68k.flags() ?);
		core_eq!(musashi, r68k.status_register());

	}

	#[test]
	fn roundtrip_d0() {
		assert_eq!(256, roundtrip_register(Register::D0, 256));
	}

	#[test]
	fn roundtrip_abcd_rr() {
		let pc = 0x40;
		let mut cpu = Core::new_mem(pc, &[0xc1, 0x01]);
		cpu.dar[0] = 0x17;
		cpu.dar[1] = 0x27;
		cpu.dar[5] = 0x55555;
		execute1(&mut cpu);

		// 17 + 27 is 44
		assert_eq!(0x44, cpu.dar[0]);
		assert_eq!(0x27, cpu.dar[1]);
		assert_eq!(0x55555, cpu.dar[5]);

		let ops = get_ops();
		assert_eq!(1, ops.len());
		assert_eq!(Operation::ReadLong(pc), ops[0]);
	}

	#[test]
	fn compare_abcd_rr() {
		let pc = 0x40;
		let mut musashi = Core::new_mem(pc, &[0xc3, 0x01]);
		musashi.dar[0] = 0x16;
		musashi.dar[1] = 0x26;

		let mut r68k = musashi.clone(); // so very self-aware!
		execute1(&mut musashi);
		r68k.execute1();

		assert_cores_equal(&musashi, &r68k, pc);
	}}<commit_msg>Add address space to read/write operations log<commit_after>// Integration with Musashi
extern crate libc;

// Register enum copied from Musashi's m68k_register_t enum
#[repr(C)]
#[derive(Copy, Clone)]
#[allow(dead_code)]
pub enum Register {
	/* Real registers */
	D0,		/* Data registers */
	D1,
	D2,
	D3,
	D4,
	D5,
	D6,
	D7,
	A0,		/* Address registers */
	A1,
	A2,
	A3,
	A4,
	A5,
	A6,
	A7,
	PC,		/* Program Counter */
	SR,		/* Status Register */
	SP,		/* The current Stack Pointer (located in A7) */
	USP,		/* User Stack Pointer */
	ISP,		/* Interrupt Stack Pointer */
	MSP,		/* Master Stack Pointer */
	SFC,		/* Source Function Code */
	DFC,		/* Destination Function Code */
	VBR,		/* Vector Base Register */
	CACR,		/* Cache Control Register */
	CAAR,		/* Cache Address Register */

	/* Assumed registers */
	/* These are cheat registers which emulate the 1-longword prefetch
	 * present in the 68000 and 68010.
	 */
	PrefAddr,	/* Last prefetch address */
	PrefData,	/* Last prefetch data */

	/* Convenience registers */
	PPC,		/* Previous value in the program counter */
	IR,			/* Instruction register */
	CpuType	/* Type of CPU being run */
}

#[repr(C)]
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum CpuType
{
	Invalid,
	M68000,
	M68010,
	M68EC020,
	M68020,
	M68030,		/* Supported by disassembler ONLY */
	M68040		/* Supported by disassembler ONLY */
}
#[link(name = "musashi", kind = "static")]
extern {
	fn m68k_init();
	fn m68k_set_cpu_type(cputype: CpuType);
	fn m68k_pulse_reset();
	fn m68k_execute(num_cycles: i32) -> i32;
	fn m68k_get_reg(context: *mut libc::c_void, regnum: Register) -> u32;
	fn m68k_set_reg(regnum: Register, value: u32);
}
#[derive(Copy, Clone, Debug, PartialEq)]
struct AddressSpace(Mode, Segment);
#[derive(Copy, Clone, Debug, PartialEq)]
enum Segment {
	Program, Data
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum Mode {
	User, Supervisor
}
const SUPERVISOR_PROGRAM: AddressSpace = AddressSpace(Mode::Supervisor, Segment::Program);
const SUPERVISOR_DATA: AddressSpace = AddressSpace(Mode::Supervisor, Segment::Data);
const USER_PROGRAM: AddressSpace = AddressSpace(Mode::User, Segment::Program);
const USER_DATA: AddressSpace = AddressSpace(Mode::User, Segment::Data);

#[derive(Copy, Clone, Debug, PartialEq)]
enum Operation {
	None,
    ReadByte(AddressSpace, u32),
    ReadWord(AddressSpace, u32),
    ReadLong(AddressSpace, u32),
    WriteByte(AddressSpace, u32, u32),
    WriteWord(AddressSpace, u32, u32),
    WriteLong(AddressSpace, u32, u32),
}
static mut musashi_memory:  [u8; 1024] = [0u8; 1024];
// as statics are not allowed to have destructors, allocate a
// big enough array to hold the small number of operations
// expected from executing a very limited number of opcodes
static mut musashi_ops: [Operation; 128] = [Operation::None; 128];
static mut musashi_opcount: usize = 0;
static mut musashi_address_space: AddressSpace = SUPERVISOR_PROGRAM;

unsafe fn register_op(op: Operation) {
	if musashi_opcount < musashi_ops.len() {
		musashi_ops[musashi_opcount] = op;
		musashi_opcount += 1;
	}
}
// callbacks from Musashi
#[no_mangle]
pub extern fn cpu_read_byte(address: u32) -> u32 {
	unsafe {
		let op = Operation::ReadByte(musashi_address_space, address);
		let address = address as usize;
		register_op(op);
		musashi_memory[address] as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_word(address: u32) -> u32 {
	unsafe {
		let op = Operation::ReadWord(musashi_address_space, address);
		let address = address as usize;
		register_op(op);
		((musashi_memory[address+0] as u32) << 8
		|(musashi_memory[address+1] as u32) << 0) as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_long(address: u32) -> u32 {
	unsafe {
		let op = Operation::ReadLong(musashi_address_space, address);
		let address = address as usize;
		register_op(op);
		((musashi_memory[address+0] as u32) << 24
		|(musashi_memory[address+1] as u32) << 16
		|(musashi_memory[address+2] as u32) <<  8
		|(musashi_memory[address+3] as u32) <<  0) as u32
	}
}

#[no_mangle]
pub extern fn cpu_write_byte(address: u32, value: u32) {
	unsafe {
		let op = Operation::WriteByte(musashi_address_space, address, value);
		let address = address as usize;
		register_op(op);
		musashi_memory[address+0] = (value & 0xff) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_word(address: u32, value: u32) {
	unsafe {
		let op = Operation::WriteWord(musashi_address_space, address, value);
		let address = address as usize;
		register_op(op);
		musashi_memory[address+0] = (value & 0xff00 >> 8) as u8;
		musashi_memory[address+1] = (value & 0x00ff >> 0) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_long(address: u32, value: u32) {
	unsafe {
		let op = Operation::WriteLong(musashi_address_space, address, value);
		let address = address as usize;
		register_op(op);
		musashi_memory[address+0] = (value & 0xff000000 >> 24) as u8;
		musashi_memory[address+1] = (value & 0x00ff0000 >> 16) as u8;
		musashi_memory[address+2] = (value & 0x0000ff00 >>  8) as u8;
		musashi_memory[address+3] = (value & 0x000000ff >>  0) as u8;
	}
}

#[no_mangle]
pub extern fn cpu_pulse_reset() {panic!("pr")}
#[no_mangle]
pub extern fn cpu_long_branch() {}
#[no_mangle]
pub extern fn cpu_set_fc(fc: u32) {
	unsafe {
		musashi_address_space = match fc {
		    1 => USER_DATA,
		    2 => USER_PROGRAM,
		    5 => SUPERVISOR_DATA,
		    6 => SUPERVISOR_PROGRAM,
		    _ => panic!("unknown fc: {}", fc),
		};
		println!("set_fc {:?}", musashi_address_space);
	}
}
#[no_mangle]
pub extern fn cpu_irq_ack(level: i32) -> i32 {panic!("ia")}
#[no_mangle]
pub extern fn cpu_instr_callback() {}

use std::ptr;

pub fn experimental_communication() {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(Register::D0, 123);
		println!("D0: {}", m68k_get_reg(ptr::null_mut(), Register::D0));
	}
}

pub fn roundtrip_register(reg: Register, value: u32) -> u32 {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(reg, value);
		m68k_get_reg(ptr::null_mut(), reg)
	}
}

use cpu::Core;

pub fn execute1(core: &mut Core) {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_pulse_reset();
		// Resetting opcount, because m68k_pulse_reset causes irrelevant
		// reads from 0x00000000 to set PC/SP, a jump to PC and
		// resetting of state. But we don't want to test those ops.
		musashi_opcount = 0;
		let regs = [Register::D0, Register::D1, Register::D2, Register::D3, Register::D4, Register::D5, Register::D6, Register::D7, Register::A0, Register::A1, Register::A2, Register::A3, Register::A4, Register::A5, Register::A6, Register::A7];
		m68k_set_reg(Register::PC, core.pc);
		m68k_set_reg(Register::SP, core.sp);
		m68k_set_reg(Register::SR, core.status_register());
		for (i, &reg) in regs.iter().enumerate() { m68k_set_reg(reg, core.dar[i]); }
		for (i,b) in core.mem.iter().enumerate() {
			musashi_memory[i] = *b;
		}
		m68k_execute(1);

		for (i, &reg) in regs.iter().enumerate() {
			core.dar[i] = m68k_get_reg(ptr::null_mut(), reg);
		}
		core.pc = m68k_get_reg(ptr::null_mut(), Register::PC);
		core.sp = m68k_get_reg(ptr::null_mut(), Register::SP);
		core.sr_to_flags(m68k_get_reg(ptr::null_mut(), Register::SR));
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use super::SUPERVISOR_PROGRAM;
	use super::musashi_ops;
	use super::musashi_opcount;
	use super::Operation;
	use cpu::Core;

	fn get_ops() -> Vec<Operation> {
		let mut res: Vec<Operation> = vec![];
		unsafe {
			for i in 0..musashi_opcount {
				res.push(musashi_ops[i]);
			}
		}
		res
	}
	macro_rules! core_eq {
	    ($left:ident , $right:ident . $field:ident [ $index:expr ]) => ({
	        match (&($left.$field[$index]), &($right.$field[$index])) {
	            (left_val, right_val) => {
	                if !(*left_val == *right_val) {
	                    panic!("core incoherence: `{}[{}]` differs \
	                           ({}: `0x{:x}`, {}: `0x{:x}`)", stringify!($field), $index, stringify!($left), left_val, stringify!($right), right_val)
	                }
	            }
	        }
	    });
	    ($left:ident , $right:ident . $field:ident () ?) => ({
	        match (&($left.$field()), &($right.$field())) {
	            (left_val, right_val) => {
	                if !(*left_val == *right_val) {
	                    panic!("core incoherence: `{}()` differs \
	                           ({}: `{:?}`, {}: `{:?}`)", stringify!($field), stringify!($left), left_val, stringify!($right), right_val)
	                }
	            }
	        }
	    });
	    ($left:ident , $right:ident . $field:ident ()) => ({
	        match (&($left.$field()), &($right.$field())) {
	            (left_val, right_val) => {
	                if !(*left_val == *right_val) {
	                    panic!("core incoherence: `{}()` differs \
	                           ({}: `0x{:x}`, {}: `0x{:x}`)", stringify!($field), stringify!($left), left_val, stringify!($right), right_val)
	                }
	            }
	        }
	    });
	    ($left:ident , $right:ident . $field:ident) => ({
	        match (&($left.$field), &($right.$field)) {
	            (left_val, right_val) => {
	                if !(*left_val == *right_val) {
	                    panic!("core incoherence: `{}` differs \
	                           ({}: `0x{:x}`, {}: `0x{:x}`)", stringify!($field), stringify!($left), left_val, stringify!($right), right_val)
	                }
	            }
	        }
	    })
	}
	fn assert_cores_equal(musashi: &Core, r68k: &Core, pc: u32) {
		let ops = get_ops();
		assert_eq!(1, ops.len());
		assert_eq!(Operation::ReadLong(SUPERVISOR_PROGRAM, pc), ops[0]);

		core_eq!(musashi, r68k.pc);
		core_eq!(musashi, r68k.sp);
		for i in (0..16).rev() {
			core_eq!(musashi, r68k.dar[i]);
		}
		core_eq!(musashi, r68k.flags() ?);
		core_eq!(musashi, r68k.status_register());

	}

	#[test]
	fn roundtrip_d0() {
		assert_eq!(256, roundtrip_register(Register::D0, 256));
	}

	#[test]
	fn roundtrip_abcd_rr() {
		let pc = 0x40;
		let mut cpu = Core::new_mem(pc, &[0xc1, 0x01]);
		cpu.dar[0] = 0x17;
		cpu.dar[1] = 0x27;
		cpu.dar[5] = 0x55555;
		execute1(&mut cpu);

		// 17 + 27 is 44
		assert_eq!(0x44, cpu.dar[0]);
		assert_eq!(0x27, cpu.dar[1]);
		assert_eq!(0x55555, cpu.dar[5]);

		let ops = get_ops();
		assert_eq!(1, ops.len());
		assert_eq!(Operation::ReadLong(SUPERVISOR_PROGRAM, pc), ops[0]);
	}

	#[test]
	fn compare_abcd_rr() {
		let pc = 0x40;
		let mut musashi = Core::new_mem(pc, &[0xc3, 0x01]);
		musashi.dar[0] = 0x16;
		musashi.dar[1] = 0x26;

		let mut r68k = musashi.clone(); // so very self-aware!
		execute1(&mut musashi);
		r68k.execute1();

		assert_cores_equal(&musashi, &r68k, pc);
	}}<|endoftext|>


<commit_before>//! Contains code relating to networking.

#[cfg(test)]
mod test;

use std::sync::mpsc::SendError;

use mioco;
use mioco::sync::mpsc::{Sender, channel};

use test_util::Tattle;

/// Represents the network state and provides various utilities acting upon it.
#[allow(missing_debug_implementations)]
pub struct NetHandle(Sender<()>);

impl NetHandle {
    /// Construct a new instance, starting a new coroutine and opening all network traffic on the spesified port.
    pub fn new() -> Self {
        NetHandle::new_tattle(None, None)
    }

    /// Constructs, with several optional structs that use global state for easier unit testing.
    pub fn new_tattle(tattle_closure_start: Option<Tattle>,
                      tattle_shutdown: Option<Tattle>)
                      -> Self {
        let (tx, rx) = channel::<()>(); // TODO: Maybe have more possible messages than () to shutdown?
        mioco::spawn(move || {
            if let Some(tattle) = tattle_closure_start {
                tattle.call();
            }
            loop {
                select!(
                    rx:r => {
                        if let Some(tattle) = tattle_shutdown {
                            tattle.call();
                        }
                        debug!("Shutting down coroutine");
                        let _ = rx.recv();
                        break;
                    },
                );
            }
        });
        NetHandle(tx)
    }

    /// Shuts down the socket/listener, closing all connections.
    pub fn shutdown(&self) -> Result<(), SendError<()>> {
        self.0.send(())
    }
}
<commit_msg>Restore some usable functions from old netcode<commit_after>//! Contains code relating to networking.

#[cfg(test)]
mod test;

use std::error::Error;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::mpsc::SendError;

use bincode::serde::{DeserializeError, deserialize, serialize};
use bincode::SizeLimit;
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use mioco;
use mioco::sync::mpsc::{Sender, channel};

use test_util::Tattle;

/// Standard number to ensure network connections are syncronized and the same protocol is being used.
/// Reexported incase it is of use for something not-networking.
pub const NET_MAGIC_NUMBER: u32 = 0xCB011043; //0xcafebade + 0x25565, because programming references.

/// Represents the network state and provides various utilities acting upon it.
#[allow(missing_debug_implementations)]
pub struct NetHandle(Sender<()>);

impl NetHandle {
    /// Construct a new instance, starting a new coroutine and opening all network traffic on the spesified port.
    pub fn new() -> Self {
        NetHandle::new_tattle(None, None)
    }

    /// Constructs, with several optional structs that use global state for easier unit testing.
    pub fn new_tattle(tattle_closure_start: Option<Tattle>,
                      tattle_shutdown: Option<Tattle>)
                      -> Self {
        let (tx, rx) = channel::<()>(); // TODO: Maybe have more possible messages than () to shutdown?
        mioco::spawn(move || {
            if let Some(tattle) = tattle_closure_start {
                tattle.call();
            }
            loop {
                select!(
                    rx:r => {
                        if let Some(tattle) = tattle_shutdown {
                            tattle.call();
                        }
                        debug!("Shutting down coroutine");
                        let _ = rx.recv();
                        break;
                    },
                );
            }
        });
        NetHandle(tx)
    }

    /// Shuts down the socket/listener, closing all connections.
    pub fn shutdown(&self) -> Result<(), SendError<()>> {
        self.0.send(())
    }
}

/// Sent in the case of an error that should be sent to the peer.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NetworkError {
    /// If the versions mismatch sufficently to become incompatible with each other.
    VersionMismatch(String, String),
    /// If both peers have should_crash == false, then this error should be sent.
    ///
    /// Do note that this error should not be rewrapped into a reerror, since it would cause a loop.
    /// Instead, it should be logged and ignored, as the connection will be killed shortly after.
    ShouldCrashBothTrue,
}

impl Display for NetworkError {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        match *self {
            NetworkError::VersionMismatch(ref ver1, ref ver2) => {
                write!(fmt,
                       "VersionMismatch: The versions of the client and server attempting to \
                        connect mismatch. ver1: {}, ver2: {}",
                       ver1,
                       ver2)
            }
            NetworkError::ShouldCrashBothTrue => {
                write!(fmt,
                       "ShouldCrashBothTrue: Both peers have should_crash == false.")
            }
        }
    }
}

impl Error for NetworkError {
    fn description(&self) -> &str {
        match *self {
            NetworkError::VersionMismatch(_, _) => {
                "VersionMismatch: The versions of the client and server attempting to connect \
                 mismatch."
            }
            NetworkError::ShouldCrashBothTrue => {
                "ShouldCrashBothTrue: Both peers have should_crash == false."
            }
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            NetworkError::VersionMismatch(_, _) => None,
            NetworkError::ShouldCrashBothTrue => None,
        }
    }
}

/// Messages that can be sent between peers to facilitate vairous actions.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NetworkPacket {
    /// Sent on connection to verify everything is in sync.
    Init {
        /// The curent version of the local game.
        ///
        /// Should be formatted according to Scematic Versioning.
        version: String,
        /// If the local game should crash when an error occours.
        ///
        /// Two peers should not have this as false, because should_crash will send the error to the
        /// remote peer and make it crash instead.
        /// This would cause a infinite loop if both were to do it.
        should_crash: bool,
    },
    /// An error that should crash the game and show an error to the user, but only on a client.
    Error(NetworkError),
    /// Increments a value internally. It's supposed to be used for unit testing.
    #[cfg(test)]
    Test(test::TestValToModify),
}

/// Parses a str to a SocketAddr.
///
/// This is a function because while str implements ToSocketAddrs, it requires a good bit of boilerplate to use.
///
/// #Panics
/// * Calling with a localhost ip address: Use 127.0.0.1 instead.
/// * Calling with an ip address that resolves to more than 1 ip address.
pub fn ip(ip_addr: &str) -> SocketAddr {
    if ip_addr.starts_with("localhost") {
        panic!("Because localhost can resolve to both 127.0.0.1, and the vairous IPV6 versions \
                of 127.0.0.1, it may not be used. Please instead use 127.0.0.1");
    }
    let mut iter = ip_addr.to_socket_addrs().unwrap();
    let ip = iter.next().unwrap();
    if iter.next() != None {
        panic!("The given ip to net::ip() resolved to more than 1 SocketAddr");
    }
    ip
}

fn deserialize_packet(to_de: &[u8]) -> Result<NetworkPacket, DeserializeError> {
    deserialize(to_de)
}

/// Returns the length of a given packet, or a None if the first four bytes do not match NET_MAGIC_NUMBER.
fn get_packet_length(to_ln: [u8; 6]) -> Option<u16> {
    let (first_four, next_two) = to_ln.split_at(4);
    let should_be_magic_num = LittleEndian::read_u32(&first_four);
    if should_be_magic_num != NET_MAGIC_NUMBER {
        return None;
    }
    let length = LittleEndian::read_u16(&next_two);
    Some(length)
}

fn seralize_packet(to_ser: &NetworkPacket) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    result.write_u32::<LittleEndian>(NET_MAGIC_NUMBER).unwrap();   // No possible errors here.
    // The NET_MAGIC_NUMBER is used before every packet, so incase the stream is desynced for whatever reason, the game doesn't just read arbratrary data and crash badly.
    // Instead, it can either recover somehow, by disconnecting and reconnecting, or just erroring gracefully.
    let mut encoded = serialize(to_ser, SizeLimit::Infinite).unwrap();
    // Since the size limit is infinite and i'm not encoding to a stream, there is no error and I can safely unwrap();
    result.write_u16::<LittleEndian>(encoded.len() as u16).unwrap();
    result.append(&mut encoded);
    result
}
<|endoftext|>


<commit_before>use rustwlc::WlcOutput;
use petgraph::graph::NodeIndex;
use uuid::Uuid;
use super::super::LayoutTree;
use super::super::core::container::{Container, ContainerType};
use ::debug_enabled;

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

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

    /// Initializes a workspace and gets the index of the root container
    pub fn init_workspace(&mut self, name: String, output_ix: NodeIndex)
                      -> NodeIndex {
        let geometry = self.tree.get(output_ix)
            .expect("init_workspace: invalid output").get_geometry()
            .expect("init_workspace: no geometry for output");
        let worksp = Container::new_workspace(name.to_string(), geometry);

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

    /// Switch to the specified workspace
    pub fn switch_to_workspace(&mut self, name: &str) {
        let maybe_active_ix = self.active_container
            .or_else(|| {
                let new_active = self.tree.follow_path(self.tree.root_ix());
                match self.tree[new_active].get_type() {
                    ContainerType::View | ContainerType::Container => {
                        Some(new_active)
                    },
                    // else try and get the root container
                    _ => self.tree.descendant_of_type(new_active, ContainerType::Container).ok()
                }
            });
        if maybe_active_ix.is_none() {
            warn!("{:#?}", self);
            warn!("No active container, cannot switch");
            return;
        }
        let active_ix = maybe_active_ix.unwrap();
        // Get the old (current) workspace
        let old_worksp_ix: NodeIndex;
        if let Ok(index) = self.tree.ancestor_of_type(active_ix, ContainerType::Workspace) {
            old_worksp_ix = index;
            trace!("Switching to workspace {}", name);
        } else {
            match self.tree[active_ix].get_type() {
                ContainerType::Workspace => {
                    old_worksp_ix = active_ix;
                    trace!("Switching to workspace {}", name);
                },
                _ => {
                    warn!("Could not find old workspace, could not set invisible");
                    return;
                }
            }
        }
        // Get the new workspace, or create one if it doesn't work
        let mut workspace_ix = self.get_or_make_workspace(name);
        if old_worksp_ix == workspace_ix {
            return;
        }
        let old_worksp_parent_ix = self.tree.parent_of(old_worksp_ix)
            .expect("Old workspace had no parent");
        let new_worksp_parent_ix = self.tree.parent_of(workspace_ix)
            .expect("New workspace had no parent");
        // Only set the old one to be invisible if new and old share output.
        if new_worksp_parent_ix == old_worksp_parent_ix {
            // Set the old one to invisible
            self.tree.set_family_visible(old_worksp_ix, false);
        }
        // Set the new one to visible
        self.tree.set_family_visible(workspace_ix, true);
        // Focus on the new output
        match self.tree[new_worksp_parent_ix] {
            Container::Output { handle, .. } => {
                WlcOutput::focus(Some(handle))
            },
            _ => unreachable!()
        }
        // Delete the old workspace if it has no views on it
        self.active_container = None;
        if self.tree.descendant_of_type(old_worksp_ix, ContainerType::View).is_err() {
            let siblings = self.tree.children_of(old_worksp_parent_ix);
            // Only remove if it's **NOT** the only workspace on the output.
            if siblings.len() > 1 {
                trace!("Removing workspace: {:?}", self.tree[old_worksp_ix].get_name()
                    .expect("Workspace had no name"));
                if let Err(err) = self.remove_workspace(old_worksp_ix) {
                    warn!("Tried to remove empty workspace {:#?}, error: {:?}",
                        old_worksp_ix, err);
                    debug!("{:#?}", self);
                    panic!("Could not remove old workspace");
                }
            }
        }
        workspace_ix = self.tree.workspace_ix_by_name(name)
            .expect("Workspace we just made was deleted!");
        let active_ix = self.tree.follow_path(workspace_ix);
        match self.tree[active_ix].get_type() {
            ContainerType::View  => {
                match self.tree[active_ix] {
                    Container::View { id, ..} => {
                        self.focus_on(id).unwrap_or_else(|_| {
                            warn!("Could not focus on {:?}", id);
                        });
                    },
                    _ => unreachable!()
                }
                self.set_active_node(active_ix)
                    .expect("Could not set new active node");
                self.tree.set_ancestor_paths_active(active_ix);
                self.validate();
                self.validate_path();
                return;
            },
            _ => {
                self.active_container = self.tree
                    .descendant_of_type(active_ix, ContainerType::View)
                    .or_else(|_| self.tree.descendant_of_type(active_ix,
                                                              ContainerType::Container)).ok();
                match self.tree[self.active_container.expect("Workspace had NO children!")] {
                    Container::View { floating, .. } => {
                        if !floating {
                            self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                        } else {
                            let root_c_ix = *self.tree.children_of(workspace_ix).get(0)
                            .expect("The workspace we are switching to had no root container");
                            self.tree.set_ancestor_paths_active(root_c_ix);
                        }
                    },
                    Container::Container { .. } => {
                        self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                    }
                    _ => unreachable!()
                };
            }
        }
        trace!("Focusing on next container");
        self.focus_on_next_container(workspace_ix);
        self.validate();
        self.validate_path();
    }

    /// Moves the active container to a new workspace.
    pub fn send_active_to_workspace(&mut self, name: &str) {
        if let Some(active_ix) = self.active_container {
            let id = self.tree[active_ix].get_id();
            self.send_to_workspace(id, name);
        }
    }
    /// Moves a container to a new workspace
    pub fn send_to_workspace(&mut self, id: Uuid, name: &str) {
        let node_ix = self.tree.lookup_id(id);
        // Ensure focus
        if let Some(active_ix) = node_ix.or(self.active_container) {
            let curr_work_ix = self.active_ix_of(ContainerType::Workspace)
                .expect("send_active: Not currently in a workspace!");
            if active_ix == self.tree.children_of(curr_work_ix)[0] {
                warn!("Tried to move the root container of a workspace, aborting move");
                return;
            }
            let next_work_ix = self.get_or_make_workspace(name);

            // Check if the workspaces are the same
            if next_work_ix == curr_work_ix {
                trace!("Attempted to move a view to the same workspace {}!", name);
                return;
            }
            self.tree.set_family_visible(curr_work_ix, false);

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

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

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

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

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

            self.tree.set_family_visible(curr_work_ix, true);

            if !self.tree[active_ix].floating() {
                self.normalize_container(active_ix);
            }
        }
        let root_ix = self.tree.root_ix();
        self.layout(root_ix);
        self.validate();
        self.validate_path();
    }

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

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

    #[test]
    pub fn switch_empty_workspaces() {
        let mut tree = basic_tree();
        tree.switch_to_workspace("5");
        tree.switch_to_workspace("4");
        tree.switch_to_workspace("5");
        tree.switch_to_workspace("4");
        tree.switch_to_workspace("2");
    }
}
<commit_msg>Only remove workspace if switching on same output<commit_after>use rustwlc::WlcOutput;
use petgraph::graph::NodeIndex;
use uuid::Uuid;
use super::super::LayoutTree;
use super::super::core::container::{Container, ContainerType};
use ::debug_enabled;

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

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

    /// Initializes a workspace and gets the index of the root container
    pub fn init_workspace(&mut self, name: String, output_ix: NodeIndex)
                      -> NodeIndex {
        let geometry = self.tree.get(output_ix)
            .expect("init_workspace: invalid output").get_geometry()
            .expect("init_workspace: no geometry for output");
        let worksp = Container::new_workspace(name.to_string(), geometry);

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

    /// Switch to the specified workspace
    pub fn switch_to_workspace(&mut self, name: &str) {
        let maybe_active_ix = self.active_container
            .or_else(|| {
                let new_active = self.tree.follow_path(self.tree.root_ix());
                match self.tree[new_active].get_type() {
                    ContainerType::View | ContainerType::Container => {
                        Some(new_active)
                    },
                    // else try and get the root container
                    _ => self.tree.descendant_of_type(new_active, ContainerType::Container).ok()
                }
            });
        if maybe_active_ix.is_none() {
            warn!("{:#?}", self);
            warn!("No active container, cannot switch");
            return;
        }
        let active_ix = maybe_active_ix.unwrap();
        // Get the old (current) workspace
        let old_worksp_ix: NodeIndex;
        if let Ok(index) = self.tree.ancestor_of_type(active_ix, ContainerType::Workspace) {
            old_worksp_ix = index;
            trace!("Switching to workspace {}", name);
        } else {
            match self.tree[active_ix].get_type() {
                ContainerType::Workspace => {
                    old_worksp_ix = active_ix;
                    trace!("Switching to workspace {}", name);
                },
                _ => {
                    warn!("Could not find old workspace, could not set invisible");
                    return;
                }
            }
        }
        // Get the new workspace, or create one if it doesn't work
        let mut workspace_ix = self.get_or_make_workspace(name);
        if old_worksp_ix == workspace_ix {
            return;
        }
        let old_worksp_parent_ix = self.tree.parent_of(old_worksp_ix)
            .expect("Old workspace had no parent");
        let new_worksp_parent_ix = self.tree.parent_of(workspace_ix)
            .expect("New workspace had no parent");
        // Only set the old one to be invisible if new and old share output.
        if new_worksp_parent_ix == old_worksp_parent_ix {
            // Set the old one to invisible
            self.tree.set_family_visible(old_worksp_ix, false);
        }
        // Set the new one to visible
        self.tree.set_family_visible(workspace_ix, true);
        // Focus on the new output
        match self.tree[new_worksp_parent_ix] {
            Container::Output { handle, .. } => {
                WlcOutput::focus(Some(handle))
            },
            _ => unreachable!()
        }
        // Delete the old workspace if it has no views on it
        self.active_container = None;
        if self.tree.descendant_of_type(old_worksp_ix, ContainerType::View).is_err() {
            let siblings = self.tree.children_of(old_worksp_parent_ix);
            // Only remove if it's **NOT** the only workspace on the output.
            // AND if the new workspace is on the same output.
            if siblings.len() > 1 && old_worksp_parent_ix == new_worksp_parent_ix {
                trace!("Removing workspace: {:?}", self.tree[old_worksp_ix].get_name()
                    .expect("Workspace had no name"));
                if let Err(err) = self.remove_workspace(old_worksp_ix) {
                    warn!("Tried to remove empty workspace {:#?}, error: {:?}",
                        old_worksp_ix, err);
                    debug!("{:#?}", self);
                    panic!("Could not remove old workspace");
                }
            }
        }
        workspace_ix = self.tree.workspace_ix_by_name(name)
            .expect("Workspace we just made was deleted!");
        let active_ix = self.tree.follow_path(workspace_ix);
        match self.tree[active_ix].get_type() {
            ContainerType::View  => {
                match self.tree[active_ix] {
                    Container::View { id, ..} => {
                        self.focus_on(id).unwrap_or_else(|_| {
                            warn!("Could not focus on {:?}", id);
                        });
                    },
                    _ => unreachable!()
                }
                self.set_active_node(active_ix)
                    .expect("Could not set new active node");
                self.tree.set_ancestor_paths_active(active_ix);
                self.validate();
                self.validate_path();
                return;
            },
            _ => {
                self.active_container = self.tree
                    .descendant_of_type(active_ix, ContainerType::View)
                    .or_else(|_| self.tree.descendant_of_type(active_ix,
                                                              ContainerType::Container)).ok();
                match self.tree[self.active_container.expect("Workspace had NO children!")] {
                    Container::View { floating, .. } => {
                        if !floating {
                            self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                        } else {
                            let root_c_ix = *self.tree.children_of(workspace_ix).get(0)
                            .expect("The workspace we are switching to had no root container");
                            self.tree.set_ancestor_paths_active(root_c_ix);
                        }
                    },
                    Container::Container { .. } => {
                        self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                    }
                    _ => unreachable!()
                };
            }
        }
        trace!("Focusing on next container");
        self.focus_on_next_container(workspace_ix);
        self.validate();
        self.validate_path();
    }

    /// Moves the active container to a new workspace.
    pub fn send_active_to_workspace(&mut self, name: &str) {
        if let Some(active_ix) = self.active_container {
            let id = self.tree[active_ix].get_id();
            self.send_to_workspace(id, name);
        }
    }
    /// Moves a container to a new workspace
    pub fn send_to_workspace(&mut self, id: Uuid, name: &str) {
        let node_ix = self.tree.lookup_id(id);
        // Ensure focus
        if let Some(active_ix) = node_ix.or(self.active_container) {
            let curr_work_ix = self.active_ix_of(ContainerType::Workspace)
                .expect("send_active: Not currently in a workspace!");
            if active_ix == self.tree.children_of(curr_work_ix)[0] {
                warn!("Tried to move the root container of a workspace, aborting move");
                return;
            }
            let next_work_ix = self.get_or_make_workspace(name);

            // Check if the workspaces are the same
            if next_work_ix == curr_work_ix {
                trace!("Attempted to move a view to the same workspace {}!", name);
                return;
            }
            self.tree.set_family_visible(curr_work_ix, false);

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

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

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

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

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

            self.tree.set_family_visible(curr_work_ix, true);

            if !self.tree[active_ix].floating() {
                self.normalize_container(active_ix);
            }
        }
        let root_ix = self.tree.root_ix();
        self.layout(root_ix);
        self.validate();
        self.validate_path();
    }

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

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

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


<commit_before>use yaml_file_handler::yaml_handler::FileHandler as YamlFileHandler;
use bear_lib_terminal::geometry::{Point, Rect};
use bear_lib_terminal::terminal;
use clap::App as Clapp;
use std::cmp::max;
use std::str::FromStr;
use std::net::ToSocketAddrs;
use std::env::home_dir;
use std::path::PathBuf;
use io;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Options {
	pub name  : String,
	pub server: String,
	pub splash: i32,
}

impl Options {
	/// Parses commandline arguments into an [`Options`](#) instance
	///
	/// Optionally reads from a config file in [YAML](http://yaml.org) format, however commandline arguments take preference thereover.
	/// The config file format is trivial: all root keys and values are of the same name and format as long commandline arguments,
	/// see `"example/config.yml"`.
	pub fn parse() -> Options {
		const USAGE: &'static str = "-c --config=[conf]   'Sets config file to load, values will be overriden by commandline args'
		                             -n --name   [name]   'Sets username, will prompt if not specified nor determined'
		                                --splash=[splash] 'Sets the amount of milliseconds the splash will be shown for (0 — off; default/invalid — 1000)'
		                             -s --server [server] 'Sets the server to connect to'";

		let matches = Clapp::new("chattium-oxide-client").version(env!("CARGO_PKG_VERSION"))
		                                                 .author("nabijaczleweli <nabijaczleweli@gmail.com>")
		                                                 .about("Chat client for chattium-oxide-server")
		                                                 .args_from_usage(USAGE)
		                                                 .get_matches();
		let mut name  : Option<String> = None;
		let mut server: Option<String> = None;
		let mut splash: Option<String> = Some("1000".to_string());

		if let Some(conf) = matches.value_of("conf") {
			let mut yaml = YamlFileHandler::new();
			if yaml.add_files(vec![conf]) {
				if let Some(yaml) = yaml.read_all_files().as_ref().map(|all| {
					let mut b = PathBuf::from(conf);
					b.set_extension("");
					&all[b.file_name().unwrap().to_str().unwrap()]
				}) {
					name   = yaml["name"]  .as_str().map(|n| n.to_string());
					server = yaml["server"].as_str().map(|s| s.to_string());
					splash = yaml["splash"].as_str().map(|s| s.to_string());
				}
			}
		}

		if let Some(cname)   = matches.value_of("name")   {if cname.len()   > 0 {name   = Some(cname.to_string())}}
		if let Some(cserver) = matches.value_of("server") {if cserver.len() > 0 {server = Some(cserver.to_string())}}
		if let Some(csplash) = matches.value_of("splash") {if csplash.len() > 0 {splash = Some(csplash.to_string())}}

		if name.is_none() {
			name = Some(match username() {
				Some(uname) =>
					if let Some("") = matches.value_of("name") {
						uname
					} else {
						terminal::print_xy(0, 0, &*&format!("Determined your username to {}.
If that's incorrect, type in your name now. Otherwise, hit <Return>: ", uname));
						terminal::refresh();
						match terminal::read_str(Point::new(0, 2), terminal::state::size().width).into_iter().flat_map(io::maybe_trimmed).next() {
							Some(rname) => rname,
							None        => uname,
						}
					},
				None => {
					let mut tname: Option<String> = None;
					while tname.is_none() {
						terminal::print_xy(0, 0, "No username specified and none could be determined.");
						let second_line = "Please type in your username now: ";
						terminal::print_xy(0, 1, second_line);
						terminal::refresh();
						for rname in terminal::read_str(Point::new(second_line.len() as i32, 1), terminal::state::size().width).into_iter().flat_map(io::maybe_trimmed) {
							tname = Some(rname);
						}
					}
					tname.unwrap()
				},
			});
			terminal::clear(None);
		}

		if server.is_some() {
			server = match validate_address(server.unwrap()) {
				Ok(sserver) => Some(sserver),
				Err(error)  => {
					terminal::print_xy(0, 3, &*&error);
					None
				},
			}
		}
		if server.is_none() {
			terminal::print_xy(0, 0, "No server specified.\nPlease type in the server address now: ");
			terminal::refresh();
			loop {
				if let Some(rserver) = terminal::read_str(Point::new(0, 2), terminal::state::size().width).into_iter().flat_map(io::maybe_trimmed).next() {
					match validate_address(rserver) {
						Ok(rserver) => {
							server = Some(rserver);
							break;
						},
						Err(error) => {
							terminal::clear(Some(Rect::from_size(Point::new(0, 3), terminal::state::size())));
							terminal::print_xy(0, 3, &*&error);
							terminal::refresh();
						},
					}
				}
			}
			terminal::clear(None);
			terminal::refresh();
		}

		assert!(name.is_some());
		assert!(server.is_some());
		assert!(splash.is_some());
		Options{
			name: name.unwrap(),
			server: server.unwrap(),
			splash: max(i32::from_str(&*&splash.unwrap()).unwrap_or(1000), 0),
		}
	}
}

/// Extract username from last segment of user's homedir
fn username() -> Option<String> {
	match home_dir() {
		Some(pbuf) =>
			match pbuf.as_path().file_name() {
				Some(fname) => fname.to_str().map(|string| string.to_string()),
				None => None,
			},
		None => None,
	}
}

fn validate_address(address: String) -> Result<String, String> {
	match (&*&address).to_socket_addrs() {
		Ok(_)      => Ok(address),
		Err(error) => Err(format!("{}", error)),
	}
}
<commit_msg>Formatting<commit_after>use yaml_file_handler::yaml_handler::FileHandler as YamlFileHandler;
use bear_lib_terminal::geometry::{Point, Rect};
use bear_lib_terminal::terminal;
use clap::App as Clapp;
use std::cmp::max;
use std::str::FromStr;
use std::net::ToSocketAddrs;
use std::env::home_dir;
use std::path::PathBuf;
use io;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Options {
	pub name  : String,
	pub server: String,
	pub splash: i32,
}

impl Options {
	/// Parses commandline arguments into an [`Options`](#) instance
	///
	/// Optionally reads from a config file in [YAML](http://yaml.org) format, however commandline arguments take preference thereover.
	/// The config file format is trivial: all root keys and values are of the same name and format as long commandline arguments,
	/// see `"example/config.yml"`.
	pub fn parse() -> Options {
		const USAGE: &'static str = "-c --config=[conf]   'Sets config file to load, values will be overriden by commandline args'
		                             -n --name   [name]   'Sets username, will prompt if not specified nor determined'
		                                --splash=[splash] 'Sets the amount of milliseconds the splash will be shown for (0 — off; default/invalid — 1000)'
		                             -s --server [server] 'Sets the server to connect to'";

		let matches = Clapp::new("chattium-oxide-client").version(env!("CARGO_PKG_VERSION"))
		                                                 .author("nabijaczleweli <nabijaczleweli@gmail.com>")
		                                                 .about("Chat client for chattium-oxide-server")
		                                                 .args_from_usage(USAGE)
		                                                 .get_matches();
		let mut name  : Option<String> = None;
		let mut server: Option<String> = None;
		let mut splash: Option<String> = Some("1000".to_string());

		if let Some(conf) = matches.value_of("conf") {
			let mut yaml = YamlFileHandler::new();
			if yaml.add_files(vec![conf]) {
				if let Some(yaml) = yaml.read_all_files().as_ref().map(|all| {
					let mut b = PathBuf::from(conf);
					b.set_extension("");
					&all[b.file_name().unwrap().to_str().unwrap()]
				}) {
					name   = yaml["name"]  .as_str().map(|n| n.to_string());
					server = yaml["server"].as_str().map(|s| s.to_string());
					splash = yaml["splash"].as_str().map(|s| s.to_string());
				}
			}
		}

		if let Some(cname)   = matches.value_of("name")   {if cname.len()   > 0 {name   = Some(cname.to_string())}}
		if let Some(cserver) = matches.value_of("server") {if cserver.len() > 0 {server = Some(cserver.to_string())}}
		if let Some(csplash) = matches.value_of("splash") {if csplash.len() > 0 {splash = Some(csplash.to_string())}}

		if name.is_none() {
			name = Some(match username() {
				Some(uname) =>
					if let Some("") = matches.value_of("name") {
						uname
					} else {
						terminal::print_xy(0, 0, &*&format!("Determined your username to {}.
If that's incorrect, type in your name now. Otherwise, hit <Return>: ", uname));
						terminal::refresh();
						match terminal::read_str(Point::new(0, 2), terminal::state::size().width).into_iter().flat_map(io::maybe_trimmed).next() {
							Some(rname) => rname,
							None        => uname,
						}
					},
				None => {
					let mut tname: Option<String> = None;
					while tname.is_none() {
						terminal::print_xy(0, 0, "No username specified and none could be determined.");
						let second_line = "Please type in your username now: ";
						terminal::print_xy(0, 1, second_line);
						terminal::refresh();
						for rname in terminal::read_str(Point::new(second_line.len() as i32, 1), terminal::state::size().width).into_iter().flat_map(io::maybe_trimmed) {
							tname = Some(rname);
						}
					}
					tname.unwrap()
				},
			});
			terminal::clear(None);
		}

		if server.is_some() {
			server = match validate_address(server.unwrap()) {
				Ok(sserver) => Some(sserver),
				Err(error)  => {
					terminal::print_xy(0, 3, &*&error);
					None
				},
			}
		}
		if server.is_none() {
			terminal::print_xy(0, 0, "No server specified.\nPlease type in the server address now: ");
			terminal::refresh();
			loop {
				if let Some(rserver) = terminal::read_str(Point::new(0, 2), terminal::state::size().width).into_iter().flat_map(io::maybe_trimmed).next() {
					match validate_address(rserver) {
						Ok(rserver) => {
							server = Some(rserver);
							break;
						},
						Err(error) => {
							terminal::clear(Some(Rect::from_size(Point::new(0, 3), terminal::state::size())));
							terminal::print_xy(0, 3, &*&error);
							terminal::refresh();
						},
					}
				}
			}
			terminal::clear(None);
			terminal::refresh();
		}

		assert!(name.is_some());
		assert!(server.is_some());
		assert!(splash.is_some());
		Options{
			name  : name.unwrap(),
			server: server.unwrap(),
			splash: max(i32::from_str(&*&splash.unwrap()).unwrap_or(1000), 0),
		}
	}
}

/// Extract username from last segment of user's homedir
fn username() -> Option<String> {
	match home_dir() {
		Some(pbuf) =>
			match pbuf.as_path().file_name() {
				Some(fname) => fname.to_str().map(|string| string.to_string()),
				None => None,
			},
		None => None,
	}
}

fn validate_address(address: String) -> Result<String, String> {
	match (&*&address).to_socket_addrs() {
		Ok(_)      => Ok(address),
		Err(error) => Err(format!("{}", error)),
	}
}
<|endoftext|>