terminal.c 74 KB

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