public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Dandan Bi <dandan.bi@intel.com>
To: edk2-devel@lists.01.org
Cc: Laszlo Ersek <lersek@redhat.com>,
	Liming Gao <liming.gao@intel.com>,
	Eric Dong <eric.dong@intel.com>
Subject: [patch 1/5] MdeModulePkg/BMMUI: Update TerminalMenu and ConsoleMenu in callback
Date: Mon, 17 Oct 2016 16:51:54 +0800	[thread overview]
Message-ID: <1476694318-18804-1-git-send-email-dandan.bi@intel.com> (raw)

In current codes, When user does some change related to Console or Terminal,
when saving data, it will update the content in TerminalMenu and ConsoleMenu
in BootMaintRouteConfig function. This patch moves the update action to the
BootMaintCallback function with EFI_BROWSER_ACTION_CHANGED type.
The reason for this change is: in BootMaintRouteConfig function when
Var_UpdateConsoleXXXOption() return failure and user discard the previous
change, we should re_update the content in the TerminalMenu and ConsoleMenu.
So we move the update action to the changed callback.

Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Dandan Bi <dandan.bi@intel.com>
---
 .../BootMaintenanceManagerUiLib/BootMaintenance.c  | 177 ++++++++++++++-------
 .../BootMaintenanceManagerUiLib/UpdatePage.c       |  16 +-
 2 files changed, 126 insertions(+), 67 deletions(-)

