Re-engineering of terminal emulator, phase 1.
[u/mdw/putty] / window.c
index 9c77861..5a36973 100644 (file)
--- a/window.c
+++ b/window.c
 #define IDM_SHOWLOG   0x0010
 #define IDM_NEWSESS   0x0020
 #define IDM_DUPSESS   0x0030
-#define IDM_RECONF    0x0040
-#define IDM_CLRSB     0x0050
-#define IDM_RESET     0x0060
+#define IDM_RESTART   0x0040
+#define IDM_RECONF    0x0050
+#define IDM_CLRSB     0x0060
+#define IDM_RESET     0x0070
 #define IDM_HELP      0x0140
 #define IDM_ABOUT     0x0150
 #define IDM_SAVEDSESS 0x0160
@@ -110,7 +111,6 @@ static struct unicode_data ucsdata;
 static int session_closed;
 
 static const struct telnet_special *specials;
-static int specials_menu_position;
 
 static struct {
     HMENU menu;
@@ -183,6 +183,110 @@ void ldisc_update(void *frontend, int echo, int edit)
 {
 }
 
+static void start_backend(void)
+{
+    const char *error;
+    char msg[1024], *title;
+    char *realhost;
+    int i;
+
+    /*
+     * Select protocol. This is farmed out into a table in a
+     * separate file to enable an ssh-free variant.
+     */
+    back = NULL;
+    for (i = 0; backends[i].backend != NULL; i++)
+       if (backends[i].protocol == cfg.protocol) {
+           back = backends[i].backend;
+           break;
+       }
+    if (back == NULL) {
+       char *str = dupprintf("%s Internal Error", appname);
+       MessageBox(NULL, "Unsupported protocol number found",
+                  str, MB_OK | MB_ICONEXCLAMATION);
+       sfree(str);
+       cleanup_exit(1);
+    }
+
+    error = back->init(NULL, &backhandle, &cfg,
+                      cfg.host, cfg.port, &realhost, cfg.tcp_nodelay,
+                      cfg.tcp_keepalives);
+    back->provide_logctx(backhandle, logctx);
+    if (error) {
+       char *str = dupprintf("%s Error", appname);
+       sprintf(msg, "Unable to open connection to\n"
+               "%.800s\n" "%s", cfg.host, error);
+       MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
+       sfree(str);
+       exit(0);
+    }
+    window_name = icon_name = NULL;
+    if (*cfg.wintitle) {
+       title = cfg.wintitle;
+    } else {
+       sprintf(msg, "%s - %s", realhost, appname);
+       title = msg;
+    }
+    sfree(realhost);
+    set_title(NULL, title);
+    set_icon(NULL, title);
+
+    /*
+     * Connect the terminal to the backend for resize purposes.
+     */
+    term_provide_resize_fn(term, back->size, backhandle);
+
+    /*
+     * Set up a line discipline.
+     */
+    ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
+
+    /*
+     * Destroy the Restart Session menu item. (This will return
+     * failure if it's already absent, as it will be the very first
+     * time we call this function. We ignore that, because as long
+     * as the menu item ends up not being there, we don't care
+     * whether it was us who removed it or not!)
+     */
+    for (i = 0; i < lenof(popup_menus); i++) {
+       DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
+    }
+
+    session_closed = FALSE;
+}
+
+static void close_session(void)
+{
+    char morestuff[100];
+    int i;
+
+    session_closed = TRUE;
+    sprintf(morestuff, "%.70s (inactive)", appname);
+    set_icon(NULL, morestuff);
+    set_title(NULL, morestuff);
+
+    if (ldisc) {
+       ldisc_free(ldisc);
+       ldisc = NULL;
+    }
+    if (back) {
+       back->free(backhandle);
+       backhandle = NULL;
+       back = NULL;
+       update_specials_menu(NULL);
+    }
+
+    /*
+     * Show the Restart Session menu item. Do a precautionary
+     * delete first to ensure we never end up with more than one.
+     */
+    for (i = 0; i < lenof(popup_menus); i++) {
+       DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
+       InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED,
+                  IDM_RESTART, "&Restart Session");
+    }
+}
+
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
     WNDCLASS wndclass;
@@ -421,7 +525,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) {
@@ -453,27 +557,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        }
     }
 
