You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

2654 lines
75 KiB

  1. #include "terminal.h"
  2. #include <malloc.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <assert.h>
  6. #include <errno.h>
  7. #include <sys/stat.h>
  8. #include <sys/wait.h>
  9. #include <sys/ioctl.h>
  10. #include <sys/epoll.h>
  11. #include <sys/eventfd.h>
  12. #include <sys/timerfd.h>
  13. #include <fcntl.h>
  14. #include <linux/input-event-codes.h>
  15. #include <xdg-shell.h>
  16. #define LOG_MODULE "terminal"
  17. #define LOG_ENABLE_DBG 0
  18. #include "log.h"
  19. #include "async.h"
  20. #include "config.h"
  21. #include "extract.h"
  22. #include "grid.h"
  23. #include "quirks.h"
  24. #include "reaper.h"
  25. #include "render.h"
  26. #include "selection.h"
  27. #include "sixel.h"
  28. #include "slave.h"
  29. #include "spawn.h"
  30. #include "util.h"
  31. #include "vt.h"
  32. #include "xmalloc.h"
  33. #define PTMX_TIMING 0
  34. const char *const XCURSOR_HIDDEN = "hidden";
  35. const char *const XCURSOR_LEFT_PTR = "left_ptr";
  36. const char *const XCURSOR_TEXT = "text";
  37. //const char *const XCURSOR_HAND2 = "hand2";
  38. const char *const XCURSOR_TOP_LEFT_CORNER = "top_left_corner";
  39. const char *const XCURSOR_TOP_RIGHT_CORNER = "top_right_corner";
  40. const char *const XCURSOR_BOTTOM_LEFT_CORNER = "bottom_left_corner";
  41. const char *const XCURSOR_BOTTOM_RIGHT_CORNER = "bottom_right_corner";
  42. const char *const XCURSOR_LEFT_SIDE = "left_side";
  43. const char *const XCURSOR_RIGHT_SIDE = "right_side";
  44. const char *const XCURSOR_TOP_SIDE = "top_side";
  45. const char *const XCURSOR_BOTTOM_SIDE = "bottom_side";
  46. static void
  47. enqueue_data_for_slave(const void *data, size_t len, size_t offset,
  48. ptmx_buffer_list_t *buffer_list)
  49. {
  50. void *copy = xmalloc(len);
  51. memcpy(copy, data, len);
  52. struct ptmx_buffer queued = {
  53. .data = copy,
  54. .len = len,
  55. .idx = offset,
  56. };
  57. tll_push_back(*buffer_list, queued);
  58. }
  59. static bool
  60. data_to_slave(struct terminal *term, const void *data, size_t len,
  61. ptmx_buffer_list_t *buffer_list)
  62. {
  63. /*
  64. * Try a synchronous write first. If we fail to write everything,
  65. * switch to asynchronous.
  66. */
  67. size_t async_idx = 0;
  68. switch (async_write(term->ptmx, data, len, &async_idx)) {
  69. case ASYNC_WRITE_REMAIN:
  70. /* Switch to asynchronous mode; let FDM write the remaining data */
  71. if (!fdm_event_add(term->fdm, term->ptmx, EPOLLOUT))
  72. return false;
  73. enqueue_data_for_slave(data, len, async_idx, buffer_list);
  74. return true;
  75. case ASYNC_WRITE_DONE:
  76. return true;
  77. case ASYNC_WRITE_ERR:
  78. LOG_ERRNO("failed to synchronously write %zu bytes to slave", len);
  79. return false;
  80. }
  81. /* Shouldn't get here */
  82. assert(false);
  83. return false;
  84. }
  85. bool
  86. term_paste_data_to_slave(struct terminal *term, const void *data, size_t len)
  87. {
  88. assert(term->is_sending_paste_data);
  89. if (term->ptmx < 0) {
  90. /* We're probably in "hold" */
  91. return false;
  92. }
  93. if (tll_length(term->ptmx_paste_buffers) > 0) {
  94. /* Don't even try to send data *now* if there's queued up
  95. * data, since that would result in events arriving out of
  96. * order. */
  97. enqueue_data_for_slave(data, len, 0, &term->ptmx_paste_buffers);
  98. return true;
  99. }
  100. return data_to_slave(term, data, len, &term->ptmx_paste_buffers);
  101. }
  102. bool
  103. term_to_slave(struct terminal *term, const void *data, size_t len)
  104. {
  105. if (term->ptmx < 0) {
  106. /* We're probably in "hold" */
  107. return false;
  108. }
  109. if (tll_length(term->ptmx_buffers) > 0 || term->is_sending_paste_data) {
  110. /*
  111. * Don't even try to send data *now* if there's queued up
  112. * data, since that would result in events arriving out of
  113. * order.
  114. *
  115. * Furthermore, if we're currently sending paste data to the
  116. * client, do *not* mix that stream with other events
  117. * (https://codeberg.org/dnkl/foot/issues/101).
  118. */
  119. enqueue_data_for_slave(data, len, 0, &term->ptmx_buffers);
  120. return true;
  121. }
  122. return data_to_slave(term, data, len, &term->ptmx_buffers);
  123. }
  124. static bool
  125. fdm_ptmx_out(struct fdm *fdm, int fd, int events, void *data)
  126. {
  127. struct terminal *term = data;
  128. /* If there is no queued data, then we shouldn't be in asynchronous mode */
  129. assert(tll_length(term->ptmx_buffers) > 0 ||
  130. tll_length(term->ptmx_paste_buffers) > 0);
  131. /* Writes a single buffer, returns if not all of it could be written */
  132. #define write_one_buffer(buffer_list) \
  133. { \
  134. switch (async_write(term->ptmx, it->item.data, it->item.len, &it->item.idx)) { \
  135. case ASYNC_WRITE_DONE: \
  136. free(it->item.data); \
  137. tll_remove(buffer_list, it); \
  138. break; \
  139. case ASYNC_WRITE_REMAIN: \
  140. /* to_slave() updated it->item.idx */ \
  141. return true; \
  142. case ASYNC_WRITE_ERR: \
  143. LOG_ERRNO("failed to asynchronously write %zu bytes to slave", \
  144. it->item.len - it->item.idx); \
  145. return false; \
  146. } \
  147. }
  148. tll_foreach(term->ptmx_paste_buffers, it)
  149. write_one_buffer(term->ptmx_paste_buffers);
  150. /* If we get here, *all* paste data buffers were successfully
  151. * flushed */
  152. if (!term->is_sending_paste_data) {
  153. tll_foreach(term->ptmx_buffers, it)
  154. write_one_buffer(term->ptmx_buffers);
  155. }
  156. /*
  157. * If we get here, *all* buffers were successfully flushed.
  158. *
  159. * Or, we're still sending paste data, in which case we do *not*
  160. * want to send the "normal" queued up data
  161. *
  162. * In both cases, we want to *disable* the FDM callback since
  163. * otherwise we'd just be called right away again, with nothing to
  164. * write.
  165. */
  166. fdm_event_del(term->fdm, term->ptmx, EPOLLOUT);
  167. return true;
  168. }
  169. #if PTMX_TIMING
  170. static struct timespec last = {0};
  171. #endif
  172. static bool
  173. fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
  174. {
  175. struct terminal *term = data;
  176. const bool pollin = events & EPOLLIN;
  177. const bool pollout = events & EPOLLOUT;
  178. const bool hup = events & EPOLLHUP;
  179. if (pollout) {
  180. if (!fdm_ptmx_out(fdm, fd, events, data))
  181. return false;
  182. }
  183. /* Prevent blinking while typing */
  184. term_cursor_blink_restart(term);
  185. term->render.app_sync_updates.flipped = false;
  186. uint8_t buf[24 * 1024];
  187. ssize_t count = sizeof(buf);
  188. const size_t max_iterations = 10;
  189. for (size_t i = 0; i < max_iterations && pollin && count == sizeof(buf); i++) {
  190. assert(pollin);
  191. count = read(term->ptmx, buf, sizeof(buf));
  192. if (count < 0) {
  193. if (errno == EAGAIN)
  194. return true;
  195. LOG_ERRNO("failed to read from pseudo terminal");
  196. return false;
  197. }
  198. vt_from_slave(term, buf, count);
  199. }
  200. if (!term->render.app_sync_updates.enabled &&
  201. !term->render.app_sync_updates.flipped)
  202. {
  203. /*
  204. * We likely need to re-render. But, we don't want to do it
  205. * immediately. Often, a single client update is done through
  206. * multiple writes. This could lead to us rendering one frame with
  207. * "intermediate" state.
  208. *
  209. * For example, we might end up rendering a frame
  210. * where the client just erased a line, while in the
  211. * next frame, the client wrote to the same line. This
  212. * causes screen "flickering".
  213. *
  214. * Mitigate by always incuring a small delay before
  215. * rendering the next frame. This gives the client
  216. * some time to finish the operation (and thus gives
  217. * us time to receive the last writes before doing any
  218. * actual rendering).
  219. *
  220. * We incur this delay *every* time we receive
  221. * input. To ensure we don't delay rendering
  222. * indefinitely, we start a second timer that is only
  223. * reset when we render.
  224. *
  225. * Note that when the client is producing data at a
  226. * very high pace, we're rate limited by the wayland
  227. * compositor anyway. The delay we introduce here only
  228. * has any effect when the renderer is idle.
  229. */
  230. uint64_t lower_ns = term->conf->tweak.delayed_render_lower_ns;
  231. uint64_t upper_ns = term->conf->tweak.delayed_render_upper_ns;
  232. if (lower_ns > 0 && upper_ns > 0) {
  233. #if PTMX_TIMING
  234. struct timespec now;
  235. clock_gettime(1, &now);
  236. if (last.tv_sec > 0 || last.tv_nsec > 0) {
  237. struct timeval diff;
  238. struct timeval l = {last.tv_sec, last.tv_nsec / 1000};
  239. struct timeval n = {now.tv_sec, now.tv_nsec / 1000};
  240. timersub(&n, &l, &diff);
  241. LOG_INFO("waited %lu µs for more input", diff.tv_usec);
  242. }
  243. last = now;
  244. #endif
  245. assert(lower_ns < 1000000000);
  246. assert(upper_ns < 1000000000);
  247. assert(upper_ns > lower_ns);
  248. timerfd_settime(
  249. term->delayed_render_timer.lower_fd, 0,
  250. &(struct itimerspec){.it_value = {.tv_nsec = lower_ns}},
  251. NULL);
  252. /* Second timeout - only reset when we render. Set to one
  253. * frame (assuming 60Hz) */
  254. if (!term->delayed_render_timer.is_armed) {
  255. timerfd_settime(
  256. term->delayed_render_timer.upper_fd, 0,
  257. &(struct itimerspec){.it_value = {.tv_nsec = upper_ns}},
  258. NULL);
  259. term->delayed_render_timer.is_armed = true;
  260. }
  261. } else
  262. render_refresh(term);
  263. }
  264. if (hup) {
  265. if (term->hold_at_exit) {
  266. fdm_del(fdm, fd);
  267. term->ptmx = -1;
  268. return true;
  269. } else
  270. return term_shutdown(term);
  271. }
  272. return true;
  273. }
  274. static bool
  275. fdm_flash(struct fdm *fdm, int fd, int events, void *data)
  276. {
  277. if (events & EPOLLHUP)
  278. return false;
  279. struct terminal *term = data;
  280. uint64_t expiration_count;
  281. ssize_t ret = read(
  282. term->flash.fd, &expiration_count, sizeof(expiration_count));
  283. if (ret < 0) {
  284. if (errno == EAGAIN)
  285. return true;
  286. LOG_ERRNO("failed to read flash timer");
  287. return false;
  288. }
  289. LOG_DBG("flash timer expired %llu times",
  290. (unsigned long long)expiration_count);
  291. term->flash.active = false;
  292. term_damage_view(term);
  293. render_refresh(term);
  294. return true;
  295. }
  296. static bool
  297. fdm_blink(struct fdm *fdm, int fd, int events, void *data)
  298. {
  299. if (events & EPOLLHUP)
  300. return false;
  301. struct terminal *term = data;
  302. uint64_t expiration_count;
  303. ssize_t ret = read(
  304. term->blink.fd, &expiration_count, sizeof(expiration_count));
  305. if (ret < 0) {
  306. if (errno == EAGAIN)
  307. return true;
  308. LOG_ERRNO("failed to read blink timer");
  309. return false;
  310. }
  311. LOG_DBG("blink timer expired %llu times",
  312. (unsigned long long)expiration_count);
  313. /* Invert blink state */
  314. term->blink.state = term->blink.state == BLINK_ON
  315. ? BLINK_OFF : BLINK_ON;
  316. /* Scan all visible cells and mark rows with blinking cells dirty */
  317. bool no_blinking_cells = true;
  318. for (int r = 0; r < term->rows; r++) {
  319. struct row *row = grid_row_in_view(term->grid, r);
  320. for (int col = 0; col < term->cols; col++) {
  321. struct cell *cell = &row->cells[col];
  322. if (cell->attrs.blink) {
  323. cell->attrs.clean = 0;
  324. row->dirty = true;
  325. no_blinking_cells = false;
  326. }
  327. }
  328. }
  329. if (no_blinking_cells) {
  330. LOG_DBG("disarming blink timer");
  331. term->blink.state = BLINK_ON;
  332. fdm_del(term->fdm, term->blink.fd);
  333. term->blink.fd = -1;
  334. } else
  335. render_refresh(term);
  336. return true;
  337. }
  338. void
  339. term_arm_blink_timer(struct terminal *term)
  340. {
  341. if (term->blink.fd >= 0)
  342. return;
  343. LOG_DBG("arming blink timer");
  344. int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
  345. if (fd < 0) {
  346. LOG_ERRNO("failed to create blink timer FD");
  347. return;
  348. }
  349. if (!fdm_add(term->fdm, fd, EPOLLIN, &fdm_blink, term)) {
  350. close(fd);
  351. return;
  352. }
  353. struct itimerspec alarm = {
  354. .it_value = {.tv_sec = 0, .tv_nsec = 500 * 1000000},
  355. .it_interval = {.tv_sec = 0, .tv_nsec = 500 * 1000000},
  356. };
  357. if (timerfd_settime(fd, 0, &alarm, NULL) < 0) {
  358. LOG_ERRNO("failed to arm blink timer");
  359. fdm_del(term->fdm, fd);
  360. }
  361. term->blink.fd = fd;
  362. }
  363. static void
  364. cursor_refresh(struct terminal *term)
  365. {
  366. term->grid->cur_row->cells[term->grid->cursor.point.col].attrs.clean = 0;
  367. term->grid->cur_row->dirty = true;
  368. render_refresh(term);
  369. }
  370. static bool
  371. fdm_cursor_blink(struct fdm *fdm, int fd, int events, void *data)
  372. {
  373. if (events & EPOLLHUP)
  374. return false;
  375. struct terminal *term = data;
  376. uint64_t expiration_count;
  377. ssize_t ret = read(
  378. term->cursor_blink.fd, &expiration_count, sizeof(expiration_count));
  379. if (ret < 0) {
  380. if (errno == EAGAIN)
  381. return true;
  382. LOG_ERRNO("failed to read cursor blink timer");
  383. return false;
  384. }
  385. LOG_DBG("cursor blink timer expired %llu times",
  386. (unsigned long long)expiration_count);
  387. /* Invert blink state */
  388. term->cursor_blink.state = term->cursor_blink.state == CURSOR_BLINK_ON
  389. ? CURSOR_BLINK_OFF : CURSOR_BLINK_ON;
  390. cursor_refresh(term);
  391. return true;
  392. }
  393. static bool
  394. fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data)
  395. {
  396. if (events & EPOLLHUP)
  397. return false;
  398. struct terminal *term = data;
  399. uint64_t unused;
  400. ssize_t ret1 = 0;
  401. ssize_t ret2 = 0;
  402. if (fd == term->delayed_render_timer.lower_fd)
  403. ret1 = read(term->delayed_render_timer.lower_fd, &unused, sizeof(unused));
  404. if (fd == term->delayed_render_timer.upper_fd)
  405. ret2 = read(term->delayed_render_timer.upper_fd, &unused, sizeof(unused));
  406. if ((ret1 < 0 || ret2 < 0)) {
  407. if (errno == EAGAIN)
  408. return true;
  409. LOG_ERRNO("failed to read timeout timer");
  410. return false;
  411. }
  412. if (ret1 > 0)
  413. LOG_DBG("lower delay timer expired");
  414. else if (ret2 > 0)
  415. LOG_DBG("upper delay timer expired");
  416. if (ret1 == 0 && ret2 == 0)
  417. return true;
  418. #if PTMX_TIMING
  419. last = (struct timespec){0};
  420. #endif
  421. /* Reset timers */
  422. struct itimerspec reset = {{0}};
  423. timerfd_settime(term->delayed_render_timer.lower_fd, 0, &reset, NULL);
  424. timerfd_settime(term->delayed_render_timer.upper_fd, 0, &reset, NULL);
  425. term->delayed_render_timer.is_armed = false;
  426. render_refresh(term);
  427. return true;
  428. }
  429. static bool
  430. fdm_app_sync_updates_timeout(
  431. struct fdm *fdm, int fd, int events, void *data)
  432. {
  433. if (events & EPOLLHUP)
  434. return false;
  435. struct terminal *term = data;
  436. uint64_t unused;
  437. ssize_t ret = read(term->render.app_sync_updates.timer_fd,
  438. &unused, sizeof(unused));
  439. if (ret < 0) {
  440. if (errno == EAGAIN)
  441. return true;
  442. LOG_ERRNO("failed to read application synchronized updates timeout timer");
  443. return false;
  444. }
  445. term_disable_app_sync_updates(term);
  446. return true;
  447. }
  448. static void
  449. initialize_color_cube(struct terminal *term)
  450. {
  451. /* First 16 entries have already been initialized from conf */
  452. for (size_t r = 0; r < 6; r++) {
  453. for (size_t g = 0; g < 6; g++) {
  454. for (size_t b = 0; b < 6; b++) {
  455. term->colors.default_table[16 + r * 6 * 6 + g * 6 + b]
  456. = r * 51 << 16 | g * 51 << 8 | b * 51;
  457. }
  458. }
  459. }
  460. for (size_t i = 0; i < 24; i++)
  461. term->colors.default_table[232 + i] = i * 11 << 16 | i * 11 << 8 | i * 11;
  462. memcpy(term->colors.table, term->colors.default_table, sizeof(term->colors.table));
  463. }
  464. static bool
  465. initialize_render_workers(struct terminal *term)
  466. {
  467. LOG_INFO("using %zu rendering threads", term->render.workers.count);
  468. if (sem_init(&term->render.workers.start, 0, 0) < 0 ||
  469. sem_init(&term->render.workers.done, 0, 0) < 0)
  470. {
  471. LOG_ERRNO("failed to instantiate render worker semaphores");
  472. return false;
  473. }
  474. int err;
  475. if ((err = mtx_init(&term->render.workers.lock, mtx_plain)) != thrd_success) {
  476. LOG_ERR("failed to instantiate render worker mutex: %s (%d)",
  477. thrd_err_as_string(err), err);
  478. goto err_sem_destroy;
  479. }
  480. term->render.workers.threads = xcalloc(
  481. term->render.workers.count, sizeof(term->render.workers.threads[0]));
  482. for (size_t i = 0; i < term->render.workers.count; i++) {
  483. struct render_worker_context *ctx = xmalloc(sizeof(*ctx));
  484. *ctx = (struct render_worker_context) {
  485. .term = term,
  486. .my_id = 1 + i,
  487. };
  488. int ret = thrd_create(
  489. &term->render.workers.threads[i], &render_worker_thread, ctx);
  490. if (ret != thrd_success) {
  491. LOG_ERR("failed to create render worker thread: %s (%d)",
  492. thrd_err_as_string(ret), ret);
  493. term->render.workers.threads[i] = 0;
  494. return false;
  495. }
  496. }
  497. return true;
  498. err_sem_destroy:
  499. sem_destroy(&term->render.workers.start);
  500. sem_destroy(&term->render.workers.done);
  501. return false;
  502. }
  503. static bool
  504. term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
  505. {
  506. for (size_t i = 0; i < 4; i++) {
  507. assert(fonts[i] != NULL);
  508. fcft_destroy(term->fonts[i]);
  509. term->fonts[i] = fonts[i];
  510. }
  511. const int old_cell_width = term->cell_width;
  512. const int old_cell_height = term->cell_height;
  513. term->cell_width = term->fonts[0]->space_advance.x > 0
  514. ? term->fonts[0]->space_advance.x : term->fonts[0]->max_advance.x;
  515. term->cell_height = max(term->fonts[0]->height,
  516. term->fonts[0]->ascent + term->fonts[0]->descent);
  517. LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height);
  518. if (term->cell_width < old_cell_width ||
  519. term->cell_height < old_cell_height)
  520. {
  521. /*
  522. * The cell size has decreased.
  523. *
  524. * This means sixels, which we cannot resize, no longer fit
  525. * into their "allocated" grid space.
  526. *
  527. * To be able to fit them, we would have to change the grid
  528. * content. Inserting empty lines _might_ seem acceptable, but
  529. * we'd also need to insert empty columns, which would break
  530. * existing layout completely.
  531. *
  532. * So we delete them.
  533. */
  534. sixel_destroy_all(term);
  535. } else if (term->cell_width != old_cell_width ||
  536. term->cell_height != old_cell_height)
  537. {
  538. sixel_cell_size_changed(term);
  539. }
  540. /* Use force, since cell-width/height may have changed */
  541. render_resize_force(term, term->width / term->scale, term->height / term->scale);
  542. return true;
  543. }
  544. static float
  545. get_font_dpi(const struct terminal *term)
  546. {
  547. /*
  548. * Use output's DPI to scale font. This is to ensure the font has
  549. * the same physical height (if measured by a ruler) regardless of
  550. * monitor.
  551. *
  552. * Conceptually, we use the physical monitor specs to calculate
  553. * the DPI, and we ignore the output's scaling factor.
  554. *
  555. * However, to deal with fractional scaling, where we're told to
  556. * render at e.g. 2x, but are then downscaled by the compositor to
  557. * e.g. 1.25, we use the scaled DPI value multiplied by the scale
  558. * factor instead.
  559. *
  560. * For integral scaling factors the resulting DPI is the same as
  561. * if we had used the physical DPI.
  562. *
  563. * For fractional scaling factors we'll get a DPI *larger* than
  564. * the physical DPI, that ends up being right when later
  565. * downscaled by the compositor.
  566. */
  567. /* Use highest DPI from outputs we're mapped on */
  568. double dpi = 0.0;
  569. assert(term->window != NULL);
  570. tll_foreach(term->window->on_outputs, it) {
  571. if (it->item->dpi > dpi)
  572. dpi = it->item->dpi;
  573. }
  574. /* If we're not mapped, use DPI from first monitor. Hopefully this is where we'll get mapped later... */
  575. if (dpi == 0.) {
  576. tll_foreach(term->wl->monitors, it) {
  577. dpi = it->item.dpi;
  578. break;
  579. }
  580. }
  581. if (dpi == 0) {
  582. /* No monitors? */
  583. dpi = 96.;
  584. }
  585. return dpi;
  586. }
  587. static enum fcft_subpixel
  588. get_font_subpixel(const struct terminal *term)
  589. {
  590. if (term->colors.alpha != 0xffff) {
  591. /* Can't do subpixel rendering on transparent background */
  592. return FCFT_SUBPIXEL_NONE;
  593. }
  594. enum wl_output_subpixel wl_subpixel;
  595. /*
  596. * Wayland doesn't tell us *which* part of the surface that goes
  597. * on a specific output, only whether the surface is mapped to an
  598. * output or not.
  599. *
  600. * Thus, when determining which subpixel mode to use, we can't do
  601. * much but select *an* output. So, we pick the first one.
  602. *
  603. * If we're not mapped at all, we pick the first available
  604. * monitor, and hope that's where we'll eventually get mapped.
  605. *
  606. * If there aren't any monitors we use the "default" subpixel
  607. * mode.
  608. */
  609. if (tll_length(term->window->on_outputs) > 0)
  610. wl_subpixel = tll_front(term->window->on_outputs)->subpixel;
  611. else if (tll_length(term->wl->monitors) > 0)
  612. wl_subpixel = tll_front(term->wl->monitors).subpixel;
  613. else
  614. wl_subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
  615. switch (wl_subpixel) {
  616. case WL_OUTPUT_SUBPIXEL_UNKNOWN: return FCFT_SUBPIXEL_DEFAULT;
  617. case WL_OUTPUT_SUBPIXEL_NONE: return FCFT_SUBPIXEL_NONE;
  618. case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: return FCFT_SUBPIXEL_HORIZONTAL_RGB;
  619. case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: return FCFT_SUBPIXEL_HORIZONTAL_BGR;
  620. case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: return FCFT_SUBPIXEL_VERTICAL_RGB;
  621. case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: return FCFT_SUBPIXEL_VERTICAL_BGR;
  622. }
  623. return FCFT_SUBPIXEL_DEFAULT;
  624. }
  625. struct font_load_data {
  626. size_t count;
  627. const char **names;
  628. const char *attrs;
  629. struct fcft_font **font;
  630. };
  631. static int
  632. font_loader_thread(void *_data)
  633. {
  634. struct font_load_data *data = _data;
  635. *data->font = fcft_from_name(data->count, data->names, data->attrs);
  636. return *data->font != NULL;
  637. }
  638. static bool
  639. reload_fonts(struct terminal *term)
  640. {
  641. const size_t count = tll_length(term->conf->fonts);
  642. char *names[count];
  643. size_t i = 0;
  644. tll_foreach(term->conf->fonts, it) {
  645. bool use_px_size = term->font_sizes[i].px_size > 0;
  646. char size[64];
  647. if (use_px_size)
  648. snprintf(size, sizeof(size), ":pixelsize=%d", term->font_sizes[i].px_size);
  649. else
  650. snprintf(size, sizeof(size), ":size=%.2f", term->font_sizes[i].pt_size);
  651. size_t len = strlen(it->item.pattern) + strlen(size) + 1;
  652. names[i] = xmalloc(len);
  653. strcpy(names[i], it->item.pattern);
  654. strcat(names[i], size);
  655. i++;
  656. }
  657. char attrs0[256], attrs1[256], attrs2[256], attrs3[256];
  658. snprintf(attrs0, sizeof(attrs0), "dpi=%.2f", term->font_dpi);
  659. snprintf(attrs1, sizeof(attrs1), "dpi=%.2f:weight=bold", term->font_dpi);
  660. snprintf(attrs2, sizeof(attrs2), "dpi=%.2f:slant=italic", term->font_dpi);
  661. snprintf(attrs3, sizeof(attrs3), "dpi=%.2f:weight=bold:slant=italic", term->font_dpi);
  662. struct fcft_font *fonts[4];
  663. struct font_load_data data[4] = {
  664. {count, (const char **)names, attrs0, &fonts[0]},
  665. {count, (const char **)names, attrs1, &fonts[1]},
  666. {count, (const char **)names, attrs2, &fonts[2]},
  667. {count, (const char **)names, attrs3, &fonts[3]},
  668. };
  669. thrd_t tids[4] = {0};
  670. for (size_t i = 0; i < 4; i++) {
  671. int ret = thrd_create(&tids[i], &font_loader_thread, &data[i]);
  672. if (ret != thrd_success) {
  673. LOG_ERR("failed to create font loader thread: %s (%d)",
  674. thrd_err_as_string(ret), ret);
  675. break;
  676. }
  677. }
  678. bool success = true;
  679. for (size_t i = 0; i < 4; i++) {
  680. if (tids[i] != 0) {
  681. int ret;
  682. thrd_join(tids[i], &ret);
  683. success = success && ret;
  684. } else
  685. success = false;
  686. }
  687. if (!success) {
  688. LOG_ERR("failed to load primary fonts");
  689. for (size_t i = 0; i < 4; i++) {
  690. fcft_destroy(fonts[i]);
  691. fonts[i] = NULL;
  692. }
  693. }
  694. for (size_t i = 0; i < count; i++)
  695. free(names[i]);
  696. return success ? term_set_fonts(term, fonts) : success;
  697. }
  698. static bool
  699. load_fonts_from_conf(struct terminal *term)
  700. {
  701. size_t i = 0;
  702. tll_foreach(term->conf->fonts, it) {
  703. term->font_sizes[i++] = (struct config_font){
  704. .pt_size = it->item.pt_size, .px_size = it->item.px_size};
  705. }
  706. return reload_fonts(term);
  707. }
  708. struct terminal *
  709. term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
  710. struct wayland *wayl, const char *foot_exe, const char *cwd,
  711. int argc, char *const *argv,
  712. void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data)
  713. {
  714. int ptmx = -1;
  715. int flash_fd = -1;
  716. int delay_lower_fd = -1;
  717. int delay_upper_fd = -1;
  718. int app_sync_updates_fd = -1;
  719. struct terminal *term = malloc(sizeof(*term));
  720. if (unlikely(term == NULL)) {
  721. LOG_ERRNO("malloc() failed");
  722. return NULL;
  723. }
  724. if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) == -1) {
  725. LOG_ERRNO("failed to open PTY");
  726. goto close_fds;
  727. }
  728. if ((flash_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) {
  729. LOG_ERRNO("failed to create flash timer FD");
  730. goto close_fds;
  731. }
  732. if ((delay_lower_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) == -1 ||
  733. (delay_upper_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) == -1)
  734. {
  735. LOG_ERRNO("failed to create delayed rendering timer FDs");
  736. goto close_fds;
  737. }
  738. if ((app_sync_updates_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) == -1)
  739. {
  740. LOG_ERRNO("failed to create application synchronized updates timer FD");
  741. goto close_fds;
  742. }
  743. if (ioctl(ptmx, TIOCSWINSZ,
  744. &(struct winsize){.ws_row = 24, .ws_col = 80}) < 0)
  745. {
  746. LOG_ERRNO("failed to set initial TIOCSWINSZ");
  747. goto close_fds;
  748. }
  749. int ptmx_flags;
  750. if ((ptmx_flags = fcntl(ptmx, F_GETFL)) < 0 ||
  751. fcntl(ptmx, F_SETFL, ptmx_flags | O_NONBLOCK) < 0)
  752. {
  753. LOG_ERRNO("failed to configure ptmx as non-blocking");
  754. goto err;
  755. }
  756. /*
  757. * Enable all FDM callbackes *except* ptmx - we can't do that
  758. * until the window has been 'configured' since we don't have a
  759. * size (and thus no grid) before then.
  760. */
  761. if (!fdm_add(fdm, flash_fd, EPOLLIN, &fdm_flash, term) ||
  762. !fdm_add(fdm, delay_lower_fd, EPOLLIN, &fdm_delayed_render, term) ||
  763. !fdm_add(fdm, delay_upper_fd, EPOLLIN, &fdm_delayed_render, term) ||
  764. !fdm_add(fdm, app_sync_updates_fd, EPOLLIN, &fdm_app_sync_updates_timeout, term))
  765. {
  766. goto err;
  767. }
  768. /* Initialize configure-based terminal attributes */
  769. *term = (struct terminal) {
  770. .fdm = fdm,
  771. .reaper = reaper,
  772. .conf = conf,
  773. .quit = false,
  774. .ptmx = ptmx,
  775. .ptmx_buffers = tll_init(),
  776. .ptmx_paste_buffers = tll_init(),
  777. .font_sizes = xmalloc(sizeof(term->font_sizes[0]) * tll_length(conf->fonts)),
  778. .font_dpi = 0.,
  779. .font_subpixel = (conf->colors.alpha == 0xffff /* Can't do subpixel rendering on transparent background */
  780. ? FCFT_SUBPIXEL_DEFAULT
  781. : FCFT_SUBPIXEL_NONE),
  782. .cursor_keys_mode = CURSOR_KEYS_NORMAL,
  783. .keypad_keys_mode = KEYPAD_NUMERICAL,
  784. .reverse_wrap = true,
  785. .auto_margin = true,
  786. .window_title_stack = tll_init(),
  787. .scale = 1,
  788. .flash = {.fd = flash_fd},
  789. .blink = {.fd = -1},
  790. .vt = {
  791. .state = 0, /* STATE_GROUND */
  792. },
  793. .colors = {
  794. .fg = conf->colors.fg,
  795. .bg = conf->colors.bg,
  796. .default_fg = conf->colors.fg,
  797. .default_bg = conf->colors.bg,
  798. .default_table = {
  799. conf->colors.regular[0],
  800. conf->colors.regular[1],
  801. conf->colors.regular[2],
  802. conf->colors.regular[3],
  803. conf->colors.regular[4],
  804. conf->colors.regular[5],
  805. conf->colors.regular[6],
  806. conf->colors.regular[7],
  807. conf->colors.bright[0],
  808. conf->colors.bright[1],
  809. conf->colors.bright[2],
  810. conf->colors.bright[3],
  811. conf->colors.bright[4],
  812. conf->colors.bright[5],
  813. conf->colors.bright[6],
  814. conf->colors.bright[7],
  815. },
  816. .alpha = conf->colors.alpha,
  817. },
  818. .origin = ORIGIN_ABSOLUTE,
  819. .default_cursor_blink = conf->cursor.blink,
  820. .default_cursor_style = conf->cursor.style,
  821. .cursor_style = conf->cursor.style,
  822. .cursor_blink = {
  823. .active = conf->cursor.blink,
  824. .state = CURSOR_BLINK_ON,
  825. .fd = -1,
  826. },
  827. .default_cursor_color = {
  828. .text = conf->cursor.color.text,
  829. .cursor = conf->cursor.color.cursor,
  830. },
  831. .cursor_color = {
  832. .text = conf->cursor.color.text,
  833. .cursor = conf->cursor.color.cursor,
  834. },
  835. .selection = {
  836. .start = {-1, -1},
  837. .end = {-1, -1},
  838. .auto_scroll = {
  839. .fd = -1,
  840. },
  841. },
  842. .normal = {.scroll_damage = tll_init(), .sixel_images = tll_init()},
  843. .alt = {.scroll_damage = tll_init(), .sixel_images = tll_init()},
  844. .grid = &term->normal,
  845. .composed_count = 0,
  846. .composed = NULL,
  847. .alt_scrolling = conf->mouse.alternate_scroll_mode,
  848. .meta = {
  849. .esc_prefix = true,
  850. .eight_bit = true,
  851. },
  852. .bell_is_urgent = conf->bell_is_urgent,
  853. .tab_stops = tll_init(),
  854. .wl = wayl,
  855. .render = {
  856. .scrollback_lines = conf->scrollback.lines,
  857. .app_sync_updates.timer_fd = app_sync_updates_fd,
  858. .workers = {
  859. .count = conf->render_worker_count,
  860. .queue = tll_init(),
  861. },
  862. .presentation_timings = conf->presentation_timings,
  863. },
  864. .delayed_render_timer = {
  865. .is_armed = false,
  866. .lower_fd = delay_lower_fd,
  867. .upper_fd = delay_upper_fd,
  868. },
  869. .sixel = {
  870. .palette_size = SIXEL_MAX_COLORS,
  871. },
  872. .hold_at_exit = conf->hold_at_exit,
  873. .shutdown_cb = shutdown_cb,
  874. .shutdown_data = shutdown_data,
  875. .foot_exe = xstrdup(foot_exe),
  876. .cwd = xstrdup(cwd),
  877. };
  878. {
  879. size_t i = 0;
  880. tll_foreach(conf->fonts, it) {
  881. term->font_sizes[i++] = (struct config_font){
  882. .pt_size = it->item.pt_size, .px_size = it->item.px_size};
  883. }
  884. }
  885. /* Start the slave/client */
  886. if ((term->slave = slave_spawn(
  887. term->ptmx, argc, term->cwd, argv,
  888. conf->term, conf->shell, conf->login_shell,
  889. &conf->notifications)) == -1)
  890. {
  891. goto err;
  892. }
  893. /* Guess scale; we're not mapped yet, so we don't know on which
  894. * output we'll be. Pick highest scale we find for now */
  895. tll_foreach(term->wl->monitors, it) {
  896. if (it->item.scale > term->scale)
  897. term->scale = it->item.scale;
  898. }
  899. initialize_color_cube(term);
  900. /* Initialize the Wayland window backend */
  901. if ((term->window = wayl_win_init(term)) == NULL)
  902. goto err;
  903. /* Load fonts */
  904. if (!term_font_dpi_changed(term))
  905. goto err;
  906. term->font_subpixel = get_font_subpixel(term);
  907. term_set_window_title(term, conf->title);
  908. /* Let the Wayland backend know we exist */
  909. tll_push_back(wayl->terms, term);
  910. switch (conf->startup_mode) {
  911. case STARTUP_WINDOWED:
  912. break;
  913. case STARTUP_MAXIMIZED:
  914. xdg_toplevel_set_maximized(term->window->xdg_toplevel);
  915. break;
  916. case STARTUP_FULLSCREEN:
  917. xdg_toplevel_set_fullscreen(term->window->xdg_toplevel, NULL);
  918. break;
  919. }
  920. if (!initialize_render_workers(term))
  921. goto err;
  922. return term;
  923. err:
  924. term->is_shutting_down = true;
  925. term_destroy(term);
  926. return NULL;
  927. close_fds:
  928. close(ptmx);
  929. fdm_del(fdm, flash_fd);
  930. fdm_del(fdm, delay_lower_fd);
  931. fdm_del(fdm, delay_upper_fd);
  932. fdm_del(fdm, app_sync_updates_fd);
  933. free(term);
  934. return NULL;
  935. }
  936. void
  937. term_window_configured(struct terminal *term)
  938. {
  939. /* Enable ptmx FDM callback */
  940. if (!term->is_shutting_down) {
  941. assert(term->window->is_configured);
  942. fdm_add(term->fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term);
  943. }
  944. }
  945. static bool
  946. fdm_shutdown(struct fdm *fdm, int fd, int events, void *data)
  947. {
  948. LOG_DBG("FDM shutdown");
  949. struct terminal *term = data;
  950. /* Kill the event FD */
  951. fdm_del(term->fdm, fd);
  952. wayl_win_destroy(term->window);
  953. term->window = NULL;
  954. struct wayland *wayl = term->wl;
  955. /*
  956. * Normally we'd get unmapped when we destroy the Wayland
  957. * above.
  958. *
  959. * However, it appears that under certain conditions, those events
  960. * are deferred (for example, when a screen locker is active), and
  961. * thus we can get here without having been unmapped.
  962. */
  963. tll_foreach(wayl->seats, it) {
  964. if (it->item.kbd_focus == term)
  965. it->item.kbd_focus = NULL;
  966. if (it->item.mouse_focus == term)
  967. it->item.mouse_focus = NULL;
  968. }
  969. void (*cb)(void *, int) = term->shutdown_cb;
  970. void *cb_data = term->shutdown_data;
  971. int exit_code = term_destroy(term);
  972. if (cb != NULL)
  973. cb(cb_data, exit_code);
  974. return true;
  975. }
  976. bool
  977. term_shutdown(struct terminal *term)
  978. {
  979. if (term->is_shutting_down)
  980. return true;
  981. term->is_shutting_down = true;
  982. /*
  983. * Close FDs then postpone self-destruction to the next poll
  984. * iteration, by creating an event FD that we trigger immediately.
  985. */
  986. term_cursor_blink_disable(term);
  987. fdm_del(term->fdm, term->selection.auto_scroll.fd);
  988. fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
  989. fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
  990. fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
  991. fdm_del(term->fdm, term->cursor_blink.fd);
  992. fdm_del(term->fdm, term->blink.fd);
  993. fdm_del(term->fdm, term->flash.fd);
  994. if (term->window != NULL && term->window->is_configured)
  995. fdm_del(term->fdm, term->ptmx);
  996. else
  997. close(term->ptmx);
  998. term->selection.auto_scroll.fd = -1;
  999. term->render.app_sync_updates.timer_fd = -1;
  1000. term->delayed_render_timer.lower_fd = -1;
  1001. term->delayed_render_timer.upper_fd = -1;
  1002. term->cursor_blink.fd = -1;
  1003. term->blink.fd = -1;
  1004. term->flash.fd = -1;
  1005. term->ptmx = -1;
  1006. int event_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
  1007. if (event_fd == -1) {
  1008. LOG_ERRNO("failed to create terminal shutdown event FD");
  1009. return false;
  1010. }
  1011. if (!fdm_add(term->fdm, event_fd, EPOLLIN, &fdm_shutdown, term)) {
  1012. close(event_fd);
  1013. return false;
  1014. }
  1015. if (write(event_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) {
  1016. LOG_ERRNO("failed to send terminal shutdown event");
  1017. fdm_del(term->fdm, event_fd);
  1018. return false;
  1019. }
  1020. return true;
  1021. }
  1022. static volatile sig_atomic_t alarm_raised;
  1023. static void
  1024. sig_alarm(int signo)
  1025. {
  1026. LOG_DBG("SIGALRM");
  1027. alarm_raised = 1;
  1028. }
  1029. int
  1030. term_destroy(struct terminal *term)
  1031. {
  1032. if (term == NULL)
  1033. return 0;
  1034. tll_foreach(term->wl->terms, it) {
  1035. if (it->item == term) {
  1036. tll_remove(term->wl->terms, it);
  1037. break;
  1038. }
  1039. }
  1040. fdm_del(term->fdm, term->selection.auto_scroll.fd);
  1041. fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
  1042. fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
  1043. fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
  1044. fdm_del(term->fdm, term->cursor_blink.fd);
  1045. fdm_del(term->fdm, term->blink.fd);
  1046. fdm_del(term->fdm, term->flash.fd);
  1047. fdm_del(term->fdm, term->ptmx);
  1048. if (term->window != NULL)
  1049. wayl_win_destroy(term->window);
  1050. mtx_lock(&term->render.workers.lock);
  1051. assert(tll_length(term->render.workers.queue) == 0);
  1052. /* Count livinig threads - we may get here when only some of the
  1053. * threads have been successfully started */
  1054. size_t worker_count = 0;
  1055. if (term->render.workers.threads != NULL) {
  1056. for (size_t i = 0; i < term->render.workers.count; i++, worker_count++) {
  1057. if (term->render.workers.threads[i] == 0)
  1058. break;
  1059. }
  1060. for (size_t i = 0; i < worker_count; i++) {
  1061. sem_post(&term->render.workers.start);
  1062. tll_push_back(term->render.workers.queue, -2);
  1063. }
  1064. }
  1065. mtx_unlock(&term->render.workers.lock);
  1066. free(term->vt.osc.data);
  1067. for (int row = 0; row < term->normal.num_rows; row++)
  1068. grid_row_free(term->normal.rows[row]);
  1069. free(term->normal.rows);
  1070. for (int row = 0; row < term->alt.num_rows; row++)
  1071. grid_row_free(term->alt.rows[row]);
  1072. free(term->alt.rows);
  1073. tll_free(term->normal.scroll_damage);
  1074. tll_free(term->alt.scroll_damage);
  1075. free(term->composed);
  1076. free(term->window_title);
  1077. tll_free_and_free(term->window_title_stack, free);
  1078. for (size_t i = 0; i < sizeof(term->fonts) / sizeof(term->fonts[0]); i++)
  1079. fcft_destroy(term->fonts[i]);
  1080. free(term->font_sizes);
  1081. free(term->search.buf);
  1082. if (term->render.workers.threads != NULL) {
  1083. for (size_t i = 0; i < term->render.workers.count; i++) {
  1084. if (term->render.workers.threads[i] != 0)
  1085. thrd_join(term->render.workers.threads[i], NULL);
  1086. }
  1087. }
  1088. free(term->render.workers.threads);
  1089. mtx_destroy(&term->render.workers.lock);
  1090. sem_destroy(&term->render.workers.start);
  1091. sem_destroy(&term->render.workers.done);
  1092. assert(tll_length(term->render.workers.queue) == 0);
  1093. tll_free(term->render.workers.queue);
  1094. tll_foreach(term->ptmx_buffers, it)
  1095. free(it->item.data);
  1096. tll_free(term->ptmx_buffers);
  1097. tll_foreach(term->ptmx_paste_buffers, it)
  1098. free(it->item.data);
  1099. tll_free(term->ptmx_paste_buffers);
  1100. tll_free(term->tab_stops);
  1101. tll_foreach(term->normal.sixel_images, it)
  1102. sixel_destroy(&it->item);
  1103. tll_free(term->normal.sixel_images);
  1104. tll_foreach(term->alt.sixel_images, it)
  1105. sixel_destroy(&it->item);
  1106. tll_free(term->alt.sixel_images);
  1107. sixel_fini(term);
  1108. free(term->foot_exe);
  1109. free(term->cwd);
  1110. int ret = EXIT_SUCCESS;
  1111. if (term->slave > 0) {
  1112. LOG_DBG("waiting for slave (PID=%u) to die", term->slave);
  1113. /*
  1114. * Note: we've closed ptmx, so the slave *should* exit...
  1115. *
  1116. * But, since it is possible to write clients that ignore
  1117. * this, we need to handle it in *some* way.
  1118. *
  1119. * So, what we do is register a SIGALRM handler, and configure
  1120. * a 2 second alarm. If the slave hasn't died after this time,
  1121. * we send it a SIGTERM, then wait another 2 seconds (using
  1122. * the same alarm mechanism). If it still hasn't died, we send
  1123. * it a SIGKILL.
  1124. *
  1125. * Note that this solution is *not* asynchronous, and any
  1126. * other events etc will be ignored during this time. This of
  1127. * course only applies to a 'foot --server' instance, where
  1128. * there might be other terminals running.
  1129. */
  1130. sigaction(SIGALRM, &(const struct sigaction){.sa_handler = &sig_alarm}, NULL);
  1131. alarm(2);
  1132. int status;
  1133. int kill_signal = SIGTERM;
  1134. while (true) {
  1135. int r = waitpid(term->slave, &status, 0);
  1136. if (r == term->slave)
  1137. break;
  1138. if (r == -1) {
  1139. assert(errno == EINTR);
  1140. if (alarm_raised) {
  1141. LOG_DBG("slave hasn't died yet, sending: %s (%d)",
  1142. kill_signal == SIGTERM ? "SIGTERM" : "SIGKILL",
  1143. kill_signal);
  1144. kill(term->slave, kill_signal);
  1145. alarm_raised = 0;
  1146. if (kill_signal != SIGKILL)
  1147. alarm(2);
  1148. kill_signal = SIGKILL;
  1149. }
  1150. }
  1151. }
  1152. /* Cancel alarm */
  1153. alarm(0);
  1154. sigaction(SIGALRM, &(const struct sigaction){.sa_handler = SIG_DFL}, NULL);
  1155. ret = EXIT_FAILURE;
  1156. if (WIFEXITED(status)) {
  1157. ret = WEXITSTATUS(status);
  1158. LOG_DBG("slave exited with code %d", ret);
  1159. } else if (WIFSIGNALED(status)) {
  1160. ret = WTERMSIG(status);
  1161. LOG_WARN("slave exited with signal %d (%s)", ret, strsignal(ret));
  1162. } else {
  1163. LOG_WARN("slave exited for unknown reason (status = 0x%08x)", status);
  1164. }
  1165. }
  1166. free(term);
  1167. #if defined(__GLIBC__)
  1168. if (!malloc_trim(0))
  1169. LOG_WARN("failed to trim memory");
  1170. #endif
  1171. return ret;
  1172. }
  1173. static inline void
  1174. erase_cell_range(struct terminal *term, struct row *row, int start, int end)
  1175. {
  1176. assert(start < term->cols);
  1177. assert(end < term->cols);
  1178. row->dirty = true;
  1179. if (unlikely(term->vt.attrs.have_bg)) {
  1180. for (int col = start; col <= end; col++) {
  1181. struct cell *c = &row->cells[col];
  1182. c->wc = 0;
  1183. c->attrs = (struct attributes){.have_bg = 1, .bg = term->vt.attrs.bg};
  1184. }
  1185. } else
  1186. memset(&row->cells[start], 0, (end - start + 1) * sizeof(row->cells[0]));
  1187. }
  1188. static inline void
  1189. erase_line(struct terminal *term, struct row *row)
  1190. {
  1191. erase_cell_range(term, row, 0, term->cols - 1);
  1192. row->linebreak = false;
  1193. }
  1194. void
  1195. term_reset(struct terminal *term, bool hard)
  1196. {
  1197. term->cursor_keys_mode = CURSOR_KEYS_NORMAL;
  1198. term->keypad_keys_mode = KEYPAD_NUMERICAL;
  1199. term->reverse = false;
  1200. term->hide_cursor = false;
  1201. term->reverse_wrap = true;
  1202. term->auto_margin = true;
  1203. term->insert_mode = false;
  1204. term->bracketed_paste = false;
  1205. term->focus_events = false;
  1206. term->mouse_tracking = MOUSE_NONE;
  1207. term->mouse_reporting = MOUSE_NORMAL;
  1208. term->charsets.selected = 0;
  1209. term->charsets.set[0] = CHARSET_ASCII;
  1210. term->charsets.set[1] = CHARSET_ASCII;
  1211. term->charsets.set[2] = CHARSET_ASCII;
  1212. term->charsets.set[3] = CHARSET_ASCII;
  1213. term->saved_charsets = term->charsets;
  1214. tll_free_and_free(term->window_title_stack, free);
  1215. term_set_window_title(term, term->conf->title);
  1216. term->scroll_region.start = 0;
  1217. term->scroll_region.end = term->rows;
  1218. free(term->vt.osc.data);
  1219. memset(&term->vt, 0, sizeof(term->vt));
  1220. term->vt.state = 0; /* GROUND */
  1221. if (term->grid == &term->alt) {
  1222. term->grid = &term->normal;
  1223. selection_cancel(term);
  1224. }
  1225. term->meta.esc_prefix = true;
  1226. term->meta.eight_bit = true;
  1227. tll_foreach(term->normal.sixel_images, it)
  1228. sixel_destroy(&it->item);
  1229. tll_free(term->normal.sixel_images);
  1230. tll_foreach(term->alt.sixel_images, it)
  1231. sixel_destroy(&it->item);
  1232. tll_free(term->alt.sixel_images);
  1233. if (!hard)
  1234. return;
  1235. term->flash.active = false;
  1236. term->blink.state = BLINK_ON;
  1237. fdm_del(term->fdm, term->blink.fd); term->blink.fd = -1;
  1238. term->colors.fg = term->colors.default_fg;
  1239. term->colors.bg = term->colors.default_bg;
  1240. for (size_t i = 0; i < 256; i++)
  1241. term->colors.table[i] = term->colors.default_table[i];
  1242. term->origin = ORIGIN_ABSOLUTE;
  1243. term->normal.cursor.lcf = false;
  1244. term->alt.cursor.lcf = false;
  1245. term->normal.cursor = (struct cursor){.point = {0, 0}};
  1246. term->normal.saved_cursor = (struct cursor){.point = {0, 0}};
  1247. term->alt.cursor = (struct cursor){.point = {0, 0}};
  1248. term->alt.saved_cursor = (struct cursor){.point = {0, 0}};
  1249. term->cursor_style = term->default_cursor_style;
  1250. if (term->conf->cursor.blink)
  1251. term_cursor_blink_enable(term);
  1252. else
  1253. term_cursor_blink_disable(term);
  1254. term->cursor_color.text = term->default_cursor_color.text;
  1255. term->cursor_color.cursor = term->default_cursor_color.cursor;
  1256. selection_cancel(term);
  1257. term->normal.offset = term->normal.view = 0;
  1258. term->alt.offset = term->alt.view = 0;
  1259. for (size_t i = 0; i < term->rows; i++) {
  1260. struct row *r = grid_row_and_alloc(&term->normal, i);
  1261. erase_line(term, r);
  1262. }
  1263. for (size_t i = 0; i < term->rows; i++) {
  1264. struct row *r = grid_row_and_alloc(&term->alt, i);
  1265. erase_line(term, r);
  1266. }
  1267. for (size_t i = term->rows; i < term->normal.num_rows; i++) {
  1268. grid_row_free(term->normal.rows[i]);
  1269. term->normal.rows[i] = NULL;
  1270. }
  1271. for (size_t i = term->rows; i < term->alt.num_rows; i++) {
  1272. grid_row_free(term->alt.rows[i]);
  1273. term->alt.rows[i] = NULL;
  1274. }
  1275. term->normal.cur_row = term->normal.rows[0];
  1276. term->alt.cur_row = term->alt.rows[0];
  1277. tll_free(term->normal.scroll_damage);
  1278. tll_free(term->alt.scroll_damage);
  1279. term->render.last_cursor.row = NULL;
  1280. term->render.was_flashing = false;
  1281. term_damage_all(term);
  1282. }
  1283. static bool
  1284. term_font_size_adjust(struct terminal *term, double amount)
  1285. {
  1286. for (size_t i = 0; i < tll_length(term->conf->fonts); i++) {
  1287. double old_pt_size = term->font_sizes[i].pt_size;
  1288. /*
  1289. * To ensure primary and user-configured fallback fonts are
  1290. * resizes by the same amount, convert pixel sizes to point
  1291. * sizes, and to the adjustment on point sizes only.
  1292. */
  1293. if (term->font_sizes[i].px_size > 0) {
  1294. double dpi = term->font_dpi;
  1295. old_pt_size = term->font_sizes[i].px_size * 72. / dpi;
  1296. }
  1297. term->font_sizes[i].pt_size = fmax(old_pt_size + amount, 0);
  1298. term->font_sizes[i].px_size = -1;
  1299. }
  1300. return reload_fonts(term);
  1301. }
  1302. bool
  1303. term_font_size_increase(struct terminal *term)
  1304. {
  1305. if (!term_font_size_adjust(term, 0.5))
  1306. return false;
  1307. return true;
  1308. }
  1309. bool
  1310. term_font_size_decrease(struct terminal *term)
  1311. {
  1312. if (!term_font_size_adjust(term, -0.5))
  1313. return false;
  1314. return true;
  1315. }
  1316. bool
  1317. term_font_size_reset(struct terminal *term)
  1318. {
  1319. return load_fonts_from_conf(term);
  1320. }
  1321. bool
  1322. term_font_dpi_changed(struct terminal *term)
  1323. {
  1324. float dpi = get_font_dpi(term);
  1325. if (dpi == term->font_dpi)
  1326. return true;
  1327. LOG_DBG("DPI changed (%.2f -> %.2f): reloading fonts", term->font_dpi, dpi);
  1328. term->font_dpi = dpi;
  1329. return reload_fonts(term);
  1330. }
  1331. void
  1332. term_font_subpixel_changed(struct terminal *term)
  1333. {
  1334. enum fcft_subpixel subpixel = get_font_subpixel(term);
  1335. if (term->font_subpixel == subpixel)
  1336. return;
  1337. #if defined(_DEBUG) && LOG_ENABLE_DBG
  1338. static const char *const str[] = {
  1339. [FCFT_SUBPIXEL_DEFAULT] = "default",
  1340. [FCFT_SUBPIXEL_NONE] = "disabled",
  1341. [FCFT_SUBPIXEL_HORIZONTAL_RGB] = "RGB",
  1342. [FCFT_SUBPIXEL_HORIZONTAL_BGR] = "BGR",
  1343. [FCFT_SUBPIXEL_VERTICAL_RGB] = "V-RGB",
  1344. [FCFT_SUBPIXEL_VERTICAL_BGR] = "V-BGR",
  1345. };
  1346. #endif
  1347. LOG_DBG("subpixel mode changed: %s -> %s", str[term->font_subpixel], str[subpixel]);
  1348. term->font_subpixel = subpixel;
  1349. term_damage_view(term);
  1350. render_refresh(term);
  1351. }
  1352. void
  1353. term_damage_rows(struct terminal *term, int start, int end)
  1354. {
  1355. assert(start <= end);
  1356. for (int r = start; r <= end; r++) {
  1357. struct row *row = grid_row(term->grid, r);
  1358. row->dirty = true;
  1359. for (int c = 0; c < term->grid->num_cols; c++)
  1360. row->cells[c].attrs.clean = 0;
  1361. }
  1362. }
  1363. void
  1364. term_damage_rows_in_view(struct terminal *term, int start, int end)
  1365. {
  1366. assert(start <= end);
  1367. for (int r = start; r <= end; r++) {
  1368. struct row *row = grid_row_in_view(term->grid, r);
  1369. row->dirty = true;
  1370. for (int c = 0; c < term->grid->num_cols; c++)
  1371. row->cells[c].attrs.clean = 0;
  1372. }
  1373. }
  1374. void
  1375. term_damage_all(struct terminal *term)
  1376. {
  1377. term_damage_rows(term, 0, term->rows - 1);
  1378. }
  1379. void
  1380. term_damage_view(struct terminal *term)
  1381. {
  1382. term_damage_rows_in_view(term, 0, term->rows - 1);
  1383. }
  1384. void
  1385. term_damage_cursor(struct terminal *term)
  1386. {
  1387. term->grid->cur_row->cells[term->grid->cursor.point.col].attrs.clean = 0;
  1388. term->grid->cur_row->dirty = true;
  1389. }
  1390. void
  1391. term_damage_margins(struct terminal *term)
  1392. {
  1393. term->render.margins = true;
  1394. }
  1395. void
  1396. term_damage_scroll(struct terminal *term, enum damage_type damage_type,
  1397. struct scroll_region region, int lines)
  1398. {
  1399. if (tll_length(term->grid->scroll_damage) > 0) {
  1400. struct damage *dmg = &tll_back(term->grid->scroll_damage);
  1401. if (dmg->type == damage_type &&
  1402. dmg->region.start == region.start &&
  1403. dmg->region.end == region.end)
  1404. {
  1405. dmg->lines += lines;
  1406. return;
  1407. }
  1408. }
  1409. struct damage dmg = {
  1410. .type = damage_type,
  1411. .region = region,
  1412. .lines = lines,
  1413. };
  1414. tll_push_back(term->grid->scroll_damage, dmg);
  1415. }
  1416. void
  1417. term_erase(struct terminal *term, const struct coord *start, const struct coord *end)
  1418. {
  1419. assert(start->row <= end->row);
  1420. assert(start->col <= end->col || start->row < end->row);
  1421. if (start->row == end->row) {
  1422. struct row *row = grid_row(term->grid, start->row);
  1423. erase_cell_range(term, row, start->col, end->col);
  1424. sixel_overwrite_by_row(term, start->row, start->col, end->col - start->col + 1);
  1425. return;
  1426. }
  1427. assert(end->row > start->row);
  1428. erase_cell_range(
  1429. term, grid_row(term->grid, start->row), start->col, term->cols - 1);
  1430. sixel_overwrite_by_row(term, start->row, start->col, term->cols - start->col);
  1431. for (int r = start->row + 1; r < end->row; r++)
  1432. erase_line(term, grid_row(term->grid, r));
  1433. sixel_overwrite_by_rectangle(
  1434. term, start->row + 1, 0, end->row - start->row, term->cols);
  1435. erase_cell_range(term, grid_row(term->grid, end->row), 0, end->col);
  1436. sixel_overwrite_by_row(term, end->row, 0, end->col + 1);
  1437. }
  1438. int
  1439. term_row_rel_to_abs(const struct terminal *term, int row)
  1440. {
  1441. switch (term->origin) {
  1442. case ORIGIN_ABSOLUTE:
  1443. return min(row, term->rows - 1);
  1444. case ORIGIN_RELATIVE:
  1445. return min(row + term->scroll_region.start, term->scroll_region.end - 1);
  1446. }
  1447. assert(false);
  1448. return -1;
  1449. }
  1450. void
  1451. term_cursor_to(struct terminal *term, int row, int col)
  1452. {
  1453. assert(row < term->rows);
  1454. assert(col < term->cols);
  1455. term->grid->cursor.lcf = false;
  1456. term->grid->cursor.point.col = col;
  1457. term->grid->cursor.point.row = row;
  1458. term->grid->cur_row = grid_row(term->grid, row);
  1459. }
  1460. void
  1461. term_cursor_home(struct terminal *term)
  1462. {
  1463. term_cursor_to(term, term_row_rel_to_abs(term, 0), 0);
  1464. }
  1465. void
  1466. term_cursor_left(struct terminal *term, int count)
  1467. {
  1468. assert(count >= 0);
  1469. int new_col = term->grid->cursor.point.col - count;
  1470. /* Reverse wrap */
  1471. if (unlikely(new_col < 0)) {
  1472. if (likely(term->reverse_wrap && term->auto_margin)) {
  1473. /* Number of rows to reverse wrap through */
  1474. int row_count = (abs(new_col) - 1) / term->cols + 1;
  1475. /* Row number cursor will end up on */
  1476. int new_row_no = term->grid->cursor.point.row - row_count;
  1477. /* New column number */
  1478. new_col = term->cols - ((abs(new_col) - 1) % term->cols + 1);
  1479. assert(new_col >= 0 && new_col < term->cols);
  1480. /* Don't back up past the scroll region */
  1481. /* TODO: should this be allowed? */
  1482. if (new_row_no < term->scroll_region.start) {
  1483. new_row_no = term->scroll_region.start;
  1484. new_col = 0;
  1485. }
  1486. struct row *new_row = grid_row(term->grid, new_row_no);
  1487. term->grid->cursor.point.col = new_col;
  1488. term->grid->cursor.point.row = new_row_no;
  1489. term->grid->cursor.lcf = false;
  1490. term->grid->cur_row = new_row;
  1491. return;
  1492. }
  1493. /* Reverse wrap disabled - don't let cursor move past first column */
  1494. new_col = 0;
  1495. }
  1496. assert(new_col >= 0);
  1497. term->grid->cursor.point.col = new_col;
  1498. term->grid->cursor.lcf = false;
  1499. }
  1500. void
  1501. term_cursor_right(struct terminal *term, int count)
  1502. {
  1503. int move_amount = min(term->cols - term->grid->cursor.point.col - 1, count);
  1504. term->grid->cursor.point.col += move_amount;
  1505. assert(term->grid->cursor.point.col < term->cols);
  1506. term->grid->cursor.lcf = false;
  1507. }
  1508. void
  1509. term_cursor_up(struct terminal *term, int count)
  1510. {
  1511. int top = term->origin == ORIGIN_ABSOLUTE ? 0 : term->scroll_region.start;
  1512. assert(term->grid->cursor.point.row >= top);
  1513. int move_amount = min(term->grid->cursor.point.row - top, count);
  1514. term_cursor_to(term, term->grid->cursor.point.row - move_amount, term->grid->cursor.point.col);
  1515. }
  1516. void
  1517. term_cursor_down(struct terminal *term, int count)
  1518. {
  1519. int bottom = term->origin == ORIGIN_ABSOLUTE ? term->rows : term->scroll_region.end;
  1520. assert(bottom >= term->grid->cursor.point.row);
  1521. int move_amount = min(bottom - term->grid->cursor.point.row - 1, count);
  1522. term_cursor_to(term, term->grid->cursor.point.row + move_amount, term->grid->cursor.point.col);
  1523. }
  1524. static bool
  1525. cursor_blink_start_timer(struct terminal *term)
  1526. {
  1527. if (term->cursor_blink.fd < 0) {
  1528. int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
  1529. if (fd < 0) {
  1530. LOG_ERRNO("failed to create cursor blink timer FD");
  1531. return false;
  1532. }
  1533. if (!fdm_add(term->fdm, fd, EPOLLIN, &fdm_cursor_blink, term)) {
  1534. close(fd);
  1535. return false;
  1536. }
  1537. term->cursor_blink.fd = fd;
  1538. }
  1539. static const struct itimerspec timer = {
  1540. .it_value = {.tv_sec = 0, .tv_nsec = 500000000},
  1541. .it_interval = {.tv_sec = 0, .tv_nsec = 500000000},
  1542. };
  1543. if (timerfd_settime(term->cursor_blink.fd, 0, &timer, NULL) < 0) {
  1544. LOG_ERRNO("failed to arm cursor blink timer");
  1545. fdm_del(term->fdm, term->cursor_blink.fd);
  1546. term->cursor_blink.fd = -1;
  1547. return false;
  1548. }
  1549. return true;
  1550. }
  1551. static bool
  1552. cursor_blink_stop_timer(struct terminal *term)
  1553. {
  1554. fdm_del(term->fdm, term->cursor_blink.fd);
  1555. term->cursor_blink.fd = -1;
  1556. return true;
  1557. }
  1558. void
  1559. term_cursor_blink_enable(struct terminal *term)
  1560. {
  1561. term->cursor_blink.state = CURSOR_BLINK_ON;
  1562. term->cursor_blink.active = term->kbd_focus
  1563. ? cursor_blink_start_timer(term) : true;
  1564. }
  1565. void
  1566. term_cursor_blink_disable(struct terminal *term)
  1567. {
  1568. term->cursor_blink.active = false;
  1569. term->cursor_blink.state = CURSOR_BLINK_ON;
  1570. cursor_blink_stop_timer(term);
  1571. }
  1572. void
  1573. term_cursor_blink_restart(struct terminal *term)
  1574. {
  1575. if (term->cursor_blink.active) {
  1576. term->cursor_blink.state = CURSOR_BLINK_ON;
  1577. term->cursor_blink.active = term->kbd_focus
  1578. ? cursor_blink_start_timer(term) : true;
  1579. }
  1580. }
  1581. static bool
  1582. selection_on_top_region(const struct terminal *term,
  1583. struct scroll_region region)
  1584. {
  1585. return region.start > 0 &&
  1586. selection_on_rows(term, 0, region.start - 1);
  1587. }
  1588. static bool
  1589. selection_on_bottom_region(const struct terminal *term,
  1590. struct scroll_region region)
  1591. {
  1592. return region.end < term->rows &&
  1593. selection_on_rows(term, region.end, term->rows - 1);
  1594. }
  1595. void
  1596. term_scroll_partial(struct terminal *term, struct scroll_region region, int rows)
  1597. {
  1598. LOG_DBG("scroll: rows=%d, region.start=%d, region.end=%d",
  1599. rows, region.start, region.end);
  1600. /* Verify scroll amount has been clamped */
  1601. assert(rows <= region.end - region.start);
  1602. /* Cancel selections that cannot be scrolled */
  1603. if (unlikely(term->selection.end.row >= 0)) {
  1604. /*
  1605. * Selection is (partly) inside either the top or bottom
  1606. * scrolling regions, or on (at least one) of the lines
  1607. * scrolled in (i.e. re-used lines).
  1608. */
  1609. if (selection_on_top_region(term, region) ||
  1610. selection_on_bottom_region(term, region) ||
  1611. selection_on_rows(term, region.end - rows, region.end - 1))
  1612. {
  1613. selection_cancel(term);
  1614. }
  1615. }
  1616. sixel_scroll_up(term, rows);
  1617. bool view_follows = term->grid->view == term->grid->offset;
  1618. term->grid->offset += rows;
  1619. term->grid->offset &= term->grid->num_rows - 1;
  1620. if (view_follows) {
  1621. selection_view_down(term, term->grid->offset);
  1622. term->grid->view = term->grid->offset;
  1623. }
  1624. /* Top non-scrolling region. */
  1625. for (int i = region.start - 1; i >= 0; i--)
  1626. grid_swap_row(term->grid, i - rows, i);
  1627. /* Bottom non-scrolling region */
  1628. for (int i = term->rows - 1; i >= region.end; i--)
  1629. grid_swap_row(term->grid, i - rows, i);
  1630. /* Erase scrolled in lines */
  1631. for (int r = region.end - rows; r < region.end; r++)
  1632. erase_line(term, grid_row_and_alloc(term->grid, r));
  1633. term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
  1634. term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
  1635. #if defined(_DEBUG)
  1636. for (int r = 0; r < term->rows; r++)
  1637. assert(grid_row(term->grid, r) != NULL);
  1638. #endif
  1639. }
  1640. void
  1641. term_scroll(struct terminal *term, int rows)
  1642. {
  1643. term_scroll_partial(term, term->scroll_region, rows);
  1644. }
  1645. void
  1646. term_scroll_reverse_partial(struct terminal *term,
  1647. struct scroll_region region, int rows)
  1648. {
  1649. LOG_DBG("scroll reverse: rows=%d, region.start=%d, region.end=%d",
  1650. rows, region.start, region.end);
  1651. /* Verify scroll amount has been clamped */
  1652. assert(rows <= region.end - region.start);
  1653. /* Cancel selections that cannot be scrolled */
  1654. if (unlikely(term->selection.end.row >= 0)) {
  1655. /*
  1656. * Selection is (partly) inside either the top or bottom
  1657. * scrolling regions, or on (at least one) of the lines
  1658. * scrolled in (i.e. re-used lines).
  1659. */
  1660. if (selection_on_top_region(term, region) ||
  1661. selection_on_bottom_region(term, region) ||
  1662. selection_on_rows(term, region.start, region.start + rows - 1))
  1663. {
  1664. selection_cancel(term);
  1665. }
  1666. }
  1667. sixel_scroll_down(term, rows);
  1668. bool view_follows = term->grid->view == term->grid->offset;
  1669. term->grid->offset -= rows;
  1670. while (term->grid->offset < 0)
  1671. term->grid->offset += term->grid->num_rows;
  1672. term->grid->offset &= term->grid->num_rows - 1;
  1673. assert(term->grid->offset >= 0);
  1674. assert(term->grid->offset < term->grid->num_rows);
  1675. if (view_follows) {
  1676. selection_view_up(term, term->grid->offset);
  1677. term->grid->view = term->grid->offset;
  1678. }
  1679. /* Bottom non-scrolling region */
  1680. for (int i = region.end + rows; i < term->rows + rows; i++)
  1681. grid_swap_row(term->grid, i, i - rows);
  1682. /* Top non-scrolling region */
  1683. for (int i = 0 + rows; i < region.start + rows; i++)
  1684. grid_swap_row(term->grid, i, i - rows);
  1685. /* Erase scrolled in lines */
  1686. for (int r = region.start; r < region.start + rows; r++)
  1687. erase_line(term, grid_row_and_alloc(term->grid, r));
  1688. term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows);
  1689. term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
  1690. #if defined(_DEBUG)
  1691. for (int r = 0; r < term->rows; r++)
  1692. assert(grid_row(term->grid, r) != NULL);
  1693. #endif
  1694. }
  1695. void
  1696. term_scroll_reverse(struct terminal *term, int rows)
  1697. {
  1698. term_scroll_reverse_partial(term, term->scroll_region, rows);
  1699. }
  1700. void
  1701. term_carriage_return(struct terminal *term)
  1702. {
  1703. term_cursor_left(term, term->grid->cursor.point.col);
  1704. }
  1705. void
  1706. term_linefeed(struct terminal *term)
  1707. {
  1708. term->grid->cur_row->linebreak = true;
  1709. term->grid->cursor.lcf = false;
  1710. if (term->grid->cursor.point.row == term->scroll_region.end - 1)
  1711. term_scroll(term, 1);
  1712. else
  1713. term_cursor_down(term, 1);
  1714. }
  1715. void
  1716. term_reverse_index(struct terminal *term)
  1717. {
  1718. if (term->grid->cursor.point.row == term->scroll_region.start)
  1719. term_scroll_reverse(term, 1);
  1720. else
  1721. term_cursor_up(term, 1);
  1722. }
  1723. void
  1724. term_reset_view(struct terminal *term)
  1725. {
  1726. if (term->grid->view == term->grid->offset)
  1727. return;
  1728. term->grid->view = term->grid->offset;
  1729. term_damage_view(term);
  1730. }
  1731. void
  1732. term_restore_cursor(struct terminal *term, const struct cursor *cursor)
  1733. {
  1734. int row = min(cursor->point.row, term->rows - 1);
  1735. int col = min(cursor->point.col, term->cols - 1);
  1736. term_cursor_to(term, row, col);
  1737. term->grid->cursor.lcf = cursor->lcf;
  1738. }
  1739. void
  1740. term_visual_focus_in(struct terminal *term)
  1741. {
  1742. if (term->visual_focus)
  1743. return;
  1744. term->visual_focus = true;
  1745. if (term->cursor_blink.active)
  1746. cursor_blink_start_timer(term);
  1747. render_refresh_csd(term);
  1748. }
  1749. void
  1750. term_visual_focus_out(struct terminal *term)
  1751. {
  1752. if (!term->visual_focus)
  1753. return;
  1754. term->visual_focus = false;
  1755. if (term->cursor_blink.active)
  1756. cursor_blink_stop_timer(term);
  1757. render_refresh_csd(term);
  1758. }
  1759. void
  1760. term_kbd_focus_in(struct terminal *term)
  1761. {
  1762. if (term->kbd_focus)
  1763. return;
  1764. term->kbd_focus = true;
  1765. if (term->render.urgency) {
  1766. term->render.urgency = false;
  1767. term_damage_margins(term);
  1768. }
  1769. cursor_refresh(term);
  1770. if (term->focus_events)
  1771. term_to_slave(term, "\033[I", 3);
  1772. }
  1773. void
  1774. term_kbd_focus_out(struct terminal *term)
  1775. {
  1776. if (!term->kbd_focus)
  1777. return;
  1778. tll_foreach(term->wl->seats, it)
  1779. if (it->item.kbd_focus == term)
  1780. return;
  1781. term->kbd_focus = false;
  1782. cursor_refresh(term);
  1783. if (term->focus_events)
  1784. term_to_slave(term, "\033[O", 3);
  1785. }
  1786. static int
  1787. linux_mouse_button_to_x(int button)
  1788. {
  1789. switch (button) {
  1790. case BTN_LEFT: return 1;
  1791. case BTN_MIDDLE: return 2;
  1792. case BTN_RIGHT: return 3;
  1793. case BTN_BACK: return 4;
  1794. case BTN_FORWARD: return 5;
  1795. case BTN_SIDE: return 8;
  1796. case BTN_EXTRA: return 9;
  1797. case BTN_TASK: return -1; /* TODO: ??? */
  1798. default:
  1799. LOG_WARN("unrecognized mouse button: %d (0x%x)", button, button);
  1800. return -1;
  1801. }
  1802. }
  1803. static int
  1804. encode_xbutton(int xbutton)
  1805. {
  1806. switch (xbutton) {
  1807. case 1: case 2: case 3:
  1808. return xbutton - 1;
  1809. case 4: case 5:
  1810. /* Like button 1 and 2, but with 64 added */
  1811. return xbutton - 4 + 64;
  1812. case 6: case 7:
  1813. /* Same as 4 and 5. Note: the offset should be something else? */
  1814. return xbutton - 6 + 64;
  1815. case 8: case 9: case 10: case 11:
  1816. /* Similar to 4 and 5, but adding 128 instead of 64 */
  1817. return xbutton - 8 + 128;
  1818. default:
  1819. LOG_ERR("cannot encode X mouse button: %d", xbutton);
  1820. return -1;
  1821. }
  1822. }
  1823. static void
  1824. report_mouse_click(struct terminal *term, int encoded_button, int row, int col,
  1825. bool release)
  1826. {
  1827. char response[128];
  1828. switch (term->mouse_reporting) {
  1829. case MOUSE_NORMAL: {
  1830. int encoded_col = 32 + col + 1;
  1831. int encoded_row = 32 + row + 1;
  1832. if (encoded_col > 255 || encoded_row > 255)
  1833. return;
  1834. snprintf(response, sizeof(response), "\033[M%c%c%c",
  1835. 32 + (release ? 3 : encoded_button), encoded_col, encoded_row);
  1836. break;
  1837. }
  1838. case MOUSE_SGR:
  1839. snprintf(response, sizeof(response), "\033[<%d;%d;%d%c",
  1840. encoded_button, col + 1, row + 1, release ? 'm' : 'M');
  1841. break;
  1842. case MOUSE_URXVT:
  1843. snprintf(response, sizeof(response), "\033[%d;%d;%dM",
  1844. 32 + (release ? 3 : encoded_button), col + 1, row + 1);
  1845. break;
  1846. case MOUSE_UTF8:
  1847. /* Unimplemented */
  1848. return;
  1849. }
  1850. term_to_slave(term, response, strlen(response));
  1851. }
  1852. static void
  1853. report_mouse_motion(struct terminal *term, int encoded_button, int row, int col)
  1854. {
  1855. report_mouse_click(term, encoded_button, row, col, false);
  1856. }
  1857. bool
  1858. term_mouse_grabbed(const struct terminal *term, struct seat *seat)
  1859. {
  1860. /*
  1861. * Mouse is grabbed by us, regardless of whether mouse tracking has been enabled or not.
  1862. */
  1863. return seat->kbd_focus == term &&
  1864. seat->kbd.shift &&
  1865. !seat->kbd.alt && /*!seat->kbd.ctrl &&*/ !seat->kbd.meta;
  1866. }
  1867. void
  1868. term_mouse_down(struct terminal *term, int button, int row, int col,
  1869. bool _shift, bool _alt, bool _ctrl)
  1870. {
  1871. /* Map libevent button event code to X button number */
  1872. int xbutton = linux_mouse_button_to_x(button);
  1873. if (xbutton == -1)
  1874. return;
  1875. int encoded = encode_xbutton(xbutton);
  1876. if (encoded == -1)
  1877. return;
  1878. bool has_focus = term->kbd_focus;
  1879. bool shift = has_focus ? _shift : false;
  1880. bool alt = has_focus ? _alt : false;
  1881. bool ctrl = has_focus ? _ctrl : false;
  1882. encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0);
  1883. switch (term->mouse_tracking) {
  1884. case MOUSE_NONE:
  1885. break;
  1886. case MOUSE_CLICK:
  1887. case MOUSE_DRAG:
  1888. case MOUSE_MOTION:
  1889. report_mouse_click(term, encoded, row, col, false);
  1890. break;
  1891. case MOUSE_X10:
  1892. /* Never enabled */
  1893. assert(false && "unimplemented");
  1894. break;
  1895. }
  1896. }
  1897. void
  1898. term_mouse_up(struct terminal *term, int button, int row, int col,
  1899. bool _shift, bool _alt, bool _ctrl)
  1900. {
  1901. /* Map libevent button event code to X button number */
  1902. int xbutton = linux_mouse_button_to_x(button);
  1903. if (xbutton == -1)
  1904. return;
  1905. if (xbutton == 4 || xbutton == 5) {
  1906. /* No release events for scroll buttons */
  1907. return;
  1908. }
  1909. int encoded = encode_xbutton(xbutton);
  1910. if (encoded == -1)
  1911. return;
  1912. bool has_focus = term->kbd_focus;
  1913. bool shift = has_focus ? _shift : false;
  1914. bool alt = has_focus ? _alt : false;
  1915. bool ctrl = has_focus ? _ctrl : false;
  1916. encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0);
  1917. switch (term->mouse_tracking) {
  1918. case MOUSE_NONE:
  1919. break;
  1920. case MOUSE_CLICK:
  1921. case MOUSE_DRAG:
  1922. case MOUSE_MOTION:
  1923. report_mouse_click(term, encoded, row, col, true);
  1924. break;
  1925. case MOUSE_X10:
  1926. /* Never enabled */
  1927. assert(false && "unimplemented");
  1928. break;
  1929. }
  1930. }
  1931. void
  1932. term_mouse_motion(struct terminal *term, int button, int row, int col,
  1933. bool _shift, bool _alt, bool _ctrl)
  1934. {
  1935. int encoded = 0;
  1936. if (button != 0) {
  1937. /* Map libevent button event code to X button number */
  1938. int xbutton = linux_mouse_button_to_x(button);
  1939. if (xbutton == -1)
  1940. return;
  1941. encoded = encode_xbutton(xbutton);
  1942. if (encoded == -1)
  1943. return;
  1944. } else
  1945. encoded = 3; /* "released" */
  1946. bool has_focus = term->kbd_focus;
  1947. bool shift = has_focus ? _shift : false;
  1948. bool alt = has_focus ? _alt : false;
  1949. bool ctrl = has_focus ? _ctrl : false;
  1950. encoded += 32; /* Motion event */
  1951. encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0);
  1952. switch (term->mouse_tracking) {
  1953. case MOUSE_NONE:
  1954. case MOUSE_CLICK:
  1955. return;
  1956. case MOUSE_DRAG:
  1957. if (button == 0)
  1958. return;
  1959. /* FALLTHROUGH */
  1960. case MOUSE_MOTION:
  1961. report_mouse_motion(term, encoded, row, col);
  1962. break;
  1963. case MOUSE_X10:
  1964. /* Never enabled */
  1965. assert(false && "unimplemented");
  1966. break;
  1967. }
  1968. }
  1969. void
  1970. term_xcursor_update_for_seat(struct terminal *term, struct seat *seat)
  1971. {
  1972. const char *xcursor
  1973. = seat->pointer.hidden ? XCURSOR_HIDDEN
  1974. : term->is_searching ? XCURSOR_LEFT_PTR
  1975. : selection_enabled(term, seat) ? XCURSOR_TEXT
  1976. : XCURSOR_LEFT_PTR;
  1977. render_xcursor_set(seat, term, xcursor);
  1978. }
  1979. void
  1980. term_xcursor_update(struct terminal *term)
  1981. {
  1982. tll_foreach(term->wl->seats, it)
  1983. term_xcursor_update_for_seat(term, &it->item);
  1984. }
  1985. void
  1986. term_set_window_title(struct terminal *term, const char *title)
  1987. {
  1988. free(term->window_title);
  1989. term->window_title = xstrdup(title);
  1990. render_refresh_title(term);
  1991. }
  1992. void
  1993. term_flash(struct terminal *term, unsigned duration_ms)
  1994. {
  1995. LOG_DBG("FLASH for %ums", duration_ms);
  1996. struct itimerspec alarm = {
  1997. .it_value = {.tv_sec = 0, .tv_nsec = duration_ms * 1000000},
  1998. };
  1999. if (timerfd_settime(term->flash.fd, 0, &alarm, NULL) < 0)
  2000. LOG_ERRNO("failed to arm flash timer");
  2001. else {
  2002. term->flash.active = true;
  2003. }
  2004. }
  2005. void
  2006. term_bell(struct terminal *term)
  2007. {
  2008. if (term->kbd_focus || !term->bell_is_urgent)
  2009. return;
  2010. /* There's no 'urgency' hint in Wayland - we just paint the margins red */
  2011. term->render.urgency = true;
  2012. term_damage_margins(term);
  2013. }
  2014. bool
  2015. term_spawn_new(const struct terminal *term)
  2016. {
  2017. return spawn(
  2018. term->reaper, term->cwd, (char *const []){term->foot_exe, NULL},
  2019. -1, -1, -1);
  2020. }
  2021. void
  2022. term_enable_app_sync_updates(struct terminal *term)
  2023. {
  2024. if (!term->render.app_sync_updates.enabled)
  2025. term->render.app_sync_updates.flipped = true;
  2026. term->render.app_sync_updates.enabled = true;
  2027. if (timerfd_settime(
  2028. term->render.app_sync_updates.timer_fd, 0,
  2029. &(struct itimerspec){.it_value = {.tv_sec = 1}}, NULL) < 0)
  2030. {
  2031. LOG_ERR("failed to arm timer for application synchronized updates");
  2032. }
  2033. /* Disarm delayed rendering timers */
  2034. timerfd_settime(
  2035. term->delayed_render_timer.lower_fd, 0,
  2036. &(struct itimerspec){{0}}, NULL);
  2037. timerfd_settime(
  2038. term->delayed_render_timer.upper_fd, 0,
  2039. &(struct itimerspec){{0}}, NULL);
  2040. term->delayed_render_timer.is_armed = false;
  2041. }
  2042. void
  2043. term_disable_app_sync_updates(struct terminal *term)
  2044. {
  2045. if (!term->render.app_sync_updates.enabled)
  2046. return;
  2047. term->render.app_sync_updates.enabled = false;
  2048. term->render.app_sync_updates.flipped = true;
  2049. render_refresh(term);
  2050. /* Reset timers */
  2051. timerfd_settime(
  2052. term->render.app_sync_updates.timer_fd, 0,
  2053. &(struct itimerspec){{0}}, NULL);
  2054. }
  2055. static inline void
  2056. print_linewrap(struct terminal *term)
  2057. {
  2058. if (likely(!term->grid->cursor.lcf)) {
  2059. /* Not and end of line */
  2060. return;
  2061. }
  2062. if (unlikely(!term->auto_margin)) {
  2063. /* Auto-wrap disabled */
  2064. return;
  2065. }
  2066. term->grid->cursor.lcf = false;
  2067. const int row = term->grid->cursor.point.row;
  2068. if (row == term->scroll_region.end - 1)
  2069. term_scroll(term, 1);
  2070. else {
  2071. const int new_row = min(row + 1, term->rows - 1);
  2072. term->grid->cursor.point.row = new_row;
  2073. term->grid->cur_row = grid_row(term->grid, new_row);
  2074. }
  2075. term->grid->cursor.point.col = 0;
  2076. }
  2077. static inline void
  2078. print_insert(struct terminal *term, int width)
  2079. {
  2080. if (likely(!term->insert_mode))
  2081. return;
  2082. assert(width > 0);
  2083. struct row *row = term->grid->cur_row;
  2084. const size_t move_count = max(0, term->cols - term->grid->cursor.point.col - width);
  2085. memmove(
  2086. &row->cells[term->grid->cursor.point.col + width],
  2087. &row->cells[term->grid->cursor.point.col],
  2088. move_count * sizeof(struct cell));
  2089. /* Mark moved cells as dirty */
  2090. for (size_t i = term->grid->cursor.point.col + width; i < term->cols; i++)
  2091. row->cells[i].attrs.clean = 0;
  2092. }
  2093. static void
  2094. print_spacer(struct terminal *term, int col)
  2095. {
  2096. struct row *row = term->grid->cur_row;
  2097. struct cell *cell = &row->cells[col];
  2098. cell->wc = CELL_MULT_COL_SPACER;
  2099. cell->attrs = term->vt.attrs;
  2100. cell->attrs.clean = 0;
  2101. }
  2102. void
  2103. term_print(struct terminal *term, wchar_t wc, int width)
  2104. {
  2105. assert(width > 0);
  2106. print_linewrap(term);
  2107. print_insert(term, width);
  2108. if (unlikely(width > 1) && likely(term->auto_margin) &&
  2109. term->grid->cursor.point.col + width > term->cols)
  2110. {
  2111. /* Multi-column character that doesn't fit on current line -
  2112. * pad with spacers */
  2113. for (size_t i = term->grid->cursor.point.col; i < term->cols; i++)
  2114. print_spacer(term, i);
  2115. /* And force a line-wrap */
  2116. term->grid->cursor.lcf = 1;
  2117. print_linewrap(term);
  2118. }
  2119. sixel_overwrite_at_cursor(term, width);
  2120. /* *Must* get current cell *after* linewrap+insert */
  2121. struct row *row = term->grid->cur_row;
  2122. struct cell *cell = &row->cells[term->grid->cursor.point.col];
  2123. cell->wc = term->vt.last_printed = wc;
  2124. cell->attrs = term->vt.attrs;
  2125. row->dirty = true;
  2126. cell->attrs.clean = 0;
  2127. /* Advance cursor the 'additional' columns while dirty:ing the cells */
  2128. for (int i = 1; i < width && term->grid->cursor.point.col < term->cols - 1; i++) {
  2129. term->grid->cursor.point.col++;
  2130. print_spacer(term, term->grid->cursor.point.col);
  2131. }
  2132. /* Advance cursor */
  2133. if (term->grid->cursor.point.col < term->cols - 1) {
  2134. term->grid->cursor.point.col++;
  2135. assert(!term->grid->cursor.lcf);
  2136. } else
  2137. term->grid->cursor.lcf = true;
  2138. }
  2139. enum term_surface
  2140. term_surface_kind(const struct terminal *term, const struct wl_surface *surface)
  2141. {
  2142. if (likely(surface == term->window->surface))
  2143. return TERM_SURF_GRID;
  2144. else if (surface == term->window->search_surface)
  2145. return TERM_SURF_SEARCH;
  2146. else if (surface == term->window->scrollback_indicator_surface)
  2147. return TERM_SURF_SCROLLBACK_INDICATOR;
  2148. else if (surface == term->window->render_timer_surface)
  2149. return TERM_SURF_RENDER_TIMER;
  2150. else if (surface == term->window->csd.surface[CSD_SURF_TITLE])
  2151. return TERM_SURF_TITLE;
  2152. else if (surface == term->window->csd.surface[CSD_SURF_LEFT])
  2153. return TERM_SURF_BORDER_LEFT;
  2154. else if (surface == term->window->csd.surface[CSD_SURF_RIGHT])
  2155. return TERM_SURF_BORDER_RIGHT;
  2156. else if (surface == term->window->csd.surface[CSD_SURF_TOP])
  2157. return TERM_SURF_BORDER_TOP;
  2158. else if (surface == term->window->csd.surface[CSD_SURF_BOTTOM])
  2159. return TERM_SURF_BORDER_BOTTOM;
  2160. else if (surface == term->window->csd.surface[CSD_SURF_MINIMIZE])
  2161. return TERM_SURF_BUTTON_MINIMIZE;
  2162. else if (surface == term->window->csd.surface[CSD_SURF_MAXIMIZE])
  2163. return TERM_SURF_BUTTON_MAXIMIZE;
  2164. else if (surface == term->window->csd.surface[CSD_SURF_CLOSE])
  2165. return TERM_SURF_BUTTON_CLOSE;
  2166. else
  2167. return TERM_SURF_NONE;
  2168. }
  2169. static bool
  2170. rows_to_text(const struct terminal *term, int start, int end,
  2171. char **text, size_t *len)
  2172. {
  2173. struct extraction_context *ctx = extract_begin(SELECTION_NONE);
  2174. if (ctx == NULL)
  2175. return false;
  2176. for (size_t r = start;
  2177. r != ((end + 1) & (term->grid->num_rows - 1));
  2178. r = (r + 1) & (term->grid->num_rows - 1))
  2179. {
  2180. const struct row *row = term->grid->rows[r];
  2181. assert(row != NULL);
  2182. for (int c = 0; c < term->cols; c++)
  2183. if (!extract_one(term, row, &row->cells[c], c, ctx))
  2184. goto out;
  2185. }
  2186. out:
  2187. return extract_finish(ctx, text, len);
  2188. }
  2189. bool
  2190. term_scrollback_to_text(const struct terminal *term, char **text, size_t *len)
  2191. {
  2192. int start = term->grid->offset + term->rows;
  2193. int end = term->grid->offset + term->rows - 1;
  2194. /* If scrollback isn't full yet, this may be NULL, so scan forward
  2195. * until we find the first non-NULL row */
  2196. while (term->grid->rows[start] == NULL) {
  2197. start++;
  2198. start &= term->grid->num_rows - 1;
  2199. }
  2200. if (end < 0)
  2201. end += term->grid->num_rows;
  2202. while (term->grid->rows[end] == NULL) {
  2203. end--;
  2204. if (end < 0)
  2205. end += term->grid->num_rows;
  2206. }
  2207. return rows_to_text(term, start, end, text, len);
  2208. }
  2209. bool
  2210. term_view_to_text(const struct terminal *term, char **text, size_t *len)
  2211. {
  2212. int start = grid_row_absolute_in_view(term->grid, 0);
  2213. int end = grid_row_absolute_in_view(term->grid, term->rows - 1);
  2214. return rows_to_text(term, start, end, text, len);
  2215. }