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