Skip to content

Using Fn key for emoji picker

RedBearAK edited this page Aug 8, 2024 · 14 revisions

Important

The Fn key on most non-Apple keyboards is unusable for a shortcut remap, because the operating system cannot see an event that indicatoes the key being pressed. The Fn key is often handled by the BIOS on PC laptops. So the following will probably only work on an Apple keyboard, such as those on Mac portables, or Apple external keyboards with an Fn key. Some fancy multi-function PC keyboards with media keys use the hid_apple keyboard driver, so they might also work if they have a Fn key. Use evtest to check whether the Fn key press can be seen.

If you have an Apple keyboard with an Fn key, that key may be usable as a modifier-only shortcut to match the recent macOS functionality of showing an emoji picker. An obvious pre-requisite needed for this is that you have some sort of emoji picker available that can be invoked with a keyboard combo of some kind. The keymapper behind Toshy's functionality is after all only able to send out keystroke events, just like you sitting at the keyboard and typing.

There is an emoji picker dialog available in GNOME, at least while using certain GTK applications. Possibly this will even work when using newer GTK applications on other desktop environments, but this is untested, so the example keymap is designed to only be active while using GNOME as a desktop environment. The default shortcut for this is Ctrl+Semicolon, so the example for GNOME will map onto that.

A stumbling block to making this work is that Fn is identified as a modifier key by the keymapper, and modifiers aren't currently allowed to be used as modifier-only shortcuts (which is generally a good thing). Trying to do so will cause an exception when loading the config file. But we can use a multipurpose_modmap to change the identity of the Fn key to something that isn't treated like a modifier, and then use that innocuous key as the single key to remap to what we want. It has to be a single key that wouldn't be used for anything else.

According to some references, several of the function keys beyond F12 may already be mapped to specific things by standard XKB keyboard layouts, but F19 may generally be safe to use without having a possibly odd side effect.

https://www.reddit.com/r/linuxquestions/comments/r9w8yh/disable_function_keys_beyond_f12/

Step 1: Multi-modmap the Fn key to F19 and Fn

The example below is using the editable "slice" found in recent versions of the default Toshy config file, labeled with user_custom_modmaps. I may actually add a modmap like this to the default config at some point. What this is doing is making Fn identify as F19 when tapped (press/release) and continue to be Fn when used as part of a combo like Fn-Clear. (Currently the only remap in the config file that tries to use the Fn key at all.)

###################################################################################################
###  SLICE_MARK_START: user_custom_modmaps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE

multipurpose_modmap("User override: Fn is F19 or Fn", {
    Key.KEY_FN:                  [Key.F19, Key.KEY_FN]     # Let Fn be F19 for modifier-only shortcut
}, when = lambda ctx:
    matchProps(not_clas=remoteStr)(ctx) )

###  SLICE_MARK_END: user_custom_modmaps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
###################################################################################################

Now when you tap Fn it will be seen as F19, and the keymapper won't complain when we remap F19 to something by itself, because it is not a designated modifier key.

Step 2: Remap F19 to the emoji picker shortcut

As described earlier, the example will be for GNOME, which sort of has an emoji picker dialog available. The shortcut is Ctrl+Semicolon, and when Toshy is enabled the Cmd key position becomes RIGHT_CTRL so the physical equivalent of Cmd+Semicolon should bring up the emoji picker where it is available (text areas in GTK apps). If this doesn't work, the remap onto that shortcut won't work, so before you waste your time make sure it functions on Cmd+Semicolon (or on Ctrl+Semicolon with Toshy disabled).

In the example below we will use the user_apps editable slice to add a custom keymap with a condtional that will make it active only in GNOME. Other custom keymaps with different conditionals can be used to do a different remap if another desktop environment also has some kind of emoji picker available, perhaps with a very different shortcut to remap onto. I may add one or more keymaps to the default Toshy config file later on. For now you'll just have to do this yourself. Putting it in the editable slice will protect it if you reinstall Toshy.

###################################################################################################
###  SLICE_MARK_START: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE

keymap("User overrides: GNOME", {
    C("F19"): C("C-Semicolon"),      # Custom remap for Fn to open emoji picker in GNOME
}, when = lambda ctx: 
        matchProps(not_clas=remoteStr)(ctx) and 
        DESKTOP_ENV == 'gnome'
)

