window.c 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967
  1. #include <windows.h>
  2. #include <commctrl.h>
  3. #include <winsock.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <ctype.h>
  7. #define PUTTY_DO_GLOBALS /* actually _define_ globals */
  8. #include "putty.h"
  9. #include "win_res.h"
  10. #define IDM_SHOWLOG 0x0010
  11. #define IDM_NEWSESS 0x0020
  12. #define IDM_DUPSESS 0x0030
  13. #define IDM_RECONF 0x0040
  14. #define IDM_CLRSB 0x0050
  15. #define IDM_RESET 0x0060
  16. #define IDM_TEL_AYT 0x0070
  17. #define IDM_TEL_BRK 0x0080
  18. #define IDM_TEL_SYNCH 0x0090
  19. #define IDM_TEL_EC 0x00a0
  20. #define IDM_TEL_EL 0x00b0
  21. #define IDM_TEL_GA 0x00c0
  22. #define IDM_TEL_NOP 0x00d0
  23. #define IDM_TEL_ABORT 0x00e0
  24. #define IDM_TEL_AO 0x00f0
  25. #define IDM_TEL_IP 0x0100
  26. #define IDM_TEL_SUSP 0x0110
  27. #define IDM_TEL_EOR 0x0120
  28. #define IDM_TEL_EOF 0x0130
  29. #define IDM_ABOUT 0x0140
  30. #define IDM_SAVEDSESS 0x0150
  31. #define IDM_SAVED_MIN 0x1000
  32. #define IDM_SAVED_MAX 0x2000
  33. #define WM_IGNORE_SIZE (WM_XUSER + 1)
  34. #define WM_IGNORE_CLIP (WM_XUSER + 2)
  35. static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
  36. static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output);
  37. static void cfgtopalette(void);
  38. static void init_palette(void);
  39. static void init_fonts(int);
  40. static int extra_width, extra_height;
  41. static int pending_netevent = 0;
  42. static WPARAM pend_netevent_wParam = 0;
  43. static LPARAM pend_netevent_lParam = 0;
  44. static void enact_pending_netevent(void);
  45. #define FONT_NORMAL 0
  46. #define FONT_BOLD 1
  47. #define FONT_UNDERLINE 2
  48. #define FONT_BOLDUND 3
  49. #define FONT_OEM 4
  50. #define FONT_OEMBOLD 5
  51. #define FONT_OEMBOLDUND 6
  52. #define FONT_OEMUND 7
  53. static HFONT fonts[8];
  54. static enum {
  55. BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
  56. } bold_mode;
  57. static enum {
  58. UND_LINE, UND_FONT
  59. } und_mode;
  60. static int descent;
  61. #define NCOLOURS 24
  62. static COLORREF colours[NCOLOURS];
  63. static HPALETTE pal;
  64. static LPLOGPALETTE logpal;
  65. static RGBTRIPLE defpal[NCOLOURS];
  66. static HWND hwnd;
  67. static int dbltime, lasttime, lastact;
  68. static Mouse_Button lastbtn;
  69. static char *window_name, *icon_name;
  70. int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
  71. static char appname[] = "PuTTY";
  72. WORD winsock_ver;
  73. WSADATA wsadata;
  74. WNDCLASS wndclass;
  75. MSG msg;
  76. int guess_width, guess_height;
  77. putty_inst = inst;
  78. winsock_ver = MAKEWORD(1, 1);
  79. if (WSAStartup(winsock_ver, &wsadata)) {
  80. MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
  81. MB_OK | MB_ICONEXCLAMATION);
  82. return 1;
  83. }
  84. if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
  85. MessageBox(NULL, "WinSock version is incompatible with 1.1",
  86. "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
  87. WSACleanup();
  88. return 1;
  89. }
  90. /* WISHLIST: maybe allow config tweaking even if winsock not present? */
  91. InitCommonControls();
  92. /*
  93. * Process the command line.
  94. */
  95. {
  96. char *p;
  97. default_protocol = DEFAULT_PROTOCOL;
  98. default_port = DEFAULT_PORT;
  99. do_defaults(NULL);
  100. p = cmdline;
  101. while (*p && isspace(*p)) p++;
  102. /*
  103. * Process command line options first. Yes, this can be
  104. * done better, and it will be as soon as I have the
  105. * energy...
  106. */
  107. while (*p == '-') {
  108. char *q = p + strcspn(p, " \t");
  109. p++;
  110. if (q == p + 3 &&
  111. tolower(p[0]) == 's' &&
  112. tolower(p[1]) == 's' &&
  113. tolower(p[2]) == 'h') {
  114. default_protocol = cfg.protocol = PROT_SSH;
  115. default_port = cfg.port = 22;
  116. } else if (q == p + 3 &&
  117. tolower(p[0]) == 'l' &&
  118. tolower(p[1]) == 'o' &&
  119. tolower(p[2]) == 'g') {
  120. logfile = "putty.log";
  121. }
  122. p = q + strspn(q, " \t");
  123. }
  124. /*
  125. * An initial @ means to activate a saved session.
  126. */
  127. if (*p == '@') {
  128. do_defaults (p+1);
  129. if (!*cfg.host && !do_config()) {
  130. WSACleanup();
  131. return 0;
  132. }
  133. } else if (*p == '&') {
  134. /*
  135. * An initial & means we've been given a command line
  136. * containing the hex value of a HANDLE for a file
  137. * mapping object, which we must then extract as a
  138. * config.
  139. */
  140. HANDLE filemap;
  141. Config *cp;
  142. if (sscanf(p+1, "%p", &filemap) == 1 &&
  143. (cp = MapViewOfFile(filemap, FILE_MAP_READ,
  144. 0, 0, sizeof(Config))) != NULL) {
  145. cfg = *cp;
  146. UnmapViewOfFile(cp);
  147. CloseHandle(filemap);
  148. } else if (!do_config()) {
  149. WSACleanup();
  150. return 0;
  151. }
  152. } else if (*p) {
  153. char *q = p;
  154. while (*p && !isspace(*p)) p++;
  155. if (*p)
  156. *p++ = '\0';
  157. strncpy (cfg.host, q, sizeof(cfg.host)-1);
  158. cfg.host[sizeof(cfg.host)-1] = '\0';
  159. while (*p && isspace(*p)) p++;
  160. if (*p)
  161. cfg.port = atoi(p);
  162. else
  163. cfg.port = -1;
  164. } else {
  165. if (!do_config()) {
  166. WSACleanup();
  167. return 0;
  168. }
  169. }
  170. }
  171. /*
  172. * Select protocol. This is farmed out into a table in a
  173. * separate file to enable an ssh-free variant.
  174. */
  175. {
  176. int i;
  177. back = NULL;
  178. for (i = 0; backends[i].backend != NULL; i++)
  179. if (backends[i].protocol == cfg.protocol) {
  180. back = backends[i].backend;
  181. break;
  182. }
  183. if (back == NULL) {
  184. MessageBox(NULL, "Unsupported protocol number found",
  185. "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
  186. WSACleanup();
  187. return 1;
  188. }
  189. }
  190. ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
  191. if (!prev) {
  192. wndclass.style = 0;
  193. wndclass.lpfnWndProc = WndProc;
  194. wndclass.cbClsExtra = 0;
  195. wndclass.cbWndExtra = 0;
  196. wndclass.hInstance = inst;
  197. wndclass.hIcon = LoadIcon (inst,
  198. MAKEINTRESOURCE(IDI_MAINICON));
  199. wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
  200. wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
  201. wndclass.lpszMenuName = NULL;
  202. wndclass.lpszClassName = appname;
  203. RegisterClass (&wndclass);
  204. }
  205. hwnd = NULL;
  206. savelines = cfg.savelines;
  207. term_init();
  208. cfgtopalette();
  209. /*
  210. * Guess some defaults for the window size. This all gets
  211. * updated later, so we don't really care too much. However, we
  212. * do want the font width/height guesses to correspond to a
  213. * large font rather than a small one...
  214. */
  215. font_width = 10;
  216. font_height = 20;
  217. extra_width = 25;
  218. extra_height = 28;
  219. term_size (cfg.height, cfg.width, cfg.savelines);
  220. guess_width = extra_width + font_width * cols;
  221. guess_height = extra_height + font_height * rows;
  222. {
  223. RECT r;
  224. HWND w = GetDesktopWindow();
  225. GetWindowRect (w, &r);
  226. if (guess_width > r.right - r.left)
  227. guess_width = r.right - r.left;
  228. if (guess_height > r.bottom - r.top)
  229. guess_height = r.bottom - r.top;
  230. }
  231. hwnd = CreateWindow (appname, appname,
  232. WS_OVERLAPPEDWINDOW | WS_VSCROLL,
  233. CW_USEDEFAULT, CW_USEDEFAULT,
  234. guess_width, guess_height,
  235. NULL, NULL, inst, NULL);
  236. /*
  237. * Initialise the fonts, simultaneously correcting the guesses
  238. * for font_{width,height}.
  239. */
  240. bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
  241. und_mode = UND_FONT;
  242. init_fonts(0);
  243. /*
  244. * Correct the guesses for extra_{width,height}.
  245. */
  246. {
  247. RECT cr, wr;
  248. GetWindowRect (hwnd, &wr);
  249. GetClientRect (hwnd, &cr);
  250. extra_width = wr.right - wr.left - cr.right + cr.left;
  251. extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
  252. }
  253. /*
  254. * Resize the window, now we know what size we _really_ want it
  255. * to be.
  256. */
  257. guess_width = extra_width + font_width * cols;
  258. guess_height = extra_height + font_height * rows;
  259. SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
  260. SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
  261. SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
  262. /*
  263. * Initialise the scroll bar.
  264. */
  265. {
  266. SCROLLINFO si;
  267. si.cbSize = sizeof(si);
  268. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
  269. si.nMin = 0;
  270. si.nMax = rows-1;
  271. si.nPage = rows;
  272. si.nPos = 0;
  273. SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
  274. }
  275. /*
  276. * Start up the telnet connection.
  277. */
  278. {
  279. char *error;
  280. char msg[1024];
  281. char *realhost;
  282. error = back->init (hwnd, cfg.host, cfg.port, &realhost);
  283. if (error) {
  284. sprintf(msg, "Unable to open connection:\n%s", error);
  285. MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
  286. return 0;
  287. }
  288. window_name = icon_name = NULL;
  289. sprintf(msg, "%s - PuTTY", realhost);
  290. set_title (msg);
  291. set_icon (msg);
  292. }
  293. session_closed = FALSE;
  294. /*
  295. * Set up the input and output buffers.
  296. */
  297. inbuf_reap = inbuf_head = 0;
  298. outbuf_reap = outbuf_head = 0;
  299. /*
  300. * Choose unscroll method
  301. */
  302. unscroll_event = US_DISP;
  303. /*
  304. * Prepare the mouse handler.
  305. */
  306. lastact = MA_NOTHING;
  307. lastbtn = MB_NOTHING;
  308. dbltime = GetDoubleClickTime();
  309. /*
  310. * Set up the session-control options on the system menu.
  311. */
  312. {
  313. HMENU m = GetSystemMenu (hwnd, FALSE);
  314. HMENU p,s;
  315. int i;
  316. AppendMenu (m, MF_SEPARATOR, 0, 0);
  317. if (cfg.protocol == PROT_TELNET) {
  318. p = CreateMenu();
  319. AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
  320. AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
  321. AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
  322. AppendMenu (p, MF_SEPARATOR, 0, 0);
  323. AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
  324. AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
  325. AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
  326. AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
  327. AppendMenu (p, MF_SEPARATOR, 0, 0);
  328. AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
  329. AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
  330. AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
  331. AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
  332. AppendMenu (p, MF_SEPARATOR, 0, 0);
  333. AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
  334. AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
  335. AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
  336. AppendMenu (m, MF_SEPARATOR, 0, 0);
  337. }
  338. AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
  339. AppendMenu (m, MF_SEPARATOR, 0, 0);
  340. AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
  341. AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
  342. s = CreateMenu();
  343. get_sesslist(TRUE);
  344. for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
  345. AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
  346. AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
  347. AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
  348. AppendMenu (m, MF_SEPARATOR, 0, 0);
  349. AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
  350. AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
  351. AppendMenu (m, MF_SEPARATOR, 0, 0);
  352. AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
  353. }
  354. /*
  355. * Finally show the window!
  356. */
  357. ShowWindow (hwnd, show);
  358. /*
  359. * Set the palette up.
  360. */
  361. pal = NULL;
  362. logpal = NULL;
  363. init_palette();
  364. has_focus = (GetForegroundWindow() == hwnd);
  365. UpdateWindow (hwnd);
  366. {
  367. int timer_id = 0, long_timer = 0;
  368. while (GetMessage (&msg, NULL, 0, 0) == 1) {
  369. /* Sometimes DispatchMessage calls routines that use their own
  370. * GetMessage loop, setup this timer so we get some control back.
  371. *
  372. * Also call term_update() from the timer so that if the host
  373. * is sending data flat out we still do redraws.
  374. */
  375. if(timer_id && long_timer) {
  376. KillTimer(hwnd, timer_id);
  377. long_timer = timer_id = 0;
  378. }
  379. if(!timer_id)
  380. timer_id = SetTimer(hwnd, 1, 20, NULL);
  381. DispatchMessage (&msg);
  382. /* This is too fast, but I'll leave it for now 'cause it shows
  383. * how often term_update is called (far too often at times!)
  384. */
  385. term_blink(0);
  386. /* If there's nothing new in the queue then we can do everything
  387. * we've delayed, reading the socket, writing, and repainting
  388. * the window.
  389. */
  390. if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
  391. if (pending_netevent) {
  392. enact_pending_netevent();
  393. term_blink(1);
  394. }
  395. } else continue;
  396. if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
  397. if (timer_id) {
  398. KillTimer(hwnd, timer_id);
  399. timer_id = 0;
  400. }
  401. if (inbuf_reap != inbuf_head)
  402. term_out();
  403. term_update();
  404. timer_id = SetTimer(hwnd, 1, 500, NULL);
  405. long_timer = 1;
  406. }
  407. }
  408. }
  409. /*
  410. * Clean up.
  411. */
  412. {
  413. int i;
  414. for (i=0; i<8; i++)
  415. if (fonts[i])
  416. DeleteObject(fonts[i]);
  417. }
  418. sfree(logpal);
  419. if (pal)
  420. DeleteObject(pal);
  421. WSACleanup();
  422. if (cfg.protocol == PROT_SSH) {
  423. random_save_seed();
  424. #ifdef MSCRYPTOAPI
  425. crypto_wrapup();
  426. #endif
  427. }
  428. return msg.wParam;
  429. }
  430. /*
  431. * Actually do the job requested by a WM_NETEVENT
  432. */
  433. static void enact_pending_netevent(void) {
  434. int i;
  435. pending_netevent = FALSE;
  436. i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
  437. if (i < 0) {
  438. char buf[1024];
  439. switch (WSABASEERR + (-i) % 10000) {
  440. case WSAECONNRESET:
  441. sprintf(buf, "Connection reset by peer");
  442. break;
  443. default:
  444. sprintf(buf, "Unexpected network error %d", -i);
  445. break;
  446. }
  447. MessageBox(hwnd, buf, "PuTTY Fatal Error",
  448. MB_ICONERROR | MB_OK);
  449. PostQuitMessage(1);
  450. } else if (i == 0) {
  451. if (cfg.close_on_exit)
  452. PostQuitMessage(0);
  453. else {
  454. session_closed = TRUE;
  455. MessageBox(hwnd, "Connection closed by remote host",
  456. "PuTTY", MB_OK | MB_ICONINFORMATION);
  457. SetWindowText (hwnd, "PuTTY (inactive)");
  458. }
  459. }
  460. }
  461. /*
  462. * Copy the colour palette from the configuration data into defpal.
  463. * This is non-trivial because the colour indices are different.
  464. */
  465. static void cfgtopalette(void) {
  466. int i;
  467. static const int ww[] = {
  468. 6, 7, 8, 9, 10, 11, 12, 13,
  469. 14, 15, 16, 17, 18, 19, 20, 21,
  470. 0, 1, 2, 3, 4, 4, 5, 5
  471. };
  472. for (i=0; i<24; i++) {
  473. int w = ww[i];
  474. defpal[i].rgbtRed = cfg.colours[w][0];
  475. defpal[i].rgbtGreen = cfg.colours[w][1];
  476. defpal[i].rgbtBlue = cfg.colours[w][2];
  477. }
  478. }
  479. /*
  480. * Set up the colour palette.
  481. */
  482. static void init_palette(void) {
  483. int i;
  484. HDC hdc = GetDC (hwnd);
  485. if (hdc) {
  486. if (cfg.try_palette &&
  487. GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
  488. logpal = smalloc(sizeof(*logpal)
  489. - sizeof(logpal->palPalEntry)
  490. + NCOLOURS * sizeof(PALETTEENTRY));
  491. logpal->palVersion = 0x300;
  492. logpal->palNumEntries = NCOLOURS;
  493. for (i = 0; i < NCOLOURS; i++) {
  494. logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
  495. logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
  496. logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
  497. logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
  498. }
  499. pal = CreatePalette (logpal);
  500. if (pal) {
  501. SelectPalette (hdc, pal, FALSE);
  502. RealizePalette (hdc);
  503. SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
  504. FALSE);
  505. }
  506. }
  507. ReleaseDC (hwnd, hdc);
  508. }
  509. if (pal)
  510. for (i=0; i<NCOLOURS; i++)
  511. colours[i] = PALETTERGB(defpal[i].rgbtRed,
  512. defpal[i].rgbtGreen,
  513. defpal[i].rgbtBlue);
  514. else
  515. for(i=0; i<NCOLOURS; i++)
  516. colours[i] = RGB(defpal[i].rgbtRed,
  517. defpal[i].rgbtGreen,
  518. defpal[i].rgbtBlue);
  519. }
  520. /*
  521. * Initialise all the fonts we will need. There may be as many as
  522. * eight or as few as one. We also:
  523. *
  524. * - check the font width and height, correcting our guesses if
  525. * necessary.
  526. *
  527. * - verify that the bold font is the same width as the ordinary
  528. * one, and engage shadow bolding if not.
  529. *
  530. * - verify that the underlined font is the same width as the
  531. * ordinary one (manual underlining by means of line drawing can
  532. * be done in a pinch).
  533. */
  534. static void init_fonts(int pick_width) {
  535. TEXTMETRIC tm;
  536. int i;
  537. int fsize[8];
  538. HDC hdc;
  539. int fw_dontcare, fw_bold;
  540. int firstchar = ' ';
  541. #ifdef CHECKOEMFONT
  542. font_messup:
  543. #endif
  544. for (i=0; i<8; i++)
  545. fonts[i] = NULL;
  546. if (cfg.fontisbold) {
  547. fw_dontcare = FW_BOLD;
  548. fw_bold = FW_BLACK;
  549. } else {
  550. fw_dontcare = FW_DONTCARE;
  551. fw_bold = FW_BOLD;
  552. }
  553. hdc = GetDC(hwnd);
  554. font_height = cfg.fontheight;
  555. font_width = pick_width;
  556. #define f(i,c,w,u) \
  557. fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
  558. c, OUT_DEFAULT_PRECIS, \
  559. CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
  560. FIXED_PITCH | FF_DONTCARE, cfg.font)
  561. if (cfg.vtmode != VT_OEMONLY) {
  562. f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
  563. SelectObject (hdc, fonts[FONT_NORMAL]);
  564. GetTextMetrics(hdc, &tm);
  565. font_height = tm.tmHeight;
  566. font_width = tm.tmAveCharWidth;
  567. f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
  568. if (bold_mode == BOLD_FONT) {
  569. f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
  570. f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
  571. }
  572. if (cfg.vtmode == VT_OEMANSI) {
  573. f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
  574. f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
  575. if (bold_mode == BOLD_FONT) {
  576. f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
  577. f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
  578. }
  579. }
  580. }
  581. else
  582. {
  583. f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
  584. SelectObject (hdc, fonts[FONT_OEM]);
  585. GetTextMetrics(hdc, &tm);
  586. font_height = tm.tmHeight;
  587. font_width = tm.tmAveCharWidth;
  588. f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
  589. if (bold_mode == BOLD_FONT) {
  590. f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
  591. f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
  592. }
  593. }
  594. #undef f
  595. descent = tm.tmAscent + 1;
  596. if (descent >= font_height)
  597. descent = font_height - 1;
  598. firstchar = tm.tmFirstChar;
  599. for (i=0; i<8; i++) {
  600. if (fonts[i]) {
  601. if (SelectObject (hdc, fonts[i]) &&
  602. GetTextMetrics(hdc, &tm) )
  603. fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
  604. else fsize[i] = -i;
  605. }
  606. else fsize[i] = -i;
  607. }
  608. ReleaseDC (hwnd, hdc);
  609. /* ... This is wrong in OEM only mode */
  610. if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
  611. (bold_mode == BOLD_FONT &&
  612. fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
  613. und_mode = UND_LINE;
  614. DeleteObject (fonts[FONT_UNDERLINE]);
  615. if (bold_mode == BOLD_FONT)
  616. DeleteObject (fonts[FONT_BOLDUND]);
  617. }
  618. if (bold_mode == BOLD_FONT &&
  619. fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
  620. bold_mode = BOLD_SHADOW;
  621. DeleteObject (fonts[FONT_BOLD]);
  622. if (und_mode == UND_FONT)
  623. DeleteObject (fonts[FONT_BOLDUND]);
  624. }
  625. #ifdef CHECKOEMFONT
  626. /* With the fascist font painting it doesn't matter if the linedraw font
  627. * isn't exactly the right size anymore so we don't have to check this.
  628. */
  629. if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
  630. if( cfg.fontcharset == OEM_CHARSET )
  631. {
  632. MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
  633. "different sizes. Using OEM-only mode instead",
  634. "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
  635. cfg.vtmode = VT_OEMONLY;
  636. }
  637. else if( firstchar < ' ' )
  638. {
  639. MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
  640. "different sizes. Using XTerm mode instead",
  641. "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
  642. cfg.vtmode = VT_XWINDOWS;
  643. }
  644. else
  645. {
  646. MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
  647. "different sizes. Using ISO8859-1 mode instead",
  648. "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
  649. cfg.vtmode = VT_POORMAN;
  650. }
  651. for (i=0; i<8; i++)
  652. if (fonts[i])
  653. DeleteObject (fonts[i]);
  654. goto font_messup;
  655. }
  656. #endif
  657. }
  658. void request_resize (int w, int h, int refont) {
  659. int width, height;
  660. #ifdef CHECKOEMFONT
  661. /* Don't do this in OEMANSI, you may get disable messages */
  662. if (refont && w != cols && (cols==80 || cols==132)
  663. && cfg.vtmode != VT_OEMANSI)
  664. #else
  665. if (refont && w != cols && (cols==80 || cols==132))
  666. #endif
  667. {
  668. /* If font width too big for screen should we shrink the font more ? */
  669. if (w==132)
  670. font_width = ((font_width*cols+w/2)/w);
  671. else
  672. font_width = 0;
  673. {
  674. int i;
  675. for (i=0; i<8; i++)
  676. if (fonts[i])
  677. DeleteObject(fonts[i]);
  678. }
  679. bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
  680. und_mode = UND_FONT;
  681. init_fonts(font_width);
  682. }
  683. width = extra_width + font_width * w;
  684. height = extra_height + font_height * h;
  685. SetWindowPos (hwnd, NULL, 0, 0, width, height,
  686. SWP_NOACTIVATE | SWP_NOCOPYBITS |
  687. SWP_NOMOVE | SWP_NOZORDER);
  688. }
  689. static void click (Mouse_Button b, int x, int y) {
  690. int thistime = GetMessageTime();
  691. if (lastbtn == b && thistime - lasttime < dbltime) {
  692. lastact = (lastact == MA_CLICK ? MA_2CLK :
  693. lastact == MA_2CLK ? MA_3CLK :
  694. lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
  695. } else {
  696. lastbtn = b;
  697. lastact = MA_CLICK;
  698. }
  699. if (lastact != MA_NOTHING)
  700. term_mouse (b, lastact, x, y);
  701. lasttime = thistime;
  702. }
  703. static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
  704. WPARAM wParam, LPARAM lParam) {
  705. HDC hdc;
  706. static int ignore_size = FALSE;
  707. static int ignore_clip = FALSE;
  708. static int just_reconfigged = FALSE;
  709. switch (message) {
  710. case WM_TIMER:
  711. if (pending_netevent)
  712. enact_pending_netevent();
  713. if (inbuf_reap != inbuf_head)
  714. term_out();
  715. term_update();
  716. return 0;
  717. case WM_CREATE:
  718. break;
  719. case WM_CLOSE:
  720. if (!cfg.warn_on_close || session_closed ||
  721. MessageBox(hwnd, "Are you sure you want to close this session?",
  722. "PuTTY Exit Confirmation",
  723. MB_ICONWARNING | MB_OKCANCEL) == IDOK)
  724. DestroyWindow(hwnd);
  725. return 0;
  726. case WM_DESTROY:
  727. PostQuitMessage (0);
  728. return 0;
  729. case WM_SYSCOMMAND:
  730. switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
  731. case IDM_SHOWLOG:
  732. showeventlog(hwnd);
  733. break;
  734. case IDM_NEWSESS:
  735. case IDM_DUPSESS:
  736. case IDM_SAVEDSESS:
  737. {
  738. char b[2048];
  739. char c[30], *cl;
  740. int freecl = FALSE;
  741. STARTUPINFO si;
  742. PROCESS_INFORMATION pi;
  743. HANDLE filemap = NULL;
  744. if (wParam == IDM_DUPSESS) {
  745. /*
  746. * Allocate a file-mapping memory chunk for the
  747. * config structure.
  748. */
  749. SECURITY_ATTRIBUTES sa;
  750. Config *p;
  751. sa.nLength = sizeof(sa);
  752. sa.lpSecurityDescriptor = NULL;
  753. sa.bInheritHandle = TRUE;
  754. filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
  755. &sa,
  756. PAGE_READWRITE,
  757. 0,
  758. sizeof(Config),
  759. NULL);
  760. if (filemap) {
  761. p = (Config *)MapViewOfFile(filemap,
  762. FILE_MAP_WRITE,
  763. 0, 0, sizeof(Config));
  764. if (p) {
  765. *p = cfg; /* structure copy */
  766. UnmapViewOfFile(p);
  767. }
  768. }
  769. sprintf(c, "putty &%p", filemap);
  770. cl = c;
  771. } else if (wParam == IDM_SAVEDSESS) {
  772. char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
  773. cl = malloc(16 + strlen(session)); /* 8, but play safe */
  774. if (!cl)
  775. cl = NULL; /* not a very important failure mode */
  776. else {
  777. sprintf(cl, "putty @%s", session);
  778. freecl = TRUE;
  779. }
  780. } else
  781. cl = NULL;
  782. GetModuleFileName (NULL, b, sizeof(b)-1);
  783. si.cb = sizeof(si);
  784. si.lpReserved = NULL;
  785. si.lpDesktop = NULL;
  786. si.lpTitle = NULL;
  787. si.dwFlags = 0;
  788. si.cbReserved2 = 0;
  789. si.lpReserved2 = NULL;
  790. CreateProcess (b, cl, NULL, NULL, TRUE,
  791. NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
  792. if (filemap)
  793. CloseHandle(filemap);
  794. if (freecl)
  795. free(cl);
  796. }
  797. break;
  798. case IDM_RECONF:
  799. if (!do_reconfig(hwnd))
  800. break;
  801. just_reconfigged = TRUE;
  802. {
  803. int i;
  804. for (i=0; i<8; i++)
  805. if (fonts[i])
  806. DeleteObject(fonts[i]);
  807. }
  808. bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
  809. und_mode = UND_FONT;
  810. init_fonts(0);
  811. sfree(logpal);
  812. /* Telnet will change local echo -> remote if the remote asks */
  813. if (cfg.protocol != PROT_TELNET)
  814. ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
  815. if (pal)
  816. DeleteObject(pal);
  817. logpal = NULL;
  818. pal = NULL;
  819. cfgtopalette();
  820. init_palette();
  821. term_size(cfg.height, cfg.width, cfg.savelines);
  822. InvalidateRect(hwnd, NULL, TRUE);
  823. SetWindowPos (hwnd, NULL, 0, 0,
  824. extra_width + font_width * cfg.width,
  825. extra_height + font_height * cfg.height,
  826. SWP_NOACTIVATE | SWP_NOCOPYBITS |
  827. SWP_NOMOVE | SWP_NOZORDER);
  828. if (IsIconic(hwnd)) {
  829. SetWindowText (hwnd,
  830. cfg.win_name_always ? window_name : icon_name);
  831. }
  832. break;
  833. case IDM_CLRSB:
  834. term_clrsb();
  835. break;
  836. case IDM_RESET:
  837. term_pwron();
  838. break;
  839. case IDM_TEL_AYT: back->special (TS_AYT); break;
  840. case IDM_TEL_BRK: back->special (TS_BRK); break;
  841. case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
  842. case IDM_TEL_EC: back->special (TS_EC); break;
  843. case IDM_TEL_EL: back->special (TS_EL); break;
  844. case IDM_TEL_GA: back->special (TS_GA); break;
  845. case IDM_TEL_NOP: back->special (TS_NOP); break;
  846. case IDM_TEL_ABORT: back->special (TS_ABORT); break;
  847. case IDM_TEL_AO: back->special (TS_AO); break;
  848. case IDM_TEL_IP: back->special (TS_IP); break;
  849. case IDM_TEL_SUSP: back->special (TS_SUSP); break;
  850. case IDM_TEL_EOR: back->special (TS_EOR); break;
  851. case IDM_TEL_EOF: back->special (TS_EOF); break;
  852. case IDM_ABOUT:
  853. showabout (hwnd);
  854. break;
  855. default:
  856. if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
  857. SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
  858. }
  859. }
  860. break;
  861. #define X_POS(l) ((int)(short)LOWORD(l))
  862. #define Y_POS(l) ((int)(short)HIWORD(l))
  863. #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
  864. #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
  865. case WM_LBUTTONDOWN:
  866. click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
  867. TO_CHR_Y(Y_POS(lParam)));
  868. SetCapture(hwnd);
  869. return 0;
  870. case WM_LBUTTONUP:
  871. term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
  872. TO_CHR_Y(Y_POS(lParam)));
  873. ReleaseCapture();
  874. return 0;
  875. case WM_MBUTTONDOWN:
  876. SetCapture(hwnd);
  877. click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
  878. TO_CHR_X(X_POS(lParam)),
  879. TO_CHR_Y(Y_POS(lParam)));
  880. return 0;
  881. case WM_MBUTTONUP:
  882. term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
  883. MA_RELEASE, TO_CHR_X(X_POS(lParam)),
  884. TO_CHR_Y(Y_POS(lParam)));
  885. ReleaseCapture();
  886. return 0;
  887. case WM_RBUTTONDOWN:
  888. SetCapture(hwnd);
  889. click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
  890. TO_CHR_X(X_POS(lParam)),
  891. TO_CHR_Y(Y_POS(lParam)));
  892. return 0;
  893. case WM_RBUTTONUP:
  894. term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
  895. MA_RELEASE, TO_CHR_X(X_POS(lParam)),
  896. TO_CHR_Y(Y_POS(lParam)));
  897. ReleaseCapture();
  898. return 0;
  899. case WM_MOUSEMOVE:
  900. /*
  901. * Add the mouse position and message time to the random
  902. * number noise, if we're using ssh.
  903. */
  904. if (cfg.protocol == PROT_SSH)
  905. noise_ultralight(lParam);
  906. if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
  907. Mouse_Button b;
  908. if (wParam & MK_LBUTTON)
  909. b = MB_SELECT;
  910. else if (wParam & MK_MBUTTON)
  911. b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
  912. else
  913. b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
  914. term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
  915. TO_CHR_Y(Y_POS(lParam)));
  916. }
  917. return 0;
  918. case WM_IGNORE_CLIP:
  919. ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
  920. break;
  921. case WM_DESTROYCLIPBOARD:
  922. if (!ignore_clip)
  923. term_deselect();
  924. ignore_clip = FALSE;
  925. return 0;
  926. case WM_PAINT:
  927. {
  928. PAINTSTRUCT p;
  929. hdc = BeginPaint (hwnd, &p);
  930. if (pal) {
  931. SelectPalette (hdc, pal, TRUE);
  932. RealizePalette (hdc);
  933. }
  934. term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
  935. p.rcPaint.right, p.rcPaint.bottom);
  936. SelectObject (hdc, GetStockObject(SYSTEM_FONT));
  937. SelectObject (hdc, GetStockObject(WHITE_PEN));
  938. EndPaint (hwnd, &p);
  939. }
  940. return 0;
  941. case WM_NETEVENT:
  942. /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
  943. * but the only one that's likely to try to overload us is FD_READ.
  944. * This means buffering just one is fine.
  945. */
  946. if (pending_netevent)
  947. enact_pending_netevent();
  948. pending_netevent = TRUE;
  949. pend_netevent_wParam=wParam;
  950. pend_netevent_lParam=lParam;
  951. return 0;
  952. case WM_SETFOCUS:
  953. has_focus = TRUE;
  954. term_out();
  955. term_update();
  956. break;
  957. case WM_KILLFOCUS:
  958. has_focus = FALSE;
  959. term_out();
  960. term_update();
  961. break;
  962. case WM_IGNORE_SIZE:
  963. ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
  964. break;
  965. case WM_ENTERSIZEMOVE:
  966. EnableSizeTip(1);
  967. break;
  968. case WM_EXITSIZEMOVE:
  969. EnableSizeTip(0);
  970. break;
  971. case WM_SIZING:
  972. {
  973. int width, height, w, h, ew, eh;
  974. LPRECT r = (LPRECT)lParam;
  975. width = r->right - r->left - extra_width;
  976. height = r->bottom - r->top - extra_height;
  977. w = (width + font_width/2) / font_width; if (w < 1) w = 1;
  978. h = (height + font_height/2) / font_height; if (h < 1) h = 1;
  979. UpdateSizeTip(hwnd, w, h);
  980. ew = width - w * font_width;
  981. eh = height - h * font_height;
  982. if (ew != 0) {
  983. if (wParam == WMSZ_LEFT ||
  984. wParam == WMSZ_BOTTOMLEFT ||
  985. wParam == WMSZ_TOPLEFT)
  986. r->left += ew;
  987. else
  988. r->right -= ew;
  989. }
  990. if (eh != 0) {
  991. if (wParam == WMSZ_TOP ||
  992. wParam == WMSZ_TOPRIGHT ||
  993. wParam == WMSZ_TOPLEFT)
  994. r->top += eh;
  995. else
  996. r->bottom -= eh;
  997. }
  998. if (ew || eh)
  999. return 1;
  1000. else
  1001. return 0;
  1002. }
  1003. /* break; (never reached) */
  1004. case WM_SIZE:
  1005. if (wParam == SIZE_MINIMIZED) {
  1006. SetWindowText (hwnd,
  1007. cfg.win_name_always ? window_name : icon_name);
  1008. break;
  1009. }
  1010. if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
  1011. SetWindowText (hwnd, window_name);
  1012. if (!ignore_size) {
  1013. int width, height, w, h;
  1014. #if 0 /* we have fixed this using WM_SIZING now */
  1015. int ew, eh;
  1016. #endif
  1017. width = LOWORD(lParam);
  1018. height = HIWORD(lParam);
  1019. w = width / font_width; if (w < 1) w = 1;
  1020. h = height / font_height; if (h < 1) h = 1;
  1021. #if 0 /* we have fixed this using WM_SIZING now */
  1022. ew = width - w * font_width;
  1023. eh = height - h * font_height;
  1024. if (ew != 0 || eh != 0) {
  1025. RECT r;
  1026. GetWindowRect (hwnd, &r);
  1027. SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
  1028. SetWindowPos (hwnd, NULL, 0, 0,
  1029. r.right - r.left - ew, r.bottom - r.top - eh,
  1030. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  1031. }
  1032. #endif
  1033. if (w != cols || h != rows || just_reconfigged) {
  1034. term_invalidate();
  1035. term_size (h, w, cfg.savelines);
  1036. back->size();
  1037. just_reconfigged = FALSE;
  1038. }
  1039. }
  1040. ignore_size = FALSE;
  1041. return 0;
  1042. case WM_VSCROLL:
  1043. switch (LOWORD(wParam)) {
  1044. case SB_BOTTOM: term_scroll(-1, 0); break;
  1045. case SB_TOP: term_scroll(+1, 0); break;
  1046. case SB_LINEDOWN: term_scroll (0, +1); break;
  1047. case SB_LINEUP: term_scroll (0, -1); break;
  1048. case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
  1049. case SB_PAGEUP: term_scroll (0, -rows/2); break;
  1050. case SB_THUMBPOSITION: case SB_THUMBTRACK:
  1051. term_scroll (1, HIWORD(wParam)); break;
  1052. }
  1053. break;
  1054. case WM_PALETTECHANGED:
  1055. if ((HWND) wParam != hwnd && pal != NULL) {
  1056. HDC hdc = get_ctx();
  1057. if (hdc) {
  1058. if (RealizePalette (hdc) > 0)
  1059. UpdateColors (hdc);
  1060. free_ctx (hdc);
  1061. }
  1062. }
  1063. break;
  1064. case WM_QUERYNEWPALETTE:
  1065. if (pal != NULL) {
  1066. HDC hdc = get_ctx();
  1067. if (hdc) {
  1068. if (RealizePalette (hdc) > 0)
  1069. UpdateColors (hdc);
  1070. free_ctx (hdc);
  1071. return TRUE;
  1072. }
  1073. }
  1074. return FALSE;
  1075. case WM_KEYDOWN:
  1076. case WM_SYSKEYDOWN:
  1077. /*
  1078. * Add the scan code and keypress timing to the random
  1079. * number noise, if we're using ssh.
  1080. */
  1081. if (cfg.protocol == PROT_SSH)
  1082. noise_ultralight(lParam);
  1083. /*
  1084. * We don't do TranslateMessage since it disassociates the
  1085. * resulting CHAR message from the KEYDOWN that sparked it,
  1086. * which we occasionally don't want. Instead, we process
  1087. * KEYDOWN, and call the Win32 translator functions so that
  1088. * we get the translations under _our_ control.
  1089. */
  1090. {
  1091. unsigned char buf[20];
  1092. int len;
  1093. len = TranslateKey (wParam, lParam, buf);
  1094. if (len == -1)
  1095. return DefWindowProc (hwnd, message, wParam, lParam);
  1096. ldisc->send (buf, len);
  1097. }
  1098. return 0;
  1099. case WM_KEYUP:
  1100. case WM_SYSKEYUP:
  1101. /*
  1102. * We handle KEYUP ourselves in order to distinghish left
  1103. * and right Alt or Control keys, which Windows won't do
  1104. * right if left to itself. See also the special processing
  1105. * at the top of TranslateKey.
  1106. */
  1107. {
  1108. BYTE keystate[256];
  1109. int ret = GetKeyboardState(keystate);
  1110. if (ret && wParam == VK_MENU) {
  1111. if (lParam & 0x1000000) keystate[VK_RMENU] = 0;
  1112. else keystate[VK_LMENU] = 0;
  1113. SetKeyboardState (keystate);
  1114. }
  1115. if (ret && wParam == VK_CONTROL) {
  1116. if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0;
  1117. else keystate[VK_LCONTROL] = 0;
  1118. SetKeyboardState (keystate);
  1119. }
  1120. }
  1121. /*
  1122. * We don't return here, in order to allow Windows to do
  1123. * its own KEYUP processing as well.
  1124. */
  1125. break;
  1126. case WM_CHAR:
  1127. case WM_SYSCHAR:
  1128. /*
  1129. * Nevertheless, we are prepared to deal with WM_CHAR
  1130. * messages, should they crop up. So if someone wants to
  1131. * post the things to us as part of a macro manoeuvre,
  1132. * we're ready to cope.
  1133. */
  1134. {
  1135. char c = xlat_kbd2tty((unsigned char)wParam);
  1136. ldisc->send (&c, 1);
  1137. }
  1138. return 0;
  1139. }
  1140. return DefWindowProc (hwnd, message, wParam, lParam);
  1141. }
  1142. /*
  1143. * Draw a line of text in the window, at given character
  1144. * coordinates, in given attributes.
  1145. *
  1146. * We are allowed to fiddle with the contents of `text'.
  1147. */
  1148. void do_text (Context ctx, int x, int y, char *text, int len,
  1149. unsigned long attr) {
  1150. int lattr = 0; /* Will be arg later for line attribute type */
  1151. COLORREF fg, bg, t;
  1152. int nfg, nbg, nfont;
  1153. HDC hdc = ctx;
  1154. RECT line_box;
  1155. int force_manual_underline = 0;
  1156. static int *IpDx = 0, IpDxLEN = 0;;
  1157. if (len>IpDxLEN || IpDx[0] != font_width*(1+!lattr)) {
  1158. int i;
  1159. if (len>IpDxLEN) {
  1160. sfree(IpDx);
  1161. IpDx = smalloc((len+16)*sizeof(int));
  1162. IpDxLEN = (len+16);
  1163. }
  1164. for(i=0; i<len; i++)
  1165. IpDx[i] = font_width;
  1166. }
  1167. x *= font_width;
  1168. y *= font_height;
  1169. if (lattr) x *= 2;
  1170. if (attr & ATTR_ACTCURS) {
  1171. attr &= (bold_mode == BOLD_COLOURS ? 0x200 : 0x300);
  1172. attr ^= ATTR_CUR_XOR;
  1173. }
  1174. nfont = 0;
  1175. if (cfg.vtmode == VT_OEMONLY)
  1176. nfont |= FONT_OEM;
  1177. /*
  1178. * Map high-half characters in order to approximate ISO using
  1179. * OEM character set. No characters are missing if the OEM codepage
  1180. * is CP850.
  1181. */
  1182. if (nfont & FONT_OEM) {
  1183. int i;
  1184. for (i=0; i<len; i++)
  1185. if (text[i] >= '\xA0' && text[i] <= '\xFF') {
  1186. #if 0
  1187. /* This is CP850 ... perfect translation */
  1188. static const char oemhighhalf[] =
  1189. "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
  1190. "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
  1191. "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
  1192. "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
  1193. "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
  1194. "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
  1195. "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
  1196. "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
  1197. "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
  1198. "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
  1199. "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
  1200. "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
  1201. ;
  1202. #endif
  1203. /* This is CP437 ... junk translation */
  1204. static const unsigned char oemhighhalf[] = {
  1205. 0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
  1206. 0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
  1207. 0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
  1208. 0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
  1209. 0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
  1210. 0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
  1211. 0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
  1212. 0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
  1213. 0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
  1214. 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
  1215. 0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
  1216. 0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
  1217. };
  1218. text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
  1219. }
  1220. }
  1221. if (attr & ATTR_GBCHR) {
  1222. int i;
  1223. /*
  1224. * GB mapping: map # to pound, and everything else stays
  1225. * normal.
  1226. */
  1227. for (i=0; i<len; i++)
  1228. if (text[i] == '#')
  1229. text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
  1230. } else if (attr & ATTR_LINEDRW) {
  1231. int i;
  1232. /* ISO 8859-1 */
  1233. static const char poorman[] =
  1234. "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
  1235. /* CP437 */
  1236. static const char oemmap_437[] =
  1237. "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
  1238. "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
  1239. /* CP850 */
  1240. static const char oemmap_850[] =
  1241. "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
  1242. "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
  1243. /* Poor windows font ... eg: windows courier */
  1244. static const char oemmap[] =
  1245. "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
  1246. "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
  1247. /*
  1248. * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
  1249. * VT100 line drawing chars; everything else stays normal.
  1250. */
  1251. switch (cfg.vtmode) {
  1252. case VT_XWINDOWS:
  1253. for (i=0; i<len; i++)
  1254. if (text[i] >= '\x60' && text[i] <= '\x7E')
  1255. text[i] += '\x01' - '\x60';
  1256. break;
  1257. case VT_OEMANSI:
  1258. /* Make sure we actually have an OEM font */
  1259. if (fonts[nfont|FONT_OEM]) {
  1260. case VT_OEMONLY:
  1261. nfont |= FONT_OEM;
  1262. for (i=0; i<len; i++)
  1263. if (text[i] >= '\x60' && text[i] <= '\x7E')
  1264. text[i] = oemmap[(unsigned char)text[i] - 0x60];
  1265. break;
  1266. }
  1267. case VT_POORMAN:
  1268. for (i=0; i<len; i++)
  1269. if (text[i] >= '\x60' && text[i] <= '\x7E')
  1270. text[i] = poorman[(unsigned char)text[i] - 0x60];
  1271. break;
  1272. }
  1273. }
  1274. nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
  1275. nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
  1276. if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
  1277. nfont |= FONT_BOLD;
  1278. if (und_mode == UND_FONT && (attr & ATTR_UNDER))
  1279. nfont |= FONT_UNDERLINE;
  1280. if (!fonts[nfont])
  1281. {
  1282. if (nfont&FONT_UNDERLINE)
  1283. force_manual_underline = 1;
  1284. /* Don't do the same for manual bold, it could be bad news. */
  1285. nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
  1286. }
  1287. if (attr & ATTR_REVERSE) {
  1288. t = nfg; nfg = nbg; nbg = t;
  1289. }
  1290. if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
  1291. nfg++;
  1292. if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
  1293. nbg++;
  1294. fg = colours[nfg];
  1295. bg = colours[nbg];
  1296. SelectObject (hdc, fonts[nfont]);
  1297. SetTextColor (hdc, fg);
  1298. SetBkColor (hdc, bg);
  1299. SetBkMode (hdc, OPAQUE);
  1300. line_box.left = x;
  1301. line_box.top = y;
  1302. line_box.right = x+font_width*len;
  1303. line_box.bottom = y+font_height;
  1304. ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
  1305. if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
  1306. SetBkMode (hdc, TRANSPARENT);
  1307. /* GRR: This draws the character outside it's box and can leave
  1308. * 'droppings' even with the clip box! I suppose I could loop it
  1309. * one character at a time ... yuk. */
  1310. ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
  1311. }
  1312. if (force_manual_underline ||
  1313. (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
  1314. HPEN oldpen;
  1315. oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
  1316. MoveToEx (hdc, x, y+descent, NULL);
  1317. LineTo (hdc, x+len*font_width, y+descent);
  1318. oldpen = SelectObject (hdc, oldpen);
  1319. DeleteObject (oldpen);
  1320. }
  1321. if (attr & ATTR_PASCURS) {
  1322. POINT pts[5];
  1323. HPEN oldpen;
  1324. pts[0].x = pts[1].x = pts[4].x = x;
  1325. pts[2].x = pts[3].x = x+font_width-1;
  1326. pts[0].y = pts[3].y = pts[4].y = y;
  1327. pts[1].y = pts[2].y = y+font_height-1;
  1328. oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
  1329. Polyline (hdc, pts, 5);
  1330. oldpen = SelectObject (hdc, oldpen);
  1331. DeleteObject (oldpen);
  1332. }
  1333. }
  1334. /*
  1335. * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII
  1336. * codes. Returns number of bytes used.
  1337. */
  1338. static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
  1339. unsigned char *p = output;
  1340. BYTE keystate[256];
  1341. int ret, code;
  1342. int cancel_alt = FALSE;
  1343. /*
  1344. * Get hold of the keyboard state, because we'll need it a few
  1345. * times shortly.
  1346. */
  1347. ret = GetKeyboardState(keystate);
  1348. /*
  1349. * Record that we pressed key so the scroll window can be reset, but
  1350. * be careful to avoid Shift-UP/Down
  1351. */
  1352. if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
  1353. seen_key_event = 1;
  1354. }
  1355. /*
  1356. * Windows does not always want to distinguish left and right
  1357. * Alt or Control keys. Thus we keep track of them ourselves.
  1358. * See also the WM_KEYUP handler.
  1359. */
  1360. if (wParam == VK_MENU) {
  1361. if (lParam & 0x1000000) keystate[VK_RMENU] = 0x80;
  1362. else keystate[VK_LMENU] = 0x80;
  1363. SetKeyboardState (keystate);
  1364. return 0;
  1365. }
  1366. if (wParam == VK_CONTROL) {
  1367. if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0x80;
  1368. else keystate[VK_LCONTROL] = 0x80;
  1369. SetKeyboardState (keystate);
  1370. return 0;
  1371. }
  1372. /*
  1373. * Prepend ESC, and cancel ALT, if ALT was pressed at the time
  1374. * and it wasn't AltGr.
  1375. */
  1376. if (lParam & 0x20000000 && (keystate[VK_LMENU] & 0x80)) {
  1377. *p++ = 0x1B;
  1378. cancel_alt = TRUE;
  1379. }
  1380. /*
  1381. * NetHack keypad mode. This may conflict with Shift-PgUp/PgDn,
  1382. * so we do it first.
  1383. */
  1384. if (cfg.nethack_keypad) {
  1385. int shift = keystate[VK_SHIFT] & 0x80;
  1386. /*
  1387. * NB the shifted versions only work with numlock off.
  1388. */
  1389. switch ( (lParam >> 16) & 0x1FF ) {
  1390. case 0x047: *p++ = shift ? 'Y' : 'y'; return p - output;
  1391. case 0x048: *p++ = shift ? 'K' : 'k'; return p - output;
  1392. case 0x049: *p++ = shift ? 'U' : 'u'; return p - output;
  1393. case 0x04B: *p++ = shift ? 'H' : 'h'; return p - output;
  1394. case 0x04C: *p++ = '.'; return p - output;
  1395. case 0x04D: *p++ = shift ? 'L' : 'l'; return p - output;
  1396. case 0x04F: *p++ = shift ? 'B' : 'b'; return p - output;
  1397. case 0x050: *p++ = shift ? 'J' : 'j'; return p - output;
  1398. case 0x051: *p++ = shift ? 'N' : 'n'; return p - output;
  1399. case 0x053: *p++ = '.'; return p - output;
  1400. }
  1401. }
  1402. /*
  1403. * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
  1404. * events: we'll deal with those now.
  1405. */
  1406. if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) {
  1407. SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
  1408. return 0;
  1409. }
  1410. if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) {
  1411. SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
  1412. return 0;
  1413. }
  1414. if ((lParam & 0x20000000) && wParam == VK_F4 && cfg.alt_f4) {
  1415. return -1;
  1416. }
  1417. if ((lParam & 0x20000000) && wParam == VK_SPACE && cfg.alt_space) {
  1418. SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
  1419. return -1;
  1420. }
  1421. /*
  1422. * In general, the strategy is to see what the Windows keymap
  1423. * translation has to say for itself, and then process function
  1424. * keys and suchlike ourselves if that fails. But first we must
  1425. * deal with the small number of special cases which the
  1426. * Windows keymap translator thinks it can do but gets wrong.
  1427. *
  1428. * First special case: we might want the Backspace key to send
  1429. * 0x7F not 0x08.
  1430. */
  1431. if (wParam == VK_BACK) {
  1432. *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
  1433. return p - output;
  1434. }
  1435. /*
  1436. * Control-Space should send ^@ (0x00), not Space.
  1437. */
  1438. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) {
  1439. *p++ = 0x00;
  1440. return p - output;
  1441. }
  1442. if (app_keypad_keys) {
  1443. /*
  1444. * If we're in applications keypad mode, we have to process it
  1445. * before char-map translation, because it will pre-empt lots
  1446. * of stuff, even if NumLock is off.
  1447. */
  1448. if (ret) {
  1449. /*
  1450. * Hack to ensure NumLock doesn't interfere with
  1451. * perception of Shift for Keypad Plus. I don't pretend
  1452. * to understand this, but it seems to work as is.
  1453. * Leave it alone, or die.
  1454. */
  1455. keystate[VK_NUMLOCK] = 0;
  1456. SetKeyboardState (keystate);
  1457. GetKeyboardState (keystate);
  1458. }
  1459. switch ( (lParam >> 16) & 0x1FF ) {
  1460. case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output;
  1461. case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output;
  1462. case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output;
  1463. case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output;
  1464. case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output;
  1465. case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output;
  1466. case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output;
  1467. case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output;
  1468. case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output;
  1469. case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output;
  1470. case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */
  1471. p += sprintf((char *)p,
  1472. (ret && (keystate[VK_SHIFT] & 0x80)) ?
  1473. "\x1BOm" : "\x1BOl");
  1474. return p - output;
  1475. case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output;
  1476. case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output;
  1477. case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output;
  1478. case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output;
  1479. case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output;
  1480. case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output;
  1481. }
  1482. }
  1483. /*
  1484. * Shift-Tab should send ESC [ Z.
  1485. */
  1486. if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_TAB) {
  1487. *p++ = 0x1B; /* ESC */
  1488. *p++ = '[';
  1489. *p++ = 'Z';
  1490. return p - output;
  1491. }
  1492. /*
  1493. * Before doing Windows charmap translation, remove LeftALT
  1494. * from the keymap, since its sole effect should be to prepend
  1495. * ESC, which we've already done. Note that removal of LeftALT
  1496. * has to happen _after_ the above call to SetKeyboardState, or
  1497. * dire things will befall.
  1498. */
  1499. if (cancel_alt) {
  1500. keystate[VK_MENU] = keystate[VK_RMENU];
  1501. keystate[VK_LMENU] = 0;
  1502. }
  1503. /*
  1504. * Attempt the Windows char-map translation.
  1505. */
  1506. if (ret) {
  1507. WORD chr;
  1508. int r;
  1509. BOOL capsOn=keystate[VK_CAPITAL] !=0;
  1510. /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
  1511. if(cfg.xlat_capslockcyr)
  1512. keystate[VK_CAPITAL] = 0;
  1513. r = ToAscii (wParam, (lParam >> 16) & 0xFF,
  1514. keystate, &chr, 0);
  1515. if(capsOn)
  1516. chr = xlat_latkbd2win((unsigned char)(chr & 0xFF));
  1517. if (r == 1) {
  1518. *p++ = xlat_kbd2tty((unsigned char)(chr & 0xFF));
  1519. return p - output;
  1520. }
  1521. }
  1522. /*
  1523. * OK, we haven't had a key code from the keymap translation.
  1524. * We'll try our various special cases and function keys, and
  1525. * then give up. (There's nothing wrong with giving up:
  1526. * Scrollock, Pause/Break, and of course the various buckybit
  1527. * keys all produce KEYDOWN events that we really _do_ want to
  1528. * ignore.)
  1529. */
  1530. /*
  1531. * Control-2 should return ^@ (0x00), Control-6 should return
  1532. * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
  1533. * the DOS keyboard handling did it, and we have nothing better
  1534. * to do with the key combo in question, we'll also map
  1535. * Control-Backquote to ^\ (0x1C).
  1536. *
  1537. * In addition a real VT100 maps Ctrl-3/4/5/7 and 8.
  1538. */
  1539. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') {
  1540. *p++ = 0x00;
  1541. return p - output;
  1542. }
  1543. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '3') {
  1544. *p++ = 0x1B;
  1545. return p - output;
  1546. }
  1547. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '4') {
  1548. *p++ = 0x1C;
  1549. return p - output;
  1550. }
  1551. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '5') {
  1552. *p++ = 0x1D;
  1553. return p - output;
  1554. }
  1555. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') {
  1556. *p++ = 0x1E;
  1557. return p - output;
  1558. }
  1559. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '7') {
  1560. *p++ = 0x1F;
  1561. return p - output;
  1562. }
  1563. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '8') {
  1564. *p++ = 0x7F;
  1565. return p - output;
  1566. }
  1567. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) {
  1568. *p++ = 0x1F;
  1569. return p - output;
  1570. }
  1571. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) {
  1572. *p++ = 0x1C;
  1573. return p - output;
  1574. }
  1575. /*
  1576. * First, all the keys that do tilde codes. (ESC '[' nn '~',
  1577. * for integer decimal nn.)
  1578. *
  1579. * We also deal with the weird ones here. Linux VCs replace F1
  1580. * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
  1581. * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
  1582. * respectively.
  1583. */
  1584. code = 0;
  1585. switch (wParam) {
  1586. case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
  1587. case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
  1588. case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
  1589. case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
  1590. case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
  1591. case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
  1592. case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
  1593. case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
  1594. case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
  1595. case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
  1596. case VK_F11: code = 23; break;
  1597. case VK_F12: code = 24; break;
  1598. case VK_HOME: code = 1; break;
  1599. case VK_INSERT: code = 2; break;
  1600. case VK_DELETE: code = 3; break;
  1601. case VK_END: code = 4; break;
  1602. case VK_PRIOR: code = 5; break;
  1603. case VK_NEXT: code = 6; break;
  1604. }
  1605. if (cfg.linux_funkeys && code >= 11 && code <= 15) {
  1606. p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
  1607. return p - output;
  1608. }
  1609. if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
  1610. p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
  1611. return p - output;
  1612. }
  1613. if (code) {
  1614. p += sprintf((char *)p, "\x1B[%d~", code);
  1615. return p - output;
  1616. }
  1617. /*
  1618. * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
  1619. * some reason seems to send VK_CLEAR to Windows...).
  1620. */
  1621. switch (wParam) {
  1622. case VK_UP:
  1623. p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A");
  1624. return p - output;
  1625. case VK_DOWN:
  1626. p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B");
  1627. return p - output;
  1628. case VK_RIGHT:
  1629. p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C");
  1630. return p - output;
  1631. case VK_LEFT:
  1632. p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D");
  1633. return p - output;
  1634. case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output;
  1635. }
  1636. return 0;
  1637. }
  1638. void set_title (char *title) {
  1639. sfree (window_name);
  1640. window_name = smalloc(1+strlen(title));
  1641. strcpy (window_name, title);
  1642. if (cfg.win_name_always || !IsIconic(hwnd))
  1643. SetWindowText (hwnd, title);
  1644. }
  1645. void set_icon (char *title) {
  1646. sfree (icon_name);
  1647. icon_name = smalloc(1+strlen(title));
  1648. strcpy (icon_name, title);
  1649. if (!cfg.win_name_always && IsIconic(hwnd))
  1650. SetWindowText (hwnd, title);
  1651. }
  1652. void set_sbar (int total, int start, int page) {
  1653. SCROLLINFO si;
  1654. si.cbSize = sizeof(si);
  1655. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
  1656. si.nMin = 0;
  1657. si.nMax = total - 1;
  1658. si.nPage = page;
  1659. si.nPos = start;
  1660. if (hwnd)
  1661. SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
  1662. }
  1663. Context get_ctx(void) {
  1664. HDC hdc;
  1665. if (hwnd) {
  1666. hdc = GetDC (hwnd);
  1667. if (hdc && pal)
  1668. SelectPalette (hdc, pal, FALSE);
  1669. return hdc;
  1670. } else
  1671. return NULL;
  1672. }
  1673. void free_ctx (Context ctx) {
  1674. SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
  1675. ReleaseDC (hwnd, ctx);
  1676. }
  1677. static void real_palette_set (int n, int r, int g, int b) {
  1678. if (pal) {
  1679. logpal->palPalEntry[n].peRed = r;
  1680. logpal->palPalEntry[n].peGreen = g;
  1681. logpal->palPalEntry[n].peBlue = b;
  1682. logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
  1683. colours[n] = PALETTERGB(r, g, b);
  1684. SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
  1685. } else
  1686. colours[n] = RGB(r, g, b);
  1687. }
  1688. void palette_set (int n, int r, int g, int b) {
  1689. static const int first[21] = {
  1690. 0, 2, 4, 6, 8, 10, 12, 14,
  1691. 1, 3, 5, 7, 9, 11, 13, 15,
  1692. 16, 17, 18, 20, 22
  1693. };
  1694. real_palette_set (first[n], r, g, b);
  1695. if (first[n] >= 18)
  1696. real_palette_set (first[n]+1, r, g, b);
  1697. if (pal) {
  1698. HDC hdc = get_ctx();
  1699. UnrealizeObject (pal);
  1700. RealizePalette (hdc);
  1701. free_ctx (hdc);
  1702. }
  1703. }
  1704. void palette_reset (void) {
  1705. int i;
  1706. for (i = 0; i < NCOLOURS; i++) {
  1707. if (pal) {
  1708. logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
  1709. logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
  1710. logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
  1711. logpal->palPalEntry[i].peFlags = 0;
  1712. colours[i] = PALETTERGB(defpal[i].rgbtRed,
  1713. defpal[i].rgbtGreen,
  1714. defpal[i].rgbtBlue);
  1715. } else
  1716. colours[i] = RGB(defpal[i].rgbtRed,
  1717. defpal[i].rgbtGreen,
  1718. defpal[i].rgbtBlue);
  1719. }
  1720. if (pal) {
  1721. HDC hdc;
  1722. SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
  1723. hdc = get_ctx();
  1724. RealizePalette (hdc);
  1725. free_ctx (hdc);
  1726. }
  1727. }
  1728. void write_clip (void *data, int len) {
  1729. HGLOBAL clipdata;
  1730. void *lock;
  1731. clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
  1732. if (!clipdata)
  1733. return;
  1734. lock = GlobalLock (clipdata);
  1735. if (!lock)
  1736. return;
  1737. memcpy (lock, data, len);
  1738. ((unsigned char *) lock) [len] = 0;
  1739. GlobalUnlock (clipdata);
  1740. SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
  1741. if (OpenClipboard (hwnd)) {
  1742. EmptyClipboard();
  1743. SetClipboardData (CF_TEXT, clipdata);
  1744. CloseClipboard();
  1745. } else
  1746. GlobalFree (clipdata);
  1747. SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
  1748. }
  1749. void get_clip (void **p, int *len) {
  1750. static HGLOBAL clipdata = NULL;
  1751. if (!p) {
  1752. if (clipdata)
  1753. GlobalUnlock (clipdata);
  1754. clipdata = NULL;
  1755. return;
  1756. } else {
  1757. if (OpenClipboard (NULL)) {
  1758. clipdata = GetClipboardData (CF_TEXT);
  1759. CloseClipboard();
  1760. if (clipdata) {
  1761. *p = GlobalLock (clipdata);
  1762. if (*p) {
  1763. *len = strlen(*p);
  1764. return;
  1765. }
  1766. }
  1767. }
  1768. }
  1769. *p = NULL;
  1770. *len = 0;
  1771. }
  1772. /*
  1773. * Move `lines' lines from position `from' to position `to' in the
  1774. * window.
  1775. */
  1776. void optimised_move (int to, int from, int lines) {
  1777. RECT r;
  1778. int min, max;
  1779. min = (to < from ? to : from);
  1780. max = to + from - min;
  1781. r.left = 0; r.right = cols * font_width;
  1782. r.top = min * font_height; r.bottom = (max+lines) * font_height;
  1783. ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
  1784. }
  1785. /*
  1786. * Print a message box and perform a fatal exit.
  1787. */
  1788. void fatalbox(char *fmt, ...) {
  1789. va_list ap;
  1790. char stuff[200];
  1791. va_start(ap, fmt);
  1792. vsprintf(stuff, fmt, ap);
  1793. va_end(ap);
  1794. MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
  1795. exit(1);
  1796. }
  1797. /*
  1798. * Beep.
  1799. */
  1800. void beep(void) {
  1801. MessageBeep(MB_OK);
  1802. }