terminal.c 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2013-2020 K. Lange
  5. *
  6. * Terminal Emulator
  7. *
  8. * Graphical terminal emulator.
  9. *
  10. * Provides a number of features:
  11. * - Windowed and full screen modes
  12. * - Antialiased fonts
  13. * - Built-in fallback bitmap font
  14. * - ANSI escape support
  15. * - 256 colors
  16. */
  17. #include <stdio.h>
  18. #include <stdint.h>
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #include <signal.h>
  22. #include <time.h>
  23. #include <unistd.h>
  24. #include <fcntl.h>
  25. #include <getopt.h>
  26. #include <errno.h>
  27. #include <pty.h>
  28. #include <wchar.h>
  29. #include <dlfcn.h>
  30. #include <pthread.h>
  31. #include <sys/stat.h>
  32. #include <sys/ioctl.h>
  33. #include <sys/wait.h>
  34. #include <sys/time.h>
  35. #include <sys/fswait.h>
  36. #define TRACE_APP_NAME "terminal"
  37. #include <toaru/trace.h>
  38. #include <toaru/decodeutf8.h>
  39. #include <toaru/yutani.h>
  40. #include <toaru/decorations.h>
  41. #include <toaru/graphics.h>
  42. #include <toaru/kbd.h>
  43. #include <toaru/termemu.h>
  44. #include <toaru/spinlock.h>
  45. #include <toaru/list.h>
  46. #include <toaru/menu.h>
  47. #include <toaru/text.h>
  48. /* 16- and 256-color palette */
  49. #include "terminal-palette.h"
  50. /* Bitmap font */
  51. #include "terminal-font.h"
  52. /* Show help text */
  53. static void usage(char * argv[]) {
  54. printf(
  55. "Terminal Emulator\n"
  56. "\n"
  57. "usage: %s [-Fbxn] [-s SCALE] [-g WIDTHxHEIGHT] [COMMAND...]\n"
  58. "\n"
  59. " -F --fullscreen \033[3mRun in fullscreen (background) mode.\033[0m\n"
  60. " -b --bitmap \033[3mUse the integrated bitmap font.\033[0m\n"
  61. " -s --scale \033[3mScale the font in antialiased mode by a given amount.\033[0m\n"
  62. " -h --help \033[3mShow this help message.\033[0m\n"
  63. " -x --grid \033[3mMake resizes round to nearest match for character cell size.\033[0m\n"
  64. " -n --no-frame \033[3mDisable decorations.\033[0m\n"
  65. " -g --geometry \033[3mSet requested terminal size WIDTHxHEIGHT\033[0m\n"
  66. "\n"
  67. " This terminal emulator provides basic support for VT220 escapes and\n"
  68. " XTerm extensions, including 256 color support and font effects.\n",
  69. argv[0]);
  70. }
  71. /* master and slave pty descriptors */
  72. static int fd_master, fd_slave;
  73. static FILE * terminal;
  74. static pid_t child_pid = 0;
  75. static int scale_fonts = 0; /* Whether fonts should be scaled */
  76. static float font_scaling = 1.0; /* How much they should be scaled by */
  77. static uint16_t term_width = 0; /* Width of the terminal (in cells) */
  78. static uint16_t term_height = 0; /* Height of the terminal (in cells) */
  79. static uint16_t font_size = 16; /* Font size according to tt library */
  80. static uint16_t char_width = 8; /* Width of a cell in pixels */
  81. static uint16_t char_height = 17; /* Height of a cell in pixels */
  82. static uint16_t char_offset = 0; /* Offset of the font within the cell */
  83. static int csr_x = 0; /* Cursor X */
  84. static int csr_y = 0; /* Cursor Y */
  85. static uint32_t current_fg = 7; /* Current foreground color */
  86. static uint32_t current_bg = 0; /* Current background color */
  87. static term_cell_t * term_buffer = NULL; /* The terminal cell buffer */
  88. static term_cell_t * term_buffer_a = NULL;
  89. static term_cell_t * term_buffer_b = NULL;
  90. static term_state_t * ansi_state = NULL; /* ANSI parser library state */
  91. static int active_buffer = 0;
  92. static int _orig_x = 0;
  93. static int _orig_y = 0;
  94. static uint32_t _orig_fg = 7;
  95. static uint32_t _orig_bg = 0;
  96. static bool cursor_on = 1; /* Whether or not the cursor should be rendered */
  97. static bool _fullscreen = 0; /* Whether or not we are running in fullscreen mode (GUI only) */
  98. static bool _no_frame = 0; /* Whether to disable decorations or not */
  99. static bool _use_aa = 1; /* Whether or not to use best-available anti-aliased renderer */
  100. static bool _free_size = 1; /* Disable rounding when resized */
  101. static struct TT_Font * _tt_font_normal = NULL;
  102. static struct TT_Font * _tt_font_bold = NULL;
  103. static struct TT_Font * _tt_font_oblique = NULL;
  104. static struct TT_Font * _tt_font_bold_oblique = NULL;
  105. static struct TT_Font * _tt_font_fallback = NULL;
  106. static struct TT_Font * _tt_font_japanese = NULL;
  107. static list_t * images_list = NULL;
  108. static int menu_bar_height = 24;
  109. /* Text selection information */
  110. static int selection = 0;
  111. static int selection_start_x = 0;
  112. static int selection_start_y = 0;
  113. static int selection_end_x = 0;
  114. static int selection_end_y = 0;
  115. static char * selection_text = NULL;
  116. static int _selection_count = 0;
  117. static int _selection_i = 0;
  118. /* Mouse state */
  119. static int last_mouse_x = -1;
  120. static int last_mouse_y = -1;
  121. static int button_state = 0;
  122. static unsigned long long mouse_ticks = 0;
  123. static yutani_window_t * window = NULL; /* GUI window */
  124. static yutani_t * yctx = NULL;
  125. /* Window flip bounds */
  126. static int32_t l_x = INT32_MAX;
  127. static int32_t l_y = INT32_MAX;
  128. static int32_t r_x = -1;
  129. static int32_t r_y = -1;
  130. static uint32_t window_width = 640;
  131. static uint32_t window_height = 480;
  132. #define TERMINAL_TITLE_SIZE 512
  133. static char terminal_title[TERMINAL_TITLE_SIZE];
  134. static size_t terminal_title_length = 0;
  135. static gfx_context_t * ctx;
  136. static struct MenuList * menu_right_click = NULL;
  137. static void render_decors(void);
  138. static void term_clear();
  139. static void reinit(void);
  140. static void term_redraw_cursor();
  141. static int decor_left_width = 0;
  142. static int decor_top_height = 0;
  143. static int decor_right_width = 0;
  144. static int decor_bottom_height = 0;
  145. static int decor_width = 0;
  146. static int decor_height = 0;
  147. struct scrollback_row {
  148. unsigned short width;
  149. term_cell_t cells[];
  150. };
  151. #define MAX_SCROLLBACK 10240
  152. static list_t * scrollback_list = NULL;
  153. static int scrollback_offset = 0;
  154. /* Menu bar entries */
  155. struct menu_bar terminal_menu_bar = {0};
  156. struct menu_bar_entries terminal_menu_entries[] = {
  157. {"File", "file"},
  158. {"Edit", "edit"},
  159. {"View", "view"},
  160. {"Help", "help"},
  161. {NULL, NULL},
  162. };
  163. /* Trigger to exit the terminal when the child process dies or
  164. * we otherwise receive an exit signal */
  165. static volatile int exit_application = 0;
  166. static void cell_redraw(uint16_t x, uint16_t y);
  167. static void cell_redraw_inverted(uint16_t x, uint16_t y);
  168. static void cell_redraw_offset(uint16_t x, uint16_t y);
  169. static void cell_redraw_offset_inverted(uint16_t x, uint16_t y);
  170. static void update_bounds(void);
  171. static uint64_t get_ticks(void) {
  172. struct timeval now;
  173. gettimeofday(&now, NULL);
  174. return (uint64_t)now.tv_sec * 1000000LL + (uint64_t)now.tv_usec;
  175. }
  176. static void display_flip(void) {
  177. if (l_x != INT32_MAX && l_y != INT32_MAX) {
  178. flip(ctx);
  179. yutani_flip_region(yctx, window, l_x, l_y, r_x - l_x, r_y - l_y);
  180. l_x = INT32_MAX;
  181. l_y = INT32_MAX;
  182. r_x = -1;
  183. r_y = -1;
  184. }
  185. }
  186. /* Returns the lower of two shorts */
  187. static int32_t min(int32_t a, int32_t b) {
  188. return (a < b) ? a : b;
  189. }
  190. /* Returns the higher of two shorts */
  191. static int32_t max(int32_t a, int32_t b) {
  192. return (a > b) ? a : b;
  193. }
  194. /*
  195. * Convert codepoint to UTF-8
  196. *
  197. * Returns length of byte sequence written.
  198. */
  199. static int to_eight(uint32_t codepoint, char * out) {
  200. memset(out, 0x00, 7);
  201. if (codepoint < 0x0080) {
  202. out[0] = (char)codepoint;
  203. } else if (codepoint < 0x0800) {
  204. out[0] = 0xC0 | (codepoint >> 6);
  205. out[1] = 0x80 | (codepoint & 0x3F);
  206. } else if (codepoint < 0x10000) {
  207. out[0] = 0xE0 | (codepoint >> 12);
  208. out[1] = 0x80 | ((codepoint >> 6) & 0x3F);
  209. out[2] = 0x80 | (codepoint & 0x3F);
  210. } else if (codepoint < 0x200000) {
  211. out[0] = 0xF0 | (codepoint >> 18);
  212. out[1] = 0x80 | ((codepoint >> 12) & 0x3F);
  213. out[2] = 0x80 | ((codepoint >> 6) & 0x3F);
  214. out[3] = 0x80 | ((codepoint) & 0x3F);
  215. } else if (codepoint < 0x4000000) {
  216. out[0] = 0xF8 | (codepoint >> 24);
  217. out[1] = 0x80 | (codepoint >> 18);
  218. out[2] = 0x80 | ((codepoint >> 12) & 0x3F);
  219. out[3] = 0x80 | ((codepoint >> 6) & 0x3F);
  220. out[4] = 0x80 | ((codepoint) & 0x3F);
  221. } else {
  222. out[0] = 0xF8 | (codepoint >> 30);
  223. out[1] = 0x80 | ((codepoint >> 24) & 0x3F);
  224. out[2] = 0x80 | ((codepoint >> 18) & 0x3F);
  225. out[3] = 0x80 | ((codepoint >> 12) & 0x3F);
  226. out[4] = 0x80 | ((codepoint >> 6) & 0x3F);
  227. out[5] = 0x80 | ((codepoint) & 0x3F);
  228. }
  229. return strlen(out);
  230. }
  231. /* Set the terminal title string */
  232. static void set_title(char * c) {
  233. int len = min(TERMINAL_TITLE_SIZE, strlen(c)+1);
  234. memcpy(terminal_title, c, len);
  235. terminal_title[len-1] = '\0';
  236. terminal_title_length = len - 1;
  237. render_decors();
  238. }
  239. /* Call a function for each selected cell */
  240. static void iterate_selection(void (*func)(uint16_t x, uint16_t y)) {
  241. if (!selection) return;
  242. if (selection_end_y < selection_start_y) {
  243. for (int x = selection_end_x; x < term_width; ++x) {
  244. func(x, selection_end_y);
  245. }
  246. for (int y = selection_end_y + 1; y < selection_start_y; ++y) {
  247. for (int x = 0; x < term_width; ++x) {
  248. func(x, y);
  249. }
  250. }
  251. for (int x = 0; x <= selection_start_x; ++x) {
  252. func(x, selection_start_y);
  253. }
  254. } else if (selection_start_y == selection_end_y) {
  255. if (selection_start_x > selection_end_x) {
  256. for (int x = selection_end_x; x <= selection_start_x; ++x) {
  257. func(x, selection_start_y);
  258. }
  259. } else {
  260. for (int x = selection_start_x; x <= selection_end_x; ++x) {
  261. func(x, selection_start_y);
  262. }
  263. }
  264. } else {
  265. for (int x = selection_start_x; x < term_width; ++x) {
  266. func(x, selection_start_y);
  267. }
  268. for (int y = selection_start_y + 1; y < selection_end_y; ++y) {
  269. for (int x = 0; x < term_width; ++x) {
  270. func(x, y);
  271. }
  272. }
  273. for (int x = 0; x <= selection_end_x; ++x) {
  274. func(x, selection_end_y);
  275. }
  276. }
  277. }
  278. /* Redraw the selection with the selection hint (inversion) */
  279. static void redraw_selection(void) {
  280. iterate_selection(cell_redraw_offset_inverted);
  281. }
  282. static term_cell_t * cell_at(uint16_t x, uint16_t _y) {
  283. int y = _y;
  284. y -= scrollback_offset;
  285. if (y >= 0) {
  286. return (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  287. } else {
  288. node_t * node = scrollback_list->tail;
  289. for (; y < -1; y++) {
  290. if (!node) break;
  291. node = node->prev;
  292. }
  293. if (node) {
  294. struct scrollback_row * row = (struct scrollback_row *)node->value;
  295. if (row && x < row->width) {
  296. return &row->cells[x];
  297. }
  298. }
  299. }
  300. return NULL;
  301. }
  302. static void mark_cell(uint16_t x, uint16_t y) {
  303. term_cell_t * c = cell_at(x,y);
  304. if (c) {
  305. c->flags |= 0x200;
  306. }
  307. }
  308. static void mark_selection(void) {
  309. iterate_selection(mark_cell);
  310. }
  311. static void red_cell(uint16_t x, uint16_t y) {
  312. term_cell_t * c = cell_at(x,y);
  313. if (c) {
  314. if (c->flags & 0x200) {
  315. c->flags &= ~(0x200);
  316. } else {
  317. c->flags |= 0x400;
  318. }
  319. }
  320. }
  321. static void flip_selection(void) {
  322. iterate_selection(red_cell);
  323. for (int y = 0; y < term_height; ++y) {
  324. for (int x = 0; x < term_width; ++x) {
  325. term_cell_t * c = cell_at(x,y);
  326. if (c) {
  327. if (c->flags & 0x200) cell_redraw_offset(x,y);
  328. if (c->flags & 0x400) cell_redraw_offset_inverted(x,y);
  329. c->flags &= ~(0x600);
  330. }
  331. }
  332. }
  333. }
  334. /* Figure out how long the UTF-8 selection string should be. */
  335. static void count_selection(uint16_t x, uint16_t _y) {
  336. int y = _y;
  337. y -= scrollback_offset;
  338. if (y >= 0) {
  339. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  340. if (!(cell->flags & ANSI_EXT_IMG)) {
  341. if (((uint32_t *)cell)[0] != 0x00000000) {
  342. char tmp[7];
  343. _selection_count += to_eight(cell->c, tmp);
  344. }
  345. }
  346. } else {
  347. node_t * node = scrollback_list->tail;
  348. for (; y < -1; y++) {
  349. if (!node) break;
  350. node = node->prev;
  351. }
  352. if (node) {
  353. struct scrollback_row * row = (struct scrollback_row *)node->value;
  354. if (row && x < row->width) {
  355. term_cell_t * cell = &row->cells[x];
  356. if (cell && ((uint32_t *)cell)[0] != 0x00000000) {
  357. char tmp[7];
  358. _selection_count += to_eight(cell->c, tmp);
  359. }
  360. }
  361. }
  362. }
  363. if (x == term_width - 1) {
  364. _selection_count++;
  365. }
  366. }
  367. /* Fill the selection text buffer with the selected text. */
  368. void write_selection(uint16_t x, uint16_t _y) {
  369. int y = _y;
  370. y -= scrollback_offset;
  371. if (y >= 0) {
  372. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  373. if (!(cell->flags & ANSI_EXT_IMG)) {
  374. if (((uint32_t *)cell)[0] != 0x00000000 && cell->c != 0xFFFF) {
  375. char tmp[7];
  376. int count = to_eight(cell->c, tmp);
  377. for (int i = 0; i < count; ++i) {
  378. selection_text[_selection_i] = tmp[i];
  379. _selection_i++;
  380. }
  381. }
  382. }
  383. } else {
  384. node_t * node = scrollback_list->tail;
  385. for (; y < -1; y++) {
  386. if (!node) break;
  387. node = node->prev;
  388. }
  389. if (node) {
  390. struct scrollback_row * row = (struct scrollback_row *)node->value;
  391. if (row && x < row->width) {
  392. term_cell_t * cell = &row->cells[x];
  393. if (cell && ((uint32_t *)cell)[0] != 0x00000000 && cell->c != 0xFFFF) {
  394. char tmp[7];
  395. int count = to_eight(cell->c, tmp);
  396. for (int i = 0; i < count; ++i) {
  397. selection_text[_selection_i] = tmp[i];
  398. _selection_i++;
  399. }
  400. }
  401. }
  402. }
  403. }
  404. if (x == term_width - 1) {
  405. selection_text[_selection_i] = '\n';;
  406. _selection_i++;
  407. }
  408. }
  409. /* Copy the selection text to the clipboard. */
  410. static char * copy_selection(void) {
  411. _selection_count = 0;
  412. iterate_selection(count_selection);
  413. if (selection_text) {
  414. free(selection_text);
  415. }
  416. if (_selection_count == 0) {
  417. return NULL;
  418. }
  419. selection_text = malloc(_selection_count + 1);
  420. selection_text[_selection_count] = '\0';
  421. _selection_i = 0;
  422. iterate_selection(write_selection);
  423. if (selection_text[_selection_count-1] == '\n') {
  424. /* Don't end on a line feed */
  425. selection_text[_selection_count-1] = '\0';
  426. }
  427. yutani_set_clipboard(yctx, selection_text);
  428. return selection_text;
  429. }
  430. static volatile int input_buffer_lock = 0;
  431. static int input_buffer_semaphore[2];
  432. static list_t * input_buffer_queue = NULL;
  433. struct input_data {
  434. size_t len;
  435. char data[];
  436. };
  437. void * handle_input_writing(void * unused) {
  438. (void)unused;
  439. while (1) {
  440. /* Read one byte from semaphore; as long as semaphore has data,
  441. * there is another input blob to write to the TTY */
  442. char tmp[1];
  443. int c = read(input_buffer_semaphore[0],tmp,1);
  444. if (c > 0) {
  445. /* Retrieve blob */
  446. spin_lock(&input_buffer_lock);
  447. node_t * blob = list_dequeue(input_buffer_queue);
  448. spin_unlock(&input_buffer_lock);
  449. /* No blobs? This shouldn't happen, but just in case, just continue */
  450. if (!blob) {
  451. continue;
  452. }
  453. /* Write blob data to the tty */
  454. struct input_data * value = blob->value;
  455. write(fd_master, value->data, value->len);
  456. free(blob->value);
  457. free(blob);
  458. } else {
  459. /* The pipe has closed, terminal is exiting */
  460. break;
  461. }
  462. }
  463. return NULL;
  464. }
  465. static void write_input_buffer(char * data, size_t len) {
  466. struct input_data * d = malloc(sizeof(struct input_data) + len);
  467. d->len = len;
  468. memcpy(&d->data, data, len);
  469. spin_lock(&input_buffer_lock);
  470. list_insert(input_buffer_queue, d);
  471. spin_unlock(&input_buffer_lock);
  472. write(input_buffer_semaphore[1], d, 1);
  473. }
  474. /* Stuffs a string into the stdin of the terminal's child process
  475. * Useful for things like the ANSI DSR command. */
  476. static void input_buffer_stuff(char * str) {
  477. size_t len = strlen(str);
  478. write_input_buffer(str, len);
  479. }
  480. /* Redraw the decorations */
  481. static void render_decors(void) {
  482. /* Don't draw decorations or bother advertising the window if in "fullscreen mode" */
  483. if (_fullscreen) return;
  484. if (!_no_frame) {
  485. /* Draw the decorations */
  486. render_decorations(window, ctx, terminal_title_length ? terminal_title : "Terminal");
  487. /* Update menu bar position and size */
  488. terminal_menu_bar.x = decor_left_width;
  489. terminal_menu_bar.y = decor_top_height;
  490. terminal_menu_bar.width = window_width;
  491. terminal_menu_bar.window = window;
  492. /* Redraw the menu bar */
  493. menu_bar_render(&terminal_menu_bar, ctx);
  494. }
  495. /* Advertise the window icon to the panel. */
  496. yutani_window_advertise_icon(yctx, window, terminal_title_length ? terminal_title : "Terminal", "utilities-terminal");
  497. /*
  498. * Flip the whole window
  499. * We do this regardless of whether we drew decorations to catch
  500. * a case where decorations are toggled.
  501. */
  502. l_x = 0; l_y = 0;
  503. r_x = window->width;
  504. r_y = window->height;
  505. display_flip();
  506. }
  507. /* Set a pixel in the terminal cell area */
  508. static inline void term_set_point(uint16_t x, uint16_t y, uint32_t color ) {
  509. GFX(ctx, (x+decor_left_width),(y+decor_top_height+menu_bar_height)) = color;
  510. }
  511. /* Draw a partial block character. */
  512. static void draw_semi_block(int c, int x, int y, uint32_t fg, uint32_t bg) {
  513. bg = premultiply(bg);
  514. fg = premultiply(fg);
  515. if (c == 0x2580) {
  516. for (uint8_t i = 0; i < char_height / 2; ++i) {
  517. for (uint8_t j = 0; j < char_width; ++j) {
  518. term_set_point(x+j,y+i,fg);
  519. }
  520. }
  521. } else if (c >= 0x2589) {
  522. c -= 0x2588;
  523. int width = char_width - ((c * char_width) / 8);
  524. for (uint8_t i = 0; i < char_height; ++i) {
  525. for (uint8_t j = 0; j < width; ++j) {
  526. term_set_point(x+j, y+i, fg);
  527. }
  528. }
  529. } else {
  530. c -= 0x2580;
  531. int height = char_height - ((c * char_height) / 8);
  532. for (uint8_t i = height; i < char_height; ++i) {
  533. for (uint8_t j = 0; j < char_width; ++j) {
  534. term_set_point(x+j, y+i,fg);
  535. }
  536. }
  537. }
  538. }
  539. #include "apps/ununicode.h"
  540. /* Write a character to the window. */
  541. static void term_write_char(uint32_t val, uint16_t x, uint16_t y, uint32_t fg, uint32_t bg, uint8_t flags) {
  542. uint32_t _fg, _bg;
  543. /* Select foreground color from palette. */
  544. if (fg < PALETTE_COLORS) {
  545. _fg = term_colors[fg];
  546. _fg |= 0xFF << 24;
  547. } else {
  548. _fg = fg;
  549. }
  550. /* Select background color from aplette. */
  551. if (bg < PALETTE_COLORS) {
  552. _bg = term_colors[bg];
  553. if (flags & ANSI_SPECBG) {
  554. _bg |= 0xFF << 24;
  555. } else {
  556. _bg |= TERM_DEFAULT_OPAC << 24;
  557. }
  558. } else {
  559. _bg = bg;
  560. }
  561. if (_fullscreen) {
  562. _bg |= 0xFF << 24;
  563. }
  564. /* Draw block characters */
  565. if (val >= 0x2580 && val <= 0x258F) {
  566. uint32_t pbg = premultiply(_bg);
  567. for (uint8_t i = 0; i < char_height; ++i) {
  568. for (uint8_t j = 0; j < char_width; ++j) {
  569. term_set_point(x+j,y+i,pbg);
  570. }
  571. }
  572. draw_semi_block(val, x, y, _fg, _bg);
  573. goto _extra_stuff;
  574. }
  575. /* Draw glyphs */
  576. if (_use_aa) {
  577. if (val == 0xFFFF) return;
  578. for (uint8_t i = 0; i < char_height; ++i) {
  579. for (uint8_t j = 0; j < char_width; ++j) {
  580. term_set_point(x+j,y+i,_bg);
  581. }
  582. }
  583. if (flags & ANSI_WIDE) {
  584. for (uint8_t i = 0; i < char_height; ++i) {
  585. for (uint8_t j = char_width; j < 2 * char_width; ++j) {
  586. term_set_point(x+j,y+i,_bg);
  587. }
  588. }
  589. }
  590. if (val < 32 || val == ' ') {
  591. goto _extra_stuff;
  592. }
  593. struct TT_Font * _font = _tt_font_normal;
  594. if (flags & ANSI_BOLD && flags & ANSI_ITALIC) {
  595. _font = _tt_font_bold_oblique;
  596. } else if (flags & ANSI_BOLD) {
  597. _font = _tt_font_bold;
  598. } else if (flags & ANSI_ITALIC) {
  599. _font = _tt_font_oblique;
  600. }
  601. unsigned int glyph = tt_glyph_for_codepoint(_font, val);
  602. /* Try the regular sans serif font as a fallback */
  603. if (!glyph) {
  604. int nglyph = tt_glyph_for_codepoint(_tt_font_fallback, val);
  605. if (nglyph) {
  606. _font = _tt_font_fallback;
  607. glyph = nglyph;
  608. }
  609. }
  610. /* Try the VL Gothic, if it's installed and this is a reasonably high codepoint */
  611. if (!glyph && _tt_font_japanese && val >= 0x2E80) {
  612. int nglyph = tt_glyph_for_codepoint(_tt_font_japanese, val);
  613. if (nglyph) {
  614. _font = _tt_font_japanese;
  615. glyph = nglyph;
  616. }
  617. }
  618. tt_set_size(_font, font_size);
  619. int _x = x + decor_left_width;
  620. int _y = y + char_offset + decor_top_height + menu_bar_height;
  621. tt_draw_glyph(ctx, _font, _x, _y, glyph, _fg);
  622. } else {
  623. /* Convert other unicode characters. */
  624. if (val > 128) {
  625. val = ununicode(val);
  626. }
  627. /* Draw using the bitmap font. */
  628. uint16_t * c = large_font[val];
  629. for (uint8_t i = 0; i < char_height; ++i) {
  630. for (uint8_t j = 0; j < char_width; ++j) {
  631. if (c[i] & (1 << (LARGE_FONT_MASK-j))) {
  632. term_set_point(x+j,y+i,_fg);
  633. } else {
  634. term_set_point(x+j,y+i,_bg);
  635. }
  636. }
  637. }
  638. }
  639. /* Draw additional text elements, like underlines and cross-outs. */
  640. _extra_stuff:
  641. if (flags & ANSI_UNDERLINE) {
  642. for (uint8_t i = 0; i < char_width; ++i) {
  643. term_set_point(x + i, y + char_height - 1, _fg);
  644. }
  645. }
  646. if (flags & ANSI_CROSS) {
  647. for (uint8_t i = 0; i < char_width; ++i) {
  648. term_set_point(x + i, y + char_height - 7, _fg);
  649. }
  650. }
  651. if (flags & ANSI_BORDER) {
  652. for (uint8_t i = 0; i < char_height; ++i) {
  653. term_set_point(x , y + i, _fg);
  654. term_set_point(x + (char_width - 1), y + i, _fg);
  655. }
  656. for (uint8_t j = 0; j < char_width; ++j) {
  657. term_set_point(x + j, y, _fg);
  658. term_set_point(x + j, y + (char_height - 1), _fg);
  659. }
  660. }
  661. /* Calculate the bounds of the updated region of the window */
  662. l_x = min(l_x, decor_left_width + x);
  663. l_y = min(l_y, decor_top_height+menu_bar_height + y);
  664. if (flags & ANSI_WIDE) {
  665. r_x = max(r_x, decor_left_width + x + char_width * 2);
  666. r_y = max(r_y, decor_top_height+menu_bar_height + y + char_height * 2);
  667. } else {
  668. r_x = max(r_x, decor_left_width + x + char_width);
  669. r_y = max(r_y, decor_top_height+menu_bar_height + y + char_height);
  670. }
  671. }
  672. /* Set a terminal cell */
  673. static void cell_set(uint16_t x, uint16_t y, uint32_t c, uint32_t fg, uint32_t bg, uint32_t flags) {
  674. /* Avoid setting cells out of range. */
  675. if (x >= term_width || y >= term_height) return;
  676. /* Calculate the cell position in the terminal buffer */
  677. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  678. /* Set cell attributes */
  679. cell->c = c;
  680. cell->fg = fg;
  681. cell->bg = bg;
  682. cell->flags = flags;
  683. }
  684. /* Redraw an embedded image cell */
  685. static void redraw_cell_image(uint16_t x, uint16_t y, term_cell_t * cell) {
  686. /* Avoid setting cells out of range. */
  687. if (x >= term_width || y >= term_height) return;
  688. /* Draw the image data */
  689. uint32_t * data = (uint32_t *)((uintptr_t)cell->bg << 32 | cell->fg);
  690. for (uint32_t yy = 0; yy < char_height; ++yy) {
  691. for (uint32_t xx = 0; xx < char_width; ++xx) {
  692. term_set_point(x * char_width + xx, y * char_height + yy, *data);
  693. data++;
  694. }
  695. }
  696. /* Update bounds */
  697. l_x = min(l_x, decor_left_width + x * char_width);
  698. l_y = min(l_y, decor_top_height+menu_bar_height + y * char_height);
  699. r_x = max(r_x, decor_left_width + x * char_width + char_width);
  700. r_y = max(r_y, decor_top_height+menu_bar_height + y * char_height + char_height);
  701. }
  702. static void cell_redraw_offset(uint16_t x, uint16_t _y) {
  703. int y = _y;
  704. int i = y;
  705. y -= scrollback_offset;
  706. if (y >= 0) {
  707. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  708. if (cell->flags & ANSI_EXT_IMG) { redraw_cell_image(x,i,cell); return; }
  709. if (((uint32_t *)cell)[0] == 0x00000000) {
  710. term_write_char(' ', x * char_width, i * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  711. } else {
  712. term_write_char(cell->c, x * char_width, i * char_height, cell->fg, cell->bg, cell->flags);
  713. }
  714. } else {
  715. node_t * node = scrollback_list->tail;
  716. for (; y < -1; y++) {
  717. if (!node) break;
  718. node = node->prev;
  719. }
  720. if (node) {
  721. struct scrollback_row * row = (struct scrollback_row *)node->value;
  722. if (row && x < row->width) {
  723. term_cell_t * cell = &row->cells[x];
  724. if (!cell || ((uint32_t *)cell)[0] == 0x00000000) {
  725. term_write_char(' ', x * char_width, i * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  726. } else {
  727. term_write_char(cell->c, x * char_width, i * char_height, cell->fg, cell->bg, cell->flags);
  728. }
  729. } else {
  730. term_write_char(' ', x * char_width, i * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  731. }
  732. }
  733. }
  734. }
  735. static void cell_redraw_offset_inverted(uint16_t x, uint16_t _y) {
  736. int y = _y;
  737. int i = y;
  738. y -= scrollback_offset;
  739. if (y >= 0) {
  740. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  741. if (cell->flags & ANSI_EXT_IMG) { redraw_cell_image(x,i,cell); return; }
  742. if (((uint32_t *)cell)[0] == 0x00000000) {
  743. term_write_char(' ', x * char_width, i * char_height, TERM_DEFAULT_BG, TERM_DEFAULT_FG, TERM_DEFAULT_FLAGS|ANSI_SPECBG);
  744. } else {
  745. term_write_char(cell->c, x * char_width, i * char_height, cell->bg, cell->fg, cell->flags);
  746. }
  747. } else {
  748. node_t * node = scrollback_list->tail;
  749. for (; y < -1; y++) {
  750. if (!node) break;
  751. node = node->prev;
  752. }
  753. if (node) {
  754. struct scrollback_row * row = (struct scrollback_row *)node->value;
  755. if (row && x < row->width) {
  756. term_cell_t * cell = &row->cells[x];
  757. if (!cell || ((uint32_t *)cell)[0] == 0x00000000) {
  758. term_write_char(' ', x * char_width, i * char_height, TERM_DEFAULT_BG, TERM_DEFAULT_FG, TERM_DEFAULT_FLAGS);
  759. } else {
  760. term_write_char(cell->c, x * char_width, i * char_height, cell->bg, cell->fg, cell->flags);
  761. }
  762. } else {
  763. term_write_char(' ', x * char_width, i * char_height, TERM_DEFAULT_BG, TERM_DEFAULT_FG, TERM_DEFAULT_FLAGS);
  764. }
  765. }
  766. }
  767. }
  768. /* Redraw a text cell normally. */
  769. static void cell_redraw(uint16_t x, uint16_t y) {
  770. /* Avoid cells out of range. */
  771. if (x >= term_width || y >= term_height) return;
  772. /* Calculate the cell position in the terminal buffer */
  773. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  774. /* If it's an image cell, redraw the image data. */
  775. if (cell->flags & ANSI_EXT_IMG) {
  776. redraw_cell_image(x,y,cell);
  777. return;
  778. }
  779. /* Special case empty cells. */
  780. if (((uint32_t *)cell)[0] == 0x00000000) {
  781. term_write_char(' ', x * char_width, y * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  782. } else {
  783. term_write_char(cell->c, x * char_width, y * char_height, cell->fg, cell->bg, cell->flags);
  784. }
  785. }
  786. /* Redraw text cell inverted. */
  787. static void cell_redraw_inverted(uint16_t x, uint16_t y) {
  788. /* Avoid cells out of range. */
  789. if (x >= term_width || y >= term_height) return;
  790. /* Calculate the cell position in the terminal buffer */
  791. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  792. /* If it's an image cell, redraw the image data. */
  793. if (cell->flags & ANSI_EXT_IMG) {
  794. redraw_cell_image(x,y,cell);
  795. return;
  796. }
  797. /* Special case empty cells. */
  798. if (((uint32_t *)cell)[0] == 0x00000000) {
  799. term_write_char(' ', x * char_width, y * char_height, TERM_DEFAULT_BG, TERM_DEFAULT_FG, TERM_DEFAULT_FLAGS | ANSI_SPECBG);
  800. } else {
  801. term_write_char(cell->c, x * char_width, y * char_height, cell->bg, cell->fg, cell->flags | ANSI_SPECBG);
  802. }
  803. }
  804. /* Redraw text cell with a surrounding box (used by cursor) */
  805. static void cell_redraw_box(uint16_t x, uint16_t y) {
  806. /* Avoid cells out of range. */
  807. if (x >= term_width || y >= term_height) return;
  808. /* Calculate the cell position in the terminal buffer */
  809. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  810. /* If it's an image cell, redraw the image data. */
  811. if (cell->flags & ANSI_EXT_IMG) {
  812. redraw_cell_image(x,y,cell);
  813. return;
  814. }
  815. /* Special case empty cells. */
  816. if (((uint32_t *)cell)[0] == 0x00000000) {
  817. term_write_char(' ', x * char_width, y * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS | ANSI_BORDER);
  818. } else {
  819. term_write_char(cell->c, x * char_width, y * char_height, cell->fg, cell->bg, cell->flags | ANSI_BORDER);
  820. }
  821. }
  822. /* Draw the cursor cell */
  823. static void render_cursor() {
  824. if (!cursor_on) return;
  825. if (!window->focused) {
  826. /* An unfocused terminal should draw an unfilled box. */
  827. cell_redraw_box(csr_x, csr_y);
  828. } else {
  829. /* A focused terminal draws a solid box. */
  830. cell_redraw_inverted(csr_x, csr_y);
  831. }
  832. }
  833. static uint8_t cursor_flipped = 0;
  834. /* A soft request to draw the cursor. */
  835. static void draw_cursor() {
  836. if (!cursor_on) return;
  837. mouse_ticks = get_ticks();
  838. cursor_flipped = 0;
  839. render_cursor();
  840. }
  841. /* Timer callback to flip (flash) the cursor */
  842. static void maybe_flip_cursor(void) {
  843. uint64_t ticks = get_ticks();
  844. if (ticks > mouse_ticks + 600000LL) {
  845. mouse_ticks = ticks;
  846. if (scrollback_offset != 0) {
  847. return; /* Don't flip cursor while drawing scrollback */
  848. }
  849. if (window->focused && cursor_flipped) {
  850. cell_redraw(csr_x, csr_y);
  851. } else {
  852. render_cursor();
  853. }
  854. display_flip();
  855. cursor_flipped = 1 - cursor_flipped;
  856. }
  857. }
  858. /* Draw all cells. Duplicates code from cell_redraw to avoid unecessary bounds checks. */
  859. static void term_redraw_all() {
  860. for (int i = 0; i < term_height; i++) {
  861. for (int x = 0; x < term_width; ++x) {
  862. /* Calculate the cell position in the terminal buffer */
  863. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (i * term_width + x) * sizeof(term_cell_t));
  864. /* If it's an image cell, redraw the image data. */
  865. if (cell->flags & ANSI_EXT_IMG) {
  866. redraw_cell_image(x,i,cell);
  867. continue;
  868. }
  869. /* Special case empty cells. */
  870. if (((uint32_t *)cell)[0] == 0x00000000) {
  871. term_write_char(' ', x * char_width, i * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  872. } else {
  873. term_write_char(cell->c, x * char_width, i * char_height, cell->fg, cell->bg, cell->flags);
  874. }
  875. }
  876. }
  877. }
  878. static void _menu_action_redraw(struct MenuEntry * self) {
  879. term_redraw_all();
  880. }
  881. /* Remove no-longer-visible image cell data. */
  882. static void flush_unused_images(void) {
  883. list_t * tmp = list_create();
  884. for (int y = 0; y < term_height; ++y) {
  885. for (int x = 0; x < term_width; ++x) {
  886. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  887. if (cell->flags & ANSI_EXT_IMG) {
  888. uint32_t * data = (uint32_t *)((uintptr_t)cell->bg << 32 | cell->fg);
  889. list_insert(tmp, data);
  890. }
  891. }
  892. }
  893. foreach(node, images_list) {
  894. if (!list_find(tmp, node->value)) {
  895. free(node->value);
  896. }
  897. }
  898. list_free(images_list);
  899. images_list = tmp;
  900. }
  901. static void term_shift_region(int top, int height, int how_much) {
  902. if (how_much == 0) return;
  903. int destination, source;
  904. int count, new_top, new_bottom;
  905. if (how_much > height) {
  906. count = 0;
  907. new_top = top;
  908. new_bottom = top + height;
  909. } else if (how_much > 0) {
  910. destination = term_width * top;
  911. source = term_width * (top + how_much);
  912. count = height - how_much;
  913. new_top = top + height - how_much;
  914. new_bottom = top + height;
  915. } else if (how_much < 0) {
  916. destination = term_width * (top - how_much);
  917. source = term_width * top;
  918. count = height + how_much;
  919. new_top = top;
  920. new_bottom = top - how_much;
  921. }
  922. /* Move from top+how_much to top */
  923. if (count) {
  924. memmove(term_buffer + destination, term_buffer + source, count * term_width * sizeof(term_cell_t));
  925. /* Move displayed as well */
  926. cell_redraw(csr_x, csr_y); /* Otherwise we may copy the inverted cursor */
  927. uintptr_t dst = (uintptr_t)ctx->backbuffer + GFX_W(ctx) * (destination / term_width * char_height) * GFX_B(ctx);
  928. uintptr_t src = (uintptr_t)ctx->backbuffer + GFX_W(ctx) * (source / term_width * char_height) * GFX_B(ctx);
  929. dst += (GFX_W(ctx) * (decor_top_height + menu_bar_height) + decor_left_width) * GFX_B(ctx);
  930. src += (GFX_W(ctx) * (decor_top_height + menu_bar_height) + decor_left_width) * GFX_B(ctx);
  931. if (dst < src) {
  932. for (int i = 0; i < count * char_height; ++i) {
  933. memmove((void*)(dst + i * GFX_W(ctx) * GFX_B(ctx)), (void*)(src + i * GFX_W(ctx) * GFX_B(ctx)), term_width * char_width * GFX_B(ctx));
  934. }
  935. } else {
  936. for (int i = (count - 1) * char_height; i >= 0; --i) {
  937. memmove((void*)(dst + i * GFX_W(ctx) * GFX_B(ctx)), (void*)(src + i * GFX_W(ctx) * GFX_B(ctx)), term_width * char_width * GFX_B(ctx));
  938. }
  939. }
  940. }
  941. l_x = 0; l_y = 0;
  942. r_x = window->width;
  943. r_y = window->height;
  944. /* Clear new lines at bottom */
  945. for (int i = new_top; i < new_bottom; ++i) {
  946. for (uint16_t x = 0; x < term_width; ++x) {
  947. cell_set(x, i, ' ', current_fg, current_bg, ansi_state->flags);
  948. cell_redraw(x, i);
  949. }
  950. }
  951. }
  952. /* Scroll the terminal up or down. */
  953. static void term_scroll(int how_much) {
  954. term_shift_region(0, term_height, how_much);
  955. /* Remove image data for image cells that are no longer on screen. */
  956. flush_unused_images();
  957. /* Flip the entire window. */
  958. yutani_flip(yctx, window);
  959. }
  960. static void insert_delete_lines(int how_many) {
  961. if (how_many == 0) return;
  962. if (how_many > 0) {
  963. /* Insert lines is equivalent to scrolling from the current line */
  964. term_shift_region(csr_y,term_height-csr_y,-how_many);
  965. } else {
  966. term_shift_region(csr_y,term_height-csr_y,-how_many);
  967. }
  968. }
  969. /* Is this a wide character? (does wcwidth == 2) */
  970. static int is_wide(uint32_t codepoint) {
  971. if (codepoint < 256) return 0;
  972. return wcwidth(codepoint) == 2;
  973. }
  974. /* Save the row that is about to be scrolled offscreen into the scrollback buffer. */
  975. static void save_scrollback(void) {
  976. /* If the scrollback is already full, remove the oldest element. */
  977. if (scrollback_list->length == MAX_SCROLLBACK) {
  978. node_t * n = list_dequeue(scrollback_list);
  979. free(n->value);
  980. free(n);
  981. }
  982. struct scrollback_row * row = malloc(sizeof(struct scrollback_row) + sizeof(term_cell_t) * term_width + 20);
  983. row->width = term_width;
  984. for (int i = 0; i < term_width; ++i) {
  985. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (i) * sizeof(term_cell_t));
  986. memcpy(&row->cells[i], cell, sizeof(term_cell_t));
  987. }
  988. list_insert(scrollback_list, row);
  989. }
  990. /* Draw the scrollback. */
  991. static void redraw_scrollback(void) {
  992. if (!scrollback_offset) {
  993. term_redraw_all();
  994. display_flip();
  995. return;
  996. }
  997. if (scrollback_offset < term_height) {
  998. for (int i = scrollback_offset; i < term_height; i++) {
  999. int y = i - scrollback_offset;
  1000. for (int x = 0; x < term_width; ++x) {
  1001. term_cell_t * cell = (term_cell_t *)((uintptr_t)term_buffer + (y * term_width + x) * sizeof(term_cell_t));
  1002. if (cell->flags & ANSI_EXT_IMG) { redraw_cell_image(x,i,cell); continue; }
  1003. if (((uint32_t *)cell)[0] == 0x00000000) {
  1004. term_write_char(' ', x * char_width, i * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  1005. } else {
  1006. term_write_char(cell->c, x * char_width, i * char_height, cell->fg, cell->bg, cell->flags);
  1007. }
  1008. }
  1009. }
  1010. node_t * node = scrollback_list->tail;
  1011. for (int i = 0; i < scrollback_offset; ++i) {
  1012. struct scrollback_row * row = (struct scrollback_row *)node->value;
  1013. int y = scrollback_offset - 1 - i;
  1014. int width = row->width;
  1015. if (width > term_width) {
  1016. width = term_width;
  1017. } else {
  1018. for (int x = row->width; x < term_width; ++x) {
  1019. term_write_char(' ', x * char_width, y * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  1020. }
  1021. }
  1022. for (int x = 0; x < width; ++x) {
  1023. term_cell_t * cell = &row->cells[x];
  1024. if (((uint32_t *)cell)[0] == 0x00000000) {
  1025. term_write_char(' ', x * char_width, y * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  1026. } else {
  1027. term_write_char(cell->c, x * char_width, y * char_height, cell->fg, cell->bg, cell->flags);
  1028. }
  1029. }
  1030. node = node->prev;
  1031. }
  1032. } else {
  1033. node_t * node = scrollback_list->tail;
  1034. for (int i = 0; i < scrollback_offset - term_height; ++i) {
  1035. node = node->prev;
  1036. }
  1037. for (int i = scrollback_offset - term_height; i < scrollback_offset; ++i) {
  1038. struct scrollback_row * row = (struct scrollback_row *)node->value;
  1039. int y = scrollback_offset - 1 - i;
  1040. int width = row->width;
  1041. if (width > term_width) {
  1042. width = term_width;
  1043. } else {
  1044. for (int x = row->width; x < term_width; ++x) {
  1045. term_write_char(' ', x * char_width, y * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  1046. }
  1047. }
  1048. for (int x = 0; x < width; ++x) {
  1049. term_cell_t * cell = &row->cells[x];
  1050. if (((uint32_t *)cell)[0] == 0x00000000) {
  1051. term_write_char(' ', x * char_width, y * char_height, TERM_DEFAULT_FG, TERM_DEFAULT_BG, TERM_DEFAULT_FLAGS);
  1052. } else {
  1053. term_write_char(cell->c, x * char_width, y * char_height, cell->fg, cell->bg, cell->flags);
  1054. }
  1055. }
  1056. node = node->prev;
  1057. }
  1058. }
  1059. display_flip();
  1060. }
  1061. /*
  1062. * ANSI callback for writing characters.
  1063. * Parses some things (\n\r, etc.) itself that should probably
  1064. * be moved into the ANSI library.
  1065. */
  1066. static void term_write(char c) {
  1067. static uint32_t unicode_state = 0;
  1068. static uint32_t codepoint = 0;
  1069. cell_redraw(csr_x, csr_y);
  1070. if (!decode(&unicode_state, &codepoint, (uint8_t)c)) {
  1071. uint32_t o = codepoint;
  1072. codepoint = 0;
  1073. if (c == '\r') {
  1074. csr_x = 0;
  1075. draw_cursor();
  1076. return;
  1077. }
  1078. if (csr_x < 0) csr_x = 0;
  1079. if (csr_y < 0) csr_y = 0;
  1080. if (csr_x == term_width) {
  1081. csr_x = 0;
  1082. ++csr_y;
  1083. if (c == '\n') return;
  1084. }
  1085. if (csr_y == term_height) {
  1086. save_scrollback();
  1087. term_scroll(1);
  1088. csr_y = term_height - 1;
  1089. }
  1090. if (c == '\n') {
  1091. ++csr_y;
  1092. if (csr_y == term_height) {
  1093. save_scrollback();
  1094. term_scroll(1);
  1095. csr_y = term_height - 1;
  1096. }
  1097. draw_cursor();
  1098. } else if (c == '\007') {
  1099. /* bell */
  1100. /* XXX play sound */
  1101. } else if (c == '\b') {
  1102. if (csr_x > 0) {
  1103. --csr_x;
  1104. }
  1105. cell_redraw(csr_x, csr_y);
  1106. draw_cursor();
  1107. } else if (c == '\t') {
  1108. csr_x += (8 - csr_x % 8);
  1109. draw_cursor();
  1110. } else {
  1111. int wide = is_wide(o);
  1112. uint8_t flags = ansi_state->flags;
  1113. if (wide && csr_x == term_width - 1) {
  1114. csr_x = 0;
  1115. ++csr_y;
  1116. }
  1117. if (wide) {
  1118. flags = flags | ANSI_WIDE;
  1119. }
  1120. cell_set(csr_x,csr_y, o, current_fg, current_bg, flags);
  1121. cell_redraw(csr_x,csr_y);
  1122. csr_x++;
  1123. if (wide && csr_x != term_width) {
  1124. cell_set(csr_x, csr_y, 0xFFFF, current_fg, current_bg, ansi_state->flags);
  1125. cell_redraw(csr_x,csr_y);
  1126. cell_redraw(csr_x-1,csr_y);
  1127. csr_x++;
  1128. }
  1129. }
  1130. } else if (unicode_state == UTF8_REJECT) {
  1131. unicode_state = 0;
  1132. codepoint = 0;
  1133. }
  1134. draw_cursor();
  1135. }
  1136. /* ANSI callback to set cursor position */
  1137. static void term_set_csr(int x, int y) {
  1138. cell_redraw(csr_x,csr_y);
  1139. csr_x = x;
  1140. csr_y = y;
  1141. draw_cursor();
  1142. }
  1143. /* ANSI callback to get cursor x position */
  1144. static int term_get_csr_x(void) {
  1145. return csr_x;
  1146. }
  1147. /* ANSI callback to get cursor y position */
  1148. static int term_get_csr_y(void) {
  1149. return csr_y;
  1150. }
  1151. /* ANSI callback to set cell image data. */
  1152. static void term_set_cell_contents(int x, int y, char * data) {
  1153. char * cell_data = malloc(char_width * char_height * sizeof(uint32_t));
  1154. memcpy(cell_data, data, char_width * char_height * sizeof(uint32_t));
  1155. list_insert(images_list, cell_data);
  1156. cell_set(x, y, ' ',
  1157. (uintptr_t)(cell_data) & 0xFFFFFFFF,
  1158. (uintptr_t)(cell_data) >> 32,
  1159. ANSI_EXT_IMG);
  1160. }
  1161. /* ANSI callback to get character cell width */
  1162. static int term_get_cell_width(void) {
  1163. return char_width;
  1164. }
  1165. /* ANSI callback to get character cell height */
  1166. static int term_get_cell_height(void) {
  1167. return char_height;
  1168. }
  1169. /* ANSI callback to set cursor visibility */
  1170. static void term_set_csr_show(int on) {
  1171. cursor_on = on;
  1172. if (on) {
  1173. draw_cursor();
  1174. }
  1175. }
  1176. /* ANSI callback to set the foreground/background colors. */
  1177. static void term_set_colors(uint32_t fg, uint32_t bg) {
  1178. current_fg = fg;
  1179. current_bg = bg;
  1180. }
  1181. /* ANSI callback to force the cursor to draw */
  1182. static void term_redraw_cursor() {
  1183. if (term_buffer) {
  1184. draw_cursor();
  1185. }
  1186. }
  1187. /* ANSI callback to set a cell to a codepoint (only ever used to set spaces) */
  1188. static void term_set_cell(int x, int y, uint32_t c) {
  1189. cell_set(x, y, c, current_fg, current_bg, ansi_state->flags);
  1190. cell_redraw(x, y);
  1191. }
  1192. /* ANSI callback to clear the terminal. */
  1193. static void term_clear(int i) {
  1194. if (i == 2) {
  1195. /* Clear all */
  1196. csr_x = 0;
  1197. csr_y = 0;
  1198. memset((void *)term_buffer, 0x00, term_width * term_height * sizeof(term_cell_t));
  1199. if (!_no_frame) {
  1200. render_decors();
  1201. }
  1202. term_redraw_all();
  1203. } else if (i == 0) {
  1204. /* Clear after cursor */
  1205. for (int x = csr_x; x < term_width; ++x) {
  1206. term_set_cell(x, csr_y, ' ');
  1207. }
  1208. for (int y = csr_y + 1; y < term_height; ++y) {
  1209. for (int x = 0; x < term_width; ++x) {
  1210. term_set_cell(x, y, ' ');
  1211. }
  1212. }
  1213. } else if (i == 1) {
  1214. /* Clear before cursor */
  1215. for (int y = 0; y < csr_y; ++y) {
  1216. for (int x = 0; x < term_width; ++x) {
  1217. term_set_cell(x, y, ' ');
  1218. }
  1219. }
  1220. for (int x = 0; x < csr_x; ++x) {
  1221. term_set_cell(x, csr_y, ' ');
  1222. }
  1223. }
  1224. flush_unused_images();
  1225. }
  1226. #define SWAP(T,a,b) do { T _a = a; a = b; b = _a; } while(0);
  1227. static void term_switch_buffer(int buffer) {
  1228. if (buffer != 0 && buffer != 1) return;
  1229. if (buffer != active_buffer) {
  1230. active_buffer = buffer;
  1231. term_buffer = active_buffer == 0 ? term_buffer_a : term_buffer_b;
  1232. SWAP(int, csr_x, _orig_x);
  1233. SWAP(int, csr_y, _orig_y);
  1234. SWAP(uint32_t, current_fg, _orig_fg);
  1235. SWAP(uint32_t, current_bg, _orig_bg);
  1236. term_redraw_all();
  1237. display_flip();
  1238. }
  1239. }
  1240. /* ANSI callbacks */
  1241. term_callbacks_t term_callbacks = {
  1242. term_write,
  1243. term_set_colors,
  1244. term_set_csr,
  1245. term_get_csr_x,
  1246. term_get_csr_y,
  1247. term_set_cell,
  1248. term_clear,
  1249. term_scroll,
  1250. term_redraw_cursor,
  1251. input_buffer_stuff,
  1252. set_title,
  1253. term_set_cell_contents,
  1254. term_get_cell_width,
  1255. term_get_cell_height,
  1256. term_set_csr_show,
  1257. term_switch_buffer,
  1258. insert_delete_lines,
  1259. };
  1260. static void handle_input(char c) {
  1261. write_input_buffer(&c, 1);
  1262. display_flip();
  1263. if (scrollback_offset != 0) {
  1264. scrollback_offset = 0;
  1265. term_redraw_all();
  1266. }
  1267. }
  1268. static void handle_input_s(char * c) {
  1269. size_t len = strlen(c);
  1270. write_input_buffer(c, len);
  1271. display_flip();
  1272. if (scrollback_offset != 0) {
  1273. scrollback_offset = 0;
  1274. term_redraw_all();
  1275. }
  1276. }
  1277. /* Scroll the view up (scrollback) */
  1278. static void scroll_up(int amount) {
  1279. int i = 0;
  1280. while (i < amount && scrollback_list && scrollback_offset < (int)scrollback_list->length) {
  1281. scrollback_offset ++;
  1282. i++;
  1283. }
  1284. redraw_scrollback();
  1285. }
  1286. /* Scroll the view down (scrollback) */
  1287. void scroll_down(int amount) {
  1288. int i = 0;
  1289. while (i < amount && scrollback_list && scrollback_offset != 0) {
  1290. scrollback_offset -= 1;
  1291. i++;
  1292. }
  1293. redraw_scrollback();
  1294. }
  1295. /* Handle a key press from Yutani */
  1296. static void key_event(int ret, key_event_t * event) {
  1297. if (ret) {
  1298. /* Ctrl-Shift-C - Copy selection */
  1299. if ((event->modifiers & KEY_MOD_LEFT_SHIFT || event->modifiers & KEY_MOD_RIGHT_SHIFT) &&
  1300. (event->modifiers & KEY_MOD_LEFT_CTRL || event->modifiers & KEY_MOD_RIGHT_CTRL) &&
  1301. (event->keycode == 'c')) {
  1302. if (selection) {
  1303. /* Copy selection */
  1304. copy_selection();
  1305. }
  1306. return;
  1307. }
  1308. /* Ctrl-Shift-V - Paste selection */
  1309. if ((event->modifiers & KEY_MOD_LEFT_SHIFT || event->modifiers & KEY_MOD_RIGHT_SHIFT) &&
  1310. (event->modifiers & KEY_MOD_LEFT_CTRL || event->modifiers & KEY_MOD_RIGHT_CTRL) &&
  1311. (event->keycode == 'v')) {
  1312. /* Paste selection */
  1313. yutani_special_request(yctx, NULL, YUTANI_SPECIAL_REQUEST_CLIPBOARD);
  1314. return;
  1315. }
  1316. if ((event->modifiers & KEY_MOD_LEFT_CTRL || event->modifiers & KEY_MOD_RIGHT_CTRL) &&
  1317. (event->keycode == '0')) {
  1318. scale_fonts = 0;
  1319. font_scaling = 1.0;
  1320. reinit();
  1321. return;
  1322. }
  1323. if ((event->modifiers & KEY_MOD_LEFT_SHIFT || event->modifiers & KEY_MOD_RIGHT_SHIFT) &&
  1324. (event->modifiers & KEY_MOD_LEFT_CTRL || event->modifiers & KEY_MOD_RIGHT_CTRL) &&
  1325. (event->keycode == '=')) {
  1326. scale_fonts = 1;
  1327. font_scaling = font_scaling * 1.2;
  1328. reinit();
  1329. return;
  1330. }
  1331. if ((event->modifiers & KEY_MOD_LEFT_CTRL || event->modifiers & KEY_MOD_RIGHT_CTRL) &&
  1332. (event->keycode == '-')) {
  1333. scale_fonts = 1;
  1334. font_scaling = font_scaling * 0.8333333;
  1335. reinit();
  1336. return;
  1337. }
  1338. /* Left alt */
  1339. if (event->modifiers & KEY_MOD_LEFT_ALT || event->modifiers & KEY_MOD_RIGHT_ALT) {
  1340. handle_input('\033');
  1341. }
  1342. /* Shift-Tab */
  1343. if ((event->modifiers & KEY_MOD_LEFT_SHIFT || event->modifiers & KEY_MOD_RIGHT_SHIFT) &&
  1344. event->key == '\t') {
  1345. handle_input_s("\033[Z");
  1346. return;
  1347. }
  1348. /* ENTER = reads as linefeed, should be carriage return */
  1349. if (event->keycode == 10) {
  1350. handle_input('\r');
  1351. return;
  1352. }
  1353. /* BACKSPACE = reads as ^H, should be ^? */
  1354. if (event->keycode == 8) {
  1355. handle_input(0x7F);
  1356. return;
  1357. }
  1358. /* Pass key value to PTY */
  1359. handle_input(event->key);
  1360. } else {
  1361. /* Special keys without ->key values */
  1362. /* Only trigger on key down */
  1363. if (event->action == KEY_ACTION_UP) return;
  1364. switch (event->keycode) {
  1365. case KEY_F1:
  1366. handle_input_s("\033OP");
  1367. break;
  1368. case KEY_F2:
  1369. handle_input_s("\033OQ");
  1370. break;
  1371. case KEY_F3:
  1372. handle_input_s("\033OR");
  1373. break;
  1374. case KEY_F4:
  1375. handle_input_s("\033OS");
  1376. break;
  1377. case KEY_F5:
  1378. handle_input_s("\033[15~");
  1379. break;
  1380. case KEY_F6:
  1381. handle_input_s("\033[17~");
  1382. break;
  1383. case KEY_F7:
  1384. handle_input_s("\033[18~");
  1385. break;
  1386. case KEY_F8:
  1387. handle_input_s("\033[19~");
  1388. break;
  1389. case KEY_F9:
  1390. handle_input_s("\033[20~");
  1391. break;
  1392. case KEY_F10:
  1393. handle_input_s("\033[21~");
  1394. break;
  1395. case KEY_F11:
  1396. handle_input_s("\033[23~");
  1397. break;
  1398. case KEY_F12:
  1399. /* Toggle decorations */
  1400. if (!_fullscreen) {
  1401. _no_frame = !_no_frame;
  1402. update_bounds();
  1403. window_width = window->width - decor_width;
  1404. window_height = window->height - (decor_height + menu_bar_height);
  1405. reinit();
  1406. }
  1407. break;
  1408. case KEY_ARROW_UP:
  1409. if (event->modifiers & KEY_MOD_LEFT_SHIFT && event->modifiers & KEY_MOD_LEFT_CTRL) {
  1410. handle_input_s("\033[6A");
  1411. } else if (event->modifiers & KEY_MOD_LEFT_CTRL) {
  1412. handle_input_s("\033[5A");
  1413. } else if (event->modifiers & KEY_MOD_LEFT_SHIFT && event->modifiers & KEY_MOD_LEFT_ALT) {
  1414. handle_input_s("\033[4A");
  1415. } else if (event->modifiers & KEY_MOD_LEFT_ALT) {
  1416. handle_input_s("\033[3A");
  1417. } else if (event->modifiers & KEY_MOD_LEFT_SHIFT) {
  1418. handle_input_s("\033[2A");
  1419. } else {
  1420. handle_input_s("\033[A");
  1421. }
  1422. break;
  1423. case KEY_ARROW_DOWN:
  1424. if (event->modifiers & KEY_MOD_LEFT_SHIFT && event->modifiers & KEY_MOD_LEFT_CTRL) {
  1425. handle_input_s("\033[6B");
  1426. } else if (event->modifiers & KEY_MOD_LEFT_CTRL) {
  1427. handle_input_s("\033[5B");
  1428. } else if (event->modifiers & KEY_MOD_LEFT_SHIFT && event->modifiers & KEY_MOD_LEFT_ALT) {
  1429. handle_input_s("\033[4B");
  1430. } else if (event->modifiers & KEY_MOD_LEFT_ALT) {
  1431. handle_input_s("\033[3B");
  1432. } else if (event->modifiers & KEY_MOD_LEFT_SHIFT) {
  1433. handle_input_s("\033[2B");
  1434. } else {
  1435. handle_input_s("\033[B");
  1436. }
  1437. break;
  1438. case KEY_ARROW_RIGHT:
  1439. if (event->modifiers & KEY_MOD_LEFT_SHIFT && event->modifiers & KEY_MOD_LEFT_CTRL) {
  1440. handle_input_s("\033[6C");
  1441. } else if (event->modifiers & KEY_MOD_LEFT_CTRL) {
  1442. handle_input_s("\033[5C");
  1443. } else if (event->modifiers & KEY_MOD_LEFT_SHIFT && event->modifiers & KEY_MOD_LEFT_ALT) {
  1444. handle_input_s("\033[4C");
  1445. } else if (event->modifiers & KEY_MOD_LEFT_ALT) {
  1446. handle_input_s("\033[3C");
  1447. } else if (event->modifiers & KEY_MOD_LEFT_SHIFT) {
  1448. handle_input_s("\033[2C");
  1449. } else {
  1450. handle_input_s("\033[C");
  1451. }
  1452. break;
  1453. case KEY_ARROW_LEFT:
  1454. if (event->modifiers & KEY_MOD_LEFT_SHIFT && event->modifiers & KEY_MOD_LEFT_CTRL) {
  1455. handle_input_s("\033[6D");
  1456. } else if (event->modifiers & KEY_MOD_LEFT_CTRL) {
  1457. handle_input_s("\033[5D");
  1458. } else if (event->modifiers & KEY_MOD_LEFT_SHIFT && event->modifiers & KEY_MOD_LEFT_ALT) {
  1459. handle_input_s("\033[4D");
  1460. } else if (event->modifiers & KEY_MOD_LEFT_ALT) {
  1461. handle_input_s("\033[3D");
  1462. } else if (event->modifiers & KEY_MOD_LEFT_SHIFT) {
  1463. handle_input_s("\033[2D");
  1464. } else {
  1465. handle_input_s("\033[D");
  1466. }
  1467. break;
  1468. case KEY_PAGE_UP:
  1469. if (event->modifiers & KEY_MOD_LEFT_SHIFT) {
  1470. scroll_up(term_height/2);
  1471. } else {
  1472. handle_input_s("\033[5~");
  1473. }
  1474. break;
  1475. case KEY_PAGE_DOWN:
  1476. if (event->modifiers & KEY_MOD_LEFT_SHIFT) {
  1477. scroll_down(term_height/2);
  1478. } else {
  1479. handle_input_s("\033[6~");
  1480. }
  1481. break;
  1482. case KEY_HOME:
  1483. if (event->modifiers & KEY_MOD_LEFT_SHIFT) {
  1484. if (scrollback_list) {
  1485. scrollback_offset = scrollback_list->length;
  1486. redraw_scrollback();
  1487. }
  1488. } else {
  1489. handle_input_s("\033[H");
  1490. }
  1491. break;
  1492. case KEY_END:
  1493. if (event->modifiers & KEY_MOD_LEFT_SHIFT) {
  1494. scrollback_offset = 0;
  1495. redraw_scrollback();
  1496. } else {
  1497. handle_input_s("\033[F");
  1498. }
  1499. break;
  1500. case KEY_DEL:
  1501. handle_input_s("\033[3~");
  1502. break;
  1503. case KEY_INSERT:
  1504. handle_input_s("\033[2~");
  1505. break;
  1506. }
  1507. }
  1508. }
  1509. /* Check if the Terminal should close. */
  1510. static void check_for_exit(void) {
  1511. /* If something has set exit_application, we should exit. */
  1512. if (exit_application) return;
  1513. pid_t pid = waitpid(-1, NULL, WNOHANG);
  1514. /* If the child has exited, we should exit. */
  1515. if (pid != child_pid) return;
  1516. /* Clean up */
  1517. exit_application = 1;
  1518. /* Write [Process terminated] */
  1519. char exit_message[] = "[Process terminated]\n";
  1520. write(fd_slave, exit_message, sizeof(exit_message));
  1521. close(input_buffer_semaphore[1]);
  1522. }
  1523. static term_cell_t * copy_terminal(int old_width, int old_height, term_cell_t * term_buffer) {
  1524. term_cell_t * new_term_buffer = malloc(sizeof(term_cell_t) * term_width * term_height);
  1525. memset(new_term_buffer, 0x0, sizeof(term_cell_t) * term_width * term_height);
  1526. int offset = 0;
  1527. if (term_height < old_height) {
  1528. while (csr_y >= term_height) {
  1529. offset++;
  1530. old_height--;
  1531. csr_y--;
  1532. }
  1533. }
  1534. for (int row = 0; row < min(old_height, term_height); ++row) {
  1535. for (int col = 0; col < min(old_width, term_width); ++col) {
  1536. term_cell_t * old_cell = (term_cell_t *)((uintptr_t)term_buffer + ((row + offset) * old_width + col) * sizeof(term_cell_t));
  1537. term_cell_t * new_cell = (term_cell_t *)((uintptr_t)new_term_buffer + (row * term_width + col) * sizeof(term_cell_t));
  1538. *new_cell = *old_cell;
  1539. }
  1540. }
  1541. if (csr_x >= term_width) {
  1542. csr_x = term_width-1;
  1543. }
  1544. return new_term_buffer;
  1545. }
  1546. /* Reinitialize the terminal after a resize. */
  1547. static void reinit(void) {
  1548. /* Figure out character sizes if fonts have changed. */
  1549. if (_use_aa) {
  1550. char_width = 8;
  1551. char_height = 17;
  1552. font_size = 13;
  1553. char_offset = 13;
  1554. if (scale_fonts) {
  1555. font_size *= font_scaling;
  1556. char_height *= font_scaling;
  1557. char_width *= font_scaling;
  1558. char_offset *= font_scaling;
  1559. }
  1560. } else {
  1561. char_width = LARGE_FONT_CELL_WIDTH;
  1562. char_height = LARGE_FONT_CELL_HEIGHT;
  1563. }
  1564. int old_width = term_width;
  1565. int old_height = term_height;
  1566. /* Resize the terminal buffer */
  1567. term_width = window_width / char_width;
  1568. term_height = window_height / char_height;
  1569. if (term_buffer) {
  1570. term_cell_t * new_a = copy_terminal(old_width, old_height, term_buffer_a);
  1571. term_cell_t * new_b = copy_terminal(old_width, old_height, term_buffer_b);
  1572. free(term_buffer_a);
  1573. term_buffer_a = new_a;
  1574. free(term_buffer_b);
  1575. term_buffer_b = new_b;
  1576. if (active_buffer == 0) {
  1577. term_buffer = new_a;
  1578. } else {
  1579. term_buffer = new_b;
  1580. }
  1581. } else {
  1582. term_buffer_a = malloc(sizeof(term_cell_t) * term_width * term_height);
  1583. memset(term_buffer_a, 0x0, sizeof(term_cell_t) * term_width * term_height);
  1584. term_buffer_b = malloc(sizeof(term_cell_t) * term_width * term_height);
  1585. memset(term_buffer_b, 0x0, sizeof(term_cell_t) * term_width * term_height);
  1586. term_buffer = term_buffer_a;
  1587. }
  1588. /* Reset the ANSI library, ensuring we keep certain values */
  1589. int old_mouse_state = 0;
  1590. if (ansi_state) old_mouse_state = ansi_state->mouse_on;
  1591. ansi_state = ansi_init(ansi_state, term_width, term_height, &term_callbacks);
  1592. ansi_state->mouse_on = old_mouse_state;
  1593. /* Redraw the window */
  1594. draw_fill(ctx, rgba(0,0,0, TERM_DEFAULT_OPAC));
  1595. render_decors();
  1596. term_redraw_all();
  1597. display_flip();
  1598. /* Send window size change ioctl */
  1599. struct winsize w;
  1600. w.ws_row = term_height;
  1601. w.ws_col = term_width;
  1602. w.ws_xpixel = term_width * char_width;
  1603. w.ws_ypixel = term_height * char_height;
  1604. ioctl(fd_master, TIOCSWINSZ, &w);
  1605. }
  1606. static void update_bounds(void) {
  1607. if (!_no_frame) {
  1608. struct decor_bounds bounds;
  1609. decor_get_bounds(window, &bounds);
  1610. decor_left_width = bounds.left_width;
  1611. decor_top_height = bounds.top_height;
  1612. decor_right_width = bounds.right_width;
  1613. decor_bottom_height = bounds.bottom_height;
  1614. decor_width = bounds.width;
  1615. decor_height = bounds.height;
  1616. menu_bar_height = 24;
  1617. } else {
  1618. decor_left_width = 0;
  1619. decor_top_height = 0;
  1620. decor_right_width = 0;
  1621. decor_bottom_height = 0;
  1622. decor_width = 0;
  1623. decor_height = 0;
  1624. menu_bar_height = 0;
  1625. }
  1626. }
  1627. /* Handle window resize event. */
  1628. static void resize_finish(int width, int height) {
  1629. static int resize_attempts = 0;
  1630. /* Calculate window size */
  1631. update_bounds();
  1632. int extra_x = decor_width;
  1633. int extra_y = decor_height + menu_bar_height;
  1634. int t_window_width = width - extra_x;
  1635. int t_window_height = height - extra_y;
  1636. /* Prevent the terminal from becoming too small. */
  1637. if (t_window_width < char_width * 20 || t_window_height < char_height * 10) {
  1638. resize_attempts++;
  1639. int n_width = extra_x + max(char_width * 20, t_window_width);
  1640. int n_height = extra_y + max(char_height * 10, t_window_height);
  1641. yutani_window_resize_offer(yctx, window, n_width, n_height);
  1642. return;
  1643. }
  1644. /* If requested, ensure the terminal resizes to a fixed size based on the cell size. */
  1645. if (!_free_size && ((t_window_width % char_width != 0 || t_window_height % char_height != 0) && resize_attempts < 3)) {
  1646. resize_attempts++;
  1647. int n_width = extra_x + t_window_width - (t_window_width % char_width);
  1648. int n_height = extra_y + t_window_height - (t_window_height % char_height);
  1649. yutani_window_resize_offer(yctx, window, n_width, n_height);
  1650. return;
  1651. }
  1652. resize_attempts = 0;
  1653. /* Accept new window size */
  1654. yutani_window_resize_accept(yctx, window, width, height);
  1655. window_width = window->width - extra_x;
  1656. window_height = window->height - extra_y;
  1657. /* Reinitialize the graphics library */
  1658. reinit_graphics_yutani(ctx, window);
  1659. /* Reinitialize the terminal buffer and ANSI library */
  1660. reinit();
  1661. /* We are done resizing. */
  1662. yutani_window_resize_done(yctx, window);
  1663. yutani_flip(yctx, window);
  1664. }
  1665. /* Insert a mouse event sequence into the PTY */
  1666. static void mouse_event(int button, int x, int y) {
  1667. if (ansi_state->mouse_on & TERMEMU_MOUSE_SGR) {
  1668. char buf[100];
  1669. sprintf(buf,"\033[<%d;%d;%d%c", button == 3 ? 0 : button, x+1, y+1, button == 3 ? 'm' : 'M');
  1670. handle_input_s(buf);
  1671. } else {
  1672. char buf[7];
  1673. sprintf(buf, "\033[M%c%c%c", button + 32, x + 33, y + 33);
  1674. handle_input_s(buf);
  1675. }
  1676. }
  1677. /* Handle Yutani messages */
  1678. static void * handle_incoming(void) {
  1679. static uint64_t last_click = 0;
  1680. yutani_msg_t * m = yutani_poll(yctx);
  1681. while (m) {
  1682. if (menu_process_event(yctx, m)) {
  1683. render_decors();
  1684. }
  1685. switch (m->type) {
  1686. case YUTANI_MSG_KEY_EVENT:
  1687. {
  1688. struct yutani_msg_key_event * ke = (void*)m->data;
  1689. int ret = (ke->event.action == KEY_ACTION_DOWN) && (ke->event.key);
  1690. key_event(ret, &ke->event);
  1691. }
  1692. break;
  1693. case YUTANI_MSG_WINDOW_FOCUS_CHANGE:
  1694. {
  1695. struct yutani_msg_window_focus_change * wf = (void*)m->data;
  1696. yutani_window_t * win = hashmap_get(yctx->windows, (void*)(uintptr_t)wf->wid);
  1697. if (win == window) {
  1698. win->focused = wf->focused;
  1699. render_decors();
  1700. }
  1701. }
  1702. break;
  1703. case YUTANI_MSG_WINDOW_CLOSE:
  1704. {
  1705. struct yutani_msg_window_close * wc = (void*)m->data;
  1706. if (wc->wid == window->wid) {
  1707. kill(child_pid, SIGKILL);
  1708. exit_application = 1;
  1709. }
  1710. }
  1711. break;
  1712. case YUTANI_MSG_SESSION_END:
  1713. {
  1714. kill(child_pid, SIGKILL);
  1715. exit_application = 1;
  1716. }
  1717. break;
  1718. case YUTANI_MSG_RESIZE_OFFER:
  1719. {
  1720. struct yutani_msg_window_resize * wr = (void*)m->data;
  1721. resize_finish(wr->width, wr->height);
  1722. }
  1723. break;
  1724. case YUTANI_MSG_CLIPBOARD:
  1725. {
  1726. struct yutani_msg_clipboard * cb = (void *)m->data;
  1727. if (selection_text) {
  1728. free(selection_text);
  1729. }
  1730. if (*cb->content == '\002') {
  1731. int size = atoi(&cb->content[2]);
  1732. FILE * clipboard = yutani_open_clipboard(yctx);
  1733. selection_text = malloc(size + 1);
  1734. fread(selection_text, 1, size, clipboard);
  1735. selection_text[size] = '\0';
  1736. fclose(clipboard);
  1737. } else {
  1738. selection_text = malloc(cb->size+1);
  1739. memcpy(selection_text, cb->content, cb->size);
  1740. selection_text[cb->size] = '\0';
  1741. }
  1742. if (ansi_state->paste_mode) {
  1743. handle_input_s("\033[200~");
  1744. handle_input_s(selection_text);
  1745. handle_input_s("\033[201~");
  1746. } else {
  1747. handle_input_s(selection_text);
  1748. }
  1749. }
  1750. break;
  1751. case YUTANI_MSG_WINDOW_MOUSE_EVENT:
  1752. {
  1753. struct yutani_msg_window_mouse_event * me = (void*)m->data;
  1754. if (me->wid != window->wid) break;
  1755. if (!_no_frame) {
  1756. int decor_response = decor_handle_event(yctx, m);
  1757. switch (decor_response) {
  1758. case DECOR_CLOSE:
  1759. kill(child_pid, SIGKILL);
  1760. exit_application = 1;
  1761. break;
  1762. case DECOR_RIGHT:
  1763. /* right click in decoration, show appropriate menu */
  1764. decor_show_default_menu(window, window->x + me->new_x, window->y + me->new_y);
  1765. break;
  1766. default:
  1767. break;
  1768. }
  1769. menu_bar_mouse_event(yctx, window, &terminal_menu_bar, me, me->new_x, me->new_y);
  1770. }
  1771. if (me->new_x < 0 || me->new_y < 0 ||
  1772. (!_no_frame && (me->new_x >= (int)window_width + (int)decor_width ||
  1773. me->new_y < (int)decor_top_height+menu_bar_height ||
  1774. me->new_y >= (int)(window_height + decor_top_height+menu_bar_height) ||
  1775. me->new_x < (int)decor_left_width ||
  1776. me->new_x >= (int)(window_width + decor_left_width))) ||
  1777. (_no_frame && (me->new_x >= (int)window_width || me->new_y >= (int)window_height))) {
  1778. if (window->mouse_state == YUTANI_CURSOR_TYPE_IBEAM) {
  1779. yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_RESET);
  1780. }
  1781. break;
  1782. }
  1783. if (!(ansi_state->mouse_on & TERMEMU_MOUSE_ENABLE)) {
  1784. if (window->mouse_state == YUTANI_CURSOR_TYPE_RESET) {
  1785. yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_IBEAM);
  1786. }
  1787. } else {
  1788. if (window->mouse_state == YUTANI_CURSOR_TYPE_IBEAM) {
  1789. yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_RESET);
  1790. }
  1791. }
  1792. int new_x = me->new_x;
  1793. int new_y = me->new_y;
  1794. if (!_no_frame) {
  1795. new_x -= decor_left_width;
  1796. new_y -= decor_top_height+menu_bar_height;
  1797. }
  1798. /* Convert from coordinate to cell positon */
  1799. new_x /= char_width;
  1800. new_y /= char_height;
  1801. if (new_x < 0 || new_y < 0) break;
  1802. if (new_x >= term_width || new_y >= term_height) break;
  1803. /* Map Cursor Action */
  1804. if ((ansi_state->mouse_on & TERMEMU_MOUSE_ENABLE) && !(me->modifiers & YUTANI_KEY_MODIFIER_SHIFT)) {
  1805. if (me->buttons & YUTANI_MOUSE_SCROLL_UP) {
  1806. mouse_event(32+32, new_x, new_y);
  1807. } else if (me->buttons & YUTANI_MOUSE_SCROLL_DOWN) {
  1808. mouse_event(32+32+1, new_x, new_y);
  1809. }
  1810. if (me->buttons != button_state) {
  1811. /* Figure out what changed */
  1812. if (me->buttons & YUTANI_MOUSE_BUTTON_LEFT &&
  1813. !(button_state & YUTANI_MOUSE_BUTTON_LEFT))
  1814. mouse_event(0, new_x, new_y);
  1815. if (me->buttons & YUTANI_MOUSE_BUTTON_MIDDLE &&
  1816. !(button_state & YUTANI_MOUSE_BUTTON_MIDDLE))
  1817. mouse_event(1, new_x, new_y);
  1818. if (me->buttons & YUTANI_MOUSE_BUTTON_RIGHT &&
  1819. !(button_state & YUTANI_MOUSE_BUTTON_RIGHT))
  1820. mouse_event(2, new_x, new_y);
  1821. if (!(me->buttons & YUTANI_MOUSE_BUTTON_LEFT) &&
  1822. button_state & YUTANI_MOUSE_BUTTON_LEFT)
  1823. mouse_event(3, new_x, new_y);
  1824. if (!(me->buttons & YUTANI_MOUSE_BUTTON_MIDDLE) &&
  1825. button_state & YUTANI_MOUSE_BUTTON_MIDDLE)
  1826. mouse_event(3, new_x, new_y);
  1827. if (!(me->buttons & YUTANI_MOUSE_BUTTON_RIGHT) &&
  1828. button_state & YUTANI_MOUSE_BUTTON_RIGHT)
  1829. mouse_event(3, new_x, new_y);
  1830. last_mouse_x = new_x;
  1831. last_mouse_y = new_y;
  1832. button_state = me->buttons;
  1833. } else if (ansi_state->mouse_on & TERMEMU_MOUSE_DRAG) {
  1834. /* Report motion for pressed buttons */
  1835. if (last_mouse_x == new_x && last_mouse_y == new_y) break;
  1836. if (button_state & YUTANI_MOUSE_BUTTON_LEFT) mouse_event(32, new_x, new_y);
  1837. if (button_state & YUTANI_MOUSE_BUTTON_MIDDLE) mouse_event(33, new_x, new_y);
  1838. if (button_state & YUTANI_MOUSE_BUTTON_RIGHT) mouse_event(34, new_x, new_y);
  1839. last_mouse_x = new_x;
  1840. last_mouse_y = new_y;
  1841. }
  1842. } else {
  1843. if (me->command == YUTANI_MOUSE_EVENT_DOWN && me->buttons & YUTANI_MOUSE_BUTTON_LEFT) {
  1844. redraw_scrollback();
  1845. uint64_t now = get_ticks();
  1846. if (now - last_click < 500000UL && (new_x == selection_start_x && new_y == selection_start_y)) {
  1847. /* Double click */
  1848. while (selection_start_x > 0) {
  1849. term_cell_t * c = cell_at(selection_start_x-1, selection_start_y);
  1850. if (!c || c->c == ' ' || !c->c) break;
  1851. selection_start_x--;
  1852. }
  1853. while (selection_end_x < term_width - 1) {
  1854. term_cell_t * c = cell_at(selection_end_x+1, selection_end_y);
  1855. if (!c || c->c == ' ' || !c->c) break;
  1856. selection_end_x++;
  1857. }
  1858. selection = 1;
  1859. } else {
  1860. last_click = get_ticks();
  1861. selection_start_x = new_x;
  1862. selection_start_y = new_y;
  1863. selection_end_x = new_x;
  1864. selection_end_y = new_y;
  1865. selection = 0;
  1866. }
  1867. redraw_selection();
  1868. display_flip();
  1869. }
  1870. if (me->command == YUTANI_MOUSE_EVENT_DRAG && me->buttons & YUTANI_MOUSE_BUTTON_LEFT ){
  1871. mark_selection();
  1872. selection_end_x = new_x;
  1873. selection_end_y = new_y;
  1874. selection = 1;
  1875. flip_selection();
  1876. display_flip();
  1877. }
  1878. if (me->command == YUTANI_MOUSE_EVENT_RAISE) {
  1879. if (me->new_x == me->old_x && me->new_y == me->old_y) {
  1880. selection = 0;
  1881. term_redraw_all();
  1882. redraw_scrollback();
  1883. display_flip();
  1884. } /* else selection */
  1885. }
  1886. if (me->buttons & YUTANI_MOUSE_SCROLL_UP) {
  1887. scroll_up(5);
  1888. } else if (me->buttons & YUTANI_MOUSE_SCROLL_DOWN) {
  1889. scroll_down(5);
  1890. } else if (me->buttons & YUTANI_MOUSE_BUTTON_RIGHT) {
  1891. if (!menu_right_click->window) {
  1892. menu_prepare(menu_right_click, yctx);
  1893. if (menu_right_click->window) {
  1894. if (window->x + me->new_x + menu_right_click->window->width > yctx->display_width) {
  1895. yutani_window_move(yctx, menu_right_click->window, window->x + me->new_x - menu_right_click->window->width, window->y + me->new_y);
  1896. } else {
  1897. yutani_window_move(yctx, menu_right_click->window, window->x + me->new_x, window->y + me->new_y);
  1898. }
  1899. yutani_flip(yctx, menu_right_click->window);
  1900. }
  1901. }
  1902. }
  1903. }
  1904. }
  1905. break;
  1906. default:
  1907. break;
  1908. }
  1909. free(m);
  1910. m = yutani_poll_async(yctx);
  1911. }
  1912. return NULL;
  1913. }
  1914. /*
  1915. * Menu Actions
  1916. */
  1917. /* File > Exit */
  1918. static void _menu_action_exit(struct MenuEntry * self) {
  1919. kill(child_pid, SIGKILL);
  1920. exit_application = 1;
  1921. }
  1922. /* We need to track these so we can retitle both of them */
  1923. static struct MenuEntry * _menu_toggle_borders_context = NULL;
  1924. static struct MenuEntry * _menu_toggle_borders_bar = NULL;
  1925. static void _menu_action_hide_borders(struct MenuEntry * self) {
  1926. _no_frame = !(_no_frame);
  1927. update_bounds();
  1928. window_width = window->width - decor_width;
  1929. window_height = window->height - (decor_height + menu_bar_height);
  1930. menu_update_icon(_menu_toggle_borders_context, _no_frame ? NULL : "check");
  1931. menu_update_icon(_menu_toggle_borders_bar, _no_frame ? NULL : "check");
  1932. reinit();
  1933. }
  1934. static struct MenuEntry * _menu_toggle_bitmap_context = NULL;
  1935. static struct MenuEntry * _menu_toggle_bitmap_bar = NULL;
  1936. static void _menu_action_toggle_tt(struct MenuEntry * self) {
  1937. _use_aa = !(_use_aa);
  1938. menu_update_icon(_menu_toggle_bitmap_context, _use_aa ? NULL : "check");
  1939. menu_update_icon(_menu_toggle_bitmap_bar, _use_aa ? NULL : "check");
  1940. reinit();
  1941. }
  1942. static void _menu_action_toggle_free_size(struct MenuEntry * self) {
  1943. _free_size = !(_free_size);
  1944. menu_update_icon(self, _free_size ? NULL : "check");
  1945. }
  1946. static void _menu_action_show_about(struct MenuEntry * self) {
  1947. char about_cmd[1024] = "\0";
  1948. strcat(about_cmd, "about \"About Terminal\" /usr/share/icons/48/utilities-terminal.png \"ToaruOS Terminal\" \"© 2013-2021 K. Lange\n-\nPart of ToaruOS, which is free software\nreleased under the NCSA/University of Illinois\nlicense.\n-\n%https://toaruos.org\n%https://github.com/klange/toaruos\" ");
  1949. char coords[100];
  1950. sprintf(coords, "%d %d &", (int)window->x + (int)window->width / 2, (int)window->y + (int)window->height / 2);
  1951. strcat(about_cmd, coords);
  1952. system(about_cmd);
  1953. render_decors();
  1954. }
  1955. static void _menu_action_show_help(struct MenuEntry * self) {
  1956. system("help-browser terminal.trt &");
  1957. render_decors();
  1958. }
  1959. static void _menu_action_copy(struct MenuEntry * self) {
  1960. copy_selection();
  1961. }
  1962. static void _menu_action_paste(struct MenuEntry * self) {
  1963. yutani_special_request(yctx, NULL, YUTANI_SPECIAL_REQUEST_CLIPBOARD);
  1964. }
  1965. static void _menu_action_set_scale(struct MenuEntry * self) {
  1966. struct MenuEntry_Normal * _self = (struct MenuEntry_Normal *)self;
  1967. if (!_self->action) {
  1968. scale_fonts = 0;
  1969. font_scaling = 1.0;
  1970. } else {
  1971. scale_fonts = 1;
  1972. font_scaling = atof(_self->action);
  1973. }
  1974. reinit();
  1975. }
  1976. static void render_decors_callback(struct menu_bar * self) {
  1977. (void)self;
  1978. render_decors();
  1979. }
  1980. int main(int argc, char ** argv) {
  1981. window_width = char_width * 80;
  1982. window_height = char_height * 24;
  1983. static struct option long_opts[] = {
  1984. {"fullscreen", no_argument, 0, 'F'},
  1985. {"bitmap", no_argument, 0, 'b'},
  1986. {"scale", required_argument, 0, 's'},
  1987. {"help", no_argument, 0, 'h'},
  1988. {"grid", no_argument, 0, 'x'},
  1989. {"no-frame", no_argument, 0, 'n'},
  1990. {"geometry", required_argument, 0, 'g'},
  1991. {"no-ft", no_argument, 0, 'f'},
  1992. {0,0,0,0}
  1993. };
  1994. /* Read some arguments */
  1995. int index, c;
  1996. while ((c = getopt_long(argc, argv, "bhxnfFls:g:", long_opts, &index)) != -1) {
  1997. if (!c) {
  1998. if (long_opts[index].flag == 0) {
  1999. c = long_opts[index].val;
  2000. }
  2001. }
  2002. switch (c) {
  2003. case 'x':
  2004. _free_size = 0;
  2005. break;
  2006. case 'n':
  2007. _no_frame = 1;
  2008. break;
  2009. case 'F':
  2010. _fullscreen = 1;
  2011. _no_frame = 1;
  2012. break;
  2013. case 'b':
  2014. _use_aa = 0;
  2015. break;
  2016. case 'h':
  2017. usage(argv);
  2018. return 0;
  2019. break;
  2020. case 's':
  2021. scale_fonts = 1;
  2022. font_scaling = atof(optarg);
  2023. break;
  2024. case 'g':
  2025. {
  2026. char * c = strstr(optarg, "x");
  2027. if (c) {
  2028. *c = '\0';
  2029. c++;
  2030. window_width = atoi(optarg);
  2031. window_height = atoi(c);
  2032. }
  2033. }
  2034. break;
  2035. case '?':
  2036. break;
  2037. default:
  2038. break;
  2039. }
  2040. }
  2041. /* Initialize the windowing library */
  2042. yctx = yutani_init();
  2043. if (!yctx) {
  2044. fprintf(stderr, "%s: failed to connect to compositor\n", argv[0]);
  2045. return 1;
  2046. }
  2047. _tt_font_normal = tt_font_from_shm("monospace");
  2048. _tt_font_bold = tt_font_from_shm("monospace.bold");
  2049. _tt_font_oblique = tt_font_from_shm("monospace.italic");
  2050. _tt_font_bold_oblique = tt_font_from_shm("monospace.bolditalic");
  2051. _tt_font_fallback = _tt_font_normal;
  2052. _tt_font_japanese = tt_font_from_file("/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf"); /* Might not be present */
  2053. /* Full screen mode forces window size to be that the display server */
  2054. if (_fullscreen) {
  2055. window_width = yctx->display_width;
  2056. window_height = yctx->display_height;
  2057. }
  2058. if (_no_frame) {
  2059. window = yutani_window_create_flags(yctx, window_width, window_height, YUTANI_WINDOW_FLAG_NO_ANIMATION);
  2060. } else {
  2061. init_decorations();
  2062. struct decor_bounds bounds;
  2063. decor_get_bounds(NULL, &bounds);
  2064. window = yutani_window_create(yctx, window_width + bounds.width, window_height + bounds.height + menu_bar_height);
  2065. }
  2066. if (_fullscreen) {
  2067. /* If fullscreen, assume we're always focused and put us on the bottom. */
  2068. yutani_set_stack(yctx, window, YUTANI_ZORDER_BOTTOM);
  2069. window->focused = 1;
  2070. } else {
  2071. window->focused = 0;
  2072. }
  2073. update_bounds();
  2074. /* Set up menus */
  2075. terminal_menu_bar.entries = terminal_menu_entries;
  2076. terminal_menu_bar.redraw_callback = render_decors_callback;
  2077. struct MenuEntry * _menu_exit = menu_create_normal("exit","exit","Exit", _menu_action_exit);
  2078. struct MenuEntry * _menu_copy = menu_create_normal(NULL, NULL, "Copy", _menu_action_copy);
  2079. struct MenuEntry * _menu_paste = menu_create_normal(NULL, NULL, "Paste", _menu_action_paste);
  2080. menu_right_click = menu_create();
  2081. menu_insert(menu_right_click, _menu_copy);
  2082. menu_insert(menu_right_click, _menu_paste);
  2083. menu_insert(menu_right_click, menu_create_separator());
  2084. if (!_fullscreen) {
  2085. _menu_toggle_borders_context = menu_create_normal(_no_frame ? NULL : "check", NULL, "Show borders", _menu_action_hide_borders);
  2086. menu_insert(menu_right_click, _menu_toggle_borders_context);
  2087. }
  2088. _menu_toggle_bitmap_context = menu_create_normal(_use_aa ? NULL : "check", NULL, "Bitmap font", _menu_action_toggle_tt);
  2089. menu_insert(menu_right_click, _menu_toggle_bitmap_context);
  2090. menu_insert(menu_right_click, menu_create_separator());
  2091. menu_insert(menu_right_click, _menu_exit);
  2092. /* Menu Bar menus */
  2093. terminal_menu_bar.set = menu_set_create();
  2094. struct MenuList * m;
  2095. m = menu_create(); /* File */
  2096. menu_insert(m, _menu_exit);
  2097. menu_set_insert(terminal_menu_bar.set, "file", m);
  2098. m = menu_create();
  2099. menu_insert(m, _menu_copy);
  2100. menu_insert(m, _menu_paste);
  2101. menu_set_insert(terminal_menu_bar.set, "edit", m);
  2102. m = menu_create();
  2103. menu_insert(m, menu_create_normal(NULL, "0.75", "75%", _menu_action_set_scale));
  2104. menu_insert(m, menu_create_normal(NULL, NULL, "100%", _menu_action_set_scale));
  2105. menu_insert(m, menu_create_normal(NULL, "1.5", "150%", _menu_action_set_scale));
  2106. menu_insert(m, menu_create_normal(NULL, "2.0", "200%", _menu_action_set_scale));
  2107. menu_set_insert(terminal_menu_bar.set, "zoom", m);
  2108. m = menu_create();
  2109. _menu_toggle_borders_bar = menu_create_normal(_no_frame ? NULL : "check", NULL, "Show borders", _menu_action_hide_borders);
  2110. menu_insert(m, _menu_toggle_borders_bar);
  2111. menu_insert(m, menu_create_submenu(NULL,"zoom","Set zoom..."));
  2112. _menu_toggle_bitmap_bar = menu_create_normal(_use_aa ? NULL : "check", NULL, "Bitmap font", _menu_action_toggle_tt);
  2113. menu_insert(m, _menu_toggle_bitmap_bar);
  2114. menu_insert(m, menu_create_normal(_free_size ? NULL : "check", NULL, "Snap to Cell Size", _menu_action_toggle_free_size));
  2115. menu_insert(m, menu_create_separator());
  2116. menu_insert(m, menu_create_normal(NULL, NULL, "Redraw", _menu_action_redraw));
  2117. menu_set_insert(terminal_menu_bar.set, "view", m);
  2118. m = menu_create();
  2119. menu_insert(m, menu_create_normal("help","help","Contents", _menu_action_show_help));
  2120. menu_insert(m, menu_create_separator());
  2121. menu_insert(m, menu_create_normal("star","star","About Terminal", _menu_action_show_about));
  2122. menu_set_insert(terminal_menu_bar.set, "help", m);
  2123. scrollback_list = list_create();
  2124. images_list = list_create();
  2125. /* Initialize the graphics context */
  2126. ctx = init_graphics_yutani_double_buffer(window);
  2127. /* Clear to black */
  2128. draw_fill(ctx, rgba(0,0,0,0));
  2129. /* Move window to screen center (XXX maybe remove this and do better window placement elsewhere */
  2130. yutani_window_move(yctx, window, yctx->display_width / 2 - window->width / 2, yctx->display_height / 2 - window->height / 2);
  2131. /* Open a PTY */
  2132. openpty(&fd_master, &fd_slave, NULL, NULL, NULL);
  2133. terminal = fdopen(fd_slave, "w");
  2134. /* Initialize the terminal buffer and ANSI library for the first time. */
  2135. reinit();
  2136. /* Run thread to handle asynchronous writes to the tty */
  2137. pthread_t input_buffer_thread;
  2138. pipe(input_buffer_semaphore);
  2139. input_buffer_queue = list_create();
  2140. pthread_create(&input_buffer_thread, NULL, handle_input_writing, NULL);
  2141. /* Make sure we're not passing anything to stdin on the child */
  2142. fflush(stdin);
  2143. /* Fork off child */
  2144. child_pid = fork();
  2145. if (!child_pid) {
  2146. setsid();
  2147. /* Prepare stdin/out/err */
  2148. dup2(fd_slave, 0);
  2149. dup2(fd_slave, 1);
  2150. dup2(fd_slave, 2);
  2151. tcsetpgrp(STDIN_FILENO, getpid());
  2152. /* Set the TERM environment variable. */
  2153. putenv("TERM=toaru");
  2154. /* Execute requested initial process */
  2155. if (argv[optind] != NULL) {
  2156. /* Run something specified by the terminal startup */
  2157. execvp(argv[optind], &argv[optind]);
  2158. fprintf(stderr, "Failed to launch requested startup application.\n");
  2159. } else {
  2160. /* Run the user's shell */
  2161. char * shell = getenv("SHELL");
  2162. if (!shell) shell = "/bin/sh"; /* fallback */
  2163. char * tokens[] = {shell,NULL};
  2164. execvp(tokens[0], tokens);
  2165. exit(1);
  2166. }
  2167. /* Failed to start */
  2168. exit_application = 1;
  2169. return 1;
  2170. } else {
  2171. /* Set up fswait to check Yutani and the PTY master */
  2172. int fds[2] = {fileno(yctx->sock), fd_master};
  2173. /* PTY read buffer */
  2174. unsigned char buf[4096];
  2175. while (!exit_application) {
  2176. /* Wait for something to happen. */
  2177. int res[] = {0,0};
  2178. fswait3(2,fds,200,res);
  2179. /* Check if the child application has closed. */
  2180. check_for_exit();
  2181. maybe_flip_cursor();
  2182. if (res[1]) {
  2183. /* Read from PTY */
  2184. ssize_t r = read(fd_master, buf, 4096);
  2185. for (ssize_t i = 0; i < r; ++i) {
  2186. ansi_put(ansi_state, buf[i]);
  2187. }
  2188. display_flip();
  2189. }
  2190. if (res[0]) {
  2191. /* Handle Yutani events. */
  2192. handle_incoming();
  2193. }
  2194. }
  2195. }
  2196. close(input_buffer_semaphore[1]);
  2197. /* Windows will close automatically on exit. */
  2198. return 0;
  2199. }