Synergy

Issue Tracker (powered by SPIT)

Bug #3375 - Patch for Volume Up, Down and Mute on OSX Clients

Status:
Accepted
Priority:
Normal
Assignee:
None
Category:
None
Target:
None
Found:
None
Created by:
Created on:
19 Sep 2012 02:29
Updated by:
Updated on:
17 Nov 2012 16:50
Platform:
None
Google ID:
None
Redmine ID:
None

Steps to reproduce:

Synergy Mac Clients do not currently support Media keys for a couple of reasons:

  1. The Synergy keymap is built up from keyboard layouts, and keyboard layouts don't contains multimedia keys.
  2. 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"));
        }

19 Sep 2012 02:32: Douglas Mayle changed Details.

Added 9986 character(s).


17 Nov 2012 16:50: Nick Bolton changed Details.

Removed 14 character(s).


17 Nov 2012 16:50: Nick Bolton changed Tracker.

Support Bug


17 Nov 2012 16:50: Nick Bolton changed Status.

New Accepted


#1

1 Feb 2013 18:32: Douglas Mayle wrote a comment.

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... :-( )


#2

17 Oct 2013 22:35: Douglas Mayle wrote a comment.

I've noticed that you've released a new version. Any chance this will get into the next one?


#3

14 Jan 2014 02:46: James Vautin wrote a comment.

+1 .. this would be VERY useful! and it's already done and working!


#4

10 Mar 2014 01:40: Nathan Rijksen wrote a comment.

Please devs, spare a few minutes of your time to integrate this patch. It adds significant functionality to your product.


#5

12 Mar 2014 08:46: Chaim Leib Halbert wrote a comment.

+1 Pretty please!