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