-    /*
-     * Select protocol. This is farmed out into a table in a
-     * separate file to enable an ssh-free variant.
-     */
-    {
-       int i;
-       back = NULL;
-       for (i = 0; backends[i].backend != NULL; i++)
-           if (backends[i].protocol == cfg.protocol) {
-               back = backends[i].backend;
-               break;
-           }
-       if (back == NULL) {
-           char *str = dupprintf("%s Internal Error", appname);
-           MessageBox(NULL, "Unsupported protocol number found",
-                      str, MB_OK | MB_ICONEXCLAMATION);
-           sfree(str);
-           cleanup_exit(1);
-       }
-    }
-
     /* Check for invalid Port number (i.e. zero) */
     if (cfg.port == 0) {
        char *str = dupprintf("%s Internal Error", appname);
@@ -603,48 +686,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
     }
 
-    /*
-     * Start up the telnet connection.
-     */
-    {
-       const char *error;
-       char msg[1024], *title;
-       char *realhost;
-
-       error = back->init(NULL, &backhandle, &cfg,
-                          cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
-       back->provide_logctx(backhandle, logctx);
-       if (error) {
-           char *str = dupprintf("%s Error", appname);
-           sprintf(msg, "Unable to open connection to\n"
-                   "%.800s\n" "%s", cfg.host, error);
-           MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
-           sfree(str);
-           return 0;
-       }
-       window_name = icon_name = NULL;
-       if (*cfg.wintitle) {
-           title = cfg.wintitle;
-       } else {
-           sprintf(msg, "%s - %s", realhost, appname);
-           title = msg;
-       }
-       sfree(realhost);
-       set_title(NULL, title);
-       set_icon(NULL, title);
-    }
-
-    /*
-     * Connect the terminal to the backend for resize purposes.
-     */
-    term_provide_resize_fn(term, back->size, backhandle);
-
-    /*
-     * Set up a line discipline.
-     */
-    ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
-
-    session_closed = FALSE;
+    start_backend();
 
     /*
      * Prepare the mouse handler.
@@ -865,13 +907,17 @@ char *do_select(SOCKET skt, int startup)
  */
 void update_specials_menu(void *frontend)
 {
-    HMENU m = GetSystemMenu(hwnd, FALSE);
+    HMENU p;
     int menu_already_exists = (specials != NULL);
     int i, j;
 
-    specials = back->get_specials(backhandle);
+    if (back)
+       specials = back->get_specials(backhandle);
+    else
+       specials = NULL;
+
     if (specials) {
-       HMENU p = CreateMenu();
+       p = CreateMenu();
        for (i = 0; specials[i].name; i++) {
            assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
            if (*specials[i].name)
@@ -880,19 +926,26 @@ void update_specials_menu(void *frontend)
            else
                AppendMenu(p, MF_SEPARATOR, 0, 0);
        }
-       for (j = 0; j < lenof(popup_menus); j++) {
-           if (menu_already_exists)
-               DeleteMenu(popup_menus[j].menu,
-                          popup_menus[j].specials_submenu_pos,
-                          MF_BYPOSITION);
-           else
-               InsertMenu(popup_menus[j].menu,
-                          popup_menus[j].specials_submenu_pos,
-                          MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+    } else
+       p = NULL;
+
+    for (j = 0; j < lenof(popup_menus); j++) {
+       if (menu_already_exists) {
+           DeleteMenu(popup_menus[j].menu,
+                      popup_menus[j].specials_submenu_pos,
+                      MF_BYPOSITION);
+           DeleteMenu(popup_menus[j].menu,
+                      popup_menus[j].specials_submenu_pos,
+                      MF_BYPOSITION);
+       }
+       if (specials) {
+           InsertMenu(popup_menus[j].menu,
+                      popup_menus[j].specials_submenu_pos,
+                      MF_BYPOSITION | MF_SEPARATOR, 0, 0);
            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");
        }
     }
 }
@@ -925,10 +978,7 @@ void connection_fatal(void *frontend, char *fmt, ...)
     if (cfg.close_on_exit == FORCE_ON)
        PostQuitMessage(1);
     else {
-       session_closed = TRUE;
-       sprintf(morestuff, "%.70s (inactive)", appname);
-       set_icon(NULL, morestuff);
-       set_title(NULL, morestuff);
+       close_session();
     }
 }
 
@@ -973,11 +1023,8 @@ static void enact_pending_netevent(void)
        if (cfg.close_on_exit == FORCE_ON ||
            cfg.close_on_exit == AUTO) PostQuitMessage(0);
        else {
-           char morestuff[100];
+           close_session();
            session_closed = TRUE;
-           sprintf(morestuff, "%.70s (inactive)", appname);
-           set_icon(NULL, morestuff);
-           set_title(NULL, morestuff);
            MessageBox(hwnd, "Connection closed by remote host",
                       appname, MB_OK | MB_ICONINFORMATION);
        }
@@ -1082,6 +1129,36 @@ static void init_palette(void)
 }
 
 /*
+ * This is a wrapper to ExtTextOut() to force Windows to display
+ * the precise glyphs we give it. Otherwise it would do its own
+ * bidi and Arabic shaping, and we would end up uncertain which
+ * characters it had put where.
+ */
+static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc,
+                         unsigned short *lpString, UINT cbCount,
+                         CONST INT *lpDx)
+{
+
+    GCP_RESULTSW gcpr;
+    char *buffer = snewn(cbCount*2+2, char);
+    char *classbuffer = snewn(cbCount, char);
+    memset(&gcpr, 0, sizeof(gcpr));
+    memset(buffer, 0, cbCount*2+2);
+    memset(classbuffer, GCPCLASS_NEUTRAL, cbCount);
+
+    gcpr.lStructSize = sizeof(gcpr);
+    gcpr.lpGlyphs = (void *)buffer;
+    gcpr.lpClass = classbuffer;
+    gcpr.nGlyphs = cbCount;
+
+    GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr,
+                          FLI_MASK | GCP_CLASSIN);
+
+    ExtTextOut(hdc, x, y, ETO_GLYPH_INDEX | ETO_CLIPPED | ETO_OPAQUE, lprc,
+              buffer, cbCount, lpDx);
+}
+
+/*
  * Initialise all the fonts we will need initially. There may be as many as
  * three or as few as one.  The other (poentially) twentyone fonts are done
  * if/when they are needed.
@@ -1691,6 +1768,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
     static int ignore_clip = FALSE;
     static int need_backend_resize = FALSE;
     static int fullscr_on_max = FALSE;
+    static UINT last_mousemove = 0;
 
     switch (message) {
       case WM_TIMER:
@@ -1707,7 +1785,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            time_t now;
            time(&now);
            if (now - last_movement > cfg.ping_interval) {
-               back->special(backhandle, TS_PING);
+               if (back)
+                   back->special(backhandle, TS_PING);
                last_movement = now;
            }
        }
@@ -1810,6 +1889,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    sfree(cl);
            }
            break;
+         case IDM_RESTART:
+           if (!back) {
+               logevent(NULL, "----- Session restarted -----");
+               start_backend();
+           }
+
+           break;
          case IDM_RECONF:
            {
                Config prev_cfg;
@@ -1842,7 +1928,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                 * Flush the line discipline's edit buffer in the
                 * case where local editing has just been disabled.
                 */
