From bd0a33e46796a9f24bba6fabb2534490fe1757a0 Mon Sep 17 00:00:00 2001
From: fufesou <13586388+fufesou@users.noreply.github.com>
Date: Tue, 3 Dec 2024 01:02:41 +0800
Subject: [PATCH] fix: linux, window, workaround, mint, mate (#10146)

* refact: linux, window, workaround, mint, mate

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: case insensitive

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
---
 flutter/lib/common.dart               | 30 ++++++++
 flutter/lib/main.dart                 | 11 ++-
 flutter/lib/web/bridge.dart           |  4 ++
 flutter/linux/my_application.cc       | 98 +++++++++++++++++++++++----
 libs/hbb_common/src/platform/linux.rs | 15 +++-
 src/flutter_ffi.rs                    | 20 ++++++
 6 files changed, 161 insertions(+), 17 deletions(-)

diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart
index 4e9744912..13ee4dd84 100644
--- a/flutter/lib/common.dart
+++ b/flutter/lib/common.dart
@@ -1,6 +1,7 @@
 import 'dart:async';
 import 'dart:convert';
 import 'dart:math';
+import 'dart:io';
 
 import 'package:back_button_interceptor/back_button_interceptor.dart';
 import 'package:desktop_multi_window/desktop_multi_window.dart';
@@ -3459,6 +3460,35 @@ Widget buildPresetPasswordWarning() {
   );
 }
 
+bool get isLinuxMateDesktop =>
+    isLinux &&
+    (Platform.environment['XDG_CURRENT_DESKTOP']?.toLowerCase() == 'mate' ||
+        Platform.environment['XDG_SESSION_DESKTOP']?.toLowerCase() == 'mate' ||
+        Platform.environment['DESKTOP_SESSION']?.toLowerCase() == 'mate');
+
+Map<String, dynamic>? _linuxOsDistro;
+
+String getLinuxOsDistroId() {
+  if (_linuxOsDistro == null) {
+    String osInfo = bind.getOsDistroInfo();
+    if (osInfo.isEmpty) {
+      _linuxOsDistro = {};
+    } else {
+      try {
+        _linuxOsDistro = jsonDecode(osInfo);
+      } catch (e) {
+        debugPrint('Failed to parse os info: $e');
+        // Don't call `bind.getOsDistroInfo()` again if failed to parse osInfo.
+        _linuxOsDistro = {};
+      }
+    }
+  }
+  return (_linuxOsDistro?['id'] ?? '') as String;
+}
+
+bool get isLinuxMint =>
+    getLinuxOsDistroId().toLowerCase().contains('linuxmint');
+
 // https://github.com/leanflutter/window_manager/blob/87dd7a50b4cb47a375b9fc697f05e56eea0a2ab3/lib/src/widgets/virtual_window_frame.dart#L44
 Widget buildVirtualWindowFrame(BuildContext context, Widget child) {
   boxShadow() => isMainDesktopWindow
diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart
index 00afbb001..3176bfb86 100644
--- a/flutter/lib/main.dart
+++ b/flutter/lib/main.dart
@@ -483,7 +483,16 @@ class _AppState extends State<App> with WidgetsBindingObserver {
                     child = keyListenerBuilder(context, child);
                   }
                   if (isLinux) {
-                    child = buildVirtualWindowFrame(context, child);
+                    // `(!(isLinuxMateDesktop || isLinuxMint))` is not used here for clarity.
+                    // `isLinuxMint` will call ffi function.
+                    if (!isLinuxMateDesktop) {
+                      if (!isLinuxMint) {
+                        debugPrint(
+                            'Linux distro is not linuxmint, and desktop is not mate, '
+                            'so we build virtual window frame.');
+                        child = buildVirtualWindowFrame(context, child);
+                      }
+                    }
                   }
                   return child;
                 },
diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart
index 208912814..ffbf66382 100644
--- a/flutter/lib/web/bridge.dart
+++ b/flutter/lib/web/bridge.dart
@@ -1828,5 +1828,9 @@ class RustdeskImpl {
     throw UnimplementedError("sessionGetConnToken");
   }
 
+  String getOsDistroInfo({dynamic hint}) {
+    return '';
+  }
+
   void dispose() {}
 }
diff --git a/flutter/linux/my_application.cc b/flutter/linux/my_application.cc
index f4247bd94..9fa947002 100644
--- a/flutter/linux/my_application.cc
+++ b/flutter/linux/my_application.cc
@@ -17,6 +17,7 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
 extern bool gIsConnectionManager;
 
 GtkWidget *find_gl_area(GtkWidget *widget);
