Now that Buttress can spot duplicate section keywords, it turns out
[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 ||
811f10b3 1656 (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
0ed2f48e 1657 cfg.resize_action == RESIZE_DISABLED)
5a73255e 1658 term_size(cfg.height, cfg.width, cfg.savelines);
1659
32874aea 1660 /* Enable or disable the scroll bar, etc */
1661 {
1662 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1663 LONG nexflag, exflag =
1664 GetWindowLong(hwnd, GWL_EXSTYLE);
1665
1666 nexflag = exflag;
5a73255e 1667 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
32874aea 1668 if (cfg.alwaysontop) {
1669 nexflag |= WS_EX_TOPMOST;
1670 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1671 SWP_NOMOVE | SWP_NOSIZE);
1672 } else {
1673 nexflag &= ~(WS_EX_TOPMOST);
1674 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1675 SWP_NOMOVE | SWP_NOSIZE);
1676 }
1677 }
1678 if (cfg.sunken_edge)
1679 nexflag |= WS_EX_CLIENTEDGE;
1680 else
1681 nexflag &= ~(WS_EX_CLIENTEDGE);
1682
1683 nflg = flag;
1ba99d2c 1684 if (is_full_screen() ?
2cae8529 1685 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
32874aea 1686 nflg |= WS_VSCROLL;
1687 else
1688 nflg &= ~WS_VSCROLL;
811f10b3 1689
1690 if (cfg.resize_action == RESIZE_DISABLED ||
1691 is_full_screen())
1692 nflg &= ~WS_THICKFRAME;
1693 else
1694 nflg |= WS_THICKFRAME;
1695
ad5c93cc 1696 if (cfg.resize_action == RESIZE_DISABLED)
811f10b3 1697 nflg &= ~WS_MAXIMIZEBOX;
32874aea 1698 else
811f10b3 1699 nflg |= WS_MAXIMIZEBOX;
32874aea 1700
1701 if (nflg != flag || nexflag != exflag) {
32874aea 1702 if (nflg != flag)
1703 SetWindowLong(hwnd, GWL_STYLE, nflg);
1704 if (nexflag != exflag)
1705 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1706
32874aea 1707 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1708 SWP_NOACTIVATE | SWP_NOCOPYBITS |
5a73255e 1709 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1710 SWP_FRAMECHANGED);
c9def1b8 1711
5a73255e 1712 init_lvl = 2;
e44d78b6 1713 }
32874aea 1714 }
5a73255e 1715
e44d78b6 1716 /* Oops */
ad5c93cc 1717 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
e44d78b6 1718 force_normal(hwnd);
5a73255e 1719 init_lvl = 2;
1720 }
1721
32874aea 1722 set_title(cfg.wintitle);
1723 if (IsIconic(hwnd)) {
1724 SetWindowText(hwnd,
1725 cfg.win_name_always ? window_name :
1726 icon_name);
3da0b1d2 1727 }
5a73255e 1728
1729 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1730 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1731 cfg.fontisbold != prev_cfg.fontisbold ||
1732 cfg.fontheight != prev_cfg.fontheight ||
1733 cfg.fontcharset != prev_cfg.fontcharset ||
1734 cfg.vtmode != prev_cfg.vtmode ||
1735 cfg.bold_colour != prev_cfg.bold_colour ||
0ed2f48e 1736 cfg.resize_action == RESIZE_DISABLED ||
1737 cfg.resize_action == RESIZE_EITHER ||
1738 (cfg.resize_action != prev_cfg.resize_action))
5a73255e 1739 init_lvl = 2;
1740
1741 InvalidateRect(hwnd, NULL, TRUE);
1742 reset_window(init_lvl);
7732d38a 1743 net_pending_errors();
32874aea 1744 }
1745 break;
bc1235d4 1746 case IDM_COPYALL:
1747 term_copyall();
1748 break;
32874aea 1749 case IDM_CLRSB:
1750 term_clrsb();
1751 break;
1752 case IDM_RESET:
1753 term_pwron();
1754 break;
1755 case IDM_TEL_AYT:
1756 back->special(TS_AYT);
7732d38a 1757 net_pending_errors();
32874aea 1758 break;
1759 case IDM_TEL_BRK:
1760 back->special(TS_BRK);
7732d38a 1761 net_pending_errors();
32874aea 1762 break;
1763 case IDM_TEL_SYNCH:
1764 back->special(TS_SYNCH);
7732d38a 1765 net_pending_errors();
32874aea 1766 break;
1767 case IDM_TEL_EC:
1768 back->special(TS_EC);
7732d38a 1769 net_pending_errors();
32874aea 1770 break;
1771 case IDM_TEL_EL:
1772 back->special(TS_EL);
7732d38a 1773 net_pending_errors();
32874aea 1774 break;
1775 case IDM_TEL_GA:
1776 back->special(TS_GA);
7732d38a 1777 net_pending_errors();
32874aea 1778 break;
1779 case IDM_TEL_NOP:
1780 back->special(TS_NOP);
7732d38a 1781 net_pending_errors();
32874aea 1782 break;
1783 case IDM_TEL_ABORT:
1784 back->special(TS_ABORT);
7732d38a 1785 net_pending_errors();
32874aea 1786 break;
1787 case IDM_TEL_AO:
1788 back->special(TS_AO);
7732d38a 1789 net_pending_errors();
32874aea 1790 break;
1791 case IDM_TEL_IP:
1792 back->special(TS_IP);
7732d38a 1793 net_pending_errors();
32874aea 1794 break;
1795 case IDM_TEL_SUSP:
1796 back->special(TS_SUSP);
7732d38a 1797 net_pending_errors();
32874aea 1798 break;
1799 case IDM_TEL_EOR:
1800 back->special(TS_EOR);
7732d38a 1801 net_pending_errors();
32874aea 1802 break;
1803 case IDM_TEL_EOF:
1804 back->special(TS_EOF);
7732d38a 1805 net_pending_errors();
32874aea 1806 break;
374330e2 1807 case IDM_ABOUT:
32874aea 1808 showabout(hwnd);
374330e2 1809 break;
70133c0e 1810 case IDM_HELP:
1811 WinHelp(hwnd, help_path,
1812 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1813 break;
0d6dcf38 1814 case SC_MOUSEMENU:
1815 /*
1816 * We get this if the System menu has been activated
1817 * using the mouse.
1818 */
1819 show_mouseptr(1);
1820 break;
dfca2656 1821 case SC_KEYMENU:
1822 /*
0d6dcf38 1823 * We get this if the System menu has been activated
1824 * using the keyboard. This might happen from within
1825 * TranslateKey, in which case it really wants to be
1826 * followed by a `space' character to actually _bring
1827 * the menu up_ rather than just sitting there in
1828 * `ready to appear' state.
dfca2656 1829 */
0d6dcf38 1830 show_mouseptr(1); /* make sure pointer is visible */
dfca2656 1831 if( lParam == 0 )
1832 PostMessage(hwnd, WM_CHAR, ' ', 0);
1833 break;
a401e5f3 1834 case IDM_FULLSCREEN:
1ba99d2c 1835 flip_full_screen();
1836 break;
32874aea 1837 default:
1838 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1839 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1840 }
374330e2 1841 }
1842 break;
37508af4 1843
1844#define X_POS(l) ((int)(short)LOWORD(l))
1845#define Y_POS(l) ((int)(short)HIWORD(l))
1846
5a73255e 1847#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1848#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
01c034ad 1849#define WHEEL_DELTA 120
1850 case WM_MOUSEWHEEL:
1851 {
32874aea 1852 wheel_accumulator += (short) HIWORD(wParam);
01c034ad 1853 wParam = LOWORD(wParam);
1854
1855 /* process events when the threshold is reached */
1856 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1857 int b;
fdedf2c8 1858
01c034ad 1859 /* reduce amount for next time */
1860 if (wheel_accumulator > 0) {
1861 b = MBT_WHEEL_UP;
1862 wheel_accumulator -= WHEEL_DELTA;
32874aea 1863 } else if (wheel_accumulator < 0) {
01c034ad 1864 b = MBT_WHEEL_DOWN;
1865 wheel_accumulator += WHEEL_DELTA;
32874aea 1866 } else
01c034ad 1867 break;
1868
1869 if (send_raw_mouse) {
1870 /* send a mouse-down followed by a mouse up */
6908fed7 1871
01c034ad 1872 term_mouse(b,
1873 MA_CLICK,
32874aea 1874 TO_CHR_X(X_POS(lParam)),
1875 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1876 wParam & MK_CONTROL, is_alt_pressed());
32874aea 1877 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1878 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1879 wParam & MK_CONTROL, is_alt_pressed());
01c034ad 1880 } else {
1881 /* trigger a scroll */
32874aea 1882 term_scroll(0,
1883 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
01c034ad 1884 }
1885 }
1886 return 0;
1887 }
374330e2 1888 case WM_LBUTTONDOWN:
374330e2 1889 case WM_MBUTTONDOWN:
374330e2 1890 case WM_RBUTTONDOWN:
01c034ad 1891 case WM_LBUTTONUP:
1892 case WM_MBUTTONUP:
374330e2 1893 case WM_RBUTTONUP:
01c034ad 1894 {
1895 int button, press;
6908fed7 1896
01c034ad 1897 switch (message) {
32874aea 1898 case WM_LBUTTONDOWN:
1899 button = MBT_LEFT;
1900 press = 1;
1901 break;
1902 case WM_MBUTTONDOWN:
1903 button = MBT_MIDDLE;
1904 press = 1;
1905 break;
1906 case WM_RBUTTONDOWN:
1907 button = MBT_RIGHT;
1908 press = 1;
1909 break;
1910 case WM_LBUTTONUP:
1911 button = MBT_LEFT;
1912 press = 0;
1913 break;
1914 case WM_MBUTTONUP:
1915 button = MBT_MIDDLE;
1916 press = 0;
1917 break;
1918 case WM_RBUTTONUP:
1919 button = MBT_RIGHT;
1920 press = 0;
1921 break;
2d466ffd 1922 default:
1923 button = press = 0; /* shouldn't happen */
01c034ad 1924 }
1925 show_mouseptr(1);
2cae8529 1926 /*
1927 * Special case: in full-screen mode, if the left
1928 * button is clicked in the very top left corner of the
1929 * window, we put up the System menu instead of doing
1930 * selection.
1931 */
1ba99d2c 1932 if (is_full_screen() && press && button == MBT_LEFT &&
2cae8529 1933 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
1934 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
1935 return 0;
1936 }
01c034ad 1937 if (press) {
32874aea 1938 click(button,
1939 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
6908fed7 1940 wParam & MK_SHIFT, wParam & MK_CONTROL,
1941 is_alt_pressed());
01c034ad 1942 SetCapture(hwnd);
1943 } else {
32874aea 1944 term_mouse(button, MA_RELEASE,
1945 TO_CHR_X(X_POS(lParam)),
1946 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1947 wParam & MK_CONTROL, is_alt_pressed());
01c034ad 1948 ReleaseCapture();
1949 }
1950 }
374330e2 1951 return 0;
1952 case WM_MOUSEMOVE:
32874aea 1953 show_mouseptr(1);
374330e2 1954 /*
1955 * Add the mouse position and message time to the random
7d6ee6ff 1956 * number noise.
374330e2 1957 */
32874aea 1958 noise_ultralight(lParam);
374330e2 1959
1960 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1961 Mouse_Button b;
1962 if (wParam & MK_LBUTTON)
6c6e7711 1963 b = MBT_LEFT;
374330e2 1964 else if (wParam & MK_MBUTTON)
6c6e7711 1965 b = MBT_MIDDLE;
374330e2 1966 else
6c6e7711 1967 b = MBT_RIGHT;
32874aea 1968 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1969 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 1970 wParam & MK_CONTROL, is_alt_pressed());
374330e2 1971 }
374330e2 1972 return 0;
d318ef8e 1973 case WM_NCMOUSEMOVE:
1974 show_mouseptr(1);
32874aea 1975 noise_ultralight(lParam);
d318ef8e 1976 return 0;
374330e2 1977 case WM_IGNORE_CLIP:
1978 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1979 break;
1980 case WM_DESTROYCLIPBOARD:
1981 if (!ignore_clip)
1982 term_deselect();
1983 ignore_clip = FALSE;
1984 return 0;
1985 case WM_PAINT:
1986 {
1987 PAINTSTRUCT p;
32874aea 1988 HideCaret(hwnd);
1989 hdc = BeginPaint(hwnd, &p);
374330e2 1990 if (pal) {
32874aea 1991 SelectPalette(hdc, pal, TRUE);
1992 RealizePalette(hdc);
374330e2 1993 }
5a73255e 1994 term_paint(hdc,
1995 (p.rcPaint.left-offset_width)/font_width,
1996 (p.rcPaint.top-offset_height)/font_height,
1997 (p.rcPaint.right-offset_width-1)/font_width,
1998 (p.rcPaint.bottom-offset_height-1)/font_height);
1999
2000 if (p.fErase ||
2001 p.rcPaint.left < offset_width ||
2002 p.rcPaint.top < offset_height ||
2003 p.rcPaint.right >= offset_width + font_width*cols ||
2004 p.rcPaint.bottom>= offset_height + font_height*rows)
2005 {
2006 HBRUSH fillcolour, oldbrush;
2007 HPEN edge, oldpen;
2008 fillcolour = CreateSolidBrush (
2009 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2010 oldbrush = SelectObject(hdc, fillcolour);
2011 edge = CreatePen(PS_SOLID, 0,
2012 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2013 oldpen = SelectObject(hdc, edge);
2014
2015 ExcludeClipRect(hdc,
2016 offset_width, offset_height,
2017 offset_width+font_width*cols,
2018 offset_height+font_height*rows);
2019
2020 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2021 p.rcPaint.right, p.rcPaint.bottom);
2022
2023 // SelectClipRgn(hdc, NULL);
2024
2025 SelectObject(hdc, oldbrush);
2026 DeleteObject(fillcolour);
2027 SelectObject(hdc, oldpen);
2028 DeleteObject(edge);
2029 }
32874aea 2030 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2031 SelectObject(hdc, GetStockObject(WHITE_PEN));
2032 EndPaint(hwnd, &p);
2033 ShowCaret(hwnd);
374330e2 2034 }
2035 return 0;
2036 case WM_NETEVENT:
59ad2c03 2037 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2038 * but the only one that's likely to try to overload us is FD_READ.
2039 * This means buffering just one is fine.
2040 */
2041 if (pending_netevent)
2042 enact_pending_netevent();
2043
2044 pending_netevent = TRUE;
32874aea 2045 pend_netevent_wParam = wParam;
2046 pend_netevent_lParam = lParam;
3ad9d396 2047 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2048 enact_pending_netevent();
2049
ec55b220 2050 time(&last_movement);
374330e2 2051 return 0;
2052 case WM_SETFOCUS:
2053 has_focus = TRUE;
32874aea 2054 CreateCaret(hwnd, caretbm, font_width, font_height);
2055 ShowCaret(hwnd);
f8a28d1f 2056 flash_window(0); /* stop */
32874aea 2057 compose_state = 0;
374330e2 2058 term_out();
2059 term_update();
2060 break;
2061 case WM_KILLFOCUS:
32874aea 2062 show_mouseptr(1);
374330e2 2063 has_focus = FALSE;
32874aea 2064 DestroyCaret();
374330e2 2065 term_out();
2066 term_update();
2067 break;
73251d5d 2068 case WM_ENTERSIZEMOVE:
5a73255e 2069#ifdef RDB_DEBUG_PATCH
2070 debug((27, "WM_ENTERSIZEMOVE"));
2071#endif
32874aea 2072 EnableSizeTip(1);
2073 resizing = TRUE;
3ad8c6db 2074 need_backend_resize = FALSE;
32874aea 2075 break;
73251d5d 2076 case WM_EXITSIZEMOVE:
32874aea 2077 EnableSizeTip(0);
2078 resizing = FALSE;
5a73255e 2079#ifdef RDB_DEBUG_PATCH
2080 debug((27, "WM_EXITSIZEMOVE"));
2081#endif
2082 if (need_backend_resize) {
2083 term_size(cfg.height, cfg.width, cfg.savelines);
2084 InvalidateRect(hwnd, NULL, TRUE);
2085 }
32874aea 2086 break;
374330e2 2087 case WM_SIZING:
5a73255e 2088 /*
2089 * This does two jobs:
2090 * 1) Keep the sizetip uptodate
2091 * 2) Make sure the window size is _stepped_ in units of the font size.
2092 */
0ed2f48e 2093 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
374330e2 2094 int width, height, w, h, ew, eh;
32874aea 2095 LPRECT r = (LPRECT) lParam;
374330e2 2096
0ed2f48e 2097 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
5a73255e 2098 (cfg.height != rows || cfg.width != cols )) {
2099 /*
0ed2f48e 2100 * Great! It seems that both the terminal size and the
2101 * font size have been changed and the user is now dragging.
2102 *
2103 * It will now be difficult to get back to the configured
2104 * font size!
2105 *
2106 * This would be easier but it seems to be too confusing.
2107
5a73255e 2108 term_size(cfg.height, cfg.width, cfg.savelines);
2109 reset_window(2);
0ed2f48e 2110 */
2111 cfg.height=rows; cfg.width=cols;
2112
5a73255e 2113 InvalidateRect(hwnd, NULL, TRUE);
2114 need_backend_resize = TRUE;
2115 }
2116
374330e2 2117 width = r->right - r->left - extra_width;
2118 height = r->bottom - r->top - extra_height;
32874aea 2119 w = (width + font_width / 2) / font_width;
2120 if (w < 1)
2121 w = 1;
2122 h = (height + font_height / 2) / font_height;
2123 if (h < 1)
2124 h = 1;
2125 UpdateSizeTip(hwnd, w, h);
374330e2 2126 ew = width - w * font_width;
2127 eh = height - h * font_height;
2128 if (ew != 0) {
2129 if (wParam == WMSZ_LEFT ||
32874aea 2130 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 2131 r->left += ew;
2132 else
2133 r->right -= ew;
2134 }
2135 if (eh != 0) {
2136 if (wParam == WMSZ_TOP ||
32874aea 2137 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 2138 r->top += eh;
2139 else
2140 r->bottom -= eh;
2141 }
2142 if (ew || eh)
2143 return 1;
2144 else
2145 return 0;
5a73255e 2146 } else {
2147 int width, height, w, h, rv = 0;
2148 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2149 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2150 LPRECT r = (LPRECT) lParam;
2151
2152 width = r->right - r->left - ex_width;
2153 height = r->bottom - r->top - ex_height;
2154
2155 w = (width + cols/2)/cols;
2156 h = (height + rows/2)/rows;
2157 if ( r->right != r->left + w*cols + ex_width)
2158 rv = 1;
2159
2160 if (wParam == WMSZ_LEFT ||
2161 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2162 r->left = r->right - w*cols - ex_width;
2163 else
2164 r->right = r->left + w*cols + ex_width;
2165
2166 if (r->bottom != r->top + h*rows + ex_height)
2167 rv = 1;
2168
2169 if (wParam == WMSZ_TOP ||
2170 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2171 r->top = r->bottom - h*rows - ex_height;
2172 else
2173 r->bottom = r->top + h*rows + ex_height;
2174
2175 return rv;
374330e2 2176 }
32874aea 2177 /* break; (never reached) */
1ba99d2c 2178 case WM_FULLSCR_ON_MAX:
2179 fullscr_on_max = TRUE;
2180 break;
374330e2 2181 case WM_SIZE:
5a73255e 2182#ifdef RDB_DEBUG_PATCH
2183 debug((27, "WM_SIZE %s (%d,%d)",
2184 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2185 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2186 (wParam == SIZE_RESTORED && resizing) ? "to":
2187 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2188 "...",
2189 LOWORD(lParam), HIWORD(lParam)));
2190#endif
0ed2f48e 2191 if (wParam == SIZE_MINIMIZED)
32874aea 2192 SetWindowText(hwnd,
2193 cfg.win_name_always ? window_name : icon_name);
374330e2 2194 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 2195 SetWindowText(hwnd, window_name);
811f10b3 2196 if (wParam == SIZE_RESTORED)
2197 clear_full_screen();
2198 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2199 make_full_screen();
2200 fullscr_on_max = FALSE;
2201 }
5a73255e 2202
ad5c93cc 2203 if (cfg.resize_action == RESIZE_DISABLED) {
5a73255e 2204 /* A resize, well it better be a minimize. */
2205 reset_window(-1);
2206 } else {
2207
1a6f78fe 2208 int width, height, w, h;
374330e2 2209
2210 width = LOWORD(lParam);
2211 height = HIWORD(lParam);
5a73255e 2212
2213 if (!resizing) {
0ed2f48e 2214 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
5a73255e 2215 was_zoomed = 1;
2216 prev_rows = rows;
2217 prev_cols = cols;
0ed2f48e 2218 if (cfg.resize_action == RESIZE_TERM) {
5a73255e 2219 w = width / font_width;
2220 if (w < 1) w = 1;
2221 h = height / font_height;
2222 if (h < 1) h = 1;
2223
2224 term_size(h, w, cfg.savelines);
2225 }
2226 reset_window(0);
2227 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2228 was_zoomed = 0;
0ed2f48e 2229 if (cfg.resize_action == RESIZE_TERM)
5a73255e 2230 term_size(prev_rows, prev_cols, cfg.savelines);
0ed2f48e 2231 if (cfg.resize_action != RESIZE_FONT)
2232 reset_window(2);
2233 else
2234 reset_window(0);
5a73255e 2235 }
2236 /* This is an unexpected resize, these will normally happen
2237 * if the window is too large. Probably either the user
2238 * selected a huge font or the screen size has changed.
2239 *
2240 * This is also called with minimize.
32874aea 2241 */
5a73255e 2242 else reset_window(-1);
2243 }
2244
2245 /*
2246 * Don't call back->size in mid-resize. (To prevent
2247 * massive numbers of resize events getting sent
2248 * down the connection during an NT opaque drag.)
2249 */
2250 if (resizing) {
0ed2f48e 2251 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
3ad8c6db 2252 need_backend_resize = TRUE;
5a73255e 2253 w = (width-cfg.window_border*2) / font_width;
2254 if (w < 1) w = 1;
2255 h = (height-cfg.window_border*2) / font_height;
2256 if (h < 1) h = 1;
2257
e44d78b6 2258 cfg.height = h;
2259 cfg.width = w;
5a73255e 2260 } else
2261 reset_window(0);
374330e2 2262 }
2263 }
374330e2 2264 return 0;
2265 case WM_VSCROLL:
2266 switch (LOWORD(wParam)) {
32874aea 2267 case SB_BOTTOM:
2268 term_scroll(-1, 0);
2269 break;
2270 case SB_TOP:
2271 term_scroll(+1, 0);
2272 break;
2273 case SB_LINEDOWN:
2274 term_scroll(0, +1);
2275 break;
2276 case SB_LINEUP:
2277 term_scroll(0, -1);
2278 break;
2279 case SB_PAGEDOWN:
2280 term_scroll(0, +rows / 2);
2281 break;
2282 case SB_PAGEUP:
2283 term_scroll(0, -rows / 2);
2284 break;
2285 case SB_THUMBPOSITION:
2286 case SB_THUMBTRACK:
2287 term_scroll(1, HIWORD(wParam));
2288 break;
2289 }
2290 break;
2291 case WM_PALETTECHANGED:
374330e2 2292 if ((HWND) wParam != hwnd && 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 }
2299 }
2300 break;
2301 case WM_QUERYNEWPALETTE:
2302 if (pal != NULL) {
2303 HDC hdc = get_ctx();
2304 if (hdc) {
32874aea 2305 if (RealizePalette(hdc) > 0)
2306 UpdateColors(hdc);
2307 free_ctx(hdc);
374330e2 2308 return TRUE;
2309 }
2310 }
2311 return FALSE;
2312 case WM_KEYDOWN:
2313 case WM_SYSKEYDOWN:
c9def1b8 2314 case WM_KEYUP:
2315 case WM_SYSKEYUP:
374330e2 2316 /*
2317 * Add the scan code and keypress timing to the random
7d6ee6ff 2318 * number noise.
374330e2 2319 */
32874aea 2320 noise_ultralight(lParam);
374330e2 2321
2322 /*
2323 * We don't do TranslateMessage since it disassociates the
2324 * resulting CHAR message from the KEYDOWN that sparked it,
2325 * which we occasionally don't want. Instead, we process
2326 * KEYDOWN, and call the Win32 translator functions so that
2327 * we get the translations under _our_ control.
2328 */
2329 {
2330 unsigned char buf[20];
2331 int len;
2332
32874aea 2333 if (wParam == VK_PROCESSKEY) {
3cf144db 2334 MSG m;
32874aea 2335 m.hwnd = hwnd;
2336 m.message = WM_KEYDOWN;
2337 m.wParam = wParam;
2338 m.lParam = lParam & 0xdfff;
2339 TranslateMessage(&m);
2340 } else {
2341 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 2342 if (len == -1)
32874aea 2343 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 2344
b2a1eade 2345 if (len != 0) {
bca9517a 2346 /*
256cb87c 2347 * Interrupt an ongoing paste. I'm not sure
2348 * this is sensible, but for the moment it's
2349 * preferable to having to faff about buffering
2350 * things.
2351 */
2352 term_nopaste();
2353
2354 /*
bca9517a 2355 * We need not bother about stdin backlogs
2356 * here, because in GUI PuTTY we can't do
2357 * anything about it anyway; there's no means
2358 * of asking Windows to hold off on KEYDOWN
2359 * messages. We _have_ to buffer everything
2360 * we're sent.
2361 */
760e88b2 2362 ldisc_send(buf, len, 1);
32874aea 2363 show_mouseptr(0);
bca9517a 2364 }
3cf144db 2365 }
374330e2 2366 }
7732d38a 2367 net_pending_errors();
374330e2 2368 return 0;
4eeb7d09 2369 case WM_INPUTLANGCHANGE:
a5ce2e9f 2370 /* wParam == Font number */
2371 /* lParam == Locale */
2372 set_input_locale((HKL)lParam);
4eeb7d09 2373 break;
71f6951d 2374 case WM_IME_NOTIFY:
2375 if(wParam == IMN_SETOPENSTATUS) {
2376 HIMC hImc = ImmGetContext(hwnd);
2377 ImmSetCompositionFont(hImc, &lfont);
2378 ImmReleaseContext(hwnd, hImc);
2379 return 0;
2380 }
2381 break;
88485e4d 2382 case WM_IME_COMPOSITION:
2383 {
2384 HIMC hIMC;
2385 int n;
2386 char *buff;
2387
2388 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2389 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2390
2391 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2392 break; /* fall back to DefWindowProc */
2393
2394 hIMC = ImmGetContext(hwnd);
2395 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2396
2397 if (n > 0) {
2398 buff = (char*) smalloc(n);
2399 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
760e88b2 2400 luni_send((unsigned short *)buff, n / 2, 1);
88485e4d 2401 free(buff);
2402 }
2403 ImmReleaseContext(hwnd, hIMC);
2404 return 1;
2405 }
2406
4eeb7d09 2407 case WM_IME_CHAR:
2408 if (wParam & 0xFF00) {
3cf144db 2409 unsigned char buf[2];
2410
2411 buf[1] = wParam;
2412 buf[0] = wParam >> 8;
760e88b2 2413 lpage_send(kbd_codepage, buf, 2, 1);
4eeb7d09 2414 } else {
2415 char c = (unsigned char) wParam;
760e88b2 2416 lpage_send(kbd_codepage, &c, 1, 1);
3cf144db 2417 }
4eeb7d09 2418 return (0);
374330e2 2419 case WM_CHAR:
2420 case WM_SYSCHAR:
2421 /*
2422 * Nevertheless, we are prepared to deal with WM_CHAR
2423 * messages, should they crop up. So if someone wants to
2424 * post the things to us as part of a macro manoeuvre,
2425 * we're ready to cope.
2426 */
32874aea 2427 {
4eeb7d09 2428 char c = (unsigned char)wParam;
760e88b2 2429 lpage_send(CP_ACP, &c, 1, 1);
374330e2 2430 }
2431 return 0;
32874aea 2432 case WM_SETCURSOR:
ca3ab3ee 2433 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
32874aea 2434 SetCursor(LoadCursor(NULL, IDC_ARROW));
2435 return TRUE;
2436 }
374330e2 2437 }
2438
32874aea 2439 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2440}
2441
2442/*
ec8679e9 2443 * Move the system caret. (We maintain one, even though it's
2444 * invisible, for the benefit of blind people: apparently some
2445 * helper software tracks the system caret, so we should arrange to
2446 * have one.)
2447 */
32874aea 2448void sys_cursor(int x, int y)
2449{
88485e4d 2450 COMPOSITIONFORM cf;
2451 HIMC hIMC;
2452
2453 if (!has_focus) return;
2454
5a73255e 2455 SetCaretPos(x * font_width + offset_width,
2456 y * font_height + offset_height);
88485e4d 2457
2458 /* IMM calls on Win98 and beyond only */
2459 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2460
2461 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2462 osVersion.dwMinorVersion == 0) return; /* 95 */
2463
2464 /* we should have the IMM functions */
2465 hIMC = ImmGetContext(hwnd);
2466 cf.dwStyle = CFS_POINT;
808d8218 2467 cf.ptCurrentPos.x = x * font_width + offset_width;
2468 cf.ptCurrentPos.y = y * font_height + offset_height;
88485e4d 2469 ImmSetCompositionWindow(hIMC, &cf);
2470
2471 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2472}
2473
2474/*
374330e2 2475 * Draw a line of text in the window, at given character
2476 * coordinates, in given attributes.
2477 *
2478 * We are allowed to fiddle with the contents of `text'.
2479 */
32874aea 2480void do_text(Context ctx, int x, int y, char *text, int len,
2481 unsigned long attr, int lattr)
2482{
374330e2 2483 COLORREF fg, bg, t;
2484 int nfg, nbg, nfont;
2485 HDC hdc = ctx;
59ad2c03 2486 RECT line_box;
2487 int force_manual_underline = 0;
32874aea 2488 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2489 int char_width = fnt_width;
2490 int text_adjust = 0;
2491 static int *IpDx = 0, IpDxLEN = 0;
2492
2493 if (attr & ATTR_WIDE)
2494 char_width *= 2;
59ad2c03 2495
4eeb7d09 2496 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2497 int i;
32874aea 2498 if (len > IpDxLEN) {
59ad2c03 2499 sfree(IpDx);
32874aea 2500 IpDx = smalloc((len + 16) * sizeof(int));
2501 IpDxLEN = (len + 16);
59ad2c03 2502 }
32874aea 2503 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2504 IpDx[i] = char_width;
59ad2c03 2505 }
374330e2 2506
5a73255e 2507 /* Only want the left half of double width lines */
2508 if (lattr != LATTR_NORM && x*2 >= cols)
2509 return;
2510
c9def1b8 2511 x *= fnt_width;
374330e2 2512 y *= font_height;
5a73255e 2513 x += offset_width;
2514 y += offset_height;
374330e2 2515
4eeb7d09 2516 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2517 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2518 attr ^= ATTR_CUR_XOR;
2519 }
2520
2521 nfont = 0;
4eeb7d09 2522 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2523 /* Assume a poorman font is borken in other ways too. */
2524 lattr = LATTR_WIDE;
2525 } else
2526 switch (lattr) {
2527 case LATTR_NORM:
2528 break;
2529 case LATTR_WIDE:
2530 nfont |= FONT_WIDE;
374330e2 2531 break;
4eeb7d09 2532 default:
2533 nfont |= FONT_WIDE + FONT_HIGH;
2534 break;
2535 }
5a73255e 2536 if (attr & ATTR_NARROW)
2537 nfont |= FONT_NARROW;
4eeb7d09 2538
2539 /* Special hack for the VT100 linedraw glyphs. */
2540 if ((attr & CSET_MASK) == 0x2300) {
5a73255e 2541 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
4eeb7d09 2542 switch ((unsigned char) (text[0])) {
2543 case 0xBA:
2544 text_adjust = -2 * font_height / 5;
2545 break;
2546 case 0xBB:
2547 text_adjust = -1 * font_height / 5;
2548 break;
2549 case 0xBC:
2550 text_adjust = font_height / 5;
2551 break;
2552 case 0xBD:
2553 text_adjust = 2 * font_height / 5;
32874aea 2554 break;
59ad2c03 2555 }
4eeb7d09 2556 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2557 text_adjust *= 2;
2558 attr &= ~CSET_MASK;
2559 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2560 attr |= (unitab_xterm['q'] & CSET_MASK);
2561 if (attr & ATTR_UNDER) {
2562 attr &= ~ATTR_UNDER;
2563 force_manual_underline = 1;
2564 }
374330e2 2565 }
2566 }
2567
4eeb7d09 2568 /* Anything left as an original character set is unprintable. */
2569 if (DIRECT_CHAR(attr)) {
2570 attr &= ~CSET_MASK;
2571 attr |= 0xFF00;
5a73255e 2572 memset(text, 0xFD, len);
4eeb7d09 2573 }
2574
2575 /* OEM CP */
2576 if ((attr & CSET_MASK) == ATTR_OEMCP)
2577 nfont |= FONT_OEM;
2578
374330e2 2579 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2580 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2581 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2582 nfont |= FONT_BOLD;
2583 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2584 nfont |= FONT_UNDERLINE;
4eeb7d09 2585 another_font(nfont);
32874aea 2586 if (!fonts[nfont]) {
2587 if (nfont & FONT_UNDERLINE)
59ad2c03 2588 force_manual_underline = 1;
2589 /* Don't do the same for manual bold, it could be bad news. */
2590
32874aea 2591 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2592 }
4eeb7d09 2593 another_font(nfont);
2594 if (!fonts[nfont])
2595 nfont = FONT_NORMAL;
374330e2 2596 if (attr & ATTR_REVERSE) {
32874aea 2597 t = nfg;
2598 nfg = nbg;
2599 nbg = t;
374330e2 2600 }
2601 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2602 nfg++;
59ad2c03 2603 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2604 nbg++;
374330e2 2605 fg = colours[nfg];
2606 bg = colours[nbg];
32874aea 2607 SelectObject(hdc, fonts[nfont]);
2608 SetTextColor(hdc, fg);
2609 SetBkColor(hdc, bg);
2610 SetBkMode(hdc, OPAQUE);
2611 line_box.left = x;
2612 line_box.top = y;
4eeb7d09 2613 line_box.right = x + char_width * len;
32874aea 2614 line_box.bottom = y + font_height;
4eeb7d09 2615
5a73255e 2616 /* Only want the left half of double width lines */
2617 if (line_box.right > font_width*cols+offset_width)
2618 line_box.right = font_width*cols+offset_width;
2619
4eeb7d09 2620 /* We're using a private area for direct to font. (512 chars.) */
2621 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2622 /* Ho Hum, dbcs fonts are a PITA! */
2623 /* To display on W9x I have to convert to UCS */
2624 static wchar_t *uni_buf = 0;
2625 static int uni_len = 0;
5a73255e 2626 int nlen, mptr;
4eeb7d09 2627 if (len > uni_len) {
2628 sfree(uni_buf);
2629 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2630 }
4eeb7d09 2631
5a73255e 2632 for(nlen = mptr = 0; mptr<len; mptr++) {
2633 uni_buf[nlen] = 0xFFFD;
2634 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2635 IpDx[nlen] += char_width;
2636 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2637 text+mptr, 2, uni_buf+nlen, 1);
2638 mptr++;
2639 }
2640 else
2641 {
2642 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2643 text+mptr, 1, uni_buf+nlen, 1);
2644 }
2645 nlen++;
2646 }
4eeb7d09 2647 if (nlen <= 0)
2648 return; /* Eeek! */
2649
2650 ExtTextOutW(hdc, x,
2651 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 2652 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2653 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2654 SetBkMode(hdc, TRANSPARENT);
2655 ExtTextOutW(hdc, x - 1,
2656 y - font_height * (lattr ==
2657 LATTR_BOT) + text_adjust,
5a73255e 2658 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2659 }
5a73255e 2660
2661 IpDx[0] = -1;
4eeb7d09 2662 } else if (DIRECT_FONT(attr)) {
2663 ExtTextOut(hdc, x,
2664 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2665 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2666 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2667 SetBkMode(hdc, TRANSPARENT);
2668
2669 /* GRR: This draws the character outside it's box and can leave
2670 * 'droppings' even with the clip box! I suppose I could loop it
2671 * one character at a time ... yuk.
2672 *
2673 * Or ... I could do a test print with "W", and use +1 or -1 for this
2674 * shift depending on if the leftmost column is blank...
2675 */
2676 ExtTextOut(hdc, x - 1,
2677 y - font_height * (lattr ==
2678 LATTR_BOT) + text_adjust,
2679 ETO_CLIPPED, &line_box, text, len, IpDx);
2680 }
2681 } else {
2682 /* And 'normal' unicode characters */
2683 static WCHAR *wbuf = NULL;
2684 static int wlen = 0;
2685 int i;
2686 if (wlen < len) {
2687 sfree(wbuf);
2688 wlen = len;
2689 wbuf = smalloc(wlen * sizeof(WCHAR));
2690 }
2691 for (i = 0; i < len; i++)
2692 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2693
2694 ExtTextOutW(hdc, x,
2695 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2696 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2697
2698 /* And the shadow bold hack. */
5a73255e 2699 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 2700 SetBkMode(hdc, TRANSPARENT);
2701 ExtTextOutW(hdc, x - 1,
2702 y - font_height * (lattr ==
2703 LATTR_BOT) + text_adjust,
2704 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2705 }
374330e2 2706 }
4eeb7d09 2707 if (lattr != LATTR_TOP && (force_manual_underline ||
2708 (und_mode == UND_LINE
2709 && (attr & ATTR_UNDER)))) {
32874aea 2710 HPEN oldpen;
4eeb7d09 2711 int dec = descent;
2712 if (lattr == LATTR_BOT)
2713 dec = dec * 2 - font_height;
2714
32874aea 2715 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2716 MoveToEx(hdc, x, y + dec, NULL);
2717 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2718 oldpen = SelectObject(hdc, oldpen);
2719 DeleteObject(oldpen);
374330e2 2720 }
4eeb7d09 2721}
2722
2723void do_cursor(Context ctx, int x, int y, char *text, int len,
2724 unsigned long attr, int lattr)
2725{
2726
2727 int fnt_width;
2728 int char_width;
2729 HDC hdc = ctx;
2730 int ctype = cfg.cursor_type;
2731
2732 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2733 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2734 do_text(ctx, x, y, text, len, attr, lattr);
2735 return;
2736 }
2737 ctype = 2;
2738 attr |= TATTR_RIGHTCURS;
2739 }
2740
2741 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2742 if (attr & ATTR_WIDE)
2743 char_width *= 2;
2744 x *= fnt_width;
2745 y *= font_height;
5a73255e 2746 x += offset_width;
2747 y += offset_height;
4eeb7d09 2748
2749 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
374330e2 2750 POINT pts[5];
32874aea 2751 HPEN oldpen;
374330e2 2752 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2753 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2754 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2755 pts[1].y = pts[2].y = y + font_height - 1;
2756 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2757 Polyline(hdc, pts, 5);
2758 oldpen = SelectObject(hdc, oldpen);
2759 DeleteObject(oldpen);
4eeb7d09 2760 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2761 int startx, starty, dx, dy, length, i;
4eeb7d09 2762 if (ctype == 1) {
32874aea 2763 startx = x;
2764 starty = y + descent;
2765 dx = 1;
2766 dy = 0;
4eeb7d09 2767 length = char_width;
32874aea 2768 } else {
4e30ff69 2769 int xadjust = 0;
4eeb7d09 2770 if (attr & TATTR_RIGHTCURS)
2771 xadjust = char_width - 1;
32874aea 2772 startx = x + xadjust;
2773 starty = y;
2774 dx = 0;
2775 dy = 1;
2776 length = font_height;
2777 }
4eeb7d09 2778 if (attr & TATTR_ACTCURS) {
32874aea 2779 HPEN oldpen;
2780 oldpen =
2781 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2782 MoveToEx(hdc, startx, starty, NULL);
2783 LineTo(hdc, startx + dx * length, starty + dy * length);
2784 oldpen = SelectObject(hdc, oldpen);
2785 DeleteObject(oldpen);
2786 } else {
2787 for (i = 0; i < length; i++) {
2788 if (i % 2 == 0) {
2789 SetPixel(hdc, startx, starty, colours[23]);
2790 }
2791 startx += dx;
2792 starty += dy;
2793 }
2794 }
4e30ff69 2795 }
374330e2 2796}
2797
5a73255e 2798/* This function gets the actual width of a character in the normal font.
2799 */
2800int CharWidth(Context ctx, int uc) {
2801 HDC hdc = ctx;
2802 int ibuf = 0;
2803
2804 /* If the font max is the same as the font ave width then this
2805 * function is a no-op.
2806 */
2807 if (!font_dualwidth) return 1;
2808
2809 switch (uc & CSET_MASK) {
2810 case ATTR_ASCII:
2811 uc = unitab_line[uc & 0xFF];
2812 break;
2813 case ATTR_LINEDRW:
2814 uc = unitab_xterm[uc & 0xFF];
2815 break;
2816 case ATTR_SCOACS:
2817 uc = unitab_scoacs[uc & 0xFF];
2818 break;
2819 }
2820 if (DIRECT_FONT(uc)) {
2821 if (dbcs_screenfont) return 1;
2822
2823 /* Speedup, I know of no font where ascii is the wrong width */
2824 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2825 return 1;
2826
2827 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2828 SelectObject(hdc, fonts[FONT_NORMAL]);
2829 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2830 another_font(FONT_OEM);
2831 if (!fonts[FONT_OEM]) return 0;
2832
2833 SelectObject(hdc, fonts[FONT_OEM]);
2834 } else
2835 return 0;
2836
2837 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2838 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2839 return 0;
2840 } else {
2841 /* Speedup, I know of no font where ascii is the wrong width */
2842 if (uc >= ' ' && uc <= '~') return 1;
2843
2844 SelectObject(hdc, fonts[FONT_NORMAL]);
2845 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2846 /* Okay that one worked */ ;
2847 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2848 /* This should work on 9x too, but it's "less accurate" */ ;
2849 else
2850 return 0;
2851 }
2852
2853 ibuf += font_width / 2 -1;
2854 ibuf /= font_width;
2855
2856 return ibuf;
2857}
2858
374330e2 2859/*
c9def1b8 2860 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2861 * codes. Returns number of bytes used or zero to drop the message
2862 * or -1 to forward the message to windows.
374330e2 2863 */
3cf144db 2864static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 2865 unsigned char *output)
2866{
374330e2 2867 BYTE keystate[256];
32874aea 2868 int scan, left_alt = 0, key_down, shift_state;
2869 int r, i, code;
2870 unsigned char *p = output;
4eeb7d09 2871 static int alt_sum = 0;
374330e2 2872
00e3ba0f 2873 HKL kbd_layout = GetKeyboardLayout(0);
2874
0c50ef57 2875 static WORD keys[3];
0c50ef57 2876 static int compose_char = 0;
2877 static WPARAM compose_key = 0;
32874aea 2878
c9def1b8 2879 r = GetKeyboardState(keystate);
32874aea 2880 if (!r)
2881 memset(keystate, 0, sizeof(keystate));
2882 else {
ec55b220 2883#if 0
4eeb7d09 2884#define SHOW_TOASCII_RESULT
32874aea 2885 { /* Tell us all about key events */
2886 static BYTE oldstate[256];
2887 static int first = 1;
2888 static int scan;
2889 int ch;
2890 if (first)
2891 memcpy(oldstate, keystate, sizeof(oldstate));
2892 first = 0;
2893
2894 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2895 debug(("+"));
2896 } else if ((HIWORD(lParam) & KF_UP)
2897 && scan == (HIWORD(lParam) & 0xFF)) {
2898 debug((". U"));
2899 } else {
2900 debug((".\n"));
2901 if (wParam >= VK_F1 && wParam <= VK_F20)
2902 debug(("K_F%d", wParam + 1 - VK_F1));
2903 else
2904 switch (wParam) {
2905 case VK_SHIFT:
2906 debug(("SHIFT"));
2907 break;
2908 case VK_CONTROL:
2909 debug(("CTRL"));
2910 break;
2911 case VK_MENU:
2912 debug(("ALT"));
2913 break;
2914 default:
2915 debug(("VK_%02x", wParam));
2916 }
2917 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2918 debug(("*"));
2919 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2920
2921 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2922 if (ch >= ' ' && ch <= '~')
2923 debug((", '%c'", ch));
2924 else if (ch)
2925 debug((", $%02x", ch));
2926
2927 if (keys[0])
2928 debug((", KB0=%02x", keys[0]));
2929 if (keys[1])
2930 debug((", KB1=%02x", keys[1]));
2931 if (keys[2])
2932 debug((", KB2=%02x", keys[2]));
2933
2934 if ((keystate[VK_SHIFT] & 0x80) != 0)
2935 debug((", S"));
2936 if ((keystate[VK_CONTROL] & 0x80) != 0)
2937 debug((", C"));
2938 if ((HIWORD(lParam) & KF_EXTENDED))
2939 debug((", E"));
2940 if ((HIWORD(lParam) & KF_UP))
2941 debug((", U"));
2942 }
2943
2944 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2945 else if ((HIWORD(lParam) & KF_UP))
2946 oldstate[wParam & 0xFF] ^= 0x80;
2947 else
2948 oldstate[wParam & 0xFF] ^= 0x81;
2949
2950 for (ch = 0; ch < 256; ch++)
2951 if (oldstate[ch] != keystate[ch])
2952 debug((", M%02x=%02x", ch, keystate[ch]));
2953
2954 memcpy(oldstate, keystate, sizeof(oldstate));
2955 }
ec55b220 2956#endif
2957
32874aea 2958 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 2959 keystate[VK_RMENU] = keystate[VK_MENU];
2960 }
2961
c9def1b8 2962
2963 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 2964 if ((cfg.funky_type == 3 ||
2965 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2966 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 2967
2968 wParam = VK_EXECUTE;
2969
2970 /* UnToggle NUMLock */
32874aea 2971 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2972 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 2973 }
2974
2975 /* And write back the 'adjusted' state */
32874aea 2976 SetKeyboardState(keystate);
c9def1b8 2977 }
2978
2979 /* Disable Auto repeat if required */
32874aea 2980 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2981 return 0;
c9def1b8 2982
32874aea 2983 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 2984 left_alt = 1;
2985
32874aea 2986 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 2987
95bbe1ae 2988 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 2989 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 2990 if (cfg.ctrlaltkeys)
2991 keystate[VK_MENU] = 0;
2992 else {
2993 keystate[VK_RMENU] = 0x80;
2994 left_alt = 0;
2995 }
2996 }
c9def1b8 2997
5a73255e 2998 alt_pressed = (left_alt && key_down);
2999
c9def1b8 3000 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 3001 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3002 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 3003
95bbe1ae 3004 /* Note if AltGr was pressed and if it was used as a compose key */
3005 if (!compose_state) {
159eba53 3006 compose_key = 0x100;
95bbe1ae 3007 if (cfg.compose_key) {
32874aea 3008 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 3009 compose_key = wParam;
3010 }
3011 if (wParam == VK_APPS)
3012 compose_key = wParam;
3013 }
3014
32874aea 3015 if (wParam == compose_key) {
3016 if (compose_state == 0
3017 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3018 1;
3019 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 3020 compose_state = 2;
3021 else
3022 compose_state = 0;
32874aea 3023 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 3024 compose_state = 0;
3025
67c339f7 3026 /*
cabfd08c 3027 * Record that we pressed key so the scroll window can be reset, but
3028 * be careful to avoid Shift-UP/Down
3029 */
a910b704 3030 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3031 wParam != VK_MENU && wParam != VK_CONTROL) {
32874aea 3032 seen_key_event = 1;
cabfd08c 3033 }
3034
32874aea 3035 if (compose_state > 1 && left_alt)
3036 compose_state = 0;
67c339f7 3037
c9def1b8 3038 /* Sanitize the number pad if not using a PC NumPad */
32874aea 3039 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3040 && cfg.funky_type != 2)
3041 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3042 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 3043 int nParam = 0;
32874aea 3044 switch (wParam) {
3045 case VK_INSERT:
3046 nParam = VK_NUMPAD0;
3047 break;
3048 case VK_END:
3049 nParam = VK_NUMPAD1;
3050 break;
3051 case VK_DOWN:
3052 nParam = VK_NUMPAD2;
3053 break;
3054 case VK_NEXT:
3055 nParam = VK_NUMPAD3;
3056 break;
3057 case VK_LEFT:
3058 nParam = VK_NUMPAD4;
3059 break;
3060 case VK_CLEAR:
3061 nParam = VK_NUMPAD5;
3062 break;
3063 case VK_RIGHT:
3064 nParam = VK_NUMPAD6;
3065 break;
3066 case VK_HOME:
3067 nParam = VK_NUMPAD7;
3068 break;
3069 case VK_UP:
3070 nParam = VK_NUMPAD8;
3071 break;
3072 case VK_PRIOR:
3073 nParam = VK_NUMPAD9;
3074 break;
3075 case VK_DELETE:
3076 nParam = VK_DECIMAL;
3077 break;
c9def1b8 3078 }
32874aea 3079 if (nParam) {
3080 if (keystate[VK_NUMLOCK] & 1)
3081 shift_state |= 1;
c9def1b8 3082 wParam = nParam;
3083 }
25d39ef6 3084 }
3085 }
3086
c9def1b8 3087 /* If a key is pressed and AltGr is not active */
32874aea 3088 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3089 /* Okay, prepare for most alts then ... */
3090 if (left_alt)
3091 *p++ = '\033';
374330e2 3092
c9def1b8 3093 /* Lets see if it's a pattern we know all about ... */
3094 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 3095 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3096 return 0;
c9def1b8 3097 }
3098 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 3099 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3100 return 0;
3101 }
3102 if (wParam == VK_INSERT && shift_state == 1) {
568dd02f 3103 term_do_paste();
32874aea 3104 return 0;
3105 }
c9def1b8 3106 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 3107 return -1;
c9def1b8 3108 }
3109 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 3110 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3111 return -1;
c9def1b8 3112 }
2cae8529 3113 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3114 (cfg.resize_action != RESIZE_DISABLED)) {
0ed2f48e 3115 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3116 flip_full_screen();
8f57d753 3117 return -1;
3118 }
ec55b220 3119 /* Control-Numlock for app-keypad mode switch */
3120 if (wParam == VK_PAUSE && shift_state == 2) {
3121 app_keypad_keys ^= 1;
3122 return 0;
3123 }
374330e2 3124
c9def1b8 3125 /* Nethack keypad */
3126 if (cfg.nethack_keypad && !left_alt) {
32874aea 3127 switch (wParam) {
3128 case VK_NUMPAD1:
3129 *p++ = shift_state ? 'B' : 'b';
3130 return p - output;
3131 case VK_NUMPAD2:
3132 *p++ = shift_state ? 'J' : 'j';
3133 return p - output;
3134 case VK_NUMPAD3:
3135 *p++ = shift_state ? 'N' : 'n';
3136 return p - output;
3137 case VK_NUMPAD4:
3138 *p++ = shift_state ? 'H' : 'h';
3139 return p - output;
3140 case VK_NUMPAD5:
3141 *p++ = shift_state ? '.' : '.';
3142 return p - output;
3143 case VK_NUMPAD6:
3144 *p++ = shift_state ? 'L' : 'l';
3145 return p - output;
3146 case VK_NUMPAD7:
3147 *p++ = shift_state ? 'Y' : 'y';
3148 return p - output;
3149 case VK_NUMPAD8:
3150 *p++ = shift_state ? 'K' : 'k';
3151 return p - output;
3152 case VK_NUMPAD9:
3153 *p++ = shift_state ? 'U' : 'u';
3154 return p - output;
3155 }
c9def1b8 3156 }
3157
3158 /* Application Keypad */
3159 if (!left_alt) {
32874aea 3160 int xkey = 0;
3161
3162 if (cfg.funky_type == 3 ||
3163 (cfg.funky_type <= 1 &&
3164 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3165 case VK_EXECUTE:
3166 xkey = 'P';
3167 break;
3168 case VK_DIVIDE:
3169 xkey = 'Q';
3170 break;
3171 case VK_MULTIPLY:
3172 xkey = 'R';
3173 break;
3174 case VK_SUBTRACT:
3175 xkey = 'S';
3176 break;
3177 }
3178 if (app_keypad_keys && !cfg.no_applic_k)
3179 switch (wParam) {
3180 case VK_NUMPAD0:
3181 xkey = 'p';
3182 break;
3183 case VK_NUMPAD1:
3184 xkey = 'q';
3185 break;
3186 case VK_NUMPAD2:
3187 xkey = 'r';
3188 break;
3189 case VK_NUMPAD3:
3190 xkey = 's';
3191 break;
3192 case VK_NUMPAD4:
3193 xkey = 't';
3194 break;
3195 case VK_NUMPAD5:
3196 xkey = 'u';
3197 break;
3198 case VK_NUMPAD6:
3199 xkey = 'v';
3200 break;
3201 case VK_NUMPAD7:
3202 xkey = 'w';
3203 break;
3204 case VK_NUMPAD8:
3205 xkey = 'x';
3206 break;
3207 case VK_NUMPAD9:
3208 xkey = 'y';
3209 break;
3210
3211 case VK_DECIMAL:
3212 xkey = 'n';
3213 break;
3214 case VK_ADD:
3215 if (cfg.funky_type == 2) {
3216 if (shift_state)
3217 xkey = 'l';
3218 else
3219 xkey = 'k';
3220 } else if (shift_state)
3221 xkey = 'm';
c9def1b8 3222 else
32874aea 3223 xkey = 'l';
3224 break;
3225
3226 case VK_DIVIDE:
3227 if (cfg.funky_type == 2)
3228 xkey = 'o';
3229 break;
3230 case VK_MULTIPLY:
3231 if (cfg.funky_type == 2)
3232 xkey = 'j';
3233 break;
3234 case VK_SUBTRACT:
3235 if (cfg.funky_type == 2)
3236 xkey = 'm';
3237 break;
3238
3239 case VK_RETURN:
3240 if (HIWORD(lParam) & KF_EXTENDED)
3241 xkey = 'M';
3242 break;
c9def1b8 3243 }
32874aea 3244 if (xkey) {
3245 if (vt52_mode) {
3246 if (xkey >= 'P' && xkey <= 'S')
3247 p += sprintf((char *) p, "\x1B%c", xkey);
3248 else
3249 p += sprintf((char *) p, "\x1B?%c", xkey);
3250 } else
3251 p += sprintf((char *) p, "\x1BO%c", xkey);
3252 return p - output;
c9def1b8 3253 }
3254 }
3255
32874aea 3256 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3257 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3258 *p++ = 0;
3259 return -2;
c9def1b8 3260 }
32874aea 3261 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3262 *p++ = 0x1B;
3263 *p++ = '[';
3264 *p++ = 'Z';
3265 return p - output;
c9def1b8 3266 }
32874aea 3267 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3268 *p++ = 0;
3269 return p - output;
c9def1b8 3270 }
32874aea 3271 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3272 *p++ = 160;
3273 return p - output;
c9def1b8 3274 }
32874aea 3275 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3276 *p++ = 3;
a5f3e637 3277 *p++ = 0;
3278 return -2;
c9def1b8 3279 }
32874aea 3280 if (wParam == VK_PAUSE) { /* Break/Pause */
3281 *p++ = 26;
3282 *p++ = 0;
3283 return -2;
95bbe1ae 3284 }
c9def1b8 3285 /* Control-2 to Control-8 are special */
32874aea 3286 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3287 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3288 return p - output;
3289 }
3290 if (shift_state == 2 && wParam == 0xBD) {
3291 *p++ = 0x1F;
3292 return p - output;
3293 }
3294 if (shift_state == 2 && wParam == 0xDF) {
3295 *p++ = 0x1C;
3296 return p - output;
3297 }
3298 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
32874aea 3299 *p++ = '\r';
3300 *p++ = '\n';
c9def1b8 3301 return p - output;
3302 }
374330e2 3303
c5e9c988 3304 /*
c9def1b8 3305 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3306 * for integer decimal nn.)
3307 *
3308 * We also deal with the weird ones here. Linux VCs replace F1
3309 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3310 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3311 * respectively.
c5e9c988 3312 */
c9def1b8 3313 code = 0;
3314 switch (wParam) {
32874aea 3315 case VK_F1:
3316 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3317 break;
3318 case VK_F2:
3319 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3320 break;
3321 case VK_F3:
3322 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3323 break;
3324 case VK_F4:
3325 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3326 break;
3327 case VK_F5:
3328 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3329 break;
3330 case VK_F6:
3331 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3332 break;
3333 case VK_F7:
3334 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3335 break;
3336 case VK_F8:
3337 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3338 break;
3339 case VK_F9:
3340 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3341 break;
3342 case VK_F10:
3343 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3344 break;
3345 case VK_F11:
3346 code = 23;
3347 break;
3348 case VK_F12:
3349 code = 24;
3350 break;
3351 case VK_F13:
3352 code = 25;
3353 break;
3354 case VK_F14:
3355 code = 26;
3356 break;
3357 case VK_F15:
3358 code = 28;
3359 break;
3360 case VK_F16:
3361 code = 29;
3362 break;
3363 case VK_F17:
3364 code = 31;
3365 break;
3366 case VK_F18:
3367 code = 32;
3368 break;
3369 case VK_F19:
3370 code = 33;
3371 break;
3372 case VK_F20:
3373 code = 34;
3374 break;
dfca2656 3375 }
3376 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3377 case VK_HOME:
3378 code = 1;
3379 break;
3380 case VK_INSERT:
3381 code = 2;
3382 break;
3383 case VK_DELETE:
3384 code = 3;
3385 break;
3386 case VK_END:
3387 code = 4;
3388 break;
3389 case VK_PRIOR:
3390 code = 5;
3391 break;
3392 case VK_NEXT:
3393 code = 6;
3394 break;
374330e2 3395 }
ec55b220 3396 /* Reorder edit keys to physical order */
32874aea 3397 if (cfg.funky_type == 3 && code <= 6)
3398 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3399
f37caa11 3400 if (vt52_mode && code > 0 && code <= 6) {
32874aea 3401 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3402 return p - output;
3403 }
3404
9bc81a2c 3405 if (cfg.funky_type == 5 && /* SCO function keys */
3406 code >= 11 && code <= 34) {
e24b1972 3407 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3408 int index = 0;
3409 switch (wParam) {
3410 case VK_F1: index = 0; break;
3411 case VK_F2: index = 1; break;
3412 case VK_F3: index = 2; break;
3413 case VK_F4: index = 3; break;
3414 case VK_F5: index = 4; break;
3415 case VK_F6: index = 5; break;
3416 case VK_F7: index = 6; break;
3417 case VK_F8: index = 7; break;
3418 case VK_F9: index = 8; break;
3419 case VK_F10: index = 9; break;
3420 case VK_F11: index = 10; break;
3421 case VK_F12: index = 11; break;
3422 }
3423 if (keystate[VK_SHIFT] & 0x80) index += 12;
3424 if (keystate[VK_CONTROL] & 0x80) index += 24;
3425 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3426 return p - output;
3427 }
9bc81a2c 3428 if (cfg.funky_type == 5 && /* SCO small keypad */
3429 code >= 1 && code <= 6) {
3430 char codes[] = "HL.FIG";
3431 if (code == 3) {
3432 *p++ = '\x7F';
3433 } else {
3434 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3435 }
3436 return p - output;
3437 }
f37caa11 3438 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3439 int offt = 0;
32874aea 3440 if (code > 15)
3441 offt++;
3442 if (code > 21)
3443 offt++;
f37caa11 3444 if (vt52_mode)
32874aea 3445 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3446 else
32874aea 3447 p +=
3448 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3449 return p - output;
3450 }
c9def1b8 3451 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 3452 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3453 return p - output;
3454 }
3455 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
ec55b220 3456 if (vt52_mode)
32874aea 3457 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3458 else
32874aea 3459 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3460 return p - output;
3461 }
3462 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3463 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3464 return p - output;
3465 }
3466 if (code) {
32874aea 3467 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3468 return p - output;
374330e2 3469 }
45dabbc5 3470
c9def1b8 3471 /*
3472 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3473 * some reason seems to send VK_CLEAR to Windows...).
3474 */
3475 {
3476 char xkey = 0;
3477 switch (wParam) {
32874aea 3478 case VK_UP:
3479 xkey = 'A';
3480 break;
3481 case VK_DOWN:
3482 xkey = 'B';
3483 break;
3484 case VK_RIGHT:
3485 xkey = 'C';
3486 break;
3487 case VK_LEFT:
3488 xkey = 'D';
3489 break;
3490 case VK_CLEAR:
3491 xkey = 'G';
3492 break;
c9def1b8 3493 }
32874aea 3494 if (xkey) {
c9def1b8 3495 if (vt52_mode)
32874aea 3496 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3497 else {
3498 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3499 /* VT100 & VT102 manuals both state the app cursor keys
3500 * only work if the app keypad is on.
3501 */
3502 if (!app_keypad_keys)
3503 app_flg = 0;
3504 /* Useful mapping of Ctrl-arrows */
3505 if (shift_state == 2)
3506 app_flg = !app_flg;
3507
3508 if (app_flg)
3509 p += sprintf((char *) p, "\x1BO%c", xkey);
3510 else
3511 p += sprintf((char *) p, "\x1B[%c", xkey);
3512 }
c9def1b8 3513 return p - output;
3514 }
3515 }
0c50ef57 3516
3517 /*
3518 * Finally, deal with Return ourselves. (Win95 seems to
3519 * foul it up when Alt is pressed, for some reason.)
3520 */
32874aea 3521 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3522 *p++ = 0x0D;
a5f3e637 3523 *p++ = 0;
3524 return -2;
0c50ef57 3525 }
4eeb7d09 3526
3527 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3528 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3529 else
3530 alt_sum = 0;
67c339f7 3531 }
374330e2 3532
c9def1b8 3533 /* Okay we've done everything interesting; let windows deal with
3534 * the boring stuff */
3535 {
a9c02454 3536 BOOL capsOn=0;
3537
3538 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3539 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3540 capsOn= !left_alt;
3541 keystate[VK_CAPITAL] = 0;
3542 }
3543
00e3ba0f 3544 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 3545#ifdef SHOW_TOASCII_RESULT
3546 if (r == 1 && !key_down) {
3547 if (alt_sum) {
8f22582c 3548 if (in_utf || dbcs_screenfont)
4eeb7d09 3549 debug((", (U+%04x)", alt_sum));
3550 else
3551 debug((", LCH(%d)", alt_sum));
3552 } else {
3553 debug((", ACH(%d)", keys[0]));
3554 }
3555 } else if (r > 0) {
3556 int r1;
3557 debug((", ASC("));
3558 for (r1 = 0; r1 < r; r1++) {
3559 debug(("%s%d", r1 ? "," : "", keys[r1]));
3560 }
3561 debug((")"));
3562 }
3563#endif
32874aea 3564 if (r > 0) {
4eeb7d09 3565 WCHAR keybuf;
256cb87c 3566
3567 /*
3568 * Interrupt an ongoing paste. I'm not sure this is
3569 * sensible, but for the moment it's preferable to
3570 * having to faff about buffering things.
3571 */
3572 term_nopaste();
3573
c9def1b8 3574 p = output;
32874aea 3575 for (i = 0; i < r; i++) {
3576 unsigned char ch = (unsigned char) keys[i];
14963b8f 3577
32874aea 3578 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3579 compose_char = ch;
32874aea 3580 compose_state++;
c9def1b8 3581 continue;
3582 }
32874aea 3583 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3584 int nc;
3585 compose_state = 0;
3586
32874aea 3587 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3588 MessageBeep(MB_ICONHAND);
c9def1b8 3589 return 0;
3590 }
4eeb7d09 3591 keybuf = nc;
760e88b2 3592 luni_send(&keybuf, 1, 1);
4eeb7d09 3593 continue;
c9def1b8 3594 }
374330e2 3595
c9def1b8 3596 compose_state = 0;
374330e2 3597
4eeb7d09 3598 if (!key_down) {
3599 if (alt_sum) {
8f22582c 3600 if (in_utf || dbcs_screenfont) {
4eeb7d09 3601 keybuf = alt_sum;
760e88b2 3602 luni_send(&keybuf, 1, 1);
4eeb7d09 3603 } else {
3604 ch = (char) alt_sum;
5471d09a 3605 /*
3606 * We need not bother about stdin
3607 * backlogs here, because in GUI PuTTY
3608 * we can't do anything about it
3609 * anyway; there's no means of asking
3610 * Windows to hold off on KEYDOWN
3611 * messages. We _have_ to buffer
3612 * everything we're sent.
3613 */
760e88b2 3614 ldisc_send(&ch, 1, 1);
4eeb7d09 3615 }
3616 alt_sum = 0;
3617 } else
760e88b2 3618 lpage_send(kbd_codepage, &ch, 1, 1);
4eeb7d09 3619 } else {
a9c02454 3620 if(capsOn && ch < 0x80) {
3621 WCHAR cbuf[2];
3622 cbuf[0] = 27;
3623 cbuf[1] = xlat_uskbd2cyrllic(ch);
760e88b2 3624 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3625 } else {
3626 char cbuf[2];
3627 cbuf[0] = '\033';
3628 cbuf[1] = ch;
760e88b2 3629 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3630 }
c9def1b8 3631 }
bca9517a 3632 show_mouseptr(0);
c9def1b8 3633 }
374330e2 3634
c9def1b8 3635 /* This is so the ALT-Numpad and dead keys work correctly. */
3636 keys[0] = 0;
3637
32874aea 3638 return p - output;
c9def1b8 3639 }
159eba53 3640 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3641 if (!left_alt)
3642 keys[0] = 0;
4eeb7d09 3643 /* If we will be using alt_sum fix the 256s */
8f22582c 3644 else if (keys[0] && (in_utf || dbcs_screenfont))
4eeb7d09 3645 keys[0] = 10;
374330e2 3646 }
3647
dfca2656 3648 /*
3649 * ALT alone may or may not want to bring up the System menu.
3650 * If it's not meant to, we return 0 on presses or releases of
3651 * ALT, to show that we've swallowed the keystroke. Otherwise
3652 * we return -1, which means Windows will give the keystroke
3653 * its default handling (i.e. bring up the System menu).
3654 */
3655 if (wParam == VK_MENU && !cfg.alt_only)
3656 return 0;
374330e2 3657
c9def1b8 3658 return -1;
374330e2 3659}
3660
32874aea 3661void set_title(char *title)
3662{
3663 sfree(window_name);
3664 window_name = smalloc(1 + strlen(title));
3665 strcpy(window_name, title);
37508af4 3666 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3667 SetWindowText(hwnd, title);
374330e2 3668}
3669
32874aea 3670void set_icon(char *title)
3671{
3672 sfree(icon_name);
3673 icon_name = smalloc(1 + strlen(title));
3674 strcpy(icon_name, title);
37508af4 3675 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3676 SetWindowText(hwnd, title);
374330e2 3677}
3678
32874aea 3679void set_sbar(int total, int start, int page)
3680{
374330e2 3681 SCROLLINFO si;
c9def1b8 3682
1ba99d2c 3683 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
32874aea 3684 return;
c9def1b8 3685
374330e2 3686 si.cbSize = sizeof(si);
c9def1b8 3687 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3688 si.nMin = 0;
3689 si.nMax = total - 1;
3690 si.nPage = page;
3691 si.nPos = start;
c1f5f956 3692 if (hwnd)
32874aea 3693 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3694}
3695
32874aea 3696Context get_ctx(void)
3697{
374330e2 3698 HDC hdc;
3699 if (hwnd) {
32874aea 3700 hdc = GetDC(hwnd);
374330e2 3701 if (hdc && pal)
32874aea 3702 SelectPalette(hdc, pal, FALSE);
374330e2 3703 return hdc;
3704 } else
3705 return NULL;
3706}
3707
32874aea 3708void free_ctx(Context ctx)
3709{
3710 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3711 ReleaseDC(hwnd, ctx);
374330e2 3712}
3713
32874aea 3714static void real_palette_set(int n, int r, int g, int b)
3715{
374330e2 3716 if (pal) {
3717 logpal->palPalEntry[n].peRed = r;
3718 logpal->palPalEntry[n].peGreen = g;
3719 logpal->palPalEntry[n].peBlue = b;
3720 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3721 colours[n] = PALETTERGB(r, g, b);
32874aea 3722 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3723 } else
3724 colours[n] = RGB(r, g, b);
3725}
3726
32874aea 3727void palette_set(int n, int r, int g, int b)
3728{
374330e2 3729 static const int first[21] = {
3730 0, 2, 4, 6, 8, 10, 12, 14,
3731 1, 3, 5, 7, 9, 11, 13, 15,
3732 16, 17, 18, 20, 22
3733 };
32874aea 3734 real_palette_set(first[n], r, g, b);
374330e2 3735 if (first[n] >= 18)
32874aea 3736 real_palette_set(first[n] + 1, r, g, b);
374330e2 3737 if (pal) {
3738 HDC hdc = get_ctx();
32874aea 3739 UnrealizeObject(pal);
3740 RealizePalette(hdc);
3741 free_ctx(hdc);
374330e2 3742 }
3743}
3744
32874aea 3745void palette_reset(void)
3746{
374330e2 3747 int i;
3748
3749 for (i = 0; i < NCOLOURS; i++) {
3750 if (pal) {
3751 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3752 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3753 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3754 logpal->palPalEntry[i].peFlags = 0;
3755 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3756 defpal[i].rgbtGreen,
3757 defpal[i].rgbtBlue);
3758 } else
3759 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3760 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3761 }
3762
3763 if (pal) {
3764 HDC hdc;
32874aea 3765 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3766 hdc = get_ctx();
32874aea 3767 RealizePalette(hdc);
3768 free_ctx(hdc);
374330e2 3769 }
3770}
3771
4eeb7d09 3772void write_aclip(char *data, int len, int must_deselect)
32874aea 3773{
374330e2 3774 HGLOBAL clipdata;
3775 void *lock;
3776
32874aea 3777 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 3778 if (!clipdata)
3779 return;
32874aea 3780 lock = GlobalLock(clipdata);
374330e2 3781 if (!lock)
3782 return;
32874aea 3783 memcpy(lock, data, len);
3784 ((unsigned char *) lock)[len] = 0;
3785 GlobalUnlock(clipdata);
374330e2 3786
f0df44da 3787 if (!must_deselect)
32874aea 3788 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 3789
32874aea 3790 if (OpenClipboard(hwnd)) {
374330e2 3791 EmptyClipboard();
32874aea 3792 SetClipboardData(CF_TEXT, clipdata);
374330e2 3793 CloseClipboard();
3794 } else
32874aea 3795 GlobalFree(clipdata);
f0df44da 3796
3797 if (!must_deselect)
32874aea 3798 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 3799}
3800
4eeb7d09 3801/*
3802 * Note: unlike write_aclip() this will not append a nul.
3803 */
3804void write_clip(wchar_t * data, int len, int must_deselect)
3805{
a7419ea4 3806 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 3807 int len2;
a7419ea4 3808 void *lock, *lock2, *lock3;
4eeb7d09 3809
3810 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3811
3812 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3813 len * sizeof(wchar_t));
3814 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3815
0e5e7f46 3816 if (!clipdata || !clipdata2) {
4eeb7d09 3817 if (clipdata)
3818 GlobalFree(clipdata);
3819 if (clipdata2)
3820 GlobalFree(clipdata2);
3821 return;
3822 }
3823 if (!(lock = GlobalLock(clipdata)))
3824 return;
3825 if (!(lock2 = GlobalLock(clipdata2)))
3826 return;
3827
3828 memcpy(lock, data, len * sizeof(wchar_t));
3829 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3830
a7419ea4 3831 if (cfg.rtf_paste) {
3832 wchar_t unitab[256];
3833 char *rtf = NULL;
3834 unsigned char *tdata = (unsigned char *)lock2;
3835 wchar_t *udata = (wchar_t *)lock;
3836 int rtflen = 0, uindex = 0, tindex = 0;
3837 int rtfsize = 0;
3838 int multilen, blen, alen, totallen, i;
3839 char before[16], after[4];
3840
3841 get_unitab(CP_ACP, unitab, 0);
3842
3843 rtfsize = 100 + strlen(cfg.font);
3844 rtf = smalloc(rtfsize);
3845 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3846 GetACP(), cfg.font);
3847 rtflen = strlen(rtf);
3848
3849 /*
3850 * We want to construct a piece of RTF that specifies the
3851 * same Unicode text. To do this we will read back in
3852 * parallel from the Unicode data in `udata' and the
3853 * non-Unicode data in `tdata'. For each character in
3854 * `tdata' which becomes the right thing in `udata' when
3855 * looked up in `unitab', we just copy straight over from
3856 * tdata. For each one that doesn't, we must WCToMB it
3857 * individually and produce a \u escape sequence.
3858 *
3859 * It would probably be more robust to just bite the bullet
3860 * and WCToMB each individual Unicode character one by one,
3861 * then MBToWC each one back to see if it was an accurate
3862 * translation; but that strikes me as a horrifying number
3863 * of Windows API calls so I want to see if this faster way
3864 * will work. If it screws up badly we can always revert to
3865 * the simple and slow way.
3866 */
3867 while (tindex < len2 && uindex < len &&
3868 tdata[tindex] && udata[uindex]) {
3869 if (tindex + 1 < len2 &&
3870 tdata[tindex] == '\r' &&
3871 tdata[tindex+1] == '\n') {
3872 tindex++;
3873 uindex++;
3874 }
3875 if (unitab[tdata[tindex]] == udata[uindex]) {
3876 multilen = 1;
3877 before[0] = '\0';
3878 after[0] = '\0';
3879 blen = alen = 0;
3880 } else {
3881 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3882 NULL, 0, NULL, NULL);
3883 if (multilen != 1) {
c3b032a6 3884 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3885 udata[uindex]);
a7419ea4 3886 alen = 1; strcpy(after, "}");
3887 } else {
3888 blen = sprintf(before, "\\u%d", udata[uindex]);
3889 alen = 0; after[0] = '\0';
3890 }
3891 }
3892 assert(tindex + multilen <= len2);
3893 totallen = blen + alen;
3894 for (i = 0; i < multilen; i++) {
3895 if (tdata[tindex+i] == '\\' ||
3896 tdata[tindex+i] == '{' ||
3897 tdata[tindex+i] == '}')
3898 totallen += 2;
3899 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3900 totallen += 6; /* \par\r\n */
3901 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3902 totallen += 4;
3903 else
3904 totallen++;
3905 }
3906
3907 if (rtfsize < rtflen + totallen + 3) {
3908 rtfsize = rtflen + totallen + 512;
3909 rtf = srealloc(rtf, rtfsize);
3910 }
3911
3912 strcpy(rtf + rtflen, before); rtflen += blen;
3913 for (i = 0; i < multilen; i++) {
3914 if (tdata[tindex+i] == '\\' ||
3915 tdata[tindex+i] == '{' ||
3916 tdata[tindex+i] == '}') {
3917 rtf[rtflen++] = '\\';
3918 rtf[rtflen++] = tdata[tindex+i];
3919 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3920 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3921 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3922 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3923 } else {
3924 rtf[rtflen++] = tdata[tindex+i];
3925 }
3926 }
3927 strcpy(rtf + rtflen, after); rtflen += alen;
3928
3929 tindex += multilen;
3930 uindex++;
3931 }
3932
3933 strcpy(rtf + rtflen, "}");
3934 rtflen += 2;
3935
3936 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3937 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3938 strcpy(lock3, rtf);
3939 GlobalUnlock(clipdata3);
3940 }
3941 sfree(rtf);
3942 } else
3943 clipdata3 = NULL;
3944
4eeb7d09 3945 GlobalUnlock(clipdata);
3946 GlobalUnlock(clipdata2);
3947
3948 if (!must_deselect)
3949 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3950
3951 if (OpenClipboard(hwnd)) {
3952 EmptyClipboard();
3953 SetClipboardData(CF_UNICODETEXT, clipdata);
3954 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 3955 if (clipdata3)
3956 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 3957 CloseClipboard();
3958 } else {
3959 GlobalFree(clipdata);
3960 GlobalFree(clipdata2);
3961 }
3962
3963 if (!must_deselect)
3964 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3965}
3966
3967void get_clip(wchar_t ** p, int *len)
32874aea 3968{
374330e2 3969 static HGLOBAL clipdata = NULL;
4eeb7d09 3970 static wchar_t *converted = 0;
3971 wchar_t *p2;
374330e2 3972
4eeb7d09 3973 if (converted) {
3974 sfree(converted);
3975 converted = 0;
3976 }
374330e2 3977 if (!p) {
3978 if (clipdata)
32874aea 3979 GlobalUnlock(clipdata);
374330e2 3980 clipdata = NULL;
3981 return;
4eeb7d09 3982 } else if (OpenClipboard(NULL)) {
2d466ffd 3983 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 3984 CloseClipboard();
4eeb7d09 3985 *p = GlobalLock(clipdata);
3986 if (*p) {
3987 for (p2 = *p; *p2; p2++);
3988 *len = p2 - *p;
3989 return;
374330e2 3990 }
2d466ffd 3991 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 3992 char *s;
3993 int i;
3994 CloseClipboard();
3995 s = GlobalLock(clipdata);
3996 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3997 *p = converted = smalloc(i * sizeof(wchar_t));
3998 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3999 *len = i - 1;
4000 return;
4001 } else
4002 CloseClipboard();
374330e2 4003 }
4004
4005 *p = NULL;
4006 *len = 0;
4007}
4008
4eeb7d09 4009#if 0
374330e2 4010/*
4011 * Move `lines' lines from position `from' to position `to' in the
4012 * window.
4013 */
32874aea 4014void optimised_move(int to, int from, int lines)
4015{
374330e2 4016 RECT r;
f67b4e85 4017 int min, max;
374330e2 4018
4019 min = (to < from ? to : from);
4020 max = to + from - min;
374330e2 4021
5a73255e 4022 r.left = offset_width;
4023 r.right = offset_width + cols * font_width;
4024 r.top = offset_height + min * font_height;
4025 r.bottom = offset_height + (max + lines) * font_height;
32874aea 4026 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 4027}
4eeb7d09 4028#endif
374330e2 4029
4030/*
4031 * Print a message box and perform a fatal exit.
4032 */
32874aea 4033void fatalbox(char *fmt, ...)
4034{
374330e2 4035 va_list ap;
4036 char stuff[200];
4037
4038 va_start(ap, fmt);
4039 vsprintf(stuff, fmt, ap);
4040 va_end(ap);
4041 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4042 exit(1);
4043}
4044
4045/*
f8a28d1f 4046 * Manage window caption / taskbar flashing, if enabled.
4047 * 0 = stop, 1 = maintain, 2 = start
4048 */
4049static void flash_window(int mode)
4050{
4051 static long last_flash = 0;
4052 static int flashing = 0;
4053 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4054 /* stop */
4055 if (flashing) {
4056 FlashWindow(hwnd, FALSE);
4057 flashing = 0;
4058 }
4059
4060 } else if (mode == 2) {
4061 /* start */
4062 if (!flashing) {
4063 last_flash = GetTickCount();
4064 flashing = 1;
4065 FlashWindow(hwnd, TRUE);
4066 }
4067
4068 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4069 /* maintain */
4070 if (flashing) {
4071 long now = GetTickCount();
4072 long fdiff = now - last_flash;
4073 if (fdiff < 0 || fdiff > 450) {
4074 last_flash = now;
4075 FlashWindow(hwnd, TRUE); /* toggle */
4076 }
4077 }
4078 }
4079}
4080
4081/*
374330e2 4082 * Beep.
4083 */
32874aea 4084void beep(int mode)
4085{
03169ad0 4086 if (mode == BELL_DEFAULT) {
eb04402e 4087 /*
4088 * For MessageBeep style bells, we want to be careful of
4089 * timing, because they don't have the nice property of
4090 * PlaySound bells that each one cancels the previous
4091 * active one. So we limit the rate to one per 50ms or so.
4092 */
4093 static long lastbeep = 0;
d51cdf1e 4094 long beepdiff;
eb04402e 4095
d51cdf1e 4096 beepdiff = GetTickCount() - lastbeep;
eb04402e 4097 if (beepdiff >= 0 && beepdiff < 50)
4098 return;
156686ef 4099 MessageBeep(MB_OK);
d51cdf1e 4100 /*
4101 * The above MessageBeep call takes time, so we record the
4102 * time _after_ it finishes rather than before it starts.
4103 */
4104 lastbeep = GetTickCount();
03169ad0 4105 } else if (mode == BELL_WAVEFILE) {
4106 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 4107 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 4108 sprintf(buf, "Unable to play sound file\n%s\n"
4109 "Using default sound instead", cfg.bell_wavefile);
32874aea 4110 MessageBox(hwnd, buf, "PuTTY Sound Error",
4111 MB_OK | MB_ICONEXCLAMATION);
03169ad0 4112 cfg.beep = BELL_DEFAULT;
4113 }
4114 }
f8a28d1f 4115 /* Otherwise, either visual bell or disabled; do nothing here */
4116 if (!has_focus) {
4117 flash_window(2); /* start */
4118 }
374330e2 4119}
8f57d753 4120
4121/*
68f9b3d9 4122 * Minimise or restore the window in response to a server-side
4123 * request.
4124 */
4125void set_iconic(int iconic)
4126{
4127 if (IsIconic(hwnd)) {
4128 if (!iconic)
4129 ShowWindow(hwnd, SW_RESTORE);
4130 } else {
4131 if (iconic)
4132 ShowWindow(hwnd, SW_MINIMIZE);
4133 }
4134}
4135
4136/*
4137 * Move the window in response to a server-side request.
4138 */
4139void move_window(int x, int y)
4140{
4141 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4142}
4143
4144/*
4145 * Move the window to the top or bottom of the z-order in response
4146 * to a server-side request.
4147 */
4148void set_zorder(int top)
4149{
1ba99d2c 4150 if (cfg.alwaysontop)
68f9b3d9 4151 return; /* ignore */
4152 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4153 SWP_NOMOVE | SWP_NOSIZE);
4154}
4155
4156/*
4157 * Refresh the window in response to a server-side request.
4158 */
4159void refresh_window(void)
4160{
4161 InvalidateRect(hwnd, NULL, TRUE);
4162}
4163
4164/*
4165 * Maximise or restore the window in response to a server-side
4166 * request.
4167 */
4168void set_zoomed(int zoomed)
4169{
1ba99d2c 4170 if (IsZoomed(hwnd)) {
4171 if (!zoomed)
4172 ShowWindow(hwnd, SW_RESTORE);
68f9b3d9 4173 } else {
4174 if (zoomed)
4175 ShowWindow(hwnd, SW_MAXIMIZE);
4176 }
4177}
4178
4179/*
4180 * Report whether the window is iconic, for terminal reports.
4181 */
4182int is_iconic(void)
4183{
4184 return IsIconic(hwnd);
4185}
4186
4187/*
4188 * Report the window's position, for terminal reports.
4189 */
4190void get_window_pos(int *x, int *y)
4191{
4192 RECT r;
4193 GetWindowRect(hwnd, &r);
4194 *x = r.left;
4195 *y = r.top;
4196}
4197
4198/*
4199 * Report the window's pixel size, for terminal reports.
4200 */
4201void get_window_pixels(int *x, int *y)
4202{
4203 RECT r;
4204 GetWindowRect(hwnd, &r);
4205 *x = r.right - r.left;
4206 *y = r.bottom - r.top;
4207}
4208
4209/*
4210 * Return the window or icon title.
4211 */
4212char *get_window_title(int icon)
4213{
4214 return icon ? icon_name : window_name;
4215}
1ba99d2c 4216
4217/*
4218 * See if we're in full-screen mode.
4219 */
4220int is_full_screen()
4221{
4222 if (!IsZoomed(hwnd))
4223 return FALSE;
4224 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4225 return FALSE;
4226 return TRUE;
4227}
4228
4229/*
4230 * Go full-screen. This should only be called when we are already
4231 * maximised.
4232 */
4233void make_full_screen()
4234{
4235 DWORD style;
4236 int x, y, w, h;
4237
4238 assert(IsZoomed(hwnd));
4239
4240 /* Remove the window furniture. */
4241 style = GetWindowLong(hwnd, GWL_STYLE);
4242 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4243 if (cfg.scrollbar_in_fullscreen)
4244 style |= WS_VSCROLL;
4245 else
4246 style &= ~WS_VSCROLL;
4247 SetWindowLong(hwnd, GWL_STYLE, style);
4248
4249 /* Resize ourselves to exactly cover the nearest monitor. */
4250#ifdef MONITOR_DEFAULTTONEAREST
4251 {
4252 HMONITOR mon;
4253 MONITORINFO mi;
4254 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4255 mi.cbSize = sizeof(mi);
4256 GetMonitorInfo(mon, &mi);
4257 x = mi.rcMonitor.left;
4258 y = mi.rcMonitor.top;
4259 w = mi.rcMonitor.right;
4260 h = mi.rcMonitor.bottom;
4261 }
4262#else
4263 x = y = 0;
4264 w = GetSystemMetrics(SM_CXSCREEN);
4265 h = GetSystemMetrics(SM_CYSCREEN);
4266#endif
4267 SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
4268
4269 /* Tick the menu item in the System menu. */
4270 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4271 MF_CHECKED);
4272}
4273
4274/*
4275 * Clear the full-screen attributes.
4276 */
4277void clear_full_screen()
4278{
4279 DWORD oldstyle, style;
4280
4281 /* Reinstate the window furniture. */
4282 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
811f10b3 4283 style |= WS_CAPTION | WS_BORDER;
4284 if (cfg.resize_action == RESIZE_DISABLED)
4285 style &= ~WS_THICKFRAME;
4286 else
4287 style |= WS_THICKFRAME;
1ba99d2c 4288 if (cfg.scrollbar)
4289 style |= WS_VSCROLL;
4290 else
4291 style &= ~WS_VSCROLL;
4292 if (style != oldstyle) {
4293 SetWindowLong(hwnd, GWL_STYLE, style);
4294 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4295 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4296 SWP_FRAMECHANGED);
4297 }
4298
4299 /* Untick the menu item in the System menu. */
4300 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4301 MF_UNCHECKED);
4302}
4303
4304/*
4305 * Toggle full-screen mode.
4306 */
4307void flip_full_screen()
4308{
4309 if (is_full_screen()) {
4310 ShowWindow(hwnd, SW_RESTORE);
4311 } else if (IsZoomed(hwnd)) {
4312 make_full_screen();
4313 } else {
4314 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4315 ShowWindow(hwnd, SW_MAXIMIZE);
4316 }
4317}