-               ldisc_send(ldisc, NULL, 0, 0);
+               if (ldisc)
+                   ldisc_send(ldisc, NULL, 0, 0);
                if (pal)
                    DeleteObject(pal);
                logpal = NULL;
@@ -1854,7 +1941,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                term_reconfig(term, &cfg);
 
                /* Pass new config data to the back end */
-               back->reconfig(backhandle, &cfg);
+               if (back)
+                   back->reconfig(backhandle, &cfg);
 
                /* Screen size changed ? */
                if (cfg.height != prev_cfg.height ||
@@ -1962,7 +2050,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            break;
          case IDM_RESET:
            term_pwron(term);
-           ldisc_send(ldisc, NULL, 0, 0);
+           if (ldisc)
+               ldisc_send(ldisc, NULL, 0, 0);
            break;
          case IDM_ABOUT:
            showabout(hwnd);
@@ -2010,7 +2099,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    if (!specials || !specials[j].name)
                        break;
                if (j == i) {
-                   back->special(backhandle, specials[i].code);
+                   if (back)
+                       back->special(backhandle, specials[i].code);
                    net_pending_errors();
                }
            }
@@ -2078,11 +2168,42 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
             * window, we put up the System menu instead of doing
             * selection.
             */
-           if (is_full_screen() && press && button == MBT_LEFT &&
-               X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
-               SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
-               return 0;
+           {
+               char mouse_on_hotspot = 0;
+               POINT pt;
+
+               GetCursorPos(&pt);
+#ifndef NO_MULTIMON
+               {
+                   HMONITOR mon;
+                   MONITORINFO mi;
+
+                   mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
+
+                   if (mon != NULL) {
+                       mi.cbSize = sizeof(MONITORINFO);
+                       GetMonitorInfo(mon, &mi);
+
+                       if (mi.rcMonitor.left == pt.x &&
+                           mi.rcMonitor.top == pt.y) {
+                           mouse_on_hotspot = 1;
+                       }
+                       CloseHandle(mon);
+                   }
+               }
+#else
+               if (pt.x == 0 && pt.y == 0) {
+                   mouse_on_hotspot = 1;
+               }
+#endif
+               if (is_full_screen() && press &&
+                   button == MBT_LEFT && mouse_on_hotspot) {
+                   SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU,
+                               MAKELPARAM(pt.x, pt.y));
+                   return 0;
+               }
            }
