How to add Activity Notification to PuTTY

For over a decade PuTTY has been my Telnet/SSH client of choice for Windows. I mostly use it as a MUD client nowadays and missed one convenience feature: activity notifications. Chat clients all flash for attention when activity occurs and it would be a great addition to streamline my social mudlife, so I set out on a quest to add it…

First of all you need to have Microsoft Visual C++ installed to compile the sourcecode for Windows which can be found on the PuTTY Download page. Get the sources unzipped and load the workspace “putty-src\WINDOWS\MSVC\PUTTY.DSW”, you will need to go through a conversion to a Visual Studio 2008 solution when using VS2008. All code additions and changes are done in the file WINDOW.C in the “putty” project.

To support activity notification we need to know when there is activity and notify the user by making the window flash if the window is not active and it has not already done a flash. This brings us down to three problems to solve: detecting activity, knowing when the window is not active and flashing the window.

 

Flashing the PuTTY Window

PuTTY already has the option to use the window flash as a visual bell so that code can be used as a template. Notice how this feature also has been implemented in the PuTTY code itself to support flash on older Windows OSes that do not support the FlashWindowEx API method. (I have left this code in place, but it has not been tested.)

The following code snippet must be added at the end of the file:

/* MARK SLETTERINK - ACTIVITY NOTIFICATION SUPPORT - FLASH METHODS */
/* SNIPPET ID: {34060BBE-6F8C-4bc1-AF1F-97B3FC8D24A4} */
static void flash_window_timer_activity(void *ctx, long now)
{
    if (flashing && now - next_flash >= 0) {
      flash_window_activity(1);
    }
}
 
static void flash_window_activity(int mode)
{
   if(mode==0) {
      /* stop */
   } else if (mode==2)
   {
      /* start */
      if (!flashing && !activity_blink_done) {
         flashing = 1;
         activity_blink_done=TRUE;
         if (p_FlashWindowEx) {
            /* For so-called "steady" mode, we use uCount=2, which
            * seems to be the traditional number of flashes used
            * by user notifications (e.g., by Explorer).
            * uCount=0 appears to enable continuous flashing, per
            * "flashing" mode, although I haven't seen this
            * documented. */
            flash_window_ex(FLASHW_ALL | FLASHW_TIMER,
               (cfg.beep_ind == B_IND_FLASH ? 0 : 2),
               0 /* system cursor blink rate */);
            /* No need to schedule timer */
         } else {
            FlashWindow(hwnd, TRUE);
            next_flash = schedule_timer(450, flash_window_timer_activity, hwnd);
         }
      }
   }
   else if(mode==1)
   {
      /* maintain */
      if (flashing && !p_FlashWindowEx) {
         FlashWindow(hwnd, TRUE);   /* toggle */
         next_flash = schedule_timer(450, flash_window_timer_activity, hwnd);
      }
 
   }
}
/* MARK SLETTERINK - END OF ACTIVITY NOTIFICATION SUPPORT - FLASH METHODS */

 

Add the following snippet to the forward declarations at the top of the file (surrounding code is show for reference to the location where it should be placed and must not be added):

#define TIMING_TIMER_ID 1234
static long timing_next_time;
 
/* MARK SLETTERINK - ACTIVITY NOTIFICATION SUPPORT - FLASH METHODS FORWARD DECLARATIONS AND VARS */
/* SNIPPET ID: {D8B4F4A3-F870-4ded-B298-EEB71701D25D}*/
static void flash_window_activity(int mode);
static BOOL window_is_active=TRUE;
static BOOL activity_blink_done=FALSE;
/* MARK SLETTERINK - END OF ACTIVITY NOTIFICATION SUPPORT - FLASH METHODS FORWARD DECLARATIONS AND VARS*/
 
static struct {
    HMENU menu;

The activity_blink_done boolean ensures the window flash gets triggered only one time once the window has become inactive and activity occurs.

 

Detecting the Window Active State

There are several ways of doing this, I embarked on the adventure of using a handler for the WM_ACTIVATE message to keep track of the current state because it seemed to blend in well with the rest of the code in place. The handler also clears the activity_blink_done variable so a new flash will be triggered when necessary.

Add the following snippet at the end of the file:

/* MARK SLETTERINK - ACTIVITY NOTIFICATION SUPPORT - WINDOW ACTIVE DETECTION */
/* SNIPPET ID: {75B2CA9C-49F4-4eb5-AFCF-E3D743C37C44} */
int Do_WM_ACTIVATE(HWND a_hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   WORD wAction = LOWORD(wParam);
   WORD wMinimized = HIWORD(wParam);
 
   if(a_hwnd == hwnd) {
      switch(wAction)
      {
         case WA_INACTIVE:
            window_is_active = FALSE;
            activity_blink_done = FALSE;
            break;
         case WA_ACTIVE:
         case WA_CLICKACTIVE:
            if(!wMinimized)
               window_is_active = TRUE;
            break;
         default:
            break;
      }
   }
}
/* MARK SLETTERINK - END OF ACTIVITY NOTIFICATION SUPPORT - WINDOW ACTIVE DETECTION */

 

With the message handler in place we need to hook it up in the window procedure. To do this we need to add a case statement for the WM_ACTIVATE message in the WndProc method. Insert the following code snippet right before default case handler (surrounding code shown for reference and must not be copied):

/* MARK SLETTERINK - ACTIVITY NOTIFICATION SUPPORT - WINDOW ACTIVE DETECTION */
/* SNIPPET ID: {5AFAA3AB-FB28-45e9-9DE7-E56648A1B5AF} */
      case WM_ACTIVATE:
         Do_WM_ACTIVATE(hwnd, message, wParam, lParam);
         break;
/* MARK SLETTERINK - END OF ACTIVITY NOTIFICATION SUPPORT - WINDOW ACTIVE DETECTION */
      default:
         if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {

 

For completeness add a forward declaration below the previous forward declaration snippet we added:

/* MARK SLETTERINK - ACTIVITY NOTIFICATION SUPPORT - WINDOW ACTIVE DETECTION FORWARD DECLARATION */
/* SNIPPET ID: {D3292300-A6A3-402f-A0A5-CE259BD25EC0} */
int Do_WM_ACTIVATE(HWND a_hwnd, UINT message, WPARAM wParam, LPARAM lParam);
/* MARK SLETTERINK - END OF ACTIVITY NOTIFICATION SUPPORT - WINDOW ACTIVE DETECTION FORWARD DECLARATION */

 

Detecting Session Activity

With the window flash and window state support in place all that is left to do is hooking it up to the session activity detection. Find the from_backend method and insert the code snippet below so the method resembles the code block:

int from_backend(void *frontend, int is_stderr, const char *data, int len)
{
/* MARK SLETTERINK - ACTIVITY NOTIFICATION SUPPORT - SESSION ACTIVITY DETECTION */
/* SNIPPET ID: {9198643F-09DF-466f-971F-EB05EA15A85E} */
   if(!window_is_active)
      flash_window_activity(2);
/* MARK SLETTERINK - END OF ACTIVITY NOTIFICATION SUPPORT - SESSION ACTIVITY DETECTION */
   return term_data(term, is_stderr, data, len);
}

 

Enjoy!

That completes the code additions. Compile the code, fire up PuTTY and enjoy the new activity notification feature!

Quest complete! You gained 1 million XP.

My Latest Track