* bug#44973: Add a macOS global hotkey function @ 2020-11-30 21:01 j 2020-12-08 20:39 ` Lars Ingebrigtsen 0 siblings, 1 reply; 19+ messages in thread From: j @ 2020-11-30 21:01 UTC (permalink / raw) To: 44973 [-- Attachment #1: Type: text/plain, Size: 1120 bytes --] MacOS makes it very difficult to assign global hotkeys to focus Emacs. For example, if I'm in another program, and I want to launch/focus Emacs with "Alt-SPC", it can't just be set in the system preferences; I'd need to use a 3rd-party program like skhd. Would it be possible to add a function or hook to Emacs which registers one or more global hotkeys (using addGlobalMonitorForEventsMatchingMask), and then when the hotkey is pressed, run an elisp function? For example, by creating a "ns-register-global hotkey" function, where the user could choose a keybinding (maybe "ALT-SPC"), and letting the user provide a hook function (maybe "org-capture-stuff") I intend this feature to be used for something like an Alfred replacement, as described here: https://www.mattduck.com/emacs-fuzzy-launcher.html . Even if it's rare for Emacs to implement an OS-specific feature, there is some precedent on Windows. If you look at w32-register-hot-keys, it's similar to, but different from my request. The implementation in nsterm.m should be simple, but it requires Obj-C and Emacs-specific development knowledge. Thanks! [-- Attachment #2: Type: text/html, Size: 1570 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-11-30 21:01 bug#44973: Add a macOS global hotkey function j @ 2020-12-08 20:39 ` Lars Ingebrigtsen 2020-12-21 0:40 ` j 0 siblings, 1 reply; 19+ messages in thread From: Lars Ingebrigtsen @ 2020-12-08 20:39 UTC (permalink / raw) To: j; +Cc: 44973 j@mremus.net writes: > MacOS makes it very difficult to assign global hotkeys to focus > Emacs. For example, if I'm in another program, and I want to > launch/focus Emacs with "Alt-SPC", it can't just be set in the system > preferences; I'd need to use a 3rd-party program like skhd. > > Would it be possible to add a function or hook to Emacs which registers one or > more global hotkeys (using addGlobalMonitorForEventsMatchingMask), and then > when the hotkey is pressed, run an elisp function? [...] > Even if it's rare for Emacs to implement an OS-specific feature, there > is some precedent on Windows. If you look at w32-register-hot-keys, > it's similar to, but different from my request. Sure, I think that makes sense. Would it be possible for you to work up a patch that adds this functionality? -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-08 20:39 ` Lars Ingebrigtsen @ 2020-12-21 0:40 ` j 2020-12-21 4:34 ` Lars Ingebrigtsen 2020-12-21 8:12 ` Alan Third 0 siblings, 2 replies; 19+ messages in thread From: j @ 2020-12-21 0:40 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 44973 [-- Attachment #1: Type: text/plain, Size: 2026 bytes --] Hi Lars, I'm pretty close to having a patch ready, but I'm stuck in one key spot. I don't know if anyone on the list could help? After the user registers a hotkey, and when they press the hotkey, MacOS will run the code below. On the line "RUN_SOME_ELISP_FUNCTION", I would expect some elisp function to be run (e.g. 'emacs-version'). But no matter what I do, it always crashes the program. I think my first problem is not knowing how to call elisp (run_hooks, safe_call, etc?) correctly, but second, I suspect if this is crashing due to a threading issue. handler = [NSEvent addGlobalMonitorForEventsMatchingMask:(NSEventMaskKeyDown) handler:^(NSEvent *event){ if (event.modifierFlags & modifier) if([event.charactersIgnoringModifiers characterAtIndex:0] == vkey) { RUN_SOME_ELISP_FUNCTION [[NSApp mainWindow] makeKeyAndOrderFront:NSApp]; } } }]; Is there anyone who might know how to fill in this piece? Thanks On Tue, Dec 8, 2020 at 12:39 PM Lars Ingebrigtsen <larsi@gnus.org> wrote: > j@mremus.net writes: > > > MacOS makes it very difficult to assign global hotkeys to focus > > Emacs. For example, if I'm in another program, and I want to > > launch/focus Emacs with "Alt-SPC", it can't just be set in the system > > preferences; I'd need to use a 3rd-party program like skhd. > > > > Would it be possible to add a function or hook to Emacs which registers > one or > > more global hotkeys (using addGlobalMonitorForEventsMatchingMask), and > then > > when the hotkey is pressed, run an elisp function? > > [...] > > > Even if it's rare for Emacs to implement an OS-specific feature, there > > is some precedent on Windows. If you look at w32-register-hot-keys, > > it's similar to, but different from my request. > > Sure, I think that makes sense. Would it be possible for you to work up > a patch that adds this functionality? > > -- > (domestic pets only, the antidote for overdose, milk.) > bloggy blog: http://lars.ingebrigtsen.no > [-- Attachment #2: Type: text/html, Size: 3322 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-21 0:40 ` j @ 2020-12-21 4:34 ` Lars Ingebrigtsen 2020-12-30 4:10 ` j 2020-12-21 8:12 ` Alan Third 1 sibling, 1 reply; 19+ messages in thread From: Lars Ingebrigtsen @ 2020-12-21 4:34 UTC (permalink / raw) To: j; +Cc: 44973 j@mremus.net writes: > But no matter what I do, it always crashes the program. I think my > first problem is not knowing how to call elisp (run_hooks, safe_call, > etc?) correctly, but second, I suspect if this is crashing due to a > threading issue. I am not at all familiar with the ns functions, but looking at the other .m files, it looks like you should be able to just say call0 (intern ("some-function")); or something like that? (Modulo threading stuff.) -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-21 4:34 ` Lars Ingebrigtsen @ 2020-12-30 4:10 ` j 2020-12-30 11:01 ` Alan Third 2020-12-30 17:08 ` Eli Zaretskii 0 siblings, 2 replies; 19+ messages in thread From: j @ 2020-12-30 4:10 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 44973 [-- Attachment #1.1: Type: text/plain, Size: 2131 bytes --] Hi Lars, Here is the patch to bind a global hotkey in mac. As long as Emacs is set as "trusted" in macOS preferences, the user can bind a two-key hotkey of the form [modifier-key] or a single-key modifier [function key], for example (mac-bind-global-hotkey [f1] 'tetris). Binding a three-key combo is left to a future patch. The code is copied from w32-register-hot-key as much as possible. The routine intentionally does not focus the window after the hotkey is hit; the user must call a function like (x-focus-frame nil) to focus the frame. I also wanted to mention that after discovering x-focus-frame, and testing my patch and shkd more, I realized that my patch is in fact very similar to shkd in functionality. The main differences are that my patch allows hotkeys to be declared directly in emacs (which I like), and that this patch acts directly on an open window (e.g. making it easier to make a hotkey to yank into an already-open buffer). On the other hand skhd would call emacsclient, which interacts with the server, even if no frame is open, offering its own advantages. Anyways, I'll leave this patch for your consideration as to whether it's the right thing to include in emacs! P.S. I looked into what other code in emacs might be using blocks. I believe nsxwidget.m also has a block in nsxwidget_webkit_execute_script. I'm guessing it's working in our cases because we're ultimately building with the Xcode C compiler. Thanks On Sun, Dec 20, 2020 at 8:35 PM Lars Ingebrigtsen <larsi@gnus.org> wrote: > j@mremus.net writes: > > > But no matter what I do, it always crashes the program. I think my > > first problem is not knowing how to call elisp (run_hooks, safe_call, > > etc?) correctly, but second, I suspect if this is crashing due to a > > threading issue. > > I am not at all familiar with the ns functions, but looking at the other > .m files, it looks like you should be able to just say > > call0 (intern ("some-function")); > > or something like that? (Modulo threading stuff.) > > -- > (domestic pets only, the antidote for overdose, milk.) > bloggy blog: http://lars.ingebrigtsen.no > [-- Attachment #1.2: Type: text/html, Size: 3049 bytes --] [-- Attachment #2: mac_bind_global_hotkey.diff --] [-- Type: application/octet-stream, Size: 14266 bytes --] 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 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-30 4:10 ` j @ 2020-12-30 11:01 ` Alan Third 2020-12-30 17:08 ` Eli Zaretskii 1 sibling, 0 replies; 19+ messages in thread From: Alan Third @ 2020-12-30 11:01 UTC (permalink / raw) To: j; +Cc: 44973, Lars Ingebrigtsen On Tue, Dec 29, 2020 at 08:10:00PM -0800, j@mremus.net wrote: > Here is the patch to bind a global hotkey in mac. As long as Emacs is set as > "trusted" in macOS preferences, the user can bind a two-key hotkey of the > form [modifier-key] or a single-key modifier [function key], for example > (mac-bind-global-hotkey [f1] 'tetris). Binding a three-key > combo is left to a future patch. Hi, thanks for this. I'll leave it up to Lars and Eli whether this gets to go in, but I've got a few comments: +;; Copyright (C) 1993-1994, 2005-2020 Free Software Foundation, Inc. Since this is a new file I'm pretty sure we just specify 2020 for copyright. +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", I take it's not possible to use the existing keyboard IO stuff for this? Like convert_ns_to_X_keysym? +mac_parse_and_add_hotkey (Lisp_Object key, Lisp_Object lispfn, int hook) ^^^ We don't use mac_ in the NS port, we use ns_. I realise this code is mac specific, but I'd rather not add a new function name prefix, not least because it's possible it will interfere with the Mac port's code, but also we may want to extend this functionality to GNUstep in the future (although that seems unlikely right now). It also means it's easy to see all the lisp functions that relate to the NS port by doing something like M-x ns-<TAB>. If you're copying functionality from the Mac port feel free to set up an alias, but please use ns for the function names. + || ((lisp_modifiers & ctrl_modifier) + && EQ (ns_command_modifier, Qcontrol)) + ) ^^^ I don't think we leave closing parens hanging like that, just stick it on the end of the previous line, please. doc: /* Bind KEY combination as a global hotkey, and run HOOK upon +invokation. This function assigns a hotkey that will run an elisp function ^^^^^^^^^^ invocation +mac_bind_key (NSEventModifierFlags modifier, unsigned vkey, You might want to call this something like ns_bind_global_hotkey so it's clearer what it does, and a short descriptive comment wouldn't go amiss. + hotkey_ids = [[NSMutableArray alloc] initWithCapacity: 1]; You're allocing this array, but not releasing it anywhere. + if (trusted) { ^^^ Opening brace should be on a new line. This function (mac_bind_key) needs this fix in a few places. + if([[event.charactersIgnoringModifiers capitalizedString] ^^^ Missing space. We could also do with some documentation. One thing I'm unsure about is that we allow users to map the left and right modifier keys separately but I think your code only allows for matching with left key settings. Is that intentional? It would seem unlikely that macOS would let us map global hotkeys to the left and right keys separately, so I understand if it's not possible. -- Alan Third ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-30 4:10 ` j 2020-12-30 11:01 ` Alan Third @ 2020-12-30 17:08 ` Eli Zaretskii 2021-01-04 5:13 ` j 1 sibling, 1 reply; 19+ messages in thread From: Eli Zaretskii @ 2020-12-30 17:08 UTC (permalink / raw) To: j; +Cc: 44973, larsi > From: j@mremus.net > Date: Tue, 29 Dec 2020 20:10:00 -0800 > Cc: 44973@debbugs.gnu.org > > Here is the patch to bind a global hotkey in mac. As long as Emacs is set as > "trusted" in macOS preferences, the user can bind a two-key hotkey of the > form [modifier-key] or a single-key modifier [function key], for example > (mac-bind-global-hotkey [f1] 'tetris). Binding a three-key > combo is left to a future patch. > > The code is copied from w32-register-hot-key as much as possible. Hmm... w32-register-hot-key is not for binding Emacs commands to platform-specific keys, it is so that the OS doesn't catch some key combinations ahead of Emacs. That is, by using w32-register-hot-key you make the key combination available for binding to a command using global-set-key and the likes. By contrast, it sounds like your patch is for letting users bind platform-specific key sequences to Emacs commands, which is something quite different. What is the rationale for adding this functionality to Emacs? ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-30 17:08 ` Eli Zaretskii @ 2021-01-04 5:13 ` j 2021-01-04 17:09 ` Eli Zaretskii 0 siblings, 1 reply; 19+ messages in thread From: j @ 2021-01-04 5:13 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 44973, Lars Ingebrigtsen [-- Attachment #1.1: Type: text/plain, Size: 2472 bytes --] Hi Eli, Alan, Here's a new patch incorporating all of Alan's feedback, with two notes: 1. This implementation is unable to differentiate between left and right modifier keys, because it depends upon NSEventModifierFlags, which doesn't differentiate between left and right. 2. hotkey_ids is a global variable, so it should not be released. To address Eli's feedback, this patch is inspired by https://www.mattduck.com/emacs-fuzzy-launcher.html , where Matt uses emacs to create a launcher like Spotlight or Alfred. Since he's using GNU/Linux and i3 wm, he simply assigned a global hotkey in his window manager config to run emacsclient and run the launcher. I believe this is also easy to do in Windows. In macOS this is much more difficult, because assigning a global hotkey can only be done in code. So a user would have to rely on a utility like skhd or Hammerspoon. My proposal is to add this patch to emacs, so that a user can assign a global hotkey in emacs without relying on a 3rd-party program. I used w32-register-hot-key as an example of another instance where emacs added an OS-specific feature in order to provide a consistent user experience across platforms. Also, even though the behavior is totally different, the actual implementation of that routine and mine are very similar. Thanks for all of your time and feedback. On Wed, Dec 30, 2020 at 9:08 AM Eli Zaretskii <eliz@gnu.org> wrote: > > From: j@mremus.net > > Date: Tue, 29 Dec 2020 20:10:00 -0800 > > Cc: 44973@debbugs.gnu.org > > > > Here is the patch to bind a global hotkey in mac. As long as Emacs is > set as > > "trusted" in macOS preferences, the user can bind a two-key hotkey of the > > form [modifier-key] or a single-key modifier [function key], for example > > (mac-bind-global-hotkey [f1] 'tetris). Binding a three-key > > combo is left to a future patch. > > > > The code is copied from w32-register-hot-key as much as possible. > > Hmm... w32-register-hot-key is not for binding Emacs commands to > platform-specific keys, it is so that the OS doesn't catch some key > combinations ahead of Emacs. That is, by using w32-register-hot-key > you make the key combination available for binding to a command using > global-set-key and the likes. > > By contrast, it sounds like your patch is for letting users bind > platform-specific key sequences to Emacs commands, which is something > quite different. What is the rationale for adding this functionality > to Emacs? > [-- Attachment #1.2: Type: text/html, Size: 3421 bytes --] [-- Attachment #2: ns_bind_global_hotkey.diff --] [-- Type: application/octet-stream, Size: 17101 bytes --] diff --git a/lisp/loadup.el b/lisp/loadup.el index 568b9fe40d..a83b5b9bae 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -1,6 +1,6 @@ ;;; loadup.el --- load up standardly loaded Lisp files for Emacs -;; Copyright (C) 1985-1986, 1992, 1994, 2001-2020 Free Software +;; Copyright (C) 1985-1986, 1992, 1994, 2001-2021 Free Software ;; Foundation, Inc. ;; Maintainer: emacs-devel@gnu.org @@ -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..cf7358a083 --- /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) 2020-2021 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 ns-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..30691552b6 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1,6 +1,6 @@ /* Keyboard and mouse input; editor command loop. -Copyright (C) 1985-1989, 1993-1997, 1999-2020 Free Software Foundation, +Copyright (C) 1985-1989, 1993-1997, 1999-2021 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -4868,7 +4868,7 @@ #define FUNCTION_KEY_OFFSET 0xff00 /* You'll notice that this table is arranged to be conveniently indexed by X Windows keysym values. */ -static const char *const lispy_function_keys[] = +const char *const lispy_function_keys[] = { /* X Keysym value */ @@ -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 (Qns_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 (Qns_global_hotkey, "ns-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, "ns-global-hotkey", + "ns-handle-global-hotkey"); +#endif #ifdef HAVE_NTGUI initial_define_lispy_key (Vspecial_event_map, "end-session", "kill-emacs"); diff --git a/src/keyboard.h b/src/keyboard.h index 41da3a6bf4..b859eb34c8 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -1,5 +1,5 @@ /* Declarations useful when processing input. - Copyright (C) 1985-1987, 1993, 2001-2020 Free Software Foundation, + Copyright (C) 1985-1987, 1993, 2001-2021 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -495,6 +495,9 @@ kbd_buffer_store_event_hold (struct input_event *event, #ifdef HAVE_NTGUI extern const char *const lispy_function_keys[]; #endif +#ifdef NS_IMPL_COCOA +extern const char *const lispy_function_keys[]; +#endif extern char const DEV_TTY[]; diff --git a/src/nsfns.m b/src/nsfns.m index c7956497c4..d361e76f07 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -1,6 +1,6 @@ /* Functions for the NeXT/Open/GNUstep and macOS window system. -Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2020 Free Software +Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2021 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -67,6 +67,112 @@ Updated by Christian Limpach (chris@nice.ch) ========================================================================== */ +#ifdef NS_IMPL_COCOA + +/* Convert a hotkey of the form [M-key] into a form that the OS can recognize. + If successful, call ns_bind_global_hotkey to register the key combination + with the OS, and set the callback to trigger an emacs event. + */ +static Lisp_Object +ns_parse_and_add_hotkey (Lisp_Object key, Lisp_Object lispfn) +{ + register Lisp_Object c; + unsigned vk_code = 0; + int lisp_modifiers = 0; + NSEventModifierFlags ns_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 = ns_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)) + ns_modifiers = ns_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))) + { + ns_modifiers = ns_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)) + ) + { + ns_modifiers = ns_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)) + ) + { + ns_modifiers = ns_modifiers | NSEventModifierFlagControl; + } + + // Bind function key + if ((ns_modifiers & NSEventModifierFlagDeviceIndependentFlagsMask) > 0) + ns_bind_global_hotkey(ns_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 +3090,66 @@ The position is returned as a cons cell (X . Y) of the return Qnil; } +#ifdef NS_IMPL_COCOA +static Lisp_Object ns_registered_hotkeys; + +DEFUN ("ns-bind-global-hotkey", + Fns_bind_global_hotkey, + Sns_bind_global_hotkey, 2, 2, 0, + doc: /* Bind KEY combination as a global hotkey, and run HOOK upon invocation. +Assigns a hotkey that will run an elisp function from anywhere in MacOS +outside Emacs. + +KEY must be either a two-key vector of the form [mod-key] or a single +function key vector [fn]. For Example: + + (ns-bind-global-hotkey [M-1] 'tetris) + (ns-bind-global-hotkey [f1] 'make-frame) + +HOOK must be a function. + +The return value is t if registering the hotkey was successful, otherwise nil. +*/) + (Lisp_Object key, Lisp_Object hook) +{ + id handler; + + key = ns_parse_and_add_hotkey (key, hook); + + if (!NILP (key) && NILP (Fmemq (Fcons(key,hook), ns_registered_hotkeys))) + { + Lisp_Object item = Fmemq (Qnil, ns_registered_hotkeys); + + if (NILP (item)) + ns_registered_hotkeys = Fcons (Fcons(key,hook), ns_registered_hotkeys); + else + XSETCAR (item, Fcons(key,hook)); + } + + return key; +} + +DEFUN ("ns-show-bound-hotkeys", Fns_show_bound_hotkeys, + Sns_show_bound_hotkeys, 0, 0, 0, + doc: /* Return list of registered hotkey vectors and functions */) + (void) +{ + return ns_registered_hotkeys; +} + +DEFUN ("ns-clear-hotkeys", Fns_clear_hotkeys, + Sns_clear_hotkeys, 0, 0, 0, + doc: /* Unbind all global hotkeys */) + (void) +{ + ns_registered_hotkeys = Qnil; + + ns_clear_hotkeys(); + + return Qt; +} +#endif + /* ========================================================================== Class implementations @@ -3136,6 +3302,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 (&Sns_bind_global_hotkey); + defsubr (&Sns_show_bound_hotkeys); + defsubr (&Sns_clear_hotkeys); +#endif defsubr (&Sx_display_mm_width); defsubr (&Sx_display_mm_height); defsubr (&Sx_display_screens); @@ -3160,6 +3331,10 @@ - (Lisp_Object)lispString defsubr (&Sx_show_tip); defsubr (&Sx_hide_tip); +#ifdef NS_IMPL_COCOA + staticpro (&ns_registered_hotkeys); + ns_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..2f9198753c 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -1,5 +1,5 @@ /* Definitions and headers for communication with NeXT/Open/GNUstep API. - Copyright (C) 1989, 1993, 2005, 2008-2020 Free Software Foundation, + Copyright (C) 1989, 1993, 2005, 2008-2021 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -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,12 @@ #define NSAPP_DATA2_RUNFILEDIALOG 11 extern void ns_init_events (struct input_event *); extern void ns_finish_events (void); +#ifdef NS_IMPL_COCOA +extern unsigned ns_lookup_vk_code (char *key); +typedef enum NSEventModifierFlags NSEventModifierFlags; +extern void ns_bind_global_hotkey (enum NSEventModifierFlags modifier, unsigned vkey, Lisp_Object lispfn); +extern void ns_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..03a81ecb0a 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1,6 +1,6 @@ /* NeXT/Open/GNUstep / macOS communication module. -*- coding: utf-8 -*- -Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2020 Free Software +Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2021 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -9721,6 +9721,106 @@ Convert an X font name (XLFD) to an NS font name. return ret; } +#ifdef NS_IMPL_COCOA + +/* 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 */ +unsigned +ns_lookup_vk_code (char *key) +{ + unsigned i; + const unsigned last_keysym = ARRAYELTS (convert_ns_to_X_keysym); + unsigned keysym; + + // adapted from ns_convert_key + for (i = 0; i < 256; i++) + if (lispy_function_keys[i] + && strcmp (lispy_function_keys[i], key) == 0) + for (keysym = 1; keysym < last_keysym; keysym += 2) + if (i == convert_ns_to_X_keysym[keysym]) + return convert_ns_to_X_keysym[keysym-1]; + + /* 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]); + } + + return -1; +} + +// Store event handler event ids. +NSMutableArray *hotkey_ids; + +/* ns_bind_global_hotkey registers the key combo with the OS, and when the + key combination is pressed, sends a GLOBAL_HOTKEY_EVENT. + After registering the hotkey, the hotkey and function are added to + hotkey_ids. + The key combo either be a two-key combo, beginining with one of + NSEventModifierFlags, or a single function key. + If Emacs is not set as "trusted" in Privacy settings, return an error. + */ +void +ns_bind_global_hotkey (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."); +} + +/* Clear list of global hotkeys. Remove them with the OS and clear them + within Emacs. */ +void +ns_clear_hotkeys (void) +{ + + for (id hotkey in hotkey_ids) { + [NSEvent removeMonitor: hotkey]; + } + + [hotkey_ids removeAllObjects]; +} +#endif void syms_of_nsterm (void) @@ -9935,6 +10035,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..db50a89f6d 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -1,6 +1,6 @@ /* Parameters and display hooks for terminal devices. -Copyright (C) 1985-1986, 1993-1994, 2001-2020 Free Software Foundation, +Copyright (C) 1985-1986, 1993-1994, 2001-2021 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -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 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2021-01-04 5:13 ` j @ 2021-01-04 17:09 ` Eli Zaretskii 2021-01-05 8:25 ` Lars Ingebrigtsen 2021-01-06 5:02 ` Richard Stallman 0 siblings, 2 replies; 19+ messages in thread From: Eli Zaretskii @ 2021-01-04 17:09 UTC (permalink / raw) To: j; +Cc: 44973, larsi > From: j@mremus.net > Date: Sun, 3 Jan 2021 21:13:47 -0800 > Cc: Lars Ingebrigtsen <larsi@gnus.org>, 44973@debbugs.gnu.org > > I used w32-register-hot-key as an example of another instance where emacs added > an OS-specific feature in order to provide a consistent user experience across > platforms. w32-register-hot-key allows a consistent UX across platforms _inside_Emacs_. Your changes, AFAIU, aims at using Emacs as a way to work around problems in the OS WM, not in Emacs running on that OS. So I'm still not sure we should install this. Lars, do you have an opinion on this? ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2021-01-04 17:09 ` Eli Zaretskii @ 2021-01-05 8:25 ` Lars Ingebrigtsen 2021-01-05 8:26 ` Lars Ingebrigtsen ` (2 more replies) 2021-01-06 5:02 ` Richard Stallman 1 sibling, 3 replies; 19+ messages in thread From: Lars Ingebrigtsen @ 2021-01-05 8:25 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 44973, j Eli Zaretskii <eliz@gnu.org> writes: > w32-register-hot-key allows a consistent UX across platforms > _inside_Emacs_. Your changes, AFAIU, aims at using Emacs as a way to > work around problems in the OS WM, not in Emacs running on that OS. > > So I'm still not sure we should install this. > > Lars, do you have an opinion on this? Since this brings up the Macos version of Emacs into feature parity with what you can do in GNU/Linux and Windows, I don't have any objections to including it. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2021-01-05 8:25 ` Lars Ingebrigtsen @ 2021-01-05 8:26 ` Lars Ingebrigtsen 2021-01-05 15:04 ` Eli Zaretskii 2021-01-05 15:04 ` Eli Zaretskii 2021-01-06 5:15 ` Richard Stallman 2 siblings, 1 reply; 19+ messages in thread From: Lars Ingebrigtsen @ 2021-01-05 8:26 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 44973, j Lars Ingebrigtsen <larsi@gnus.org> writes: > Since this brings up the Macos version of Emacs into feature parity with > what you can do in GNU/Linux and Windows, I don't have any objections to > including it. (There's still the issue of code blocks, though -- we're not accepting those until gcc supports those, I think?) -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2021-01-05 8:26 ` Lars Ingebrigtsen @ 2021-01-05 15:04 ` Eli Zaretskii 0 siblings, 0 replies; 19+ messages in thread From: Eli Zaretskii @ 2021-01-05 15:04 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 44973, j > From: Lars Ingebrigtsen <larsi@gnus.org> > Cc: j@mremus.net, 44973@debbugs.gnu.org > Date: Tue, 05 Jan 2021 09:26:56 +0100 > > Lars Ingebrigtsen <larsi@gnus.org> writes: > > > Since this brings up the Macos version of Emacs into feature parity with > > what you can do in GNU/Linux and Windows, I don't have any objections to > > including it. > > (There's still the issue of code blocks, though -- we're not accepting > those until gcc supports those, I think?) Right. ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2021-01-05 8:25 ` Lars Ingebrigtsen 2021-01-05 8:26 ` Lars Ingebrigtsen @ 2021-01-05 15:04 ` Eli Zaretskii 2021-01-06 5:15 ` Richard Stallman 2 siblings, 0 replies; 19+ messages in thread From: Eli Zaretskii @ 2021-01-05 15:04 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 44973, j > From: Lars Ingebrigtsen <larsi@gnus.org> > Cc: j@mremus.net, 44973@debbugs.gnu.org > Date: Tue, 05 Jan 2021 09:25:24 +0100 > > Eli Zaretskii <eliz@gnu.org> writes: > > > w32-register-hot-key allows a consistent UX across platforms > > _inside_Emacs_. Your changes, AFAIU, aims at using Emacs as a way to > > work around problems in the OS WM, not in Emacs running on that OS. > > > > So I'm still not sure we should install this. > > > > Lars, do you have an opinion on this? > > Since this brings up the Macos version of Emacs into feature parity with > what you can do in GNU/Linux and Windows, I don't have any objections to > including it. Then I guess we can install it (once the other problem is solved). ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2021-01-05 8:25 ` Lars Ingebrigtsen 2021-01-05 8:26 ` Lars Ingebrigtsen 2021-01-05 15:04 ` Eli Zaretskii @ 2021-01-06 5:15 ` Richard Stallman 2 siblings, 0 replies; 19+ messages in thread From: Richard Stallman @ 2021-01-06 5:15 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 44973, j [[[ To any NSA and FBI agents reading my email: please consider ]]] [[[ whether defending the US Constitution against all enemies, ]]] [[[ foreign or domestic, requires you to follow Snowden's example. ]]] You and Eli gave two analyses of the nature of this change: Eli: > w32-register-hot-key allows a consistent UX across platforms > _inside_Emacs_. Your changes, AFAIU, aims at using Emacs as a way to > work around problems in the OS WM, not in Emacs running on that OS. Lars: > Since this brings up the Macos version of Emacs into feature parity with > what you can do in GNU/Linux and Windows, If Lars is right, it is ok to install. If Eli is right, it should not be installed. So it's a matter of which of those two is right. I have no idea. But you can compare notes and figure out. Is it possible that, in some way, both are right? If so, this is a strange situation and worth deeper study. -- Dr Richard Stallman Chief GNUisance of the GNU Project (https://gnu.org) Founder, Free Software Foundation (https://fsf.org) Internet Hall-of-Famer (https://internethalloffame.org) ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2021-01-04 17:09 ` Eli Zaretskii 2021-01-05 8:25 ` Lars Ingebrigtsen @ 2021-01-06 5:02 ` Richard Stallman 1 sibling, 0 replies; 19+ messages in thread From: Richard Stallman @ 2021-01-06 5:02 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 44973, larsi, j [[[ To any NSA and FBI agents reading my email: please consider ]]] [[[ whether defending the US Constitution against all enemies, ]]] [[[ foreign or domestic, requires you to follow Snowden's example. ]]] > w32-register-hot-key allows a consistent UX across platforms > _inside_Emacs_. Your changes, AFAIU, aims at using Emacs as a way to > work around problems in the OS WM, not in Emacs running on that OS. That sure sounds like a MacOS-specific feature. If so, we should not include it in a GNU program. -- Dr Richard Stallman Chief GNUisance of the GNU Project (https://gnu.org) Founder, Free Software Foundation (https://fsf.org) Internet Hall-of-Famer (https://internethalloffame.org) ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-21 0:40 ` j 2020-12-21 4:34 ` Lars Ingebrigtsen @ 2020-12-21 8:12 ` Alan Third 2020-12-21 16:18 ` Eli Zaretskii 1 sibling, 1 reply; 19+ messages in thread From: Alan Third @ 2020-12-21 8:12 UTC (permalink / raw) To: j; +Cc: 44973, Lars Ingebrigtsen On Sun, Dec 20, 2020 at 04:40:37PM -0800, j@mremus.net wrote: > Hi Lars, > > I'm pretty close to having a patch ready, but I'm stuck in one key spot. I > don't know if anyone on the list could help? > > After the user registers a hotkey, and when they press the hotkey, MacOS > will run the code below. On the line "RUN_SOME_ELISP_FUNCTION", I would > expect some elisp function to be run (e.g. 'emacs-version'). > > But no matter what I do, it always crashes the program. I think my first > problem is not knowing how to call elisp (run_hooks, safe_call, etc?) > correctly, but second, I suspect if this is crashing due to a threading > issue. > > handler = [NSEvent > addGlobalMonitorForEventsMatchingMask:(NSEventMaskKeyDown) > handler:^(NSEvent *event){ > if (event.modifierFlags & modifier) > if([event.charactersIgnoringModifiers characterAtIndex:0] == > vkey) { > RUN_SOME_ELISP_FUNCTION > [[NSApp mainWindow] makeKeyAndOrderFront:NSApp]; > } > } > }]; I think you probably want to send an event to Emacs, that way a user can bind it to anything they like. Look at, say, [EmacsApp openFile:] for how do that. One potential problem with your approach is that we don't accept code using Objective C blocks as it's incompatible with GCC. Unless that policy has changed? -- Alan Third ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-21 8:12 ` Alan Third @ 2020-12-21 16:18 ` Eli Zaretskii 2020-12-21 16:34 ` Alan Third 0 siblings, 1 reply; 19+ messages in thread From: Eli Zaretskii @ 2020-12-21 16:18 UTC (permalink / raw) To: Alan Third; +Cc: 44973, alan, larsi, j > Date: Mon, 21 Dec 2020 08:12:04 +0000 > From: Alan Third <alan@idiocy.org> > Cc: 44973@debbugs.gnu.org, Lars Ingebrigtsen <larsi@gnus.org> > > One potential problem with your approach is that we don't accept code > using Objective C blocks as it's incompatible with GCC. Unless that > policy has changed? It hasn't changed, but does this code really need to use that feature? The last time we had a much more grave problem: the system header used for some code itself used ObjC blocks, and thus couldn't be compiled by GCC. It looks like this case is not that problematic? ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-21 16:18 ` Eli Zaretskii @ 2020-12-21 16:34 ` Alan Third 2020-12-22 5:20 ` Richard Stallman 0 siblings, 1 reply; 19+ messages in thread From: Alan Third @ 2020-12-21 16:34 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 44973, larsi, j On Mon, Dec 21, 2020 at 06:18:58PM +0200, Eli Zaretskii wrote: > > Date: Mon, 21 Dec 2020 08:12:04 +0000 > > From: Alan Third <alan@idiocy.org> > > Cc: 44973@debbugs.gnu.org, Lars Ingebrigtsen <larsi@gnus.org> > > > > One potential problem with your approach is that we don't accept code > > using Objective C blocks as it's incompatible with GCC. Unless that > > policy has changed? > > It hasn't changed, but does this code really need to use that feature? > The last time we had a much more grave problem: the system header used > for some code itself used ObjC blocks, and thus couldn't be compiled > by GCC. It looks like this case is not that problematic? The class method used takes a block as an argument. I don't think there are any work-arounds. The GCC work to support blocks appears to be progressing, but slowly: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78352 -- Alan Third ^ permalink raw reply [flat|nested] 19+ messages in thread
* bug#44973: Add a macOS global hotkey function 2020-12-21 16:34 ` Alan Third @ 2020-12-22 5:20 ` Richard Stallman 0 siblings, 0 replies; 19+ messages in thread From: Richard Stallman @ 2020-12-22 5:20 UTC (permalink / raw) To: Alan Third; +Cc: 44973, larsi, alan, j [[[ To any NSA and FBI agents reading my email: please consider ]]] [[[ whether defending the US Constitution against all enemies, ]]] [[[ foreign or domestic, requires you to follow Snowden's example. ]]] > The GCC work to support blocks appears to be progressing, but slowly: > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78352 I am glad it is making progress. We will get there eventually. -- Dr Richard Stallman Chief GNUisance of the GNU Project (https://gnu.org) Founder, Free Software Foundation (https://fsf.org) Internet Hall-of-Famer (https://internethalloffame.org) ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2021-01-06 5:15 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2020-11-30 21:01 bug#44973: Add a macOS global hotkey function j 2020-12-08 20:39 ` Lars Ingebrigtsen 2020-12-21 0:40 ` j 2020-12-21 4:34 ` Lars Ingebrigtsen 2020-12-30 4:10 ` j 2020-12-30 11:01 ` Alan Third 2020-12-30 17:08 ` Eli Zaretskii 2021-01-04 5:13 ` j 2021-01-04 17:09 ` Eli Zaretskii 2021-01-05 8:25 ` Lars Ingebrigtsen 2021-01-05 8:26 ` Lars Ingebrigtsen 2021-01-05 15:04 ` Eli Zaretskii 2021-01-05 15:04 ` Eli Zaretskii 2021-01-06 5:15 ` Richard Stallman 2021-01-06 5:02 ` Richard Stallman 2020-12-21 8:12 ` Alan Third 2020-12-21 16:18 ` Eli Zaretskii 2020-12-21 16:34 ` Alan Third 2020-12-22 5:20 ` Richard Stallman
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.