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