text: //---------------------------------------------------------------------------
  //
  // Copyright (c) Microsoft Corporation 1993-1996
  //
  // File: port.c
  //
  // This files contains the dialog code for the Port Settings property page.
  //
  // History:
  //   2-09-94 ScottH     Created
  //  11-06-95 ScottH     Ported to NT
  //
  //---------------------------------------------------------------------------
  
  
  #include "proj.h"           
  
  // This is the structure that is used to fill the 
  // max speed listbox
  typedef struct _Bauds
      {
      DWORD   dwDTERate;
      int     ids;
      } Bauds;
  
  static Bauds g_rgbauds[] = {
          { 110L,         IDS_BAUD_110     },
          { 300L,         IDS_BAUD_300     },
          { 1200L,        IDS_BAUD_1200    },
          { 2400L,        IDS_BAUD_2400    },
          { 4800L,        IDS_BAUD_4800    },
          { 9600L,        IDS_BAUD_9600    },
          { 19200,        IDS_BAUD_19200   },
          { 38400,        IDS_BAUD_38400   },
          { 57600,        IDS_BAUD_57600   },
          { 115200,       IDS_BAUD_115200  },
          { 230400,       IDS_BAUD_230400  },
          { 460800,       IDS_BAUD_460800  },
          { 921600,       IDS_BAUD_921600  },
          };
  
  // Command IDs for the parity listbox
  #define CMD_PARITY_EVEN         1
  #define CMD_PARITY_ODD          2
  #define CMD_PARITY_NONE         3
  #define CMD_PARITY_MARK         4
  #define CMD_PARITY_SPACE        5
  
  // Command IDs for the flow control listbox
  #define CMD_FLOWCTL_XONXOFF      1
  #define CMD_FLOWCTL_HARDWARE     2
  #define CMD_FLOWCTL_NONE         3
  
  // This table is the generic port settings table
  // that is used to fill the various listboxes
  typedef struct _PortValues
      {
      union {
          BYTE bytesize;
          BYTE cmd;
          BYTE stopbits;
          };
      int ids;
      } PortValues, FAR * LPPORTVALUES;
  
  
  #pragma data_seg(DATASEG_READONLY)
  
  // This is the structure that is used to fill the data bits listbox
  static PortValues s_rgbytesize[] = {
          { 5,  IDS_BYTESIZE_5  },
          { 6,  IDS_BYTESIZE_6  },
          { 7,  IDS_BYTESIZE_7  },
          { 8,  IDS_BYTESIZE_8  },
          };
  
  // This is the structure that is used to fill the parity listbox
  static PortValues s_rgparity[] = {
          { CMD_PARITY_EVEN,  IDS_PARITY_EVEN  },
          { CMD_PARITY_ODD,   IDS_PARITY_ODD   },
          { CMD_PARITY_NONE,  IDS_PARITY_NONE  },
          { CMD_PARITY_MARK,  IDS_PARITY_MARK  },
          { CMD_PARITY_SPACE, IDS_PARITY_SPACE },
          };
  
  // This is the structure that is used to fill the stopbits listbox
  static PortValues s_rgstopbits[] = {
          { ONESTOPBIT,   IDS_STOPBITS_1   },
          { ONE5STOPBITS, IDS_STOPBITS_1_5 },
          { TWOSTOPBITS,  IDS_STOPBITS_2   },
          };
  
  // This is the structure that is used to fill the flow control listbox
  static PortValues s_rgflowctl[] = {
          { CMD_FLOWCTL_XONXOFF,  IDS_FLOWCTL_XONXOFF  },
          { CMD_FLOWCTL_HARDWARE, IDS_FLOWCTL_HARDWARE },
          { CMD_FLOWCTL_NONE,     IDS_FLOWCTL_NONE     },
          };
  
  #pragma data_seg()
  
  
  typedef struct tagPORT
      {
      HWND hdlg;              // dialog handle
      HWND hwndBaudRate;
      HWND hwndDataBits;
      HWND hwndParity;
      HWND hwndStopBits;
      HWND hwndFlowCtl;
  
      LPPORTINFO pportinfo;   // pointer to shared working buffer
      
      } PORT, FAR * PPORT;
  
      
  // This structure contains the default settings for the dialog
  static struct _DefPortSettings
      {
      int  iSelBaud;
      int  iSelDataBits;
      int  iSelParity;
      int  iSelStopBits;
      int  iSelFlowCtl;
      } s_defportsettings;
  
  // These are default settings
  #define DEFAULT_BAUDRATE            9600L
  #define DEFAULT_BYTESIZE            8
  #define DEFAULT_PARITY              CMD_PARITY_NONE
  #define DEFAULT_STOPBITS            ONESTOPBIT
  #define DEFAULT_FLOWCTL             CMD_FLOWCTL_NONE
  
  
  #define Port_GetPtr(hwnd)           (PPORT)GetWindowLongPtr(hwnd, DWLP_USER)
  #define Port_SetPtr(hwnd, lp)       (PPORT)SetWindowLongPtr(hwnd, DWLP_USER, (ULONG_PTR)(lp))
  
  UINT WINAPI FeFiFoFum(HWND hwndOwner, LPCTSTR pszPortName);
  
  
  /*----------------------------------------------------------
  Purpose: Fills the baud rate combobox with the possible baud
           rates that Windows supports.
  Returns: --
  Cond:    --
  */
  void PRIVATE Port_FillBaud(
      PPORT this)
      {
      HWND hwndCB = this->hwndBaudRate;
      WIN32DCB FAR * pdcb = &this->pportinfo->dcb;
      int i;
      int n;
      int iMatch = -1;
      int iDef = -1;
      int iSel;
      TCHAR sz[MAXMEDLEN];
  
      // Fill the listbox
      for (i = 0; i < ARRAYSIZE(g_rgbauds); i++)
          {
          n = ComboBox_AddString(hwndCB, SzFromIDS(g_hinst, g_rgbauds[i].ids, sz, SIZECHARS(sz)));
          ComboBox_SetItemData(hwndCB, n, g_rgbauds[i].dwDTERate);
  
          // Keep our eyes peeled for important values
          if (DEFAULT_BAUDRATE == g_rgbauds[i].dwDTERate)
              {
              iDef = n;
              }
          if (pdcb->BaudRate == g_rgbauds[i].dwDTERate)
              {
              iMatch = n;
              }
          }
  
      ASSERT(-1 != iDef);
      s_defportsettings.iSelBaud = iDef;
  
      // Does the DCB baudrate exist in our list of baud rates?
      if (-1 == iMatch)
          {
          // No; choose the default
          iSel = iDef;
          }
      else 
          {
          // Yes; choose the matched value
          ASSERT(-1 != iMatch);
          iSel = iMatch;
          }
      ComboBox_SetCurSel(hwndCB, iSel);
      }
  
  
  /*----------------------------------------------------------
  Purpose: Fills the bytesize combobox with the possible byte sizes.
  Returns: --
  Cond:    --
  */
  void PRIVATE Port_FillDataBits(
      PPORT this)
      {
      HWND hwndCB = this->hwndDataBits;
      WIN32DCB FAR * pdcb = &this->pportinfo->dcb;
      int i;
      int iSel;
      int n;
      int iMatch = -1;
      int iDef = -1;
      TCHAR sz[MAXMEDLEN];
  
      // Fill the listbox
      for (i = 0; i < ARRAYSIZE(s_rgbytesize); i++)
          {
          n = ComboBox_AddString(hwndCB, SzFromIDS(g_hinst, s_rgbytesize[i].ids, sz, SIZECHARS(sz)));
          ComboBox_SetItemData(hwndCB, n, s_rgbytesize[i].bytesize);
  
          // Keep our eyes peeled for important values
          if (DEFAULT_BYTESIZE == s_rgbytesize[i].bytesize)
              {
              iDef = n;
              }
          if (pdcb->ByteSize == s_rgbytesize[i].bytesize)
              {
              iMatch = n;
              }
          }
  
      ASSERT(-1 != iDef);
      s_defportsettings.iSelDataBits = iDef;
  
      // Does the DCB value exist in our list?
      if (-1 == iMatch)
          {
          // No; choose the default
          iSel = iDef;
          }
      else 
          {
          // Yes; choose the matched value
          ASSERT(-1 != iMatch);
          iSel = iMatch;
          }
      ComboBox_SetCurSel(hwndCB, iSel);
      }
  
  
  /*----------------------------------------------------------
  Purpose: Fills the parity combobox with the possible settings.
  Returns: --
  Cond:    --
  */
  void PRIVATE Port_FillParity(
      PPORT this)
      {
      HWND hwndCB = this->hwndParity;
      WIN32DCB FAR * pdcb = &this->pportinfo->dcb;
      int i;
      int iSel;
      int n;
      int iMatch = -1;
      int iDef = -1;
      TCHAR sz[MAXMEDLEN];
  
      // Fill the listbox
      for (i = 0; i < ARRAYSIZE(s_rgparity); i++)
          {
          n = ComboBox_AddString(hwndCB, SzFromIDS(g_hinst, s_rgparity[i].ids, sz, SIZECHARS(sz)));
          ComboBox_SetItemData(hwndCB, n, s_rgparity[i].cmd);
  
          // Keep our eyes peeled for important values
          if (DEFAULT_PARITY == s_rgparity[i].cmd)
              {
              iDef = n;
              }
          switch (s_rgparity[i].cmd)
              {
          case CMD_PARITY_EVEN:
              if (EVENPARITY == pdcb->Parity)
                  iMatch = n;
              break;
  
          case CMD_PARITY_ODD:
              if (ODDPARITY == pdcb->Parity)
                  iMatch = n;
              break;
  
          case CMD_PARITY_NONE:
              if (NOPARITY == pdcb->Parity)
                  iMatch = n;
              break;
  
          case CMD_PARITY_MARK:
              if (MARKPARITY == pdcb->Parity)
                  iMatch = n;
              break;
  
          case CMD_PARITY_SPACE:
              if (SPACEPARITY == pdcb->Parity)
                  iMatch = n;
              break;
  
          default:
              ASSERT(0);
              break;
              }
          }
  
      ASSERT(-1 != iDef);
      s_defportsettings.iSelParity = iDef;
  
      // Does the DCB value exist in our list?
      if (-1 == iMatch)
          {
          // No; choose the default
          iSel = iDef;
          }
      else 
          {
          // Yes; choose the matched value
          ASSERT(-1 != iMatch);
          iSel = iMatch;
          }
      ComboBox_SetCurSel(hwndCB, iSel);
      }
  
  
  /*----------------------------------------------------------
  Purpose: Fills the stopbits combobox with the possible settings.
  Returns: --
  Cond:    --
  */
  void PRIVATE Port_FillStopBits(
      PPORT this)
      {
      HWND hwndCB = this->hwndStopBits;
      WIN32DCB FAR * pdcb = &this->pportinfo->dcb;
      int i;
      int iSel;
      int n;
      int iMatch = -1;
      int iDef = -1;
      TCHAR sz[MAXMEDLEN];
  
      // Fill the listbox
      for (i = 0; i < ARRAYSIZE(s_rgstopbits); i++)
          {
          n = ComboBox_AddString(hwndCB, SzFromIDS(g_hinst, s_rgstopbits[i].ids, sz, SIZECHARS(sz)));
          ComboBox_SetItemData(hwndCB, n, s_rgstopbits[i].stopbits);
  
          // Keep our eyes peeled for important values
          if (DEFAULT_STOPBITS == s_rgstopbits[i].stopbits)
              {
              iDef = n;
              }
          if (pdcb->StopBits == s_rgstopbits[i].stopbits)
              {
              iMatch = n;
              }
          }
  
      ASSERT(-1 != iDef);
      s_defportsettings.iSelStopBits = iDef;
  
      // Does the DCB value exist in our list?
      if (-1 == iMatch)
          {
          // No; choose the default
          iSel = iDef;
          }
      else 
          {
          // Yes; choose the matched value
          ASSERT(-1 != iMatch);
          iSel = iMatch;
          }
      ComboBox_SetCurSel(hwndCB, iSel);
      }
  
  
  /*----------------------------------------------------------
  Purpose: Fills the flow control combobox with the possible settings.
  Returns: --
  Cond:    --
  */
  void PRIVATE Port_FillFlowCtl(
      PPORT this)
      {
      HWND hwndCB = this->hwndFlowCtl;
      WIN32DCB FAR * pdcb = &this->pportinfo->dcb;
      int i;
      int iSel;
      int n;
      int iMatch = -1;
      int iDef = -1;
      TCHAR sz[MAXMEDLEN];
  
      // Fill the listbox
      for (i = 0; i < ARRAYSIZE(s_rgflowctl); i++)
          {
          n = ComboBox_AddString(hwndCB, SzFromIDS(g_hinst, s_rgflowctl[i].ids, sz, SIZECHARS(sz)));
          ComboBox_SetItemData(hwndCB, n, s_rgflowctl[i].cmd);
  
          // Keep our eyes peeled for important values
          if (DEFAULT_FLOWCTL == s_rgflowctl[i].cmd)
              {
              iDef = n;
              }
          switch (s_rgflowctl[i].cmd)
              {
          case CMD_FLOWCTL_XONXOFF:
              if (TRUE == pdcb->fOutX && FALSE == pdcb->fOutxCtsFlow)
                  iMatch = n;
              break;
  
          case CMD_FLOWCTL_HARDWARE:
              if (FALSE == pdcb->fOutX && TRUE == pdcb->fOutxCtsFlow)
                  iMatch = n;
              break;
  
          case CMD_FLOWCTL_NONE:
              if (FALSE == pdcb->fOutX && FALSE == pdcb->fOutxCtsFlow)
                  iMatch = n;
              break;
  
          default:
              ASSERT(0);
              break;
              }
          }
  
      ASSERT(-1 != iDef);
      s_defportsettings.iSelFlowCtl = iDef;
  
      // Does the DCB value exist in our list?
      if (-1 == iMatch)
          {
          // No; choose the default
          iSel = iDef;
          }
      else 
          {
          // Yes; choose the matched value
          ASSERT(-1 != iMatch);
          iSel = iMatch;
          }
      ComboBox_SetCurSel(hwndCB, iSel);
      }
  
  
  /*----------------------------------------------------------
  Purpose: WM_INITDIALOG Handler
  Returns: FALSE when we assign the control focus
  Cond:    --
  */
  BOOL PRIVATE Port_OnInitDialog(
      PPORT this,
      HWND hwndFocus,
      LPARAM lParam)              // expected to be PROPSHEETINFO 
      {
      LPPROPSHEETPAGE lppsp = (LPPROPSHEETPAGE)lParam;
      HWND hwnd = this->hdlg;
  
      ASSERT((LPTSTR)lppsp->lParam);
  
      this->pportinfo = (LPPORTINFO)lppsp->lParam;
  
      // Save away the window handles
      this->hwndBaudRate = GetDlgItem(hwnd, IDC_PS_BAUDRATE);
      this->hwndDataBits = GetDlgItem(hwnd, IDC_PS_DATABITS);
      this->hwndParity = GetDlgItem(hwnd, IDC_PS_PARITY);
      this->hwndStopBits = GetDlgItem(hwnd, IDC_PS_STOPBITS);
      this->hwndFlowCtl = GetDlgItem(hwnd, IDC_PS_FLOWCTL);
  
      Port_FillBaud(this);
      Port_FillDataBits(this);
      Port_FillParity(this);
      Port_FillStopBits(this);
      Port_FillFlowCtl(this);
  
  #if !defined(SUPPORT_FIFO)
  
      // Hide and disable the Advanced button
      ShowWindow(GetDlgItem(hwnd, IDC_PS_ADVANCED), FALSE);
      EnableWindow(GetDlgItem(hwnd, IDC_PS_ADVANCED), FALSE);
  
  #endif
  
      return TRUE;   // allow USER to set the initial focus
      }
  
  /*----------------------------------------------------------
  Purpose: WM_COMMAND Handler
  Returns: --
  Cond:    --
  */
  void PRIVATE Port_OnCommand(
      PPORT this,
      int id,
      HWND hwndCtl,
      UINT uNotifyCode)
      {
      HWND hwnd = this->hdlg;
      
      switch (id)
          {
      case IDC_PS_PB_RESTORE:
          // Set the values to the default settings
          ComboBox_SetCurSel(this->hwndBaudRate, s_defportsettings.iSelBaud);
          ComboBox_SetCurSel(this->hwndDataBits, s_defportsettings.iSelDataBits);
          ComboBox_SetCurSel(this->hwndParity, s_defportsettings.iSelParity);
          ComboBox_SetCurSel(this->hwndStopBits, s_defportsettings.iSelStopBits);
          ComboBox_SetCurSel(this->hwndFlowCtl, s_defportsettings.iSelFlowCtl);
          break;
  
  #ifdef SUPPORT_FIFO
  
      case IDC_PS_ADVANCED:
          FeFiFoFum(this->hdlg, this->pportinfo->szFriendlyName);
          break;
  
  #endif
  
      default:
          switch (uNotifyCode) {
          case CBN_SELCHANGE:
             PropSheet_Changed(GetParent(hwnd), hwnd);
             break;
          }
          break;
          }
      }
  
  
  /*----------------------------------------------------------
  Purpose: PSN_APPLY handler
  Returns: --
  Cond:    --
  */
  void PRIVATE Port_OnApply(
      PPORT this)
      {
      int iSel;
      BYTE cmd;
      WIN32DCB FAR * pdcb = &this->pportinfo->dcb;
  
      // Determine new speed settings
      iSel = ComboBox_GetCurSel(this->hwndBaudRate);
      pdcb->BaudRate = (DWORD)ComboBox_GetItemData(this->hwndBaudRate, iSel);
  
  
      // Determine new byte size
      iSel = ComboBox_GetCurSel(this->hwndDataBits);
      pdcb->ByteSize = (BYTE)ComboBox_GetItemData(this->hwndDataBits, iSel);
  
  
      // Determine new parity settings
      iSel = ComboBox_GetCurSel(this->hwndParity);
      cmd = (BYTE)ComboBox_GetItemData(this->hwndParity, iSel);
      switch (cmd)
          {
      case CMD_PARITY_EVEN:
          pdcb->fParity = TRUE;
          pdcb->Parity = EVENPARITY;
          break;
  
      case CMD_PARITY_ODD:
          pdcb->fParity = TRUE;
          pdcb->Parity = ODDPARITY;
          break;
  
      case CMD_PARITY_NONE:
          pdcb->fParity = FALSE;
          pdcb->Parity = NOPARITY;
          break;
  
      case CMD_PARITY_MARK:
          pdcb->fParity = TRUE;
          pdcb->Parity = MARKPARITY;
          break;
  
      case CMD_PARITY_SPACE:
          pdcb->fParity = TRUE;
          pdcb->Parity = SPACEPARITY;
          break;
  
      default:
          ASSERT(0);
          break;
          }
  
      // Determine new stopbits setting
      iSel = ComboBox_GetCurSel(this->hwndStopBits);
      pdcb->StopBits = (BYTE)ComboBox_GetItemData(this->hwndStopBits, iSel);
  
  
      // Determine new flow control settings
      iSel = ComboBox_GetCurSel(this->hwndFlowCtl);
      cmd = (BYTE)ComboBox_GetItemData(this->hwndFlowCtl, iSel);
      switch (cmd)
          {
      case CMD_FLOWCTL_XONXOFF:
          pdcb->fOutX = TRUE;
          pdcb->fInX = TRUE;
          pdcb->fOutxCtsFlow = FALSE;
          pdcb->fRtsControl = RTS_CONTROL_DISABLE;
          break;
  
      case CMD_FLOWCTL_HARDWARE:
          pdcb->fOutX = FALSE;
          pdcb->fInX = FALSE;
          pdcb->fOutxCtsFlow = TRUE;
          pdcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
          break;
  
      case CMD_FLOWCTL_NONE:
          pdcb->fOutX = FALSE;
          pdcb->fInX = FALSE;
          pdcb->fOutxCtsFlow = FALSE;
          pdcb->fRtsControl = RTS_CONTROL_DISABLE;
          break;
  
      default:
          ASSERT(0);      // should never be here
          break;
          }
  
      this->pportinfo->idRet = IDOK;
      }
  
  
  /*----------------------------------------------------------
  Purpose: WM_NOTIFY handler
  Returns: varies
  Cond:    --
  */
  LRESULT PRIVATE Port_OnNotify(
      PPORT this,
      int idFrom,
      NMHDR FAR * lpnmhdr)
      {
      LRESULT lRet = 0;
      
      switch (lpnmhdr->code)
          {
      case PSN_SETACTIVE:
          break;
  
      case PSN_KILLACTIVE:
          // N.b. This message is not sent if user clicks Cancel!
          // N.b. This message is sent prior to PSN_APPLY
          //
          break;
  
      case PSN_APPLY:
          Port_OnApply(this);
          break;
  
      default:
          break;
          }
  
      return lRet;
      }
  
  
  /////////////////////////////////////////////////////  EXPORTED FUNCTIONS
  
  static BOOL s_bPortRecurse = FALSE;
  
  LRESULT INLINE Port_DefProc(
      HWND hDlg, 
      UINT msg,
      WPARAM wParam,
      LPARAM lParam) 
      {
      ENTER_X()
          {
          s_bPortRecurse = TRUE;
          }
      LEAVE_X()
  
      return DefDlgProc(hDlg, msg, wParam, lParam); 
      }
  
  // Context help header file and arrays for devmgr ports tab
  // Created 2/21/98 by WGruber NTUA and DoronH NTDEV
  
  //
  // "Port Settings" Dialog Box
  //
  
  #define IDH_NOHELP  ((DWORD)-1)
  
  #define IDH_DEVMGR_PORTSET_ADVANCED 15840   // "&Advanced" (Button)
  #define IDH_DEVMGR_PORTSET_BPS      15841   // "" (ComboBox)
  #define IDH_DEVMGR_PORTSET_DATABITS 15842   // "" (ComboBox)
  #define IDH_DEVMGR_PORTSET_PARITY   15843   // "" (ComboBox)
  #define IDH_DEVMGR_PORTSET_STOPBITS 15844   // "" (ComboBox)
  #define IDH_DEVMGR_PORTSET_FLOW     15845   // "" (ComboBox)
  #define IDH_DEVMGR_PORTSET_DEFAULTS 15892   // "&Restore Defaults" (Button)
  
  
  #pragma data_seg(DATASEG_READONLY)
  const static DWORD rgHelpIDs[] = {                              // old winhelp IDs
          IDC_STATIC,             IDH_NOHELP, 
          IDC_PS_PORT,            IDH_NOHELP,
          IDC_PS_LBL_BAUDRATE,    IDH_DEVMGR_PORTSET_BPS,         // IDH_PORT_BAUD,
          IDC_PS_BAUDRATE,        IDH_DEVMGR_PORTSET_BPS,         // IDH_PORT_BAUD,
          IDC_PS_LBL_DATABITS,    IDH_DEVMGR_PORTSET_DATABITS,    // IDH_PORT_DATA,
          IDC_PS_DATABITS,        IDH_DEVMGR_PORTSET_DATABITS,    // IDH_PORT_DATA,
          IDC_PS_LBL_PARITY,      IDH_DEVMGR_PORTSET_PARITY,      // IDH_PORT_PARITY,
          IDC_PS_PARITY,          IDH_DEVMGR_PORTSET_PARITY,      // IDH_PORT_PARITY,
          IDC_PS_LBL_STOPBITS,    IDH_DEVMGR_PORTSET_STOPBITS,    // IDH_PORT_STOPBITS,
          IDC_PS_STOPBITS,        IDH_DEVMGR_PORTSET_STOPBITS,    // IDH_PORT_STOPBITS,
          IDC_PS_LBL_FLOWCTL,     IDH_DEVMGR_PORTSET_FLOW,        // IDH_PORT_FLOW,
          IDC_PS_FLOWCTL,         IDH_DEVMGR_PORTSET_FLOW,        // IDH_PORT_FLOW,
          IDC_PS_PB_RESTORE,      IDH_DEVMGR_PORTSET_DEFAULTS,    // IDH_PORT_RESTORE,
          IDC_PS_ADVANCED,        IDH_DEVMGR_PORTSET_ADVANCED,
          0, 0 };
  #pragma data_seg()
  
  /*----------------------------------------------------------
  Purpose: Real dialog proc
  Returns: varies
  Cond:    --
  */
  LRESULT Port_DlgProc(
      PPORT this,
      UINT message,
      WPARAM wParam,
      LPARAM lParam)
      {
  
      switch (message)
          {
          HANDLE_MSG(this, WM_INITDIALOG, Port_OnInitDialog);
          HANDLE_MSG(this, WM_COMMAND, Port_OnCommand);
          HANDLE_MSG(this, WM_NOTIFY, Port_OnNotify);
  
          case WM_HELP:
              WinHelp(((LPHELPINFO)lParam)->hItemHandle, c_szWinHelpFile, HELP_WM_HELP, (ULONG_PTR)(LPVOID)rgHelpIDs);
              return 0;
      
          case WM_CONTEXTMENU:
              WinHelp((HWND)wParam, c_szWinHelpFile, HELP_CONTEXTMENU, (ULONG_PTR)(LPVOID)rgHelpIDs);
              return 0;
  
          default:
              return Port_DefProc(this->hdlg, message, wParam, lParam);
          }
      }
  
  
  /*----------------------------------------------------------
  Purpose: Dialog Wrapper
  Returns: varies
  Cond:    --
  */
  INT_PTR CALLBACK Port_WrapperProc(
      HWND hDlg,          // std params
      UINT message,
      WPARAM wParam,
      LPARAM lParam)
      {
      PPORT this;
  
      // Cool windowsx.h dialog technique.  For full explanation, see
      //  WINDOWSX.TXT.  This supports multiple-instancing of dialogs.
      //
      ENTER_X()
          {
          if (s_bPortRecurse)
              {
              s_bPortRecurse = FALSE;
              LEAVE_X()
              return FALSE;
              }
          }
      LEAVE_X()
  
      this = Port_GetPtr(hDlg);
      if (this == NULL)
          {
          if (message == WM_INITDIALOG)
              {
              this = (PPORT)LocalAlloc(LPTR, sizeof(PORT));
              if (!this)
                  {
                  MsgBox(g_hinst,
                         hDlg, 
                         MAKEINTRESOURCE(IDS_OOM_PORT), 
                         MAKEINTRESOURCE(IDS_CAP_PORT),
                         NULL,
                         MB_ERROR);
                  EndDialog(hDlg, IDCANCEL);
                  return (BOOL)Port_DefProc(hDlg, message, wParam, lParam);
                  }
              this->hdlg = hDlg;
              Port_SetPtr(hDlg, this);
              }
          else
              {
              return (BOOL)Port_DefProc(hDlg, message, wParam, lParam);
              }
          }
  
      if (message == WM_DESTROY)
          {
          Port_DlgProc(this, message, wParam, lParam);
          LocalFree((HLOCAL)OFFSETOF(this));
          Port_SetPtr(hDlg, NULL);
          return 0;
          }
  
      return SetDlgMsgResult(hDlg, message, Port_DlgProc(this, message, wParam, lParam));
      }
  
  
  #ifdef SUPPORT_FIFO
  
  //
  // Advanced Port Settings
  //
  
  #pragma data_seg(DATASEG_READONLY)
  
  // Fifo related strings
  
  TCHAR const FAR c_szSettings[] = TEXT("Settings");
  TCHAR const FAR c_szComxFifo[] = TEXT("Fifo");
  TCHAR const FAR c_szEnh[] = TEXT("386Enh");
  TCHAR const FAR c_szSystem[] = TEXT("system.ini");
  
  //
  // "Advanced Communications Port Properties" Dialog Box
  //
  #define IDH_DEVMGR_PORTSET_ADV_USEFIFO  16885   // "&Use FIFO buffers (requires 16550 compatible UART)" (Button)
  #define IDH_DEVMGR_PORTSET_ADV_TRANS    16842   // "" (msctls_trackbar32)
  //  #define IDH_DEVMGR_PORTSET_ADV_DEVICES  161027  // "" (ComboBox)
  #define IDH_DEVMGR_PORTSET_ADV_RECV     16821   // "" (msctls_trackbar32)
  // #define IDH_DEVMGR_PORTSET_ADV_NUMBER   16846    // "" (ComboBox)
  #define IDH_DEVMGR_PORTSET_ADV_DEFAULTS 16844
  
  
  const DWORD rgAdvHelpIDs[] =
  {
      IDC_STATIC              IDW_NOHELP,
  
      IDC_FIFO_USAGE,         IDH_DEVMGR_PORTSET_ADV_USEFIFO, // "Use FIFO buffers (requires 16550 compatible UART)" (Button)
  
      IDC_LBL_RXFIFO,         IDH_NOHELP,                     // "&Receive Buffer:" (Static)
      IDC_RXFIFO_USAGE,       IDH_DEVMGR_PORTSET_ADV_RECV,    // "" (msctls_trackbar32)
      IDC_LBL_RXFIFO_LO,      IDH_NOHELP,                     // "Low (%d)" (Static)
      IDC_LBL_RXFIFO_HI,      IDH_NOHELP,                     // "High (%d)" (Static)
  
      IDC_LBL_TXFIFO,         IDH_NOHELP,                     // "&Transmit Buffer:" (Static)
      IDC_TXFIFO_USAGE,       IDH_DEVMGR_PORTSET_ADV_TRANS,   // "" (msctls_trackbar32)
      IDC_LBL_TXFIFO_LO,      IDH_NOHELP,                     // "Low (%d)" (Static)
      IDC_LBL_TXFIFO_HI,      IDH_NOHELP,                     // "High (%d)" (Static)
  
      IDC_DEFAULTS,           IDH_DEVMGR_PORTSET_ADV_DEFAULTS,// "&Restore Defaults" (Button)
      0, 0
  };
  
  #pragma data_seg()
  
  
  /*----------------------------------------------------------
  Purpose: Set the dialog controls
  
  Returns: --
  Cond:    --
  */
  void DisplayAdvSettings(
      HWND hDlg,
      BYTE RxTrigger,
      BYTE TxTrigger,
      BOOL bUseFifo)
      {
      SendDlgItemMessage(hDlg, IDC_RXFIFO_USAGE, TBM_SETRANGE, 0, 0x30000);
      SendDlgItemMessage(hDlg, IDC_TXFIFO_USAGE, TBM_SETRANGE, 0, 0x30000);
  
      // Use FIFO?
      if ( !bUseFifo ) 
          {
          // No
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO), FALSE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO_LO), FALSE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO_HI), FALSE);
          EnableWindow(GetDlgItem(hDlg, IDC_RXFIFO_USAGE), FALSE);
  
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO), FALSE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO_LO), FALSE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO_HI), FALSE);
          EnableWindow(GetDlgItem(hDlg, IDC_TXFIFO_USAGE), FALSE);
          CheckDlgButton(hDlg, IDC_FIFO_USAGE, FALSE);
          } 
      else 
          {
          CheckDlgButton(hDlg, IDC_FIFO_USAGE, TRUE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO), TRUE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO_LO), TRUE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO_HI), TRUE);
          EnableWindow(GetDlgItem(hDlg, IDC_RXFIFO_USAGE), TRUE);
  
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO), TRUE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO_LO), TRUE);
          EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO_HI), TRUE);
          EnableWindow(GetDlgItem(hDlg, IDC_TXFIFO_USAGE), TRUE);
          SendDlgItemMessage(hDlg, IDC_RXFIFO_USAGE, TBM_SETPOS,
              TRUE, RxTrigger);
          SendDlgItemMessage(hDlg, IDC_TXFIFO_USAGE, TBM_SETPOS,
              TRUE, TxTrigger/4);
          }
      }
  
  
  typedef struct tagSETTINGS 
      {
      BYTE fifoon;
      BYTE txfifosize;
      BYTE dsron;
      BYTE rxtriggersize;
      } SETTINGS;
  
  typedef enum 
      {
      ACT_GET,
      ACT_SET
      } ACTION;
  
  BYTE RxTriggerValues[4]={0,0x40,0x80,0xC0};
  
  
  /*----------------------------------------------------------
  Purpose: Gets or sets the advanced settings of the port
  
  Returns: --
  Cond:    --
  */
  void GetSetAdvSettings(
      LPCTSTR pszPortName,
      BYTE FAR *RxTrigger,
      BYTE FAR *TxTrigger,
      BOOL FAR * pbUseFifo,
      ACTION action)
      {
      LPFINDDEV pfd;
      DWORD cbData;
      SETTINGS settings;
      TCHAR szFifo[256];
      TCHAR OnStr[2] = TEXT("0");
  
      ASSERT(pszPortName);
  
      // In Win95, the FIFO settings were (wrongfully) stored in the 
      // device key.  I've changed this to look in the driver key. 
      // (scotth)
  
      if (FindDev_Create(&pfd, c_pguidPort, c_szFriendlyName, pszPortName) ||
          FindDev_Create(&pfd, c_pguidPort, c_szPortName, pszPortName) ||
          FindDev_Create(&pfd, c_pguidModem, c_szPortName, pszPortName))
          {
          switch (action)
              {
          case ACT_GET:
              ASSERT(4 == sizeof(SETTINGS));
  
              cbData = sizeof(SETTINGS);
              if (ERROR_SUCCESS != RegQueryValueEx(pfd->hkeyDrv, c_szSettings, NULL,
                  NULL, (LPBYTE)&settings, &cbData)) 
                  {
                  // Default settings if not in registry
                  settings.fifoon = 0x02;
                  settings.dsron = 0;
                  settings.txfifosize = 16;
                  settings.rxtriggersize = 0x80;
                  }
              if (!settings.fifoon)
                  *pbUseFifo = FALSE;
              else
                  *pbUseFifo = TRUE;
              settings.rxtriggersize = settings.rxtriggersize % 0xC1;
              *RxTrigger = settings.rxtriggersize/0x40;
              *TxTrigger = settings.txfifosize % 17;
              break;
  
          case ACT_SET:
              if (FALSE == *pbUseFifo)
                  settings.fifoon = 0;
              else
                  settings.fifoon = 2;
  
              settings.rxtriggersize = RxTriggerValues[*RxTrigger];
              settings.dsron = 0;
              settings.txfifosize = (*TxTrigger)*5+1;
              RegSetValueEx(pfd->hkeyDrv, c_szSettings, 0, REG_BINARY,
                  (LPBYTE)&settings, sizeof(SETTINGS));
              break;
  
          default:
              ASSERT(0);
              break;
              }
  
          cbData = sizeof(szFifo) - 6;    // leave room for "fifo" on the end
          RegQueryValueEx(pfd->hkeyDrv, c_szPortName, NULL, NULL, (LPBYTE)szFifo,
              &cbData);
  
          FindDev_Destroy(pfd);
  
          lstrcat(szFifo, c_szComxFifo);
          if (*pbUseFifo)
              WritePrivateProfileString(c_szEnh, szFifo, NULL, c_szSystem);
          else
              WritePrivateProfileString(c_szEnh, szFifo, OnStr, c_szSystem);
          }
      }
  
  
  
  /*----------------------------------------------------------
  Purpose: Dialog proc for advanced port settings
  
  Returns: standard
  Cond:    --
  */
  BOOL CALLBACK AdvPort_DlgProc(
      HWND hDlg,
      UINT uMsg,
      WPARAM wParam,
      LPARAM lParam)
      {
      BOOL bRet = FALSE;
      BYTE rxtrigger, txtrigger;
      BOOL bUseFifo;
      LPCTSTR pszPortName;
  
      switch (uMsg) 
          {
      case WM_INITDIALOG:
          pszPortName = (LPCTSTR)lParam;
          SetWindowLongPtr(hDlg, DWLP_USER, (ULONG_PTR)pszPortName);
  
          GetSetAdvSettings(pszPortName, &rxtrigger, &txtrigger, &bUseFifo, ACT_GET);
          DisplayAdvSettings(hDlg, rxtrigger, txtrigger, bUseFifo);
          break;
  
      case WM_COMMAND:
          pszPortName = (LPCTSTR)GetWindowLongPtr(hDlg, DWLP_USER);
          if (!pszPortName)
              {
              ASSERT(0);
              break;
              }
  
          switch (wParam) 
              {
          case IDOK:
              if (IsDlgButtonChecked(hDlg, IDC_FIFO_USAGE))
                  bUseFifo = TRUE;
              else
                  bUseFifo = FALSE;
  
              rxtrigger = (BYTE)SendDlgItemMessage(hDlg,
                  IDC_RXFIFO_USAGE, TBM_GETPOS, 0, 0);
              txtrigger = (BYTE)SendDlgItemMessage(hDlg,
                  IDC_TXFIFO_USAGE, TBM_GETPOS, 0, 0);
  
              GetSetAdvSettings(pszPortName, &rxtrigger, &txtrigger, &bUseFifo, ACT_SET);
  
              // Fall thru
              //  |    |
              //  v    v
  
          case IDCANCEL:
              EndDialog(hDlg, IDOK == wParam);
              break;
  
          case IDC_FIFO_USAGE:
              if (!IsDlgButtonChecked(hDlg, IDC_FIFO_USAGE))
                  DisplayAdvSettings(hDlg, 0, 0, FALSE);
              else 
                  {
                  EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO_LO), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDC_LBL_RXFIFO_HI), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDC_RXFIFO_USAGE), TRUE);
  
                  EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO_LO), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDC_LBL_TXFIFO_HI), TRUE);
                  EnableWindow(GetDlgItem(hDlg, IDC_TXFIFO_USAGE), TRUE);
                  }
              break;
  
          case IDC_DEFAULTS:
              DisplayAdvSettings(hDlg, 2, 12, TRUE);
              break;
              }
          break;
  
      case WM_HELP:
          WinHelp(((LPHELPINFO)lParam)->hItemHandle, c_szWinHelpFile, HELP_WM_HELP, (DWORD)(LPVOID)rgAdvHelpIDs);
          return 0;
  
      case WM_CONTEXTMENU:
          WinHelp((HWND)wParam, c_szWinHelpFile, HELP_CONTEXTMENU, (DWORD)(LPVOID)rgAdvHelpIDs);
          return 0;
  
      default:
          break;
          }
      return bRet;
      }
  
  
  /*----------------------------------------------------------
  Purpose: Private entry point to show the Advanced Fifo dialog
  
  Returns: IDOK or IDCANCEL
  
  Cond:    --
  */
  UINT WINAPI FeFiFoFum(
      HWND hwndOwner,
      LPCTSTR pszPortName)
      {
      UINT uRet = (UINT)-1;
  
      // Invoke the advanced dialog
      if (pszPortName)
          {
          uRet = DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_ADV_PORT), 
                  hwndOwner, AdvPort_DlgProc, (LPARAM)pszPortName);
          }
      return uRet;
      }
  
  #endif // SUPPORT_FIFO
  