+
            if (press) {
                click(button,
                      TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
@@ -2099,7 +2220,21 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        }
        return 0;
       case WM_MOUSEMOVE:
-       show_mouseptr(1);
+       {
+           /*
+            * Windows seems to like to occasionally send MOUSEMOVE
+            * events even if the mouse hasn't moved. Don't unhide
+            * the mouse pointer in this case.
+            */
+           static WPARAM wp = 0;
+           static LPARAM lp = 0;
+           if (wParam != wp || lParam != lp ||
+               last_mousemove != WM_MOUSEMOVE) {
+               show_mouseptr(1);
+               wp = wParam; lp = lParam;
+               last_mousemove = WM_MOUSEMOVE;
+           }
+       }
        /*
         * Add the mouse position and message time to the random
         * number noise.
@@ -2122,7 +2257,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        }
        return 0;
       case WM_NCMOUSEMOVE:
-       show_mouseptr(1);
+       {
+           static WPARAM wp = 0;
+           static LPARAM lp = 0;
+           if (wParam != wp || lParam != lp ||
+               last_mousemove != WM_NCMOUSEMOVE) {
+               show_mouseptr(1);
+               wp = wParam; lp = lParam;
+               last_mousemove = WM_NCMOUSEMOVE;
+           }
+       }
        noise_ultralight(lParam);
        break;
       case WM_IGNORE_CLIP:
@@ -2528,7 +2672,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                     * we're sent.
                     */
                    term_seen_key_event(term);
-                   ldisc_send(ldisc, buf, len, 1);
+                   if (ldisc)
+                       ldisc_send(ldisc, buf, len, 1);
                    show_mouseptr(0);
                }
            }
@@ -2576,7 +2721,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                 */
                term_seen_key_event(term);
                for (i = 0; i < n; i += 2) {
-                   luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
+                   if (ldisc)
+                       luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
                }
                free(buff);
            }
@@ -2591,11 +2737,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            buf[1] = wParam;
            buf[0] = wParam >> 8;
            term_seen_key_event(term);
-           lpage_send(ldisc, kbd_codepage, buf, 2, 1);
+           if (ldisc)
+               lpage_send(ldisc, kbd_codepage, buf, 2, 1);
        } else {
            char c = (unsigned char) wParam;
            term_seen_key_event(term);
-           lpage_send(ldisc, kbd_codepage, &c, 1, 1);
+           if (ldisc)
+               lpage_send(ldisc, kbd_codepage, &c, 1, 1);
        }
        return (0);
       case WM_CHAR:
@@ -2609,7 +2757,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        {
            char c = (unsigned char)wParam;
            term_seen_key_event(term);
-           lpage_send(ldisc, CP_ACP, &c, 1, 1);
+           if (ldisc)
+               lpage_send(ldisc, CP_ACP, &c, 1, 1);
        }
        return 0;
       case WM_SETCURSOR:
@@ -2618,6 +2767,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;
@@ -2742,7 +2901,7 @@ 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,
+void do_text(Context ctx, int x, int y, wchar_t *text, int len,
             unsigned long attr, int lattr)
 {
     COLORREF fg, bg, t;
@@ -2750,11 +2909,14 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     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;
 
@@ -2802,43 +2964,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);
@@ -2885,7 +3043,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;
@@ -2900,15 +3058,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++;
        }
@@ -2927,10 +3090,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);
 
@@ -2944,7 +3118,7 @@ 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 */
@@ -2957,11 +3131,15 @@ void do_text(Context ctx, int x, int y, char *text, int 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];
 
-       ExtTextOutW(hdc, x,
+       /* 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);
+/*     ExtTextOutW(hdc, x,
                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
                    ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
+ */
 
        /* And the shadow bold hack. */
        if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
@@ -2988,7 +3166,7 @@ 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,
+void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
               unsigned long attr, int lattr)
 {
 
@@ -2998,7 +3176,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;
        }