diff --git a/MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenance.c b/MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenance.c
index a190596..92c44ea 100644
--- a/MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenance.c
+++ b/MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenance.c
@@ -442,10 +442,112 @@ BmmExtractDevicePathFromHiiHandle (
   return ConvertDevicePathToText(DevicePathFromHandle (DriverHandle), FALSE, FALSE);
 
 }
 
 /**
+  Update the terminal content in TerminalMenu.
+
+  @param BmmData           The BMM fake NV data.
+
+**/
+VOID
+UpdateTerminalContent (
+  IN BMM_FAKE_NV_DATA       *BmmData
+  )
+{
+  UINT16                          Index;
+  BM_TERMINAL_CONTEXT             *NewTerminalContext;
+  BM_MENU_ENTRY                   *NewMenuEntry;
+
+  for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
+    NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Index);
+    ASSERT (NewMenuEntry != NULL);
+    NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
+    NewTerminalContext->BaudRateIndex = BmmData->COMBaudRate[Index];
+    ASSERT (BmmData->COMBaudRate[Index] < (sizeof (BaudRateList) / sizeof (BaudRateList[0])));
+    NewTerminalContext->BaudRate      = BaudRateList[BmmData->COMBaudRate[Index]].Value;
+    NewTerminalContext->DataBitsIndex = BmmData->COMDataRate[Index];
+    ASSERT (BmmData->COMDataRate[Index] < (sizeof (DataBitsList) / sizeof (DataBitsList[0])));
+    NewTerminalContext->DataBits      = (UINT8) DataBitsList[BmmData->COMDataRate[Index]].Value;
+    NewTerminalContext->StopBitsIndex = BmmData->COMStopBits[Index];
+    ASSERT (BmmData->COMStopBits[Index] < (sizeof (StopBitsList) / sizeof (StopBitsList[0])));
+    NewTerminalContext->StopBits      = (UINT8) StopBitsList[BmmData->COMStopBits[Index]].Value;
+    NewTerminalContext->ParityIndex   = BmmData->COMParity[Index];
+    ASSERT (BmmData->COMParity[Index] < (sizeof (ParityList) / sizeof (ParityList[0])));
+    NewTerminalContext->Parity        = (UINT8) ParityList[BmmData->COMParity[Index]].Value;
+    NewTerminalContext->TerminalType  = BmmData->COMTerminalType[Index];
+    NewTerminalContext->FlowControl   = BmmData->COMFlowControl[Index];
+    ChangeTerminalDevicePath (
+      NewTerminalContext->DevicePath,
+      FALSE
+      );
+  }
+}
+
+/**
+  Update the console content in ConsoleMenu.
+
+  @param BmmData           The BMM fake NV data.
+
+**/
+VOID
+UpdateConsoleContent(
+  IN CHAR16                 *ConsoleName,
+  IN BMM_FAKE_NV_DATA       *BmmData
+  )
+{
+  UINT16                          Index;
+  BM_CONSOLE_CONTEXT              *NewConsoleContext;
+  BM_TERMINAL_CONTEXT             *NewTerminalContext;
+  BM_MENU_ENTRY                   *NewMenuEntry;
+
+  if (StrCmp (ConsoleName, L"ConIn") == 0) {
+    for (Index = 0; Index < ConsoleInpMenu.MenuNumber; Index++){
+      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleInpMenu, Index);
+      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;
+      ASSERT (Index < MAX_MENU_NUMBER);
+      NewConsoleContext->IsActive = BmmData->ConsoleInCheck[Index];
+    }
+    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
+      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);
+      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
+      ASSERT (Index + ConsoleInpMenu.MenuNumber < MAX_MENU_NUMBER);
+      NewTerminalContext->IsConIn = BmmData->ConsoleInCheck[Index + ConsoleInpMenu.MenuNumber];
+    }
+  }
+
+  if (StrCmp (ConsoleName, L"ConOut") == 0) {
+    for (Index = 0; Index < ConsoleOutMenu.MenuNumber; Index++){
+      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleOutMenu, Index);
+      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;
+      ASSERT (Index < MAX_MENU_NUMBER);
+      NewConsoleContext->IsActive = BmmData->ConsoleOutCheck[Index];
+    }
+    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
+      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);
+      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
+      ASSERT (Index + ConsoleOutMenu.MenuNumber < MAX_MENU_NUMBER);
+      NewTerminalContext->IsConOut = BmmData->ConsoleOutCheck[Index + ConsoleOutMenu.MenuNumber];
+    }
+  }
+  if (StrCmp (ConsoleName, L"ErrOut") == 0) {
+    for (Index = 0; Index < ConsoleErrMenu.MenuNumber; Index++){
+      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleErrMenu, Index);
+      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;
+      ASSERT (Index < MAX_MENU_NUMBER);
+      NewConsoleContext->IsActive = BmmData->ConsoleErrCheck[Index];
+    }
+    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
+      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);
+      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
+      ASSERT (Index + ConsoleErrMenu.MenuNumber < MAX_MENU_NUMBER);
+      NewTerminalContext->IsStdErr = BmmData->ConsoleErrCheck[Index + ConsoleErrMenu.MenuNumber];
+    }
+  }
+}
+
+/**
   This function allows a caller to extract the current configuration for one
   or more named elements from the target driver.
 
   @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
   @param Request         A null-terminated Unicode string in <ConfigRequest> format.
@@ -587,12 +689,10 @@ BootMaintRouteConfig (
   EFI_STATUS                      Status;
   UINTN                           BufferSize;
   EFI_HII_CONFIG_ROUTING_PROTOCOL *ConfigRouting;
   BMM_FAKE_NV_DATA                *NewBmmData;
   BMM_FAKE_NV_DATA                *OldBmmData;
-  BM_CONSOLE_CONTEXT              *NewConsoleContext;
-  BM_TERMINAL_CONTEXT             *NewTerminalContext;
   BM_MENU_ENTRY                   *NewMenuEntry;
   BM_LOAD_CONTEXT                 *NewLoadContext;
   UINT16                          Index;
   BOOLEAN                         TerminalAttChange;
   BMM_CALLBACK_DATA               *Private; 
@@ -733,31 +833,10 @@ BootMaintRouteConfig (
          CompareMem (&NewBmmData->COMTerminalType[Index], &OldBmmData->COMTerminalType[Index], sizeof (NewBmmData->COMTerminalType[Index])) == 0 &&
          CompareMem (&NewBmmData->COMFlowControl[Index], &OldBmmData->COMFlowControl[Index], sizeof (NewBmmData->COMFlowControl[Index])) == 0) {
       continue;
     }
 
-    NewMenuEntry = BOpt_GetMenuEntry (&TerminalMenu, Index);
-    ASSERT (NewMenuEntry != NULL);
-    NewTerminalContext = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
-    NewTerminalContext->BaudRateIndex = NewBmmData->COMBaudRate[Index];
-    ASSERT (NewBmmData->COMBaudRate[Index] < (sizeof (BaudRateList) / sizeof (BaudRateList[0])));
-    NewTerminalContext->BaudRate      = BaudRateList[NewBmmData->COMBaudRate[Index]].Value;
-    NewTerminalContext->DataBitsIndex = NewBmmData->COMDataRate[Index];
-    ASSERT (NewBmmData->COMDataRate[Index] < (sizeof (DataBitsList) / sizeof (DataBitsList[0])));
-    NewTerminalContext->DataBits      = (UINT8) DataBitsList[NewBmmData->COMDataRate[Index]].Value;
-    NewTerminalContext->StopBitsIndex = NewBmmData->COMStopBits[Index];
-    ASSERT (NewBmmData->COMStopBits[Index] < (sizeof (StopBitsList) / sizeof (StopBitsList[0])));
-    NewTerminalContext->StopBits      = (UINT8) StopBitsList[NewBmmData->COMStopBits[Index]].Value;
-    NewTerminalContext->ParityIndex   = NewBmmData->COMParity[Index];
-    ASSERT (NewBmmData->COMParity[Index] < (sizeof (ParityList) / sizeof (ParityList[0])));
-    NewTerminalContext->Parity        = (UINT8) ParityList[NewBmmData->COMParity[Index]].Value;
-    NewTerminalContext->TerminalType  = NewBmmData->COMTerminalType[Index];
-    NewTerminalContext->FlowControl   = NewBmmData->COMFlowControl[Index];
-    ChangeTerminalDevicePath (
-      NewTerminalContext->DevicePath,
-      FALSE
-      );
     TerminalAttChange = TRUE;
   }
   if (TerminalAttChange) {
     Var_UpdateConsoleInpOption ();
     Var_UpdateConsoleOutOption ();
@@ -765,54 +844,18 @@ BootMaintRouteConfig (
   }
   //
   // Check data which located in Console Options Menu and save the settings if need
   //
   if (CompareMem (NewBmmData->ConsoleInCheck, OldBmmData->ConsoleInCheck, sizeof (NewBmmData->ConsoleInCheck)) != 0){
-    for (Index = 0; Index < ConsoleInpMenu.MenuNumber; Index++){
-      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleInpMenu, Index);
-      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;
-      ASSERT (Index < MAX_MENU_NUMBER);
-      NewConsoleContext->IsActive = NewBmmData->ConsoleInCheck[Index];
-    }
-    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
-      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);
-      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
-      ASSERT (Index + ConsoleInpMenu.MenuNumber < MAX_MENU_NUMBER);
-      NewTerminalContext->IsConIn = NewBmmData->ConsoleInCheck[Index + ConsoleInpMenu.MenuNumber];
-    }
     Var_UpdateConsoleInpOption();
   }
 
   if (CompareMem (NewBmmData->ConsoleOutCheck, OldBmmData->ConsoleOutCheck, sizeof (NewBmmData->ConsoleOutCheck)) != 0){
-    for (Index = 0; Index < ConsoleOutMenu.MenuNumber; Index++){
-      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleOutMenu, Index);
-      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;
-      ASSERT (Index < MAX_MENU_NUMBER);
-      NewConsoleContext->IsActive = NewBmmData->ConsoleOutCheck[Index];
-    }
-    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
-      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);
-      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
-      ASSERT (Index + ConsoleOutMenu.MenuNumber < MAX_MENU_NUMBER);
-      NewTerminalContext->IsConOut = NewBmmData->ConsoleOutCheck[Index + ConsoleOutMenu.MenuNumber];
-    }
     Var_UpdateConsoleOutOption();
   }
 
   if (CompareMem (NewBmmData->ConsoleErrCheck, OldBmmData->ConsoleErrCheck, sizeof (NewBmmData->ConsoleErrCheck)) != 0){
-    for (Index = 0; Index < ConsoleErrMenu.MenuNumber; Index++){
-      NewMenuEntry                = BOpt_GetMenuEntry(&ConsoleErrMenu, Index);
-      NewConsoleContext           = (BM_CONSOLE_CONTEXT *)NewMenuEntry->VariableContext;
-      ASSERT (Index < MAX_MENU_NUMBER);
-      NewConsoleContext->IsActive = NewBmmData->ConsoleErrCheck[Index];
-    }
-    for (Index = 0; Index < TerminalMenu.MenuNumber; Index++) {
-      NewMenuEntry                = BOpt_GetMenuEntry (&TerminalMenu, Index);
-      NewTerminalContext          = (BM_TERMINAL_CONTEXT *) NewMenuEntry->VariableContext;
-      ASSERT (Index + ConsoleErrMenu.MenuNumber < MAX_MENU_NUMBER);
-      NewTerminalContext->IsStdErr = NewBmmData->ConsoleErrCheck[Index + ConsoleErrMenu.MenuNumber];
-    }
     Var_UpdateErrorOutOption();
   }
 
   if (CompareMem (NewBmmData->BootDescriptionData, OldBmmData->BootDescriptionData, sizeof (NewBmmData->BootDescriptionData)) != 0 ||
        CompareMem (NewBmmData->BootOptionalData, OldBmmData->BootOptionalData, sizeof (NewBmmData->BootOptionalData)) != 0) {
@@ -1074,10 +1117,26 @@ BootMaintCallback (
 
       default:
         break;
       }
     }
+    //
+    // Update the content in Terminal menu and Console menu here.
+    //
+    if (QuestionId == COM_BAUD_RATE_QUESTION_ID + Private->CurrentTerminal || QuestionId == COM_DATA_RATE_QUESTION_ID + Private->CurrentTerminal ||
+      QuestionId == COM_PARITY_QUESTION_ID + Private->CurrentTerminal || QuestionId == COM_STOP_BITS_QUESTION_ID + Private->CurrentTerminal ||
+      QuestionId == COM_TERMINAL_QUESTION_ID + Private->CurrentTerminal || QuestionId == COM_FLOWCONTROL_QUESTION_ID + Private->CurrentTerminal
+    ) {
+      UpdateTerminalContent(CurrentFakeNVMap);
+    }
+    if ((QuestionId >= CON_IN_DEVICE_QUESTION_ID) && (QuestionId < CON_IN_DEVICE_QUESTION_ID + MAX_MENU_NUMBER)) {
+      UpdateConsoleContent (L"ConIn",CurrentFakeNVMap);
+    } else if ((QuestionId >= CON_OUT_DEVICE_QUESTION_ID) && (QuestionId < CON_OUT_DEVICE_QUESTION_ID + MAX_MENU_NUMBER)) {
+      UpdateConsoleContent (L"ConOut", CurrentFakeNVMap);
+    } else if ((QuestionId >= CON_ERR_DEVICE_QUESTION_ID) && (QuestionId < CON_ERR_DEVICE_QUESTION_ID + MAX_MENU_NUMBER)) {
+      UpdateConsoleContent (L"ConErr", CurrentFakeNVMap);
+    }
   }
 
   //
   // Pass changed uncommitted data back to Form Browser
   //
diff --git a/MdeModulePkg/Library/BootMaintenanceManagerUiLib/UpdatePage.c b/MdeModulePkg/Library/BootMaintenanceManagerUiLib/UpdatePage.c
index 9e79826..ee8ff5d 100644
--- a/MdeModulePkg/Library/BootMaintenanceManagerUiLib/UpdatePage.c
+++ b/MdeModulePkg/Library/BootMaintenanceManagerUiLib/UpdatePage.c
@@ -517,11 +517,11 @@ UpdateConsolePage (
       (EFI_QUESTION_ID) (QuestionIdBase + Index),
       VARSTORE_ID_BOOT_MAINT,
       (UINT16) (VariableOffsetBase + Index),
       NewMenuEntry->DisplayStringToken,
       NewMenuEntry->HelpStringToken,
-      0,
+      EFI_IFR_FLAG_CALLBACK,
       CheckFlags,
       NULL
       );
   }
 
@@ -546,11 +546,11 @@ UpdateConsolePage (
       (EFI_QUESTION_ID) (QuestionIdBase + Index),
       VARSTORE_ID_BOOT_MAINT,
       (UINT16) (VariableOffsetBase + Index),
       NewMenuEntry->DisplayStringToken,
       NewMenuEntry->HelpStringToken,
-      0,
+      EFI_IFR_FLAG_CALLBACK,
       CheckFlags,
       NULL
       );
 
     Index++;
@@ -829,11 +829,11 @@ UpdateTerminalPage (
     (EFI_QUESTION_ID) (COM_BAUD_RATE_QUESTION_ID + CurrentTerminal),
     VARSTORE_ID_BOOT_MAINT,
     (UINT16) (COM_BAUD_RATE_VAR_OFFSET + CurrentTerminal),
     STRING_TOKEN (STR_COM_BAUD_RATE),
     STRING_TOKEN (STR_COM_BAUD_RATE),
-    0,
+    EFI_IFR_FLAG_CALLBACK,
     EFI_IFR_NUMERIC_SIZE_1,
     OptionsOpCodeHandle,
     NULL
     );
   
@@ -862,11 +862,11 @@ UpdateTerminalPage (
     (EFI_QUESTION_ID) (COM_DATA_RATE_QUESTION_ID + CurrentTerminal),
     VARSTORE_ID_BOOT_MAINT,
     (UINT16) (COM_DATA_RATE_VAR_OFFSET + CurrentTerminal),
     STRING_TOKEN (STR_COM_DATA_BITS),
     STRING_TOKEN (STR_COM_DATA_BITS),
-    0,
+    EFI_IFR_FLAG_CALLBACK,
     EFI_IFR_NUMERIC_SIZE_1,
     OptionsOpCodeHandle,
     NULL
     );
 
@@ -894,11 +894,11 @@ UpdateTerminalPage (
     (EFI_QUESTION_ID) (COM_PARITY_QUESTION_ID + CurrentTerminal),
     VARSTORE_ID_BOOT_MAINT,
     (UINT16) (COM_PARITY_VAR_OFFSET + CurrentTerminal),
     STRING_TOKEN (STR_COM_PARITY),
     STRING_TOKEN (STR_COM_PARITY),
-    0,
+    EFI_IFR_FLAG_CALLBACK,
     EFI_IFR_NUMERIC_SIZE_1,
     OptionsOpCodeHandle,
     NULL
     );
 
@@ -926,11 +926,11 @@ UpdateTerminalPage (
     (EFI_QUESTION_ID) (COM_STOP_BITS_QUESTION_ID + CurrentTerminal),
     VARSTORE_ID_BOOT_MAINT,
     (UINT16) (COM_STOP_BITS_VAR_OFFSET + CurrentTerminal),
     STRING_TOKEN (STR_COM_STOP_BITS),
     STRING_TOKEN (STR_COM_STOP_BITS),
-    0,
+    EFI_IFR_FLAG_CALLBACK,
     EFI_IFR_NUMERIC_SIZE_1,
     OptionsOpCodeHandle,
     NULL
     );
 
@@ -958,11 +958,11 @@ UpdateTerminalPage (
     (EFI_QUESTION_ID) (COM_TERMINAL_QUESTION_ID + CurrentTerminal),
     VARSTORE_ID_BOOT_MAINT,
     (UINT16) (COM_TERMINAL_VAR_OFFSET + CurrentTerminal),
     STRING_TOKEN (STR_COM_TERMI_TYPE),
     STRING_TOKEN (STR_COM_TERMI_TYPE),
-    0,
+    EFI_IFR_FLAG_CALLBACK,
     EFI_IFR_NUMERIC_SIZE_1,
     OptionsOpCodeHandle,
     NULL
     );
 
@@ -989,11 +989,11 @@ UpdateTerminalPage (
     (EFI_QUESTION_ID) (COM_FLOWCONTROL_QUESTION_ID + CurrentTerminal),
     VARSTORE_ID_BOOT_MAINT,
     (UINT16) (COM_FLOWCONTROL_VAR_OFFSET + CurrentTerminal),
     STRING_TOKEN (STR_COM_FLOW_CONTROL),
     STRING_TOKEN (STR_COM_FLOW_CONTROL),
-    0,
+    EFI_IFR_FLAG_CALLBACK,
     EFI_IFR_NUMERIC_SIZE_1,
     OptionsOpCodeHandle,
     NULL
     );
 
-- 
1.9.5.msysgit.1



             reply	other threads:[~2016-10-17  8:52 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-10-17  8:51 Dandan Bi [this message]
2016-10-17  8:51 ` [patch 2/5] MdeModulePkg/BMMUI: Remove the incorrect and useless codes Dandan Bi
2016-10-17  8:51 ` [patch 3/5] MdeModulePkg/BMMUI: Make the BmmFakeNvData and BmmOldFakeNVData consistent Dandan Bi
2016-10-17  8:51 ` [patch 4/5] MdeModulePkg/BMMUI: Show "Change Boot/Driver order" page correctly Dandan Bi
2016-10-17  8:51 ` [patch 5/5] MdeModulePkg/BMMUI: Add error handling codes Dandan Bi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1476694318-18804-1-git-send-email-dandan.bi@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox