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