| 1 | /** |
| 2 | * @file grid_ref.h |
| 3 | * |
| 4 | * Terminal grid reference type for referencing a resolved position in the |
| 5 | * terminal grid. |
| 6 | */ |
| 7 | |
| 8 | #ifndef GHOSTTY_VT_GRID_REF_H |
| 9 | #define GHOSTTY_VT_GRID_REF_H |
| 10 | |
| 11 | #include <stddef.h> |
| 12 | #include <stdint.h> |
| 13 | #include <ghostty/vt/types.h> |
| 14 | #include <ghostty/vt/screen.h> |
| 15 | #include <ghostty/vt/style.h> |
| 16 | |
| 17 | #ifdef __cplusplus |
| 18 | extern "C" { |
| 19 | #endif |
| 20 | |
| 21 | /** @defgroup grid_ref Grid Reference |
| 22 | * |
| 23 | * A grid reference is a reference to a specific cell position in the |
| 24 | * terminal. Obtain a grid reference from `ghostty_terminal_grid_ref` |
| 25 | * for untracked or `ghostty_terminal_grid_ref_track` for tracked. Untracked |
| 26 | * vs tracked is explained next. |
| 27 | * |
| 28 | * Important: The grid reference APIs are not meant to be used as the core of a render |
| 29 | * loop. They are not built to sustain the framerates needed for rendering large |
| 30 | * screens. Use the render state API for that. |
| 31 | * |
| 32 | * ## Untracked vs Tracked References |
| 33 | * |
| 34 | * ### Untracked Reference |
| 35 | * |
| 36 | * An untracked grid reference is a value type that snapshots a specific |
| 37 | * cell. It is only valid until the next update to the terminal instance. |
| 38 | * There is no guarantee that it will remain valid after any operation, |
| 39 | * even if a seemingly unrelated part of the grid is changed. These are meant |
| 40 | * to be read and have their values cached immediately after obtaining it. |
| 41 | * |
| 42 | * An untracked grid reference has a performance cost in its initial lookup, |
| 43 | * but doesn't affect the ongoing performance of the terminal in any way, |
| 44 | * since it is a one-time snapshot. |
| 45 | * |
| 46 | * ### Tracked Reference |
| 47 | * |
| 48 | * A tracked grid reference follows its cell across normal screen operations. |
| 49 | * For example scrolling, scrollback pruning, resize/reflow, and other |
| 50 | * terminal mutations update the tracked reference automatically. |
| 51 | * |
| 52 | * A tracked reference can still lose its original semantic location. This can |
| 53 | * happen when the underlying grid is reset, pruned, or otherwise discarded in a |
| 54 | * way that cannot be mapped to a meaningful new cell. In that state, |
| 55 | * ghostty_tracked_grid_ref_has_value() returns false and |
| 56 | * ghostty_tracked_grid_ref_snapshot() / ghostty_tracked_grid_ref_point() return |
| 57 | * GHOSTTY_NO_VALUE. The handle remains valid, and callers may move it to a new |
| 58 | * point with ghostty_tracked_grid_ref_set(). |
| 59 | * |
| 60 | * To read cell data from a tracked reference, first snapshot it with |
| 61 | * ghostty_tracked_grid_ref_snapshot(). The returned `GhosttyGridRef` is again |
| 62 | * an untracked reference and follows the same short lifetime rules as any other |
| 63 | * untracked grid reference. |
| 64 | * |
| 65 | * A tracked reference belongs to the terminal screen/page-list that was active |
| 66 | * when it was created or last set. Converting it to a point uses that owning |
| 67 | * screen/page-list, even if the terminal has since switched between primary and |
| 68 | * alternate screens. Calling ghostty_tracked_grid_ref_set() resolves the new |
| 69 | * point against the terminal's currently active screen/page-list and may move |
| 70 | * the tracked reference between screens. |
| 71 | * |
| 72 | * Tracked references are owned by the caller and must be freed with |
| 73 | * ghostty_tracked_grid_ref_free(). If the terminal that created a tracked |
| 74 | * reference is freed first, the handle remains valid only for tracked-grid-ref |
| 75 | * APIs: it reports no value and can still be freed. |
| 76 | * |
| 77 | * Each tracked reference adds bookkeeping to terminal mutations. Use them |
| 78 | * sparingly for long-lived anchors such as selections, search state, marks, |
| 79 | * or application-side bookmarks. |
| 80 | * |
| 81 | * ## Lifetime |
| 82 | * |
| 83 | * An untracked reference is a snapshot. It doesn't need to be freed. |
| 84 | * The safety of accessing the value is documented explicitly above: it |
| 85 | * is only safe to access any data until the next terminal mutating |
| 86 | * operation (including free). |
| 87 | * |
| 88 | * A tracked reference is allocated and must be freed when it is no |
| 89 | * longer needed. A tracked reference may outlive the terminal that created it; |
| 90 | * after terminal free, it reports no value and can still be freed. |
| 91 | * |
| 92 | * ## Examples |
| 93 | * |
| 94 | * @snippet c-vt-grid-traverse/src/main.c grid-ref-traverse |
| 95 | * @snippet c-vt-grid-ref-tracked/src/main.c grid-ref-tracked |
| 96 | * |
| 97 | * @{ |
| 98 | */ |
| 99 | |
| 100 | /** |
| 101 | * A resolved reference to a terminal cell position. |
| 102 | * |
| 103 | * This is a sized struct. Use GHOSTTY_INIT_SIZED() to initialize it. |
| 104 | * |
| 105 | * @ingroup grid_ref |
| 106 | */ |
| 107 | typedef struct { |
| 108 | size_t size; |
| 109 | void *node; |
| 110 | uint16_t x; |
| 111 | uint16_t y; |
| 112 | } GhosttyGridRef; |
| 113 | |
| 114 | /** |
| 115 | * Get the cell from a grid reference. |
| 116 | * |
| 117 | * @param ref Pointer to the grid reference |
| 118 | * @param[out] out_cell On success, set to the cell at the ref's position (may be NULL) |
| 119 | * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the ref's |
| 120 | * node is NULL |
| 121 | * |
| 122 | * @ingroup grid_ref |
| 123 | */ |
| 124 | GHOSTTY_API GhosttyResult ghostty_grid_ref_cell(const GhosttyGridRef *ref, |
| 125 | GhosttyCell *out_cell); |
| 126 | |
| 127 | /** |
| 128 | * Get the row from a grid reference. |
| 129 | * |
| 130 | * @param ref Pointer to the grid reference |
| 131 | * @param[out] out_row On success, set to the row at the ref's position (may be NULL) |
| 132 | * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the ref's |
| 133 | * node is NULL |
| 134 | * |
| 135 | * @ingroup grid_ref |
| 136 | */ |
| 137 | GHOSTTY_API GhosttyResult ghostty_grid_ref_row(const GhosttyGridRef *ref, |
| 138 | GhosttyRow *out_row); |
| 139 | |
| 140 | /** |
| 141 | * Get the grapheme cluster codepoints for the cell at the grid reference's |
| 142 | * position. |
| 143 | * |
| 144 | * Writes the full grapheme cluster (the cell's primary codepoint followed by |
| 145 | * any combining codepoints) into the provided buffer. If the cell has no text, |
| 146 | * out_len is set to 0 and GHOSTTY_SUCCESS is returned. |
| 147 | * |
| 148 | * If the buffer is too small (or NULL), the function returns |
| 149 | * GHOSTTY_OUT_OF_SPACE and writes the required number of codepoints to |
| 150 | * out_len. The caller can then retry with a sufficiently sized buffer. |
| 151 | * |
| 152 | * @param ref Pointer to the grid reference |
| 153 | * @param buf Output buffer of uint32_t codepoints (may be NULL) |
| 154 | * @param buf_len Number of uint32_t elements in the buffer |
| 155 | * @param[out] out_len On success, the number of codepoints written. On |
| 156 | * GHOSTTY_OUT_OF_SPACE, the required buffer size in codepoints. |
| 157 | * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the ref's |
| 158 | * node is NULL, GHOSTTY_OUT_OF_SPACE if the buffer is too small |
| 159 | * |
| 160 | * @ingroup grid_ref |
| 161 | */ |
| 162 | GHOSTTY_API GhosttyResult ghostty_grid_ref_graphemes(const GhosttyGridRef *ref, |
| 163 | uint32_t *buf, |
| 164 | size_t buf_len, |
| 165 | size_t *out_len); |
| 166 | |
| 167 | /** |
| 168 | * Get the hyperlink URI for the cell at the grid reference's position. |
| 169 | * |
| 170 | * Writes the URI bytes into the provided buffer. If the cell has no |
| 171 | * hyperlink, out_len is set to 0 and GHOSTTY_SUCCESS is returned. |
| 172 | * |
| 173 | * If the buffer is too small (or NULL), the function returns |
| 174 | * GHOSTTY_OUT_OF_SPACE and writes the required number of bytes to |
| 175 | * out_len. The caller can then retry with a sufficiently sized buffer. |
| 176 | * |
| 177 | * @param ref Pointer to the grid reference |
| 178 | * @param buf Output buffer for the URI bytes (may be NULL) |
| 179 | * @param buf_len Size of the output buffer in bytes |
| 180 | * @param[out] out_len On success, the number of bytes written. On |
| 181 | * GHOSTTY_OUT_OF_SPACE, the required buffer size in bytes. |
| 182 | * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the ref's |
| 183 | * node is NULL, GHOSTTY_OUT_OF_SPACE if the buffer is too small |
| 184 | * |
| 185 | * @ingroup grid_ref |
| 186 | */ |
| 187 | GHOSTTY_API GhosttyResult ghostty_grid_ref_hyperlink_uri( |
| 188 | const GhosttyGridRef *ref, |
| 189 | uint8_t *buf, |
| 190 | size_t buf_len, |
| 191 | size_t *out_len); |
| 192 | |
| 193 | /** |
| 194 | * Get the style of the cell at the grid reference's position. |
| 195 | * |
| 196 | * @param ref Pointer to the grid reference |
| 197 | * @param[out] out_style On success, set to the cell's style (may be NULL) |
| 198 | * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the ref's |
| 199 | * node is NULL |
| 200 | * |
| 201 | * @ingroup grid_ref |
| 202 | */ |
| 203 | GHOSTTY_API GhosttyResult ghostty_grid_ref_style(const GhosttyGridRef *ref, |
| 204 | GhosttyStyle *out_style); |
| 205 | |
| 206 | /** @} */ |
| 207 | |
| 208 | #ifdef __cplusplus |
| 209 | } |
| 210 | #endif |
| 211 | |
| 212 | #endif /* GHOSTTY_VT_GRID_REF_H */ |