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