X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/47d1dab7c8a0d34b5b07c0f7d9ee98edda4b6ec7..7374c7790ee32f36855e4257eb15d2fe43e277ea:/windows/window.c diff --git a/windows/window.c b/windows/window.c index aa805d23..42aa7fb7 100644 --- a/windows/window.c +++ b/windows/window.c @@ -1,7 +1,13 @@ +/* + * window.c - the PuTTY(tel) main program, which runs a PuTTY terminal + * emulator and backend in a window. + */ + #include #include #include #include +#include #include #define PUTTY_DO_GLOBALS /* actually _define_ globals */ @@ -109,7 +115,7 @@ static Backend *back; static void *backhandle; static struct unicode_data ucsdata; -static int session_closed; +static int must_close_session, session_closed; static int reconfiguring = FALSE; static const struct telnet_special *specials = NULL; @@ -229,7 +235,7 @@ static void start_backend(void) if (error) { char *str = dupprintf("%s Error", appname); sprintf(msg, "Unable to open connection to\n" - "%.800s\n" "%s", cfg.host, error); + "%.800s\n" "%s", cfg_dest(&cfg), error); MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); sfree(str); exit(0); @@ -811,8 +817,31 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) term_set_focus(term, GetForegroundWindow() == hwnd); UpdateWindow(hwnd); - if (GetMessage(&msg, NULL, 0, 0) == 1) { - while (msg.message != WM_QUIT) { + while (1) { + HANDLE *handles; + int nhandles, n; + + handles = handle_get_events(&nhandles); + + n = MsgWaitForMultipleObjects(nhandles, handles, FALSE, INFINITE, + QS_ALLINPUT); + + if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { + handle_got_event(handles[n - WAIT_OBJECT_0]); + sfree(handles); + if (must_close_session) + close_session(); + continue; + } + + sfree(handles); + + if (GetMessage(&msg, NULL, 0, 0) != 1) + break; + do { + if (msg.message == WM_QUIT) + goto finished; /* two-level break */ + if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg))) DispatchMessage(&msg); /* Send the paste buffer if there's anything to send */ @@ -821,23 +850,17 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * we've delayed, reading the socket, writing, and repainting * the window. */ - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - continue; - - /* The messages seem unreliable; especially if we're being tricky */ - term_set_focus(term, GetForegroundWindow() == hwnd); + if (must_close_session) + close_session(); + } while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); - net_pending_errors(); + /* The messages seem unreliable; especially if we're being tricky */ + term_set_focus(term, GetForegroundWindow() == hwnd); - /* There's no point rescanning everything in the message queue - * so we do an apparently unnecessary wait here - */ - WaitMessage(); - if (GetMessage(&msg, NULL, 0, 0) != 1) - break; - } + net_pending_errors(); } + finished: cleanup_exit(msg.wParam); /* this doesn't return... */ return msg.wParam; /* ... but optimiser doesn't know */ } @@ -1051,7 +1074,7 @@ void connection_fatal(void *frontend, char *fmt, ...) if (cfg.close_on_exit == FORCE_ON) PostQuitMessage(1); else { - close_session(); + must_close_session = TRUE; } } @@ -1079,27 +1102,13 @@ static void enact_netevent(WPARAM wParam, LPARAM lParam) { static int reentering = 0; extern int select_result(WPARAM, LPARAM); - int ret; if (reentering) return; /* don't unpend the pending */ reentering = 1; - ret = select_result(wParam, lParam); + select_result(wParam, lParam); reentering = 0; - - if (ret == 0 && !session_closed) { - /* Abnormal exits will already have set session_closed and taken - * appropriate action. */ - if (cfg.close_on_exit == FORCE_ON || - cfg.close_on_exit == AUTO) PostQuitMessage(0); - else { - close_session(); - session_closed = TRUE; - MessageBox(hwnd, "Connection closed by remote host", - appname, MB_OK | MB_ICONINFORMATION); - } - } } /* @@ -1621,8 +1630,8 @@ static void reset_window(int reinit) { #endif } } else { - if ( font_width != win_width/term->cols || - font_height != win_height/term->rows) { + if ( font_width * term->cols != win_width || + font_height * term->rows != win_height) { /* Our only choice at this point is to change the * size of the terminal; Oh well. */ @@ -1844,7 +1853,29 @@ static int is_shift_pressed(void) static int resizing; -void notify_remote_exit(void *fe) { /* stub not needed in this frontend */ } +void notify_remote_exit(void *fe) +{ + int exitcode; + + if (!session_closed && + (exitcode = back->exitcode(backhandle)) >= 0) { + /* Abnormal exits will already have set session_closed and taken + * appropriate action. */ + if (cfg.close_on_exit == FORCE_ON || + (cfg.close_on_exit == AUTO && exitcode != INT_MAX)) { + PostQuitMessage(0); + } else { + must_close_session = TRUE; + session_closed = TRUE; + /* exitcode == INT_MAX indicates that the connection was closed + * by a fatal error, so an error box will be coming our way and + * we should not generate this informational one. */ + if (exitcode != INT_MAX) + MessageBox(hwnd, "Connection closed by remote host", + appname, MB_OK | MB_ICONINFORMATION); + } + } +} void timer_change_notify(long next) { @@ -1987,6 +2018,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case IDM_RESTART: if (!back) { logevent(NULL, "----- Session restarted -----"); + term_pwron(term, FALSE); start_backend(); } @@ -2154,7 +2186,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, term_clrsb(term); break; case IDM_RESET: - term_pwron(term); + term_pwron(term, TRUE); if (ldisc) ldisc_send(ldisc, NULL, 0, 0); break; @@ -3722,31 +3754,31 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, if (cfg.nethack_keypad && !left_alt) { switch (wParam) { case VK_NUMPAD1: - *p++ = shift_state ? 'B' : 'b'; + *p++ = "bB\002\002"[shift_state & 3]; return p - output; case VK_NUMPAD2: - *p++ = shift_state ? 'J' : 'j'; + *p++ = "jJ\012\012"[shift_state & 3]; return p - output; case VK_NUMPAD3: - *p++ = shift_state ? 'N' : 'n'; + *p++ = "nN\016\016"[shift_state & 3]; return p - output; case VK_NUMPAD4: - *p++ = shift_state ? 'H' : 'h'; + *p++ = "hH\010\010"[shift_state & 3]; return p - output; case VK_NUMPAD5: *p++ = shift_state ? '.' : '.'; return p - output; case VK_NUMPAD6: - *p++ = shift_state ? 'L' : 'l'; + *p++ = "lL\014\014"[shift_state & 3]; return p - output; case VK_NUMPAD7: - *p++ = shift_state ? 'Y' : 'y'; + *p++ = "yY\031\031"[shift_state & 3]; return p - output; case VK_NUMPAD8: - *p++ = shift_state ? 'K' : 'k'; + *p++ = "kK\013\013"[shift_state & 3]; return p - output; case VK_NUMPAD9: - *p++ = shift_state ? 'U' : 'u'; + *p++ = "uU\025\025"[shift_state & 3]; return p - output; } } @@ -4399,6 +4431,12 @@ void palette_set(void *frontend, int n, int r, int g, int b) UnrealizeObject(pal); RealizePalette(hdc); free_ctx(hdc); + } else { + if (n == (ATTR_DEFBG>>ATTR_BGSHIFT)) + /* If Default Background changes, we need to ensure any + * space between the text area and the window border is + * redrawn. */ + InvalidateRect(hwnd, NULL, TRUE); } } @@ -4427,6 +4465,10 @@ void palette_reset(void *frontend) hdc = get_ctx(frontend); RealizePalette(hdc); free_ctx(hdc); + } else { + /* Default Background may have changed. Ensure any space between + * text area and window border is redrawn. */ + InvalidateRect(hwnd, NULL, TRUE); } } @@ -4462,7 +4504,7 @@ void write_aclip(void *frontend, char *data, int len, int must_deselect) /* * Note: unlike write_aclip() this will not append a nul. */ -void write_clip(void *frontend, wchar_t * data, int len, int must_deselect) +void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect) { HGLOBAL clipdata, clipdata2, clipdata3; int len2; @@ -4498,14 +4540,83 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect) int rtfsize = 0; int multilen, blen, alen, totallen, i; char before[16], after[4]; + int fgcolour, lastfgcolour = 0; + int bgcolour, lastbgcolour = 0; + int attrBold, lastAttrBold = 0; + int attrUnder, lastAttrUnder = 0; + int palette[NALLCOLOURS]; + int numcolours; get_unitab(CP_ACP, unitab, 0); rtfsize = 100 + strlen(cfg.font.name); rtf = snewn(rtfsize, char); - sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0 ", - GetACP(), cfg.font.name); - rtflen = strlen(rtf); + rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d", + cfg.font.name, cfg.font.height*2); + + /* + * Add colour palette + * {\colortbl ;\red255\green0\blue0;\red0\green0\blue128;} + */ + + /* + * First - Determine all colours in use + * o Foregound and background colours share the same palette + */ + if (attr) { + memset(palette, 0, sizeof(palette)); + for (i = 0; i < (len-1); i++) { + fgcolour = ((attr[i] & ATTR_FGMASK) >> ATTR_FGSHIFT); + bgcolour = ((attr[i] & ATTR_BGMASK) >> ATTR_BGSHIFT); + + if (attr[i] & ATTR_REVERSE) { + int tmpcolour = fgcolour; /* Swap foreground and background */ + fgcolour = bgcolour; + bgcolour = tmpcolour; + } + + if (bold_mode == BOLD_COLOURS && (attr[i] & ATTR_BOLD)) { + if (fgcolour < 8) /* ANSI colours */ + fgcolour += 8; + else if (fgcolour >= 256) /* Default colours */ + fgcolour ++; + } + + if (attr[i] & ATTR_BLINK) { + if (bgcolour < 8) /* ANSI colours */ + bgcolour += 8; + else if (bgcolour >= 256) /* Default colours */ + bgcolour ++; + } + + palette[fgcolour]++; + palette[bgcolour]++; + } + + /* + * Next - Create a reduced palette + */ + numcolours = 0; + for (i = 0; i < NALLCOLOURS; i++) { + if (palette[i] != 0) + palette[i] = ++numcolours; + } + + /* + * Finally - Write the colour table + */ + rtf = sresize(rtf, rtfsize + (numcolours * 25), char); + strcat(rtf, "{\\colortbl ;"); + rtflen = strlen(rtf); + + for (i = 0; i < NALLCOLOURS; i++) { + if (palette[i] != 0) { + rtflen += sprintf(&rtf[rtflen], "\\red%d\\green%d\\blue%d;", defpal[i].rgbtRed, defpal[i].rgbtGreen, defpal[i].rgbtBlue); + } + } + strcpy(&rtf[rtflen], "}"); + rtflen ++; + } /* * We want to construct a piece of RTF that specifies the @@ -4532,7 +4643,96 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect) tdata[tindex+1] == '\n') { tindex++; uindex++; + } + + /* + * Set text attributes + */ + if (attr) { + if (rtfsize < rtflen + 64) { + rtfsize = rtflen + 512; + rtf = sresize(rtf, rtfsize, char); + } + + /* + * Determine foreground and background colours + */ + fgcolour = ((attr[tindex] & ATTR_FGMASK) >> ATTR_FGSHIFT); + bgcolour = ((attr[tindex] & ATTR_BGMASK) >> ATTR_BGSHIFT); + + if (attr[tindex] & ATTR_REVERSE) { + int tmpcolour = fgcolour; /* Swap foreground and background */ + fgcolour = bgcolour; + bgcolour = tmpcolour; + } + + if (bold_mode == BOLD_COLOURS && (attr[tindex] & ATTR_BOLD)) { + if (fgcolour < 8) /* ANSI colours */ + fgcolour += 8; + else if (fgcolour >= 256) /* Default colours */ + fgcolour ++; + } + + if (attr[tindex] & ATTR_BLINK) { + if (bgcolour < 8) /* ANSI colours */ + bgcolour += 8; + else if (bgcolour >= 256) /* Default colours */ + bgcolour ++; + } + + /* + * Collect other attributes + */ + if (bold_mode != BOLD_COLOURS) + attrBold = attr[tindex] & ATTR_BOLD; + else + attrBold = 0; + + attrUnder = attr[tindex] & ATTR_UNDER; + + /* + * Reverse video + * o If video isn't reversed, ignore colour attributes for default foregound + * or background. + * o Special case where bolded text is displayed using the default foregound + * and background colours - force to bolded RTF. + */ + if (!(attr[tindex] & ATTR_REVERSE)) { + if (bgcolour >= 256) /* Default color */ + bgcolour = -1; /* No coloring */ + + if (fgcolour >= 256) { /* Default colour */ + if (bold_mode == BOLD_COLOURS && (fgcolour & 1) && bgcolour == -1) + attrBold = ATTR_BOLD; /* Emphasize text with bold attribute */ + + fgcolour = -1; /* No coloring */ + } + } + + /* + * Write RTF text attributes + */ + if (lastfgcolour != fgcolour) { + lastfgcolour = fgcolour; + rtflen += sprintf(&rtf[rtflen], "\\cf%d ", (fgcolour >= 0) ? palette[fgcolour] : 0); + } + + if (lastbgcolour != bgcolour) { + lastbgcolour = bgcolour; + rtflen += sprintf(&rtf[rtflen], "\\highlight%d ", (bgcolour >= 0) ? palette[bgcolour] : 0); + } + + if (lastAttrBold != attrBold) { + lastAttrBold = attrBold; + rtflen += sprintf(&rtf[rtflen], "%s", attrBold ? "\\b " : "\\b0 "); + } + + if (lastAttrUnder != attrUnder) { + lastAttrUnder = attrUnder; + rtflen += sprintf(&rtf[rtflen], "%s", attrUnder ? "\\ul " : "\\ulnone "); + } } + if (unitab[tdata[tindex]] == udata[uindex]) { multilen = 1; before[0] = '\0'; @@ -4591,12 +4791,13 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect) uindex++; } - strcpy(rtf + rtflen, "}"); - rtflen += 2; + rtf[rtflen++] = '}'; /* Terminate RTF stream */ + rtf[rtflen++] = '\0'; + rtf[rtflen++] = '\0'; clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen); if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) { - strcpy(lock3, rtf); + memcpy(lock3, rtf, rtflen); GlobalUnlock(clipdata3); } sfree(rtf); @@ -4993,6 +5194,10 @@ static void make_full_screen() ss.bottom - ss.top, SWP_FRAMECHANGED); + /* We may have changed size as a result */ + + reset_window(0); + /* Tick the menu item in the System menu. */ CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN, MF_CHECKED);