├── .gitignore ├── CMakeLists.txt ├── LICENSES └── GPL-2.0-or-later.txt ├── README.md ├── fcitx5-tmux.kdev4 ├── fcitx5.tmux.in └── src ├── CMakeLists.txt └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | .* 3 | !.git* 4 | .git/ 5 | *.tar.* 6 | *.kdev4 7 | *.kate-swp 8 | *.orig 9 | tags 10 | astyle.sh 11 | cscope.* 12 | *.part 13 | XF86keysym.h 14 | keysymdef.h 15 | *~ 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(fcitx5-tmux) 4 | 5 | include(FeatureSummary) 6 | include(GNUInstallDirs) 7 | 8 | find_package(Fcitx5Utils REQUIRED) 9 | 10 | include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake") 11 | 12 | add_subdirectory(src) 13 | 14 | configure_file(fcitx5.tmux.in fcitx5.tmux @ONLY) 15 | install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fcitx5.tmux DESTINATION ${CMAKE_INSTALL_DATADIR}/tmux-fcitx5) 16 | -------------------------------------------------------------------------------- /LICENSES/GPL-2.0-or-later.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | 3 | Version 2, June 1991 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 7 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this license 10 | document, but changing it is not allowed. 11 | 12 | Preamble 13 | 14 | The licenses for most software are designed to take away your freedom to share 15 | and change it. By contrast, the GNU General Public License is intended to 16 | guarantee your freedom to share and change free software--to make sure the 17 | software is free for all its users. This General Public License applies to 18 | most of the Free Software Foundation's software and to any other program whose 19 | authors commit to using it. (Some other Free Software Foundation software 20 | is covered by the GNU Lesser General Public License instead.) You can apply 21 | it to your programs, too. 22 | 23 | When we speak of free software, we are referring to freedom, not price. Our 24 | General Public Licenses are designed to make sure that you have the freedom 25 | to distribute copies of free software (and charge for this service if you 26 | wish), that you receive source code or can get it if you want it, that you 27 | can change the software or use pieces of it in new free programs; and that 28 | you know you can do these things. 29 | 30 | To protect your rights, we need to make restrictions that forbid anyone to 31 | deny you these rights or to ask you to surrender the rights. These restrictions 32 | translate to certain responsibilities for you if you distribute copies of 33 | the software, or if you modify it. 34 | 35 | For example, if you distribute copies of such a program, whether gratis or 36 | for a fee, you must give the recipients all the rights that you have. You 37 | must make sure that they, too, receive or can get the source code. And you 38 | must show them these terms so they know their rights. 39 | 40 | We protect your rights with two steps: (1) copyright the software, and (2) 41 | offer you this license which gives you legal permission to copy, distribute 42 | and/or modify the software. 43 | 44 | Also, for each author's protection and ours, we want to make certain that 45 | everyone understands that there is no warranty for this free software. If 46 | the software is modified by someone else and passed on, we want its recipients 47 | to know that what they have is not the original, so that any problems introduced 48 | by others will not reflect on the original authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software patents. We 51 | wish to avoid the danger that redistributors of a free program will individually 52 | obtain patent licenses, in effect making the program proprietary. To prevent 53 | this, we have made it clear that any patent must be licensed for everyone's 54 | free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and modification 57 | follow. 58 | 59 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 60 | 61 | 0. This License applies to any program or other work which contains a notice 62 | placed by the copyright holder saying it may be distributed under the terms 63 | of this General Public License. The "Program", below, refers to any such program 64 | or work, and a "work based on the Program" means either the Program or any 65 | derivative work under copyright law: that is to say, a work containing the 66 | Program or a portion of it, either verbatim or with modifications and/or translated 67 | into another language. (Hereinafter, translation is included without limitation 68 | in the term "modification".) Each licensee is addressed as "you". 69 | 70 | Activities other than copying, distribution and modification are not covered 71 | by this License; they are outside its scope. The act of running the Program 72 | is not restricted, and the output from the Program is covered only if its 73 | contents constitute a work based on the Program (independent of having been 74 | made by running the Program). Whether that is true depends on what the Program 75 | does. 76 | 77 | 1. You may copy and distribute verbatim copies of the Program's source code 78 | as you receive it, in any medium, provided that you conspicuously and appropriately 79 | publish on each copy an appropriate copyright notice and disclaimer of warranty; 80 | keep intact all the notices that refer to this License and to the absence 81 | of any warranty; and give any other recipients of the Program a copy of this 82 | License along with the Program. 83 | 84 | You may charge a fee for the physical act of transferring a copy, and you 85 | may at your option offer warranty protection in exchange for a fee. 86 | 87 | 2. You may modify your copy or copies of the Program or any portion of it, 88 | thus forming a work based on the Program, and copy and distribute such modifications 89 | or work under the terms of Section 1 above, provided that you also meet all 90 | of these conditions: 91 | 92 | a) You must cause the modified files to carry prominent notices stating that 93 | you changed the files and the date of any change. 94 | 95 | b) You must cause any work that you distribute or publish, that in whole or 96 | in part contains or is derived from the Program or any part thereof, to be 97 | licensed as a whole at no charge to all third parties under the terms of this 98 | License. 99 | 100 | c) If the modified program normally reads commands interactively when run, 101 | you must cause it, when started running for such interactive use in the most 102 | ordinary way, to print or display an announcement including an appropriate 103 | copyright notice and a notice that there is no warranty (or else, saying that 104 | you provide a warranty) and that users may redistribute the program under 105 | these conditions, and telling the user how to view a copy of this License. 106 | (Exception: if the Program itself is interactive but does not normally print 107 | such an announcement, your work based on the Program is not required to print 108 | an announcement.) 109 | 110 | These requirements apply to the modified work as a whole. If identifiable 111 | sections of that work are not derived from the Program, and can be reasonably 112 | considered independent and separate works in themselves, then this License, 113 | and its terms, do not apply to those sections when you distribute them as 114 | separate works. But when you distribute the same sections as part of a whole 115 | which is a work based on the Program, the distribution of the whole must be 116 | on the terms of this License, whose permissions for other licensees extend 117 | to the entire whole, and thus to each and every part regardless of who wrote 118 | it. 119 | 120 | Thus, it is not the intent of this section to claim rights or contest your 121 | rights to work written entirely by you; rather, the intent is to exercise 122 | the right to control the distribution of derivative or collective works based 123 | on the Program. 124 | 125 | In addition, mere aggregation of another work not based on the Program with 126 | the Program (or with a work based on the Program) on a volume of a storage 127 | or distribution medium does not bring the other work under the scope of this 128 | License. 129 | 130 | 3. You may copy and distribute the Program (or a work based on it, under Section 131 | 2) in object code or executable form under the terms of Sections 1 and 2 above 132 | provided that you also do one of the following: 133 | 134 | a) Accompany it with the complete corresponding machine-readable source code, 135 | which must be distributed under the terms of Sections 1 and 2 above on a medium 136 | customarily used for software interchange; or, 137 | 138 | b) Accompany it with a written offer, valid for at least three years, to give 139 | any third party, for a charge no more than your cost of physically performing 140 | source distribution, a complete machine-readable copy of the corresponding 141 | source code, to be distributed under the terms of Sections 1 and 2 above on 142 | a medium customarily used for software interchange; or, 143 | 144 | c) Accompany it with the information you received as to the offer to distribute 145 | corresponding source code. (This alternative is allowed only for noncommercial 146 | distribution and only if you received the program in object code or executable 147 | form with such an offer, in accord with Subsection b above.) 148 | 149 | The source code for a work means the preferred form of the work for making 150 | modifications to it. For an executable work, complete source code means all 151 | the source code for all modules it contains, plus any associated interface 152 | definition files, plus the scripts used to control compilation and installation 153 | of the executable. However, as a special exception, the source code distributed 154 | need not include anything that is normally distributed (in either source or 155 | binary form) with the major components (compiler, kernel, and so on) of the 156 | operating system on which the executable runs, unless that component itself 157 | accompanies the executable. 158 | 159 | If distribution of executable or object code is made by offering access to 160 | copy from a designated place, then offering equivalent access to copy the 161 | source code from the same place counts as distribution of the source code, 162 | even though third parties are not compelled to copy the source along with 163 | the object code. 164 | 165 | 4. You may not copy, modify, sublicense, or distribute the Program except 166 | as expressly provided under this License. Any attempt otherwise to copy, modify, 167 | sublicense or distribute the Program is void, and will automatically terminate 168 | your rights under this License. However, parties who have received copies, 169 | or rights, from you under this License will not have their licenses terminated 170 | so long as such parties remain in full compliance. 171 | 172 | 5. You are not required to accept this License, since you have not signed 173 | it. However, nothing else grants you permission to modify or distribute the 174 | Program or its derivative works. These actions are prohibited by law if you 175 | do not accept this License. Therefore, by modifying or distributing the Program 176 | (or any work based on the Program), you indicate your acceptance of this License 177 | to do so, and all its terms and conditions for copying, distributing or modifying 178 | the Program or works based on it. 179 | 180 | 6. Each time you redistribute the Program (or any work based on the Program), 181 | the recipient automatically receives a license from the original licensor 182 | to copy, distribute or modify the Program subject to these terms and conditions. 183 | You may not impose any further restrictions on the recipients' exercise of 184 | the rights granted herein. You are not responsible for enforcing compliance 185 | by third parties to this License. 186 | 187 | 7. If, as a consequence of a court judgment or allegation of patent infringement 188 | or for any other reason (not limited to patent issues), conditions are imposed 189 | on you (whether by court order, agreement or otherwise) that contradict the 190 | conditions of this License, they do not excuse you from the conditions of 191 | this License. If you cannot distribute so as to satisfy simultaneously your 192 | obligations under this License and any other pertinent obligations, then as 193 | a consequence you may not distribute the Program at all. For example, if a 194 | patent license would not permit royalty-free redistribution of the Program 195 | by all those who receive copies directly or indirectly through you, then the 196 | only way you could satisfy both it and this License would be to refrain entirely 197 | from distribution of the Program. 198 | 199 | If any portion of this section is held invalid or unenforceable under any 200 | particular circumstance, the balance of the section is intended to apply and 201 | the section as a whole is intended to apply in other circumstances. 202 | 203 | It is not the purpose of this section to induce you to infringe any patents 204 | or other property right claims or to contest validity of any such claims; 205 | this section has the sole purpose of protecting the integrity of the free 206 | software distribution system, which is implemented by public license practices. 207 | Many people have made generous contributions to the wide range of software 208 | distributed through that system in reliance on consistent application of that 209 | system; it is up to the author/donor to decide if he or she is willing to 210 | distribute software through any other system and a licensee cannot impose 211 | that choice. 212 | 213 | This section is intended to make thoroughly clear what is believed to be a 214 | consequence of the rest of this License. 215 | 216 | 8. If the distribution and/or use of the Program is restricted in certain 217 | countries either by patents or by copyrighted interfaces, the original copyright 218 | holder who places the Program under this License may add an explicit geographical 219 | distribution limitation excluding those countries, so that distribution is 220 | permitted only in or among countries not thus excluded. In such case, this 221 | License incorporates the limitation as if written in the body of this License. 222 | 223 | 9. The Free Software Foundation may publish revised and/or new versions of 224 | the General Public License from time to time. Such new versions will be similar 225 | in spirit to the present version, but may differ in detail to address new 226 | problems or concerns. 227 | 228 | Each version is given a distinguishing version number. If the Program specifies 229 | a version number of this License which applies to it and "any later version", 230 | you have the option of following the terms and conditions either of that version 231 | or of any later version published by the Free Software Foundation. If the 232 | Program does not specify a version number of this License, you may choose 233 | any version ever published by the Free Software Foundation. 234 | 235 | 10. If you wish to incorporate parts of the Program into other free programs 236 | whose distribution conditions are different, write to the author to ask for 237 | permission. For software which is copyrighted by the Free Software Foundation, 238 | write to the Free Software Foundation; we sometimes make exceptions for this. 239 | Our decision will be guided by the two goals of preserving the free status 240 | of all derivatives of our free software and of promoting the sharing and reuse 241 | of software generally. 242 | 243 | NO WARRANTY 244 | 245 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 246 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 247 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM 248 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 249 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 250 | FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 251 | OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 252 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 253 | 254 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 255 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 256 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 257 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE 258 | OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA 259 | OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES 260 | OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH 261 | HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 262 | END OF TERMS AND CONDITIONS 263 | 264 | How to Apply These Terms to Your New Programs 265 | 266 | If you develop a new program, and you want it to be of the greatest possible 267 | use to the public, the best way to achieve this is to make it free software 268 | which everyone can redistribute and change under these terms. 269 | 270 | To do so, attach the following notices to the program. It is safest to attach 271 | them to the start of each source file to most effectively convey the exclusion 272 | of warranty; and each file should have at least the "copyright" line and a 273 | pointer to where the full notice is found. 274 | 275 | 276 | 277 | Copyright (C) 278 | 279 | This program is free software; you can redistribute it and/or modify it under 280 | the terms of the GNU General Public License as published by the Free Software 281 | Foundation; either version 2 of the License, or (at your option) any later 282 | version. 283 | 284 | This program is distributed in the hope that it will be useful, but WITHOUT 285 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 286 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 287 | 288 | You should have received a copy of the GNU General Public License along with 289 | this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 290 | Street, Fifth Floor, Boston, MA 02110-1301, USA. 291 | 292 | Also add information on how to contact you by electronic and paper mail. 293 | 294 | If the program is interactive, make it output a short notice like this when 295 | it starts in an interactive mode: 296 | 297 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 298 | with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, 299 | and you are welcome to redistribute it under certain conditions; type `show 300 | c' for details. 301 | 302 | The hypothetical commands `show w' and `show c' should show the appropriate 303 | parts of the General Public License. Of course, the commands you use may be 304 | called something other than `show w' and `show c'; they could even be mouse-clicks 305 | or menu items--whatever suits your program. 306 | 307 | You should also get your employer (if you work as a programmer) or your school, 308 | if any, to sign a "copyright disclaimer" for the program, if necessary. Here 309 | is a sample; alter the names: 310 | 311 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' 312 | (which makes passes at compilers) written by James Hacker. 313 | 314 | , 1 April 1989 Ty Coon, President of Vice This General 315 | Public License does not permit incorporating your program into proprietary 316 | programs. If your program is a subroutine library, you may consider it more 317 | useful to permit linking proprietary applications with the library. If this 318 | is what you want to do, use the GNU Lesser General Public License instead 319 | of this License. 320 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A Tmux Fcitx client 2 | ==================== 3 | 4 | What this project is: 5 | 6 | Make tmux a fcitx client. It allows you to type with input method without using graphical display server. For example, you can type with Fcitx under TTY. 7 | 8 | What this project is not: 9 | 10 | Display fcitx state in tmux. That is not the goal of this project. 11 | 12 | Dependenices: 13 | 14 | 1. Need fcitx5 to compile 15 | 2. Need dbus-send to run. 16 | 17 | Compile: 18 | ``` 19 | mkdir build 20 | cd build 21 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr 22 | cmake --build . 23 | sudo cmake --install . 24 | ``` 25 | 26 | Usage: 27 | Add following content to ~/.tmux.conf 28 | ``` 29 | run /usr/share/tmux-fcitx5/fcitx5.tmux 30 | ``` 31 | 32 | Known issue: 33 | 34 | Not all key combinition works, specially for modifier key only hotkey and key release event due to Tmux bind-key limitation. 35 | Trigger key (Control+Space) repeatedly press functionality does not work properly. So you will see Control+space enumerate over different input method if you have more than one input method configured. 36 | 37 | Keyboard layout related feature won't work too, which is expected. 38 | -------------------------------------------------------------------------------- /fcitx5-tmux.kdev4: -------------------------------------------------------------------------------- 1 | [Project] 2 | Name=fcitx5-tmux 3 | Manager=KDevCMakeManager 4 | -------------------------------------------------------------------------------- /fcitx5.tmux.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | 5 | fcitx5_interpolation="\#{fcitx5}" 6 | 7 | set_tmux_option() { 8 | local option=$1 9 | local value=$2 10 | tmux set-option -gq "$option" "$value" 11 | } 12 | 13 | get_tmux_option() { 14 | local option 15 | local default_value 16 | local option_value 17 | option="$1" 18 | default_value="$2" 19 | option_value="$(tmux show-option -qv "$option")" 20 | if [ -z "$option_value" ]; then 21 | option_value="$(tmux show-option -gqv "$option")" 22 | fi 23 | if [ -z "$option_value" ]; then 24 | echo "$default_value" 25 | else 26 | echo "$option_value" 27 | fi 28 | } 29 | 30 | do_interpolation() { 31 | local all_interpolated="$1" 32 | value=$(get_tmux_option "@fcitx5") 33 | all_interpolated=${all_interpolated//${fcitx5_interpolation}/${value}} 34 | echo "$all_interpolated" 35 | } 36 | 37 | update_tmux_option() { 38 | local option 39 | local option_value 40 | local new_option_value 41 | option=$1 42 | option_value=$(get_tmux_option "$option") 43 | new_option_value=$(do_interpolation "$option_value") 44 | set_tmux_option "$option" "$new_option_value" 45 | } 46 | 47 | to_ascii() { 48 | LC_CTYPE=C printf "%d" "'$1'" 49 | } 50 | 51 | main() { 52 | pid=$(tmux display-message -pF '#{pid}') 53 | tmux run -b "@CMAKE_INSTALL_FULL_LIBEXECDIR@/fcitx5-tmux $pid" 54 | declare -A KEYS=( 55 | [F1]=0xffbe 56 | [F2]=0xffbf 57 | [F3]=0xffc0 58 | [F4]=0xffc1 59 | [F5]=0xffc2 60 | [F6]=0xffc3 61 | [F7]=0xffc4 62 | [F8]=0xffc5 63 | [F9]=0xffc6 64 | [F10]=0xffc7 65 | [F11]=0xffc8 66 | [F12]=0xffc9 67 | [Insert]=0xff63 68 | [Delete]=0xffff 69 | [Home]=0xff50 70 | [End]=0xff57 71 | [PageDown]=0xff56 72 | [PageUp]=0xff55 73 | [Tab]=0xff09 74 | [Space]=0x0020 75 | [BSpace]=0xff08 76 | [Enter]=0xff0d 77 | [Escape]=0xff1b 78 | [Up]=0xff52 79 | [Down]=0xff54 80 | [Left]=0xff51 81 | [Right]=0xff53 82 | [KP/]=0xffaf 83 | [KP*]=0xffaa 84 | [KP-]=0xffad 85 | [KP7]=0xffb7 86 | [KP8]=0xffb8 87 | [KP9]=0xffb9 88 | [KP+]=0xffab 89 | [KP4]=0xffb4 90 | [KP5]=0xffb5 91 | [KP6]=0xffb6 92 | [KP1]=0xffb1 93 | [KP2]=0xffb2 94 | [KP3]=0xffb3 95 | [KPEnter]=0xff8d 96 | [KP0]=0xffb0 97 | [KP.]=0xffae 98 | ) 99 | for KEY in a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 0 , . / "\\;" "'" "[" "]" "-" "=" "!" "@" "#" "^" "(" ")" "_" "+"; do 100 | VALUE=$(to_ascii "$KEY") 101 | tmux bind-key -n "$KEY" run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:0" 102 | tmux bind-key -n "C-$KEY" run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:4" 103 | tmux bind-key -n "M-$KEY" run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:8" 104 | tmux bind-key -n "M-C-$KEY" run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:12" 105 | done 106 | for KEY in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "\"" "$" "%" "*" "&"; do 107 | VALUE=$(to_ascii "$KEY") 108 | tmux bind-key -n "$KEY" run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:0" 109 | done 110 | 111 | for KEY in "${!KEYS[@]}"; do 112 | VALUE=${KEYS[$KEY]} 113 | tmux bind-key -n ${KEY} run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:0" 114 | tmux bind-key -n C-${KEY} run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:4" 115 | tmux bind-key -n M-${KEY} run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:8" 116 | tmux bind-key -n M-C-${KEY} run "dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5.Tmux-$pid /tmux org.fcitx.Fcitx.TmuxProxy.ProcessKeyEvent uint32:$VALUE uint32:12" 117 | done 118 | } 119 | main 120 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(fcitx5-tmux main.cpp) 3 | target_link_libraries(fcitx5-tmux Fcitx5::Utils) 4 | 5 | install(TARGETS fcitx5-tmux RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}) 6 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2022~2022 CSSlayer 3 | * 4 | * SPDX-License-Identifier: GPL-2.0-or-later 5 | * 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace fcitx; 15 | 16 | std::string toTmuxKey(const Key &key) { 17 | std::string result; 18 | if (key.states() & KeyState::Alt) { 19 | result.append("M-"); 20 | } 21 | if (key.states() & KeyState::Ctrl) { 22 | result.append("C-"); 23 | } 24 | 25 | const static std::unordered_map tmuxKeymap = { 26 | {FcitxKey_F1, "F1"}, 27 | {FcitxKey_F2, "F2"}, 28 | {FcitxKey_F3, "F3"}, 29 | {FcitxKey_F4, "F4"}, 30 | {FcitxKey_F5, "F5"}, 31 | {FcitxKey_F6, "F6"}, 32 | {FcitxKey_F7, "F7"}, 33 | {FcitxKey_F8, "F8"}, 34 | {FcitxKey_F9, "F9"}, 35 | {FcitxKey_F10, "F10"}, 36 | {FcitxKey_F11, "F11"}, 37 | {FcitxKey_F12, "F12"}, 38 | {FcitxKey_Insert, "Insert"}, 39 | {FcitxKey_Delete, "Delete"}, 40 | {FcitxKey_Home, "Home"}, 41 | {FcitxKey_End, "End"}, 42 | {FcitxKey_Page_Down, "PageDown"}, 43 | {FcitxKey_Page_Up, "PageUp"}, 44 | {FcitxKey_Tab, "Tab"}, 45 | // {FcitxKey_BTab, ""}, 46 | {FcitxKey_space, "Space"}, 47 | {FcitxKey_BackSpace, "BSpace"}, 48 | {FcitxKey_Return, "Enter"}, 49 | {FcitxKey_Escape, "Escape"}, 50 | {FcitxKey_Up, "Up"}, 51 | {FcitxKey_Down, "Down"}, 52 | {FcitxKey_Left, "Left"}, 53 | {FcitxKey_Right, "Right"}, 54 | {FcitxKey_KP_Divide, "KP/"}, 55 | {FcitxKey_KP_Multiply, "KP*"}, 56 | {FcitxKey_KP_Subtract, "KP-"}, 57 | {FcitxKey_KP_7, "KP7"}, 58 | {FcitxKey_KP_8, "KP8"}, 59 | {FcitxKey_KP_9, "KP9"}, 60 | {FcitxKey_KP_Add, "KP+"}, 61 | {FcitxKey_KP_4, "KP4"}, 62 | {FcitxKey_KP_5, "KP5"}, 63 | {FcitxKey_KP_6, "KP6"}, 64 | {FcitxKey_KP_1, "KP1"}, 65 | {FcitxKey_KP_2, "KP2"}, 66 | {FcitxKey_KP_3, "KP3"}, 67 | {FcitxKey_KP_Enter, "KPEnter"}, 68 | {FcitxKey_KP_0, "KP0"}, 69 | {FcitxKey_KP_Decimal, "KP."}, 70 | }; 71 | 72 | if (auto iter = tmuxKeymap.find(key.sym()); iter != tmuxKeymap.end()) { 73 | result.append(iter->second); 74 | } else if (key.sym() >= FcitxKey_space && 75 | key.sym() <= FcitxKey_asciitilde) { 76 | result.push_back(static_cast(key.sym())); 77 | } else { 78 | return ""; 79 | } 80 | return result; 81 | } 82 | 83 | class InputContextProxy : public dbus::ObjectVTable { 84 | public: 85 | InputContextProxy(dbus::Bus &bus) : bus_(bus), watcher_(bus) { 86 | handler_ = watcher_.watchService("org.freedesktop.portal.Fcitx", 87 | [this](const std::string &, 88 | const std::string &oldName, 89 | const std::string &newName) { 90 | if (!oldName.empty()) { 91 | reset(); 92 | } 93 | if (!newName.empty()) { 94 | init(); 95 | } 96 | }); 97 | bus_.addObjectVTable("/tmux", "org.fcitx.Fcitx.TmuxProxy", *this); 98 | } 99 | 100 | void reset() { 101 | path_ = dbus::ObjectPath(); 102 | createIC_.reset(); 103 | } 104 | void init() { 105 | auto call = bus_.createMethodCall("org.freedesktop.portal.Fcitx", 106 | "/org/freedesktop/portal/inputmethod", 107 | "org.fcitx.Fcitx.InputMethod1", 108 | "CreateInputContext"); 109 | std::vector> params; 110 | params.emplace_back("program", "tmux"); 111 | params.emplace_back("display", "tmux"); 112 | call << params; 113 | createIC_ = call.callAsync(0, [this](dbus::Message &reply) { 114 | if (reply.isError()) { 115 | return true; 116 | } 117 | // Skip UUID; 118 | reply >> path_; 119 | bus_.createMethodCall("org.freedesktop.portal.Fcitx", 120 | path_.path().c_str(), 121 | "org.fcitx.Fcitx.InputContext1", "FocusIn") 122 | .send(); 123 | auto cap = bus_.createMethodCall( 124 | "org.freedesktop.portal.Fcitx", path_.path().c_str(), 125 | "org.fcitx.Fcitx.InputContext1", "SetCapability"); 126 | cap << static_cast( 127 | CapabilityFlags(CapabilityFlag::ClientSideInputPanel)); 128 | cap.send(); 129 | signals_.push_back(bus_.addMatch( 130 | dbus::MatchRule("org.freedesktop.portal.Fcitx", path_.path(), 131 | "org.fcitx.Fcitx.InputContext1", 132 | "CommitString"), 133 | [](dbus::Message &msg) { 134 | if (msg.isError()) { 135 | return true; 136 | } 137 | std::string text; 138 | msg >> text; 139 | startProcess({"tmux", "send-key", "-l", text}); 140 | return true; 141 | })); 142 | signals_.push_back(bus_.addMatch( 143 | dbus::MatchRule("org.freedesktop.portal.Fcitx", path_.path(), 144 | "org.fcitx.Fcitx.InputContext1", 145 | "UpdateClientSideUI"), 146 | [](dbus::Message &msg) { 147 | // a(si)ia(si)a(si)a(ss)iibb 148 | if (msg.isError()) { 149 | return true; 150 | } 151 | std::vector> 152 | preeditStrings, auxUpStrings, auxDownStrings; 153 | std::vector> 154 | candidates; 155 | int cursorIndex; 156 | int cursor; 157 | msg >> preeditStrings >> cursor >> auxUpStrings >> 158 | auxDownStrings >> candidates >> cursorIndex; 159 | std::string result; 160 | for (auto &item : auxUpStrings) { 161 | result.append(std::get(item)); 162 | } 163 | if (cursor >= 0) { 164 | cursor += result.size(); 165 | } 166 | for (auto &item : preeditStrings) { 167 | result.append(std::get(item)); 168 | } 169 | if (cursor >= 0) { 170 | result.insert(cursor, "|"); 171 | } 172 | result.append(" "); 173 | int idx = 0; 174 | for (auto &item : candidates) { 175 | if (idx == cursorIndex) { 176 | result.append("*"); 177 | } 178 | result.append(std::get<0>(item)); 179 | result.append(std::get<1>(item)); 180 | result.append(" "); 181 | idx += 1; 182 | } 183 | startProcess( 184 | {"tmux", "set-option", "-gq", "@fcitx5", result}); 185 | return true; 186 | })); 187 | signals_.push_back(bus_.addMatch( 188 | dbus::MatchRule("org.freedesktop.portal.Fcitx", path_.path(), 189 | "org.fcitx.Fcitx.InputContext1", "ForwardKey"), 190 | [](dbus::Message &msg) { 191 | if (msg.isError()) { 192 | return true; 193 | } 194 | uint32_t sym, states; 195 | bool isRelease; 196 | msg >> sym >> states >> isRelease; 197 | if (!isRelease) { 198 | Key key(static_cast(sym), KeyStates(states)); 199 | auto keyStr = toTmuxKey(key); 200 | if (!keyStr.empty()) { 201 | startProcess({"tmux", "send-key", keyStr}); 202 | } else { 203 | if (!key.states().test(KeyState::Ctrl)) { 204 | if (auto str = Key::keySymToUTF8(key.sym()); 205 | !str.empty()) { 206 | startProcess( 207 | {"tmux", "send-key", "-l", str}); 208 | } 209 | } 210 | } 211 | } 212 | return true; 213 | })); 214 | createIC_.reset(); 215 | return true; 216 | }); 217 | } 218 | 219 | void processKeyEvent(uint32_t sym, uint32_t state) { 220 | auto call = bus_.createMethodCall( 221 | "org.freedesktop.portal.Fcitx", path_.path().c_str(), 222 | "org.fcitx.Fcitx.InputContext1", "ProcessKeyEvent"); 223 | call << sym << 0u << state << false << 0u; 224 | auto reply = call.call(0); 225 | Key key{KeySym(sym), KeyStates(state)}; 226 | bool handled = false; 227 | if (!reply.isError()) { 228 | reply >> handled; 229 | } 230 | 231 | if (!handled) { 232 | auto keyStr = toTmuxKey(key); 233 | if (!keyStr.empty()) { 234 | startProcess({"tmux", "send-key", keyStr}); 235 | } 236 | } 237 | } 238 | 239 | private: 240 | FCITX_OBJECT_VTABLE_METHOD(processKeyEvent, "ProcessKeyEvent", "uu", ""); 241 | 242 | dbus::Bus &bus_; 243 | dbus::ObjectPath path_; 244 | dbus::ServiceWatcher watcher_; 245 | std::unique_ptr> handler_; 246 | std::unique_ptr createIC_; 247 | std::vector> signals_; 248 | }; 249 | 250 | int main(int argc, char *argv[]) { 251 | if (argc != 2) { 252 | return 1; 253 | } 254 | 255 | UnixFD fd; 256 | pid_t tmuxPid = 0; 257 | try { 258 | tmuxPid = std::stoi(argv[1]); 259 | fd = UnixFD::own(syscall(SYS_pidfd_open, tmuxPid, 0)); 260 | } catch (...) { 261 | return 1; 262 | } 263 | 264 | if (!fd.isValid()) { 265 | return 1; 266 | } 267 | 268 | EventLoop event; 269 | dbus::Bus bus{dbus::BusType::Session}; 270 | bus.attachEventLoop(&event); 271 | 272 | if (!bus.requestName("org.fcitx.Fcitx5.Tmux-" + std::to_string(tmuxPid), 273 | Flags{})) { 274 | return 1; 275 | } 276 | 277 | auto ioEvent = 278 | event.addIOEvent(fd.fd(), {IOEventFlag::In, IOEventFlag::Err}, 279 | [&event](EventSource *, int, IOEventFlags) { 280 | event.exit(); 281 | return true; 282 | }); 283 | 284 | InputContextProxy proxy(bus); 285 | 286 | event.exec(); 287 | return 0; 288 | } 289 | --------------------------------------------------------------------------------