Created a shiny new abstraction for the socket handling. Has many
[u/mdw/putty] / window.c
index 0ab58cf..1e38a48 100644 (file)
--- a/window.c
+++ b/window.c
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <time.h>
 
 #define PUTTY_DO_GLOBALS                      /* actually _define_ globals */
 #include "putty.h"
+#include "winstuff.h"
 #include "storage.h"
 #include "win_res.h"
 
 #define WM_IGNORE_SIZE (WM_XUSER + 1)
 #define WM_IGNORE_CLIP (WM_XUSER + 2)
 
+/* Needed for Chinese support and apparently not always defined. */
+#ifndef VK_PROCESSKEY
+#define VK_PROCESSKEY 0xE5
+#endif
+
 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
 static void cfgtopalette(void);
@@ -57,6 +64,8 @@ static WPARAM pend_netevent_wParam = 0;
 static LPARAM pend_netevent_lParam = 0;
 static void enact_pending_netevent(void);
 
+static time_t last_movement = 0;
+
 #define FONT_NORMAL 0
 #define FONT_BOLD 1
 #define FONT_UNDERLINE 2
@@ -104,7 +113,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     MSG msg;
     int guess_width, guess_height;
 
-    putty_inst = inst;
+    hinst = inst;
     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
 
     winsock_ver = MAKEWORD(1, 1);
@@ -120,9 +129,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        return 1;
     }
     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
+    sk_init();
 
     InitCommonControls();
 
+    /* Ensure a Maximize setting in Explorer doesn't maximise the
+     * config box. */
+    defuse_showwindow();
+
     /*
      * Process the command line.
      */
@@ -132,7 +146,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        default_protocol = DEFAULT_PROTOCOL;
        default_port = DEFAULT_PORT;
 
-       do_defaults(NULL);
+       do_defaults(NULL, &cfg);
 
        p = cmdline;
        while (*p && isspace(*p)) p++;
@@ -190,7 +204,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         * An initial @ means to activate a saved session.
         */
        if (*p == '@') {
-           do_defaults (p+1);
+           int i = strlen(p);
+           while (i > 1 && isspace(p[i-1]))
+               i--;
+           p[i] = '\0';
+           do_defaults (p+1, &cfg);
            if (!*cfg.host && !do_config()) {
                WSACleanup();
                return 0;
@@ -345,14 +363,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     }
 
     {
-       int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
-       if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
-       if (cfg.locksize)   winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
-       hwnd = CreateWindow (appname, appname,
-                           winmode,
-                           CW_USEDEFAULT, CW_USEDEFAULT,
-                           guess_width, guess_height,
-                           NULL, NULL, inst, NULL);
+       int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
+       if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
+       if (cfg.locksize)   winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
+       hwnd = CreateWindow (appname, appname,
+                            winmode,
+                            CW_USEDEFAULT, CW_USEDEFAULT,
+                            guess_width, guess_height,
+                            NULL, NULL, inst, NULL);
     }
 
     /*
@@ -418,7 +436,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        char msg[1024], *title;
        char *realhost;
 
-       error = back->init (hwnd, cfg.host, cfg.port, &realhost);
+       error = back->init (cfg.host, cfg.port, &realhost);
        if (error) {
            sprintf(msg, "Unable to open connection:\n%s", error);
            MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
@@ -512,10 +530,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     has_focus = (GetForegroundWindow() == hwnd);
     UpdateWindow (hwnd);
 
+    if (GetMessage (&msg, NULL, 0, 0) == 1)
     {
        int timer_id = 0, long_timer = 0;
 
-       while (GetMessage (&msg, NULL, 0, 0) == 1) {
+       while (msg.message != WM_QUIT) {
            /* Sometimes DispatchMessage calls routines that use their own
             * GetMessage loop, setup this timer so we get some control back.
             *
@@ -530,9 +549,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                timer_id = SetTimer(hwnd, 1, 20, NULL);
            DispatchMessage (&msg);
 
-           /* This is too fast, but I'll leave it for now 'cause it shows
-            * how often term_update is called (far too often at times!)
-            */
+           /* Make sure we blink everything that needs it. */
            term_blink(0);
 
            /* Send the paste buffer if there's anything to send */
@@ -542,31 +559,44 @@ 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_NOREMOVE)) {
-               if (pending_netevent) {
-                   enact_pending_netevent();
+           if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+               continue;
 
-                   term_blink(1);
-               }
-           } else continue;
-           if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
-               if (timer_id) {
-                   KillTimer(hwnd, timer_id);
-                   timer_id = 0;
-               }
-                HideCaret(hwnd);
-               if (inbuf_head)
-                   term_out();
-               term_update();
-                ShowCaret(hwnd);
-               if (!has_focus)
-                  timer_id = SetTimer(hwnd, 1, 2000, NULL);
-               else if (cfg.blinktext)
-                  timer_id = SetTimer(hwnd, 1, 250, NULL);
-               else
-                  timer_id = SetTimer(hwnd, 1, 500, NULL);
-               long_timer = 1;
+           if (pending_netevent) {
+               enact_pending_netevent();
+
+               /* Force the cursor blink on */
+               term_blink(1);
+
+               if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+                   continue;
            }
