Index: xpra/platform/darwin/gui.py
===================================================================
--- xpra/platform/darwin/gui.py	(revision 15112)
+++ xpra/platform/darwin/gui.py	(working copy)
@@ -17,12 +17,16 @@
 from Quartz import CGWindowListCopyWindowInfo, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowListOptionAll #@UnresolvedImport
 from AppKit import NSAppleEventManager, NSScreen, NSObject, NSBeep   #@UnresolvedImport
 from AppKit import NSApplication, NSWorkspace, NSWorkspaceActiveSpaceDidChangeNotification, NSWorkspaceWillSleepNotification, NSWorkspaceDidWakeNotification     #@UnresolvedImport
+from Foundation import NSUserNotification, NSUserNotificationCenter, NSUserNotificationDefaultSoundName #@UnresolvedImport
 
 from xpra.util import envbool
+from xpra.client.notifications.notifier_base import NotifierBase
 from xpra.log import Logger
+
 log = Logger("osx", "events")
 workspacelog = Logger("osx", "events", "workspace")
 scrolllog = Logger("osx", "events", "scroll")
+notifylog = Logger("osx", "notify")
 
 
 SLEEP_HANDLER = envbool("XPRA_OSX_SLEEP_HANDLER", True)
@@ -91,6 +95,65 @@
     osxapp.ready()
 
 
+notification_center = None
+class OSX_Notifier(NotifierBase):
+    def show_notify(self, dbus_id, tray, nid, app_name, replaces_nid, app_icon, summary, body, expire_timeout):
+        global notification_center
+        notification = NSUserNotification.alloc().init()
+        notification.setTitle_(summary)
+        notification.setInformativeText_(body)
+        notification.setIdentifier_("%s" % nid)
+        #enable sound:
+        notification.setSoundName_(NSUserNotificationDefaultSoundName)
+        notifylog("show_notify(..) %s(%s)", notification_center.deliverNotification_, notification)
+        notification_center.deliverNotification_(notification)
+
+    def close_notify(self, nid):
+        pass
+
+class OSX_Subprocess_Notifier(NotifierBase):
+    def show_notify(self, dbus_id, tray, nid, app_name, replaces_nid, app_icon, summary, body, expire_timeout):
+        from xpra.platform.darwin import osx_notifier
+        osx_notifier_file = osx_notifier.__file__
+        if osx_notifier_file.endswith("pyc"):
+            os.unlink(osx_notifier_file)
+            osx_notifier_file = osx_notifier_file[:-1]
+        import time
+        cmd = ["python", osx_notifier_file, "%s-%s" % (int(time.time()), nid), summary, body]
+        from xpra.child_reaper import getChildReaper
+        import subprocess
+        env = os.environ.copy()
+        for x in ("DYLD_LIBRARY_PATH", "XDG_CONFIG_DIRS", "XDG_DATA_DIRS",
+                  "GTK_DATA_PREFIX", "GTK_EXE_PREFIX", "GTK_PATH",
+                  "GTK2_RC_FILES", "GTK_IM_MODULE_FILE", "GDK_PIXBUF_MODULE_FILE",
+                  "PANGO_RC_FILE", "PANGO_LIBDIR", "PANGO_SYSCONFDIR",
+                  "CHARSETALIASDIR",
+                  "GST_BUNDLE_CONTENTS", "PYTHON", "PYTHONHOME",
+                  "PYTHONPATH"):
+            if x in env:
+                del env[x]
+        notifylog("running %s with env=%s", cmd, env)
+        proc = subprocess.Popen(cmd, env=env)
+        proc.wait()
+        notifylog("returned %i", proc.poll())
+        getChildReaper().add_process(proc, "notifier-%s" % nid, cmd, True, True)
+
+    def close_notify(self, nid):
+        pass
+    
+
+def get_native_notifier_classes():
+    global notification_center
+    if notification_center is None:
+        notification_center = NSUserNotificationCenter.defaultUserNotificationCenter() or False
+        notifylog("notification_center=%s", notification_center)
+    v = []
+    if notification_center:
+        v = [OSX_Notifier]
+    v.append(OSX_Subprocess_Notifier)
+    notifylog("get_native_notifier_classes()=%s", v)
+    return v
+
 def get_native_tray_menu_helper_classes():
     from xpra.platform.darwin.osx_menu import getOSXMenuHelper
     return [getOSXMenuHelper]
Index: xpra/platform/darwin/osx_notifier.py
===================================================================
--- xpra/platform/darwin/osx_notifier.py	(nonexistent)
+++ xpra/platform/darwin/osx_notifier.py	(working copy)
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# This file is part of Xpra.
+# Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk>
+# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
+# later version. See the file COPYING for details.
+
+import sys
+
+from Foundation import NSUserNotification, NSUserNotificationCenter, NSUserNotificationDefaultSoundName #@UnresolvedImport
+
+
+notification_center = None
+
+
+def show_notify(nid, summary, body):
+    notification = NSUserNotification.alloc().init()
+    notification.setTitle_(summary)
+    notification.setInformativeText_(body)
+    notification.setIdentifier_("%s" % nid)
+    #enable sound:
+    notification.setSoundName_(NSUserNotificationDefaultSoundName)
+    notification_center.deliverNotification_(notification)
+
+
+def main():
+    if len(sys.argv)<4:
+        print("Error: not enough arguments")
+        return 1
+    global notification_center
+    notification_center = NSUserNotificationCenter.defaultUserNotificationCenter()
+    if not notification_center:
+        print("Error: cannot access notification center")
+        return 2
+    nid = sys.argv[1]
+    summary = sys.argv[2]
+    body = sys.argv[3]
+    show_notify(nid, summary, body)
+    return 0
+
+
+if __name__ == "__main__":
+    v = main()
+    sys.exit(v)
