binaryninja/flowgraph/
layout.rs

1//! The [`FlowGraphLayout`] trait allows you to customize the layout of flow graphs.
2
3use crate::flowgraph::{FlowGraph, FlowGraphNode};
4use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
5use crate::string::{BnString, IntoCStr};
6use binaryninjacore_sys::*;
7use std::ffi::c_void;
8use std::ptr::NonNull;
9
10/// Registers a custom [`FlowGraphLayout`], this allows you to customize the layout of flow graphs.
11pub fn register_flowgraph_layout<C: FlowGraphLayout>(
12    name: &str,
13    custom: C,
14) -> (&'static C, CoreFlowGraphLayout) {
15    let renderer = Box::leak(Box::new(custom));
16    let mut callbacks = BNCustomFlowGraphLayout {
17        context: renderer as *mut _ as *mut c_void,
18        layout: Some(cb_layout::<C>),
19    };
20    let name_raw = name.to_cstr();
21    let result = unsafe { BNRegisterFlowGraphLayout(name_raw.as_ptr(), &mut callbacks) };
22    let core = unsafe { CoreFlowGraphLayout::from_raw(NonNull::new(result).unwrap()) };
23    (renderer, core)
24}
25
26/// The interface responsible for laying out a [`FlowGraph`].
27pub trait FlowGraphLayout: Sized + Sync + Send + 'static {
28    /// Perform the flow graph layout, returning `true` if successful.
29    ///
30    /// The implementation is responsible for doing four main things (usually in this order):
31    ///
32    /// 1. Adjusting `nodes` positions using [`FlowGraphNode::set_position`].
33    /// 2. Setting the edge points (e.g. the lines between nodes) using [`FlowGraphNode::set_outgoing_edge_points`].
34    /// 3. Setting the `nodes` visibility region using [`FlowGraphNode::set_visibility_region`].
35    /// 4. Setting the size of the graph using [`FlowGraph::set_size`].
36    fn layout(&self, graph: &FlowGraph, nodes: &[FlowGraphNode]) -> bool;
37}
38
39pub struct CoreFlowGraphLayout {
40    pub(crate) handle: NonNull<BNFlowGraphLayout>,
41}
42
43impl CoreFlowGraphLayout {
44    pub(crate) unsafe fn from_raw(handle: NonNull<BNFlowGraphLayout>) -> CoreFlowGraphLayout {
45        Self { handle }
46    }
47
48    pub fn all() -> Array<CoreFlowGraphLayout> {
49        let mut count = 0;
50        let result = unsafe { BNGetFlowGraphLayouts(&mut count) };
51        unsafe { Array::new(result, count, ()) }
52    }
53
54    pub fn by_name(name: &str) -> Option<CoreFlowGraphLayout> {
55        let name_raw = name.to_cstr();
56        let layout_ptr = unsafe { BNGetFlowGraphLayoutByName(name_raw.as_ptr()) };
57        Some(unsafe { CoreFlowGraphLayout::from_raw(NonNull::new(layout_ptr)?) })
58    }
59
60    pub fn name(&self) -> String {
61        unsafe { BnString::into_string(BNGetFlowGraphLayoutName(self.handle.as_ptr())) }
62    }
63}
64
65impl FlowGraphLayout for CoreFlowGraphLayout {
66    fn layout(&self, graph: &FlowGraph, nodes: &[FlowGraphNode]) -> bool {
67        // SAFETY: FlowGraphNode to *mut BNFlowGraphNode is safe (repr transparent)
68        unsafe {
69            BNFlowGraphLayoutLayout(
70                self.handle.as_ptr(),
71                graph.handle,
72                nodes.as_ptr() as *mut _,
73                nodes.len(),
74            )
75        }
76    }
77}
78
79impl CoreArrayProvider for CoreFlowGraphLayout {
80    type Raw = *mut BNFlowGraphLayout;
81    type Context = ();
82    type Wrapped<'a> = Self;
83}
84
85unsafe impl CoreArrayProviderInner for CoreFlowGraphLayout {
86    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
87        BNFreeFlowGraphLayoutList(raw)
88    }
89
90    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
91        // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
92        let handle = NonNull::new(*raw).unwrap();
93        CoreFlowGraphLayout::from_raw(handle)
94    }
95}
96
97unsafe impl Send for CoreFlowGraphLayout {}
98unsafe impl Sync for CoreFlowGraphLayout {}
99
100/// Represents a queued flow graph layout request, given out by [`FlowGraph::request_layout`].
101pub struct FlowGraphLayoutRequest {
102    pub(crate) handle: NonNull<BNFlowGraphLayoutRequest>,
103}
104
105impl FlowGraphLayoutRequest {
106    pub(crate) unsafe fn ref_from_raw(
107        handle: NonNull<BNFlowGraphLayoutRequest>,
108    ) -> Ref<FlowGraphLayoutRequest> {
109        Ref::new(Self { handle })
110    }
111
112    /// The flow graph that this request is for.
113    pub fn graph(&self) -> Ref<FlowGraph> {
114        unsafe {
115            FlowGraph::ref_from_raw(BNGetGraphForFlowGraphLayoutRequest(self.handle.as_ptr()))
116        }
117    }
118
119    /// Returns `true` if the layout request has completed.
120    pub fn is_complete(&self) -> bool {
121        unsafe { BNIsFlowGraphLayoutRequestComplete(self.handle.as_ptr()) }
122    }
123
124    /// Removes the request from the flow graphs layout queue, and sets [`FlowGraph::is_layout_complete`]
125    pub fn abort(&self) {
126        unsafe { BNAbortFlowGraphLayoutRequest(self.handle.as_ptr()) }
127    }
128}
129
130impl ToOwned for FlowGraphLayoutRequest {
131    type Owned = Ref<Self>;
132
133    fn to_owned(&self) -> Self::Owned {
134        unsafe { RefCountable::inc_ref(self) }
135    }
136}
137
138unsafe impl RefCountable for FlowGraphLayoutRequest {
139    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
140        Ref::new(Self {
141            handle: NonNull::new(BNNewFlowGraphLayoutRequestReference(handle.handle.as_ptr()))
142                .unwrap(),
143        })
144    }
145
146    unsafe fn dec_ref(handle: &Self) {
147        BNFreeFlowGraphLayoutRequest(handle.handle.as_ptr());
148    }
149}
150
151unsafe extern "C" fn cb_layout<C: FlowGraphLayout>(
152    ctxt: *mut c_void,
153    graph: *mut BNFlowGraph,
154    nodes: *mut *mut BNFlowGraphNode,
155    node_count: usize,
156) -> bool {
157    let ctxt = ctxt as *mut C;
158    let nodes_slice = core::slice::from_raw_parts(nodes, node_count);
159    let nodes: Vec<_> = nodes_slice
160        .iter()
161        .map(|ptr| unsafe { FlowGraphNode::from_raw(*ptr) })
162        .collect();
163    (*ctxt).layout(&FlowGraph::from_raw(graph), &nodes)
164}