Support the SSH-2 mechanism for sending signals to a running session. Neither
[u/mdw/putty] / window.c
index d4ebcf5..9f1dbe3 100644 (file)
--- a/window.c
+++ b/window.c
@@ -111,6 +111,7 @@ static struct unicode_data ucsdata;
 static int session_closed;
 
 static const struct telnet_special *specials;
+static int n_specials;
 
 static struct {
     HMENU menu;
@@ -525,7 +526,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
        /* See if host is of the form user@host */
        if (cfg.host[0] != '\0') {
-           char *atsign = strchr(cfg.host, '@');
+           char *atsign = strrchr(cfg.host, '@');
            /* Make sure we're not overflowing the user field */
            if (atsign) {
                if (atsign - cfg.host < sizeof cfg.username) {
@@ -686,8 +687,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
     }
 
-    start_backend();
-
     /*
      * Prepare the mouse handler.
      */
@@ -742,7 +741,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        }
     }
 
-    update_specials_menu(NULL);
+    start_backend();
 
     /*
      * Set up the initial input locale.
@@ -917,20 +916,48 @@ void update_specials_menu(void *frontend)
        specials = NULL;
 
     if (specials) {
-       p = CreateMenu();
-       for (i = 0; specials[i].name; i++) {
+       /* We can't use Windows to provide a stack for submenus, so
+        * here's a lame "stack" that will do for now. */
+       HMENU saved_menu = NULL;
+       int nesting = 1;
+       p = CreatePopupMenu();
+       for (i = 0; nesting > 0; i++) {
            assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
-           if (*specials[i].name)
+           switch (specials[i].code) {
+             case TS_SEP:
+               AppendMenu(p, MF_SEPARATOR, 0, 0);
+               break;
+             case TS_SUBMENU:
+               assert(nesting < 2);
+               nesting++;
+               saved_menu = p; /* XXX lame stacking */
+               p = CreatePopupMenu();
+               AppendMenu(saved_menu, MF_POPUP | MF_ENABLED,
+                          (UINT) p, specials[i].name);
+               break;
+             case TS_EXITMENU:
+               nesting--;
+               if (nesting) {
+                   p = saved_menu; /* XXX lame stacking */
+                   saved_menu = NULL;
+               }
+               break;
+             default:
                AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
                           specials[i].name);
-           else
-               AppendMenu(p, MF_SEPARATOR, 0, 0);
+               break;
+           }
        }
-    } else
+       /* Squirrel the highest special. */
+       n_specials = i - 1;
+    } else {
        p = NULL;
+       n_specials = 0;
+    }
 
     for (j = 0; j < lenof(popup_menus); j++) {
        if (menu_already_exists) {
+           /* XXX does this free up all submenus? */
            DeleteMenu(popup_menus[j].menu,
                       popup_menus[j].specials_submenu_pos,
                       MF_BYPOSITION);
@@ -945,7 +972,7 @@ void update_specials_menu(void *frontend)
            InsertMenu(popup_menus[j].menu,
                       popup_menus[j].specials_submenu_pos,
                       MF_BYPOSITION | MF_POPUP | MF_ENABLED,
-                      (UINT) p, "Special Command");
+                      (UINT) p, "S&pecial Command");
        }
     }
 }
@@ -1136,7 +1163,7 @@ static void init_palette(void)
  */
 static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc,
                          unsigned short *lpString, UINT cbCount,
-                         CONST INT *lpDx)
+                         CONST INT *lpDx, int opaque)
 {
 
     GCP_RESULTSW gcpr;
@@ -1152,10 +1179,11 @@ static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc,
     gcpr.nGlyphs = cbCount;
 
     GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr,
-                          FLI_MASK | GCP_CLASSIN);
+                          FLI_MASK | GCP_CLASSIN | GCP_DIACRITIC);
 
-    ExtTextOut(hdc, x, y, ETO_GLYPH_INDEX | ETO_CLIPPED | ETO_OPAQUE, lprc,
-              buffer, cbCount, lpDx);
+    ExtTextOut(hdc, x, y,
+              ETO_GLYPH_INDEX | ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
+              lprc, buffer, cbCount, lpDx);
 }
 
 /*
@@ -2089,20 +2117,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            }
            if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
                int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
-               int j;
                /*
                 * Ensure we haven't been sent a bogus SYSCOMMAND
                 * which would cause us to reference invalid memory
                 * and crash. Perhaps I'm just too paranoid here.
                 */
-               for (j = 0; j < i; j++)
-                   if (!specials || !specials[j].name)
-                       break;
-               if (j == i) {
-                   if (back)
-                       back->special(backhandle, specials[i].code);
-                   net_pending_errors();
-               }
+               if (i >= n_specials)
+                   break;
+               if (back)
+                   back->special(backhandle, specials[i].code);
+               net_pending_errors();
            }
        }
        break;
@@ -2188,7 +2212,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                            mi.rcMonitor.top == pt.y) {
                            mouse_on_hotspot = 1;
                        }
-                       CloseHandle(mon);
                    }
                }
 #else
@@ -2767,6 +2790,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            return TRUE;
        }
        break;
