I have conducted an experiment with the following ~/.XCompose file, and tried to document it relatively thoroughly below.
It is my hope that OP and/or others may find some of this (unfortunately rather voluminous) information helpful or at least somewhat interesting, though it is difficult for me to know precisely whether or how they will be able to use it. Of course, to interpret any meaning from the results presented here, the context/environment in which the test was conducted matters greatly. Details about my system are attached, in the file "system_synposis_bbhuit.txt". (In case that attachment gets lost somehow, I will simply post the same information in a follow-up message.) %% THREE XCOMPOSE RULES to test $ cat ~/.XCompose include "%L" <U0D19> : "ങ്ങ" # Ajith's auto-geminate rule, (U0D19) => (U0D19) (U0D4D) (U0D19) <Multi_key> <s> <x> : "✄" # (U2704) white scissors, h/t David Wright <Z> : "ARGA WARGA IN THE DARGA instead of Z" I started an X Window session, and then in a few applications I tested whether the three custom rules above were effective. For the tests, I used a couple of terminal emulators for X (xterm and mlterm) and a couple of graphical web browsers (firefox-esr and qutebrowser):davidson@bbhuit:0 ~$ dpkg-query -l --no-pager xterm mlterm firefox-esr qutebrowser Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad) ||/ Name Version Architecture Description +++-==============-====================-============-============================================================ ii firefox-esr 68.10.0esr-1~deb10u1 amd64 Mozilla Firefox web browser - Extended Support Release (ESR) ii mlterm 3.8.6-2 amd64 MultiLingual TERMinal ii qutebrowser 1.6.1-2 all Keyboard-driven, vim-like browser based on PyQt5 ii xterm 344-1 amd64 X terminal emulator %% ARGA WARGA The third rule ('Z' => "ARGA WARGA...Z") was easiest to test: 1. Open the application. (If necessary, navigate to a text entry field.) 2. (While under "us" keyboard layout) strike the key labeled "Z" on my standard US laptop keyboard 3. Observe whether ARGA WARGA IN THE DARGA instead of Z is printed. The two terminal emulators passed this test. The graphical web browsers failed in an interesting way. Instead of displaying ARGA WARGA IN THE DARGA instead of Z in the text entry field, they displayed just A Also, upon starting up (but only on the first start-up since installation of the three rules in ~/.XCompose) , firefox-esr issued the following errors, which appear to be explanatory: (firefox-esr:11619): Gtk-WARNING **: 16:27:05.630: GTK+ supports to output one char only: "ങ്ങ" # Ajith's auto-geminate rule, (U0D19) => (U0D19) (U0D4D) (U0D19): <U0D19> : "ങ്ങ" # Ajith's auto-geminate rule, (U0D19) => (U0D19) (U0D4D) (U0D19) (firefox-esr:11619): Gtk-WARNING **: 16:27:05.630: GTK+ supports to output one char only: "ARGA WARGA IN THE DARGA instead of Z": <Z> : "ARGA WARGA IN THE DARGA instead of Z" (I have split each error into three separate lines above to improve legibility.) Although both web browsers performed identically, failing in the same way, only firefox-esr issued this explanatory error message. Unlike firefox-esr, qutebrowser does not use libgtk (do "apt-cache depends qutebrowser" to see its dependencies), but both graphical web browsers failed this test identically. %% WHITE SCISSORS The second rule (David Wright's white scissors rule) was tested as follows: 1. Open the application. (If necessary, navigate to a text entry field.) 2. Under "us" keyboard layout, hold down the compose key. Strike the key labeled "S" on my standard US laptop keyboard. Release the compose key. Strike the key labeled "X". 3. Observe whether ✄ is printed. All four applications passed this test. (I should mention that I have noticed that sometimes when I use the compose key, it seems to get ignored on the first try. It happens frequently enough that it has become second nature to me to simply backspace and try again, when this happens. Whether this is human error, or something else, is not something I have spent time investigating. Whatever the cause, it does not trouble me more than other commonly self-inflicted typos do.) NB: If, like me, you occasionally confuse the Ctrl key with the compose key, then keep in mind that when testing this rule in a terminal emulator, that typing Ctrl-s (presumably by accident) may well stop the terminal from echoing entered characters to the display until you enter Ctrl-q to resume normal terminal operation. (That is to say, Ctrl-s doesn't "break your keyboard", it only pauses your terminal's printout to the display until you resume it with Ctrl-q.) %% AUTO-GEMINATION Finally. The first rule in the ~/.XCompose file above <U0D19> : "ങ്ങ" # Ajith's auto-geminate rule, (U0D19) => (U0D19) (U0D4D) (U0D19) I tested as follows (after installing the "Lohit TrueType font for Malayalam Language" package, fonts-lohit-mlym): 1. I opened an xterm (uxterm, actually) and issued the following command to alter the X Window display keymap: $ setxkbmap -display $DISPLAY -layout us,in -variant ,mal This configures --as my alternate keyboard layout in the current X display-- the variant "mal" (malayalam) of the "in" (indian) keyboard layout, so that switching to the alternate layout will permit me to enter malayalam characters. On my system, the longer command below is equivalent to the command above, because the setting for XKBOPTIONS that the longer command specifies (with the -option flag), is already set as a default in my /etc/default/keyboard (see attached system synopsis for details of this): $ setxkbmap -display $DISPLAY -layout us,in -variant ,mal -option grp:caps_toggle I mention this equivalence because the longer command makes explicit whick key permits me to switch from my main "us" keyboard layout to the alternate layout "in.mal" (and back again). NB: In these commands, make note of the commas. In particular the comma in ",mal" is not a typo. NBB: I believe at least one other poster here correctly urged caution when experimenting with setxkbmap. You can render your keyboard non-functional in the X session. Have a plan for shutting down the X session without the use of your keyboard. 2. To test applications besides xterm itself, I launched the application from the xterm just opened. (When testing xterm, I skipped this step.) $ mlterm # or firefox-esr or qutebrowser 3. If necessary, navigate to a text entry field. 4. Strike the capslock key to switch to the alternate keyboard layout configured in step 1. 5. Hold down the shift key. Strike the key labeled "U" on my standard US laptop keyboard. 6. Observe whether ങ്ങ is printed, according to the custom auto-geminate rule in ~/.XCompose. The terminal emulators passed this test. The graphical web browsers failed, instead printing only ങ The error message from firefox-esr above seems to suggest why that is. Another thing I think is interesting is that while the terminal emulators did pass this test and the browsers failed, it is only the graphical web browsers which seem to transform/meld a typed sequence of "ങ" followed by "്" followed by "ങ" into what appears (to my utterly ignorant eye) to be a "freshly synthetic" glyph. (That three-character sequence is typed, on my standard US laptop keyboard under the "in.mal" keyboard layout, by striking the "U" key while holding down shift, then striking the "D" key (unshifted), and then again striking the "U" key while holding down shift.) Finally, in case it is of interest to anyone, in the attached file "xev_output_annotated.txt" is some xev(1) output with commentary that gives a detailed close-up of performing steps 4 through 6 in an xterm. (As with the system synopsis, if it happens to "get lost" I'll just post it the message body of a follow-up.)
This is a synopsis of my (buster) system, which I have set up to make it relatively simple to switch between english, french, and russian in linux virtual terminals. $ uname -a Linux bbhuit 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2+deb10u1 (2020-06-07) x86_64 GNU/Linux %% DISPLAY (on the console) My default virtual consoles (ttys) display english and french text pretty well: $ cat /etc/default/console-setup # CONFIGURATION FILE FOR SETUPCON # Consult the console-setup(5) manual page # See also setupcon(1) ACTIVE_CONSOLES="/dev/tty[1-6]" CHARMAP="UTF-8" CODESET="Lat15" FONTFACE="Terminus" FONTSIZE="12x24" VIDEOMODE= Supplementing this, I have a user configuration which I can deploy on a case-by-case basis in a given tty with "setupcon --current-tty -f russian" so that russian text is displayed well enough, at the cost of losing a few latin glyphs: $ cat ~/.console-setup.russian ACTIVE_CONSOLES="/dev/tty[1-6]" CHARMAP="UTF-8" CODESET="CyrSlav" FONTFACE="Terminus" FONTSIZE="12x24" VIDEOMODE= %% KEYBOARD (on the console, and in X) My default keyboard setup (below) allows at the console entry of most latin characters I will ever care to type (at least in french or english) on a bog-standard US laptop keyboard using "us" layout, using the "menu" key as the compose key. ("The compose key", set by the second element in XKBOPTIONS below, is the referent of the keysym <Multi_key> in /usr/share/X11/locale/$LANG/Compose.) The capslock key toggles to-and-from russian "ru" keyboard layout, so that I can type cyrillic characters FOR ME TO ENJOY. By the way, shift + capslock in both layouts performs THE ORIGINAL FUNCTION OF CAPSLOCK, WHICH IS SHOUTING. In an X Window display, many more characters become available, both for entry and display. $ cat /etc/default/keyboard # KEYBOARD CONFIGURATION FILE # Consult the keyboard(5) manual page # See also setxkbmap(1) XKBMODEL="pc101" XKBLAYOUT="us,ru" XKBVARIANT="," XKBOPTIONS="grp:caps_toggle,compose:menu" BACKSPACE="guess" %% BEYOND THE CONSOLE When an X Window display is needed, I run from a tty: $ startx I have no ~/.xinitrc file. (If I had one, startx would use it.) My ~/.xsession file is below. It is meant to start a window manager, namely ratpoison: $ cat ~/.xsession #!/bin/sh exec ratpoison -f ${HOME}/.ratpoisonrc I believe it gets executed by one of the scripts in the /etc/X11/Xsession.d/ directory, as a consequence of running startx. It is executable by owner: $ ls -l ~/.xsession -rwxr--r-- 1 davidson davidson 49 juil. 9 02:44 /home/davidson/.xsession The following line in my window manager configuration file sets up a shortcut key for launching lxterm, which is a locale-sensitive wrapper around xterm, the venerable terminal emulator for the X Window System. Since I use UTF-8 locales, it launches uxterm (xterm wrapped for that sort of locale). $ head -n 1 ~/.ratpoisonrc bind c exec lxterm +sb -bg black -fg steelblue1 -selbg gray20
$ man xev [...] DESCRIPTION Xev creates a window and then asks the X server to send it events whenever anything happens to the window (such as it being moved, resized, typed in, clicked in, etc.). You can also attach it to an existing window. It is useful for seeing what causes events to occur and to display the information that they contain; it is essentially a debugging and development tool, and should not be needed in normal usage. This is the command I issued from within an xterm (uxterm, really) to launch xev and attach it to that terminal's window. $ xev -id $WINDOWID -event keyboard # I press enter, of course # And here is the release of the enter key: KeyRelease event, serial 18, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76502401, (1362,764), root:(1364,766), state 0x0, keycode 36 (keysym 0xff0d, Return), same_screen YES, XLookupString gives 1 bytes: (0d) "" XFilterEvent returns: False # Next, I strike the capslock key to enter the Malayalam keymap, "ml". # The capslock key's keycode happens to be 66. This is a relatively # low-level identifier, mechanical, relatively meaningless in itself, # fixed by factual accident. # # Its keysym, under my configuration, is apparently <ISO_Next_Group>. # # A keysym is an identifier of somewhat higher abstraction than a # keycode, and is meant to *mean* something. (I mean, just compare the # two names to see this!) # # The following *three* records correspond to striking this key. # # NB: For some reason, each key struck seems to correspond to *three* # records in the xev output: # # 1. a KeyRelease event (kind of weird, but that's how it is). # 2. a KeyPress event. # 3. a KeyRelease event. KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76506392, (1362,764), root:(1364,766), state 0x0, keycode 66 (keysym 0xfe08, ISO_Next_Group), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False KeyPress event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76506392, (1362,764), root:(1364,766), state 0x0, keycode 66 (keysym 0xfe08, ISO_Next_Group), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76506480, (1362,764), root:(1364,766), state 0x2000, keycode 66 (keysym 0xfe08, ISO_Next_Group), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False # Next, I hold down a shift key (left shift, as it happens). # # NB: The following *two* records correspond to this. First a # KeyRelease, then a KeyPress. KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76509510, (1362,764), root:(1364,766), state 0x2000, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False KeyPress event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76509510, (1362,764), root:(1364,766), state 0x2000, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False # Then, still holding down the shift key, I strike the key labeled "U" # on my standard US laptop keyboard. # # NB: The next *four* records correspond to this action. # # * The first two events are due to holding down the key labeled # "U" (KeyRelease event followed by KeyPress), # # * followed by a third, MAGICALLY INTERPOLATED, KeyPress event # (presumably due to the relevant entry in ~/.XCompose), # # * and wrapped up with the KeyRelease event corresponding to # releasing that "U" labeled key. KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76509907, (1362,764), root:(1364,766), state 0x2001, keycode 30 (keysym 0x1000d19, U0D19), same_screen YES, XLookupString gives 3 bytes: (e0 b4 99) "à´" XFilterEvent returns: False KeyPress event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76509907, (1362,764), root:(1364,766), state 0x2001, keycode 30 (keysym 0x1000d19, U0D19), same_screen YES, XLookupString gives 3 bytes: (e0 b4 99) "à´" XmbLookupString gives 3 bytes: (e0 b4 99) "à´" XFilterEvent returns: True # This one is THE MAGIC! KeyPress event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76509907, (1362,764), root:(1364,766), state 0x2001, keycode 0 (keysym 0x0, NoSymbol), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 9 bytes: (e0 b4 99 e0 b5 8d e0 b4 99) "à´àµà´" XFilterEvent returns: False KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76509998, (1362,764), root:(1364,766), state 0x2001, keycode 30 (keysym 0x1000d19, U0D19), same_screen YES, XLookupString gives 3 bytes: (e0 b4 99) "à´" XFilterEvent returns: False # And now I release the shift key. KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76510083, (1362,764), root:(1364,766), state 0x2001, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False # Having wrapped up my experiment (and happily observing the desired # character echoed to the terminal), I strike the capslock key to # return to a more familiar-to-me keymap. Three records, as per usual, # correspond to this action: Release, Press, Release. KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76514882, (1362,764), root:(1364,766), state 0x2000, keycode 66 (keysym 0xfe08, ISO_Next_Group), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False KeyPress event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76514882, (1362,764), root:(1364,766), state 0x2000, keycode 66 (keysym 0xfe08, ISO_Next_Group), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76514968, (1362,764), root:(1364,766), state 0x0, keycode 66 (keysym 0xfe08, ISO_Next_Group), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False # Here I hold down the control key, as a preliminary step to entering # Ctrl-c, to kill xev. Two events (Release followed by Press), # correspond to this preliminary action. The victim of the killing # strike --'c'-- has obviously failed to record its own assassination. KeyRelease event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76517083, (1362,764), root:(1364,766), state 0x0, keycode 37 (keysym 0xffe3, Control_L), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False KeyPress event, serial 21, synthetic NO, window 0x60000f, root 0x102, subw 0x0, time 76517083, (1362,764), root:(1364,766), state 0x0, keycode 37 (keysym 0xffe3, Control_L), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False