diff --git a/lisp/loadup.el b/lisp/loadup.el
index 568b9fe40d..f9be0f3645 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -335,6 +335,7 @@
       (when (featurep 'charprop)
         (load "international/mule-util")
         (load "international/ucs-normalize")
+        (load "ns-fns")
         (load "term/ns-win"))))
 (if (fboundp 'x-create-frame)
     ;; Do it after loading term/foo-win.el since the value of the
diff --git a/lisp/ns-fns.el b/lisp/ns-fns.el
new file mode 100644
index 0000000000..54aefc8cb0
--- /dev/null
+++ b/lisp/ns-fns.el
@@ -0,0 +1,40 @@
+;;; ns-fns.el --- Lisp routines for NeXT/Open/GNUstep/macOS window system  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 1993-1994, 2005-2020 Free Software Foundation, Inc.
+
+;; Keywords: internal
+;; Package: emacs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;
+;;; Commentary:
+
+
+;;; Code:
+(defgroup ns nil
+  "GNUstep/macOS specific features."
+  :group 'environment)
+
+(defun mac-handle-global-hotkey (event)
+  "Handles global hotkey presses, running EVENT.
+Not intended to be called directly"
+  (interactive "e")
+
+  (apply (cdr event)))
+
+(provide 'ns-fns)
+;;; ns-fns.el ends here
diff --git a/src/keyboard.c b/src/keyboard.c
index 2e0143379a..59a17d4e48 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -6010,6 +6010,11 @@ make_lispy_event (struct input_event *event)
 	return list2 (res, list2 (event->frame_or_window, location));
       }
 
+#ifdef NS_IMPL_COCOA
+      case GLOBAL_HOTKEY_EVENT:
+          return list2 (Qmac_global_hotkey, event->arg);
+#endif
+
     case USER_SIGNAL_EVENT:
       /* A user signal.  */
       {
@@ -11759,6 +11764,10 @@ syms_of_keyboard (void)
   DEFSYM (Qcommand_execute, "command-execute");
   DEFSYM (Qinternal_echo_keystrokes_prefix, "internal-echo-keystrokes-prefix");
 
+#ifdef NS_IMPL_COCOA
+  DEFSYM (Qmac_global_hotkey, "mac-global-hotkey");
+#endif
+
   accent_key_syms = Qnil;
   staticpro (&accent_key_syms);
 
@@ -12459,6 +12468,11 @@ keys_of_keyboard (void)
 
   initial_define_lispy_key (Vspecial_event_map, "delete-frame",
 			    "handle-delete-frame");
+
+#ifdef NS_IMPL_COCOA
+  initial_define_lispy_key (Vspecial_event_map, "mac-global-hotkey",
+                            "mac-handle-global-hotkey");
+#endif
 #ifdef HAVE_NTGUI
   initial_define_lispy_key (Vspecial_event_map, "end-session",
 			    "kill-emacs");
diff --git a/src/nsfns.m b/src/nsfns.m
index c7956497c4..7aeb84e880 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -67,6 +67,169 @@ Updated by Christian Limpach (chris@nice.ch)
 
    ========================================================================== */
 
+#ifdef NS_IMPL_COCOA
+const char *const lispy_to_mac_function_keys[] =
+  {
+    "up", "down", "left", "right",          /* NSUpArrowfunctionKey    0xF700 */
+    "f1", "f2", "f3", "f4", "f5",           /* NSF1FunctionKey         0xF704 */
+    "f6", "f7", "f8", "f9", "f10",
+    "f11", "f12", "f13", "f14", "f15",
+    "f16", "f17", "f18", "f19", "f20",
+    "f21", "f22", "f23", "f24", "f25",
+    "f26", "f27", "f28", "f29", "f30",
+    "f31", "f32", "f33", "f34", "f35",      /* NSF35FunctionKey        0xF726 */
+    "insert", "delete", "home",             /* NSInsertfunctionKey     0xF727 */
+    "begin", "end", "prior", "next",
+    "print", "scroll", "pause",
+    0,                                      /* NSSysReqFunctionKey     0xF731 */
+    "break", "reset",
+    0,
+    "menu",
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    "select", "execute", "undo", "redo",
+    "find", "help", "mode-change"           /* NSModeSwitchFunctionKey 0xF747 */
+  };
+
+/* Lookup virtual keycode from string representing the name of a
+   non-ascii keystroke into the corresponding virtual key, using
+   lispy_function_keys.  */
+/* Adapted from w32fns.c */
+static unsigned
+lookup_vk_code (char *key)
+{
+  unsigned i;
+
+  for (i = 0; i < 71; i++)
+    if (lispy_to_mac_function_keys[i]
+	&& strcmp (lispy_to_mac_function_keys[i], key) == 0)
+      return (i | 0xF700);
+
+  /* Alphanumerics map to themselves.  */
+  if (key[1] == 0)
+    {
+      if ((key[0] >= 'A' && key[0] <= 'Z')
+          || (key[0] >= '0' && key[0] <= '9'))
+        return key[0];
+      if (key[0] >= 'a' && key[0] <= 'z')
+        return toupper(key[0]);
+    }
+
+  // tab, enter, and backspace are special cases
+  if (strcmp (key, "tab") == 0)
+    return 9;
+  if (strcmp (key, "enter") == 0)
+    return 13;
+   if (strcmp (key, "escape") == 0)
+    return 27;
+  if (strcmp (key, "backspace") == 0)
+    return 127;
+
+  return -1;
+}
+
+static Lisp_Object
+mac_parse_and_add_hotkey (Lisp_Object key, Lisp_Object lispfn, int hook)
+{
+  /* Copied from Fdefine_key and store_in_keymap.  */
+  register Lisp_Object c;
+  unsigned vk_code = 0;
+  int lisp_modifiers = 0;
+  NSEventModifierFlags mac_modifiers = 0;
+  Lisp_Object res = Qnil;
+  char* vkname;
+
+  CHECK_VECTOR (key);
+
+  if (ASIZE (key) != 1)
+    return Qnil;
+
+  c = AREF (key, 0);
+
+  if (CONSP (c) && lucid_event_type_list_p (c))
+    c = Fevent_convert_list (c);
+
+  if (! FIXNUMP (c) && ! SYMBOLP (c))
+    error ("Key definition is invalid");
+
+  if (! FUNCTIONP(lispfn))
+    error ("HOOK argument is not a function");
+
+  /* Work out the base key and the modifiers.  */
+  if (SYMBOLP (c))
+    {
+
+      c = parse_modifiers (c);
+      lisp_modifiers = XFIXNUM (Fcar (Fcdr (c)));
+      c = Fcar (c);
+      if (!SYMBOLP (c))
+	emacs_abort ();
+
+      vkname = SSDATA (SYMBOL_NAME (c));
+      if (vkname[0] == 0)
+        error("Key definition is invalid");
+      else
+        vk_code = lookup_vk_code (vkname);
+    }
+  else if (FIXNUMP (c))
+    {
+      lisp_modifiers = XFIXNUM (c) & ~CHARACTERBITS;
+      /* Many ascii characters are their own virtual key code.  */
+      vk_code = XFIXNUM (c) & CHARACTERBITS;
+    }
+
+  if (vk_code < 0 || vk_code > 0xF747)
+    return Qnil;
+  else if ((vk_code >= 0xF700) && (vk_code <= 0xF747))
+    mac_modifiers = mac_modifiers | NSEventModifierFlagFunction;
+
+  /* Bind key combinations based on modifier mappings.  */
+  if (((lisp_modifiers & hyper_modifier)
+       && EQ (ns_command_modifier, Qhyper))
+      || ((lisp_modifiers & super_modifier)
+          && EQ (ns_command_modifier, Qsuper))
+      || ((lisp_modifiers & meta_modifier)
+          && EQ (ns_command_modifier, Qmeta))
+      || ((lisp_modifiers & ctrl_modifier)
+          && EQ (ns_command_modifier, Qcontrol))
+      )
+    {
+      mac_modifiers = mac_modifiers | NSEventModifierFlagCommand;
+    }
+
+  if (((lisp_modifiers & hyper_modifier)
+       && EQ (ns_alternate_modifier, Qhyper))
+      || ((lisp_modifiers & super_modifier)
+          && EQ (ns_alternate_modifier, Qsuper))
+      || ((lisp_modifiers & meta_modifier)
+          && EQ (ns_alternate_modifier, Qmeta))
+      || ((lisp_modifiers & ctrl_modifier)
+          && EQ (ns_alternate_modifier, Qcontrol))
+      )
+    {
+      mac_modifiers = mac_modifiers | NSEventModifierFlagOption;
+    }
+
+  if (((lisp_modifiers & hyper_modifier)
+       && EQ (ns_control_modifier, Qhyper))
+      || ((lisp_modifiers & super_modifier)
+          && EQ (ns_control_modifier, Qsuper))
+      || ((lisp_modifiers & meta_modifier)
+          && EQ (ns_control_modifier, Qmeta))
+      || ((lisp_modifiers & ctrl_modifier)
+          && EQ (ns_control_modifier, Qcontrol))
+      )
+    {
+      mac_modifiers = mac_modifiers | NSEventModifierFlagControl;
+    }
+
+  // Bind function key
+  if ((mac_modifiers & NSEventModifierFlagDeviceIndependentFlagsMask) > 0)
+    mac_bind_key(mac_modifiers, vk_code, lispfn);
+
+  return key;
+}
+#endif
+
 /* Let the user specify a Nextstep display with a Lisp object.
    OBJECT may be nil, a frame or a terminal object.
    nil stands for the selected frame--or, if that is not a Nextstep frame,
@@ -2984,6 +3147,56 @@ The position is returned as a cons cell (X . Y) of the
   return Qnil;
 }
 
+#ifdef NS_IMPL_COCOA
+static Lisp_Object mac_registered_hotkeys;
+
+DEFUN ("mac-bind-global-hotkey",
+       Fmac_bind_global_hotkey,
+       Smac_bind_global_hotkey, 2, 2, 0,
+       doc: /* Bind KEY combination as a global hotkey, and run HOOK upon
+invokation. This function assigns a hotkey that will run an elisp function
+from anywhere in MacOS outside Emacs.  The return value is t if registering
+the hotkey was successful, otherwise nil.  */)
+ (Lisp_Object key, Lisp_Object hook)
+{
+  id handler;
+
+  key = mac_parse_and_add_hotkey (key, hook, 1);
+
+  if (!NILP (key) && NILP (Fmemq (Fcons(key,hook), mac_registered_hotkeys)))
+    {
+      Lisp_Object item = Fmemq (Qnil, mac_registered_hotkeys);
+
+      if (NILP (item))
+        mac_registered_hotkeys = Fcons (Fcons(key,hook), mac_registered_hotkeys);
+      else
+        XSETCAR (item, Fcons(key,hook));
+    }
+
+  return key;
+}
+
+DEFUN ("mac-show-bound-hotkeys", Fmac_show_bound_hotkeys,
+       Smac_show_bound_hotkeys, 0, 0, 0,
+       doc: /* Return list of registered hot-key IDs.  */)
+  (void)
+{
+  return mac_registered_hotkeys;
+}
+
+DEFUN ("mac-clear-hotkeys", Fmac_clear_hotkeys,
+       Smac_clear_hotkeys, 0, 0, 0,
+       doc: /* Unbind all global hotkeys */)
+  (void)
+{
+  mac_registered_hotkeys = Qnil;
+
+  mac_clear_hotkeys();
+
+  return Qt;
+}
+#endif
+
 /* ==========================================================================
 
     Class implementations
@@ -3136,6 +3349,11 @@ - (Lisp_Object)lispString
   defsubr (&Sns_set_mouse_absolute_pixel_position);
   defsubr (&Sns_mouse_absolute_pixel_position);
   defsubr (&Sns_show_character_palette);
+#ifdef NS_IMPL_COCOA
+  defsubr (&Smac_bind_global_hotkey);
+  defsubr (&Smac_show_bound_hotkeys);
+  defsubr (&Smac_clear_hotkeys);
+#endif
   defsubr (&Sx_display_mm_width);
   defsubr (&Sx_display_mm_height);
   defsubr (&Sx_display_screens);
@@ -3160,6 +3378,10 @@ - (Lisp_Object)lispString
   defsubr (&Sx_show_tip);
   defsubr (&Sx_hide_tip);
 
+#ifdef NS_IMPL_COCOA
+  staticpro (&mac_registered_hotkeys);
+  mac_registered_hotkeys = Qnil;
+#endif
   as_status = 0;
   as_script = Qnil;
   staticpro (&as_script);
diff --git a/src/nsterm.h b/src/nsterm.h
index f292993d8f..dfade9004b 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -379,6 +379,7 @@ #define NS_DRAW_TO_BUFFER 1
 #ifdef NS_IMPL_COCOA
   BOOL shouldKeepRunning;
   BOOL isFirst;
+  NSStatusItem *theItem;
 #endif
 #ifdef NS_IMPL_GNUSTEP
   BOOL applicationDidFinishLaunchingCalled;
@@ -1230,6 +1231,11 @@ #define NSAPP_DATA2_RUNFILEDIALOG 11
 extern void ns_init_events (struct input_event *);
 extern void ns_finish_events (void);
 
+#ifdef NS_IMPL_COCOA
+typedef enum NSEventModifierFlags NSEventModifierFlags;
+extern void mac_bind_key (enum NSEventModifierFlags modifier, unsigned vkey, Lisp_Object lispfn);
+extern void mac_clear_hotkeys (void);
+#endif
 
 #ifdef NS_IMPL_GNUSTEP
 extern char gnustep_base_version[];  /* version tracking */
diff --git a/src/nsterm.m b/src/nsterm.m
index fa38350a2f..c556eb3a05 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -9721,6 +9721,58 @@ Convert an X font name (XLFD) to an NS font name.
   return ret;
 }
 
+#ifdef NS_IMPL_COCOA
+// Store event handler event ids.
+NSMutableArray *hotkey_ids;
+
+void
+mac_bind_key (NSEventModifierFlags modifier, unsigned vkey,
+                       Lisp_Object lispfn)
+{
+  id handler;
+
+  // Check if Emacs is trusted.
+  NSDictionary *options = @{(__bridge id) kAXTrustedCheckOptionPrompt: @YES};
+  Boolean trusted = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
+
+  if (hotkey_ids == nil)
+    hotkey_ids = [[NSMutableArray alloc] initWithCapacity: 1];
+
+  if (trusted) {
+    handler = [NSEvent
+                addGlobalMonitorForEventsMatchingMask: (NSEventMaskKeyDown)
+                                              handler:^(NSEvent *event) {
+        if ((event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask) == modifier) {
+          if([[event.charactersIgnoringModifiers capitalizedString]
+               characterAtIndex:0] == vkey) {
+            struct frame *emacsframe = SELECTED_FRAME ();
+            NSEvent *theEvent = [NSApp currentEvent];
+
+            emacs_event->kind = GLOBAL_HOTKEY_EVENT;
+            emacs_event->arg = lispfn;
+            EV_TRAILER(theEvent);
+          }
+        }
+      }];
+
+    [hotkey_ids addObject: handler];
+  }
+  else {
+    error("Emacs app isn't trusted. Enable in system settings.");
+  }
+}
+
+void
+mac_clear_hotkeys (void)
+{
+
+  for (id hotkey in hotkey_ids) {
+    [NSEvent removeMonitor: hotkey];
+  }
+
+  [hotkey_ids removeAllObjects];
+}
+#endif
 
 void
 syms_of_nsterm (void)
@@ -9935,6 +9987,7 @@ Nil means use fullscreen the old (< 10.7) way.  The old way works better with
   DEFSYM (QCmouse, ":mouse");
 
 #ifdef NS_IMPL_COCOA
+  DEFSYM (Qglobal_hotkey_hook, "global-hotkey-hook");
   Fprovide (Qcocoa, Qnil);
   syms_of_macfont ();
 #else
diff --git a/src/termhooks.h b/src/termhooks.h
index d18b750c3a..b9c32a1ccd 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -188,6 +188,11 @@ #define EMACS_TERMHOOKS_H
   USER_SIGNAL_EVENT,		/* A user signal.
                                    code is a number identifying it,
                                    index into lispy_user_signals.  */
+#ifdef NS_IMPL_COCOA
+  GLOBAL_HOTKEY_EVENT,          /* An event for when a hotkey is pressed
+                                   outside of emacs that is setup to execute
+                                   an elisp function.  */
+#endif
 
   /* Help events.  Member `frame_or_window' of the input_event is the
      frame on which the event occurred, and member `arg' contains