+      case WM_SYSCOLORCHANGE:
+       if (cfg.system_colour) {
+           /* Refresh palette from system colours. */
+           /* XXX actually this zaps the entire palette. */
+           systopalette();
+           init_palette();
+           /* Force a repaint of the terminal window. */
+           term_invalidate(term);
+       }
+       break;
       case WM_AGENT_CALLBACK:
        {
            struct agent_callback *c = (struct agent_callback *)lParam;
@@ -2891,19 +2924,22 @@ static void sys_cursor_update(void)
  *
  * We are allowed to fiddle with the contents of `text'.
  */
-void do_text(Context ctx, int x, int y, char *text, int len,
-            unsigned long attr, int lattr)
+void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
+                     unsigned long attr, int lattr)
 {
     COLORREF fg, bg, t;
     int nfg, nbg, nfont;
     HDC hdc = ctx;
     RECT line_box;
     int force_manual_underline = 0;
-    int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
-    int char_width = fnt_width;
+    int fnt_width, char_width;
     int text_adjust = 0;
     static int *IpDx = 0, IpDxLEN = 0;
 
+    lattr &= LATTR_MODE;
+
+    char_width = fnt_width = font_width * (1 + (lattr != LATTR_NORM));
+
     if (attr & ATTR_WIDE)
        char_width *= 2;
 
@@ -2951,43 +2987,39 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        nfont |= FONT_NARROW;
 
     /* Special hack for the VT100 linedraw glyphs. */
-    if ((attr & CSET_MASK) == 0x2300) {
-       if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
-           switch ((unsigned char) (text[0])) {
-             case 0xBA:
-               text_adjust = -2 * font_height / 5;
-               break;
-             case 0xBB:
-               text_adjust = -1 * font_height / 5;
-               break;
-             case 0xBC:
-               text_adjust = font_height / 5;
-               break;
-             case 0xBD:
-               text_adjust = 2 * font_height / 5;
-               break;
-           }
-           if (lattr == LATTR_TOP || lattr == LATTR_BOT)
-               text_adjust *= 2;
-           attr &= ~CSET_MASK;
-           text[0] = (char) (ucsdata.unitab_xterm['q'] & CHAR_MASK);
-           attr |= (ucsdata.unitab_xterm['q'] & CSET_MASK);
-           if (attr & ATTR_UNDER) {
-               attr &= ~ATTR_UNDER;
-               force_manual_underline = 1;
-           }
+    if (text[0] >= 0x23BA && text[0] <= 0x23BD) {
+       switch ((unsigned char) (text[0])) {
+         case 0xBA:
+           text_adjust = -2 * font_height / 5;
+           break;
+         case 0xBB:
+           text_adjust = -1 * font_height / 5;
+           break;
+         case 0xBC:
+           text_adjust = font_height / 5;
+           break;
+         case 0xBD:
+           text_adjust = 2 * font_height / 5;
+           break;
+       }
+       if (lattr == LATTR_TOP || lattr == LATTR_BOT)
+           text_adjust *= 2;
+       text[0] = ucsdata.unitab_xterm['q'];
+       if (attr & ATTR_UNDER) {
+           attr &= ~ATTR_UNDER;
+           force_manual_underline = 1;
        }
     }
 
     /* Anything left as an original character set is unprintable. */
-    if (DIRECT_CHAR(attr)) {
-       attr &= ~CSET_MASK;
-       attr |= 0xFF00;
-       memset(text, 0xFD, len);
+    if (DIRECT_CHAR(text[0])) {
+       int i;
+       for (i = 0; i < len; i++)
+           text[i] = 0xFFFD;
     }
 
     /* OEM CP */
-    if ((attr & CSET_MASK) == ATTR_OEMCP)
+    if ((text[0] & CSET_MASK) == CSET_OEMCP)
        nfont |= FONT_OEM;
 
     nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
@@ -3023,7 +3055,10 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     SelectObject(hdc, fonts[nfont]);
     SetTextColor(hdc, fg);
     SetBkColor(hdc, bg);
-    SetBkMode(hdc, OPAQUE);
+    if (attr & TATTR_COMBINING)
+       SetBkMode(hdc, TRANSPARENT);
+    else
+       SetBkMode(hdc, OPAQUE);
     line_box.left = x;
     line_box.top = y;
     line_box.right = x + char_width * len;
@@ -3034,7 +3069,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        line_box.right = font_width*term->cols+offset_width;
 
     /* We're using a private area for direct to font. (512 chars.) */
-    if (ucsdata.dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
+    if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) {
        /* Ho Hum, dbcs fonts are a PITA! */
        /* To display on W9x I have to convert to UCS */
        static wchar_t *uni_buf = 0;
@@ -3049,15 +3084,20 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        for(nlen = mptr = 0; mptr<len; mptr++) {
            uni_buf[nlen] = 0xFFFD;
            if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
+               char dbcstext[2];
+               dbcstext[0] = text[mptr] & 0xFF;
+               dbcstext[1] = text[mptr+1] & 0xFF;
                IpDx[nlen] += char_width;
                MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
-                                  text+mptr, 2, uni_buf+nlen, 1);
+                                   dbcstext, 2, uni_buf+nlen, 1);
                mptr++;
            }
            else
            {
+               char dbcstext[1];
+               dbcstext[0] = text[mptr] & 0xFF;
                MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
-                                  text+mptr, 1, uni_buf+nlen, 1);
+                                   dbcstext, 1, uni_buf+nlen, 1);
            }
            nlen++;
        }
@@ -3076,10 +3116,21 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        }
 
        IpDx[0] = -1;
-    } else if (DIRECT_FONT(attr)) {
+    } else if (DIRECT_FONT(text[0])) {
+       static char *directbuf = NULL;
+       static int directlen = 0;
+       int i;
+       if (len > directlen) {
+           directlen = len;
+           directbuf = sresize(directbuf, directlen, char);
+       }
+
+       for (i = 0; i < len; i++)
+           directbuf[i] = text[i] & 0xFF;
+
        ExtTextOut(hdc, x,
                   y - font_height * (lattr == LATTR_BOT) + text_adjust,
-                  ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
+                  ETO_CLIPPED | ETO_OPAQUE, &line_box, directbuf, len, IpDx);
        if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
            SetBkMode(hdc, TRANSPARENT);
 
@@ -3093,24 +3144,26 @@ void do_text(Context ctx, int x, int y, char *text, int len,
            ExtTextOut(hdc, x - 1,
                       y - font_height * (lattr ==
                                          LATTR_BOT) + text_adjust,
-                      ETO_CLIPPED, &line_box, text, len, IpDx);
+                      ETO_CLIPPED, &line_box, directbuf, len, IpDx);
        }
     } else {
        /* And 'normal' unicode characters */
        static WCHAR *wbuf = NULL;
        static int wlen = 0;
        int i;
+
        if (wlen < len) {
            sfree(wbuf);
            wlen = len;
            wbuf = snewn(wlen, WCHAR);
        }
+
        for (i = 0; i < len; i++)
-           wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
+           wbuf[i] = text[i];
 
        /* print Glyphs as they are, without Windows' Shaping*/
        exact_textout(hdc, x, y - font_height * (lattr == LATTR_BOT) + text_adjust,
-                     &line_box, wbuf, len, IpDx);
+                     &line_box, wbuf, len, IpDx, !(attr & TATTR_COMBINING));
 /*     ExtTextOutW(hdc, x,
                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
                    ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
@@ -3141,7 +3194,25 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     }
 }
 
-void do_cursor(Context ctx, int x, int y, char *text, int len,
+/*
+ * Wrapper that handles combining characters.
+ */
+void do_text(Context ctx, int x, int y, wchar_t *text, int len,
+            unsigned long attr, int lattr)
+{
+    if (attr & TATTR_COMBINING) {
+       unsigned long a = 0;
+       attr &= ~TATTR_COMBINING;
+       while (len--) {
+           do_text_internal(ctx, x, y, text, 1, attr | a, lattr);
+           text++;
+           a = TATTR_COMBINING;
+       }
+    } else
+       do_text_internal(ctx, x, y, text, len, attr, lattr);
+}
+
+void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
               unsigned long attr, int lattr)
 {
 
@@ -3151,7 +3222,7 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
     int ctype = cfg.cursor_type;
 
     if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
-       if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
+       if (*text != UCSWIDE) {
            do_text(ctx, x, y, text, len, attr, lattr);
            return;
        }
@@ -3228,13 +3299,13 @@ int char_width(Context ctx, int uc) {
     if (!font_dualwidth) return 1;
 
     switch (uc & CSET_MASK) {
-      case ATTR_ASCII:
+      case CSET_ASCII:
        uc = ucsdata.unitab_line[uc & 0xFF];
        break;
-      case ATTR_LINEDRW:
+      case CSET_LINEDRW:
        uc = ucsdata.unitab_xterm[uc & 0xFF];
        break;
-      case ATTR_SCOACS:
+      case CSET_SCOACS:
        uc = ucsdata.unitab_scoacs[uc & 0xFF];
        break;
     }
@@ -3242,12 +3313,12 @@ int char_width(Context ctx, int uc) {
        if (ucsdata.dbcs_screenfont) return 1;
 
        /* Speedup, I know of no font where ascii is the wrong width */
-       if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
+       if ((uc&~CSET_MASK) >= ' ' && (uc&~CSET_MASK)<= '~')
            return 1;
 
-       if ( (uc & CSET_MASK) == ATTR_ACP ) {
+       if ( (uc & CSET_MASK) == CSET_ACP ) {
            SelectObject(hdc, fonts[FONT_NORMAL]);
-       } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
+       } else if ( (uc & CSET_MASK) == CSET_OEMCP ) {
            another_font(FONT_OEM);
            if (!fonts[FONT_OEM]) return 0;
 
@@ -3255,8 +3326,8 @@ int char_width(Context ctx, int uc) {
        } else
            return 0;
 
-       if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
-            GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
+       if ( GetCharWidth32(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1 &&
+            GetCharWidth(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1)
            return 0;
     } else {
        /* Speedup, I know of no font where ascii is the wrong width */