window.c 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574
  1. #include <windows.h>
  2. #include <commctrl.h>
  3. #include <winsock.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #define PUTTY_DO_GLOBALS /* actually _define_ globals */
  7. #include "putty.h"
  8. #include "win_res.h"
  9. #define IDM_SHOWLOG 501
  10. #define IDM_NEWSESS 502
  11. #define IDM_DUPSESS 503
  12. #define IDM_RECONF 504
  13. #define IDM_CLRSB 505
  14. #define IDM_RESET 506
  15. #define IDM_TEL_AYT 507
  16. #define IDM_TEL_BRK 508
  17. #define IDM_TEL_SYNCH 509
  18. #define IDM_TEL_EC 510
  19. #define IDM_TEL_EL 511
  20. #define IDM_TEL_GA 512
  21. #define IDM_TEL_NOP 513
  22. #define IDM_TEL_ABORT 514
  23. #define IDM_TEL_AO 515
  24. #define IDM_TEL_IP 516
  25. #define IDM_TEL_SUSP 517
  26. #define IDM_TEL_EOR 518
  27. #define IDM_TEL_EOF 519
  28. #define IDM_ABOUT 520
  29. #define WM_IGNORE_SIZE (WM_USER + 2)
  30. #define WM_IGNORE_CLIP (WM_USER + 3)
  31. static int WINAPI WndProc (HWND, UINT, WPARAM, LPARAM);
  32. static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output);
  33. static void cfgtopalette(void);
  34. static void init_palette(void);
  35. static void init_fonts(void);
  36. static int extra_width, extra_height;
  37. #define FONT_NORMAL 0
  38. #define FONT_BOLD 1
  39. #define FONT_UNDERLINE 2
  40. #define FONT_BOLDUND 3
  41. #define FONT_OEM 4
  42. #define FONT_OEMBOLD 5
  43. #define FONT_OEMBOLDUND 6
  44. #define FONT_OEMUND 7
  45. static HFONT fonts[8];
  46. static enum {
  47. BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
  48. } bold_mode;
  49. static enum {
  50. UND_LINE, UND_FONT
  51. } und_mode;
  52. static int descent;
  53. #define NCOLOURS 24
  54. static COLORREF colours[NCOLOURS];
  55. static HPALETTE pal;
  56. static LPLOGPALETTE logpal;
  57. static RGBTRIPLE defpal[NCOLOURS];
  58. static HWND hwnd;
  59. static int dbltime, lasttime, lastact;
  60. static Mouse_Button lastbtn;
  61. static char *window_name, *icon_name;
  62. int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
  63. static char appname[] = "PuTTY";
  64. WORD winsock_ver;
  65. WSADATA wsadata;
  66. WNDCLASS wndclass;
  67. MSG msg;
  68. int guess_width, guess_height;
  69. winsock_ver = MAKEWORD(1, 1);
  70. if (WSAStartup(winsock_ver, &wsadata)) {
  71. MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
  72. MB_OK | MB_ICONEXCLAMATION);
  73. return 1;
  74. }
  75. if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
  76. MessageBox(NULL, "WinSock version is incompatible with 1.1",
  77. "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
  78. WSACleanup();
  79. return 1;
  80. }
  81. /* WISHLIST: maybe allow config tweaking even if winsock not present? */
  82. InitCommonControls();
  83. /*
  84. * Process the command line.
  85. */
  86. {
  87. char *p;
  88. do_defaults(NULL);
  89. p = cmdline;
  90. while (*p && isspace(*p)) p++;
  91. /*
  92. * An initial @ means to activate a saved session.
  93. */
  94. if (*p == '@') {
  95. do_defaults (p+1);
  96. if (!*cfg.host && !do_config()) {
  97. WSACleanup();
  98. return 0;
  99. }
  100. } else if (*p == '&') {
  101. /*
  102. * An initial & means we've been given a command line
  103. * containing the hex value of a HANDLE for a file
  104. * mapping object, which we must then extract as a
  105. * config.
  106. */
  107. HANDLE filemap;
  108. Config *cp;
  109. if (sscanf(p+1, "%x", &filemap) == 1 &&
  110. (cp = MapViewOfFile(filemap, FILE_MAP_READ,
  111. 0, 0, sizeof(Config))) != NULL) {
  112. cfg = *cp;
  113. UnmapViewOfFile(cp);
  114. CloseHandle(filemap);
  115. } else if (!do_config()) {
  116. WSACleanup();
  117. return 0;
  118. }
  119. } else if (*p) {
  120. char *q = p;
  121. while (*p && !isspace(*p)) p++;
  122. if (*p)
  123. *p++ = '\0';
  124. strncpy (cfg.host, q, sizeof(cfg.host)-1);
  125. cfg.host[sizeof(cfg.host)-1] = '\0';
  126. while (*p && isspace(*p)) p++;
  127. if (*p)
  128. cfg.port = atoi(p);
  129. else
  130. cfg.port = -1;
  131. } else {
  132. if (!do_config()) {
  133. WSACleanup();
  134. return 0;
  135. }
  136. }
  137. }
  138. back = (cfg.protocol == PROT_SSH ? &ssh_backend : &telnet_backend);
  139. if (!prev) {
  140. wndclass.style = 0;
  141. wndclass.lpfnWndProc = WndProc;
  142. wndclass.cbClsExtra = 0;
  143. wndclass.cbWndExtra = 0;
  144. wndclass.hInstance = inst;
  145. wndclass.hIcon = LoadIcon (inst,
  146. MAKEINTRESOURCE(IDI_MAINICON));
  147. wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
  148. wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
  149. wndclass.lpszMenuName = NULL;
  150. wndclass.lpszClassName = appname;
  151. RegisterClass (&wndclass);
  152. }
  153. hwnd = NULL;
  154. savelines = cfg.savelines;
  155. term_init();
  156. cfgtopalette();
  157. /*
  158. * Guess some defaults for the window size. This all gets
  159. * updated later, so we don't really care too much. However, we
  160. * do want the font width/height guesses to correspond to a
  161. * large font rather than a small one...
  162. */
  163. font_width = 10;
  164. font_height = 20;
  165. extra_width = 25;
  166. extra_height = 28;
  167. term_size (cfg.height, cfg.width, cfg.savelines);
  168. guess_width = extra_width + font_width * cols;
  169. guess_height = extra_height + font_height * rows;
  170. {
  171. RECT r;
  172. HWND w = GetDesktopWindow();
  173. GetWindowRect (w, &r);
  174. if (guess_width > r.right - r.left)
  175. guess_width = r.right - r.left;
  176. if (guess_height > r.bottom - r.top)
  177. guess_height = r.bottom - r.top;
  178. }
  179. hwnd = CreateWindow (appname, appname,
  180. WS_OVERLAPPEDWINDOW | WS_VSCROLL,
  181. CW_USEDEFAULT, CW_USEDEFAULT,
  182. guess_width, guess_height,
  183. NULL, NULL, inst, NULL);
  184. /*
  185. * Initialise the fonts, simultaneously correcting the guesses
  186. * for font_{width,height}.
  187. */
  188. bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
  189. und_mode = UND_FONT;
  190. init_fonts();
  191. /*
  192. * Correct the guesses for extra_{width,height}.
  193. */
  194. {
  195. RECT cr, wr;
  196. GetWindowRect (hwnd, &wr);
  197. GetClientRect (hwnd, &cr);
  198. extra_width = wr.right - wr.left - cr.right + cr.left;
  199. extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
  200. }
  201. /*
  202. * Resize the window, now we know what size we _really_ want it
  203. * to be.
  204. */
  205. guess_width = extra_width + font_width * cols;
  206. guess_height = extra_height + font_height * rows;
  207. SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
  208. SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
  209. SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
  210. /*
  211. * Initialise the scroll bar.
  212. */
  213. {
  214. SCROLLINFO si;
  215. si.cbSize = sizeof(si);
  216. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
  217. si.nMin = 0;
  218. si.nMax = rows-1;
  219. si.nPage = rows;
  220. si.nPos = 0;
  221. SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
  222. }
  223. /*
  224. * Start up the telnet connection.
  225. */
  226. {
  227. char *error;
  228. char msg[1024];
  229. char *realhost;
  230. error = back->init (hwnd, cfg.host, cfg.port, &realhost);
  231. if (error) {
  232. sprintf(msg, "Unable to open connection:\n%s", error);
  233. MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
  234. return 0;
  235. }
  236. window_name = icon_name = NULL;
  237. sprintf(msg, "PuTTY: %s", realhost);
  238. set_title (msg);
  239. set_icon (msg);
  240. }
  241. /*
  242. * Set up the input and output buffers.
  243. */
  244. inbuf_reap = inbuf_head = 0;
  245. outbuf_reap = outbuf_head = 0;
  246. /*
  247. * Prepare the mouse handler.
  248. */
  249. lastact = MA_NOTHING;
  250. lastbtn = MB_NOTHING;
  251. dbltime = GetDoubleClickTime();
  252. /*
  253. * Set up the session-control options on the system menu.
  254. */
  255. {
  256. HMENU m = GetSystemMenu (hwnd, FALSE);
  257. HMENU p;
  258. AppendMenu (m, MF_SEPARATOR, 0, 0);
  259. if (cfg.protocol == PROT_TELNET) {
  260. p = CreateMenu();
  261. AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
  262. AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
  263. AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
  264. AppendMenu (p, MF_SEPARATOR, 0, 0);
  265. AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
  266. AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
  267. AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
  268. AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
  269. AppendMenu (p, MF_SEPARATOR, 0, 0);
  270. AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
  271. AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
  272. AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
  273. AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
  274. AppendMenu (p, MF_SEPARATOR, 0, 0);
  275. AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
  276. AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
  277. AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
  278. AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "Show Negotiation");
  279. AppendMenu (m, MF_SEPARATOR, 0, 0);
  280. }
  281. AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "New Session");
  282. AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "Duplicate Session");
  283. AppendMenu (m, MF_ENABLED, IDM_RECONF, "Change Settings");
  284. AppendMenu (m, MF_SEPARATOR, 0, 0);
  285. AppendMenu (m, MF_ENABLED, IDM_CLRSB, "Clear Scrollback");
  286. AppendMenu (m, MF_ENABLED, IDM_RESET, "Reset Terminal");
  287. AppendMenu (m, MF_SEPARATOR, 0, 0);
  288. AppendMenu (m, MF_ENABLED, IDM_ABOUT, "About PuTTY");
  289. }
  290. /*
  291. * Finally show the window!
  292. */
  293. ShowWindow (hwnd, show);
  294. /*
  295. * Set the palette up.
  296. */
  297. pal = NULL;
  298. logpal = NULL;
  299. init_palette();
  300. has_focus = (GetForegroundWindow() == hwnd);
  301. UpdateWindow (hwnd);
  302. while (GetMessage (&msg, NULL, 0, 0)) {
  303. DispatchMessage (&msg);
  304. if (inbuf_reap != inbuf_head)
  305. term_out();
  306. /* In idle moments, do a full screen update */
  307. if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
  308. term_update();
  309. }
  310. /*
  311. * Clean up.
  312. */
  313. {
  314. int i;
  315. for (i=0; i<8; i++)
  316. if (fonts[i])
  317. DeleteObject(fonts[i]);
  318. }
  319. sfree(logpal);
  320. if (pal)
  321. DeleteObject(pal);
  322. WSACleanup();
  323. if (cfg.protocol == PROT_SSH)
  324. random_save_seed();
  325. return msg.wParam;
  326. }
  327. /*
  328. * Copy the colour palette from the configuration data into defpal.
  329. * This is non-trivial because the colour indices are different.
  330. */
  331. static void cfgtopalette(void) {
  332. int i;
  333. static const int ww[] = {
  334. 6, 7, 8, 9, 10, 11, 12, 13,
  335. 14, 15, 16, 17, 18, 19, 20, 21,
  336. 0, 1, 2, 3, 4, 4, 5, 5
  337. };
  338. for (i=0; i<24; i++) {
  339. int w = ww[i];
  340. defpal[i].rgbtRed = cfg.colours[w][0];
  341. defpal[i].rgbtGreen = cfg.colours[w][1];
  342. defpal[i].rgbtBlue = cfg.colours[w][2];
  343. }
  344. }
  345. /*
  346. * Set up the colour palette.
  347. */
  348. static void init_palette(void) {
  349. int i;
  350. HDC hdc = GetDC (hwnd);
  351. if (hdc) {
  352. if (cfg.try_palette &&
  353. GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
  354. logpal = smalloc(sizeof(*logpal)
  355. - sizeof(logpal->palPalEntry)
  356. + NCOLOURS * sizeof(PALETTEENTRY));
  357. logpal->palVersion = 0x300;
  358. logpal->palNumEntries = NCOLOURS;
  359. for (i = 0; i < NCOLOURS; i++) {
  360. logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
  361. logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
  362. logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
  363. logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
  364. }
  365. pal = CreatePalette (logpal);
  366. if (pal) {
  367. SelectPalette (hdc, pal, FALSE);
  368. RealizePalette (hdc);
  369. SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
  370. FALSE);
  371. }
  372. }
  373. ReleaseDC (hwnd, hdc);
  374. }
  375. if (pal)
  376. for (i=0; i<NCOLOURS; i++)
  377. colours[i] = PALETTERGB(defpal[i].rgbtRed,
  378. defpal[i].rgbtGreen,
  379. defpal[i].rgbtBlue);
  380. else
  381. for(i=0; i<NCOLOURS; i++)
  382. colours[i] = RGB(defpal[i].rgbtRed,
  383. defpal[i].rgbtGreen,
  384. defpal[i].rgbtBlue);
  385. }
  386. /*
  387. * Initialise all the fonts we will need. There may be as many as
  388. * eight or as few as one. We also:
  389. *
  390. * - check the font width and height, correcting our guesses if
  391. * necessary.
  392. *
  393. * - verify that the bold font is the same width as the ordinary
  394. * one, and engage shadow bolding if not.
  395. *
  396. * - verify that the underlined font is the same width as the
  397. * ordinary one (manual underlining by means of line drawing can
  398. * be done in a pinch).
  399. *
  400. * - verify, in OEM/ANSI combined mode, that the OEM and ANSI base
  401. * fonts are the same size, and shift to OEM-only mode if not.
  402. */
  403. static void init_fonts(void) {
  404. TEXTMETRIC tm;
  405. int i, j;
  406. int widths[5];
  407. HDC hdc;
  408. int fw_dontcare, fw_bold;
  409. for (i=0; i<8; i++)
  410. fonts[i] = NULL;
  411. if (cfg.fontisbold) {
  412. fw_dontcare = FW_BOLD;
  413. fw_bold = FW_BLACK;
  414. } else {
  415. fw_dontcare = FW_DONTCARE;
  416. fw_bold = FW_BOLD;
  417. }
  418. #define f(i,c,w,u) \
  419. fonts[i] = CreateFont (cfg.fontheight, 0, 0, 0, w, FALSE, u, FALSE, \
  420. c, OUT_DEFAULT_PRECIS, \
  421. CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
  422. FIXED_PITCH | FF_DONTCARE, cfg.font)
  423. if (cfg.vtmode != VT_OEMONLY) {
  424. f(FONT_NORMAL, ANSI_CHARSET, fw_dontcare, FALSE);
  425. f(FONT_UNDERLINE, ANSI_CHARSET, fw_dontcare, TRUE);
  426. }
  427. if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_OEMONLY) {
  428. f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
  429. f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
  430. }
  431. if (bold_mode == BOLD_FONT) {
  432. if (cfg.vtmode != VT_OEMONLY) {
  433. f(FONT_BOLD, ANSI_CHARSET, fw_bold, FALSE);
  434. f(FONT_BOLDUND, ANSI_CHARSET, fw_bold, TRUE);
  435. }
  436. if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_OEMONLY) {
  437. f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
  438. f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
  439. }
  440. } else {
  441. fonts[FONT_BOLD] = fonts[FONT_BOLDUND] = NULL;
  442. fonts[FONT_OEMBOLD] = fonts[FONT_OEMBOLDUND] = NULL;
  443. }
  444. #undef f
  445. hdc = GetDC(hwnd);
  446. if (cfg.vtmode == VT_OEMONLY)
  447. j = 4;
  448. else
  449. j = 0;
  450. for (i=0; i<(cfg.vtmode == VT_OEMANSI ? 5 : 4); i++) {
  451. if (fonts[i+j]) {
  452. SelectObject (hdc, fonts[i+j]);
  453. GetTextMetrics(hdc, &tm);
  454. if (i == 0 || i == 4) {
  455. font_height = tm.tmHeight;
  456. font_width = tm.tmAveCharWidth;
  457. descent = tm.tmAscent + 1;
  458. if (descent >= font_height)
  459. descent = font_height - 1;
  460. }
  461. widths[i] = tm.tmAveCharWidth;
  462. }
  463. }
  464. ReleaseDC (hwnd, hdc);
  465. if (widths[FONT_UNDERLINE] != widths[FONT_NORMAL] ||
  466. (bold_mode == BOLD_FONT &&
  467. widths[FONT_BOLDUND] != widths[FONT_BOLD])) {
  468. und_mode = UND_LINE;
  469. DeleteObject (fonts[FONT_UNDERLINE]);
  470. if (bold_mode == BOLD_FONT)
  471. DeleteObject (fonts[FONT_BOLDUND]);
  472. }
  473. if (bold_mode == BOLD_FONT &&
  474. widths[FONT_BOLD] != widths[FONT_NORMAL]) {
  475. bold_mode = BOLD_SHADOW;
  476. DeleteObject (fonts[FONT_BOLD]);
  477. if (und_mode == UND_FONT)
  478. DeleteObject (fonts[FONT_BOLDUND]);
  479. }
  480. if (cfg.vtmode == VT_OEMANSI && widths[FONT_OEM] != widths[FONT_NORMAL]) {
  481. MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
  482. "different sizes. Using OEM-only mode instead",
  483. "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
  484. cfg.vtmode = VT_OEMONLY;
  485. for (i=0; i<4; i++)
  486. if (fonts[i])
  487. DeleteObject (fonts[i]);
  488. }
  489. }
  490. void request_resize (int w, int h) {
  491. int width = extra_width + font_width * w;
  492. int height = extra_height + font_height * h;
  493. SetWindowPos (hwnd, NULL, 0, 0, width, height,
  494. SWP_NOACTIVATE | SWP_NOCOPYBITS |
  495. SWP_NOMOVE | SWP_NOZORDER);
  496. }
  497. static void click (Mouse_Button b, int x, int y) {
  498. int thistime = GetMessageTime();
  499. if (lastbtn == b && thistime - lasttime < dbltime) {
  500. lastact = (lastact == MA_CLICK ? MA_2CLK :
  501. lastact == MA_2CLK ? MA_3CLK :
  502. lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
  503. } else {
  504. lastbtn = b;
  505. lastact = MA_CLICK;
  506. }
  507. if (lastact != MA_NOTHING)
  508. term_mouse (b, lastact, x, y);
  509. lasttime = thistime;
  510. }
  511. static int WINAPI WndProc (HWND hwnd, UINT message,
  512. WPARAM wParam, LPARAM lParam) {
  513. HDC hdc;
  514. static int ignore_size = FALSE;
  515. static int ignore_clip = FALSE;
  516. static int just_reconfigged = FALSE;
  517. switch (message) {
  518. case WM_CREATE:
  519. break;
  520. case WM_DESTROY:
  521. PostQuitMessage (0);
  522. return 0;
  523. case WM_SYSCOMMAND:
  524. switch (wParam) {
  525. case IDM_SHOWLOG:
  526. shownegot(hwnd);
  527. break;
  528. case IDM_NEWSESS:
  529. case IDM_DUPSESS:
  530. {
  531. char b[2048];
  532. char c[30], *cl;
  533. STARTUPINFO si;
  534. PROCESS_INFORMATION pi;
  535. HANDLE filemap = NULL;
  536. if (wParam == IDM_DUPSESS) {
  537. /*
  538. * Allocate a file-mapping memory chunk for the
  539. * config structure.
  540. */
  541. SECURITY_ATTRIBUTES sa;
  542. Config *p;
  543. sa.nLength = sizeof(sa);
  544. sa.lpSecurityDescriptor = NULL;
  545. sa.bInheritHandle = TRUE;
  546. filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
  547. &sa,
  548. PAGE_READWRITE,
  549. 0,
  550. sizeof(Config),
  551. NULL);
  552. if (filemap) {
  553. p = (Config *)MapViewOfFile(filemap,
  554. FILE_MAP_WRITE,
  555. 0, 0, sizeof(Config));
  556. if (p) {
  557. *p = cfg; /* structure copy */
  558. UnmapViewOfFile(p);
  559. }
  560. }
  561. sprintf(c, "putty &%08x", filemap);
  562. cl = c;
  563. } else
  564. cl = NULL;
  565. GetModuleFileName (NULL, b, sizeof(b)-1);
  566. si.cb = sizeof(si);
  567. si.lpReserved = NULL;
  568. si.lpDesktop = NULL;
  569. si.lpTitle = NULL;
  570. si.dwFlags = 0;
  571. si.cbReserved2 = 0;
  572. si.lpReserved2 = NULL;
  573. CreateProcess (b, cl, NULL, NULL, TRUE,
  574. NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
  575. if (filemap)
  576. CloseHandle(filemap);
  577. }
  578. break;
  579. case IDM_RECONF:
  580. if (!do_reconfig(hwnd))
  581. break;
  582. just_reconfigged = TRUE;
  583. {
  584. int i;
  585. for (i=0; i<8; i++)
  586. if (fonts[i])
  587. DeleteObject(fonts[i]);
  588. }
  589. bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
  590. und_mode = UND_FONT;
  591. init_fonts();
  592. sfree(logpal);
  593. if (pal)
  594. DeleteObject(pal);
  595. logpal = NULL;
  596. pal = NULL;
  597. cfgtopalette();
  598. init_palette();
  599. term_size(cfg.height, cfg.width, cfg.savelines);
  600. InvalidateRect(hwnd, NULL, TRUE);
  601. SetWindowPos (hwnd, NULL, 0, 0,
  602. extra_width + font_width * cfg.width,
  603. extra_height + font_height * cfg.height,
  604. SWP_NOACTIVATE | SWP_NOCOPYBITS |
  605. SWP_NOMOVE | SWP_NOZORDER);
  606. if (IsIconic(hwnd)) {
  607. SetWindowText (hwnd,
  608. cfg.win_name_always ? window_name : icon_name);
  609. }
  610. break;
  611. case IDM_CLRSB:
  612. term_clrsb();
  613. break;
  614. case IDM_RESET:
  615. term_pwron();
  616. break;
  617. case IDM_TEL_AYT: back->special (TS_AYT); break;
  618. case IDM_TEL_BRK: back->special (TS_BRK); break;
  619. case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
  620. case IDM_TEL_EC: back->special (TS_EC); break;
  621. case IDM_TEL_EL: back->special (TS_EL); break;
  622. case IDM_TEL_GA: back->special (TS_GA); break;
  623. case IDM_TEL_NOP: back->special (TS_NOP); break;
  624. case IDM_TEL_ABORT: back->special (TS_ABORT); break;
  625. case IDM_TEL_AO: back->special (TS_AO); break;
  626. case IDM_TEL_IP: back->special (TS_IP); break;
  627. case IDM_TEL_SUSP: back->special (TS_SUSP); break;
  628. case IDM_TEL_EOR: back->special (TS_EOR); break;
  629. case IDM_TEL_EOF: back->special (TS_EOF); break;
  630. case IDM_ABOUT:
  631. showabout (hwnd);
  632. break;
  633. }
  634. break;
  635. #define X_POS(l) ((int)(short)LOWORD(l))
  636. #define Y_POS(l) ((int)(short)HIWORD(l))
  637. #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
  638. #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
  639. case WM_LBUTTONDOWN:
  640. click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
  641. TO_CHR_Y(Y_POS(lParam)));
  642. SetCapture(hwnd);
  643. return 0;
  644. case WM_LBUTTONUP:
  645. term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
  646. TO_CHR_Y(Y_POS(lParam)));
  647. ReleaseCapture();
  648. return 0;
  649. case WM_MBUTTONDOWN:
  650. SetCapture(hwnd);
  651. click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
  652. TO_CHR_X(X_POS(lParam)),
  653. TO_CHR_Y(Y_POS(lParam)));
  654. return 0;
  655. case WM_MBUTTONUP:
  656. term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
  657. MA_RELEASE, TO_CHR_X(X_POS(lParam)),
  658. TO_CHR_Y(Y_POS(lParam)));
  659. ReleaseCapture();
  660. return 0;
  661. case WM_RBUTTONDOWN:
  662. SetCapture(hwnd);
  663. click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
  664. TO_CHR_X(X_POS(lParam)),
  665. TO_CHR_Y(Y_POS(lParam)));
  666. return 0;
  667. case WM_RBUTTONUP:
  668. term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
  669. MA_RELEASE, TO_CHR_X(X_POS(lParam)),
  670. TO_CHR_Y(Y_POS(lParam)));
  671. ReleaseCapture();
  672. return 0;
  673. case WM_MOUSEMOVE:
  674. /*
  675. * Add the mouse position and message time to the random
  676. * number noise, if we're using ssh.
  677. */
  678. if (cfg.protocol == PROT_SSH)
  679. noise_ultralight(lParam);
  680. if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
  681. Mouse_Button b;
  682. if (wParam & MK_LBUTTON)
  683. b = MB_SELECT;
  684. else if (wParam & MK_MBUTTON)
  685. b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
  686. else
  687. b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
  688. term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
  689. TO_CHR_Y(Y_POS(lParam)));
  690. }
  691. return 0;
  692. case WM_IGNORE_CLIP:
  693. ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
  694. break;
  695. case WM_DESTROYCLIPBOARD:
  696. if (!ignore_clip)
  697. term_deselect();
  698. ignore_clip = FALSE;
  699. return 0;
  700. case WM_PAINT:
  701. {
  702. PAINTSTRUCT p;
  703. hdc = BeginPaint (hwnd, &p);
  704. if (pal) {
  705. SelectPalette (hdc, pal, TRUE);
  706. RealizePalette (hdc);
  707. }
  708. term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
  709. p.rcPaint.right, p.rcPaint.bottom);
  710. SelectObject (hdc, GetStockObject(SYSTEM_FONT));
  711. SelectObject (hdc, GetStockObject(WHITE_PEN));
  712. EndPaint (hwnd, &p);
  713. }
  714. return 0;
  715. case WM_NETEVENT:
  716. {
  717. int i = back->msg (wParam, lParam);
  718. if (i < 0) {
  719. char buf[1024];
  720. switch (WSABASEERR + (-i) % 10000) {
  721. case WSAECONNRESET:
  722. sprintf(buf, "Connection reset by peer");
  723. break;
  724. default:
  725. sprintf(buf, "Unexpected network error %d", -i);
  726. break;
  727. }
  728. MessageBox(hwnd, buf, "PuTTY Fatal Error",
  729. MB_ICONERROR | MB_OK);
  730. PostQuitMessage(1);
  731. } else if (i == 0) {
  732. if (cfg.close_on_exit)
  733. PostQuitMessage(0);
  734. else {
  735. MessageBox(hwnd, "Connection closed by remote host",
  736. "PuTTY", MB_OK | MB_ICONINFORMATION);
  737. SetWindowText (hwnd, "PuTTY (inactive)");
  738. }
  739. }
  740. }
  741. return 0;
  742. case WM_SETFOCUS:
  743. has_focus = TRUE;
  744. term_out();
  745. term_update();
  746. break;
  747. case WM_KILLFOCUS:
  748. has_focus = FALSE;
  749. term_out();
  750. term_update();
  751. break;
  752. case WM_IGNORE_SIZE:
  753. ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
  754. break;
  755. case WM_SIZING:
  756. {
  757. int width, height, w, h, ew, eh;
  758. LPRECT r = (LPRECT)lParam;
  759. width = r->right - r->left - extra_width;
  760. height = r->bottom - r->top - extra_height;
  761. w = (width + font_width/2) / font_width; if (w < 1) w = 1;
  762. h = (height + font_height/2) / font_height; if (h < 1) h = 1;
  763. ew = width - w * font_width;
  764. eh = height - h * font_height;
  765. if (ew != 0) {
  766. if (wParam == WMSZ_LEFT ||
  767. wParam == WMSZ_BOTTOMLEFT ||
  768. wParam == WMSZ_TOPLEFT)
  769. r->left += ew;
  770. else
  771. r->right -= ew;
  772. }
  773. if (eh != 0) {
  774. if (wParam == WMSZ_TOP ||
  775. wParam == WMSZ_TOPRIGHT ||
  776. wParam == WMSZ_TOPLEFT)
  777. r->top += eh;
  778. else
  779. r->bottom -= eh;
  780. }
  781. if (ew || eh)
  782. return 1;
  783. else
  784. return 0;
  785. }
  786. break;
  787. case WM_SIZE:
  788. if (wParam == SIZE_MINIMIZED) {
  789. SetWindowText (hwnd,
  790. cfg.win_name_always ? window_name : icon_name);
  791. break;
  792. }
  793. if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
  794. SetWindowText (hwnd, window_name);
  795. if (!ignore_size) {
  796. int width, height, w, h;
  797. #if 0 /* we have fixed this using WM_SIZING now */
  798. int ew, eh;
  799. #endif
  800. width = LOWORD(lParam);
  801. height = HIWORD(lParam);
  802. w = width / font_width; if (w < 1) w = 1;
  803. h = height / font_height; if (h < 1) h = 1;
  804. #if 0 /* we have fixed this using WM_SIZING now */
  805. ew = width - w * font_width;
  806. eh = height - h * font_height;
  807. if (ew != 0 || eh != 0) {
  808. RECT r;
  809. GetWindowRect (hwnd, &r);
  810. SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
  811. SetWindowPos (hwnd, NULL, 0, 0,
  812. r.right - r.left - ew, r.bottom - r.top - eh,
  813. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  814. }
  815. #endif
  816. if (w != cols || h != rows || just_reconfigged) {
  817. term_invalidate();
  818. term_size (h, w, cfg.savelines);
  819. back->size();
  820. just_reconfigged = FALSE;
  821. }
  822. }
  823. ignore_size = FALSE;
  824. return 0;
  825. case WM_VSCROLL:
  826. switch (LOWORD(wParam)) {
  827. case SB_BOTTOM: term_scroll(-1, 0); break;
  828. case SB_TOP: term_scroll(+1, 0); break;
  829. case SB_LINEDOWN: term_scroll (0, +1); break;
  830. case SB_LINEUP: term_scroll (0, -1); break;
  831. case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
  832. case SB_PAGEUP: term_scroll (0, -rows/2); break;
  833. case SB_THUMBPOSITION: case SB_THUMBTRACK:
  834. term_scroll (1, HIWORD(wParam)); break;
  835. }
  836. break;
  837. case WM_PALETTECHANGED:
  838. if ((HWND) wParam != hwnd && pal != NULL) {
  839. HDC hdc = get_ctx();
  840. if (hdc) {
  841. if (RealizePalette (hdc) > 0)
  842. UpdateColors (hdc);
  843. free_ctx (hdc);
  844. }
  845. }
  846. break;
  847. case WM_QUERYNEWPALETTE:
  848. if (pal != NULL) {
  849. HDC hdc = get_ctx();
  850. if (hdc) {
  851. if (RealizePalette (hdc) > 0)
  852. UpdateColors (hdc);
  853. free_ctx (hdc);
  854. return TRUE;
  855. }
  856. }
  857. return FALSE;
  858. case WM_KEYDOWN:
  859. case WM_SYSKEYDOWN:
  860. /*
  861. * Add the scan code and keypress timing to the random
  862. * number noise, if we're using ssh.
  863. */
  864. if (cfg.protocol == PROT_SSH)
  865. noise_ultralight(lParam);
  866. /*
  867. * We don't do TranslateMessage since it disassociates the
  868. * resulting CHAR message from the KEYDOWN that sparked it,
  869. * which we occasionally don't want. Instead, we process
  870. * KEYDOWN, and call the Win32 translator functions so that
  871. * we get the translations under _our_ control.
  872. */
  873. {
  874. unsigned char buf[20];
  875. int len;
  876. len = TranslateKey (wParam, lParam, buf);
  877. back->send (buf, len);
  878. }
  879. return 0;
  880. case WM_KEYUP:
  881. case WM_SYSKEYUP:
  882. /*
  883. * We handle KEYUP ourselves in order to distinghish left
  884. * and right Alt or Control keys, which Windows won't do
  885. * right if left to itself. See also the special processing
  886. * at the top of TranslateKey.
  887. */
  888. {
  889. BYTE keystate[256];
  890. int ret = GetKeyboardState(keystate);
  891. if (ret && wParam == VK_MENU) {
  892. if (lParam & 0x1000000) keystate[VK_RMENU] = 0;
  893. else keystate[VK_LMENU] = 0;
  894. SetKeyboardState (keystate);
  895. }
  896. if (ret && wParam == VK_CONTROL) {
  897. if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0;
  898. else keystate[VK_LCONTROL] = 0;
  899. SetKeyboardState (keystate);
  900. }
  901. }
  902. /*
  903. * We don't return here, in order to allow Windows to do
  904. * its own KEYUP processing as well.
  905. */
  906. break;
  907. case WM_CHAR:
  908. case WM_SYSCHAR:
  909. /*
  910. * Nevertheless, we are prepared to deal with WM_CHAR
  911. * messages, should they crop up. So if someone wants to
  912. * post the things to us as part of a macro manoeuvre,
  913. * we're ready to cope.
  914. */
  915. {
  916. char c = wParam;
  917. back->send (&c, 1);
  918. }
  919. return 0;
  920. }
  921. return DefWindowProc (hwnd, message, wParam, lParam);
  922. }
  923. /*
  924. * Draw a line of text in the window, at given character
  925. * coordinates, in given attributes.
  926. *
  927. * We are allowed to fiddle with the contents of `text'.
  928. */
  929. void do_text (Context ctx, int x, int y, char *text, int len,
  930. unsigned long attr) {
  931. COLORREF fg, bg, t;
  932. int nfg, nbg, nfont;
  933. HDC hdc = ctx;
  934. x *= font_width;
  935. y *= font_height;
  936. if (attr & ATTR_ACTCURS) {
  937. attr &= (bold_mode == BOLD_COLOURS ? 0x200 : 0x300);
  938. attr ^= ATTR_CUR_XOR;
  939. }
  940. nfont = 0;
  941. if (cfg.vtmode == VT_OEMONLY)
  942. nfont |= FONT_OEM;
  943. /*
  944. * Map high-half characters in order to approximate ISO using
  945. * OEM character set. Characters missing are 0xC3 (Atilde) and
  946. * 0xCC (Igrave).
  947. */
  948. if (nfont & FONT_OEM) {
  949. int i;
  950. for (i=0; i<len; i++)
  951. if (text[i] >= '\xA0' && text[i] <= '\xFF') {
  952. static const char oemhighhalf[] =
  953. "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
  954. "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
  955. "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
  956. "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
  957. "\xB7\xB5\xB6\x41\x8E\x8F\x92\x80" /* C0-C7 */
  958. "\xD4\x90\xD2\xD3\x49\xD6\xD7\xD8" /* C8-CF */
  959. "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
  960. "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
  961. "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
  962. "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
  963. "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
  964. "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
  965. ;
  966. text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
  967. }
  968. }
  969. if (attr & ATTR_GBCHR) {
  970. int i;
  971. /*
  972. * GB mapping: map # to pound, and everything else stays
  973. * normal.
  974. */
  975. for (i=0; i<len; i++)
  976. if (text[i] == '#')
  977. text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
  978. } else if (attr & ATTR_LINEDRW) {
  979. int i;
  980. static const char poorman[] =
  981. "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
  982. static const char oemmap[] =
  983. "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
  984. "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
  985. /*
  986. * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
  987. * VT100 line drawing chars; everything else stays normal.
  988. */
  989. switch (cfg.vtmode) {
  990. case VT_XWINDOWS:
  991. for (i=0; i<len; i++)
  992. if (text[i] >= '\x60' && text[i] <= '\x7E')
  993. text[i] += '\x01' - '\x60';
  994. break;
  995. case VT_OEMANSI:
  996. case VT_OEMONLY:
  997. nfont |= FONT_OEM;
  998. for (i=0; i<len; i++)
  999. if (text[i] >= '\x60' && text[i] <= '\x7E')
  1000. text[i] = oemmap[(unsigned char)text[i] - 0x60];
  1001. break;
  1002. case VT_POORMAN:
  1003. for (i=0; i<len; i++)
  1004. if (text[i] >= '\x60' && text[i] <= '\x7E')
  1005. text[i] = poorman[(unsigned char)text[i] - 0x60];
  1006. break;
  1007. }
  1008. }
  1009. nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
  1010. nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
  1011. if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
  1012. nfont |= FONT_BOLD;
  1013. if (und_mode == UND_FONT && (attr & ATTR_UNDER))
  1014. nfont |= FONT_UNDERLINE;
  1015. if (attr & ATTR_REVERSE) {
  1016. t = nfg; nfg = nbg; nbg = t;
  1017. }
  1018. if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
  1019. nfg++;
  1020. fg = colours[nfg];
  1021. bg = colours[nbg];
  1022. SelectObject (hdc, fonts[nfont]);
  1023. SetTextColor (hdc, fg);
  1024. SetBkColor (hdc, bg);
  1025. SetBkMode (hdc, OPAQUE);
  1026. TextOut (hdc, x, y, text, len);
  1027. if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
  1028. SetBkMode (hdc, TRANSPARENT);
  1029. TextOut (hdc, x-1, y, text, len);
  1030. }
  1031. if (und_mode == UND_LINE && (attr & ATTR_UNDER)) {
  1032. HPEN oldpen;
  1033. oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
  1034. MoveToEx (hdc, x, y+descent, NULL);
  1035. LineTo (hdc, x+len*font_width, y+descent);
  1036. oldpen = SelectObject (hdc, oldpen);
  1037. DeleteObject (oldpen);
  1038. }
  1039. if (attr & ATTR_PASCURS) {
  1040. POINT pts[5];
  1041. HPEN oldpen;
  1042. pts[0].x = pts[1].x = pts[4].x = x;
  1043. pts[2].x = pts[3].x = x+font_width-1;
  1044. pts[0].y = pts[3].y = pts[4].y = y;
  1045. pts[1].y = pts[2].y = y+font_height-1;
  1046. oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
  1047. Polyline (hdc, pts, 5);
  1048. oldpen = SelectObject (hdc, oldpen);
  1049. DeleteObject (oldpen);
  1050. }
  1051. }
  1052. /*
  1053. * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII
  1054. * codes. Returns number of bytes used.
  1055. */
  1056. static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
  1057. unsigned char *p = output;
  1058. BYTE keystate[256];
  1059. int ret, code;
  1060. int cancel_alt = FALSE;
  1061. /*
  1062. * Get hold of the keyboard state, because we'll need it a few
  1063. * times shortly.
  1064. */
  1065. ret = GetKeyboardState(keystate);
  1066. /*
  1067. * Windows does not always want to distinguish left and right
  1068. * Alt or Control keys. Thus we keep track of them ourselves.
  1069. * See also the WM_KEYUP handler.
  1070. */
  1071. if (wParam == VK_MENU) {
  1072. if (lParam & 0x1000000) keystate[VK_RMENU] = 0x80;
  1073. else keystate[VK_LMENU] = 0x80;
  1074. SetKeyboardState (keystate);
  1075. return 0;
  1076. }
  1077. if (wParam == VK_CONTROL) {
  1078. if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0x80;
  1079. else keystate[VK_LCONTROL] = 0x80;
  1080. SetKeyboardState (keystate);
  1081. return 0;
  1082. }
  1083. /*
  1084. * Prepend ESC, and cancel ALT, if ALT was pressed at the time
  1085. * and it wasn't AltGr.
  1086. */
  1087. if (lParam & 0x20000000 && (keystate[VK_LMENU] & 0x80)) {
  1088. *p++ = 0x1B;
  1089. cancel_alt = TRUE;
  1090. }
  1091. /*
  1092. * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
  1093. * events: we'll deal with those now.
  1094. */
  1095. if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) {
  1096. SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
  1097. return 0;
  1098. }
  1099. if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) {
  1100. SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
  1101. return 0;
  1102. }
  1103. if ((lParam & 0x20000000) && wParam == VK_F4) {
  1104. SendMessage (hwnd, WM_DESTROY, 0, 0);
  1105. return 0;
  1106. }
  1107. /*
  1108. * In general, the strategy is to see what the Windows keymap
  1109. * translation has to say for itself, and then process function
  1110. * keys and suchlike ourselves if that fails. But first we must
  1111. * deal with the small number of special cases which the
  1112. * Windows keymap translator thinks it can do but gets wrong.
  1113. *
  1114. * First special case: we might want the Backspace key to send
  1115. * 0x7F not 0x08.
  1116. */
  1117. if (wParam == VK_BACK) {
  1118. *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
  1119. return p - output;
  1120. }
  1121. /*
  1122. * Control-Space should send ^@ (0x00), not Space.
  1123. */
  1124. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) {
  1125. *p++ = 0x00;
  1126. return p - output;
  1127. }
  1128. /*
  1129. * If we're in applications keypad mode, we have to process it
  1130. * before char-map translation, because it will pre-empt lots
  1131. * of stuff, even if NumLock is off.
  1132. */
  1133. if (app_keypad_keys) {
  1134. if (ret) {
  1135. /*
  1136. * Hack to ensure NumLock doesn't interfere with
  1137. * perception of Shift for Keypad Plus. I don't pretend
  1138. * to understand this, but it seems to work as is.
  1139. * Leave it alone, or die.
  1140. */
  1141. keystate[VK_NUMLOCK] = 0;
  1142. SetKeyboardState (keystate);
  1143. GetKeyboardState (keystate);
  1144. }
  1145. switch ( (lParam >> 16) & 0x1FF ) {
  1146. case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output;
  1147. case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output;
  1148. case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output;
  1149. case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output;
  1150. case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output;
  1151. case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output;
  1152. case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output;
  1153. case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output;
  1154. case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output;
  1155. case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output;
  1156. case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */
  1157. p += sprintf((char *)p,
  1158. (ret && (keystate[VK_SHIFT] & 0x80)) ?
  1159. "\x1BOm" : "\x1BOl");
  1160. return p - output;
  1161. case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output;
  1162. case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output;
  1163. case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output;
  1164. case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output;
  1165. case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output;
  1166. case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output;
  1167. }
  1168. }
  1169. /*
  1170. * Before doing Windows charmap translation, remove LeftALT
  1171. * from the keymap, since its sole effect should be to prepend
  1172. * ESC, which we've already done. Note that removal of LeftALT
  1173. * has to happen _after_ the above call to SetKeyboardState, or
  1174. * dire things will befall.
  1175. */
  1176. if (cancel_alt) {
  1177. keystate[VK_MENU] = keystate[VK_RMENU];
  1178. keystate[VK_LMENU] = 0;
  1179. }
  1180. /*
  1181. * Attempt the Windows char-map translation.
  1182. */
  1183. if (ret) {
  1184. WORD chr;
  1185. int r = ToAscii (wParam, (lParam >> 16) & 0xFF,
  1186. keystate, &chr, 0);
  1187. if (r == 1) {
  1188. *p++ = chr & 0xFF;
  1189. return p - output;
  1190. }
  1191. }
  1192. /*
  1193. * OK, we haven't had a key code from the keymap translation.
  1194. * We'll try our various special cases and function keys, and
  1195. * then give up. (There's nothing wrong with giving up:
  1196. * Scrollock, Pause/Break, and of course the various buckybit
  1197. * keys all produce KEYDOWN events that we really _do_ want to
  1198. * ignore.)
  1199. */
  1200. /*
  1201. * Control-2 should return ^@ (0x00), Control-6 should return
  1202. * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
  1203. * the DOS keyboard handling did it, and we have nothing better
  1204. * to do with the key combo in question, we'll also map
  1205. * Control-Backquote to ^\ (0x1C).
  1206. */
  1207. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') {
  1208. *p++ = 0x00;
  1209. return p - output;
  1210. }
  1211. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') {
  1212. *p++ = 0x1E;
  1213. return p - output;
  1214. }
  1215. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) {
  1216. *p++ = 0x1F;
  1217. return p - output;
  1218. }
  1219. if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) {
  1220. *p++ = 0x1C;
  1221. return p - output;
  1222. }
  1223. /*
  1224. * First, all the keys that do tilde codes. (ESC '[' nn '~',
  1225. * for integer decimal nn.)
  1226. *
  1227. * We also deal with the weird ones here. Linux VCs replace F1
  1228. * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
  1229. * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
  1230. * respectively.
  1231. */
  1232. code = 0;
  1233. switch (wParam) {
  1234. case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
  1235. case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
  1236. case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
  1237. case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
  1238. case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
  1239. case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
  1240. case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
  1241. case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
  1242. case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
  1243. case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
  1244. case VK_F11: code = 23; break;
  1245. case VK_F12: code = 24; break;
  1246. case VK_HOME: code = 1; break;
  1247. case VK_INSERT: code = 2; break;
  1248. case VK_DELETE: code = 3; break;
  1249. case VK_END: code = 4; break;
  1250. case VK_PRIOR: code = 5; break;
  1251. case VK_NEXT: code = 6; break;
  1252. }
  1253. if (cfg.linux_funkeys && code >= 11 && code <= 15) {
  1254. p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
  1255. return p - output;
  1256. }
  1257. if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
  1258. p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
  1259. return p - output;
  1260. }
  1261. if (code) {
  1262. p += sprintf((char *)p, "\x1B[%d~", code);
  1263. return p - output;
  1264. }
  1265. /*
  1266. * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
  1267. * some reason seems to send VK_CLEAR to Windows...).
  1268. */
  1269. switch (wParam) {
  1270. case VK_UP:
  1271. p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A");
  1272. return p - output;
  1273. case VK_DOWN:
  1274. p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B");
  1275. return p - output;
  1276. case VK_RIGHT:
  1277. p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C");
  1278. return p - output;
  1279. case VK_LEFT:
  1280. p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D");
  1281. return p - output;
  1282. case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output;
  1283. }
  1284. return 0;
  1285. }
  1286. void set_title (char *title) {
  1287. sfree (window_name);
  1288. window_name = smalloc(1+strlen(title));
  1289. strcpy (window_name, title);
  1290. if (cfg.win_name_always || !IsIconic(hwnd))
  1291. SetWindowText (hwnd, title);
  1292. }
  1293. void set_icon (char *title) {
  1294. sfree (icon_name);
  1295. icon_name = smalloc(1+strlen(title));
  1296. strcpy (icon_name, title);
  1297. if (!cfg.win_name_always && IsIconic(hwnd))
  1298. SetWindowText (hwnd, title);
  1299. }
  1300. void set_sbar (int total, int start, int page) {
  1301. SCROLLINFO si;
  1302. si.cbSize = sizeof(si);
  1303. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
  1304. si.nMin = 0;
  1305. si.nMax = total - 1;
  1306. si.nPage = page;
  1307. si.nPos = start;
  1308. if (hwnd)
  1309. SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
  1310. }
  1311. Context get_ctx() {
  1312. HDC hdc;
  1313. if (hwnd) {
  1314. hdc = GetDC (hwnd);
  1315. if (hdc && pal)
  1316. SelectPalette (hdc, pal, FALSE);
  1317. return hdc;
  1318. } else
  1319. return NULL;
  1320. }
  1321. void free_ctx (Context ctx) {
  1322. SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
  1323. ReleaseDC (hwnd, ctx);
  1324. }
  1325. static void real_palette_set (int n, int r, int g, int b) {
  1326. if (pal) {
  1327. logpal->palPalEntry[n].peRed = r;
  1328. logpal->palPalEntry[n].peGreen = g;
  1329. logpal->palPalEntry[n].peBlue = b;
  1330. logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
  1331. colours[n] = PALETTERGB(r, g, b);
  1332. SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
  1333. } else
  1334. colours[n] = RGB(r, g, b);
  1335. }
  1336. void palette_set (int n, int r, int g, int b) {
  1337. static const int first[21] = {
  1338. 0, 2, 4, 6, 8, 10, 12, 14,
  1339. 1, 3, 5, 7, 9, 11, 13, 15,
  1340. 16, 17, 18, 20, 22
  1341. };
  1342. real_palette_set (first[n], r, g, b);
  1343. if (first[n] >= 18)
  1344. real_palette_set (first[n]+1, r, g, b);
  1345. if (pal) {
  1346. HDC hdc = get_ctx();
  1347. UnrealizeObject (pal);
  1348. RealizePalette (hdc);
  1349. free_ctx (hdc);
  1350. }
  1351. }
  1352. void palette_reset (void) {
  1353. int i;
  1354. for (i = 0; i < NCOLOURS; i++) {
  1355. if (pal) {
  1356. logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
  1357. logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
  1358. logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
  1359. logpal->palPalEntry[i].peFlags = 0;
  1360. colours[i] = PALETTERGB(defpal[i].rgbtRed,
  1361. defpal[i].rgbtGreen,
  1362. defpal[i].rgbtBlue);
  1363. } else
  1364. colours[i] = RGB(defpal[i].rgbtRed,
  1365. defpal[i].rgbtGreen,
  1366. defpal[i].rgbtBlue);
  1367. }
  1368. if (pal) {
  1369. HDC hdc;
  1370. SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
  1371. hdc = get_ctx();
  1372. RealizePalette (hdc);
  1373. free_ctx (hdc);
  1374. }
  1375. }
  1376. void write_clip (void *data, int len) {
  1377. HGLOBAL clipdata;
  1378. void *lock;
  1379. clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
  1380. if (!clipdata)
  1381. return;
  1382. lock = GlobalLock (clipdata);
  1383. if (!lock)
  1384. return;
  1385. memcpy (lock, data, len);
  1386. ((unsigned char *) lock) [len] = 0;
  1387. GlobalUnlock (clipdata);
  1388. SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
  1389. if (OpenClipboard (hwnd)) {
  1390. EmptyClipboard();
  1391. SetClipboardData (CF_TEXT, clipdata);
  1392. CloseClipboard();
  1393. } else
  1394. GlobalFree (clipdata);
  1395. SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
  1396. }
  1397. void get_clip (void **p, int *len) {
  1398. static HGLOBAL clipdata = NULL;
  1399. if (!p) {
  1400. if (clipdata)
  1401. GlobalUnlock (clipdata);
  1402. clipdata = NULL;
  1403. return;
  1404. } else {
  1405. if (OpenClipboard (NULL)) {
  1406. clipdata = GetClipboardData (CF_TEXT);
  1407. CloseClipboard();
  1408. if (clipdata) {
  1409. *p = GlobalLock (clipdata);
  1410. if (*p) {
  1411. *len = strlen(*p);
  1412. return;
  1413. }
  1414. }
  1415. }
  1416. }
  1417. *p = NULL;
  1418. *len = 0;
  1419. }
  1420. /*
  1421. * Move `lines' lines from position `from' to position `to' in the
  1422. * window.
  1423. */
  1424. void optimised_move (int to, int from, int lines) {
  1425. RECT r;
  1426. int min, max, d;
  1427. min = (to < from ? to : from);
  1428. max = to + from - min;
  1429. d = max - min;
  1430. r.left = 0; r.right = cols * font_width;
  1431. r.top = min * font_height; r.bottom = (max+lines) * font_height;
  1432. ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
  1433. }
  1434. /*
  1435. * Print a message box and perform a fatal exit.
  1436. */
  1437. void fatalbox(char *fmt, ...) {
  1438. va_list ap;
  1439. char stuff[200];
  1440. va_start(ap, fmt);
  1441. vsprintf(stuff, fmt, ap);
  1442. va_end(ap);
  1443. MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
  1444. exit(1);
  1445. }
  1446. /*
  1447. * Beep.
  1448. */
  1449. void beep(void) {
  1450. MessageBeep(MB_OK);
  1451. }