+void try_set_transparent(GtkWindow* window, GdkScreen* screen, FlView* view);
 
 // Implements GApplication::activate.
 static void my_application_activate(GApplication* application) {
@@ -79,21 +80,7 @@ static void my_application_activate(GApplication* application) {
   gtk_widget_show(GTK_WIDGET(view));
   gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
 
-  // https://github.com/flutter/flutter/issues/152154
-  // Remove this workaround when flutter version is updated.
-  GtkWidget *gl_area = find_gl_area(GTK_WIDGET(view));
-  if (gl_area != NULL) {
-    gtk_gl_area_set_has_alpha(GTK_GL_AREA(gl_area), TRUE);
-  }
-
-  if (screen != NULL) {
-    GdkVisual *visual = NULL;
-    gtk_widget_set_app_paintable(GTK_WIDGET(window), TRUE);
-    visual = gdk_screen_get_rgba_visual(screen);
-    if (visual != NULL && gdk_screen_is_composited(screen)) {
-      gtk_widget_set_visual(GTK_WIDGET(window), visual);
-    }
-  }
+  try_set_transparent(window, screen, view);
 
   fl_register_plugins(FL_PLUGIN_REGISTRY(view));
 
@@ -162,3 +149,84 @@ GtkWidget *find_gl_area(GtkWidget *widget)
 
   return NULL;
 }
+
+bool is_linux_mint()
+{
+  bool is_mint = false;
+  char line[256];
+  FILE *fp = fopen("/etc/os-release", "r");
+  if (fp == NULL) {
+    return false;
+  }
+  while (fgets(line, sizeof(line), fp)) {
+    if (strstr(line, "ID=linuxmint") != NULL) {
+        is_mint = true;
+        break;
+    }
+  }
+  fclose(fp);
+
+  return is_mint;
+}
+
+bool is_desktop_mate()
+{
+  const char* desktop = NULL;
+  desktop = getenv("XDG_CURRENT_DESKTOP");
+  printf("Linux desktop, XDG_CURRENT_DESKTOP: %s\n", desktop == NULL ? "" : desktop);
+  if (desktop == NULL) {
+    desktop = getenv("XDG_SESSION_DESKTOP");
+    printf("Linux desktop, XDG_SESSION_DESKTOP: %s\n", desktop == NULL ? "" : desktop);
+  }
+  if (desktop == NULL) {
+      desktop = getenv("DESKTOP_SESSION");
+      printf("Linux desktop, DESKTOP_SESSION: %s\n", desktop == NULL ? "" : desktop);
+  }
+  if (desktop != NULL && strcasecmp(desktop, "mate") == 0) {
+    return true;
+  }
+  return false;
+}
+
+bool skip_setting_transparent()
+{
+  if (is_desktop_mate()) {
+    printf("Linux desktop, MATE\n");
+    return true;
+  }
+
+  if (is_linux_mint()) {
+    printf("Linux desktop, Linux Mint\n");
+    return true;
+  }
+
+  return false;
+}
+
+// https://github.com/flutter/flutter/issues/152154
+// Remove this workaround when flutter version is updated.
+void try_set_transparent(GtkWindow* window, GdkScreen* screen, FlView* view)
+{
+  GtkWidget *gl_area = NULL;
+
+  if (skip_setting_transparent()) {
+    printf("Skip setting transparent\n");
+    return;
+  }
+
+  printf("Try setting transparent\n");
+  
+  gl_area = find_gl_area(GTK_WIDGET(view));
+  if (gl_area != NULL) {
+    gtk_gl_area_set_has_alpha(GTK_GL_AREA(gl_area), TRUE);
+  }
+
+  if (screen != NULL) {
+    GdkVisual *visual = NULL;
+    gtk_widget_set_app_paintable(GTK_WIDGET(window), TRUE);
+    visual = gdk_screen_get_rgba_visual(screen);
+    if (visual != NULL && gdk_screen_is_composited(screen)) {
+      gtk_widget_set_visual(GTK_WIDGET(window), visual);
+    }
+  }
+}
diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs
index 60c8714d8..31481ca78 100644
--- a/libs/hbb_common/src/platform/linux.rs
+++ b/libs/hbb_common/src/platform/linux.rs
@@ -13,22 +13,35 @@ pub const XDG_CURRENT_DESKTOP: &str = "XDG_CURRENT_DESKTOP";
 
 pub struct Distro {
     pub name: String,
+    pub id: String,
     pub version_id: String,
 }
 
 impl Distro {
     fn new() -> Self {
+        // to-do:
+        // 1. Remove `run_cmds`, read file once
+        // 2. Add more distro infos
         let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release")
             .unwrap_or_default()
             .trim()
             .trim_matches('"')
             .to_string();
+        let id = run_cmds("awk -F'=' '/^ID=/ {print $2}' /etc/os-release")
+            .unwrap_or_default()
+            .trim()
+            .trim_matches('"')
+            .to_string();
         let version_id = run_cmds("awk -F'=' '/^VERSION_ID=/ {print $2}' /etc/os-release")
             .unwrap_or_default()
             .trim()
             .trim_matches('"')
             .to_string();
-        Self { name, version_id }
+        Self {
+            name,
+            id,
+            version_id,
+        }
     }
 }
 
diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs
index 4c875be49..4630ac333 100644
--- a/src/flutter_ffi.rs
+++ b/src/flutter_ffi.rs
@@ -19,6 +19,7 @@ use hbb_common::allow_err;
 use hbb_common::{
     config::{self, LocalConfig, PeerConfig, PeerInfoSerde},
     fs, lazy_static, log,
+    message_proto::Hash,
     rendezvous_proto::ConnType,
     ResultType,
 };
@@ -2341,6 +2342,25 @@ pub fn main_audio_support_loopback() -> SyncReturn<bool> {
     SyncReturn(is_surpport)
 }
 
+pub fn get_os_distro_info() -> SyncReturn<String> {
+    #[cfg(target_os = "linux")]
+    {
+        let distro = &hbb_common::platform::linux::DISTRO;
+        SyncReturn(
+            serde_json::to_string(&HashMap::from([
+                ("name", distro.name.clone()),
+                ("id", distro.id.clone()),
+                ("version_id", distro.version_id.clone()),
+            ]))
+            .unwrap_or_default(),
+        )
+    }
+    #[cfg(not(target_os = "linux"))]
+    {
+        SyncReturn("".to_owned())
+    }
+}
+
 #[cfg(target_os = "android")]
 pub mod server_side {
     use hbb_common::{config, log};