+
+           /* Okay there is now nothing to do so we make sure the screen is
+            * completely up to date then tell windows to call us in a little 
+            * while.
+            */
+           if (timer_id) {
+               KillTimer(hwnd, timer_id);
+               timer_id = 0;
+           }
+            HideCaret(hwnd);
+           if (inbuf_head)
+               term_out();
+           term_update();
+            ShowCaret(hwnd);
+           if (!has_focus)
+              timer_id = SetTimer(hwnd, 1, 59500, NULL);
+           else
+              timer_id = SetTimer(hwnd, 1, 250, NULL);
+           long_timer = 1;
+       
+           /* There's no point rescanning everything in the message queue
+            * so we do an apperently unneccesary wait here 
+            */
+           WaitMessage();
+           if (GetMessage (&msg, NULL, 0, 0) != 1)
+               break;
        }
     }
 
@@ -595,6 +625,28 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
 }
 
 /*
+ * Set up, or shut down, an AsyncSelect. Called from winnet.c.
+ */
+char *do_select(SOCKET skt, int startup) {
+    int msg, events;
+    if (startup) {
+       msg = WM_NETEVENT;
+       events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
+    } else {
+       msg = events = 0;
+    }
+    if (!hwnd)
+       return "do_select(): internal error (hwnd==NULL)";
+    if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
+        switch (WSAGetLastError()) {
+          case WSAENETDOWN: return "Network is down";
+          default: return "WSAAsyncSelect(): unknown error";
+        }
+    }
+    return NULL;
+}
+
+/*
  * Print a message box and close the connection.
  */
 void connection_fatal(char *fmt, ...) {
@@ -617,8 +669,9 @@ void connection_fatal(char *fmt, ...) {
  * Actually do the job requested by a WM_NETEVENT
  */
 static void enact_pending_netevent(void) {
-    int i;
     static int reentering = 0;
+    extern int select_result(WPARAM, LPARAM);
+    int ret;
 
     if (reentering)
         return;                        /* don't unpend the pending */
@@ -626,22 +679,10 @@ static void enact_pending_netevent(void) {
     pending_netevent = FALSE;
 
     reentering = 1;
-    i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
+    ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
     reentering = 0;
 
-    if (i < 0) {
-       char buf[1024];
-       switch (WSABASEERR + (-i) % 10000) {
-         case WSAECONNRESET:
-           sprintf(buf, "Connection reset by peer");
-           break;
-         default:
-           sprintf(buf, "Unexpected network error %d", -i);
-           break;
-       }
-        connection_fatal(buf);
-    }
-    if (i <= 0) {
+    if (ret == 0) {
        if (cfg.close_on_exit)
            PostQuitMessage(0);
        else {
@@ -745,7 +786,7 @@ font_messup:
 
     if (cfg.fontisbold) {
        fw_dontcare = FW_BOLD;
-       fw_bold = FW_BLACK;
+       fw_bold = FW_HEAVY;
    } else {
        fw_dontcare = FW_DONTCARE;
        fw_bold = FW_BOLD;
@@ -1013,6 +1054,16 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
         HideCaret(hwnd);
        term_update();
         ShowCaret(hwnd);
+       if (cfg.ping_interval > 0)
+        {
+           time_t now;
+           time(&now);
+           if (now-last_movement > cfg.ping_interval * 60 - 10)
+           {
+              back->special(TS_PING);
+              last_movement = now;
+           }
+        }
        return 0;
       case WM_CREATE:
        break;
@@ -1290,6 +1341,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
        pending_netevent = TRUE;
        pend_netevent_wParam=wParam;
        pend_netevent_lParam=lParam;
+       time(&last_movement);
        return 0;
       case WM_SETFOCUS:
        has_focus = TRUE;
@@ -1450,12 +1502,29 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
            unsigned char buf[20];
            int len;
 
-           len = TranslateKey (message, wParam, lParam, buf);
-           if (len == -1)
-               return DefWindowProc (hwnd, message, wParam, lParam);
-           ldisc->send (buf, len);
+            if (wParam==VK_PROCESSKEY) {
+               MSG m;
+                m.hwnd = hwnd;
+                m.message = WM_KEYDOWN;
+                m.wParam = wParam;
+                m.lParam = lParam & 0xdfff;
+                TranslateMessage(&m);
+            } else {
+               len = TranslateKey (message, wParam, lParam, buf);
+               if (len == -1)
+                   return DefWindowProc (hwnd, message, wParam, lParam);
+               ldisc->send (buf, len);
+           }
        }
        return 0;
+      case WM_IME_CHAR:
+       {
+           unsigned char buf[2];
+
+           buf[1] = wParam;
+           buf[0] = wParam >> 8;
+           ldisc->send (buf, 2);
+       }
       case WM_CHAR:
       case WM_SYSCHAR:
        /*
@@ -1569,16 +1638,7 @@ void do_text (Context ctx, int x, int y, char *text, int len,
            }
     }
 
-    if (attr & ATTR_GBCHR) {
-       int i;
-       /*
-        * GB mapping: map # to pound, and everything else stays
-        * normal.
-        */
-       for (i=0; i<len; i++)
-           if (text[i] == '#')
-               text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
-    } else if (attr & ATTR_LINEDRW) {
+    if (attr & ATTR_LINEDRW) {
        int i;
        /* ISO 8859-1 */
        static const char poorman[] =
@@ -1602,6 +1662,8 @@ void do_text (Context ctx, int x, int y, char *text, int len,
        /*
         * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
         * VT100 line drawing chars; everything else stays normal.
+        *
+        * Actually '_' maps to space too, but that's done before.
         */
        switch (cfg.vtmode) {
          case VT_XWINDOWS:
@@ -1717,15 +1779,6 @@ static int check_compose(int first, int second) {
     static int recurse = 0;
     int nc = -1;
 
-    if(0)
-    {
-       char buf[256];
-       char * p;
-       sprintf(buf, "cc(%d,%d)", first, second);
-       for(p=buf; *p; p++)
-           c_write1(*p);
-    }
-
     for(c=composetbl; *c; c++) {
        if( (*c)[0] == first && (*c)[1] == second)
        {
@@ -1752,44 +1805,105 @@ static int check_compose(int first, int second) {
  * codes. Returns number of bytes used or zero to drop the message
  * or -1 to forward the message to windows.
  */
-static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
+static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
+                       unsigned char *output) {
     BYTE keystate[256];
     int  scan, left_alt = 0, key_down, shift_state;
     int  r, i, code;
     unsigned char * p = output;
 
-static WORD keys[3];
-static int compose_state = 0;
-static int compose_char = 0;
-static WPARAM compose_key = 0;
+    static WORD keys[3];
+    static int compose_state = 0;
+    static int compose_char = 0;
+    static WPARAM compose_key = 0;
 
     r = GetKeyboardState(keystate);
     if (!r) memset(keystate, 0, sizeof(keystate));
     else
     {
+#if 0
+       {  /* Tell us all about key events */
+         static BYTE oldstate[256];
+         static int first = 1;
+         static int scan;
+         int ch;
+         if(first) memcpy(oldstate, keystate, sizeof(oldstate));
+         first=0;
+
+         if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
+            debug(("+"));
+         } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
+            debug((". U"));
+         } else {
+            debug((".\n"));
+            if (wParam >= VK_F1 && wParam <= VK_F20 )
+               debug(("K_F%d", wParam+1-VK_F1));
+            else switch(wParam)
+            {
+            case VK_SHIFT:   debug(("SHIFT")); break;
+            case VK_CONTROL: debug(("CTRL")); break;
+            case VK_MENU:    debug(("ALT")); break;
+            default:         debug(("VK_%02x", wParam));
+            }
+            if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
+               debug(("*"));
+            debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
+
+            ch = MapVirtualKey(wParam, 2);
+            if (ch>=' ' && ch<='~') debug((", '%c'", ch));
+            else if (ch)            debug((", $%02x", ch));
+
+            if (keys[0]) debug((", KB0=%02x", keys[0]));
+            if (keys[1]) debug((", KB1=%02x", keys[1]));
+            if (keys[2]) debug((", KB2=%02x", keys[2]));
+
+            if ( (keystate[VK_SHIFT]&0x80)!=0)     debug((", S"));
+            if ( (keystate[VK_CONTROL]&0x80)!=0)   debug((", C"));
+            if ( (HIWORD(lParam)&KF_EXTENDED) )    debug((", E"));
+            if ( (HIWORD(lParam)&KF_UP) )          debug((", U"));
+         }
+
+         if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
+            ;
+         else if ( (HIWORD(lParam)&KF_UP) ) 
+            oldstate[wParam&0xFF] ^= 0x80;
+         else
+            oldstate[wParam&0xFF] ^= 0x81;
+
+         for(ch=0; ch<256; ch++)
+            if (oldstate[ch] != keystate[ch])
+               debug((", M%02x=%02x", ch, keystate[ch]));
+
+         memcpy(oldstate, keystate, sizeof(oldstate));
+       }
+#endif
+
        /* Note if AltGr was pressed and if it was used as a compose key */
-       if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
-       {
-           keystate[VK_RMENU] = keystate[VK_MENU];
-           if (!compose_state) compose_key = wParam;
-       }
-       if (wParam == VK_APPS && !compose_state)
-           compose_key = wParam;
+       if (cfg.compose_key) {
+           if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
+           {
+               keystate[VK_RMENU] = keystate[VK_MENU];
+               if (!compose_state) compose_key = wParam;
+           }
+           if (wParam == VK_APPS && !compose_state)
+               compose_key = wParam;
 
-       if (wParam == compose_key)
-       {
-            if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
-              compose_state = 1;
-           else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
-              compose_state = 2;
-           else
-              compose_state = 0;
-       }
-       else if (compose_state==1 && wParam != VK_CONTROL)
-          compose_state = 0;
+           if (wParam == compose_key)
+           {
+               if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
+                   compose_state = 1;
+               else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
+                   compose_state = 2;
+               else
+                   compose_state = 0;
+           }
+           else if (compose_state==1 && wParam != VK_CONTROL)
+               compose_state = 0;
+       } else
+           compose_state = 0;
 
        /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
-       if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
+       if ( (cfg.funky_type == 3 || (cfg.funky_type <= 1 && app_keypad_keys))
              && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
 
            wParam = VK_EXECUTE;
@@ -1835,7 +1949,7 @@ static WPARAM compose_key = 0;
 
     /* Sanitize the number pad if not using a PC NumPad */
     if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
-         || cfg.nethack_keypad || compose_state )
+       || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
     {
        if ((HIWORD(lParam)&KF_EXTENDED) == 0)
        {
@@ -1890,6 +2004,11 @@ static WPARAM compose_key = 0;
             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
             return -1;
        }
+       /* Control-Numlock for app-keypad mode switch */
+       if (wParam == VK_PAUSE && shift_state == 2) {
+           app_keypad_keys ^= 1;
+           return 0;
+       }
 
        /* Nethack keypad */
        if (cfg.nethack_keypad && !left_alt) {
@@ -1910,9 +2029,9 @@ static WPARAM compose_key = 0;
        if (!left_alt) {
           int xkey = 0;
 
-          if ( cfg.funky_type == 0 ||
-             ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
-              case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
+          if ( cfg.funky_type == 3 ||
+             ( cfg.funky_type <= 1 && app_keypad_keys)) switch(wParam) {
+              case VK_EXECUTE: xkey = 'P'; break;
               case VK_DIVIDE:  xkey = 'Q'; break;
               case VK_MULTIPLY:xkey = 'R'; break;
               case VK_SUBTRACT:xkey = 'S'; break;
@@ -1930,9 +2049,17 @@ static WPARAM compose_key = 0;
               case VK_NUMPAD9: xkey = 'y'; break;
 
               case VK_DECIMAL: xkey = 'n'; break;
-              case VK_ADD:     if(shift_state) xkey = 'm'; 
-                               else            xkey = 'l';
+              case VK_ADD:     if(cfg.funky_type==2) { 
+                                   if(shift_state) xkey = 'l';
+                                   else            xkey = 'k';
+                               } else if(shift_state)  xkey = 'm'; 
+                                 else                  xkey = 'l';
                                break;
+
+              case VK_DIVIDE:  if(cfg.funky_type==2) xkey = 'o'; break;
+              case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
+              case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
+
               case VK_RETURN:
                                if (HIWORD(lParam)&KF_EXTENDED)
                                    xkey = 'M';
@@ -2031,12 +2158,18 @@ static WPARAM compose_key = 0;
          case VK_PRIOR: code = 5; break;
          case VK_NEXT: code = 6; break;
        }
+       /* Reorder edit keys to physical order */
+       if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
+
        if (cfg.funky_type == 1 && 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) {
-           p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
+           if (vt52_mode)
+               p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
+           else
+               p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
            return p - output;
        }
        if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
@@ -2072,6 +2205,16 @@ static WPARAM compose_key = 0;
                return p - output;
            }
        }
+
+       /*
+        * Finally, deal with Return ourselves. (Win95 seems to
+        * foul it up when Alt is pressed, for some reason.)
+        */
+       if (wParam == VK_RETURN)       /* Return */
+       {
+           *p++ = 0x0D;
+           return p-output;
+       }
     }
 
     /* Okay we've done everything interesting; let windows deal with 
@@ -2102,7 +2245,7 @@ static WPARAM compose_key = 0;
 
                    if ((nc=check_compose(compose_char,ch)) == -1)
                    {
-                       c_write1('\007');
+                       MessageBeep(MB_ICONHAND);
                        return 0;
                    }
                    *p++ = xlat_kbd2tty((unsigned char)nc);
@@ -2130,10 +2273,9 @@ static WPARAM compose_key = 0;
     }
 
     /* This stops ALT press-release doing a 'COMMAND MENU' function */
-    if (message == WM_SYSKEYUP && wParam == VK_MENU) 
-    {
-       keystate[VK_MENU] = 0;
-       return 0;
+    if (!cfg.alt_only) {
+       if (message == WM_SYSKEYUP && wParam == VK_MENU) 
+           return 0;
     }
 
     return -1;