Bug #3375 - Patch for Volume Up, Down and Mute on OSX Clients
Steps to reproduce:
Synergy Mac Clients do not currently support Media keys for a couple of reasons:
- The Synergy keymap is built up from keyboard layouts, and keyboard layouts don't contains multimedia keys.
- CGEventCreateKeyboardEvent is unable to create a multimedia event, probably for the same reason as above.
I handle the first problem by manually adding these keys to the s_controlKeys structure. To handle the second problem, I wrote a separate application to listen in on and serialize these multimedia events. I then wrote a patch to build a custom event from these serialized events in the case of volume up, down, and mute. To my knowledge, this is the only way possible to simulate these keypresses (the other solution I've seen is to watch for these keypresses, and directly interact with the sound system, which introduces all sorts of edge cases not worth dealing with)
I can't see how to attach a file, so I'm pasting the contents of the patch into the 'Additional comments' section.
I believe I'm leaking CFDataRef's, though since I'm unsure as to the required lifetime with regards to posting the event, I've left it as an exercise for whomever accepts this patch. I'm not a Mac application developer, and given the number of times I press Vol Up/Down/Mute before launching, I'm okay with the leaks on my system, as long as the volume keys work.
Versions and operating systems:
I have only tested this on OSX Mountain Lion as a client, talking to a Linux (Ubuntu) server. The Linux server is running 1.4.10, and my patch was against trunk.
Temporary workarounds:
None
Additional comments:
Index: src/lib/platform/COSXKeyState.cpp
===================================================================
--- src/lib/platform/COSXKeyState.cpp (revision 1659)
+++ src/lib/platform/COSXKeyState.cpp (working copy)
@@ -36,6 +36,20 @@
static const UInt32 s_superVK = kVK_Command;
static const UInt32 s_capsLockVK = kVK_CapsLock;
static const UInt32 s_numLockVK = kVK_ANSI_KeypadClear; // 71
+
+const UInt8 VolUp_Press[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x40, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x40, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x37, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x02, 0xC0, 0x38, 0x44, 0x34, 0x00, 0x00, 0x44, 0x92, 0x20, 0x00, 0x00, 0x02, 0xC0, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3A, 0xB0, 0xAB, 0x9F, 0x70, 0x00, 0x00, 0x17, 0x86, 0x00, 0x01, 0x40, 0x3B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x40, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6B, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0x40, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0F, 0x40, 0x54, 0x00, 0x00, 0x0B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+const UInt8 VolUp_Release[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x40, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x40, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x37, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x02, 0xC0, 0x38, 0x44, 0x34, 0x00, 0x00, 0x44, 0x92, 0x20, 0x00, 0x00, 0x02, 0xC0, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3A, 0xA9, 0xFE, 0x64, 0xF0, 0x00, 0x00, 0x17, 0x86, 0x00, 0x01, 0x40, 0x3B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x40, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6B, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0x40, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0F, 0x40, 0x54, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+const UInt8 VolDown_Press[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x40, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x40, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x37, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x02, 0xC0, 0x38, 0x43, 0xFE, 0x00, 0x00, 0x44, 0x9E, 0x00, 0x00, 0x00, 0x02, 0xC0, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3A, 0x8C, 0x11, 0x65, 0xC0, 0x00, 0x00, 0x18, 0xCE, 0x00, 0x01, 0x40, 0x3B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x40, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6B, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0x40, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0F, 0x40, 0x54, 0x00, 0x01, 0x0A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+const UInt8 VolDown_Release[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x40, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x40, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x37, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x02, 0xC0, 0x38, 0x43, 0xFE, 0x00, 0x00, 0x44, 0x9E, 0x00, 0x00, 0x00, 0x02, 0xC0, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3A, 0x91, 0xCD, 0xB8, 0x60, 0x00, 0x00, 0x18, 0xCE, 0x00, 0x01, 0x40, 0x3B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x40, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6B, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0x40, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0F, 0x40, 0x54, 0x00, 0x01, 0x0B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+const UInt8 Mute_Press[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x40, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x40, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x37, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x02, 0xC0, 0x38, 0x43, 0x99, 0x80, 0x00, 0x44, 0x5A, 0x40, 0x00, 0x00, 0x02, 0xC0, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3A, 0x25, 0x88, 0x1E, 0xD8, 0x00, 0x00, 0x18, 0xFC, 0x00, 0x01, 0x40, 0x3B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x40, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6B, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0x40, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0F, 0x40, 0x54, 0x00, 0x07, 0x0A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+const UInt8 Mute_Release[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x40, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x40, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x37, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x02, 0xC0, 0x38, 0x43, 0x99, 0x80, 0x00, 0x44, 0x5A, 0x40, 0x00, 0x00, 0x02, 0xC0, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3A, 0x2B, 0xBA, 0x93, 0xA8, 0x00, 0x00, 0x18, 0xFC, 0x00, 0x01, 0x40, 0x3B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x40, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x6B, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0x40, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0F, 0x40, 0x54, 0x00, 0x07, 0x0B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+
#else
// Hardcoded virtual key table on 10.4 and below.
static const UInt32 s_shiftVK = 56;
@@ -103,6 +117,11 @@
{ kKeyKP_Divide, kVK_ANSI_KeypadDivide },
{ kKeyKP_Subtract, kVK_ANSI_KeypadMinus },
{ kKeyKP_Enter, kVK_ANSI_KeypadEnter },
+
+ // Multimedia keys with virtual key codes
+ { kKeyAudioUp, kVK_VolumeUp },
+ { kKeyAudioDown, kVK_VolumeDown },
+ { kKeyAudioMute, kVK_Mute },
#else
// Hardcoded virtual key table on 10.4 and below.
// cursor keys.
@@ -490,9 +509,35 @@
LOG((CLOG_DEBUG1 " %%%%%%%%03x (%%%%%%%%08x) %%%%%%%%s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
// let system figure out character for us
- ref = CGEventCreateKeyboardEvent(0, mapKeyButtonToVirtualKey(
- keystroke.m_data.m_button.m_button),
- keystroke.m_data.m_button.m_press);
+ if (mapKeyButtonToVirtualKey(keystroke.m_data.m_button.m_button) == kVK_VolumeUp) {
+ if (keystroke.m_data.m_button.m_press) {
+ CFDataRef data_press = CFDataCreate(kCFAllocatorDefault, VolUp_Press, sizeof(VolUp_Press));
+ ref = CGEventCreateFromData(kCFAllocatorDefault, data_press);
+ } else {
+ CFDataRef data_release = CFDataCreate(kCFAllocatorDefault, VolUp_Release, sizeof(VolUp_Release));
+ ref = CGEventCreateFromData(kCFAllocatorDefault, data_release);
+ }
+ } else if (mapKeyButtonToVirtualKey(keystroke.m_data.m_button.m_button) == kVK_VolumeDown) {
+ if (keystroke.m_data.m_button.m_press) {
+ CFDataRef data_press = CFDataCreate(kCFAllocatorDefault, VolDown_Press, sizeof(VolDown_Press));
+ ref = CGEventCreateFromData(kCFAllocatorDefault, data_press);
+ } else {
+ CFDataRef data_release = CFDataCreate(kCFAllocatorDefault, VolDown_Release, sizeof(VolDown_Release));
+ ref = CGEventCreateFromData(kCFAllocatorDefault, data_release);
+ }
+ } else if (mapKeyButtonToVirtualKey(keystroke.m_data.m_button.m_button) == kVK_Mute) {
+ if (keystroke.m_data.m_button.m_press) {
+ CFDataRef data_press = CFDataCreate(kCFAllocatorDefault, Mute_Press, sizeof(Mute_Press));
+ ref = CGEventCreateFromData(kCFAllocatorDefault, data_press);
+ } else {
+ CFDataRef data_release = CFDataCreate(kCFAllocatorDefault, Mute_Release, sizeof(Mute_Release));
+ ref = CGEventCreateFromData(kCFAllocatorDefault, data_release);
+ }
+ } else {
+ ref = CGEventCreateKeyboardEvent(0, mapKeyButtonToVirtualKey(
+ keystroke.m_data.m_button.m_button),
+ keystroke.m_data.m_button.m_press);
+ }
if (ref == NULL) {
LOG((CLOG_CRIT "unable to create keyboard event for keystroke"));
}
#1
Do you have any timeline on incorporating this? I was thinking of fixing some more Linux/OS X issues, but have been holding off since I didn't want to maintain my own source tree (I'd have to incorporate this patch, but develop against mainline while ignoring the patch... :-( )
Write comment