###  SLICE_MARK_END: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
###################################################################################################

Like all the "General" keymaps at the end of the config file, this has the conditional that keeps it from being active while you're using any app in the "remotes" group, such as virtual machines or VNC/RDP, NoMachine, AnyDesk, etc. And then the DESKTOP_ENV == 'gnome' condition to leave the keymap only active while logged into a GNOME session.

Once the changes from step 1 and step 2 are saved in the config file, test that no mistakes were made by using this command in the terminal:

toshy-debug

Or you can use this older, longer alias to do the same thing:

toshy-config-verbose-start

This starts up just the config file with verbose debugging output, which will quickly reveal if you have a missing comma or something. If you have a problem implementing this and can't figure it out, submit an issue.

The background services can be restarted from the tray icon menu, or by stopping the verbose command with Ctrl+C or Cmd+Dot and running this command:

toshy-services-restart

That command will also safely terminate the verbose command if it is running in another tab or terminal window and you just forgot about it. All the commands are relatively robust in this manner, making them easy to use without stressing about remembering to stop one thing before doing something else. I'm lazy and wanted it all to be as automatic as possible for mys--I mean, other users.

Note

A simple test on a Mac mini M1 running Sonoma with a full-size external USB Apple keyboard showed no effect in macOS when the Fn key was pressed (i.e., no emoji picker showed up). So technically you might get functionality out of this on a full external Apple keyboard that would not be possible in macOS on a desktop Mac.

Other Desktop Environments

KDE Plasma Emoji Selector shortcut

Looks like KDE Plasma has had an Emoji Selector app for a few years, and there is a global shortcut for launching it. But it really isn't the same kind of thing as the emoji picker in GTK apps, which I think is built into GTK. There would need to be a corresponding tool built into Qt apps like the KDE apps. Perhaps that will happen at some point, but right now Emoji Selector is just an app that can be launched and used to copy emojis into the clipboard, to paste in your original app window. That's not how the GNOME or macOS emoji pickers work. They are more like inline virtual keyboards.

