INCOMPATIBLE CHANGE to the SSH2 private key file format. There is
[sgt/putty] / window.c
CommitLineData
374330e2 1#include <windows.h>
49bad831 2#include <imm.h>
374330e2 3#include <commctrl.h>
a7419ea4 4#include <richedit.h>
f0a70059 5#include <mmsystem.h>
4d331a77 6#ifndef AUTO_WINSOCK
7#ifdef WINSOCK_TWO
8#include <winsock2.h>
9#else
374330e2 10#include <winsock.h>
4d331a77 11#endif
12#endif
a401e5f3 13
374330e2 14#include <stdio.h>
15#include <stdlib.h>
1d470ad2 16#include <ctype.h>
ec55b220 17#include <time.h>
a7419ea4 18#include <assert.h>
374330e2 19
32874aea 20#define PUTTY_DO_GLOBALS /* actually _define_ globals */
374330e2 21#include "putty.h"
8c3cd914 22#include "winstuff.h"
d5859615 23#include "storage.h"
374330e2 24#include "win_res.h"
25
6833a413 26#define IDM_SHOWLOG 0x0010
27#define IDM_NEWSESS 0x0020
28#define IDM_DUPSESS 0x0030
29#define IDM_RECONF 0x0040
30#define IDM_CLRSB 0x0050
31#define IDM_RESET 0x0060
32#define IDM_TEL_AYT 0x0070
33#define IDM_TEL_BRK 0x0080
34#define IDM_TEL_SYNCH 0x0090
35#define IDM_TEL_EC 0x00a0
36#define IDM_TEL_EL 0x00b0
37#define IDM_TEL_GA 0x00c0
38#define IDM_TEL_NOP 0x00d0
39#define IDM_TEL_ABORT 0x00e0
40#define IDM_TEL_AO 0x00f0
41#define IDM_TEL_IP 0x0100
42#define IDM_TEL_SUSP 0x0110
43#define IDM_TEL_EOR 0x0120
44#define IDM_TEL_EOF 0x0130
45#define IDM_ABOUT 0x0140
46#define IDM_SAVEDSESS 0x0150
bc1235d4 47#define IDM_COPYALL 0x0160
a401e5f3 48#define IDM_FULLSCREEN 0x0170
6833a413 49
32874aea 50#define IDM_SESSLGP 0x0250 /* log type printable */
51#define IDM_SESSLGA 0x0260 /* log type all chars */
52#define IDM_SESSLGE 0x0270 /* log end */
6833a413 53#define IDM_SAVED_MIN 0x1000
54#define IDM_SAVED_MAX 0x2000
374330e2 55
59ad2c03 56#define WM_IGNORE_CLIP (WM_XUSER + 2)
374330e2 57
3cf144db 58/* Needed for Chinese support and apparently not always defined. */
59#ifndef VK_PROCESSKEY
60#define VK_PROCESSKEY 0xE5
61#endif
62
5eaf1e06 63/* Needed for mouse wheel support and not defined in earlier SDKs. */
64#ifndef WM_MOUSEWHEEL
65#define WM_MOUSEWHEEL 0x020A
66#endif
67
32874aea 68static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
69static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
70 unsigned char *output);
374330e2 71static void cfgtopalette(void);
72static void init_palette(void);
5a73255e 73static void init_fonts(int, int);
4eeb7d09 74static void another_font(int);
75static void deinit_fonts(void);
374330e2 76
5a73255e 77/* Window layout information */
78static void reset_window(int);
6003660f 79static int full_screen = 0;
a401e5f3 80static int extra_width, extra_height;
5a73255e 81static int font_width, font_height, font_dualwidth;
82static int offset_width, offset_height;
83static int was_zoomed = 0;
84static int prev_rows, prev_cols;
85
59ad2c03 86static int pending_netevent = 0;
87static WPARAM pend_netevent_wParam = 0;
88static LPARAM pend_netevent_lParam = 0;
89static void enact_pending_netevent(void);
f8a28d1f 90static void flash_window(int mode);
8f57d753 91static void flip_full_screen(void);
59ad2c03 92
ec55b220 93static time_t last_movement = 0;
94
374330e2 95#define FONT_NORMAL 0
96#define FONT_BOLD 1
97#define FONT_UNDERLINE 2
98#define FONT_BOLDUND 3
4eeb7d09 99#define FONT_WIDE 0x04
100#define FONT_HIGH 0x08
101#define FONT_NARROW 0x10
5a73255e 102
4eeb7d09 103#define FONT_OEM 0x20
104#define FONT_OEMBOLD 0x21
105#define FONT_OEMUND 0x22
106#define FONT_OEMBOLDUND 0x23
5a73255e 107
108#define FONT_MAXNO 0x2F
4eeb7d09 109#define FONT_SHIFT 5
110static HFONT fonts[FONT_MAXNO];
71f6951d 111static LOGFONT lfont;
4eeb7d09 112static int fontflag[FONT_MAXNO];
374330e2 113static enum {
114 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
115} bold_mode;
116static enum {
117 UND_LINE, UND_FONT
118} und_mode;
119static int descent;
120
121#define NCOLOURS 24
122static COLORREF colours[NCOLOURS];
123static HPALETTE pal;
124static LPLOGPALETTE logpal;
125static RGBTRIPLE defpal[NCOLOURS];
126
127static HWND hwnd;
128
934c0b7a 129static HBITMAP caretbm;
130
374330e2 131static int dbltime, lasttime, lastact;
132static Mouse_Button lastbtn;
133
01c034ad 134/* this allows xterm-style mouse handling. */
135static int send_raw_mouse = 0;
136static int wheel_accumulator = 0;
137
374330e2 138static char *window_name, *icon_name;
139
03f23ad6 140static int compose_state = 0;
141
1ece9da7 142static OSVERSIONINFO osVersion;
88485e4d 143
0965bee0 144/* Dummy routine, only required in plink. */
32874aea 145void ldisc_update(int echo, int edit)
146{
147}
6f34e365 148
32874aea 149int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
150{
374330e2 151 static char appname[] = "PuTTY";
152 WORD winsock_ver;
153 WSADATA wsadata;
154 WNDCLASS wndclass;
155 MSG msg;
156 int guess_width, guess_height;
157
8c3cd914 158 hinst = inst;
67779be7 159 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
73251d5d 160
374330e2 161 winsock_ver = MAKEWORD(1, 1);
162 if (WSAStartup(winsock_ver, &wsadata)) {
163 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
164 MB_OK | MB_ICONEXCLAMATION);
165 return 1;
166 }
167 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
168 MessageBox(NULL, "WinSock version is incompatible with 1.1",
169 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
170 WSACleanup();
171 return 1;
172 }
173 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
8df7a775 174 sk_init();
374330e2 175
176 InitCommonControls();
177
301b66db 178 /* Ensure a Maximize setting in Explorer doesn't maximise the
179 * config box. */
180 defuse_showwindow();
181
88485e4d 182 {
183 ZeroMemory(&osVersion, sizeof(osVersion));
536840e6 184 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
1ece9da7 185 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
186 MessageBox(NULL, "Windows refuses to report a version",
187 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
188 return 1;
189 }
88485e4d 190 }
191
374330e2 192 /*
193 * Process the command line.
194 */
195 {
196 char *p;
197
e277c42d 198 default_protocol = DEFAULT_PROTOCOL;
199 default_port = DEFAULT_PORT;
e1c8e0ed 200 cfg.logtype = LGTYP_NONE;
e277c42d 201
a9422f39 202 do_defaults(NULL, &cfg);
374330e2 203
204 p = cmdline;
32874aea 205 while (*p && isspace(*p))
206 p++;
374330e2 207
208 /*
e277c42d 209 * Process command line options first. Yes, this can be
210 * done better, and it will be as soon as I have the
211 * energy...
212 */
213 while (*p == '-') {
214 char *q = p + strcspn(p, " \t");
215 p++;
216 if (q == p + 3 &&
217 tolower(p[0]) == 's' &&
32874aea 218 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
e277c42d 219 default_protocol = cfg.protocol = PROT_SSH;
220 default_port = cfg.port = 22;
de3df031 221 } else if (q == p + 7 &&
32874aea 222 tolower(p[0]) == 'c' &&
223 tolower(p[1]) == 'l' &&
224 tolower(p[2]) == 'e' &&
225 tolower(p[3]) == 'a' &&
226 tolower(p[4]) == 'n' &&
227 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
228 /*
229 * `putty -cleanup'. Remove all registry entries
230 * associated with PuTTY, and also find and delete
231 * the random seed file.
232 */
233 if (MessageBox(NULL,
234 "This procedure will remove ALL Registry\n"
235 "entries associated with PuTTY, and will\n"
236 "also remove the PuTTY random seed file.\n"
237 "\n"
238 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
239 "SESSIONS. Are you really sure you want\n"
240 "to continue?",
241 "PuTTY Warning",
242 MB_YESNO | MB_ICONWARNING) == IDYES) {
243 cleanup_all();
244 }
245 exit(0);
e277c42d 246 }
247 p = q + strspn(q, " \t");
248 }
249
250 /*
374330e2 251 * An initial @ means to activate a saved session.
252 */
253 if (*p == '@') {
f317611e 254 int i = strlen(p);
32874aea 255 while (i > 1 && isspace(p[i - 1]))
f317611e 256 i--;
257 p[i] = '\0';
32874aea 258 do_defaults(p + 1, &cfg);
374330e2 259 if (!*cfg.host && !do_config()) {
260 WSACleanup();
261 return 0;
262 }
263 } else if (*p == '&') {
264 /*
265 * An initial & means we've been given a command line
266 * containing the hex value of a HANDLE for a file
267 * mapping object, which we must then extract as a
268 * config.
269 */
270 HANDLE filemap;
271 Config *cp;
32874aea 272 if (sscanf(p + 1, "%p", &filemap) == 1 &&
374330e2 273 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
274 0, 0, sizeof(Config))) != NULL) {
275 cfg = *cp;
276 UnmapViewOfFile(cp);
277 CloseHandle(filemap);
278 } else if (!do_config()) {
279 WSACleanup();
280 return 0;
281 }
282 } else if (*p) {
283 char *q = p;
32874aea 284 /*
285 * If the hostname starts with "telnet:", set the
286 * protocol to Telnet and process the string as a
287 * Telnet URL.
288 */
289 if (!strncmp(q, "telnet:", 7)) {
290 char c;
291
292 q += 7;
70887be9 293 if (q[0] == '/' && q[1] == '/')
294 q += 2;
32874aea 295 cfg.protocol = PROT_TELNET;
296 p = q;
297 while (*p && *p != ':' && *p != '/')
298 p++;
299 c = *p;
300 if (*p)
301 *p++ = '\0';
302 if (c == ':')
303 cfg.port = atoi(p);
304 else
305 cfg.port = -1;
306 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
307 cfg.host[sizeof(cfg.host) - 1] = '\0';
308 } else {
309 while (*p && !isspace(*p))
310 p++;
311 if (*p)
312 *p++ = '\0';
313 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
314 cfg.host[sizeof(cfg.host) - 1] = '\0';
315 while (*p && isspace(*p))
316 p++;
317 if (*p)
318 cfg.port = atoi(p);
319 else
320 cfg.port = -1;
321 }
374330e2 322 } else {
323 if (!do_config()) {
324 WSACleanup();
325 return 0;
326 }
327 }
13eafebf 328
381b1876 329 /*
330 * Trim leading whitespace off the hostname if it's there.
331 */
332 {
333 int space = strspn(cfg.host, " \t");
334 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
335 }
336
13eafebf 337 /* See if host is of the form user@host */
338 if (cfg.host[0] != '\0') {
339 char *atsign = strchr(cfg.host, '@');
340 /* Make sure we're not overflowing the user field */
341 if (atsign) {
32874aea 342 if (atsign - cfg.host < sizeof cfg.username) {
343 strncpy(cfg.username, cfg.host, atsign - cfg.host);
344 cfg.username[atsign - cfg.host] = '\0';
13eafebf 345 }
32874aea 346 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
13eafebf 347 }
348 }
503ddb97 349
350 /*
351 * Trim a colon suffix off the hostname if it's there.
352 */
353 cfg.host[strcspn(cfg.host, ":")] = '\0';
374330e2 354 }
355
89ee5268 356 /*
357 * Select protocol. This is farmed out into a table in a
358 * separate file to enable an ssh-free variant.
359 */
360 {
32874aea 361 int i;
362 back = NULL;
363 for (i = 0; backends[i].backend != NULL; i++)
364 if (backends[i].protocol == cfg.protocol) {
365 back = backends[i].backend;
366 break;
367 }
368 if (back == NULL) {
369 MessageBox(NULL, "Unsupported protocol number found",
370 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
371 WSACleanup();
372 return 1;
373 }
89ee5268 374 }
5bc238bb 375
b278b14a 376 /* Check for invalid Port number (i.e. zero) */
377 if (cfg.port == 0) {
32874aea 378 MessageBox(NULL, "Invalid Port Number",
379 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
380 WSACleanup();
381 return 1;
b278b14a 382 }
383
374330e2 384 if (!prev) {
32874aea 385 wndclass.style = 0;
386 wndclass.lpfnWndProc = WndProc;
387 wndclass.cbClsExtra = 0;
388 wndclass.cbWndExtra = 0;
389 wndclass.hInstance = inst;
390 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
391 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
5a73255e 392 wndclass.hbrBackground = NULL;
32874aea 393 wndclass.lpszMenuName = NULL;
374330e2 394 wndclass.lpszClassName = appname;
395
32874aea 396 RegisterClass(&wndclass);
374330e2 397 }
398
399 hwnd = NULL;
400
401 savelines = cfg.savelines;
402 term_init();
403
404 cfgtopalette();
405
406 /*
407 * Guess some defaults for the window size. This all gets
408 * updated later, so we don't really care too much. However, we
409 * do want the font width/height guesses to correspond to a
410 * large font rather than a small one...
411 */
32874aea 412
374330e2 413 font_width = 10;
414 font_height = 20;
415 extra_width = 25;
416 extra_height = 28;
32874aea 417 term_size(cfg.height, cfg.width, cfg.savelines);
374330e2 418 guess_width = extra_width + font_width * cols;
419 guess_height = extra_height + font_height * rows;
420 {
421 RECT r;
422 HWND w = GetDesktopWindow();
32874aea 423 GetWindowRect(w, &r);
374330e2 424 if (guess_width > r.right - r.left)
425 guess_width = r.right - r.left;
426 if (guess_height > r.bottom - r.top)
427 guess_height = r.bottom - r.top;
428 }
429
c9def1b8 430 {
32874aea 431 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
432 int exwinmode = 0;
433 if (!cfg.scrollbar)
434 winmode &= ~(WS_VSCROLL);
ad5c93cc 435 if (cfg.resize_action == RESIZE_DISABLED)
32874aea 436 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
437 if (cfg.alwaysontop)
438 exwinmode |= WS_EX_TOPMOST;
439 if (cfg.sunken_edge)
440 exwinmode |= WS_EX_CLIENTEDGE;
441 hwnd = CreateWindowEx(exwinmode, appname, appname,
442 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
443 guess_width, guess_height,
444 NULL, NULL, inst, NULL);
445 }
374330e2 446
447 /*
448 * Initialise the fonts, simultaneously correcting the guesses
449 * for font_{width,height}.
450 */
5a73255e 451 init_fonts(0,0);
374330e2 452
453 /*
454 * Correct the guesses for extra_{width,height}.
455 */
456 {
457 RECT cr, wr;
32874aea 458 GetWindowRect(hwnd, &wr);
459 GetClientRect(hwnd, &cr);
5a73255e 460 offset_width = offset_height = cfg.window_border;
461 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
462 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
374330e2 463 }
464
465 /*
466 * Resize the window, now we know what size we _really_ want it
467 * to be.
468 */
469 guess_width = extra_width + font_width * cols;
470 guess_height = extra_height + font_height * rows;
32874aea 471 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
472 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
374330e2 473
474 /*
934c0b7a 475 * Set up a caret bitmap, with no content.
476 */
477 {
32874aea 478 char *bits;
479 int size = (font_width + 15) / 16 * 2 * font_height;
480 bits = smalloc(size);
481 memset(bits, 0, size);
482 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
483 sfree(bits);
934c0b7a 484 }
5b7ce734 485 CreateCaret(hwnd, caretbm, font_width, font_height);
934c0b7a 486
487 /*
374330e2 488 * Initialise the scroll bar.
489 */
490 {
491 SCROLLINFO si;
492
493 si.cbSize = sizeof(si);
c9def1b8 494 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 495 si.nMin = 0;
32874aea 496 si.nMax = rows - 1;
374330e2 497 si.nPage = rows;
498 si.nPos = 0;
32874aea 499 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
374330e2 500 }
501
502 /*
503 * Start up the telnet connection.
504 */
505 {
506 char *error;
9ca5da42 507 char msg[1024], *title;
374330e2 508 char *realhost;
509
32874aea 510 error = back->init(cfg.host, cfg.port, &realhost);
374330e2 511 if (error) {
582c0054 512 sprintf(msg, "Unable to open connection to\n"
32874aea 513 "%.800s\n" "%s", cfg.host, error);
374330e2 514 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
515 return 0;
516 }
517 window_name = icon_name = NULL;
32874aea 518 if (*cfg.wintitle) {
519 title = cfg.wintitle;
520 } else {
521 sprintf(msg, "%s - PuTTY", realhost);
522 title = msg;
523 }
6e1ebb76 524 sfree(realhost);
32874aea 525 set_title(title);
526 set_icon(title);
374330e2 527 }
528
d85548fe 529 session_closed = FALSE;
530
374330e2 531 /*
374330e2 532 * Prepare the mouse handler.
533 */
534 lastact = MA_NOTHING;
01c034ad 535 lastbtn = MBT_NOTHING;
374330e2 536 dbltime = GetDoubleClickTime();
537
538 /*
539 * Set up the session-control options on the system menu.
540 */
541 {
32874aea 542 HMENU m = GetSystemMenu(hwnd, FALSE);
543 HMENU p, s;
0a4aa984 544 int i;
374330e2 545
32874aea 546 AppendMenu(m, MF_SEPARATOR, 0, 0);
374330e2 547 if (cfg.protocol == PROT_TELNET) {
548 p = CreateMenu();
32874aea 549 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
550 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
551 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
552 AppendMenu(p, MF_SEPARATOR, 0, 0);
553 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
554 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
555 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
556 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
557 AppendMenu(p, MF_SEPARATOR, 0, 0);
558 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
559 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
560 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
561 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
562 AppendMenu(p, MF_SEPARATOR, 0, 0);
563 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
564 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
565 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
566 "Telnet Command");
567 AppendMenu(m, MF_SEPARATOR, 0, 0);
568 }
569 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
570 AppendMenu(m, MF_SEPARATOR, 0, 0);
571 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
572 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
0a4aa984 573 s = CreateMenu();
574 get_sesslist(TRUE);
32874aea 575 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
576 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
577 sessions[i]);
578 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
579 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
580 AppendMenu(m, MF_SEPARATOR, 0, 0);
581 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
582 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
583 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
584 AppendMenu(m, MF_SEPARATOR, 0, 0);
2cae8529 585 AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
586 MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
a401e5f3 587 AppendMenu(m, MF_SEPARATOR, 0, 0);
32874aea 588 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
374330e2 589 }
590
591 /*
592 * Finally show the window!
593 */
32874aea 594 ShowWindow(hwnd, show);
f5eca4f8 595 SetForegroundWindow(hwnd);
374330e2 596
597 /*
e1c8e0ed 598 * Open the initial log file if there is one.
599 */
600 logfopen();
601
602 /*
374330e2 603 * Set the palette up.
604 */
605 pal = NULL;
606 logpal = NULL;
607 init_palette();
608
609 has_focus = (GetForegroundWindow() == hwnd);
32874aea 610 UpdateWindow(hwnd);
374330e2 611
32874aea 612 if (GetMessage(&msg, NULL, 0, 0) == 1) {
59ad2c03 613 int timer_id = 0, long_timer = 0;
614
ec55b220 615 while (msg.message != WM_QUIT) {
59ad2c03 616 /* Sometimes DispatchMessage calls routines that use their own
617 * GetMessage loop, setup this timer so we get some control back.
618 *
619 * Also call term_update() from the timer so that if the host
620 * is sending data flat out we still do redraws.
621 */
32874aea 622 if (timer_id && long_timer) {
59ad2c03 623 KillTimer(hwnd, timer_id);
624 long_timer = timer_id = 0;
625 }
32874aea 626 if (!timer_id)
59ad2c03 627 timer_id = SetTimer(hwnd, 1, 20, NULL);
32874aea 628 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
629 DispatchMessage(&msg);
59ad2c03 630
ec55b220 631 /* Make sure we blink everything that needs it. */
59ad2c03 632 term_blink(0);
633
c9def1b8 634 /* Send the paste buffer if there's anything to send */
635 term_paste();
636
59ad2c03 637 /* If there's nothing new in the queue then we can do everything
638 * we've delayed, reading the socket, writing, and repainting
639 * the window.
640 */
32874aea 641 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
ec55b220 642 continue;
59ad2c03 643
ec55b220 644 if (pending_netevent) {
645 enact_pending_netevent();
646
647 /* Force the cursor blink on */
648 term_blink(1);
649
32874aea 650 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
ec55b220 651 continue;
59ad2c03 652 }
ec55b220 653
654 /* Okay there is now nothing to do so we make sure the screen is
655 * completely up to date then tell windows to call us in a little
656 * while.
657 */
658 if (timer_id) {
659 KillTimer(hwnd, timer_id);
660 timer_id = 0;
661 }
32874aea 662 HideCaret(hwnd);
a748a096 663 term_out();
ec55b220 664 term_update();
32874aea 665 ShowCaret(hwnd);
f8a28d1f 666
667 flash_window(1); /* maintain */
668
156686ef 669 if (in_vbell)
32874aea 670 /* Hmm, term_update didn't want to do an update too soon ... */
671 timer_id = SetTimer(hwnd, 1, 50, NULL);
156686ef 672 else if (!has_focus)
f8a28d1f 673 timer_id = SetTimer(hwnd, 1, 500, NULL);
ec55b220 674 else
32874aea 675 timer_id = SetTimer(hwnd, 1, 100, NULL);
ec55b220 676 long_timer = 1;
32874aea 677
ec55b220 678 /* There's no point rescanning everything in the message queue
156686ef 679 * so we do an apparently unnecessary wait here
ec55b220 680 */
681 WaitMessage();
32874aea 682 if (GetMessage(&msg, NULL, 0, 0) != 1)
ec55b220 683 break;
59ad2c03 684 }
374330e2 685 }
686
687 /*
688 * Clean up.
689 */
4eeb7d09 690 deinit_fonts();
374330e2 691 sfree(logpal);
692 if (pal)
693 DeleteObject(pal);
694 WSACleanup();
695
8f203108 696 if (cfg.protocol == PROT_SSH) {
374330e2 697 random_save_seed();
8f203108 698#ifdef MSCRYPTOAPI
699 crypto_wrapup();
700#endif
701 }
374330e2 702
703 return msg.wParam;
704}
705
706/*
8df7a775 707 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
708 */
32874aea 709char *do_select(SOCKET skt, int startup)
710{
8df7a775 711 int msg, events;
712 if (startup) {
713 msg = WM_NETEVENT;
3ad9d396 714 events = (FD_CONNECT | FD_READ | FD_WRITE |
715 FD_OOB | FD_CLOSE | FD_ACCEPT);
8df7a775 716 } else {
717 msg = events = 0;
718 }
719 if (!hwnd)
720 return "do_select(): internal error (hwnd==NULL)";
32874aea 721 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
722 switch (WSAGetLastError()) {
723 case WSAENETDOWN:
724 return "Network is down";
725 default:
726 return "WSAAsyncSelect(): unknown error";
727 }
8df7a775 728 }
729 return NULL;
730}
731
732/*
01c034ad 733 * set or clear the "raw mouse message" mode
734 */
735void set_raw_mouse_mode(int activate)
736{
737 send_raw_mouse = activate;
738 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
739}
740
741/*
8d5de777 742 * Print a message box and close the connection.
743 */
32874aea 744void connection_fatal(char *fmt, ...)
745{
8d5de777 746 va_list ap;
747 char stuff[200];
748
749 va_start(ap, fmt);
750 vsprintf(stuff, fmt, ap);
751 va_end(ap);
752 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
b41069ff 753 if (cfg.close_on_exit == COE_ALWAYS)
32874aea 754 PostQuitMessage(1);
8d5de777 755 else {
32874aea 756 session_closed = TRUE;
757 SetWindowText(hwnd, "PuTTY (inactive)");
8d5de777 758 }
759}
760
761/*
59ad2c03 762 * Actually do the job requested by a WM_NETEVENT
763 */
32874aea 764static void enact_pending_netevent(void)
765{
9dde0b46 766 static int reentering = 0;
8df7a775 767 extern int select_result(WPARAM, LPARAM);
768 int ret;
9dde0b46 769
770 if (reentering)
32874aea 771 return; /* don't unpend the pending */
9dde0b46 772
59ad2c03 773 pending_netevent = FALSE;
9dde0b46 774
775 reentering = 1;
32874aea 776 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
9dde0b46 777 reentering = 0;
59ad2c03 778
b41069ff 779 if (ret == 0 && !session_closed) {
32874aea 780 /* Abnormal exits will already have set session_closed and taken
781 * appropriate action. */
b41069ff 782 if (cfg.close_on_exit == COE_ALWAYS ||
32874aea 783 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
59ad2c03 784 else {
32874aea 785 session_closed = TRUE;
786 SetWindowText(hwnd, "PuTTY (inactive)");
787 MessageBox(hwnd, "Connection closed by remote host",
788 "PuTTY", MB_OK | MB_ICONINFORMATION);
59ad2c03 789 }
790 }
791}
792
793/*
374330e2 794 * Copy the colour palette from the configuration data into defpal.
795 * This is non-trivial because the colour indices are different.
796 */
32874aea 797static void cfgtopalette(void)
798{
374330e2 799 int i;
800 static const int ww[] = {
801 6, 7, 8, 9, 10, 11, 12, 13,
802 14, 15, 16, 17, 18, 19, 20, 21,
803 0, 1, 2, 3, 4, 4, 5, 5
804 };
805
32874aea 806 for (i = 0; i < 24; i++) {
374330e2 807 int w = ww[i];
808 defpal[i].rgbtRed = cfg.colours[w][0];
809 defpal[i].rgbtGreen = cfg.colours[w][1];
810 defpal[i].rgbtBlue = cfg.colours[w][2];
811 }
812}
813
814/*
815 * Set up the colour palette.
816 */
32874aea 817static void init_palette(void)
818{
374330e2 819 int i;
32874aea 820 HDC hdc = GetDC(hwnd);
374330e2 821 if (hdc) {
32874aea 822 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
374330e2 823 logpal = smalloc(sizeof(*logpal)
824 - sizeof(logpal->palPalEntry)
825 + NCOLOURS * sizeof(PALETTEENTRY));
826 logpal->palVersion = 0x300;
827 logpal->palNumEntries = NCOLOURS;
828 for (i = 0; i < NCOLOURS; i++) {
829 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
830 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
831 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
832 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
833 }
32874aea 834 pal = CreatePalette(logpal);
374330e2 835 if (pal) {
32874aea 836 SelectPalette(hdc, pal, FALSE);
837 RealizePalette(hdc);
838 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
374330e2 839 }
840 }
32874aea 841 ReleaseDC(hwnd, hdc);
374330e2 842 }
843 if (pal)
32874aea 844 for (i = 0; i < NCOLOURS; i++)
374330e2 845 colours[i] = PALETTERGB(defpal[i].rgbtRed,
846 defpal[i].rgbtGreen,
847 defpal[i].rgbtBlue);
848 else
32874aea 849 for (i = 0; i < NCOLOURS; i++)
374330e2 850 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 851 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 852}
853
854/*
4eeb7d09 855 * Initialise all the fonts we will need initially. There may be as many as
856 * three or as few as one. The other (poentially) twentyone fonts are done
857 * if/when they are needed.
858 *
859 * We also:
374330e2 860 *
861 * - check the font width and height, correcting our guesses if
862 * necessary.
863 *
864 * - verify that the bold font is the same width as the ordinary
865 * one, and engage shadow bolding if not.
866 *
867 * - verify that the underlined font is the same width as the
868 * ordinary one (manual underlining by means of line drawing can
869 * be done in a pinch).
374330e2 870 */
5a73255e 871static void init_fonts(int pick_width, int pick_height)
32874aea 872{
374330e2 873 TEXTMETRIC tm;
4eeb7d09 874 CPINFO cpinfo;
875 int fontsize[3];
97fc891e 876 int i;
374330e2 877 HDC hdc;
878 int fw_dontcare, fw_bold;
879
4eeb7d09 880 for (i = 0; i < FONT_MAXNO; i++)
374330e2 881 fonts[i] = NULL;
882
5a73255e 883 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
884 und_mode = UND_FONT;
885
374330e2 886 if (cfg.fontisbold) {
887 fw_dontcare = FW_BOLD;
5b80d07f 888 fw_bold = FW_HEAVY;
32874aea 889 } else {
374330e2 890 fw_dontcare = FW_DONTCARE;
891 fw_bold = FW_BOLD;
892 }
893
97fc891e 894 hdc = GetDC(hwnd);
895
5a73255e 896 if (pick_height)
897 font_height = pick_height;
898 else {
899 font_height = cfg.fontheight;
900 if (font_height > 0) {
901 font_height =
902 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
903 }
3b2c664e 904 }
59ad2c03 905 font_width = pick_width;
97fc891e 906
374330e2 907#define f(i,c,w,u) \
97fc891e 908 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
374330e2 909 c, OUT_DEFAULT_PRECIS, \
910 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
911 FIXED_PITCH | FF_DONTCARE, cfg.font)
97fc891e 912
4eeb7d09 913 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
97fc891e 914
71f6951d 915 lfont.lfHeight = font_height;
916 lfont.lfWidth = font_width;
917 lfont.lfEscapement = 0;
918 lfont.lfOrientation = 0;
919 lfont.lfWeight = fw_dontcare;
920 lfont.lfItalic = FALSE;
921 lfont.lfUnderline = FALSE;
922 lfont.lfStrikeOut = FALSE;
923 lfont.lfCharSet = cfg.fontcharset;
924 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
925 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
926 lfont.lfQuality = DEFAULT_QUALITY;
927 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
928 strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
929
4eeb7d09 930 SelectObject(hdc, fonts[FONT_NORMAL]);
931 GetTextMetrics(hdc, &tm);
5a73255e 932
933 if (pick_width == 0 || pick_height == 0) {
934 font_height = tm.tmHeight;
935 font_width = tm.tmAveCharWidth;
936 }
937 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
938
939#ifdef RDB_DEBUG_PATCH
940 debug(23, "Primary font H=%d, AW=%d, MW=%d",
941 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
942#endif
97fc891e 943
4eeb7d09 944 {
945 CHARSETINFO info;
946 DWORD cset = tm.tmCharSet;
947 memset(&info, 0xFF, sizeof(info));
97fc891e 948
4eeb7d09 949 /* !!! Yes the next line is right */
950 if (cset == OEM_CHARSET)
951 font_codepage = GetOEMCP();
952 else
953 if (TranslateCharsetInfo
954 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
955 info.ciACP;
956 else
957 font_codepage = -1;
32874aea 958
4eeb7d09 959 GetCPInfo(font_codepage, &cpinfo);
960 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
961 }
97fc891e 962
4eeb7d09 963 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
97fc891e 964
4eeb7d09 965 /*
966 * Some fonts, e.g. 9-pt Courier, draw their underlines
967 * outside their character cell. We successfully prevent
968 * screen corruption by clipping the text output, but then
969 * we lose the underline completely. Here we try to work
970 * out whether this is such a font, and if it is, we set a
971 * flag that causes underlines to be drawn by hand.
972 *
973 * Having tried other more sophisticated approaches (such
974 * as examining the TEXTMETRIC structure or requesting the
975 * height of a string), I think we'll do this the brute
976 * force way: we create a small bitmap, draw an underlined
977 * space on it, and test to see whether any pixels are
978 * foreground-coloured. (Since we expect the underline to
979 * go all the way across the character cell, we only search
980 * down a single column of the bitmap, half way across.)
981 */
982 {
983 HDC und_dc;
984 HBITMAP und_bm, und_oldbm;
985 int i, gotit;
986 COLORREF c;
987
988 und_dc = CreateCompatibleDC(hdc);
989 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
990 und_oldbm = SelectObject(und_dc, und_bm);
991 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
992 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
993 SetTextColor(und_dc, RGB(255, 255, 255));
994 SetBkColor(und_dc, RGB(0, 0, 0));
995 SetBkMode(und_dc, OPAQUE);
996 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
997 gotit = FALSE;
998 for (i = 0; i < font_height; i++) {
999 c = GetPixel(und_dc, font_width / 2, i);
1000 if (c != RGB(0, 0, 0))
1001 gotit = TRUE;
1002 }
1003 SelectObject(und_dc, und_oldbm);
1004 DeleteObject(und_bm);
1005 DeleteDC(und_dc);
1006 if (!gotit) {
1007 und_mode = UND_LINE;
1008 DeleteObject(fonts[FONT_UNDERLINE]);
1009 fonts[FONT_UNDERLINE] = 0;
32874aea 1010 }
4eeb7d09 1011 }
97fc891e 1012
4eeb7d09 1013 if (bold_mode == BOLD_FONT) {
1014 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
374330e2 1015 }
1016#undef f
1017
97fc891e 1018 descent = tm.tmAscent + 1;
1019 if (descent >= font_height)
1020 descent = font_height - 1;
374330e2 1021
4eeb7d09 1022 for (i = 0; i < 3; i++) {
97fc891e 1023 if (fonts[i]) {
32874aea 1024 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
4eeb7d09 1025 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
32874aea 1026 else
4eeb7d09 1027 fontsize[i] = -i;
32874aea 1028 } else
4eeb7d09 1029 fontsize[i] = -i;
374330e2 1030 }
1031
32874aea 1032 ReleaseDC(hwnd, hdc);
374330e2 1033
4eeb7d09 1034 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
374330e2 1035 und_mode = UND_LINE;
32874aea 1036 DeleteObject(fonts[FONT_UNDERLINE]);
4eeb7d09 1037 fonts[FONT_UNDERLINE] = 0;
374330e2 1038 }
1039
4eeb7d09 1040 if (bold_mode == BOLD_FONT &&
1041 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
374330e2 1042 bold_mode = BOLD_SHADOW;
32874aea 1043 DeleteObject(fonts[FONT_BOLD]);
4eeb7d09 1044 fonts[FONT_BOLD] = 0;
374330e2 1045 }
4eeb7d09 1046 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1047
1048 init_ucs_tables();
1049}
1050
1051static void another_font(int fontno)
1052{
1053 int basefont;
1054 int fw_dontcare, fw_bold;
1055 int c, u, w, x;
1056 char *s;
1057
1058 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1059 return;
1060
1061 basefont = (fontno & ~(FONT_BOLDUND));
1062 if (basefont != fontno && !fontflag[basefont])
1063 another_font(basefont);
97fc891e 1064
4eeb7d09 1065 if (cfg.fontisbold) {
1066 fw_dontcare = FW_BOLD;
1067 fw_bold = FW_HEAVY;
1068 } else {
1069 fw_dontcare = FW_DONTCARE;
1070 fw_bold = FW_BOLD;
1071 }
1072
1073 c = cfg.fontcharset;
1074 w = fw_dontcare;
1075 u = FALSE;
1076 s = cfg.font;
1077 x = font_width;
1078
1079 if (fontno & FONT_WIDE)
1080 x *= 2;
1081 if (fontno & FONT_NARROW)
5a73255e 1082 x = (x+1)/2;
4eeb7d09 1083 if (fontno & FONT_OEM)
1084 c = OEM_CHARSET;
1085 if (fontno & FONT_BOLD)
1086 w = fw_bold;
1087 if (fontno & FONT_UNDERLINE)
1088 u = TRUE;
1089
1090 fonts[fontno] =
1091 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1092 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1093 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1094 FIXED_PITCH | FF_DONTCARE, s);
1095
1096 fontflag[fontno] = 1;
1097}
1098
1099static void deinit_fonts(void)
1100{
1101 int i;
1102 for (i = 0; i < FONT_MAXNO; i++) {
1103 if (fonts[i])
1104 DeleteObject(fonts[i]);
1105 fonts[i] = 0;
1106 fontflag[i] = 0;
374330e2 1107 }
1108}
1109
5a73255e 1110void request_resize(int w, int h)
32874aea 1111{
59ad2c03 1112 int width, height;
c9def1b8 1113
1114 /* If the window is maximized supress resizing attempts */
5a73255e 1115 if (IsZoomed(hwnd)) {
ad5c93cc 1116 if (cfg.resize_action != RESIZE_FONT)
5a73255e 1117 return;
1118 }
32874aea 1119
ad5c93cc 1120 if (cfg.resize_action == RESIZE_DISABLED) return;
5a73255e 1121 if (h == rows && w == cols) return;
1122
1123 /* Sanity checks ... */
1124 {
32874aea 1125 static int first_time = 1;
1126 static RECT ss;
1127
1128 switch (first_time) {
1129 case 1:
1130 /* Get the size of the screen */
1131 if (GetClientRect(GetDesktopWindow(), &ss))
1132 /* first_time = 0 */ ;
1133 else {
1134 first_time = 2;
1135 break;
1136 }
1137 case 0:
1138 /* Make sure the values are sane */
5a73255e 1139 width = (ss.right - ss.left - extra_width) / 4;
1140 height = (ss.bottom - ss.top - extra_height) / 6;
32874aea 1141
5a73255e 1142 if (w > width || h > height)
1143 return;
32874aea 1144 if (w < 15)
1145 w = 15;
1146 if (h < 1)
5a73255e 1147 h = 1;
32874aea 1148 }
c9def1b8 1149 }
59ad2c03 1150
5a73255e 1151 term_size(h, w, cfg.savelines);
1152
ad5c93cc 1153 if (cfg.resize_action != RESIZE_FONT) {
5a73255e 1154 width = extra_width + font_width * w;
1155 height = extra_height + font_height * h;
374330e2 1156
5a73255e 1157 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1158 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1159 SWP_NOMOVE | SWP_NOZORDER);
1160 } else
1161 reset_window(0);
1162
1163 InvalidateRect(hwnd, NULL, TRUE);
1164}
1165
1166static void reset_window(int reinit) {
1167 /*
1168 * This function decides how to resize or redraw when the
1169 * user changes something.
1170 *
1171 * This function doesn't like to change the terminal size but if the
1172 * font size is locked that may be it's only soluion.
1173 */
1174 int win_width, win_height;
1175 RECT cr, wr;
1176
1177#ifdef RDB_DEBUG_PATCH
1178 debug((27, "reset_window()"));
1179#endif
1180
1181 /* Current window sizes ... */
1182 GetWindowRect(hwnd, &wr);
1183 GetClientRect(hwnd, &cr);
1184
1185 win_width = cr.right - cr.left;
1186 win_height = cr.bottom - cr.top;
1187
1188 /* Are we being forced to reload the fonts ? */
1189 if (reinit>1) {
1190#ifdef RDB_DEBUG_PATCH
1191 debug((27, "reset_window() -- Forced deinit"));
1192#endif
1193 deinit_fonts();
1194 init_fonts(0,0);
1195 }
1196
1197 /* Oh, looks like we're minimised */
1198 if (win_width == 0 || win_height == 0)
1199 return;
1200
1201 /* Is the window out of position ? */
1202 if ( !reinit &&
1203 (offset_width != (win_width-font_width*cols)/2 ||
1204 offset_height != (win_height-font_height*rows)/2) ){
1205 offset_width = (win_width-font_width*cols)/2;
1206 offset_height = (win_height-font_height*rows)/2;
1207 InvalidateRect(hwnd, NULL, TRUE);
1208#ifdef RDB_DEBUG_PATCH
1209 debug((27, "reset_window() -> Reposition terminal"));
1210#endif
1211 }
1212
6003660f 1213 if (IsZoomed(hwnd)) {
5a73255e 1214 /* We're fullscreen, this means we must not change the size of
1215 * the window so it's the font size or the terminal itself.
1216 */
1217
1218 extra_width = wr.right - wr.left - cr.right + cr.left;
1219 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1220
ad5c93cc 1221 if (cfg.resize_action == RESIZE_FONT) {
5a73255e 1222 if ( font_width != win_width/cols ||
1223 font_height != win_height/rows) {
1224 deinit_fonts();
1225 init_fonts(win_width/cols, win_height/rows);
1226 offset_width = (win_width-font_width*cols)/2;
1227 offset_height = (win_height-font_height*rows)/2;
1228 InvalidateRect(hwnd, NULL, TRUE);
1229#ifdef RDB_DEBUG_PATCH
1230 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1231 font_width, font_height));
1232#endif
1233 }
1234 } else {
1235 if ( font_width != win_width/cols ||
1236 font_height != win_height/rows) {
1237 /* Our only choice at this point is to change the
1238 * size of the terminal; Oh well.
1239 */
1240 term_size( win_height/font_height, win_width/font_width,
1241 cfg.savelines);
1242 offset_width = (win_width-font_width*cols)/2;
1243 offset_height = (win_height-font_height*rows)/2;
1244 InvalidateRect(hwnd, NULL, TRUE);
1245#ifdef RDB_DEBUG_PATCH
1246 debug((27, "reset_window() -> Zoomed term_size"));
1247#endif
1248 }
1249 }
1250 return;
1251 }
1252
1253 /* Hmm, a force re-init means we should ignore the current window
1254 * so we resize to the default font size.
1255 */
1256 if (reinit>0) {
1257#ifdef RDB_DEBUG_PATCH
1258 debug((27, "reset_window() -> Forced re-init"));
1259#endif
1260
1261 offset_width = offset_height = cfg.window_border;
1262 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1263 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1264
1265 if (win_width != font_width*cols + offset_width*2 ||
1266 win_height != font_height*rows + offset_height*2) {
1267
1268 /* If this is too large windows will resize it to the maximum
1269 * allowed window size, we will then be back in here and resize
1270 * the font or terminal to fit.
1271 */
1272 SetWindowPos(hwnd, NULL, 0, 0,
1273 font_width*cols + extra_width,
1274 font_height*rows + extra_height,
1275 SWP_NOMOVE | SWP_NOZORDER);
1276 }
1277 return;
1278 }
1279
1280 /* Okay the user doesn't want us to change the font so we try the
1281 * window. But that may be too big for the screen which forces us
1282 * to change the terminal.
1283 */
ad5c93cc 1284 if ((cfg.resize_action != RESIZE_FONT && reinit==0) || reinit>0) {
5a73255e 1285 offset_width = offset_height = cfg.window_border;
1286 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1287 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1288
1289 if (win_width != font_width*cols + offset_width*2 ||
1290 win_height != font_height*rows + offset_height*2) {
1291
1292 static RECT ss;
1293 int width, height;
1294
1295 GetClientRect(GetDesktopWindow(), &ss);
1296 width = (ss.right - ss.left - extra_width) / font_width;
1297 height = (ss.bottom - ss.top - extra_height) / font_height;
1298
1299 /* Grrr too big */
1300 if ( rows > height || cols > width ) {
1301 if ( height > rows ) height = rows;
1302 if ( width > cols ) width = cols;
1303 term_size(height, width, cfg.savelines);
1304#ifdef RDB_DEBUG_PATCH
1305 debug((27, "reset_window() -> term resize to (%d,%d)",
1306 height, width));
1307#endif
1308 }
1309
1310 SetWindowPos(hwnd, NULL, 0, 0,
1311 font_width*cols + extra_width,
1312 font_height*rows + extra_height,
1313 SWP_NOMOVE | SWP_NOZORDER);
1314
1315 InvalidateRect(hwnd, NULL, TRUE);
1316#ifdef RDB_DEBUG_PATCH
1317 debug((27, "reset_window() -> window resize to (%d,%d)",
1318 font_width*cols + extra_width,
1319 font_height*rows + extra_height));
1320#endif
1321 }
1322 return;
1323 }
1324
1325 /* We're allowed to or must change the font but do we want to ? */
1326
1327 if (font_width != (win_width-cfg.window_border*2)/cols ||
1328 font_height != (win_height-cfg.window_border*2)/rows) {
1329
1330 deinit_fonts();
1331 init_fonts((win_width-cfg.window_border*2)/cols,
1332 (win_height-cfg.window_border*2)/rows);
1333 offset_width = (win_width-font_width*cols)/2;
1334 offset_height = (win_height-font_height*rows)/2;
1335
1336 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1337 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1338
1339 InvalidateRect(hwnd, NULL, TRUE);
1340#ifdef RDB_DEBUG_PATCH
1341 debug((25, "reset_window() -> font resize to (%d,%d)",
1342 font_width, font_height));
1343#endif
1344 }
374330e2 1345}
1346
6908fed7 1347static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
32874aea 1348{
fdedf2c8 1349 int thistime = GetMessageTime();
1350
b90840c3 1351 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1352 lastbtn = MBT_NOTHING;
6908fed7 1353 term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
01c034ad 1354 return;
1355 }
1356
fdedf2c8 1357 if (lastbtn == b && thistime - lasttime < dbltime) {
374330e2 1358 lastact = (lastact == MA_CLICK ? MA_2CLK :
1359 lastact == MA_2CLK ? MA_3CLK :
1360 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1361 } else {
1362 lastbtn = b;
1363 lastact = MA_CLICK;
1364 }
1365 if (lastact != MA_NOTHING)
6908fed7 1366 term_mouse(b, lastact, x, y, shift, ctrl, alt);
fdedf2c8 1367 lasttime = thistime;
374330e2 1368}
1369
01c034ad 1370/*
1371 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1372 * into a cooked one (SELECT, EXTEND, PASTE).
1373 */
32874aea 1374Mouse_Button translate_button(Mouse_Button button)
1375{
01c034ad 1376 if (button == MBT_LEFT)
1377 return MBT_SELECT;
1378 if (button == MBT_MIDDLE)
1379 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1380 if (button == MBT_RIGHT)
1381 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
2d466ffd 1382 return 0; /* shouldn't happen */
01c034ad 1383}
1384
32874aea 1385static void show_mouseptr(int show)
1386{
554c540d 1387 static int cursor_visible = 1;
32874aea 1388 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1389 show = 1;
554c540d 1390 if (cursor_visible && !show)
32874aea 1391 ShowCursor(FALSE);
554c540d 1392 else if (!cursor_visible && show)
32874aea 1393 ShowCursor(TRUE);
554c540d 1394 cursor_visible = show;
1395}
1396
6908fed7 1397static int is_alt_pressed(void)
1398{
1399 BYTE keystate[256];
1400 int r = GetKeyboardState(keystate);
1401 if (!r)
1402 return FALSE;
1403 if (keystate[VK_MENU] & 0x80)
1404 return TRUE;
1405 if (keystate[VK_RMENU] & 0x80)
1406 return TRUE;
1407 return FALSE;
1408}
1409
32874aea 1410static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1411 WPARAM wParam, LPARAM lParam)
1412{
374330e2 1413 HDC hdc;
374330e2 1414 static int ignore_clip = FALSE;
ffc31afe 1415 static int resizing = FALSE;
3ad8c6db 1416 static int need_backend_resize = FALSE;
374330e2 1417
1418 switch (message) {
59ad2c03 1419 case WM_TIMER:
1420 if (pending_netevent)
1421 enact_pending_netevent();
a748a096 1422 term_out();
32874aea 1423 noise_regular();
1424 HideCaret(hwnd);
59ad2c03 1425 term_update();
32874aea 1426 ShowCaret(hwnd);
1427 if (cfg.ping_interval > 0) {
1428 time_t now;
1429 time(&now);
1430 if (now - last_movement > cfg.ping_interval) {
1431 back->special(TS_PING);
1432 last_movement = now;
1433 }
1434 }
7732d38a 1435 net_pending_errors();
59ad2c03 1436 return 0;
374330e2 1437 case WM_CREATE:
1438 break;
68130d34 1439 case WM_CLOSE:
32874aea 1440 show_mouseptr(1);
d85548fe 1441 if (!cfg.warn_on_close || session_closed ||
32874aea 1442 MessageBox(hwnd,
1443 "Are you sure you want to close this session?",
68130d34 1444 "PuTTY Exit Confirmation",
1445 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1446 DestroyWindow(hwnd);
1447 return 0;
374330e2 1448 case WM_DESTROY:
32874aea 1449 show_mouseptr(1);
1450 PostQuitMessage(0);
374330e2 1451 return 0;
6833a413 1452 case WM_SYSCOMMAND:
1453 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1454 case IDM_SHOWLOG:
c5e9c988 1455 showeventlog(hwnd);
374330e2 1456 break;
1457 case IDM_NEWSESS:
1458 case IDM_DUPSESS:
6833a413 1459 case IDM_SAVEDSESS:
374330e2 1460 {
1461 char b[2048];
1462 char c[30], *cl;
e4e4cc7e 1463 int freecl = FALSE;
374330e2 1464 STARTUPINFO si;
1465 PROCESS_INFORMATION pi;
1466 HANDLE filemap = NULL;
1467
1468 if (wParam == IDM_DUPSESS) {
1469 /*
1470 * Allocate a file-mapping memory chunk for the
1471 * config structure.
1472 */
1473 SECURITY_ATTRIBUTES sa;
1474 Config *p;
1475
1476 sa.nLength = sizeof(sa);
1477 sa.lpSecurityDescriptor = NULL;
1478 sa.bInheritHandle = TRUE;
32874aea 1479 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
374330e2 1480 &sa,
1481 PAGE_READWRITE,
32874aea 1482 0, sizeof(Config), NULL);
374330e2 1483 if (filemap) {
32874aea 1484 p = (Config *) MapViewOfFile(filemap,
1485 FILE_MAP_WRITE,
1486 0, 0, sizeof(Config));
374330e2 1487 if (p) {
1488 *p = cfg; /* structure copy */
1489 UnmapViewOfFile(p);
1490 }
1491 }
1d470ad2 1492 sprintf(c, "putty &%p", filemap);
374330e2 1493 cl = c;
0a4aa984 1494 } else if (wParam == IDM_SAVEDSESS) {
32874aea 1495 char *session =
1496 sessions[(lParam - IDM_SAVED_MIN) / 16];
1497 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
e4e4cc7e 1498 if (!cl)
1499 cl = NULL; /* not a very important failure mode */
94e6450e 1500 else {
1501 sprintf(cl, "putty @%s", session);
1502 freecl = TRUE;
1503 }
374330e2 1504 } else
6833a413 1505 cl = NULL;
374330e2 1506
32874aea 1507 GetModuleFileName(NULL, b, sizeof(b) - 1);
374330e2 1508 si.cb = sizeof(si);
1509 si.lpReserved = NULL;
1510 si.lpDesktop = NULL;
1511 si.lpTitle = NULL;
1512 si.dwFlags = 0;
1513 si.cbReserved2 = 0;
1514 si.lpReserved2 = NULL;
32874aea 1515 CreateProcess(b, cl, NULL, NULL, TRUE,
1516 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
374330e2 1517
1518 if (filemap)
1519 CloseHandle(filemap);
e4e4cc7e 1520 if (freecl)
dcbde236 1521 sfree(cl);
374330e2 1522 }
1523 break;
32874aea 1524 case IDM_RECONF:
1525 {
5a73255e 1526 Config prev_cfg;
1527 int init_lvl = 1;
1528
32874aea 1529 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
5a73255e 1530 prev_cfg = cfg;
e1c8e0ed 1531
32874aea 1532 if (!do_reconfig(hwnd))
1533 break;
e1c8e0ed 1534
2cae8529 1535 {
1536 /* Disable full-screen if resizing forbidden */
1537 HMENU m = GetSystemMenu (hwnd, FALSE);
1538 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1539 (cfg.resize_action == RESIZE_DISABLED)
1540 ? MF_GRAYED : MF_ENABLED);
1541 /* Gracefully unzoom if necessary */
1542 if (full_screen &&
1543 (cfg.resize_action == RESIZE_DISABLED)) {
1544 flip_full_screen();
1545 }
a401e5f3 1546 }
1547
5a73255e 1548 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1549 prev_cfg.logtype != cfg.logtype) {
e1c8e0ed 1550 logfclose(); /* reset logging */
1551 logfopen();
1552 }
1553
32874aea 1554 sfree(logpal);
1555 /*
1556 * Flush the line discipline's edit buffer in the
1557 * case where local editing has just been disabled.
1558 */
760e88b2 1559 ldisc_send(NULL, 0, 0);
32874aea 1560 if (pal)
1561 DeleteObject(pal);
1562 logpal = NULL;
1563 pal = NULL;
1564 cfgtopalette();
1565 init_palette();
1566
5a73255e 1567 /* Screen size changed ? */
1568 if (cfg.height != prev_cfg.height ||
1569 cfg.width != prev_cfg.width ||
1570 cfg.savelines != prev_cfg.savelines ||
ad5c93cc 1571 cfg.resize_action != RESIZE_TERM)
5a73255e 1572 term_size(cfg.height, cfg.width, cfg.savelines);
1573
32874aea 1574 /* Enable or disable the scroll bar, etc */
1575 {
1576 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1577 LONG nexflag, exflag =
1578 GetWindowLong(hwnd, GWL_EXSTYLE);
1579
1580 nexflag = exflag;
5a73255e 1581 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
32874aea 1582 if (cfg.alwaysontop) {
1583 nexflag |= WS_EX_TOPMOST;
1584 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1585 SWP_NOMOVE | SWP_NOSIZE);
1586 } else {
1587 nexflag &= ~(WS_EX_TOPMOST);
1588 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1589 SWP_NOMOVE | SWP_NOSIZE);
1590 }
1591 }
1592 if (cfg.sunken_edge)
1593 nexflag |= WS_EX_CLIENTEDGE;
1594 else
1595 nexflag &= ~(WS_EX_CLIENTEDGE);
1596
1597 nflg = flag;
2cae8529 1598 if (full_screen ?
1599 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
32874aea 1600 nflg |= WS_VSCROLL;
1601 else
1602 nflg &= ~WS_VSCROLL;
ad5c93cc 1603 if (cfg.resize_action == RESIZE_DISABLED)
32874aea 1604 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1605 else
1606 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1607
1608 if (nflg != flag || nexflag != exflag) {
32874aea 1609 if (nflg != flag)
1610 SetWindowLong(hwnd, GWL_STYLE, nflg);
1611 if (nexflag != exflag)
1612 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1613
32874aea 1614 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1615 SWP_NOACTIVATE | SWP_NOCOPYBITS |
5a73255e 1616 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1617 SWP_FRAMECHANGED);
c9def1b8 1618
5a73255e 1619 init_lvl = 2;
e44d78b6 1620 }
32874aea 1621 }
5a73255e 1622
e44d78b6 1623 /* Oops */
ad5c93cc 1624 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
e44d78b6 1625 force_normal(hwnd);
5a73255e 1626 init_lvl = 2;
1627 }
1628
32874aea 1629 set_title(cfg.wintitle);
1630 if (IsIconic(hwnd)) {
1631 SetWindowText(hwnd,
1632 cfg.win_name_always ? window_name :
1633 icon_name);
3da0b1d2 1634 }
5a73255e 1635
1636 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1637 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1638 cfg.fontisbold != prev_cfg.fontisbold ||
1639 cfg.fontheight != prev_cfg.fontheight ||
1640 cfg.fontcharset != prev_cfg.fontcharset ||
1641 cfg.vtmode != prev_cfg.vtmode ||
1642 cfg.bold_colour != prev_cfg.bold_colour ||
ad5c93cc 1643 (cfg.resize_action != RESIZE_FONT &&
1644 prev_cfg.resize_action == RESIZE_FONT))
5a73255e 1645 init_lvl = 2;
1646
1647 InvalidateRect(hwnd, NULL, TRUE);
1648 reset_window(init_lvl);
7732d38a 1649 net_pending_errors();
32874aea 1650 }
1651 break;
bc1235d4 1652 case IDM_COPYALL:
1653 term_copyall();
1654 break;
32874aea 1655 case IDM_CLRSB:
1656 term_clrsb();
1657 break;
1658 case IDM_RESET:
1659 term_pwron();
1660 break;
1661 case IDM_TEL_AYT:
1662 back->special(TS_AYT);
7732d38a 1663 net_pending_errors();
32874aea 1664 break;
1665 case IDM_TEL_BRK:
1666 back->special(TS_BRK);
7732d38a 1667 net_pending_errors();
32874aea 1668 break;
1669 case IDM_TEL_SYNCH:
1670 back->special(TS_SYNCH);
7732d38a 1671 net_pending_errors();
32874aea 1672 break;
1673 case IDM_TEL_EC:
1674 back->special(TS_EC);
7732d38a 1675 net_pending_errors();
32874aea 1676 break;
1677 case IDM_TEL_EL:
1678 back->special(TS_EL);
7732d38a 1679 net_pending_errors();
32874aea 1680 break;
1681 case IDM_TEL_GA:
1682 back->special(TS_GA);
7732d38a 1683 net_pending_errors();
32874aea 1684 break;
1685 case IDM_TEL_NOP:
1686 back->special(TS_NOP);
7732d38a 1687 net_pending_errors();
32874aea 1688 break;
1689 case IDM_TEL_ABORT:
1690 back->special(TS_ABORT);
7732d38a 1691 net_pending_errors();
32874aea 1692 break;
1693 case IDM_TEL_AO:
1694 back->special(TS_AO);
7732d38a 1695 net_pending_errors();
32874aea 1696 break;
1697 case IDM_TEL_IP:
1698 back->special(TS_IP);
7732d38a 1699 net_pending_errors();
32874aea 1700 break;
1701 case IDM_TEL_SUSP:
1702 back->special(TS_SUSP);
7732d38a 1703 net_pending_errors();
32874aea 1704 break;
1705 case IDM_TEL_EOR:
1706 back->special(TS_EOR);
7732d38a 1707 net_pending_errors();
32874aea 1708 break;
1709 case IDM_TEL_EOF:
1710 back->special(TS_EOF);
7732d38a 1711 net_pending_errors();
32874aea 1712 break;
374330e2 1713 case IDM_ABOUT:
32874aea 1714 showabout(hwnd);
374330e2 1715 break;
0d6dcf38 1716 case SC_MOUSEMENU:
1717 /*
1718 * We get this if the System menu has been activated
1719 * using the mouse.
1720 */
1721 show_mouseptr(1);
1722 break;
dfca2656 1723 case SC_KEYMENU:
1724 /*
0d6dcf38 1725 * We get this if the System menu has been activated
1726 * using the keyboard. This might happen from within
1727 * TranslateKey, in which case it really wants to be
1728 * followed by a `space' character to actually _bring
1729 * the menu up_ rather than just sitting there in
1730 * `ready to appear' state.
dfca2656 1731 */
0d6dcf38 1732 show_mouseptr(1); /* make sure pointer is visible */
dfca2656 1733 if( lParam == 0 )
1734 PostMessage(hwnd, WM_CHAR, ' ', 0);
1735 break;
a401e5f3 1736 case IDM_FULLSCREEN:
1737 flip_full_screen();
1738 break;
32874aea 1739 default:
1740 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1741 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1742 }
374330e2 1743 }
1744 break;
37508af4 1745
1746#define X_POS(l) ((int)(short)LOWORD(l))
1747#define Y_POS(l) ((int)(short)HIWORD(l))
1748
5a73255e 1749#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1750#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
01c034ad 1751#define WHEEL_DELTA 120
1752 case WM_MOUSEWHEEL:
1753 {
32874aea 1754 wheel_accumulator += (short) HIWORD(wParam);
01c034ad 1755 wParam = LOWORD(wParam);
1756
1757 /* process events when the threshold is reached */
1758 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1759 int b;
fdedf2c8 1760
01c034ad 1761 /* reduce amount for next time */
1762 if (wheel_accumulator > 0) {
1763 b = MBT_WHEEL_UP;
1764 wheel_accumulator -= WHEEL_DELTA;
32874aea 1765 } else if (wheel_accumulator < 0) {
01c034ad 1766 b = MBT_WHEEL_DOWN;
1767 wheel_accumulator += WHEEL_DELTA;
32874aea 1768 } else
01c034ad 1769 break;
1770
1771 if (send_raw_mouse) {
1772 /* send a mouse-down followed by a mouse up */
6908fed7 1773
01c034ad 1774 term_mouse(b,
1775 MA_CLICK,
32874aea 1776 TO_CHR_X(X_POS(lParam)),
1777 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1778 wParam & MK_CONTROL, is_alt_pressed());
32874aea 1779 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1780 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1781 wParam & MK_CONTROL, is_alt_pressed());
01c034ad 1782 } else {
1783 /* trigger a scroll */
32874aea 1784 term_scroll(0,
1785 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
01c034ad 1786 }
1787 }
1788 return 0;
1789 }
374330e2 1790 case WM_LBUTTONDOWN:
374330e2 1791 case WM_MBUTTONDOWN:
374330e2 1792 case WM_RBUTTONDOWN:
01c034ad 1793 case WM_LBUTTONUP:
1794 case WM_MBUTTONUP:
374330e2 1795 case WM_RBUTTONUP:
01c034ad 1796 {
1797 int button, press;
6908fed7 1798
01c034ad 1799 switch (message) {
32874aea 1800 case WM_LBUTTONDOWN:
1801 button = MBT_LEFT;
1802 press = 1;
1803 break;
1804 case WM_MBUTTONDOWN:
1805 button = MBT_MIDDLE;
1806 press = 1;
1807 break;
1808 case WM_RBUTTONDOWN:
1809 button = MBT_RIGHT;
1810 press = 1;
1811 break;
1812 case WM_LBUTTONUP:
1813 button = MBT_LEFT;
1814 press = 0;
1815 break;
1816 case WM_MBUTTONUP:
1817 button = MBT_MIDDLE;
1818 press = 0;
1819 break;
1820 case WM_RBUTTONUP:
1821 button = MBT_RIGHT;
1822 press = 0;
1823 break;
2d466ffd 1824 default:
1825 button = press = 0; /* shouldn't happen */
01c034ad 1826 }
1827 show_mouseptr(1);
2cae8529 1828 /*
1829 * Special case: in full-screen mode, if the left
1830 * button is clicked in the very top left corner of the
1831 * window, we put up the System menu instead of doing
1832 * selection.
1833 */
1834 if (full_screen && press && button == MBT_LEFT &&
1835 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
1836 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
1837 return 0;
1838 }
01c034ad 1839 if (press) {
32874aea 1840 click(button,
1841 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
6908fed7 1842 wParam & MK_SHIFT, wParam & MK_CONTROL,
1843 is_alt_pressed());
01c034ad 1844 SetCapture(hwnd);
1845 } else {
32874aea 1846 term_mouse(button, MA_RELEASE,
1847 TO_CHR_X(X_POS(lParam)),
1848 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1849 wParam & MK_CONTROL, is_alt_pressed());
01c034ad 1850 ReleaseCapture();
1851 }
1852 }
374330e2 1853 return 0;
1854 case WM_MOUSEMOVE:
32874aea 1855 show_mouseptr(1);
374330e2 1856 /*
1857 * Add the mouse position and message time to the random
7d6ee6ff 1858 * number noise.
374330e2 1859 */
32874aea 1860 noise_ultralight(lParam);
374330e2 1861
1862 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1863 Mouse_Button b;
1864 if (wParam & MK_LBUTTON)
6c6e7711 1865 b = MBT_LEFT;
374330e2 1866 else if (wParam & MK_MBUTTON)
6c6e7711 1867 b = MBT_MIDDLE;
374330e2 1868 else
6c6e7711 1869 b = MBT_RIGHT;
32874aea 1870 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1871 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1872 wParam & MK_CONTROL, is_alt_pressed());
374330e2 1873 }
374330e2 1874 return 0;
d318ef8e 1875 case WM_NCMOUSEMOVE:
1876 show_mouseptr(1);
32874aea 1877 noise_ultralight(lParam);
d318ef8e 1878 return 0;
374330e2 1879 case WM_IGNORE_CLIP:
1880 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1881 break;
1882 case WM_DESTROYCLIPBOARD:
1883 if (!ignore_clip)
1884 term_deselect();
1885 ignore_clip = FALSE;
1886 return 0;
1887 case WM_PAINT:
1888 {
1889 PAINTSTRUCT p;
32874aea 1890 HideCaret(hwnd);
1891 hdc = BeginPaint(hwnd, &p);
374330e2 1892 if (pal) {
32874aea 1893 SelectPalette(hdc, pal, TRUE);
1894 RealizePalette(hdc);
374330e2 1895 }
5a73255e 1896 term_paint(hdc,
1897 (p.rcPaint.left-offset_width)/font_width,
1898 (p.rcPaint.top-offset_height)/font_height,
1899 (p.rcPaint.right-offset_width-1)/font_width,
1900 (p.rcPaint.bottom-offset_height-1)/font_height);
1901
1902 if (p.fErase ||
1903 p.rcPaint.left < offset_width ||
1904 p.rcPaint.top < offset_height ||
1905 p.rcPaint.right >= offset_width + font_width*cols ||
1906 p.rcPaint.bottom>= offset_height + font_height*rows)
1907 {
1908 HBRUSH fillcolour, oldbrush;
1909 HPEN edge, oldpen;
1910 fillcolour = CreateSolidBrush (
1911 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1912 oldbrush = SelectObject(hdc, fillcolour);
1913 edge = CreatePen(PS_SOLID, 0,
1914 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1915 oldpen = SelectObject(hdc, edge);
1916
1917 ExcludeClipRect(hdc,
1918 offset_width, offset_height,
1919 offset_width+font_width*cols,
1920 offset_height+font_height*rows);
1921
1922 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1923 p.rcPaint.right, p.rcPaint.bottom);
1924
1925 // SelectClipRgn(hdc, NULL);
1926
1927 SelectObject(hdc, oldbrush);
1928 DeleteObject(fillcolour);
1929 SelectObject(hdc, oldpen);
1930 DeleteObject(edge);
1931 }
32874aea 1932 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1933 SelectObject(hdc, GetStockObject(WHITE_PEN));
1934 EndPaint(hwnd, &p);
1935 ShowCaret(hwnd);
374330e2 1936 }
1937 return 0;
1938 case WM_NETEVENT:
59ad2c03 1939 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1940 * but the only one that's likely to try to overload us is FD_READ.
1941 * This means buffering just one is fine.
1942 */
1943 if (pending_netevent)
1944 enact_pending_netevent();
1945
1946 pending_netevent = TRUE;
32874aea 1947 pend_netevent_wParam = wParam;
1948 pend_netevent_lParam = lParam;
3ad9d396 1949 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1950 enact_pending_netevent();
1951
ec55b220 1952 time(&last_movement);
374330e2 1953 return 0;
1954 case WM_SETFOCUS:
1955 has_focus = TRUE;
32874aea 1956 CreateCaret(hwnd, caretbm, font_width, font_height);
1957 ShowCaret(hwnd);
f8a28d1f 1958 flash_window(0); /* stop */
32874aea 1959 compose_state = 0;
374330e2 1960 term_out();
1961 term_update();
1962 break;
1963 case WM_KILLFOCUS:
32874aea 1964 show_mouseptr(1);
374330e2 1965 has_focus = FALSE;
32874aea 1966 DestroyCaret();
374330e2 1967 term_out();
1968 term_update();
1969 break;
73251d5d 1970 case WM_ENTERSIZEMOVE:
5a73255e 1971#ifdef RDB_DEBUG_PATCH
1972 debug((27, "WM_ENTERSIZEMOVE"));
1973#endif
32874aea 1974 EnableSizeTip(1);
1975 resizing = TRUE;
3ad8c6db 1976 need_backend_resize = FALSE;
32874aea 1977 break;
73251d5d 1978 case WM_EXITSIZEMOVE:
32874aea 1979 EnableSizeTip(0);
1980 resizing = FALSE;
5a73255e 1981#ifdef RDB_DEBUG_PATCH
1982 debug((27, "WM_EXITSIZEMOVE"));
1983#endif
1984 if (need_backend_resize) {
1985 term_size(cfg.height, cfg.width, cfg.savelines);
1986 InvalidateRect(hwnd, NULL, TRUE);
1987 }
32874aea 1988 break;
374330e2 1989 case WM_SIZING:
5a73255e 1990 /*
1991 * This does two jobs:
1992 * 1) Keep the sizetip uptodate
1993 * 2) Make sure the window size is _stepped_ in units of the font size.
1994 */
ad5c93cc 1995 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
374330e2 1996 int width, height, w, h, ew, eh;
32874aea 1997 LPRECT r = (LPRECT) lParam;
374330e2 1998
5a73255e 1999 if ( !need_backend_resize &&
2000 (cfg.height != rows || cfg.width != cols )) {
2001 /*
2002 * Great! It seems the host has been changing the terminal
2003 * size, well the user is now grabbing so this is probably
2004 * the least confusing solution in the long run even though
2005 * it a is suprise. Unfortunatly the only way to prevent
2006 * this seems to be to let the host change the window size
2007 * and as that's a user option we're still right back here.
2008 */
2009 term_size(cfg.height, cfg.width, cfg.savelines);
2010 reset_window(2);
2011 InvalidateRect(hwnd, NULL, TRUE);
2012 need_backend_resize = TRUE;
2013 }
2014
374330e2 2015 width = r->right - r->left - extra_width;
2016 height = r->bottom - r->top - extra_height;
32874aea 2017 w = (width + font_width / 2) / font_width;
2018 if (w < 1)
2019 w = 1;
2020 h = (height + font_height / 2) / font_height;
2021 if (h < 1)
2022 h = 1;
2023 UpdateSizeTip(hwnd, w, h);
374330e2 2024 ew = width - w * font_width;
2025 eh = height - h * font_height;
2026 if (ew != 0) {
2027 if (wParam == WMSZ_LEFT ||
32874aea 2028 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 2029 r->left += ew;
2030 else
2031 r->right -= ew;
2032 }
2033 if (eh != 0) {
2034 if (wParam == WMSZ_TOP ||
32874aea 2035 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 2036 r->top += eh;
2037 else
2038 r->bottom -= eh;
2039 }
2040 if (ew || eh)
2041 return 1;
2042 else
2043 return 0;
5a73255e 2044 } else {
2045 int width, height, w, h, rv = 0;
2046 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2047 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2048 LPRECT r = (LPRECT) lParam;
2049
2050 width = r->right - r->left - ex_width;
2051 height = r->bottom - r->top - ex_height;
2052
2053 w = (width + cols/2)/cols;
2054 h = (height + rows/2)/rows;
2055 if ( r->right != r->left + w*cols + ex_width)
2056 rv = 1;
2057
2058 if (wParam == WMSZ_LEFT ||
2059 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2060 r->left = r->right - w*cols - ex_width;
2061 else
2062 r->right = r->left + w*cols + ex_width;
2063
2064 if (r->bottom != r->top + h*rows + ex_height)
2065 rv = 1;
2066
2067 if (wParam == WMSZ_TOP ||
2068 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2069 r->top = r->bottom - h*rows - ex_height;
2070 else
2071 r->bottom = r->top + h*rows + ex_height;
2072
2073 return rv;
374330e2 2074 }
32874aea 2075 /* break; (never reached) */
374330e2 2076 case WM_SIZE:
5a73255e 2077#ifdef RDB_DEBUG_PATCH
2078 debug((27, "WM_SIZE %s (%d,%d)",
2079 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2080 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2081 (wParam == SIZE_RESTORED && resizing) ? "to":
2082 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2083 "...",
2084 LOWORD(lParam), HIWORD(lParam)));
2085#endif
374330e2 2086 if (wParam == SIZE_MINIMIZED) {
32874aea 2087 SetWindowText(hwnd,
2088 cfg.win_name_always ? window_name : icon_name);
374330e2 2089 break;
2090 }
2091 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 2092 SetWindowText(hwnd, window_name);
5a73255e 2093
ad5c93cc 2094 if (cfg.resize_action == RESIZE_DISABLED) {
5a73255e 2095 /* A resize, well it better be a minimize. */
2096 reset_window(-1);
2097 } else {
2098
1a6f78fe 2099 int width, height, w, h;
374330e2 2100
2101 width = LOWORD(lParam);
2102 height = HIWORD(lParam);
5a73255e 2103
2104 if (!resizing) {
2105 if (wParam == SIZE_MAXIMIZED) {
2106 was_zoomed = 1;
2107 prev_rows = rows;
2108 prev_cols = cols;
ad5c93cc 2109 if (cfg.resize_action != RESIZE_FONT) {
5a73255e 2110 w = width / font_width;
2111 if (w < 1) w = 1;
2112 h = height / font_height;
2113 if (h < 1) h = 1;
2114
2115 term_size(h, w, cfg.savelines);
2116 }
2117 reset_window(0);
2118 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2119 was_zoomed = 0;
ad5c93cc 2120 if (cfg.resize_action != RESIZE_FONT)
5a73255e 2121 term_size(prev_rows, prev_cols, cfg.savelines);
2122 reset_window(0);
2123 }
2124 /* This is an unexpected resize, these will normally happen
2125 * if the window is too large. Probably either the user
2126 * selected a huge font or the screen size has changed.
2127 *
2128 * This is also called with minimize.
32874aea 2129 */
5a73255e 2130 else reset_window(-1);
2131 }
2132
2133 /*
2134 * Don't call back->size in mid-resize. (To prevent
2135 * massive numbers of resize events getting sent
2136 * down the connection during an NT opaque drag.)
2137 */
2138 if (resizing) {
ad5c93cc 2139 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
3ad8c6db 2140 need_backend_resize = TRUE;
5a73255e 2141 w = (width-cfg.window_border*2) / font_width;
2142 if (w < 1) w = 1;
2143 h = (height-cfg.window_border*2) / font_height;
2144 if (h < 1) h = 1;
2145
e44d78b6 2146 cfg.height = h;
2147 cfg.width = w;
5a73255e 2148 } else
2149 reset_window(0);
374330e2 2150 }
2151 }
374330e2 2152 return 0;
2153 case WM_VSCROLL:
2154 switch (LOWORD(wParam)) {
32874aea 2155 case SB_BOTTOM:
2156 term_scroll(-1, 0);
2157 break;
2158 case SB_TOP:
2159 term_scroll(+1, 0);
2160 break;
2161 case SB_LINEDOWN:
2162 term_scroll(0, +1);
2163 break;
2164 case SB_LINEUP:
2165 term_scroll(0, -1);
2166 break;
2167 case SB_PAGEDOWN:
2168 term_scroll(0, +rows / 2);
2169 break;
2170 case SB_PAGEUP:
2171 term_scroll(0, -rows / 2);
2172 break;
2173 case SB_THUMBPOSITION:
2174 case SB_THUMBTRACK:
2175 term_scroll(1, HIWORD(wParam));
2176 break;
2177 }
2178 break;
2179 case WM_PALETTECHANGED:
374330e2 2180 if ((HWND) wParam != hwnd && pal != NULL) {
2181 HDC hdc = get_ctx();
2182 if (hdc) {
32874aea 2183 if (RealizePalette(hdc) > 0)
2184 UpdateColors(hdc);
2185 free_ctx(hdc);
374330e2 2186 }
2187 }
2188 break;
2189 case WM_QUERYNEWPALETTE:
2190 if (pal != NULL) {
2191 HDC hdc = get_ctx();
2192 if (hdc) {
32874aea 2193 if (RealizePalette(hdc) > 0)
2194 UpdateColors(hdc);
2195 free_ctx(hdc);
374330e2 2196 return TRUE;
2197 }
2198 }
2199 return FALSE;
2200 case WM_KEYDOWN:
2201 case WM_SYSKEYDOWN:
c9def1b8 2202 case WM_KEYUP:
2203 case WM_SYSKEYUP:
374330e2 2204 /*
2205 * Add the scan code and keypress timing to the random
7d6ee6ff 2206 * number noise.
374330e2 2207 */
32874aea 2208 noise_ultralight(lParam);
374330e2 2209
2210 /*
2211 * We don't do TranslateMessage since it disassociates the
2212 * resulting CHAR message from the KEYDOWN that sparked it,
2213 * which we occasionally don't want. Instead, we process
2214 * KEYDOWN, and call the Win32 translator functions so that
2215 * we get the translations under _our_ control.
2216 */
2217 {
2218 unsigned char buf[20];
2219 int len;
2220
32874aea 2221 if (wParam == VK_PROCESSKEY) {
3cf144db 2222 MSG m;
32874aea 2223 m.hwnd = hwnd;
2224 m.message = WM_KEYDOWN;
2225 m.wParam = wParam;
2226 m.lParam = lParam & 0xdfff;
2227 TranslateMessage(&m);
2228 } else {
2229 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 2230 if (len == -1)
32874aea 2231 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 2232
b2a1eade 2233 if (len != 0) {
bca9517a 2234 /*
256cb87c 2235 * Interrupt an ongoing paste. I'm not sure
2236 * this is sensible, but for the moment it's
2237 * preferable to having to faff about buffering
2238 * things.
2239 */
2240 term_nopaste();
2241
2242 /*
bca9517a 2243 * We need not bother about stdin backlogs
2244 * here, because in GUI PuTTY we can't do
2245 * anything about it anyway; there's no means
2246 * of asking Windows to hold off on KEYDOWN
2247 * messages. We _have_ to buffer everything
2248 * we're sent.
2249 */
760e88b2 2250 ldisc_send(buf, len, 1);
32874aea 2251 show_mouseptr(0);
bca9517a 2252 }
3cf144db 2253 }
374330e2 2254 }
7732d38a 2255 net_pending_errors();
374330e2 2256 return 0;
4eeb7d09 2257 case WM_INPUTLANGCHANGE:
3cf144db 2258 {
4eeb7d09 2259 /* wParam == Font number */
2260 /* lParam == Locale */
2261 char lbuf[20];
2262 HKL NewInputLocale = (HKL) lParam;
2263
2264 // lParam == GetKeyboardLayout(0);
2265
2266 GetLocaleInfo(LOWORD(NewInputLocale),
2267 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2268
2269 kbd_codepage = atoi(lbuf);
2270 }
2271 break;
71f6951d 2272 case WM_IME_NOTIFY:
2273 if(wParam == IMN_SETOPENSTATUS) {
2274 HIMC hImc = ImmGetContext(hwnd);
2275 ImmSetCompositionFont(hImc, &lfont);
2276 ImmReleaseContext(hwnd, hImc);
2277 return 0;
2278 }
2279 break;
88485e4d 2280 case WM_IME_COMPOSITION:
2281 {
2282 HIMC hIMC;
2283 int n;
2284 char *buff;
2285
2286 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2287 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2288
2289 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2290 break; /* fall back to DefWindowProc */
2291
2292 hIMC = ImmGetContext(hwnd);
2293 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2294
2295 if (n > 0) {
2296 buff = (char*) smalloc(n);
2297 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
760e88b2 2298 luni_send((unsigned short *)buff, n / 2, 1);
88485e4d 2299 free(buff);
2300 }
2301 ImmReleaseContext(hwnd, hIMC);
2302 return 1;
2303 }
2304
4eeb7d09 2305 case WM_IME_CHAR:
2306 if (wParam & 0xFF00) {
3cf144db 2307 unsigned char buf[2];
2308
2309 buf[1] = wParam;
2310 buf[0] = wParam >> 8;
760e88b2 2311 lpage_send(kbd_codepage, buf, 2, 1);
4eeb7d09 2312 } else {
2313 char c = (unsigned char) wParam;
760e88b2 2314 lpage_send(kbd_codepage, &c, 1, 1);
3cf144db 2315 }
4eeb7d09 2316 return (0);
374330e2 2317 case WM_CHAR:
2318 case WM_SYSCHAR:
2319 /*
2320 * Nevertheless, we are prepared to deal with WM_CHAR
2321 * messages, should they crop up. So if someone wants to
2322 * post the things to us as part of a macro manoeuvre,
2323 * we're ready to cope.
2324 */
32874aea 2325 {
4eeb7d09 2326 char c = (unsigned char)wParam;
760e88b2 2327 lpage_send(CP_ACP, &c, 1, 1);
374330e2 2328 }
2329 return 0;
32874aea 2330 case WM_SETCURSOR:
ca3ab3ee 2331 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
32874aea 2332 SetCursor(LoadCursor(NULL, IDC_ARROW));
2333 return TRUE;
2334 }
374330e2 2335 }
2336
32874aea 2337 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2338}
2339
2340/*
ec8679e9 2341 * Move the system caret. (We maintain one, even though it's
2342 * invisible, for the benefit of blind people: apparently some
2343 * helper software tracks the system caret, so we should arrange to
2344 * have one.)
2345 */
32874aea 2346void sys_cursor(int x, int y)
2347{
88485e4d 2348 COMPOSITIONFORM cf;
2349 HIMC hIMC;
2350
2351 if (!has_focus) return;
2352
5a73255e 2353 SetCaretPos(x * font_width + offset_width,
2354 y * font_height + offset_height);
88485e4d 2355
2356 /* IMM calls on Win98 and beyond only */
2357 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2358
2359 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2360 osVersion.dwMinorVersion == 0) return; /* 95 */
2361
2362 /* we should have the IMM functions */
2363 hIMC = ImmGetContext(hwnd);
2364 cf.dwStyle = CFS_POINT;
808d8218 2365 cf.ptCurrentPos.x = x * font_width + offset_width;
2366 cf.ptCurrentPos.y = y * font_height + offset_height;
88485e4d 2367 ImmSetCompositionWindow(hIMC, &cf);
2368
2369 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2370}
2371
2372/*
374330e2 2373 * Draw a line of text in the window, at given character
2374 * coordinates, in given attributes.
2375 *
2376 * We are allowed to fiddle with the contents of `text'.
2377 */
32874aea 2378void do_text(Context ctx, int x, int y, char *text, int len,
2379 unsigned long attr, int lattr)
2380{
374330e2 2381 COLORREF fg, bg, t;
2382 int nfg, nbg, nfont;
2383 HDC hdc = ctx;
59ad2c03 2384 RECT line_box;
2385 int force_manual_underline = 0;
32874aea 2386 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2387 int char_width = fnt_width;
2388 int text_adjust = 0;
2389 static int *IpDx = 0, IpDxLEN = 0;
2390
2391 if (attr & ATTR_WIDE)
2392 char_width *= 2;
59ad2c03 2393
4eeb7d09 2394 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2395 int i;
32874aea 2396 if (len > IpDxLEN) {
59ad2c03 2397 sfree(IpDx);
32874aea 2398 IpDx = smalloc((len + 16) * sizeof(int));
2399 IpDxLEN = (len + 16);
59ad2c03 2400 }
32874aea 2401 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2402 IpDx[i] = char_width;
59ad2c03 2403 }
374330e2 2404
5a73255e 2405 /* Only want the left half of double width lines */
2406 if (lattr != LATTR_NORM && x*2 >= cols)
2407 return;
2408
c9def1b8 2409 x *= fnt_width;
374330e2 2410 y *= font_height;
5a73255e 2411 x += offset_width;
2412 y += offset_height;
374330e2 2413
4eeb7d09 2414 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2415 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2416 attr ^= ATTR_CUR_XOR;
2417 }
2418
2419 nfont = 0;
4eeb7d09 2420 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2421 /* Assume a poorman font is borken in other ways too. */
2422 lattr = LATTR_WIDE;
2423 } else
2424 switch (lattr) {
2425 case LATTR_NORM:
2426 break;
2427 case LATTR_WIDE:
2428 nfont |= FONT_WIDE;
374330e2 2429 break;
4eeb7d09 2430 default:
2431 nfont |= FONT_WIDE + FONT_HIGH;
2432 break;
2433 }
5a73255e 2434 if (attr & ATTR_NARROW)
2435 nfont |= FONT_NARROW;
4eeb7d09 2436
2437 /* Special hack for the VT100 linedraw glyphs. */
2438 if ((attr & CSET_MASK) == 0x2300) {
5a73255e 2439 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
4eeb7d09 2440 switch ((unsigned char) (text[0])) {
2441 case 0xBA:
2442 text_adjust = -2 * font_height / 5;
2443 break;
2444 case 0xBB:
2445 text_adjust = -1 * font_height / 5;
2446 break;
2447 case 0xBC:
2448 text_adjust = font_height / 5;
2449 break;
2450 case 0xBD:
2451 text_adjust = 2 * font_height / 5;
32874aea 2452 break;
59ad2c03 2453 }
4eeb7d09 2454 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2455 text_adjust *= 2;
2456 attr &= ~CSET_MASK;
2457 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2458 attr |= (unitab_xterm['q'] & CSET_MASK);
2459 if (attr & ATTR_UNDER) {
2460 attr &= ~ATTR_UNDER;
2461 force_manual_underline = 1;
2462 }
374330e2 2463 }
2464 }
2465
4eeb7d09 2466 /* Anything left as an original character set is unprintable. */
2467 if (DIRECT_CHAR(attr)) {
2468 attr &= ~CSET_MASK;
2469 attr |= 0xFF00;
5a73255e 2470 memset(text, 0xFD, len);
4eeb7d09 2471 }
2472
2473 /* OEM CP */
2474 if ((attr & CSET_MASK) == ATTR_OEMCP)
2475 nfont |= FONT_OEM;
2476
374330e2 2477 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2478 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2479 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2480 nfont |= FONT_BOLD;
2481 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2482 nfont |= FONT_UNDERLINE;
4eeb7d09 2483 another_font(nfont);
32874aea 2484 if (!fonts[nfont]) {
2485 if (nfont & FONT_UNDERLINE)
59ad2c03 2486 force_manual_underline = 1;
2487 /* Don't do the same for manual bold, it could be bad news. */
2488
32874aea 2489 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2490 }
4eeb7d09 2491 another_font(nfont);
2492 if (!fonts[nfont])
2493 nfont = FONT_NORMAL;
374330e2 2494 if (attr & ATTR_REVERSE) {
32874aea 2495 t = nfg;
2496 nfg = nbg;
2497 nbg = t;
374330e2 2498 }
2499 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2500 nfg++;
59ad2c03 2501 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2502 nbg++;
374330e2 2503 fg = colours[nfg];
2504 bg = colours[nbg];
32874aea 2505 SelectObject(hdc, fonts[nfont]);
2506 SetTextColor(hdc, fg);
2507 SetBkColor(hdc, bg);
2508 SetBkMode(hdc, OPAQUE);
2509 line_box.left = x;
2510 line_box.top = y;
4eeb7d09 2511 line_box.right = x + char_width * len;
32874aea 2512 line_box.bottom = y + font_height;
4eeb7d09 2513
5a73255e 2514 /* Only want the left half of double width lines */
2515 if (line_box.right > font_width*cols+offset_width)
2516 line_box.right = font_width*cols+offset_width;
2517
4eeb7d09 2518 /* We're using a private area for direct to font. (512 chars.) */
2519 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2520 /* Ho Hum, dbcs fonts are a PITA! */
2521 /* To display on W9x I have to convert to UCS */
2522 static wchar_t *uni_buf = 0;
2523 static int uni_len = 0;
5a73255e 2524 int nlen, mptr;
4eeb7d09 2525 if (len > uni_len) {
2526 sfree(uni_buf);
2527 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2528 }
4eeb7d09 2529
5a73255e 2530 for(nlen = mptr = 0; mptr<len; mptr++) {
2531 uni_buf[nlen] = 0xFFFD;
2532 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2533 IpDx[nlen] += char_width;
2534 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2535 text+mptr, 2, uni_buf+nlen, 1);
2536 mptr++;
2537 }
2538 else
2539 {
2540 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2541 text+mptr, 1, uni_buf+nlen, 1);
2542 }
2543 nlen++;
2544 }
4eeb7d09 2545 if (nlen <= 0)
2546 return; /* Eeek! */
2547
2548 ExtTextOutW(hdc, x,
2549 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 2550 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2551 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2552 SetBkMode(hdc, TRANSPARENT);
2553 ExtTextOutW(hdc, x - 1,
2554 y - font_height * (lattr ==
2555 LATTR_BOT) + text_adjust,
5a73255e 2556 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2557 }
5a73255e 2558
2559 IpDx[0] = -1;
4eeb7d09 2560 } else if (DIRECT_FONT(attr)) {
2561 ExtTextOut(hdc, x,
2562 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2563 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2564 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2565 SetBkMode(hdc, TRANSPARENT);
2566
2567 /* GRR: This draws the character outside it's box and can leave
2568 * 'droppings' even with the clip box! I suppose I could loop it
2569 * one character at a time ... yuk.
2570 *
2571 * Or ... I could do a test print with "W", and use +1 or -1 for this
2572 * shift depending on if the leftmost column is blank...
2573 */
2574 ExtTextOut(hdc, x - 1,
2575 y - font_height * (lattr ==
2576 LATTR_BOT) + text_adjust,
2577 ETO_CLIPPED, &line_box, text, len, IpDx);
2578 }
2579 } else {
2580 /* And 'normal' unicode characters */
2581 static WCHAR *wbuf = NULL;
2582 static int wlen = 0;
2583 int i;
2584 if (wlen < len) {
2585 sfree(wbuf);
2586 wlen = len;
2587 wbuf = smalloc(wlen * sizeof(WCHAR));
2588 }
2589 for (i = 0; i < len; i++)
2590 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2591
2592 ExtTextOutW(hdc, x,
2593 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2594 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2595
2596 /* And the shadow bold hack. */
5a73255e 2597 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 2598 SetBkMode(hdc, TRANSPARENT);
2599 ExtTextOutW(hdc, x - 1,
2600 y - font_height * (lattr ==
2601 LATTR_BOT) + text_adjust,
2602 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2603 }
374330e2 2604 }
4eeb7d09 2605 if (lattr != LATTR_TOP && (force_manual_underline ||
2606 (und_mode == UND_LINE
2607 && (attr & ATTR_UNDER)))) {
32874aea 2608 HPEN oldpen;
4eeb7d09 2609 int dec = descent;
2610 if (lattr == LATTR_BOT)
2611 dec = dec * 2 - font_height;
2612
32874aea 2613 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2614 MoveToEx(hdc, x, y + dec, NULL);
2615 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2616 oldpen = SelectObject(hdc, oldpen);
2617 DeleteObject(oldpen);
374330e2 2618 }
4eeb7d09 2619}
2620
2621void do_cursor(Context ctx, int x, int y, char *text, int len,
2622 unsigned long attr, int lattr)
2623{
2624
2625 int fnt_width;
2626 int char_width;
2627 HDC hdc = ctx;
2628 int ctype = cfg.cursor_type;
2629
2630 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2631 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2632 do_text(ctx, x, y, text, len, attr, lattr);
2633 return;
2634 }
2635 ctype = 2;
2636 attr |= TATTR_RIGHTCURS;
2637 }
2638
2639 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2640 if (attr & ATTR_WIDE)
2641 char_width *= 2;
2642 x *= fnt_width;
2643 y *= font_height;
5a73255e 2644 x += offset_width;
2645 y += offset_height;
4eeb7d09 2646
2647 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
374330e2 2648 POINT pts[5];
32874aea 2649 HPEN oldpen;
374330e2 2650 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2651 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2652 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2653 pts[1].y = pts[2].y = y + font_height - 1;
2654 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2655 Polyline(hdc, pts, 5);
2656 oldpen = SelectObject(hdc, oldpen);
2657 DeleteObject(oldpen);
4eeb7d09 2658 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2659 int startx, starty, dx, dy, length, i;
4eeb7d09 2660 if (ctype == 1) {
32874aea 2661 startx = x;
2662 starty = y + descent;
2663 dx = 1;
2664 dy = 0;
4eeb7d09 2665 length = char_width;
32874aea 2666 } else {
4e30ff69 2667 int xadjust = 0;
4eeb7d09 2668 if (attr & TATTR_RIGHTCURS)
2669 xadjust = char_width - 1;
32874aea 2670 startx = x + xadjust;
2671 starty = y;
2672 dx = 0;
2673 dy = 1;
2674 length = font_height;
2675 }
4eeb7d09 2676 if (attr & TATTR_ACTCURS) {
32874aea 2677 HPEN oldpen;
2678 oldpen =
2679 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2680 MoveToEx(hdc, startx, starty, NULL);
2681 LineTo(hdc, startx + dx * length, starty + dy * length);
2682 oldpen = SelectObject(hdc, oldpen);
2683 DeleteObject(oldpen);
2684 } else {
2685 for (i = 0; i < length; i++) {
2686 if (i % 2 == 0) {
2687 SetPixel(hdc, startx, starty, colours[23]);
2688 }
2689 startx += dx;
2690 starty += dy;
2691 }
2692 }
4e30ff69 2693 }
374330e2 2694}
2695
5a73255e 2696/* This function gets the actual width of a character in the normal font.
2697 */
2698int CharWidth(Context ctx, int uc) {
2699 HDC hdc = ctx;
2700 int ibuf = 0;
2701
2702 /* If the font max is the same as the font ave width then this
2703 * function is a no-op.
2704 */
2705 if (!font_dualwidth) return 1;
2706
2707 switch (uc & CSET_MASK) {
2708 case ATTR_ASCII:
2709 uc = unitab_line[uc & 0xFF];
2710 break;
2711 case ATTR_LINEDRW:
2712 uc = unitab_xterm[uc & 0xFF];
2713 break;
2714 case ATTR_SCOACS:
2715 uc = unitab_scoacs[uc & 0xFF];
2716 break;
2717 }
2718 if (DIRECT_FONT(uc)) {
2719 if (dbcs_screenfont) return 1;
2720
2721 /* Speedup, I know of no font where ascii is the wrong width */
2722 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2723 return 1;
2724
2725 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2726 SelectObject(hdc, fonts[FONT_NORMAL]);
2727 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2728 another_font(FONT_OEM);
2729 if (!fonts[FONT_OEM]) return 0;
2730
2731 SelectObject(hdc, fonts[FONT_OEM]);
2732 } else
2733 return 0;
2734
2735 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2736 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2737 return 0;
2738 } else {
2739 /* Speedup, I know of no font where ascii is the wrong width */
2740 if (uc >= ' ' && uc <= '~') return 1;
2741
2742 SelectObject(hdc, fonts[FONT_NORMAL]);
2743 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2744 /* Okay that one worked */ ;
2745 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2746 /* This should work on 9x too, but it's "less accurate" */ ;
2747 else
2748 return 0;
2749 }
2750
2751 ibuf += font_width / 2 -1;
2752 ibuf /= font_width;
2753
2754 return ibuf;
2755}
2756
374330e2 2757/*
c9def1b8 2758 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2759 * codes. Returns number of bytes used or zero to drop the message
2760 * or -1 to forward the message to windows.
374330e2 2761 */
3cf144db 2762static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 2763 unsigned char *output)
2764{
374330e2 2765 BYTE keystate[256];
32874aea 2766 int scan, left_alt = 0, key_down, shift_state;
2767 int r, i, code;
2768 unsigned char *p = output;
4eeb7d09 2769 static int alt_sum = 0;
374330e2 2770
00e3ba0f 2771 HKL kbd_layout = GetKeyboardLayout(0);
2772
0c50ef57 2773 static WORD keys[3];
0c50ef57 2774 static int compose_char = 0;
2775 static WPARAM compose_key = 0;
32874aea 2776
c9def1b8 2777 r = GetKeyboardState(keystate);
32874aea 2778 if (!r)
2779 memset(keystate, 0, sizeof(keystate));
2780 else {
ec55b220 2781#if 0
4eeb7d09 2782#define SHOW_TOASCII_RESULT
32874aea 2783 { /* Tell us all about key events */
2784 static BYTE oldstate[256];
2785 static int first = 1;
2786 static int scan;
2787 int ch;
2788 if (first)
2789 memcpy(oldstate, keystate, sizeof(oldstate));
2790 first = 0;
2791
2792 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2793 debug(("+"));
2794 } else if ((HIWORD(lParam) & KF_UP)
2795 && scan == (HIWORD(lParam) & 0xFF)) {
2796 debug((". U"));
2797 } else {
2798 debug((".\n"));
2799 if (wParam >= VK_F1 && wParam <= VK_F20)
2800 debug(("K_F%d", wParam + 1 - VK_F1));
2801 else
2802 switch (wParam) {
2803 case VK_SHIFT:
2804 debug(("SHIFT"));
2805 break;
2806 case VK_CONTROL:
2807 debug(("CTRL"));
2808 break;
2809 case VK_MENU:
2810 debug(("ALT"));
2811 break;
2812 default:
2813 debug(("VK_%02x", wParam));
2814 }
2815 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2816 debug(("*"));
2817 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2818
2819 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2820 if (ch >= ' ' && ch <= '~')
2821 debug((", '%c'", ch));
2822 else if (ch)
2823 debug((", $%02x", ch));
2824
2825 if (keys[0])
2826 debug((", KB0=%02x", keys[0]));
2827 if (keys[1])
2828 debug((", KB1=%02x", keys[1]));
2829 if (keys[2])
2830 debug((", KB2=%02x", keys[2]));
2831
2832 if ((keystate[VK_SHIFT] & 0x80) != 0)
2833 debug((", S"));
2834 if ((keystate[VK_CONTROL] & 0x80) != 0)
2835 debug((", C"));
2836 if ((HIWORD(lParam) & KF_EXTENDED))
2837 debug((", E"));
2838 if ((HIWORD(lParam) & KF_UP))
2839 debug((", U"));
2840 }
2841
2842 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2843 else if ((HIWORD(lParam) & KF_UP))
2844 oldstate[wParam & 0xFF] ^= 0x80;
2845 else
2846 oldstate[wParam & 0xFF] ^= 0x81;
2847
2848 for (ch = 0; ch < 256; ch++)
2849 if (oldstate[ch] != keystate[ch])
2850 debug((", M%02x=%02x", ch, keystate[ch]));
2851
2852 memcpy(oldstate, keystate, sizeof(oldstate));
2853 }
ec55b220 2854#endif
2855
32874aea 2856 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 2857 keystate[VK_RMENU] = keystate[VK_MENU];
2858 }
2859
c9def1b8 2860
2861 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 2862 if ((cfg.funky_type == 3 ||
2863 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2864 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 2865
2866 wParam = VK_EXECUTE;
2867
2868 /* UnToggle NUMLock */
32874aea 2869 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2870 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 2871 }
2872
2873 /* And write back the 'adjusted' state */
32874aea 2874 SetKeyboardState(keystate);
c9def1b8 2875 }
2876
2877 /* Disable Auto repeat if required */
32874aea 2878 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2879 return 0;
c9def1b8 2880
32874aea 2881 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 2882 left_alt = 1;
2883
32874aea 2884 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 2885
95bbe1ae 2886 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 2887 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 2888 if (cfg.ctrlaltkeys)
2889 keystate[VK_MENU] = 0;
2890 else {
2891 keystate[VK_RMENU] = 0x80;
2892 left_alt = 0;
2893 }
2894 }
c9def1b8 2895
5a73255e 2896 alt_pressed = (left_alt && key_down);
2897
c9def1b8 2898 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 2899 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2900 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 2901
95bbe1ae 2902 /* Note if AltGr was pressed and if it was used as a compose key */
2903 if (!compose_state) {
159eba53 2904 compose_key = 0x100;
95bbe1ae 2905 if (cfg.compose_key) {
32874aea 2906 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 2907 compose_key = wParam;
2908 }
2909 if (wParam == VK_APPS)
2910 compose_key = wParam;
2911 }
2912
32874aea 2913 if (wParam == compose_key) {
2914 if (compose_state == 0
2915 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2916 1;
2917 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 2918 compose_state = 2;
2919 else
2920 compose_state = 0;
32874aea 2921 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 2922 compose_state = 0;
2923
67c339f7 2924 /*
cabfd08c 2925 * Record that we pressed key so the scroll window can be reset, but
2926 * be careful to avoid Shift-UP/Down
2927 */
a910b704 2928 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
2929 wParam != VK_MENU && wParam != VK_CONTROL) {
32874aea 2930 seen_key_event = 1;
cabfd08c 2931 }
2932
32874aea 2933 if (compose_state > 1 && left_alt)
2934 compose_state = 0;
67c339f7 2935
c9def1b8 2936 /* Sanitize the number pad if not using a PC NumPad */
32874aea 2937 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2938 && cfg.funky_type != 2)
2939 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2940 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 2941 int nParam = 0;
32874aea 2942 switch (wParam) {
2943 case VK_INSERT:
2944 nParam = VK_NUMPAD0;
2945 break;
2946 case VK_END:
2947 nParam = VK_NUMPAD1;
2948 break;
2949 case VK_DOWN:
2950 nParam = VK_NUMPAD2;
2951 break;
2952 case VK_NEXT:
2953 nParam = VK_NUMPAD3;
2954 break;
2955 case VK_LEFT:
2956 nParam = VK_NUMPAD4;
2957 break;
2958 case VK_CLEAR:
2959 nParam = VK_NUMPAD5;
2960 break;
2961 case VK_RIGHT:
2962 nParam = VK_NUMPAD6;
2963 break;
2964 case VK_HOME:
2965 nParam = VK_NUMPAD7;
2966 break;
2967 case VK_UP:
2968 nParam = VK_NUMPAD8;
2969 break;
2970 case VK_PRIOR:
2971 nParam = VK_NUMPAD9;
2972 break;
2973 case VK_DELETE:
2974 nParam = VK_DECIMAL;
2975 break;
c9def1b8 2976 }
32874aea 2977 if (nParam) {
2978 if (keystate[VK_NUMLOCK] & 1)
2979 shift_state |= 1;
c9def1b8 2980 wParam = nParam;
2981 }
25d39ef6 2982 }
2983 }
2984
c9def1b8 2985 /* If a key is pressed and AltGr is not active */
32874aea 2986 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2987 /* Okay, prepare for most alts then ... */
2988 if (left_alt)
2989 *p++ = '\033';
374330e2 2990
c9def1b8 2991 /* Lets see if it's a pattern we know all about ... */
2992 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 2993 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2994 return 0;
c9def1b8 2995 }
2996 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 2997 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2998 return 0;
2999 }
3000 if (wParam == VK_INSERT && shift_state == 1) {
568dd02f 3001 term_do_paste();
32874aea 3002 return 0;
3003 }
c9def1b8 3004 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 3005 return -1;
c9def1b8 3006 }
3007 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 3008 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3009 return -1;
c9def1b8 3010 }
2cae8529 3011 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3012 (cfg.resize_action != RESIZE_DISABLED)) {
8f57d753 3013 flip_full_screen();
3014 return -1;
3015 }
ec55b220 3016 /* Control-Numlock for app-keypad mode switch */
3017 if (wParam == VK_PAUSE && shift_state == 2) {
3018 app_keypad_keys ^= 1;
3019 return 0;
3020 }
374330e2 3021
c9def1b8 3022 /* Nethack keypad */
3023 if (cfg.nethack_keypad && !left_alt) {
32874aea 3024 switch (wParam) {
3025 case VK_NUMPAD1:
3026 *p++ = shift_state ? 'B' : 'b';
3027 return p - output;
3028 case VK_NUMPAD2:
3029 *p++ = shift_state ? 'J' : 'j';
3030 return p - output;
3031 case VK_NUMPAD3:
3032 *p++ = shift_state ? 'N' : 'n';
3033 return p - output;
3034 case VK_NUMPAD4:
3035 *p++ = shift_state ? 'H' : 'h';
3036 return p - output;
3037 case VK_NUMPAD5:
3038 *p++ = shift_state ? '.' : '.';
3039 return p - output;
3040 case VK_NUMPAD6:
3041 *p++ = shift_state ? 'L' : 'l';
3042 return p - output;
3043 case VK_NUMPAD7:
3044 *p++ = shift_state ? 'Y' : 'y';
3045 return p - output;
3046 case VK_NUMPAD8:
3047 *p++ = shift_state ? 'K' : 'k';
3048 return p - output;
3049 case VK_NUMPAD9:
3050 *p++ = shift_state ? 'U' : 'u';
3051 return p - output;
3052 }
c9def1b8 3053 }
3054
3055 /* Application Keypad */
3056 if (!left_alt) {
32874aea 3057 int xkey = 0;
3058
3059 if (cfg.funky_type == 3 ||
3060 (cfg.funky_type <= 1 &&
3061 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3062 case VK_EXECUTE:
3063 xkey = 'P';
3064 break;
3065 case VK_DIVIDE:
3066 xkey = 'Q';
3067 break;
3068 case VK_MULTIPLY:
3069 xkey = 'R';
3070 break;
3071 case VK_SUBTRACT:
3072 xkey = 'S';
3073 break;
3074 }
3075 if (app_keypad_keys && !cfg.no_applic_k)
3076 switch (wParam) {
3077 case VK_NUMPAD0:
3078 xkey = 'p';
3079 break;
3080 case VK_NUMPAD1:
3081 xkey = 'q';
3082 break;
3083 case VK_NUMPAD2:
3084 xkey = 'r';
3085 break;
3086 case VK_NUMPAD3:
3087 xkey = 's';
3088 break;
3089 case VK_NUMPAD4:
3090 xkey = 't';
3091 break;
3092 case VK_NUMPAD5:
3093 xkey = 'u';
3094 break;
3095 case VK_NUMPAD6:
3096 xkey = 'v';
3097 break;
3098 case VK_NUMPAD7:
3099 xkey = 'w';
3100 break;
3101 case VK_NUMPAD8:
3102 xkey = 'x';
3103 break;
3104 case VK_NUMPAD9:
3105 xkey = 'y';
3106 break;
3107
3108 case VK_DECIMAL:
3109 xkey = 'n';
3110 break;
3111 case VK_ADD:
3112 if (cfg.funky_type == 2) {
3113 if (shift_state)
3114 xkey = 'l';
3115 else
3116 xkey = 'k';
3117 } else if (shift_state)
3118 xkey = 'm';
c9def1b8 3119 else
32874aea 3120 xkey = 'l';
3121 break;
3122
3123 case VK_DIVIDE:
3124 if (cfg.funky_type == 2)
3125 xkey = 'o';
3126 break;
3127 case VK_MULTIPLY:
3128 if (cfg.funky_type == 2)
3129 xkey = 'j';
3130 break;
3131 case VK_SUBTRACT:
3132 if (cfg.funky_type == 2)
3133 xkey = 'm';
3134 break;
3135
3136 case VK_RETURN:
3137 if (HIWORD(lParam) & KF_EXTENDED)
3138 xkey = 'M';
3139 break;
c9def1b8 3140 }
32874aea 3141 if (xkey) {
3142 if (vt52_mode) {
3143 if (xkey >= 'P' && xkey <= 'S')
3144 p += sprintf((char *) p, "\x1B%c", xkey);
3145 else
3146 p += sprintf((char *) p, "\x1B?%c", xkey);
3147 } else
3148 p += sprintf((char *) p, "\x1BO%c", xkey);
3149 return p - output;
c9def1b8 3150 }
3151 }
3152
32874aea 3153 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3154 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3155 *p++ = 0;
3156 return -2;
c9def1b8 3157 }
32874aea 3158 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3159 *p++ = 0x1B;
3160 *p++ = '[';
3161 *p++ = 'Z';
3162 return p - output;
c9def1b8 3163 }
32874aea 3164 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3165 *p++ = 0;
3166 return p - output;
c9def1b8 3167 }
32874aea 3168 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3169 *p++ = 160;
3170 return p - output;
c9def1b8 3171 }
32874aea 3172 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3173 *p++ = 3;
a5f3e637 3174 *p++ = 0;
3175 return -2;
c9def1b8 3176 }
32874aea 3177 if (wParam == VK_PAUSE) { /* Break/Pause */
3178 *p++ = 26;
3179 *p++ = 0;
3180 return -2;
95bbe1ae 3181 }
c9def1b8 3182 /* Control-2 to Control-8 are special */
32874aea 3183 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3184 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3185 return p - output;
3186 }
3187 if (shift_state == 2 && wParam == 0xBD) {
3188 *p++ = 0x1F;
3189 return p - output;
3190 }
3191 if (shift_state == 2 && wParam == 0xDF) {
3192 *p++ = 0x1C;
3193 return p - output;
3194 }
3195 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
32874aea 3196 *p++ = '\r';
3197 *p++ = '\n';
c9def1b8 3198 return p - output;
3199 }
374330e2 3200
c5e9c988 3201 /*
c9def1b8 3202 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3203 * for integer decimal nn.)
3204 *
3205 * We also deal with the weird ones here. Linux VCs replace F1
3206 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3207 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3208 * respectively.
c5e9c988 3209 */
c9def1b8 3210 code = 0;
3211 switch (wParam) {
32874aea 3212 case VK_F1:
3213 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3214 break;
3215 case VK_F2:
3216 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3217 break;
3218 case VK_F3:
3219 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3220 break;
3221 case VK_F4:
3222 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3223 break;
3224 case VK_F5:
3225 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3226 break;
3227 case VK_F6:
3228 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3229 break;
3230 case VK_F7:
3231 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3232 break;
3233 case VK_F8:
3234 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3235 break;
3236 case VK_F9:
3237 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3238 break;
3239 case VK_F10:
3240 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3241 break;
3242 case VK_F11:
3243 code = 23;
3244 break;
3245 case VK_F12:
3246 code = 24;
3247 break;
3248 case VK_F13:
3249 code = 25;
3250 break;
3251 case VK_F14:
3252 code = 26;
3253 break;
3254 case VK_F15:
3255 code = 28;
3256 break;
3257 case VK_F16:
3258 code = 29;
3259 break;
3260 case VK_F17:
3261 code = 31;
3262 break;
3263 case VK_F18:
3264 code = 32;
3265 break;
3266 case VK_F19:
3267 code = 33;
3268 break;
3269 case VK_F20:
3270 code = 34;
3271 break;
dfca2656 3272 }
3273 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3274 case VK_HOME:
3275 code = 1;
3276 break;
3277 case VK_INSERT:
3278 code = 2;
3279 break;
3280 case VK_DELETE:
3281 code = 3;
3282 break;
3283 case VK_END:
3284 code = 4;
3285 break;
3286 case VK_PRIOR:
3287 code = 5;
3288 break;
3289 case VK_NEXT:
3290 code = 6;
3291 break;
374330e2 3292 }
ec55b220 3293 /* Reorder edit keys to physical order */
32874aea 3294 if (cfg.funky_type == 3 && code <= 6)
3295 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3296
f37caa11 3297 if (vt52_mode && code > 0 && code <= 6) {
32874aea 3298 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3299 return p - output;
3300 }
3301
9bc81a2c 3302 if (cfg.funky_type == 5 && /* SCO function keys */
3303 code >= 11 && code <= 34) {
e24b1972 3304 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3305 int index = 0;
3306 switch (wParam) {
3307 case VK_F1: index = 0; break;
3308 case VK_F2: index = 1; break;
3309 case VK_F3: index = 2; break;
3310 case VK_F4: index = 3; break;
3311 case VK_F5: index = 4; break;
3312 case VK_F6: index = 5; break;
3313 case VK_F7: index = 6; break;
3314 case VK_F8: index = 7; break;
3315 case VK_F9: index = 8; break;
3316 case VK_F10: index = 9; break;
3317 case VK_F11: index = 10; break;
3318 case VK_F12: index = 11; break;
3319 }
3320 if (keystate[VK_SHIFT] & 0x80) index += 12;
3321 if (keystate[VK_CONTROL] & 0x80) index += 24;
3322 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3323 return p - output;
3324 }
9bc81a2c 3325 if (cfg.funky_type == 5 && /* SCO small keypad */
3326 code >= 1 && code <= 6) {
3327 char codes[] = "HL.FIG";
3328 if (code == 3) {
3329 *p++ = '\x7F';
3330 } else {
3331 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3332 }
3333 return p - output;
3334 }
f37caa11 3335 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3336 int offt = 0;
32874aea 3337 if (code > 15)
3338 offt++;
3339 if (code > 21)
3340 offt++;
f37caa11 3341 if (vt52_mode)
32874aea 3342 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3343 else
32874aea 3344 p +=
3345 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3346 return p - output;
3347 }
c9def1b8 3348 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 3349 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3350 return p - output;
3351 }
3352 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
ec55b220 3353 if (vt52_mode)
32874aea 3354 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3355 else
32874aea 3356 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3357 return p - output;
3358 }
3359 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3360 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3361 return p - output;
3362 }
3363 if (code) {
32874aea 3364 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3365 return p - output;
374330e2 3366 }
45dabbc5 3367
c9def1b8 3368 /*
3369 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3370 * some reason seems to send VK_CLEAR to Windows...).
3371 */
3372 {
3373 char xkey = 0;
3374 switch (wParam) {
32874aea 3375 case VK_UP:
3376 xkey = 'A';
3377 break;
3378 case VK_DOWN:
3379 xkey = 'B';
3380 break;
3381 case VK_RIGHT:
3382 xkey = 'C';
3383 break;
3384 case VK_LEFT:
3385 xkey = 'D';
3386 break;
3387 case VK_CLEAR:
3388 xkey = 'G';
3389 break;
c9def1b8 3390 }
32874aea 3391 if (xkey) {
c9def1b8 3392 if (vt52_mode)
32874aea 3393 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3394 else {
3395 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3396 /* VT100 & VT102 manuals both state the app cursor keys
3397 * only work if the app keypad is on.
3398 */
3399 if (!app_keypad_keys)
3400 app_flg = 0;
3401 /* Useful mapping of Ctrl-arrows */
3402 if (shift_state == 2)
3403 app_flg = !app_flg;
3404
3405 if (app_flg)
3406 p += sprintf((char *) p, "\x1BO%c", xkey);
3407 else
3408 p += sprintf((char *) p, "\x1B[%c", xkey);
3409 }
c9def1b8 3410 return p - output;
3411 }
3412 }
0c50ef57 3413
3414 /*
3415 * Finally, deal with Return ourselves. (Win95 seems to
3416 * foul it up when Alt is pressed, for some reason.)
3417 */
32874aea 3418 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3419 *p++ = 0x0D;
a5f3e637 3420 *p++ = 0;
3421 return -2;
0c50ef57 3422 }
4eeb7d09 3423
3424 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3425 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3426 else
3427 alt_sum = 0;
67c339f7 3428 }
374330e2 3429
c9def1b8 3430 /* Okay we've done everything interesting; let windows deal with
3431 * the boring stuff */
3432 {
a9c02454 3433 BOOL capsOn=0;
3434
3435 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3436 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3437 capsOn= !left_alt;
3438 keystate[VK_CAPITAL] = 0;
3439 }
3440
00e3ba0f 3441 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 3442#ifdef SHOW_TOASCII_RESULT
3443 if (r == 1 && !key_down) {
3444 if (alt_sum) {
8f22582c 3445 if (in_utf || dbcs_screenfont)
4eeb7d09 3446 debug((", (U+%04x)", alt_sum));
3447 else
3448 debug((", LCH(%d)", alt_sum));
3449 } else {
3450 debug((", ACH(%d)", keys[0]));
3451 }
3452 } else if (r > 0) {
3453 int r1;
3454 debug((", ASC("));
3455 for (r1 = 0; r1 < r; r1++) {
3456 debug(("%s%d", r1 ? "," : "", keys[r1]));
3457 }
3458 debug((")"));
3459 }
3460#endif
32874aea 3461 if (r > 0) {
4eeb7d09 3462 WCHAR keybuf;
256cb87c 3463
3464 /*
3465 * Interrupt an ongoing paste. I'm not sure this is
3466 * sensible, but for the moment it's preferable to
3467 * having to faff about buffering things.
3468 */
3469 term_nopaste();
3470
c9def1b8 3471 p = output;
32874aea 3472 for (i = 0; i < r; i++) {
3473 unsigned char ch = (unsigned char) keys[i];
14963b8f 3474
32874aea 3475 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3476 compose_char = ch;
32874aea 3477 compose_state++;
c9def1b8 3478 continue;
3479 }
32874aea 3480 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3481 int nc;
3482 compose_state = 0;
3483
32874aea 3484 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3485 MessageBeep(MB_ICONHAND);
c9def1b8 3486 return 0;
3487 }
4eeb7d09 3488 keybuf = nc;
760e88b2 3489 luni_send(&keybuf, 1, 1);
4eeb7d09 3490 continue;
c9def1b8 3491 }
374330e2 3492
c9def1b8 3493 compose_state = 0;
374330e2 3494
4eeb7d09 3495 if (!key_down) {
3496 if (alt_sum) {
8f22582c 3497 if (in_utf || dbcs_screenfont) {
4eeb7d09 3498 keybuf = alt_sum;
760e88b2 3499 luni_send(&keybuf, 1, 1);
4eeb7d09 3500 } else {
3501 ch = (char) alt_sum;
5471d09a 3502 /*
3503 * We need not bother about stdin
3504 * backlogs here, because in GUI PuTTY
3505 * we can't do anything about it
3506 * anyway; there's no means of asking
3507 * Windows to hold off on KEYDOWN
3508 * messages. We _have_ to buffer
3509 * everything we're sent.
3510 */
760e88b2 3511 ldisc_send(&ch, 1, 1);
4eeb7d09 3512 }
3513 alt_sum = 0;
3514 } else
760e88b2 3515 lpage_send(kbd_codepage, &ch, 1, 1);
4eeb7d09 3516 } else {
a9c02454 3517 if(capsOn && ch < 0x80) {
3518 WCHAR cbuf[2];
3519 cbuf[0] = 27;
3520 cbuf[1] = xlat_uskbd2cyrllic(ch);
760e88b2 3521 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3522 } else {
3523 char cbuf[2];
3524 cbuf[0] = '\033';
3525 cbuf[1] = ch;
760e88b2 3526 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3527 }
c9def1b8 3528 }
bca9517a 3529 show_mouseptr(0);
c9def1b8 3530 }
374330e2 3531
c9def1b8 3532 /* This is so the ALT-Numpad and dead keys work correctly. */
3533 keys[0] = 0;
3534
32874aea 3535 return p - output;
c9def1b8 3536 }
159eba53 3537 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3538 if (!left_alt)
3539 keys[0] = 0;
4eeb7d09 3540 /* If we will be using alt_sum fix the 256s */
8f22582c 3541 else if (keys[0] && (in_utf || dbcs_screenfont))
4eeb7d09 3542 keys[0] = 10;
374330e2 3543 }
3544
dfca2656 3545 /*
3546 * ALT alone may or may not want to bring up the System menu.
3547 * If it's not meant to, we return 0 on presses or releases of
3548 * ALT, to show that we've swallowed the keystroke. Otherwise
3549 * we return -1, which means Windows will give the keystroke
3550 * its default handling (i.e. bring up the System menu).
3551 */
3552 if (wParam == VK_MENU && !cfg.alt_only)
3553 return 0;
374330e2 3554
c9def1b8 3555 return -1;
374330e2 3556}
3557
32874aea 3558void set_title(char *title)
3559{
3560 sfree(window_name);
3561 window_name = smalloc(1 + strlen(title));
3562 strcpy(window_name, title);
37508af4 3563 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3564 SetWindowText(hwnd, title);
374330e2 3565}
3566
32874aea 3567void set_icon(char *title)
3568{
3569 sfree(icon_name);
3570 icon_name = smalloc(1 + strlen(title));
3571 strcpy(icon_name, title);
37508af4 3572 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3573 SetWindowText(hwnd, title);
374330e2 3574}
3575
32874aea 3576void set_sbar(int total, int start, int page)
3577{
374330e2 3578 SCROLLINFO si;
c9def1b8 3579
32874aea 3580 if (!cfg.scrollbar)
3581 return;
c9def1b8 3582
374330e2 3583 si.cbSize = sizeof(si);
c9def1b8 3584 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3585 si.nMin = 0;
3586 si.nMax = total - 1;
3587 si.nPage = page;
3588 si.nPos = start;
c1f5f956 3589 if (hwnd)
32874aea 3590 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3591}
3592
32874aea 3593Context get_ctx(void)
3594{
374330e2 3595 HDC hdc;
3596 if (hwnd) {
32874aea 3597 hdc = GetDC(hwnd);
374330e2 3598 if (hdc && pal)
32874aea 3599 SelectPalette(hdc, pal, FALSE);
374330e2 3600 return hdc;
3601 } else
3602 return NULL;
3603}
3604
32874aea 3605void free_ctx(Context ctx)
3606{
3607 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3608 ReleaseDC(hwnd, ctx);
374330e2 3609}
3610
32874aea 3611static void real_palette_set(int n, int r, int g, int b)
3612{
374330e2 3613 if (pal) {
3614 logpal->palPalEntry[n].peRed = r;
3615 logpal->palPalEntry[n].peGreen = g;
3616 logpal->palPalEntry[n].peBlue = b;
3617 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3618 colours[n] = PALETTERGB(r, g, b);
32874aea 3619 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3620 } else
3621 colours[n] = RGB(r, g, b);
3622}
3623
32874aea 3624void palette_set(int n, int r, int g, int b)
3625{
374330e2 3626 static const int first[21] = {
3627 0, 2, 4, 6, 8, 10, 12, 14,
3628 1, 3, 5, 7, 9, 11, 13, 15,
3629 16, 17, 18, 20, 22
3630 };
32874aea 3631 real_palette_set(first[n], r, g, b);
374330e2 3632 if (first[n] >= 18)
32874aea 3633 real_palette_set(first[n] + 1, r, g, b);
374330e2 3634 if (pal) {
3635 HDC hdc = get_ctx();
32874aea 3636 UnrealizeObject(pal);
3637 RealizePalette(hdc);
3638 free_ctx(hdc);
374330e2 3639 }
3640}
3641
32874aea 3642void palette_reset(void)
3643{
374330e2 3644 int i;
3645
3646 for (i = 0; i < NCOLOURS; i++) {
3647 if (pal) {
3648 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3649 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3650 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3651 logpal->palPalEntry[i].peFlags = 0;
3652 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3653 defpal[i].rgbtGreen,
3654 defpal[i].rgbtBlue);
3655 } else
3656 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3657 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3658 }
3659
3660 if (pal) {
3661 HDC hdc;
32874aea 3662 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3663 hdc = get_ctx();
32874aea 3664 RealizePalette(hdc);
3665 free_ctx(hdc);
374330e2 3666 }
3667}
3668
4eeb7d09 3669void write_aclip(char *data, int len, int must_deselect)
32874aea 3670{
374330e2 3671 HGLOBAL clipdata;
3672 void *lock;
3673
32874aea 3674 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 3675 if (!clipdata)
3676 return;
32874aea 3677 lock = GlobalLock(clipdata);
374330e2 3678 if (!lock)
3679 return;
32874aea 3680 memcpy(lock, data, len);
3681 ((unsigned char *) lock)[len] = 0;
3682 GlobalUnlock(clipdata);
374330e2 3683
f0df44da 3684 if (!must_deselect)
32874aea 3685 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 3686
32874aea 3687 if (OpenClipboard(hwnd)) {
374330e2 3688 EmptyClipboard();
32874aea 3689 SetClipboardData(CF_TEXT, clipdata);
374330e2 3690 CloseClipboard();
3691 } else
32874aea 3692 GlobalFree(clipdata);
f0df44da 3693
3694 if (!must_deselect)
32874aea 3695 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 3696}
3697
4eeb7d09 3698/*
3699 * Note: unlike write_aclip() this will not append a nul.
3700 */
3701void write_clip(wchar_t * data, int len, int must_deselect)
3702{
a7419ea4 3703 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 3704 int len2;
a7419ea4 3705 void *lock, *lock2, *lock3;
4eeb7d09 3706
3707 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3708
3709 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3710 len * sizeof(wchar_t));
3711 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3712
a7419ea4 3713 if (!clipdata || !clipdata2 || !clipdata3) {
4eeb7d09 3714 if (clipdata)
3715 GlobalFree(clipdata);
3716 if (clipdata2)
3717 GlobalFree(clipdata2);
a7419ea4 3718 if (clipdata3)
3719 GlobalFree(clipdata3);
4eeb7d09 3720 return;
3721 }
3722 if (!(lock = GlobalLock(clipdata)))
3723 return;
3724 if (!(lock2 = GlobalLock(clipdata2)))
3725 return;
3726
3727 memcpy(lock, data, len * sizeof(wchar_t));
3728 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3729
a7419ea4 3730 if (cfg.rtf_paste) {
3731 wchar_t unitab[256];
3732 char *rtf = NULL;
3733 unsigned char *tdata = (unsigned char *)lock2;
3734 wchar_t *udata = (wchar_t *)lock;
3735 int rtflen = 0, uindex = 0, tindex = 0;
3736 int rtfsize = 0;
3737 int multilen, blen, alen, totallen, i;
3738 char before[16], after[4];
3739
3740 get_unitab(CP_ACP, unitab, 0);
3741
3742 rtfsize = 100 + strlen(cfg.font);
3743 rtf = smalloc(rtfsize);
3744 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3745 GetACP(), cfg.font);
3746 rtflen = strlen(rtf);
3747
3748 /*
3749 * We want to construct a piece of RTF that specifies the
3750 * same Unicode text. To do this we will read back in
3751 * parallel from the Unicode data in `udata' and the
3752 * non-Unicode data in `tdata'. For each character in
3753 * `tdata' which becomes the right thing in `udata' when
3754 * looked up in `unitab', we just copy straight over from
3755 * tdata. For each one that doesn't, we must WCToMB it
3756 * individually and produce a \u escape sequence.
3757 *
3758 * It would probably be more robust to just bite the bullet
3759 * and WCToMB each individual Unicode character one by one,
3760 * then MBToWC each one back to see if it was an accurate
3761 * translation; but that strikes me as a horrifying number
3762 * of Windows API calls so I want to see if this faster way
3763 * will work. If it screws up badly we can always revert to
3764 * the simple and slow way.
3765 */
3766 while (tindex < len2 && uindex < len &&
3767 tdata[tindex] && udata[uindex]) {
3768 if (tindex + 1 < len2 &&
3769 tdata[tindex] == '\r' &&
3770 tdata[tindex+1] == '\n') {
3771 tindex++;
3772 uindex++;
3773 }
3774 if (unitab[tdata[tindex]] == udata[uindex]) {
3775 multilen = 1;
3776 before[0] = '\0';
3777 after[0] = '\0';
3778 blen = alen = 0;
3779 } else {
3780 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3781 NULL, 0, NULL, NULL);
3782 if (multilen != 1) {
c3b032a6 3783 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3784 udata[uindex]);
a7419ea4 3785 alen = 1; strcpy(after, "}");
3786 } else {
3787 blen = sprintf(before, "\\u%d", udata[uindex]);
3788 alen = 0; after[0] = '\0';
3789 }
3790 }
3791 assert(tindex + multilen <= len2);
3792 totallen = blen + alen;
3793 for (i = 0; i < multilen; i++) {
3794 if (tdata[tindex+i] == '\\' ||
3795 tdata[tindex+i] == '{' ||
3796 tdata[tindex+i] == '}')
3797 totallen += 2;
3798 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3799 totallen += 6; /* \par\r\n */
3800 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3801 totallen += 4;
3802 else
3803 totallen++;
3804 }
3805
3806 if (rtfsize < rtflen + totallen + 3) {
3807 rtfsize = rtflen + totallen + 512;
3808 rtf = srealloc(rtf, rtfsize);
3809 }
3810
3811 strcpy(rtf + rtflen, before); rtflen += blen;
3812 for (i = 0; i < multilen; i++) {
3813 if (tdata[tindex+i] == '\\' ||
3814 tdata[tindex+i] == '{' ||
3815 tdata[tindex+i] == '}') {
3816 rtf[rtflen++] = '\\';
3817 rtf[rtflen++] = tdata[tindex+i];
3818 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3819 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3820 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3821 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3822 } else {
3823 rtf[rtflen++] = tdata[tindex+i];
3824 }
3825 }
3826 strcpy(rtf + rtflen, after); rtflen += alen;
3827
3828 tindex += multilen;
3829 uindex++;
3830 }
3831
3832 strcpy(rtf + rtflen, "}");
3833 rtflen += 2;
3834
3835 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3836 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3837 strcpy(lock3, rtf);
3838 GlobalUnlock(clipdata3);
3839 }
3840 sfree(rtf);
3841 } else
3842 clipdata3 = NULL;
3843
4eeb7d09 3844 GlobalUnlock(clipdata);
3845 GlobalUnlock(clipdata2);
3846
3847 if (!must_deselect)
3848 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3849
3850 if (OpenClipboard(hwnd)) {
3851 EmptyClipboard();
3852 SetClipboardData(CF_UNICODETEXT, clipdata);
3853 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 3854 if (clipdata3)
3855 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 3856 CloseClipboard();
3857 } else {
3858 GlobalFree(clipdata);
3859 GlobalFree(clipdata2);
3860 }
3861
3862 if (!must_deselect)
3863 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3864}
3865
3866void get_clip(wchar_t ** p, int *len)
32874aea 3867{
374330e2 3868 static HGLOBAL clipdata = NULL;
4eeb7d09 3869 static wchar_t *converted = 0;
3870 wchar_t *p2;
374330e2 3871
4eeb7d09 3872 if (converted) {
3873 sfree(converted);
3874 converted = 0;
3875 }
374330e2 3876 if (!p) {
3877 if (clipdata)
32874aea 3878 GlobalUnlock(clipdata);
374330e2 3879 clipdata = NULL;
3880 return;
4eeb7d09 3881 } else if (OpenClipboard(NULL)) {
2d466ffd 3882 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 3883 CloseClipboard();
4eeb7d09 3884 *p = GlobalLock(clipdata);
3885 if (*p) {
3886 for (p2 = *p; *p2; p2++);
3887 *len = p2 - *p;
3888 return;
374330e2 3889 }
2d466ffd 3890 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 3891 char *s;
3892 int i;
3893 CloseClipboard();
3894 s = GlobalLock(clipdata);
3895 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3896 *p = converted = smalloc(i * sizeof(wchar_t));
3897 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3898 *len = i - 1;
3899 return;
3900 } else
3901 CloseClipboard();
374330e2 3902 }
3903
3904 *p = NULL;
3905 *len = 0;
3906}
3907
4eeb7d09 3908#if 0
374330e2 3909/*
3910 * Move `lines' lines from position `from' to position `to' in the
3911 * window.
3912 */
32874aea 3913void optimised_move(int to, int from, int lines)
3914{
374330e2 3915 RECT r;
f67b4e85 3916 int min, max;
374330e2 3917
3918 min = (to < from ? to : from);
3919 max = to + from - min;
374330e2 3920
5a73255e 3921 r.left = offset_width;
3922 r.right = offset_width + cols * font_width;
3923 r.top = offset_height + min * font_height;
3924 r.bottom = offset_height + (max + lines) * font_height;
32874aea 3925 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 3926}
4eeb7d09 3927#endif
374330e2 3928
3929/*
3930 * Print a message box and perform a fatal exit.
3931 */
32874aea 3932void fatalbox(char *fmt, ...)
3933{
374330e2 3934 va_list ap;
3935 char stuff[200];
3936
3937 va_start(ap, fmt);
3938 vsprintf(stuff, fmt, ap);
3939 va_end(ap);
3940 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3941 exit(1);
3942}
3943
3944/*
f8a28d1f 3945 * Manage window caption / taskbar flashing, if enabled.
3946 * 0 = stop, 1 = maintain, 2 = start
3947 */
3948static void flash_window(int mode)
3949{
3950 static long last_flash = 0;
3951 static int flashing = 0;
3952 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3953 /* stop */
3954 if (flashing) {
3955 FlashWindow(hwnd, FALSE);
3956 flashing = 0;
3957 }
3958
3959 } else if (mode == 2) {
3960 /* start */
3961 if (!flashing) {
3962 last_flash = GetTickCount();
3963 flashing = 1;
3964 FlashWindow(hwnd, TRUE);
3965 }
3966
3967 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3968 /* maintain */
3969 if (flashing) {
3970 long now = GetTickCount();
3971 long fdiff = now - last_flash;
3972 if (fdiff < 0 || fdiff > 450) {
3973 last_flash = now;
3974 FlashWindow(hwnd, TRUE); /* toggle */
3975 }
3976 }
3977 }
3978}
3979
3980/*
374330e2 3981 * Beep.
3982 */
32874aea 3983void beep(int mode)
3984{
03169ad0 3985 if (mode == BELL_DEFAULT) {
eb04402e 3986 /*
3987 * For MessageBeep style bells, we want to be careful of
3988 * timing, because they don't have the nice property of
3989 * PlaySound bells that each one cancels the previous
3990 * active one. So we limit the rate to one per 50ms or so.
3991 */
3992 static long lastbeep = 0;
d51cdf1e 3993 long beepdiff;
eb04402e 3994
d51cdf1e 3995 beepdiff = GetTickCount() - lastbeep;
eb04402e 3996 if (beepdiff >= 0 && beepdiff < 50)
3997 return;
156686ef 3998 MessageBeep(MB_OK);
d51cdf1e 3999 /*
4000 * The above MessageBeep call takes time, so we record the
4001 * time _after_ it finishes rather than before it starts.
4002 */
4003 lastbeep = GetTickCount();
03169ad0 4004 } else if (mode == BELL_WAVEFILE) {
4005 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 4006 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 4007 sprintf(buf, "Unable to play sound file\n%s\n"
4008 "Using default sound instead", cfg.bell_wavefile);
32874aea 4009 MessageBox(hwnd, buf, "PuTTY Sound Error",
4010 MB_OK | MB_ICONEXCLAMATION);
03169ad0 4011 cfg.beep = BELL_DEFAULT;
4012 }
4013 }
f8a28d1f 4014 /* Otherwise, either visual bell or disabled; do nothing here */
4015 if (!has_focus) {
4016 flash_window(2); /* start */
4017 }
374330e2 4018}
8f57d753 4019
4020/*
4021 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4022 * implementation.
a401e5f3 4023 * Revised by <wez@thebrainroom.com>
8f57d753 4024 */
4025static void flip_full_screen(void)
4026{
6003660f 4027 WINDOWPLACEMENT wp;
4028 LONG style;
a401e5f3 4029
6003660f 4030 wp.length = sizeof(wp);
4031 GetWindowPlacement(hwnd, &wp);
a401e5f3 4032
6003660f 4033 full_screen = !full_screen;
a401e5f3 4034
4035 if (full_screen) {
6003660f 4036 if (wp.showCmd == SW_SHOWMAXIMIZED) {
4037 /* Ooops it was already 'zoomed' we have to unzoom it before
4038 * everything will work right.
4039 */
4040 wp.showCmd = SW_SHOWNORMAL;
4041 SetWindowPlacement(hwnd, &wp);
4042 }
4043
4044 style = GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION;
4045 style &= ~WS_VSCROLL;
4046 if (cfg.scrollbar_in_fullscreen)
4047 style |= WS_VSCROLL;
4048 SetWindowLong(hwnd, GWL_STYLE, style);
a401e5f3 4049
6003660f 4050 /* This seems to be needed otherwize explorer doesn't notice
4051 * we want to go fullscreen and it's bar is still visible
4052 */
4053 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4054 SWP_NOACTIVATE | SWP_NOCOPYBITS |
4055 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4056 SWP_FRAMECHANGED);
4057
4058 wp.showCmd = SW_SHOWMAXIMIZED;
4059 SetWindowPlacement(hwnd, &wp);
8f57d753 4060 } else {
6003660f 4061 style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4062 style &= ~WS_VSCROLL;
4063 if (cfg.scrollbar)
4064 style |= WS_VSCROLL;
4065 SetWindowLong(hwnd, GWL_STYLE, style);
4066
4067 /* Don't need to do a SetWindowPos as the resize will force a
4068 * full redraw.
4069 */
4070 wp.showCmd = SW_SHOWNORMAL;
4071 SetWindowPlacement(hwnd, &wp);
8f57d753 4072 }
6003660f 4073
a401e5f3 4074 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4075 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
8f57d753 4076}