<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ying xu</title>
    <description>The latest articles on DEV Community by ying xu (@ying_xu_0f8a58231902eb42b).</description>
    <link>https://dev.to/ying_xu_0f8a58231902eb42b</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4043455%2Fa1b9cc73-0046-4723-b2b6-24d100edfbd5.png</url>
      <title>DEV Community: ying xu</title>
      <link>https://dev.to/ying_xu_0f8a58231902eb42b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ying_xu_0f8a58231902eb42b"/>
    <language>en</language>
    <item>
      <title>A step-by-step guide to running Linux applications on your Android desktop</title>
      <dc:creator>ying xu</dc:creator>
      <pubDate>Wed, 26 Aug 2026 07:17:12 +0000</pubDate>
      <link>https://dev.to/ying_xu_0f8a58231902eb42b/a-step-by-step-guide-to-running-linux-applications-on-your-android-desktop-53f4</link>
      <guid>https://dev.to/ying_xu_0f8a58231902eb42b/a-step-by-step-guide-to-running-linux-applications-on-your-android-desktop-53f4</guid>
      <description>&lt;p&gt;As those who read our previous article likely know, OpenFDE is a Linux desktop environment built upon the AOSP graphics stack. While it is essentially an Android-based desktop, we have adapted the display and control mechanisms for Linux applications within the AOSP environment. This enables the seamless, integrated display of both Linux and Android applications, allowing users to run programs from different platforms—and leverage their respective software ecosystems—without even noticing the underlying differences. The result is OpenFDE: a Linux desktop environment that integrates seamlessly with Android.&lt;/p&gt;

&lt;p&gt;Of course, this technology is not limited to the OpenFDE desktop; it can also be applied to other Android systems, such as smartphones and tablets. Let’s take a look at the underlying principles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explanation of Principles
&lt;/h3&gt;

&lt;p&gt;Our concept for OpenFDE involves providing an entry point to launch Linux applications directly; therefore, after careful consideration, we initially adopted VNC technology to handle Linux input and output management.&lt;/p&gt;

&lt;p&gt;VNC (Virtual Network Computing) is essentially a remote desktop control technology that enables users to access and control other computers remotely via the Internet or a local area network (LAN). It works by running a VNC server program on the remote computer; the user connects to this server using a VNC client program, allowing them to view and interact with the remote computer's desktop from their own machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7pb54x1xaqre8fvpu93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7pb54x1xaqre8fvpu93.png" alt="principle" width="799" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The core principle involves the VNC app communicating with the VNC server via the RFB protocol; mouse and keyboard actions are transmitted to the VNC server as input. The VNC server utilizes the Xvnc module to implement the X protocol and communicate with Linux applications, while simultaneously forwarding text (in both Chinese and English) generated by the app to the IBus input method framework for processing (the specifics of the input method mechanism will be detailed later). Keyboard and mouse inputs are passed directly to the Linux application, which returns display data to the VNC server; finally, the display frames are sent back to the app as output.&lt;/p&gt;

&lt;p&gt;It is worth noting that VNC relies on the RFB (Remote Frame Buffer) protocol for screen sharing between the client and server. It operates by rendering images locally and then transmitting them remotely to the client for decoding and display; this architecture ensures platform independence and cross-platform compatibility.&lt;/p&gt;

&lt;p&gt;We selected this solution due to the technology's maturity and short development cycle, which allow for rapid requirement implementation and a product-grade user experience. However, there are drawbacks: VNC is primarily designed for remote control and imposes specific demands regarding efficiency and bandwidth (though latency remains within a few frames when running locally), and there are certain limitations regarding user interaction.&lt;/p&gt;

&lt;p&gt;Let me now explain the key points in detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  VNC server and client
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Resulting Effect
&lt;/h4&gt;

&lt;p&gt;When using the TigerVNC server, the actual parameters used to start &lt;code&gt;vnc server&lt;/code&gt; after installation via &lt;code&gt;apt&lt;/code&gt; are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/bin/Xtigervnc :2 &lt;span class="nt"&gt;-desktop&lt;/span&gt; firefox_web_browser &lt;span class="nt"&gt;-auth&lt;/span&gt; /home/warlice/.Xauthority &lt;span class="nt"&gt;-geometry&lt;/span&gt; 1900x1200 &lt;span class="nt"&gt;-depth&lt;/span&gt; 24 &lt;span class="nt"&gt;-rfbwait&lt;/span&gt; 30000 &lt;span class="nt"&gt;-rfbport&lt;/span&gt; 5902 &lt;span class="nt"&gt;-pn&lt;/span&gt; &lt;span class="nt"&gt;-SecurityTypes&lt;/span&gt; None &lt;span class="nt"&gt;-BlacklistThreshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10000000 &lt;span class="nt"&gt;-BlacklistTimeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;firefox_web_browser The script content is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
fde-set-ime-engine firefox_web_browser &amp;amp;   &lt;span class="c"&gt;# Start input method, explained later&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GDK_BACKEND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x11
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;QT_QPA_PLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xcb
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GTK_IM_MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ibus
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;QT_IM_MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ibus
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;QT4_IM_MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ibus
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;im&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ibus
firefox &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The details regarding the input method are described later; here, the startup parameter configuration for &lt;code&gt;vncserver&lt;/code&gt; specifies the layout dimensions (width and height), port, and connection timeout. Additionally, it specifies the startup program—Firefox—which is the application the user intends to launch and use. On OpenFDE, the implementation logic is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  List all available programs on the Linux system and provide launch entry points.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1k775luf4ixxq5d1lcrf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1k775luf4ixxq5d1lcrf.png" alt="example" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  When the user clicks the Linux program icon:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, start the &lt;code&gt;vncserver&lt;/code&gt; using the parameters mentioned above and launch the corresponding Linux application. Once startup is complete, open a new window within the FDE Android application and establish a VNC connection to display the frame data transmitted via the RFB protocol, while simultaneously sending input events—such as keyboard and mouse actions—to the server in accordance with the protocol.&lt;/p&gt;

