From 0becaf749e830b0c56c86eb5e1b9e32cc2a622dd Mon Sep 17 00:00:00 2001
From: Andri Yngvason <andri@yngvason.is>
Date: Mon, 30 Nov 2020 20:51:01 +0000
Subject: [PATCH] libvncclient/rfbproto: Add SendExtendedKeyEvent()

This function sends Qemu extended key events to servers that support
such messages. It returns FALSE if the event is not supported.
---
 libvncclient/rfbproto.c | 32 ++++++++++++++++++++++++++++++++
 rfb/rfbclient.h         | 11 +++++++++++
 2 files changed, 43 insertions(+)

diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c
index 51ec5a0e..6edf79b2 100644
--- a/libvncclient/rfbproto.c
+++ b/libvncclient/rfbproto.c
@@ -1350,6 +1350,9 @@ SetFormatAndEncodings(rfbClient* client)
   if (se->nEncodings < MAX_ENCODINGS)
     encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingXvp);
 
+  if (se->nEncodings < MAX_ENCODINGS)
+    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingQemuExtendedKeyEvent);
+
   /* client extensions */
   for(e = rfbClientExtensions; e; e = e->next)
     if(e->encodings) {
@@ -1586,6 +1589,31 @@ SendKeyEvent(rfbClient* client, uint32_t key, rfbBool down)
 }
 
 
+/*
+ * SendExtendedKeyEvent.
+ */
+
+rfbBool
+SendExtendedKeyEvent(rfbClient* client, uint32_t keysym, uint32_t keycode, rfbBool down)
+{
+  rfbQemuExtendedKeyEventMsg ke;
+
+  /* FIXME: rfbQemuEvent also covers audio events, but this model for checking
+   * for supported messages is somewhat limited, so I'll leave this as is for
+   * now.
+   */
+  if (!SupportsClient2Server(client, rfbQemuEvent)) return FALSE;
+
+  memset(&ke, 0, sizeof(ke));
+  ke.type = rfbQemuEvent;
+  ke.subtype = 0; /* key event subtype */
+  ke.down = rfbClientSwap16IfLE(!!down);
+  ke.keysym = rfbClientSwap32IfLE(keysym);
+  ke.keycode = rfbClientSwap32IfLE(keycode);
+  return WriteToRFBServer(client, (char *)&ke, sz_rfbQemuExtendedKeyEventMsg);
+}
+
+
 /*
  * SendClientCutText.
  */
@@ -2073,6 +2101,10 @@ HandleRFBServerMessage(rfbClient* client)
 
 #endif
 
+      case rfbEncodingQemuExtendedKeyEvent:
+        SetClient2Server(client, rfbQemuEvent);
+        break;
+
       default:
 	 {
 	   rfbBool handled = FALSE;
diff --git a/rfb/rfbclient.h b/rfb/rfbclient.h
index 37e6f5f7..9d33dc04 100644
--- a/rfb/rfbclient.h
+++ b/rfb/rfbclient.h
@@ -570,6 +570,17 @@ extern rfbBool SendPointerEvent(rfbClient* client,int x, int y, int buttonMask);
  * @return true if the key event was send successfully, false otherwise
  */
 extern rfbBool SendKeyEvent(rfbClient* client,uint32_t key, rfbBool down);
+/**
+ * The same as SendKeyEvent, except a key code will be sent along with the
+ * symbol if the server supports extended key events.
+ * @param client The client through which to send the key event
+ * @param keysym An rfbKeySym defined in rfb/keysym.h
+ * @param keycode An XT key code
+ * @param down true if this was a key down event, false otherwise
+ * @return true if the extended key event is supported and was sent
+ * successfully, false otherwise
+ */
+extern rfbBool SendExtendedKeyEvent(rfbClient* client, uint32_t keysym, uint32_t keycode, rfbBool down);
 /**
  * Places a string on the server's clipboard. Use this function if you want to
  * be able to copy and paste between the server and your application. For