@@ -3075,13 +3253,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;
     }
@@ -3089,12 +3267,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;
 
@@ -3102,8 +3280,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 */
@@ -3140,6 +3318,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
     HKL kbd_layout = GetKeyboardLayout(0);
 
+    /* keys is for ToAsciiEx. There's some ick here, see below. */
     static WORD keys[3];
     static int compose_char = 0;
     static WPARAM compose_key = 0;
@@ -3229,8 +3408,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
 
        /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
-       if ((cfg.funky_type == 3 ||
-            (cfg.funky_type <= 1 && term->app_keypad_keys &&
+       if ((cfg.funky_type == FUNKY_VT400 ||
+            (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys &&
              !cfg.no_applic_k))
            && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
 
@@ -3296,8 +3475,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
     /* Sanitize the number pad if not using a PC NumPad */
     if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
-                    && cfg.funky_type != 2)
-       || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
+                    && cfg.funky_type != FUNKY_XTERM)
+       || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) {
        if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
            int nParam = 0;
            switch (wParam) {
@@ -3426,8 +3605,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        if (!left_alt) {
            int xkey = 0;
 
-           if (cfg.funky_type == 3 ||
-               (cfg.funky_type <= 1 &&
+           if (cfg.funky_type == FUNKY_VT400 ||
+               (cfg.funky_type <= FUNKY_LINUX &&
                 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
                  case VK_EXECUTE:
                    xkey = 'P';
@@ -3479,7 +3658,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    xkey = 'n';
                    break;
                  case VK_ADD:
-                   if (cfg.funky_type == 2) {
+                   if (cfg.funky_type == FUNKY_XTERM) {
                        if (shift_state)
                            xkey = 'l';
                        else
@@ -3491,15 +3670,15 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    break;
 
                  case VK_DIVIDE:
-                   if (cfg.funky_type == 2)
+                   if (cfg.funky_type == FUNKY_XTERM)
                        xkey = 'o';
                    break;
                  case VK_MULTIPLY:
-                   if (cfg.funky_type == 2)
+                   if (cfg.funky_type == FUNKY_XTERM)
                        xkey = 'j';
                    break;
                  case VK_SUBTRACT:
-                   if (cfg.funky_type == 2)
+                   if (cfg.funky_type == FUNKY_XTERM)
                        xkey = 'm';
                    break;
 
@@ -3671,7 +3850,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            break;
        }
        /* Reorder edit keys to physical order */
-       if (cfg.funky_type == 3 && code <= 6)
+       if (cfg.funky_type == FUNKY_VT400 && code <= 6)
            code = "\0\2\1\4\5\3\6"[code];
 
        if (term->vt52_mode && code > 0 && code <= 6) {
@@ -3679,7 +3858,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            return p - output;
        }
 
-       if (cfg.funky_type == 5 &&     /* SCO function keys */
+       if (cfg.funky_type == FUNKY_SCO &&     /* SCO function keys */
            code >= 11 && code <= 34) {
            char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
            int index = 0;
@@ -3702,7 +3881,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            p += sprintf((char *) p, "\x1B[%c", codes[index]);
            return p - output;
        }
-       if (cfg.funky_type == 5 &&     /* SCO small keypad */
+       if (cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */
            code >= 1 && code <= 6) {
            char codes[] = "HL.FIG";
            if (code == 3) {
@@ -3712,7 +3891,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            }
            return p - output;
        }
-       if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
+       if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {
            int offt = 0;
            if (code > 15)
                offt++;
@@ -3725,11 +3904,11 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
            return p - output;
        }
-       if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
+       if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
            p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
            return p - output;
        }
-       if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
+       if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
            if (term->vt52_mode)
                p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
            else
@@ -3831,7 +4010,27 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            keystate[VK_CAPITAL] = 0;
        }
 
-       r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
+       /* XXX how do we know what the max size of the keys array should
+        * be is? There's indication on MS' website of an Inquire/InquireEx
+        * functioning returning a KBINFO structure which tells us. */
+       if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+           /* XXX 'keys' parameter is declared in MSDN documentation as
+            * 'LPWORD lpChar'.
+            * The experience of a French user indicates that on
+            * Win98, WORD[] should be passed in, but on Win2K, it should
+            * be BYTE[]. German WinXP and my Win2K with "US International"
+            * driver corroborate this.
+            * Experimentally I've conditionalised the behaviour on the
+            * Win9x/NT split, but I suspect it's worse than that.
+            * See wishlist item `win-dead-keys' for more horrible detail
+            * and speculations. */
+           BYTE keybs[3];
+           int i;
+           r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout);
+           for (i=0; i<3; i++) keys[i] = keybs[i];
+       } else {
+           r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
+       }
 #ifdef SHOW_TOASCII_RESULT
        if (r == 1 && !key_down) {
            if (alt_sum) {
@@ -3880,7 +4079,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    }
                    keybuf = nc;
                    term_seen_key_event(term);
-                   luni_send(ldisc, &keybuf, 1, 1);
+                   if (ldisc)
+                       luni_send(ldisc, &keybuf, 1, 1);
                    continue;
                }
 
@@ -3891,7 +4091,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        if (in_utf(term) || ucsdata.dbcs_screenfont) {
                            keybuf = alt_sum;
                            term_seen_key_event(term);
-                           luni_send(ldisc, &keybuf, 1, 1);
+                           if (ldisc)
+                               luni_send(ldisc, &keybuf, 1, 1);
                        } else {
                            ch = (char) alt_sum;
                            /*
@@ -3904,12 +4105,14 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                             * everything we're sent.
                             */
                            term_seen_key_event(term);
-                           ldisc_send(ldisc, &ch, 1, 1);
+                           if (ldisc)
+                               ldisc_send(ldisc, &ch, 1, 1);
                        }
                        alt_sum = 0;
                    } else {
                        term_seen_key_event(term);
-                       lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
+                       if (ldisc)
+                           lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
                    }
                } else {
                    if(capsOn && ch < 0x80) {
@@ -3917,14 +4120,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        cbuf[0] = 27;
                        cbuf[1] = xlat_uskbd2cyrllic(ch);
                        term_seen_key_event(term);
-                       luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
+                       if (ldisc)
+                           luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
                    } else {
                        char cbuf[2];
                        cbuf[0] = '\033';
                        cbuf[1] = ch;
                        term_seen_key_event(term);
-                       lpage_send(ldisc, kbd_codepage,
-                                  cbuf+!left_alt, 1+!!left_alt, 1);
+                       if (ldisc)
+                           lpage_send(ldisc, kbd_codepage,
+                                      cbuf+!left_alt, 1+!!left_alt, 1);
                    }
                }
                show_mouseptr(0);
@@ -4433,10 +4638,10 @@ void beep(void *frontend, int mode)
     } else if (mode == BELL_WAVEFILE) {
        if (!PlaySound(cfg.bell_wavefile.path, NULL,
                       SND_ASYNC | SND_FILENAME)) {
-           char buf[sizeof(cfg.bell_wavefile) + 80];
+           char buf[sizeof(cfg.bell_wavefile.path) + 80];
            char otherbuf[100];
            sprintf(buf, "Unable to play sound file\n%s\n"
-                   "Using default sound instead", cfg.bell_wavefile);
+                   "Using default sound instead", cfg.bell_wavefile.path);
            sprintf(otherbuf, "%.70s Sound Error", appname);
            MessageBox(hwnd, buf, otherbuf,
                       MB_OK | MB_ICONEXCLAMATION);
@@ -4570,7 +4775,7 @@ char *get_window_title(void *frontend, int icon)
 /*
  * See if we're in full-screen mode.
  */
-int is_full_screen()
+static int is_full_screen()
 {
     if (!IsZoomed(hwnd))
        return FALSE;
@@ -4584,7 +4789,7 @@ int is_full_screen()
  * one monitor is present. */
 static int get_fullscreen_rect(RECT * ss)
 {
-#ifdef MONITOR_DEFAULTTONEAREST
+#if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON)
        HMONITOR mon;
        MONITORINFO mi;
        mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
@@ -4609,7 +4814,7 @@ static int get_fullscreen_rect(RECT * ss)
  * Go full-screen. This should only be called when we are already
  * maximised.
  */
-void make_full_screen()
+static void make_full_screen()
 {
     DWORD style;
        RECT ss;
@@ -4643,7 +4848,7 @@ void make_full_screen()
 /*
  * Clear the full-screen attributes.
  */
-void clear_full_screen()
+static void clear_full_screen()
 {
     DWORD oldstyle, style;
 
@@ -4673,7 +4878,7 @@ void clear_full_screen()
 /*
  * Toggle full-screen mode.
  */
-void flip_full_screen()
+static void flip_full_screen()
 {
     if (is_full_screen()) {
        ShowWindow(hwnd, SW_RESTORE);