&lt;p&gt;This provides a seamless user experience, just like launching a Linux program directly on Android.&lt;/p&gt;

&lt;h4&gt;
  
  
  Source Code Solution
&lt;/h4&gt;

&lt;p&gt;When chaining these functions together, modifications inevitably become necessary, requiring customization. After extensive comparative research, the project source code used is as follows.&lt;/p&gt;

&lt;p&gt;APP: &lt;a href="https://github.com/iiordanov/remote-desktop-clients" rel="noopener noreferrer"&gt;GitHub - iiordanov/remote-desktop-clients: VNC, RDP, SPICE, and oVirt/RHEV/Proxmox Clients for Android and Blackberry 10&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project involves the source code for bVNC, aRDP, aSPICE, and Opaque—four Android remote desktop clients. We are using the bVNC module; although there is a paid "Pro" version available, the documentation indicates that it merely adds encryption capabilities.&lt;/p&gt;

&lt;p&gt;Server: &lt;a href="https://github.com/TigerVNC/tigervnc" rel="noopener noreferrer"&gt;GitHub - TigerVNC/tigervnc: High performance, multi-platform VNC client and server&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source code handling on the server side is somewhat more complex. The server must manage Linux applications and handle actual input and output—specifically, the graphical interfaces of these Linux programs. It utilizes the Xorg X server; VNC Server invokes Xorg via platform-specific libraries (such as Xvnc) and, following its own internal logic, converts the data into the RFB protocol for interaction with the client.&lt;/p&gt;

&lt;p&gt;When compiling libraries on Linux that depend on xorg-xserver, you can compile &lt;em&gt;x0vncserver&lt;/em&gt; if you only need to perform debugging.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;x0vncserver - an inefficient VNC server which continuously polls any X&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;He cannot use all &lt;code&gt;vncserver&lt;/code&gt; startup parameters; the result of launching it is a desktop output. You can refer to&lt;a href="https://github.com/TigerVNC/tigervnc" rel="noopener noreferrer"&gt;GitHub - TigerVNC/tigervnc: High performance, multi-platform VNC client and server&lt;/a&gt; to explore the implementation logic of the server.&lt;/p&gt;

&lt;h4&gt;
  
  
  Compiling TigerVNC Server
&lt;/h4&gt;

&lt;p&gt;FDE uses the apt source code to modify and compile the TigerVNCServer deb package. The specific steps are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Open /etc/apt/sources.list and uncomment all deb-src entries.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;source &lt;/span&gt;tigervnc-standalone-server
// After entering the &lt;span class="nb"&gt;source &lt;/span&gt;code directory
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;equivs devscripts &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mk-build-deps &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"apt-get"&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;DEB_BUILD_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"parallel=8"&lt;/span&gt; dpkg-buildpackage &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;-uc&lt;/span&gt; &lt;span class="nt"&gt;-us&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source code is managed using the quilt tool. For specific usage, please refer to:&lt;/p&gt;

&lt;p&gt;[&lt;strong&gt;quilt Tool Usage Guide&lt;/strong&gt;]  &lt;a href="https://openfde.atlassian.net/wiki/spaces/OpenFDE1/pages/15138835" rel="noopener noreferrer"&gt;f. quilt Tool Usage Guide&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;After successful compilation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt;  tigervnc-standalone-server_1.10.1+dfsg-3_arm64.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Input Processing and Input Methods
&lt;/h4&gt;

