use std::cell::RefCell;
use std::cmp::{max, min};
use std::collections::HashMap;
use std::f64::consts::TAU;
use std::iter::zip;
use std::rc::Rc;
use anyhow::Context;
use arrayvec::ArrayVec;
use niri_config::{Action, Config};
use niri_ipc::SizeChange;
use pango::{Alignment, FontDescription};
use pangocairo::cairo::{self, ImageSurface};
use smithay::backend::allocator::Fourcc;
use smithay::backend::input::TouchSlot;
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
use smithay::backend::renderer::element::Kind;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::backend::renderer::{ExportMem, Texture as _};
use smithay::input::keyboard::{Keysym, ModifiersState};
use smithay::output::{Output, WeakOutput};
use smithay::utils::{Buffer, Physical, Point, Rectangle, Scale, Size, Transform};
use crate::animation::{Animation, Clock};
use crate::layout::floating::DIRECTIONAL_MOVE_PX;
use crate::niri_render_elements;
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
use crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};
use crate::render_helpers::{render_to_texture, RenderTarget};
use crate::utils::to_physical_precise_round;
const SELECTION_BORDER: i32 = 2;
const PADDING: i32 = 8;
const RADIUS: i32 = 16;
const FONT: &str = "sans 14px";
const BORDER: i32 = 4;
const TEXT_HIDE_P: &str =
"Press <span face='mono' bgcolor='#2C2C2C'> Space </span> to save the screenshot.\n\
Press <span face='mono' bgcolor='#2C2C2C'> P </span> to hide the pointer.";
const TEXT_SHOW_P: &str =
"Press <span face='mono' bgcolor='#2C2C2C'> Space </span> to save the screenshot.\n\
Press <span face='mono' bgcolor='#2C2C2C'> P </span> to show the pointer.";
// Ideally the screenshot UI should support cross-output selections. However, that poses some
// technical challenges when the outputs have different scales and such. So, this implementation
// allows only single-output selections for now.
//
// As a consequence of this, selection coordinates are in output-local coordinate space.
#[allow(clippy::large_enum_variant)]
pub enum ScreenshotUi {
Closed {
last_selection: Option<(WeakOutput, Rectangle<i32, Physical>)>,
clock: Clock,
config: Rc<RefCell<Config>>,
},
Open {
selection: (Output, Point<i32, Physical>, Point<i32, Physical>),
output_data: HashMap<Output, OutputData>,
button: Button,
show_pointer: bool,
open_anim: Animation,
clock: Clock,
config: Rc<RefCell<Config>>,
},
}
pub enum Button {
Up,
Down {
touch_slot: Option<TouchSlot>,
on_capture_button: bool,
last_pos: (Output, Point<i32, Physical>),
},
}
pub struct OutputData {
size: Size<i32, Physical>,
scale: f64,
transform: Transform,
// Output, screencast, screen capture.
screenshot: [OutputScreenshot; 3],
buffers: [SolidColorBuffer; 8],
locations: [Point<i32, Physical>; 8],
panel: Option<(TextureBuffer<GlesTexture>, TextureBuffer<GlesTexture>)>,
}
pub struct OutputScreenshot {
texture: GlesTexture,
buffer: PrimaryGpuTextureRenderElement,
pointer: Option<PrimaryGpuTextureRenderElement>,
}
niri_render_elements! {
ScreenshotUiRenderElement => {
Screenshot = PrimaryGpuTextureRenderElement,
SolidColor = SolidColorRenderElement,
}
}
impl Button {
fn is_down(&self) -> bool {
matches!(self, Self::Down { .. })
}
fn is_dragging_selection(&