But there is a global shortcut that can be remapped onto from the Fn key (Meta+Dot), so a similar keymap can work for Plasma desktops. Quick reminder, Meta/Super/Win/Cmd are all the same key code. If you were to try to use that shortcut while Toshy was enabled, it would not work in terminals (because Meta isn't available) and in non-terminal apps it would work on the physical Ctrl+Dot keys (because physical Ctrl becomes the Meta key).

This keymap should do the job for Plasma:

###################################################################################################
###  SLICE_MARK_START: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE

keymap("User overrides: KDE Plasma", {
    C("F19"): C("Super-Dot"),      # Custom remap for Fn to open Emoji Selector app in KDE Plasma
}, when = lambda ctx: 
        matchProps(not_clas=remoteStr)(ctx) and 
        DESKTOP_ENV == 'kde'
)

###  SLICE_MARK_END: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
###################################################################################################

The same instructions from step 2 apply, and of course the same multipurpose_modmap from step 1 would need to be in place to change the identity of the Fn key when tapped.

What about a mixed GTK/Qt environment?

If you're using GTK apps that support the Ctrl+Semicolon shortcut and will show the emoji picker, but you aren't in GNOME, the instructions above would not activate the Fn key (F19) remap in general, and you wouldn't want to. But you can override the Fn key remap when you are in specific apps in that case, even if you are living entirely in KDE Plasma and want to remap Fn to the Meta+Dot shortcut outside of those specific GTK apps. Let's see how we would do this.

I would probably make a list in this case, where you can place the app class strings of any GTK apps you come across where you can get the emoji picker to appear. As described earlier, if you have Toshy enabled, you'd try the physical equivalent of Cmd+Semicolon to see what happens. Then use the Toshy diagnostic dialog shortcut to see the app class string of the main app window (not the emoji picker, make that go away first) and add it to the list. The diagnostic dialog shortcut is Shift+Opt+Cmd+I,I, with the I,I indicating to quickly double-tap the "I" key while holding the modifiers. You should see a Zenity GTK dialog appear shortly, showing a lot of useful information.

Below is an example starter list, which will actually be a "list of dicts" to feed to the matchProps() function for matching. I like to name the variable in these cases with _lod on the end just to remind me what's inside it.

Note

Those more familiar with Python dictionary structures might find it a little odd that the keys in each dict are not quoted strings, which they normally would be. Well, in this case the Toshy config file uses a sort of "trick" of defining all the valid keys or these specific dicts as variables that will evaluate to strings. You won't format the keys of a Python dict like this in most other circumstances, outside of the Toshy config file. But here, it provides easier visual separation when syntax highlighting is being applied in an editor like VSCode. Or even right here on GitHub.

The clas key and matchProps() argument is for the app class you want to target for matching. Matching is case-insensitive unless forced to be sensitive with another argument.

###################################################################################################
###  SLICE_MARK_START: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE

GTK_emoji_picker_apps_lod = [
    {clas: 'org.gnome.Calendar'},
    {clas: 'org.gnome.TextEditor'},
    {clas: 'org.gnome.Epiphany'},
]

###  SLICE_MARK_END: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
###################################################################################################

This list can then be used to match a keymap. Maybe we can even use the same GNOME keymap with an or in place to activate it in either GNOME or when the app matches this list.

###################################################################################################
###  SLICE_MARK_START: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE

GTK_emoji_picker_apps_lod = [
    {clas: 'org.gnome.Calendar'},
    {clas: 'org.gnome.TextEditor'},
    {clas: 'org.gnome.Epiphany'},
]

keymap("User overrides: GNOME", {
    C("F19"): C("C-Semicolon"),     # Custom remap for Fn to open emoji picker in GNOME/GTK apps
}, when = lambda ctx: 
        matchProps(not_clas=remoteStr)(ctx) and 
        ( DESKTOP_ENV == 'gnome' or matchProps(lst=GTK_emoji_picker_apps_lod)(ctx) )
)

keymap("User overrides: KDE Plasma", {
    C("F19"): C("Super-Dot"),       # Custom remap for Fn to open Emoji Selector app in KDE Plasma
}, when = lambda ctx: 
        matchProps(not_clas=remoteStr)(ctx) and 
        DESKTOP_ENV == 'kde'
)

###  SLICE_MARK_END: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
###################################################################################################

That should do it. Now we have the makings of a good start on supporting this Fn remap in as many desktops and specific apps as possible. If you happen to be in GNOME, the first keymap will be active. Or, if you are not in GNOME but using a GTK app that you've placed in the list, the first keymap will be active. Finally, if you are not in GNOME or one of those GTK apps in the list, and you are in KDE Plasma, the second keymap will be active and a different remap will happen, to open the "Emoji Selector" app.

This could be done slightly differently, without trying to combine the GNOME and specific GTK app conditions. And perhaps it would be more clear this way:

###################################################################################################
###  SLICE_MARK_START: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE

GTK_emoji_picker_apps_lod = [
    {clas: 'org.gnome.Calendar'},
    {clas: 'org.gnome.TextEditor'},
    {clas: 'org.gnome.Epiphany'},
]

keymap("User overrides: GTK emoji picker apps", {
    C("F19"): C("C-Semicolon"),     # Custom remap for Fn to open emoji picker in specific GTK apps
}, when = matchProps(lst=GTK_emoji_picker_apps_lod) )

keymap("User overrides: GNOME", {
    C("F19"): C("C-Semicolon"),     # Custom remap for Fn to open emoji picker in GNOME desktop
}, when = lambda ctx: 
        matchProps(not_clas=remoteStr)(ctx) and 
        DESKTOP_ENV == 'gnome'
)

keymap("User overrides: KDE Plasma", {
    C("F19"): C("Super-Dot"),       # Custom remap for Fn to open Emoji Selector app in KDE Plasma
}, when = lambda ctx: 
        matchProps(not_clas=remoteStr)(ctx) and 
        DESKTOP_ENV == 'kde'
)

###  SLICE_MARK_END: user_apps  ###  EDITS OUTSIDE THESE MARKS WILL BE LOST ON UPGRADE
###################################################################################################

The keymaps here for GNOME and KDE Plasma can also contain other custom remaps you want to be active only in those specific desktop environments, like custom overrides for the Cmd+Space app menu/launcher shortcut. You might have such a custom remap after following the instructions in the Wiki page about customizing your Cmd+Space shortcut remap. See link:

https://github.com/RedBearAK/toshy/wiki/How-to-(persistently)-change-the-Cmd-Space-remap

§