&lt;p&gt;After starting the server and client and establishing a connection, communication follows the RFB protocol. The output is frame data, and what frame data to display is determined by the server side, which basically does not need to be modified. Most of the client's business logic is focused on handling input, i.e., mouse and keyboard events. The simplest approach is to pass all events directly to the server side in the activity's dispatchKeyEvent, which is how bVNC handles it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    @Override
    public boolean dispatchKeyEvent&lt;span class="o"&gt;(&lt;/span&gt;KeyEvent event&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;canvas.getKeyboard&lt;span class="o"&gt;()&lt;/span&gt;.keyEvent&lt;span class="o"&gt;(&lt;/span&gt;event.getKeyCode&lt;span class="o"&gt;()&lt;/span&gt;, event&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send data packets according to the message format:&lt;/p&gt;

&lt;p&gt;Protocol message - mouse event message is as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  +--------------+--------------+--------------+
  | No. of bytes | Type &lt;span class="o"&gt;[&lt;/span&gt;Value] | Description  |
  +--------------+--------------+--------------+
  | 1            | U8 &lt;span class="o"&gt;[&lt;/span&gt;5]       | message-type |
  | 1            | U8           | button-mask  |
  | 2            | U16          | x-position   |
  | 2            | U16          | y-position   |
  +--------------+--------------+--------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Protocol message - keyboard event message is as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  +--------------+--------------+--------------+
  | No. of bytes | Type &lt;span class="o"&gt;[&lt;/span&gt;Value] | Description  |
  +--------------+--------------+--------------+
  | 1            | U8 &lt;span class="o"&gt;[&lt;/span&gt;4]       | message-type |
  | 1            | U8           | down-flag    |
  | 2            |              | padding      |
  | 4            | U32          | key          |
  +--------------+--------------+--------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For details, please refer to&lt;/p&gt;

&lt;p&gt;[&lt;strong&gt;RFB Protocol Documentation&lt;/strong&gt;]  &lt;a href="https://www.babeler.cn/transfer/input/" rel="noopener noreferrer"&gt;Input Protocol · RFB Remote Framebuffer Protocol&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It should be noted whether the keyboard keysym used by Android is consistent with the keyboard layout recognized by Linux, that is, whether the keysym of sent events can be correctly recognized on the server side.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Input Method Requirements
&lt;/h4&gt;

&lt;p&gt;In the use of VNC, there is one requirement that has never been well resolved: text input. English characters are relatively easy to handle—you can directly use key presses to output the desired characters. However, for Chinese characters and other characters in the unicode character set, Linux cannot directly generate them. It has been verified that Linux can receive a small number of Chinese characters in xkeysyms; you can bind a Chinese character to a keyboard key, but the number is also limited by the number of keyboard keys.&lt;/p&gt;

&lt;p&gt;Therefore, if you want to input Chinese, the best approach is to add /usr/bin/ibus-daemon -d &amp;amp; to the startup script and then set the input method to the desired one. However, this also has a problem: the input method running on Linux is disconnected from the FDE desktop usage. How to handle the installation and uninstallation of input methods on Linux? Additionally, the display of the candidate word box is also an issue—if compatibility is not handled well, it will not be displayed in VNC.&lt;/p&gt;

&lt;p&gt;For these reasons, FDE's VNC application integration solution adopts a more complete implementation. Modifications have been made to each stage of the input chain, though it ends at the ibus input method; X programs only serve as the text-receiving party.&lt;/p&gt;

&lt;p&gt;The result is that using the Android input method on the Android VNC client side allows Chinese characters to be output to Linux programs opened on the server side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F22dlsqcvvlel1ogdzrpr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F22dlsqcvvlel1ogdzrpr.png" alt="example" width="799" height="850"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This involves modifications in three stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The VNC APP can invoke the input method, intercept and forward characters generated by the input method to the vncserver, without affecting the original normal pathway;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The VNC server can forward received characters to the ibus input method, without affecting the original normal pathway;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The ibus input method can be normally invoked, receive character input into X programs, and does not affect the original normal input.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  VNC APP
&lt;/h4&gt;

&lt;p&gt;To implement input method invocation in the APP, EditText.request() suffices. However, to intercept and obtain the input method content, you need to override EditText, inject a custom InputConnection, and then obtain the input text through the commitText interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@Override
public boolean commitText&lt;span class="o"&gt;(&lt;/span&gt;CharSequence text, int newCursorPosition&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;DEBUG&lt;span class="o"&gt;){&lt;/span&gt;
        Log.d&lt;span class="o"&gt;(&lt;/span&gt;TAG, &lt;span class="s2"&gt;"commitText() called with: text = ["&lt;/span&gt; + text + &lt;span class="s2"&gt;"], mTextView = ["&lt;/span&gt; + detectEventEditText + &lt;span class="s2"&gt;"]"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;detectEventEditText &lt;span class="o"&gt;==&lt;/span&gt; null&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;super.commitText&lt;span class="o"&gt;(&lt;/span&gt;text, newCursorPosition&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;text instanceof Spanned&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        Spanned spanned &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Spanned&lt;span class="o"&gt;)&lt;/span&gt; text&lt;span class="p"&gt;;&lt;/span&gt;
        SuggestionSpan[] spans &lt;span class="o"&gt;=&lt;/span&gt; spanned.getSpans&lt;span class="o"&gt;(&lt;/span&gt;0, text.length&lt;span class="o"&gt;()&lt;/span&gt;, SuggestionSpan.class&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        Reflector.invokeMethodExceptionSafe&lt;span class="o"&gt;(&lt;/span&gt;mIMM, &lt;span class="s2"&gt;"registerSuggestionSpansForNotification"&lt;/span&gt;,
                new Reflector.TypedObject&lt;span class="o"&gt;(&lt;/span&gt;spans, SuggestionSpan[].class&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    Reflector.invokeMethodExceptionSafe&lt;span class="o"&gt;(&lt;/span&gt;detectEventEditText, &lt;span class="s2"&gt;"resetErrorChangedFlag"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    Reflector.invokeMethodExceptionSafe&lt;span class="o"&gt;(&lt;/span&gt;detectEventEditText, &lt;span class="s2"&gt;"hideErrorIfUnchanged"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;mInputModeFlag &lt;span class="o"&gt;==&lt;/span&gt; INPUT_MODE_ONLY_KEYBOARD&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(!&lt;/span&gt;TextUtils.isEmpty&lt;span class="o"&gt;(&lt;/span&gt;text&lt;span class="o"&gt;)){&lt;/span&gt;
        detectEventEditText.getCanvas&lt;span class="o"&gt;()&lt;/span&gt;.getKeyboard&lt;span class="o"&gt;()&lt;/span&gt;.keyEvent&lt;span class="o"&gt;(&lt;/span&gt;0xff, null, text.toString&lt;span class="o"&gt;())&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actually, you could directly use com.android.internal.inputmethod.EditableInputConnection, but it is an internal final class. The author integrated it into the APP by copying the source code, and used reflection for some functions that cannot be called directly.&lt;/p&gt;

&lt;p&gt;Key events not generated by the input method are sent directly via the RFB protocol by calling the interface in the activity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@Override
public boolean dispatchKeyEvent&lt;span class="o"&gt;(&lt;/span&gt;KeyEvent event&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;canvas.getKeyboard&lt;span class="o"&gt;()&lt;/span&gt;.keyEvent&lt;span class="o"&gt;(&lt;/span&gt;event.getKeyCode&lt;span class="o"&gt;()&lt;/span&gt;, event&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is that characters generated by the input method use 0xff as the keycode, and are handled separately in RfbProto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// Normal key press
public synchronized void writeKeyEvent&lt;span class="o"&gt;(&lt;/span&gt;int keySym, int metaState, boolean down&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;viewOnly&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    eventBufLen &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;down&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        writeModifierKeyEvents&lt;span class="o"&gt;(&lt;/span&gt;metaState, down&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keySym &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 0&lt;span class="o"&gt;)&lt;/span&gt;
        writeKeyEvent&lt;span class="o"&gt;(&lt;/span&gt;keySym, down&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    // Always release all modifiers after an &lt;span class="s2"&gt;"up"&lt;/span&gt; event
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;down&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        writeModifierKeyEvents&lt;span class="o"&gt;(&lt;/span&gt;metaState, down&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    try &lt;span class="o"&gt;{&lt;/span&gt;
        os.write&lt;span class="o"&gt;(&lt;/span&gt;eventBuf, 0, eventBufLen&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; catch &lt;span class="o"&gt;(&lt;/span&gt;IOException e&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        Log.e&lt;span class="o"&gt;(&lt;/span&gt;TAG, &lt;span class="s2"&gt;"Failed to write key event to VNC server."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        e.printStackTrace&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// Input character
public synchronized void writeKeyStringEvent&lt;span class="o"&gt;(&lt;/span&gt;int keycode, char c, boolean down&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;viewOnly &lt;span class="o"&gt;||&lt;/span&gt; os &lt;span class="o"&gt;==&lt;/span&gt; null &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        int keySym &lt;span class="o"&gt;=&lt;/span&gt; c&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keySym &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 0xff&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            keySym +&lt;span class="o"&gt;=&lt;/span&gt; 0x1000000&lt;span class="p"&gt;;&lt;/span&gt; //out of keyboard
        &lt;span class="o"&gt;}&lt;/span&gt;
        Log.d&lt;span class="o"&gt;(&lt;/span&gt;TAG, &lt;span class="s2"&gt;"writeKeyStringEvent() called with: keySym = ["&lt;/span&gt; + keySym + &lt;span class="s2"&gt;"], down = ["&lt;/span&gt; + down + &lt;span class="s2"&gt;"]"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        eventBufLen &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keySym &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 0&lt;span class="o"&gt;)&lt;/span&gt;
            writeSpacialKeyEvent&lt;span class="o"&gt;(&lt;/span&gt;keycode, keySym, down&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        try &lt;span class="o"&gt;{&lt;/span&gt;
            os.write&lt;span class="o"&gt;(&lt;/span&gt;eventBuf, 0, eventBufLen&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; catch &lt;span class="o"&gt;(&lt;/span&gt;IOException e&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            Log.e&lt;span class="o"&gt;(&lt;/span&gt;TAG, &lt;span class="s2"&gt;"Failed to write key event to VNC server."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            e.printStackTrace&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the code at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitee.com/openfde/remote-desktop-clients/blob/fde_w/bVNC/src/main/java/com/iiordanov/bVNC/DetectEventEditText.java" rel="noopener noreferrer"&gt;bVNC/src/main/java/com/iiordanov/bVNC/DetectEventEditText.java · OpenFDE/remote-desktop-clients - Gitee&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitee.com/openfde/remote-desktop-clients/blob/fde_w/bVNC/src/main/java/com/iiordanov/bVNC/DetectInputConnection.java" rel="noopener noreferrer"&gt;bVNC/src/main/java/com/iiordanov/bVNC/DetectInputConnection.java · OpenFDE/remote-desktop-clients - Gitee&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitee.com/openfde/remote-desktop-clients/blob/fde_w/bVNC/src/main/java/com/iiordanov/bVNC/RfbProto.java" rel="noopener noreferrer"&gt;bVNC/src/main/java/com/iiordanov/bVNC/RfbProto.java · OpenFDE/remote-desktop-clients - Gitee&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  VNC server
&lt;/h4&gt;

&lt;p&gt;Upon receiving a KeyEvent from the VNC APP, according to the original protocol, the reserved bits are used as a flag to distinguish between input characters and key presses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitee.com/openfde/fde_tigervncserver/blob/fde_w/common/rfb/SMsgReader.cxx" rel="noopener noreferrer"&gt;common/rfb/SMsgReader.cxx · OpenFDE/fde_tigervncserver - Gitee&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;void SMsgReader::readKeyEvent&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  // bool down &lt;span class="o"&gt;=&lt;/span&gt; is-&amp;gt;readU8&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  // is-&amp;gt;skip&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  // rdr::U32 key &lt;span class="o"&gt;=&lt;/span&gt; is-&amp;gt;readU32&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  // handler-&amp;gt;keyEvent&lt;span class="o"&gt;(&lt;/span&gt;key, 0, down&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  bool down &lt;span class="o"&gt;=&lt;/span&gt; is-&amp;gt;readU8&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  rdr::U32 keycode &lt;span class="o"&gt;=&lt;/span&gt; is-&amp;gt;readU16&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
//    is-&amp;gt;skip&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; Use this reserved bit to distinguish between input characters and key presses 0xff
  rdr::U32 key &lt;span class="o"&gt;=&lt;/span&gt; is-&amp;gt;readU32&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  handler-&amp;gt;keyEvent&lt;span class="o"&gt;(&lt;/span&gt;key, keycode, down&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other modifications on the server side use the quilt patch approach mentioned above; the main changes are all in this patch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitee.com/openfde/fde_tigervncserver/blob/fde_w/debian/patches/fde_patch_001.diff" rel="noopener noreferrer"&gt;debian/patches/fde_patch_001.diff · OpenFDE/fde_tigervncserver - Gitee&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key points are as follows. Simply put, a 4-digit unicode is split into 4 key press events. For example, to send "我" (unicode "6211"), it would send "6", "2", "1", "1" as 4 down/up events respectively. If there is a "0", a special keysym needs to be sent; otherwise, the ibus protocol will convert it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
 &lt;span class="k"&gt;*&lt;/span&gt; vncKeyboardEvent&lt;span class="o"&gt;()&lt;/span&gt; - add X11 events &lt;span class="k"&gt;for &lt;/span&gt;the given RFB key event
 &lt;span class="k"&gt;*&lt;/span&gt;/
void vncKeyboardEvent&lt;span class="o"&gt;(&lt;/span&gt;KeySym keysym, unsigned xtcode, int down&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    /&lt;span class="k"&gt;*&lt;/span&gt; Simple &lt;span class="k"&gt;case&lt;/span&gt;: the client has specified the key &lt;span class="k"&gt;*&lt;/span&gt;/
    //    xtcode &lt;span class="o"&gt;=&lt;/span&gt; 0xff&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;xtcode &lt;span class="o"&gt;==&lt;/span&gt; 0xff&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        //hardcode keysym &lt;span class="o"&gt;=&lt;/span&gt;  0xcccc , transfer IBUS_KEY_Adiaeresis maybe never used
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;down&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            vncKeysymKeyboardEvent&lt;span class="o"&gt;(&lt;/span&gt;0xcccc, down&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            int fakeSym &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keysym &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; 12&lt;span class="o"&gt;)&lt;/span&gt; &amp;amp; 0xf&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;fakeSym &lt;span class="o"&gt;==&lt;/span&gt; 0&lt;span class="o"&gt;){&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;0xcccd, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;0xcccd, 0, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;fakeSym, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;fakeSym, 0, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            fakeSym &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keysym &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; 8&lt;span class="o"&gt;)&lt;/span&gt; &amp;amp; 0xf&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;fakeSym &lt;span class="o"&gt;==&lt;/span&gt; 0&lt;span class="o"&gt;){&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;0xcccd, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;0xcccd, 0, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;fakeSym, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;fakeSym, 0, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            fakeSym &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keysym &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; 4&lt;span class="o"&gt;)&lt;/span&gt; &amp;amp; 0xf&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;fakeSym &lt;span class="o"&gt;==&lt;/span&gt; 0&lt;span class="o"&gt;){&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;0xcccd, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;0xcccd, 0, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;fakeSym, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;fakeSym, 0, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            fakeSym &lt;span class="o"&gt;=&lt;/span&gt; keysym &amp;amp; 0xf&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;fakeSym &lt;span class="o"&gt;==&lt;/span&gt; 0&lt;span class="o"&gt;){&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;0xcccd, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;0xcccd, 0, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;fakeSym, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;fakeSym, 0, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            vncKeysymKeyboardEvent&lt;span class="o"&gt;(&lt;/span&gt;0xcccc, down&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
        fakeSendKeycode&lt;span class="o"&gt;(&lt;/span&gt;keysym, down, xtcode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be understood as an encoding process. The reason this treatment is necessary is due to the ibus protocol transmission process: it seems that keysym types are only not escaped when they are 1 byte, or some defined in X11's keysymdef.h.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ibus Input Method
&lt;/h4&gt;

&lt;p&gt;Ultimately, text input still uses the input method on Linux. It is started via environment variables and serves no other purpose than to decode the content sent by vncserver and generate characters.&lt;/p&gt;

&lt;p&gt;Simply follow the ibus input method flow and register a key callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    g_signal_connect&lt;span class="o"&gt;(&lt;/span&gt;engine, &lt;span class="s2"&gt;"process-key-event"&lt;/span&gt;, G_CALLBACK&lt;span class="o"&gt;(&lt;/span&gt;engine_process_key_event_cb&lt;span class="o"&gt;)&lt;/span&gt;, NULL&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The keyval, keycode, and state sent by vncserver across 4 events are combined to generate a unicode character, which is then committed as text. Normal key presses do not have the flag bit 0xcccc used above, so they effectively bypass this input method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gboolean engine_process_key_event_cb&lt;span class="o"&gt;(&lt;/span&gt;IBusEngine &lt;span class="k"&gt;*&lt;/span&gt;engine,
                                     guint keyval,
                                     guint keycode,
                                     guint state&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    LOG_INFO&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"engine_process_key_event"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    ibus_engine_show_lookup_table&lt;span class="o"&gt;(&lt;/span&gt;engine&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    // ibus_engine_show_preedit_text&lt;span class="o"&gt;(&lt;/span&gt;engine&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    ibus_engine_show_auxiliary_text&lt;span class="o"&gt;(&lt;/span&gt;engine&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    std::thread::id t1_id &lt;span class="o"&gt;=&lt;/span&gt; std::this_thread::get_id&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    // char receiver[100]&lt;span class="p"&gt;;&lt;/span&gt; //
    // sprintf&lt;span class="o"&gt;(&lt;/span&gt;receiver, &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\r&lt;/span&gt;&lt;span class="s2"&gt;    Received data  keycode:%d keyval:%d flag:%d text:%d "&lt;/span&gt;,keycode, keyval, flag, text&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    // engine_commit_text&lt;span class="o"&gt;(&lt;/span&gt;engine, ibus_text_new_from_string&lt;span class="o"&gt;(&lt;/span&gt;receiver&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    //1.unicode start
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keyval &lt;span class="o"&gt;==&lt;/span&gt; 0xcccc &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!(&lt;/span&gt;state &amp;amp; IBUS_RELEASE_MASK&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        // sprintf&lt;span class="o"&gt;(&lt;/span&gt;receiver, &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\r&lt;/span&gt;&lt;span class="s2"&gt;   1.unicode start   keycode:%d maks:%d  "&lt;/span&gt;,keycode, state &amp;amp; IBUS_RELEASE_MASK&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        // engine_commit_text&lt;span class="o"&gt;(&lt;/span&gt;engine, ibus_text_new_from_string&lt;span class="o"&gt;(&lt;/span&gt;receiver&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        flag &lt;span class="o"&gt;=&lt;/span&gt; 3&lt;span class="p"&gt;;&lt;/span&gt;
        text &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt;
        processing &lt;span class="o"&gt;=&lt;/span&gt; 1&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;TRUE&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    //2.add unicode
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;flag &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!(&lt;/span&gt;state &amp;amp; IBUS_RELEASE_MASK&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        // sprintf&lt;span class="o"&gt;(&lt;/span&gt;receiver, &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    2.add unicode   flag:%d maks:%d  "&lt;/span&gt;,flag, state &amp;amp; IBUS_RELEASE_MASK&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        // engine_commit_text&lt;span class="o"&gt;(&lt;/span&gt;engine, ibus_text_new_from_string&lt;span class="o"&gt;(&lt;/span&gt;receiver&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;keyval &lt;span class="o"&gt;==&lt;/span&gt; 0xcccd&lt;span class="o"&gt;){&lt;/span&gt;
            text +&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0 &amp;lt;&amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;flag &lt;span class="k"&gt;*&lt;/span&gt; 4&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
            text +&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keyval &amp;lt;&amp;lt; &lt;span class="o"&gt;(&lt;/span&gt;flag &lt;span class="k"&gt;*&lt;/span&gt; 4&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        // char input_string[100]&lt;span class="p"&gt;;&lt;/span&gt; //1
        // sprintf&lt;span class="o"&gt;(&lt;/span&gt;input_string, &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\r&lt;/span&gt;&lt;span class="s2"&gt;   Generate character  bit flag:%d keyval:%d text:%d"&lt;/span&gt;,flag, keyval, text&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        // engine_commit_text&lt;span class="o"&gt;(&lt;/span&gt;engine, ibus_text_new_from_string&lt;span class="o"&gt;(&lt;/span&gt;input_string&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        flag--&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;TRUE&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    //3.unicode over
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;keyval &lt;span class="o"&gt;==&lt;/span&gt; 0xcccc &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;state &amp;amp; IBUS_RELEASE_MASK&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        // sprintf&lt;span class="o"&gt;(&lt;/span&gt;receiver, &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\r&lt;/span&gt;&lt;span class="s2"&gt;    3.unicode over   keycode:%d maks:%d  flag:%d text:%d"&lt;/span&gt;,keycode, state &amp;amp; IBUS_RELEASE_MASK, flag, text&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        // engine_commit_text&lt;span class="o"&gt;(&lt;/span&gt;engine, ibus_text_new_from_string&lt;span class="o"&gt;(&lt;/span&gt;receiver&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        flag &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        wchar_t unicodeChar &lt;span class="o"&gt;=&lt;/span&gt; static_cast&amp;lt;wchar_t&amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;text&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        std::wstring_convert &amp;lt;std::codecvt_utf8_utf16&amp;lt;wchar_t&amp;gt;&amp;gt; converter&lt;span class="p"&gt;;&lt;/span&gt;
        std::string utf8Str &lt;span class="o"&gt;=&lt;/span&gt; converter.to_bytes&lt;span class="o"&gt;(&lt;/span&gt;unicodeChar&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        // char input_string[100]&lt;span class="p"&gt;;&lt;/span&gt; //
        // sprintf&lt;span class="o"&gt;(&lt;/span&gt;input_string, &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\r&lt;/span&gt;&lt;span class="s2"&gt;     Key press keyval:%d keycode:%d text:%d  final====&amp;gt; "&lt;/span&gt;, keyval, keycode, text&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        // engine_commit_text&lt;span class="o"&gt;(&lt;/span&gt;engine, ibus_text_new_from_string&lt;span class="o"&gt;(&lt;/span&gt;input_string&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        engine_commit_text&lt;span class="o"&gt;(&lt;/span&gt;engine, ibus_text_new_from_string&lt;span class="o"&gt;(&lt;/span&gt;utf8Str.c_str&lt;span class="o"&gt;()))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        processing &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;TRUE&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;processing&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;TRUE&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    text &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt;
    flag &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    // sprintf&lt;span class="o"&gt;(&lt;/span&gt;receiver, &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\r&lt;/span&gt;&lt;span class="s2"&gt;     4.  maks:%d  "&lt;/span&gt; , state &amp;amp; IBUS_RELEASE_MASK&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    // engine_commit_text&lt;span class="o"&gt;(&lt;/span&gt;engine, ibus_text_new_from_string&lt;span class="o"&gt;(&lt;/span&gt;receiver&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;state &amp;amp; IBUS_RELEASE_MASK&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;FALSE&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;FALSE&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Clipboard
&lt;/h4&gt;

&lt;p&gt;RFB has two versions of the clipboard protocol, &lt;a href="https://www.babeler.cn/transfer/input/clipboard.html" rel="noopener noreferrer"&gt;Clipboard · RFB Remote Framebuffer Protocol&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The old one does not support Chinese, so the extended clipboard pseudo-protocol needs to be used.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;RFB 3.8 protocol limitation: the clipboard can only transmit the Latin-1 character set. In 2016, Cendio Ossman merged the &lt;a href="https://github.com/rfbproto/rfbproto/commit/08018f655acd52970680b34021159924357efb5d" rel="noopener noreferrer"&gt;Extended Clipboard Pseudo-Encoding&lt;/a&gt; into the main protocol branch, supporting transmission of the unicode character set in clipboard messages. UltraVNC/TigerVNC/RealVNC servers all support this extended protocol, while x11vnc has not yet provided support (2021/8/11).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The extended clipboard pseudo-protocol requires support from both client and server software. The message extends &lt;code&gt;ServerCutText&lt;/code&gt; and &lt;code&gt;ClientCutText&lt;/code&gt;, as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  +--------------+--------------+--------------+
  | No. of bytes | Type &lt;span class="o"&gt;[&lt;/span&gt;Value] | Description  |
  +--------------+--------------+--------------+
  | 1            | U8 &lt;span class="o"&gt;[&lt;/span&gt;3/6]     | message-type |
  | 3            |              | padding      |
  | 4            | S32          | length       |
  | 4            | U32          | text-type    |
  | length-4     | U8 array     | text         |
  +--------------+--------------+--------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The remote-desktop-clients used in this project also does not implement this, &lt;a href="https://github.com/iiordanov/remote-desktop-clients/issues/285" rel="noopener noreferrer"&gt;VNC clipboard for utf8 text · Issue #285 · iiordanov/remote-desktop-clients&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The author implemented text copying through the extended clipboard pseudo-protocol. Since this protocol supports multiple formats, &lt;strong&gt;ZlibOutStream&lt;/strong&gt; was used to convert the copied content. See the following patch for code modifications:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitee.com/openfde/remote-desktop-clients/commit/dd23ad0d01b409a10e973fdb6ccf19dc94d7f35f" rel="noopener noreferrer"&gt;Login - Gitee.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact, it can also implement file copying, but the author did not further implement it. remote-desktop-clients does not even implement Chinese (unicode) copying; the author speculates this is because clipboard usage scenarios on mobile are too rare, whereas OpenFDE has this requirement.&lt;/p&gt;

&lt;p&gt;In summary, most of the principles of the VNC-based Linux application integration solution for the OpenFDE desktop environment are as described above. There are many more details to handle. It has already been released as a mature product, and we are subsequently researching methods to directly use xserver to run Linux programs within the OpenFDE desktop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Future Plans
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1dpu1nhdt08qg7r8zjl2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1dpu1nhdt08qg7r8zjl2.png" alt="principle" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are currently developing a new integration solution: porting xserver to our Android desktop. This way, starting an X Server within OpenFDE to connect to Linux programs allows direct transmission of the program's display content without going through screen image forwarding, greatly simplifying the flow. It offers better performance, compatibility, and user experience. At the same time, we will strive to achieve a window system that is as close as possible to that of Linux programs. Stay tuned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F84e42un3eqvfjdixow79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F84e42un3eqvfjdixow79.png" alt="example" width="799" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's all for this technical sharing session. Writing isn't easy, so if you found it helpful, please follow us for more~&lt;/p&gt;

</description>
      <category>linux</category>
      <category>opensource</category>
      <category>android</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>How to Run Android Apps Natively on Linux?</title>
      <dc:creator>ying xu</dc:creator>
      <pubDate>Mon, 27 Jul 2026 09:53:12 +0000</pubDate>
      <link>https://dev.to/ying_xu_0f8a58231902eb42b/technical-analysis-how-to-run-android-apps-natively-on-linux-acm</link>
      <guid>https://dev.to/ying_xu_0f8a58231902eb42b/technical-analysis-how-to-run-android-apps-natively-on-linux-acm</guid>
      <description>&lt;p&gt;It is well known that the Android system runs on the Linux kernel . However, Android's rich and mature software ecosystem stands in stark contrast to that of Linux ; given that they share the same kernel, why can't Android applications run directly on Linux? &lt;/p&gt;

&lt;p&gt;There are currently two main technical solutions for running Android on Linux: &lt;strong&gt;virtual machines&lt;/strong&gt; and &lt;strong&gt;containerization&lt;/strong&gt; . Virtual machines consume significant hardware resources and result in complete isolation—preventing interaction between the two systems—whereas containerization avoids these drawbacks, making it our preferred choice . Specifically, containerization effectively carves out a dedicated space for Android to run within the Linux environment while maintaining a channel for interaction between the two systems .&lt;/p&gt;

&lt;p&gt;Therefore, based on this, we decided to create an open-source desktop, &lt;strong&gt;OpenFDE&lt;/strong&gt;, which allows Android applications to run directly on Linux systems .&lt;/p&gt;

&lt;p&gt;To provide a better understanding of our open-source project, this article outlines OpenFDE's software architecture and display logic .&lt;/p&gt;




&lt;h2&gt;
  
  
  1. OpenFDE Overall Software Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────────────────────────────────────┐
│                                   Application Layer                                    │
│   ┌────────────────────────┬────────────────────────┐      ┌────────────┬──────────┐   │
│   │     Android App1       │      Android App2      │      │  X11 APP1  │ X11 APP1 │   │
│   └────────────────────────┴────────────────────────┘      └─────┬──────┴────┬─────┘   │
│                                                                  │           │         │
│                                                            ┌─────▼───────────▼──────┐  │
│                                                            │        XServer         │  │
│                                                            └────────────┬───────────┘  │
├─────────────────────────────────────────────────────────────────────────┼──────────────┤
│                                  API Framework Layer                    │              │
│   ┌────────────────────────┬────────────────────────┐  ┌────────────────▼───────────┐  │
│   │    Activity Manager    │     Window Manager     │  │   X11 Client (libX11, etc) │  │
│   ├────────────────────────┼────────────────────────┤  ├────────────────────────────┤  │
│   │    Content Providers   │      View System       │  │    DIX Driver (wiglamor)   │  │
│   ├────────────────────────┼────────────────────────┤  │            Glamor          │  │
│   │    Resource Manager    │    Location Manager    │  ├────────────────────────────┤  │
│   ├────────────────────────┴────────────────────────┤  │    EGL / GLX / OpenGL/ES   │  │
│   │             Android Runtime (ART)               │  ├────────────────────────────┤  │
│   └─────────────────────────────────────────────────┘  │   User-space Drivers       │  │
├────────────────────────────────────────────────────────┤   (libGL-Mesa-DRI, Mesa3D) │  │
│                                     Native Libs        │  ├────────────────────────────┤  │
│   ┌────────────────────────┬────────────────────────┐  │      LibDRM / GNU LibC     │  │
│   │          SSL           │        Graphics        │  └────────────────────────────┘  │
│   ├────────────────────────┴────────────────────────┤             ▲                    │
│   │   AOSP LibC with Modified Dynamic Libs Loader   │─────────────┘                    │
├─────────────────────────────────────────────────────┴──────────────────────────────────┤
│                           LXC Isolation and Fusion Layer                               │
│   ┌──────────────────┬──────────────────┬──────────────────┬──────────────────────┐    │
│   │ Filesystem Fusion│  Network Fusion  │  IPC Fusion &amp;amp;    │   Clipboard Fusion   │    │
│   │                  │                  │   Acceleration   │                      │    │
│   └──────────────────┴──────────────────┴──────────────────┴──────────────────────┘    │
├────────────────────────────────────────────────────────────────────────────────────────┤
│                           Standard Desktop Device Drivers                              │
├────────────────────────────────────────────────────────────────────────────────────────┤
│                                 Standard Linux Kernel                                  │
└────────────────────────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image above illustrates our current overall technical architecture . From the bottom up, the architecture comprises :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Standard Linux Kernel&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LXC Isolation and Integration Layer&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Framework Layer&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Layer&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux Desktop Graphics Stack&lt;/strong&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let us examine each of these components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Application Layer&lt;/strong&gt;: The FDE application layer primarily encompasses Android applications and Linux X11 applications . While Android applications run natively, Linux X11 applications operate within the Linux system; we integrate their display into the Android environment using a VNC client running within FDE, while simultaneously forwarding input events (such as keyboard/mouse signals and IME inputs) . We are currently developing an Android-based X Server to replace the Linux-based VNC X Server, aiming to achieve superior display performance and enhanced compatibility .&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API Framework Layer&lt;/strong&gt;: It provides a set of standard API interfaces that enable applications to communicate and interact with the underlying system; these APIs cover functions such as accessing device drivers, managing file systems, and handling network communications . OpenFDE directly inherits the Android API design framework, ensuring that both adhere to a unified set of APIs for peripherals and power management . This significantly alleviates issues associated with the coexistence of different API generations, as well as the haphazard and chaotic nature of modifications during the usage and development of graphical interfaces .&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LXC Isolation and Integration Layer&lt;/strong&gt;: The LXC isolation layer isolates the Linux and Android file systems and API layers while sharing the underlying Linux kernel . The integration layer leverages standard desktop protocols (such as Wayland) and the Linux desktop graphics stack to provide key functions like graphical display, input device management, and window management . Additionally, we are working to package the Linux desktop graphics stack's OpenGL/ES components into dynamic libraries that can be called directly by Android, thereby ensuring compatibility with a wider range of hardware .&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Display Integration Logic
&lt;/h2&gt;

&lt;p&gt;Having covered the overall software architecture of OpenFDE, let us now discuss how the display component is integrated—a particularly challenging aspect of the entire integration solution .&lt;/p&gt;

&lt;h3&gt;
  
  
  Standard Linux DRI Display Framework
&lt;/h3&gt;

&lt;p&gt;The logic of the DRI display framework on Linux (currently the most efficient display framework) is that the client directly invokes the GPU for rendering; once rendering is complete, the output is submitted to the compositor, which then performs composition and submits the result for display .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────┐    1. Render Request     ┌──────────────────┐
│ Client/App       ├─────────────────────────&amp;gt;│ GPU              │
└────────┬─────────┘                          └────────┬─────────┘
         │                                             │
         │ 2. Send Composition Request                 │ 3. Fetch Render Result
         ▼                                             ▼    for Composition
┌──────────────────┐                          ┌──────────────────┐
│ Compositor       ├─────────────────────────&amp;gt;│ DPU              │
└──────────────────┘    4. Send Composition   └──────────────────┘
                             Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Native Android Display Logic
&lt;/h3&gt;

&lt;p&gt;Android's display logic involves the application requesting buffers from SurfaceFlinger; SurfaceFlinger invokes Gralloc to fulfill the request and synchronizes the result for the application's use . The rendering process is executed directly by the application using the GPU, and the final output is handed over to SurfaceFlinger, which invokes HWC (Hardware Composer) to perform composition .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 ┌──────────────────────────┐
                 │ Application (Self-Render)│&amp;lt;──────────┐
                 └─────┬──────────────┬─────┘           │
                       │              │                 │
  Submit Rendering     │              │ Apply Buffer    │ Return
  Results              │              │                 │
                       ▼              ▼                 │
┌─────────────┐   ┌─────────────────────────┐     ┌─────┴──────────┐
│ Display     │&amp;lt;──┤     surfaceflinger      ├────&amp;gt;│    gralloc     │
└──────▲──────┘   └───────────┬─────────────┘     └────────────────┘
       │                      │ Call
       │                      ▼
       │          ┌─────────────────────────┐
       └──────────┤  hwcomposer (Composition│
                  └─────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  OpenFDE Merged Display Architecture
&lt;/h3&gt;

&lt;p&gt;Running Android on Linux allows these two workflows to be merged :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The initial stages follow the standard Android process .&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hwcomposer&lt;/code&gt; is modified to act as a Wayland client (thereby losing hardware composition capabilities) .&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;SurfaceFlinger&lt;/code&gt; requests composition from &lt;code&gt;hwcomposer&lt;/code&gt;, the latter returns data indicating that the GPU should handle the entire composition task .&lt;/li&gt;
&lt;li&gt;Consequently, &lt;code&gt;SurfaceFlinger&lt;/code&gt; invokes the GPU to perform the composition and sends the result to &lt;code&gt;hwcomposer&lt;/code&gt; for submission .&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;hwcomposer&lt;/code&gt; transmits the buffer via the Wayland protocol to the Linux compositor (&lt;code&gt;mutter&lt;/code&gt;), which then sends it to the Linux DPU for display .
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               ┌──────────────────────────┐
               │    App (Self-rendering)  │&amp;lt;──────────┐
               └─────┬──────────────┬─────┘           │
                     │              │                 │
Submit Rendering     │              │ Request Buffer  │ Allocate
Results              │              │                 │
                     ▼              ▼                 │
┌─────────────┐ Ask  ┌──────────────┴──────┐     ┌─────┴──────────┐
│ hwcomposer  │&amp;lt;────┤    surfaceflinger    ├────&amp;gt;│    gralloc     │
│ (wayland    │     └──────────────▲───────┘     └────────────────┘
│   client)   ├────────────────────┘
└──────┬──────┘ Respond: ALL DONE BY GPU
       │
       │ Transmit Buffer via Wayland
       ▼
┌─────────────┐
│   mutter    │
│(Composition)│
└──────┬──────┘
       │
       ▼
┌─────────────┐
│   Display   │
└─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, this approach entails some performance overhead, but it effectively achieves our objective .&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Summary &amp;amp; What's Next
&lt;/h2&gt;

&lt;p&gt;These are the two core components of how FDE currently operates . Beyond this, we have also worked on integrating Linux applications into Android, upgrading the UI, fine-tuning interactions, and more . &lt;/p&gt;

&lt;p&gt;In the next installment, we'll dive into the technical principles behind FDE's Linux application integration .&lt;br&gt;
"""&lt;/p&gt;

</description>
      <category>android</category>
      <category>linux</category>
      <category>software</category>
    </item>
  </channel>
</rss>
