New improved bell handling. Choice between visual and audible bell;
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Fri, 13 Apr 2001 10:52:36 +0000 (10:52 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Fri, 13 Apr 2001 10:52:36 +0000 (10:52 +0000)
configurable bell overload handling. Thanks to Robert de Bath for
galvanising me into doing this, but I've had to rip most of his code
out and redo it myself...

git-svn-id: svn://svn.tartarus.org/sgt/putty@1039 cda61777-01e9-0310-a592-d414129be87e

putty.h
settings.c
terminal.c
windlg.c
window.c

diff --git a/putty.h b/putty.h
index 061074b..b560688 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -72,6 +72,9 @@ GLOBAL int outbuf_head, outbuf_reap;
 
 GLOBAL int has_focus;
 
+GLOBAL int in_vbell;
+GLOBAL long vbell_timeout;
+
 GLOBAL int app_cursor_keys, app_keypad_keys, vt52_mode;
 GLOBAL int repeat_off, cr_lf_return;
 
@@ -214,7 +217,11 @@ typedef struct {
     int lfhascr;
     int cursor_type;                  /* 0=block 1=underline 2=vertical */
     int blink_cur;
-    int beep;
+    int beep;                         /* 0=none 1=defaultsound 2=visual */
+    int bellovl;                      /* bell overload protection active? */
+    int bellovl_n;                    /* number of bells to cause overload */
+    int bellovl_t;                    /* time interval for overload (seconds) */
+    int bellovl_s;                    /* period of silence to re-enable bell (s) */
     int scrollbar;
     int locksize;
     int bce;
index 559d481..e1bfbc3 100644 (file)
@@ -104,6 +104,10 @@ void save_settings (char *section, int do_host, Config *cfg) {
     write_setting_i (sesskey, "CurType", cfg->cursor_type);
     write_setting_i (sesskey, "BlinkCur", cfg->blink_cur);
     write_setting_i (sesskey, "Beep", cfg->beep);
+    write_setting_i (sesskey, "BellOverload", cfg->bellovl);
+    write_setting_i (sesskey, "BellOverloadN", cfg->bellovl_n);
+    write_setting_i (sesskey, "BellOverloadT", cfg->bellovl_t);
+    write_setting_i (sesskey, "BellOverloadS", cfg->bellovl_s);
     write_setting_i (sesskey, "ScrollbackLines", cfg->savelines);
     write_setting_i (sesskey, "DECOriginMode", cfg->dec_om);
     write_setting_i (sesskey, "AutoWrapMode", cfg->wrap_mode);
@@ -256,6 +260,10 @@ void load_settings (char *section, int do_host, Config *cfg) {
     gppi (sesskey, "CurType", 0, &cfg->cursor_type);
     gppi (sesskey, "BlinkCur", 0, &cfg->blink_cur);
     gppi (sesskey, "Beep", 1, &cfg->beep);
+    gppi (sesskey, "BellOverload", 1, &cfg->bellovl);
+    gppi (sesskey, "BellOverloadN", 5, &cfg->bellovl_n);
+    gppi (sesskey, "BellOverloadT", 2, &cfg->bellovl_t);
+    gppi (sesskey, "BellOverloadS", 5, &cfg->bellovl_s);
     gppi (sesskey, "ScrollbackLines", 200, &cfg->savelines);
     gppi (sesskey, "DECOriginMode", 0, &cfg->dec_om);
     gppi (sesskey, "AutoWrapMode", 1, &cfg->wrap_mode);
index 9ef0c5c..e6c1504 100644 (file)
@@ -55,6 +55,17 @@ static unsigned long *disptext;             /* buffer of text on real screen */
 static unsigned long *wanttext;               /* buffer of text we want on screen */
 static unsigned long *alttext;        /* buffer of text on alt. screen */
 
+#define VBELL_TIMEOUT 100             /* millisecond duration of visual bell */
+
+struct beeptime {
+    struct beeptime *next;
+    long ticks;
+};
+static struct beeptime *beephead, *beeptail;
+int nbeeps;
+int beep_overloaded;
+long lastbeep;
+
 static unsigned char *selspace;               /* buffer for building selections in */
 
 #define TSIZE (sizeof(*text))
@@ -183,6 +194,7 @@ static void power_on(void) {
     alt_cset = cset = 0;
     cset_attr[0] = cset_attr[1] = ATTR_ASCII;
     rvideo = 0;
+    in_vbell = FALSE;
     cursor_on = 1;
     save_attr = curr_attr = ATTR_DEFAULT;
     term_editing = term_echoing = FALSE;
@@ -255,6 +267,10 @@ void term_init(void) {
     deselect();
     rows = cols = -1;
     power_on();
+    beephead = beeptail = NULL;
+    nbeeps = 0;
+    lastbeep = FALSE;
+    beep_overloaded = FALSE;
 }
 
 /*
@@ -773,9 +789,6 @@ static void do_osc(void) {
 void term_out(void) {
     int c, inbuf_reap;
 
-static int beep_overload = 0;
-    int beep_count = 0;
-
     for(inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++)
     {
         c = inbuf[inbuf_reap];
@@ -829,9 +842,77 @@ static int beep_overload = 0;
                }
                break;
              case '\007':
-               beep_count++; 
-               if(beep_count>6) beep_overload=1;
-               disptop = scrtop;
+               {
+                   struct beeptime *newbeep;
+                   long ticks;
+
+                   ticks = GetTickCount();
+debug(("beep received at %ld, last was %ld, nbeeps=%d\n", ticks, lastbeep, nbeeps));
+
+                   if (!beep_overloaded) {
+                       newbeep = smalloc(sizeof(struct beeptime));
+                       newbeep->ticks = ticks;
+                       newbeep->next = NULL;
+                       if (!beephead)
+                           beephead = newbeep;
+                       else
+                           beeptail->next = newbeep;
+                       beeptail = newbeep;
+                       nbeeps++;
+                   }
+
+                   /*
+                    * Throw out any beeps that happened more than
+                    * t seconds ago.
+                    */
+                   while (beephead &&
+                          beephead->ticks < ticks - cfg.bellovl_t*1000) {
+                       struct beeptime *tmp = beephead;
+                       beephead = tmp->next;
+debug(("throwing out beep received at %ld\n", tmp->ticks));
+                       sfree(tmp);
+                       if (!beephead)
+                           beeptail = NULL;
+                       nbeeps--;
+                   }
+
+                   if (cfg.bellovl && beep_overloaded &&
+                       ticks-lastbeep >= cfg.bellovl_s * 1000) {
+                       /*
+                        * If we're currently overloaded and the
+                        * last beep was more than s seconds ago,
+                        * leave overload mode.
+                        */
+debug(("silence reigns, leaving overload mode\n"));
+                       beep_overloaded = FALSE;
+                   } else if (cfg.bellovl && !beep_overloaded &&
+                              nbeeps >= cfg.bellovl_n) {
+                       /*
+                        * Now, if we have n or more beeps
+                        * remaining in the queue, go into overload
+                        * mode.
+                        */
+debug(("%d beeps between times %ld and %ld, overload!\n",
+       nbeeps, beephead->ticks, ticks));
+                       beep_overloaded = TRUE;
+                   }
+                   lastbeep = ticks;
+
+                   /*
+                    * Perform an actual beep if we're not overloaded.
+                    */
+                   if ((!cfg.bellovl || !beep_overloaded) && cfg.beep != 0) {
+debug(("not overloaded; performing a beep\n"));
+                       if (cfg.beep != 2)
+                           beep(cfg.beep);
+                       else if(cfg.beep == 2) {
+                           in_vbell = TRUE;
+                           vbell_timeout = ticks + VBELL_TIMEOUT;
+                           term_update();
+                       }
+                   }
+                   disptop = scrtop;
+               }
                break;
              case '\b':
                if (curs_x == 0 && curs_y == 0)
@@ -1753,13 +1834,6 @@ static int beep_overload = 0;
            check_selection (cpos, cpos+1);
     }
     inbuf_head = 0;
-
-    if (beep_overload)
-    {
-       if(!beep_count) beep_overload=0;
-    }
-    else if(beep_count && beep_count<5 && cfg.beep)
-       beep(beep_count/3);
 }
 
 /*
@@ -1783,7 +1857,23 @@ static void do_paint (Context ctx, int may_optimise){
     int i, j, start, our_curs_y;
     unsigned long attr, rv, cursor;
     char ch[1024];
+    long ticks;
 
+    /*
+     * Check the visual bell state.
+     */
+    if (in_vbell) {
+       ticks = GetTickCount();
+       if (ticks - vbell_timeout >= 0)
+           in_vbell = FALSE;
+    }
+
+    /* Depends on:
+     * screen array, disptop, scrtop,
+     * selection, rv, 
+     * cfg.blinkpc, blink_is_real, tblinker, 
+     * curs_y, curs_x, blinker, cfg.blink_cur, cursor_on, has_focus
+     */
     if (cursor_on) {
         if (has_focus) {
            if (blinker || !cfg.blink_cur)
@@ -1796,8 +1886,9 @@ static void do_paint (Context ctx, int may_optimise){
        if (wrapnext)
            cursor |= ATTR_RIGHTCURS;
     }
-    else           cursor = 0;
-    rv = (rvideo ? ATTR_REVERSE : 0);
+    else
+       cursor = 0;
+    rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
     our_curs_y = curs_y + (scrtop - disptop) / (cols+1);
 
     for (i=0; i<rows; i++) {
index e619469..916a9e5 100644 (file)
--- a/windlg.c
+++ b/windlg.c
@@ -223,6 +223,7 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue,
     sessionpanelend,
 
     loggingpanelstart,
+    IDC_TITLE_LOGGING,
     IDC_BOX_LOGGING1,
     IDC_LSTATSTATIC,
     IDC_LSTATOFF,
@@ -269,7 +270,6 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue,
     IDC_WRAPMODE,
     IDC_DECOM,
     IDC_LFHASCR,
-    IDC_BEEP,
     IDC_BCE,
     IDC_BLINKTEXT,
     IDC_ANSWERBACK,
@@ -284,6 +284,24 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue,
     IDC_EDITNO,
     terminalpanelend,
 
+    bellpanelstart,
+    IDC_TITLE_BELL,
+    IDC_BOX_BELL1,
+    IDC_BOX_BELL2,
+    IDC_BELLSTATIC,
+    IDC_BELL_DISABLED,
+    IDC_BELL_DEFAULT,
+    IDC_BELL_VISUAL,
+    IDC_BELLOVL,
+    IDC_BELLOVLNSTATIC,
+    IDC_BELLOVLN,
+    IDC_BELLOVLTSTATIC,
+    IDC_BELLOVLT,
+    IDC_BELLOVLEXPLAIN,
+    IDC_BELLOVLSSTATIC,
+    IDC_BELLOVLS,
+    bellpanelend,
+
     windowpanelstart,
     IDC_TITLE_WINDOW,
     IDC_BOX_WINDOW1,
@@ -547,7 +565,14 @@ static void init_dlg_ctrls(HWND hwnd) {
     SetDlgItemInt (hwnd, IDC_SAVEEDIT, cfg.savelines, FALSE);
     fmtfont (fontstatic);
     SetDlgItemText (hwnd, IDC_FONTSTATIC, fontstatic);
-    CheckDlgButton (hwnd, IDC_BEEP, cfg.beep);
+    CheckRadioButton (hwnd, IDC_BELL_DISABLED, IDC_BELL_VISUAL,
+                     cfg.beep==0 ? IDC_BELL_DISABLED :
+                     cfg.beep==1 ? IDC_BELL_DEFAULT : IDC_BELL_VISUAL);
+    CheckDlgButton (hwnd, IDC_BELLOVL, cfg.bellovl);
+    SetDlgItemInt (hwnd, IDC_BELLOVLN, cfg.bellovl_n, FALSE);
+    SetDlgItemInt (hwnd, IDC_BELLOVLT, cfg.bellovl_t, FALSE);
+    SetDlgItemInt (hwnd, IDC_BELLOVLS, cfg.bellovl_s, FALSE);
+
     CheckDlgButton (hwnd, IDC_BCE, cfg.bce);
     CheckDlgButton (hwnd, IDC_BLINKTEXT, cfg.blinktext);
 
@@ -738,7 +763,7 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) {
         struct ctlpos cp;
         ctlposinit(&cp, hwnd, 80, 3, 13);
         bartitle(&cp, "Options controlling session logging",
-                 IDC_TITLE_TERMINAL);
+                 IDC_TITLE_LOGGING);
         beginbox(&cp, NULL, IDC_BOX_LOGGING1);
         radiobig(&cp,
                  "Session logging:", IDC_LSTATSTATIC,
@@ -752,7 +777,7 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) {
     }
 
     if (panel == terminalpanelstart) {
-        /* The Terminal panel. Accelerators used: [acgo] wdlben hts */
+        /* The Terminal panel. Accelerators used: [acgo] wdlen hts */
         struct ctlpos cp;
         ctlposinit(&cp, hwnd, 80, 3, 13);
         bartitle(&cp, "Options controlling the terminal emulation",
@@ -762,7 +787,6 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) {
         checkbox(&cp, "Auto &wrap mode initially on", IDC_WRAPMODE);
         checkbox(&cp, "&DEC Origin Mode initially on", IDC_DECOM);
         checkbox(&cp, "Implicit CR in every &LF", IDC_LFHASCR);
-        checkbox(&cp, "&Beep enabled", IDC_BEEP);
         checkbox(&cp, "Use background colour to &erase screen", IDC_BCE);
         checkbox(&cp, "Enable bli&nking text", IDC_BLINKTEXT);
         multiedit(&cp,
@@ -783,6 +807,35 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) {
         endbox(&cp);
     }
 
+    if (panel == bellpanelstart) {
+        /* The Bell panel. Accelerators used: [acgo] bdsm */
+        struct ctlpos cp;
+        ctlposinit(&cp, hwnd, 80, 3, 13);
+        bartitle(&cp, "Options controlling the terminal bell",
+                 IDC_TITLE_BELL);
+        beginbox(&cp, "Set the style of bell",
+                 IDC_BOX_BELL1);
+        radiobig(&cp,
+                 "Action to happen when a &bell occurs:", IDC_BELLSTATIC,
+                 "None (bell disabled)", IDC_BELL_DISABLED,
+                 "Play Windows Default Sound", IDC_BELL_DEFAULT,
+                 "Visual bell (flash window)", IDC_BELL_VISUAL, NULL);
+        endbox(&cp);
+       beginbox(&cp, "Control the bell overload behaviour",
+                IDC_BOX_BELL2);
+       checkbox(&cp, "Bell is temporarily &disabled when over-used",
+                IDC_BELLOVL);
+       staticedit(&cp, "Over-use means this &many bells...",
+                  IDC_BELLOVLNSTATIC, IDC_BELLOVLN, 20);
+       staticedit(&cp, "... in this many &seconds",
+                  IDC_BELLOVLTSTATIC, IDC_BELLOVLT, 20);
+       statictext(&cp, "The bell is re-enabled after a few seconds of silence.",
+                  IDC_BELLOVLEXPLAIN);
+       staticedit(&cp, "Seconds of silence required",
+                  IDC_BELLOVLSSTATIC, IDC_BELLOVLS, 20);
+        endbox(&cp);
+    }
+
     if (panel == keyboardpanelstart) {
         /* The Keyboard panel. Accelerators used: [acgo] h?sr~lxvunpymietd */
         struct ctlpos cp;
@@ -1165,6 +1218,7 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
         treeview_insert(&tvfaff, 1, "Logging");
         treeview_insert(&tvfaff, 0, "Terminal");
         treeview_insert(&tvfaff, 1, "Keyboard");
+        treeview_insert(&tvfaff, 1, "Bell");
         treeview_insert(&tvfaff, 0, "Window");
         treeview_insert(&tvfaff, 1, "Appearance");
         treeview_insert(&tvfaff, 1, "Translation");
@@ -1232,6 +1286,8 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
                create_controls(hwnd, dlgtype, keyboardpanelstart);
            if (!strcmp(buffer, "Terminal"))
                create_controls(hwnd, dlgtype, terminalpanelstart);
+           if (!strcmp(buffer, "Bell"))
+               create_controls(hwnd, dlgtype, bellpanelstart);
            if (!strcmp(buffer, "Window"))
                create_controls(hwnd, dlgtype, windowpanelstart);
            if (!strcmp(buffer, "Appearance"))
@@ -1588,11 +1644,33 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
                SetDlgItemText (hwnd, IDC_FONTSTATIC, fontstatic);
            }
            break;
-         case IDC_BEEP:
+         case IDC_BELL_DISABLED:
+         case IDC_BELL_DEFAULT:
+         case IDC_BELL_VISUAL:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               if (LOWORD(wParam)==IDC_BELL_DISABLED) cfg.beep = 0;
+               if (LOWORD(wParam)==IDC_BELL_DEFAULT) cfg.beep = 1;
+               if (LOWORD(wParam)==IDC_BELL_VISUAL) cfg.beep = 2;
+            }
+           break;
+         case IDC_BELLOVL:
            if (HIWORD(wParam) == BN_CLICKED ||
                HIWORD(wParam) == BN_DOUBLECLICKED)
-               cfg.beep = IsDlgButtonChecked (hwnd, IDC_BEEP);
+               cfg.bellovl = IsDlgButtonChecked (hwnd, IDC_BELLOVL);
            break;
+          case IDC_BELLOVLN:
+            if (HIWORD(wParam) == EN_CHANGE)
+                MyGetDlgItemInt (hwnd, IDC_BELLOVLN, &cfg.bellovl_n);
+            break;
+          case IDC_BELLOVLT:
+            if (HIWORD(wParam) == EN_CHANGE)
+                MyGetDlgItemInt (hwnd, IDC_BELLOVLT, &cfg.bellovl_t);
+            break;
+          case IDC_BELLOVLS:
+            if (HIWORD(wParam) == EN_CHANGE)
+                MyGetDlgItemInt (hwnd, IDC_BELLOVLS, &cfg.bellovl_s);
+            break;
          case IDC_BLINKTEXT:
            if (HIWORD(wParam) == BN_CLICKED ||
                HIWORD(wParam) == BN_DOUBLECLICKED)
index 0855949..8a9c6c7 100644 (file)
--- a/window.c
+++ b/window.c
@@ -595,14 +595,17 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                term_out();
            term_update();
             ShowCaret(hwnd);
-           if (!has_focus)
+           if (in_vbell)
+              /* Hmm, term_update didn't want to do an update too soon ... */
+              timer_id = SetTimer(hwnd, 1, 50, NULL);
+           else if (!has_focus)
               timer_id = SetTimer(hwnd, 1, 59500, NULL);
            else
               timer_id = SetTimer(hwnd, 1, 100, NULL);
            long_timer = 1;
        
            /* There's no point rescanning everything in the message queue
-            * so we do an apperently unneccesary wait here 
+            * so we do an apparently unnecessary wait here
             */
            WaitMessage();
            if (GetMessage (&msg, NULL, 0, 0) != 1)
@@ -2633,21 +2636,7 @@ void fatalbox(char *fmt, ...) {
 /*
  * Beep.
  */
-void beep(int errorbeep) {
-    static long last_beep = 0;
-    long now, beep_diff;
-
-    now = GetTickCount();
-    beep_diff = now-last_beep;
-
-    /* Make sure we only respond to one beep per packet or so */
-    if (beep_diff>=0 && beep_diff<50)
-        return;
-
-    if(errorbeep)
-       MessageBeep(MB_ICONHAND);
-    else
-       MessageBeep(MB_OK);
-
-    last_beep = GetTickCount();
+void beep(int mode) {
+    if (mode == 1)
+       MessageBeep(MB_OK);
 }