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