use crate::*;
use crate::options_::options_get_number_;
use crate::options_::options_get_string_;
struct layout_sets_entry {
name: SyncCharPtr,
arrange: Option<unsafe fn(*mut window)>,
}
impl layout_sets_entry {
const fn new(name: &'static CStr, arrange: unsafe fn(*mut window)) -> Self {
Self {
name: SyncCharPtr::new(name),
arrange: Some(arrange),
}
}
}
const LAYOUT_SETS_LEN: usize = 7;
static LAYOUT_SETS: [layout_sets_entry; LAYOUT_SETS_LEN] = [
layout_sets_entry::new(c"even-horizontal", layout_set_even_h),
layout_sets_entry::new(c"even-vertical", layout_set_even_v),
layout_sets_entry::new(c"main-horizontal", layout_set_main_h),
layout_sets_entry::new(c"main-horizontal-mirrored", layout_set_main_h_mirrored),
layout_sets_entry::new(c"main-vertical", layout_set_main_v),
layout_sets_entry::new(c"main-vertical-mirrored", layout_set_main_v_mirrored),
layout_sets_entry::new(c"tiled", layout_set_tiled),
];
unsafe fn layout_set_first_tiled(w: *mut window) -> *mut window_pane {
unsafe {
for wp in tailq_foreach::<_, discr_entry>(&raw mut (*w).panes).map(NonNull::as_ptr) {
if !(*wp).layout_cell.is_null() && layout_cell_is_tiled((*wp).layout_cell) != 0 {
return wp;
}
}
null_mut()
}
}
pub unsafe fn layout_set_lookup(name: *const u8) -> i32 {
unsafe {
let mut matched: i32 = -1;
for (i, ls) in LAYOUT_SETS.iter().enumerate() {
if libc::strcmp(ls.name.as_ptr(), name) == 0 {
return i as i32;
}
}
for (i, ls) in LAYOUT_SETS.iter().enumerate() {
if libc::strncmp(ls.name.as_ptr(), name, strlen(name)) == 0 {
if matched != -1 {
return -1;
}
matched = i as i32;
}
}
matched
}
}
pub unsafe fn layout_set_select(w: *mut window, mut layout: u32) -> u32 {
unsafe {
if layout > LAYOUT_SETS_LEN as u32 - 1 {
layout = LAYOUT_SETS_LEN as u32 - 1;
}
if let Some(arrange) = LAYOUT_SETS[layout as usize].arrange {
arrange(w);
}
(*w).lastlayout = layout as i32;
layout
}
}
pub unsafe fn layout_set_next(w: *mut window) -> u32 {
unsafe {
let mut layout: u32;
if (*w).lastlayout == -1 {
layout = 0;
} else {
layout = ((*w).lastlayout + 1) as u32;
if layout > LAYOUT_SETS_LEN as u32 - 1 {
layout = 0;
}
}
if let Some(arrange) = LAYOUT_SETS[layout as usize].arrange {
arrange(w);
}
(*w).lastlayout = layout as i32;
layout
}
}
pub unsafe fn layout_set_previous(w: *mut window) -> u32 {
unsafe {
let mut layout: u32;
if (*w).lastlayout == -1 {
layout = (LAYOUT_SETS_LEN - 1) as u32;
} else {
layout = (*w).lastlayout as u32;
if layout == 0 {
layout = (LAYOUT_SETS_LEN - 1) as u32;
} else {
layout -= 1;
}
}
if let Some(arrange) = LAYOUT_SETS[layout as usize].arrange {
arrange(w);
}
(*w).lastlayout = layout as i32;
layout
}
}
pub unsafe fn layout_set_even(w: *mut window, type_: layout_type) {
let __func__ = c!("layout_set_even");
unsafe {
let mut sx: u32;
let mut sy: u32;
layout_print_cell((*w).layout_root, __func__, 1);
let n = window_count_panes(w);
if n <= 1 {
return;
}
layout_free(w, 0);
let lc = layout_create_cell(null_mut());
(*w).layout_root = lc;
if type_ == layout_type::LAYOUT_LEFTRIGHT {
sx = (n * (PANE_MINIMUM + 1)) - 1;
if sx < (*w).sx {
sx = (*w).sx;
}
sy = (*w).sy;
} else {
sy = (n * (PANE_MINIMUM + 1)) - 1;
if sy < (*w).sy {
sy = (*w).sy;
}
sx = (*w).sx;
}
layout_set_size(lc, sx, sy, 0, 0);
layout_make_node(lc, type_);
for wp in tailq_foreach::<_, discr_entry>(&raw mut (*w).panes).map(NonNull::as_ptr) {
let lcnew = layout_create_cell(lc);
layout_make_leaf(lcnew, wp);
(*lcnew).sx = (*w).sx;
(*lcnew).sy = (*w).sy;
tailq_insert_tail(&raw mut (*lc).cells, lcnew);
}
layout_spread_cell(w, lc);
layout_fix_offsets(w);
layout_fix_panes(w, null_mut());
layout_print_cell((*w).layout_root, __func__, 1);
window_resize(w, (*lc).sx, (*lc).sy, -1, -1);
notify_window(c"window-layout-changed", w);
server_redraw_window(w);
}
}
unsafe fn layout_set_even_h(w: *mut window) {
unsafe {
layout_set_even(w, layout_type::LAYOUT_LEFTRIGHT);
}
}
unsafe fn layout_set_even_v(w: *mut window) {
unsafe {
layout_set_even(w, layout_type::LAYOUT_TOPBOTTOM);
}
}
pub unsafe fn layout_set_main_h(w: *mut window) {
let __func__ = c!("layout_set_main_h");
unsafe {
let mut cause = null_mut();
layout_print_cell((*w).layout_root, __func__, 1);
let mut n = window_count_panes(w);
if n <= 1 {
return;
}
n -= 1;
let sy = (*w).sy - 1;
let mut s = options_get_string_((*w).options, "main-pane-height");
let mut mainh = args_string_percentage(s, 0, sy as i64, sy as i64, &raw mut cause) as u32;
if !cause.is_null() {
mainh = 24;
free_(cause);
}
let mut otherh: u32;
if mainh + PANE_MINIMUM >= sy {
if sy <= PANE_MINIMUM + PANE_MINIMUM {
mainh = PANE_MINIMUM;
} else {
mainh = sy - PANE_MINIMUM;
}
otherh = PANE_MINIMUM;
} else {
s = options_get_string_((*w).options, "other-pane-height");
otherh = args_string_percentage(s, 0, sy as i64, sy as i64, &raw mut cause) as u32;
if !cause.is_null() || otherh == 0 {
otherh = sy - mainh;
free_(cause);
} else if otherh > sy || sy - otherh < mainh {
otherh = sy - mainh;
} else {
mainh = sy - otherh;
}
}
let mut sx = (n * (PANE_MINIMUM + 1)) - 1;
if sx < (*w).sx {
sx = (*w).sx;
}
layout_free(w, 1);
let lc = layout_create_cell(null_mut());
(*w).layout_root = lc;
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lc, layout_type::LAYOUT_TOPBOTTOM);
let wpmain = layout_set_first_tiled(w);
let lcmain = (*wpmain).layout_cell;
(*lcmain).parent = lc;
layout_set_size(lcmain, sx, mainh, 0, 0);
tailq_insert_tail(&raw mut (*lc).cells, lcmain);
if n == 1 {
let mut wp = tailq_next::<_, _, discr_entry>(wpmain);
while !wp.is_null() && layout_cell_is_tiled((*wp).layout_cell) == 0 {
wp = tailq_next::<_, _, discr_entry>(wp);
}
tailq_insert_tail(&raw mut (*lc).cells, (*wp).layout_cell);
(*(*wp).layout_cell).parent = lc;
} else {
let lcother = layout_create_cell(lc);
layout_set_size(lcother, sx, otherh, 0, 0);
layout_make_node(lcother, layout_type::LAYOUT_LEFTRIGHT);
tailq_insert_tail(&raw mut (*lc).cells, lcother);
for wp in tailq_foreach::<_, discr_entry>(&raw mut (*w).panes).map(NonNull::as_ptr) {
if wp == wpmain {
continue;
}
let lcchild = (*wp).layout_cell;
tailq_insert_tail(&raw mut (*lcother).cells, lcchild);
(*lcchild).parent = lcother;
if layout_cell_is_tiled(lcchild) != 0 {
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
}
}
layout_spread_cell(w, lcother);
}
layout_fix_offsets(w);
layout_fix_panes(w, null_mut());
layout_print_cell((*w).layout_root, __func__, 1);
window_resize(w, (*lc).sx, (*lc).sy, -1, -1);
notify_window(c"window-layout-changed", w);
server_redraw_window(w);
}
}
pub unsafe fn layout_set_main_h_mirrored(w: *mut window) {
let __func__ = c!("layout_set_main_h_mirrored");
unsafe {
let mut otherh: u32;
let mut cause: *mut u8 = null_mut();
layout_print_cell((*w).layout_root, __func__, 1);
let mut n = window_count_panes(w);
if n <= 1 {
return;
}
n -= 1;
let sy = (*w).sy - 1;
let s = options_get_string_((*w).options, "main-pane-height");
let mut mainh = args_string_percentage(s, 0, sy as i64, sy as i64, &raw mut cause) as u32;
if !cause.is_null() {
mainh = 24;
free_(cause);
}
if mainh + PANE_MINIMUM >= sy {
if sy <= PANE_MINIMUM + PANE_MINIMUM {
mainh = PANE_MINIMUM;
} else {
mainh = sy - PANE_MINIMUM;
}
otherh = PANE_MINIMUM;
} else {
let s = options_get_string_((*w).options, "other-pane-height");
otherh = args_string_percentage(s, 0, sy as i64, sy as i64, &raw mut cause) as u32;
if !cause.is_null() || otherh == 0 {
otherh = sy - mainh;
free_(cause);
} else if otherh > sy || sy - otherh < mainh {
otherh = sy - mainh;
} else {
mainh = sy - otherh;
}
}
let mut sx = (n * (PANE_MINIMUM + 1)) - 1;
if sx < (*w).sx {
sx = (*w).sx;
}
layout_free(w, 1);
let lc = layout_create_cell(null_mut());
(*w).layout_root = lc;
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lc, layout_type::LAYOUT_TOPBOTTOM);
let wpmain = layout_set_first_tiled(w);
let lcmain = (*wpmain).layout_cell;
(*lcmain).parent = lc;
layout_set_size(lcmain, sx, mainh, 0, 0);
tailq_insert_tail(&raw mut (*lc).cells, lcmain);
if n == 1 {
let mut wp = tailq_next::<_, _, discr_entry>(wpmain);
while !wp.is_null() && layout_cell_is_tiled((*wp).layout_cell) == 0 {
wp = tailq_next::<_, _, discr_entry>(wp);
}
tailq_insert_head(&raw mut (*lc).cells, (*wp).layout_cell);
(*(*wp).layout_cell).parent = lc;
} else {
let lcother = layout_create_cell(lc);
layout_set_size(lcother, sx, otherh, 0, 0);
layout_make_node(lcother, layout_type::LAYOUT_LEFTRIGHT);
tailq_insert_head(&raw mut (*lc).cells, lcother);
for wp in tailq_foreach::<_, discr_entry>(&raw mut (*w).panes).map(NonNull::as_ptr) {
if wp == wpmain {
continue;
}
let lcchild = (*wp).layout_cell;
tailq_insert_tail(&raw mut (*lcother).cells, lcchild);
(*lcchild).parent = lcother;
if layout_cell_is_tiled(lcchild) != 0 {
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
}
}
layout_spread_cell(w, lcother);
}
layout_fix_offsets(w);
layout_fix_panes(w, null_mut());
layout_print_cell((*w).layout_root, __func__, 1);
window_resize(w, (*lc).sx, (*lc).sy, -1, -1);
notify_window(c"window-layout-changed", w);
server_redraw_window(w);
}
}
pub unsafe fn layout_set_main_v(w: *mut window) {
let __func__ = c!("layout_set_main_v");
let mut cause = null_mut();
unsafe {
layout_print_cell((*w).layout_root, __func__, 1);
let mut n = window_count_panes(w);
if n <= 1 {
return;
}
n -= 1;
let sx = (*w).sx - 1;
let s = options_get_string_((*w).options, "main-pane-width");
let mut mainw: u32 =
args_string_percentage(s, 0, sx as i64, sx as i64, &raw mut cause) as u32;
if !cause.is_null() {
mainw = 80;
free_(cause);
}
let mut otherw;
if mainw + PANE_MINIMUM >= sx {
if sx <= PANE_MINIMUM + PANE_MINIMUM {
mainw = PANE_MINIMUM;
} else {
mainw = sx - PANE_MINIMUM;
}
otherw = PANE_MINIMUM;
} else {
let s = options_get_string_((*w).options, "other-pane-width");
otherw = args_string_percentage(s, 0, sx as i64, sx as i64, &raw mut cause) as u32;
if !cause.is_null() || otherw == 0 {
otherw = sx - mainw;
free_(cause);
} else if otherw > sx || sx - otherw < mainw {
otherw = sx - mainw;
} else {
mainw = sx - otherw;
}
}
let mut sy = (n * (PANE_MINIMUM + 1)) - 1;
if sy < (*w).sy {
sy = (*w).sy;
}
layout_free(w, 1);
let lc = layout_create_cell(null_mut());
(*w).layout_root = lc;
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lc, layout_type::LAYOUT_LEFTRIGHT);
let wpmain = layout_set_first_tiled(w);
let lcmain = (*wpmain).layout_cell;
(*lcmain).parent = lc;
layout_set_size(lcmain, mainw, sy, 0, 0);
tailq_insert_tail(&raw mut (*lc).cells, lcmain);
if n == 1 {
let mut wp = tailq_next::<_, _, discr_entry>(wpmain);
while !wp.is_null() && layout_cell_is_tiled((*wp).layout_cell) == 0 {
wp = tailq_next::<_, _, discr_entry>(wp);
}
tailq_insert_tail(&raw mut (*lc).cells, (*wp).layout_cell);
(*(*wp).layout_cell).parent = lc;
} else {
let lcother = layout_create_cell(lc);
layout_set_size(lcother, otherw, sy, 0, 0);
layout_make_node(lcother, layout_type::LAYOUT_TOPBOTTOM);
tailq_insert_tail(&raw mut (*lc).cells, lcother);
for wp in tailq_foreach::<_, discr_entry>(&raw mut (*w).panes).map(NonNull::as_ptr) {
if wp == wpmain {
continue;
}
let lcchild = (*wp).layout_cell;
tailq_insert_tail(&raw mut (*lcother).cells, lcchild);
(*lcchild).parent = lcother;
if layout_cell_is_tiled(lcchild) != 0 {
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
}
}
layout_spread_cell(w, lcother);
}
layout_fix_offsets(w);
layout_fix_panes(w, null_mut());
layout_print_cell((*w).layout_root, __func__, 1);
window_resize(w, (*lc).sx, (*lc).sy, -1, -1);
notify_window(c"window-layout-changed", w);
server_redraw_window(w);
}
}
pub unsafe fn layout_set_main_v_mirrored(w: *mut window) {
let __func__ = c!("layout_set_main_v_mirrored");
unsafe {
let mut cause: *mut u8 = null_mut();
layout_print_cell((*w).layout_root, __func__, 1);
let mut n = window_count_panes(w);
if n <= 1 {
return;
}
n -= 1;
let sx = (*w).sx - 1;
let s = options_get_string_((*w).options, "main-pane-width");
let mut mainw = args_string_percentage(s, 0, sx as i64, sx as i64, &raw mut cause) as u32;
if !cause.is_null() {
mainw = 80;
free_(cause);
}
let mut otherw: u32;
if mainw + PANE_MINIMUM >= sx {
if sx <= PANE_MINIMUM + PANE_MINIMUM {
mainw = PANE_MINIMUM;
} else {
mainw = sx - PANE_MINIMUM;
}
otherw = PANE_MINIMUM;
} else {
let s = options_get_string_((*w).options, "other-pane-width");
otherw = args_string_percentage(s, 0, sx as i64, sx as i64, &raw mut cause) as u32;
if !cause.is_null() || otherw == 0 {
otherw = sx - mainw;
free_(cause);
} else if otherw > sx || sx - otherw < mainw {
otherw = sx - mainw;
} else {
mainw = sx - otherw;
}
}
let mut sy = (n * (PANE_MINIMUM + 1)) - 1;
if sy < (*w).sy {
sy = (*w).sy;
}
layout_free(w, 1);
let lc = layout_create_cell(null_mut());
(*w).layout_root = lc;
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lc, layout_type::LAYOUT_LEFTRIGHT);
let wpmain = layout_set_first_tiled(w);
let lcmain = (*wpmain).layout_cell;
(*lcmain).parent = lc;
layout_set_size(lcmain, mainw, sy, 0, 0);
tailq_insert_tail(&raw mut (*lc).cells, lcmain);
if n == 1 {
let mut wp = tailq_next::<_, _, discr_entry>(wpmain);
while !wp.is_null() && layout_cell_is_tiled((*wp).layout_cell) == 0 {
wp = tailq_next::<_, _, discr_entry>(wp);
}
tailq_insert_head(&raw mut (*lc).cells, (*wp).layout_cell);
(*(*wp).layout_cell).parent = lc;
} else {
let lcother = layout_create_cell(lc);
layout_make_node(lcother, layout_type::LAYOUT_TOPBOTTOM);
layout_set_size(lcother, otherw, sy, 0, 0);
tailq_insert_head(&raw mut (*lc).cells, lcother);
for wp in tailq_foreach::<_, discr_entry>(&raw mut (*w).panes).map(NonNull::as_ptr) {
if wp == wpmain {
continue;
}
let lcchild = (*wp).layout_cell;
tailq_insert_tail(&raw mut (*lcother).cells, lcchild);
(*lcchild).parent = lcother;
if layout_cell_is_tiled(lcchild) != 0 {
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
}
}
layout_spread_cell(w, lcother);
}
layout_fix_offsets(w);
layout_fix_panes(w, null_mut());
layout_print_cell((*w).layout_root, __func__, 1);
window_resize(w, (*lc).sx, (*lc).sy, -1, -1);
notify_window(c"window-layout-changed", w);
server_redraw_window(w);
}
}
pub unsafe fn layout_set_tiled(w: *mut window) {
let __func__ = c!("layout_set_tiled");
unsafe {
layout_print_cell((*w).layout_root, __func__, 1);
let n = window_count_panes(w);
if n <= 1 {
return;
}
let max_columns = options_get_number_((*w).options, "tiled-layout-max-columns") as u32;
let mut rows = 1;
let mut columns = 1;
while rows * columns < n {
rows += 1;
if rows * columns < n && (max_columns == 0 || columns < max_columns) {
columns += 1;
}
}
let mut width = ((*w).sx - (columns - 1)) / columns;
if width < PANE_MINIMUM {
width = PANE_MINIMUM;
}
let mut height = ((*w).sy - (rows - 1)) / rows;
if height < PANE_MINIMUM {
height = PANE_MINIMUM;
}
layout_free(w, 0);
let lc = layout_create_cell(null_mut());
(*w).layout_root = lc;
let mut sx = ((width + 1) * columns) - 1;
if sx < (*w).sx {
sx = (*w).sx;
}
let mut sy = ((height + 1) * rows) - 1;
if sy < (*w).sy {
sy = (*w).sy;
}
layout_set_size(lc, sx, sy, 0, 0);
layout_make_node(lc, layout_type::LAYOUT_TOPBOTTOM);
let mut wp = tailq_first(&raw mut (*w).panes);
for j in 0..rows {
if wp.is_null() {
break;
}
let lcrow = layout_create_cell(lc);
layout_set_size(lcrow, (*w).sx, height, 0, 0);
tailq_insert_tail(&raw mut (*lc).cells, lcrow);
if n - (j * columns) == 1 || columns == 1 {
layout_make_leaf(lcrow, wp);
wp = tailq_next::<_, _, discr_entry>(wp);
continue;
}
layout_make_node(lcrow, layout_type::LAYOUT_LEFTRIGHT);
let mut i = 0;
for i_ in 0..columns {
i = i_;
let lcchild = layout_create_cell(lcrow);
layout_set_size(lcchild, width, height, 0, 0);
layout_make_leaf(lcchild, wp);
tailq_insert_tail(&raw mut (*lcrow).cells, lcchild);
wp = tailq_next::<_, _, discr_entry>(wp);
if wp.is_null() {
break;
}
i += 1;
}
if i == columns {
i -= 1;
}
let used = ((i + 1) * (width + 1)) - 1;
if (*w).sx <= used {
continue;
}
let lcchild = tailq_last(&raw mut (*lcrow).cells);
layout_resize_adjust(
w,
lcchild,
layout_type::LAYOUT_LEFTRIGHT,
((*w).sx - used) as i32,
);
}
let used = (rows * height) + rows - 1;
if (*w).sy > used {
let lcrow = tailq_last(&raw mut (*lc).cells);
layout_resize_adjust(
w,
lcrow,
layout_type::LAYOUT_TOPBOTTOM,
((*w).sy - used) as i32,
);
}
layout_fix_offsets(w);
layout_fix_panes(w, null_mut());
layout_print_cell((*w).layout_root, __func__, 1);
window_resize(w, (*lc).sx, (*lc).sy, -1, -1);
notify_window(c"window-layout-changed", w);
server_redraw_window(w);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lookup_exact_names() {
unsafe {
assert_eq!(layout_set_lookup(crate::c!("even-horizontal")), 0);
assert_eq!(layout_set_lookup(crate::c!("even-vertical")), 1);
assert_eq!(layout_set_lookup(crate::c!("tiled")), 6);
assert_eq!(layout_set_lookup(crate::c!("main-horizontal")), 2);
assert_eq!(layout_set_lookup(crate::c!("main-vertical")), 4);
}
}
#[test]
fn lookup_unique_prefix() {
unsafe {
assert_eq!(layout_set_lookup(crate::c!("ti")), 6);
assert_eq!(layout_set_lookup(crate::c!("even-h")), 0);
}
}
#[test]
fn lookup_ambiguous_and_unknown_return_minus_one() {
unsafe {
assert_eq!(layout_set_lookup(crate::c!("even")), -1);
assert_eq!(layout_set_lookup(crate::c!("main-h")), -1);
assert_eq!(layout_set_lookup(crate::c!("does-not-exist")), -1);
}
}
#[test]
fn lookup_every_name_roundtrips() {
unsafe {
for (i, ls) in LAYOUT_SETS.iter().enumerate() {
assert_eq!(
layout_set_lookup(ls.name.as_ptr()),
i as i32,
"name at index {i} did not resolve to itself"
);
}
}
}
#[test]
fn lookup_main_vertical_variants() {
unsafe {
assert_eq!(layout_set_lookup(crate::c!("main-vertical")), 4);
assert_eq!(layout_set_lookup(crate::c!("main-vertical-mirrored")), 5);
assert_eq!(layout_set_lookup(crate::c!("main-v")), -1);
assert_eq!(layout_set_lookup(crate::c!("main-vertical-m")), 5);
}
}
#[test]
fn lookup_empty_string_ambiguous() {
unsafe {
assert_eq!(layout_set_lookup(crate::c!("")), -1);
}
}
#[test]
fn lookup_tiled_prefixes_all_resolve() {
unsafe {
for p in ["t", "ti", "til", "tile", "tiled"] {
let mut s = p.to_string();
s.push('\0');
assert_eq!(layout_set_lookup(s.as_bytes().as_ptr()), 6, "prefix {p:?}");
}
}
}
#[test]
fn lookup_case_sensitive_and_main_ambiguous() {
unsafe {
assert_eq!(layout_set_lookup(crate::c!("MAIN-VERTICAL")), -1);
assert_eq!(layout_set_lookup(crate::c!("main")), -1);
assert_eq!(layout_set_lookup(crate::c!("main-horizontal-mirrored")), 3);
}
}
}