mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 02:18:47 +00:00
fix(gui-client): use Wayland rendering backend on Linux (#10849)
Previously, we opted into the X11 GTK backend when rendering the GUI Client's window. This is causing issues on newer Linux distributions such as Fedora 43 where Wayland is now the only available compositor. Removing the X11 GTK requires us to draw our own CSDs such as titlebars and a close button. This PR does exactly that by adding a minimalistic title bar. To make better use of the space, we move the section headers into there. |Before|After| |---|---| |<img width="1900" height="1174" alt="Screenshot From 2025-11-11 11-14-11" src="https://github.com/user-attachments/assets/9439a69b-65ba-41d6-b1f8-4448e0f80728" />|<img width="1800" height="1000" alt="Screenshot From 2025-11-11 11-40-55" src="https://github.com/user-attachments/assets/7884b2cc-3d9c-4b47-9a1e-c6462aef36ab" />| |<img width="1900" height="1174" alt="Screenshot From 2025-11-11 11-14-16" src="https://github.com/user-attachments/assets/2cfea825-5c08-45a5-873c-5afcbc1dbf16" />|<img width="1800" height="1000" alt="Screenshot From 2025-11-11 11-40-58" src="https://github.com/user-attachments/assets/43ddd7c9-ce65-42f7-b972-28c6b172b70d" />| |<img width="1900" height="1174" alt="Screenshot From 2025-11-11 11-14-19" src="https://github.com/user-attachments/assets/446873a7-9023-4266-9377-ea7b8b4353ee" />|<img width="1800" height="1000" alt="Screenshot From 2025-11-11 11-41-01" src="https://github.com/user-attachments/assets/64439383-f33f-461d-9b4a-6b4138bd675b" />| |<img width="1900" height="1174" alt="Screenshot From 2025-11-11 11-14-22" src="https://github.com/user-attachments/assets/6c39e06c-1d77-471f-91f1-32a78b90a21c" />|<img width="1800" height="1000" alt="Screenshot From 2025-11-11 11-41-04" src="https://github.com/user-attachments/assets/b56912cb-9c85-4b5a-9295-dae6139b25c6" />| |<img width="1900" height="1174" alt="Screenshot From 2025-11-11 11-14-26" src="https://github.com/user-attachments/assets/5a5d638c-15bf-4523-8466-2e0977a03e22" />|<img width="1800" height="1000" alt="Screenshot From 2025-11-11 11-41-06" src="https://github.com/user-attachments/assets/ed169b52-ef86-4dc4-8f25-852da622eaa1" />|
This commit is contained in:
@@ -45,10 +45,6 @@ export default function AdvancedSettingsPage({
|
||||
|
||||
return (
|
||||
<div className="container p-4">
|
||||
<div className="pb-2">
|
||||
<h2 className="text-xl font-semibold">Advanced Settings</h2>
|
||||
</div>
|
||||
|
||||
<p className="text-neutral-900 mb-6">
|
||||
<strong>WARNING</strong>: These settings are intended for internal debug
|
||||
purposes <strong>only</strong>. Changing these is not supported and will
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
GeneralSettingsViewModel,
|
||||
SessionViewModel,
|
||||
} from "../generated/bindings";
|
||||
import Titlebar from "./Titlebar";
|
||||
|
||||
export default function App() {
|
||||
const [session, setSession] = useState<SessionViewModel | null>(null);
|
||||
@@ -83,92 +84,120 @@ export default function App() {
|
||||
const isDev = import.meta.env.DEV;
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-neutral-50 flex flex-row">
|
||||
<Sidebar
|
||||
aria-label="Sidebar"
|
||||
className="w-52 flex-shrink-0 border-r border-neutral-200"
|
||||
>
|
||||
<SidebarItems>
|
||||
<SidebarItemGroup>
|
||||
<ReactRouterSidebarItem icon={HomeIcon} href="/overview">
|
||||
Overview
|
||||
</ReactRouterSidebarItem>
|
||||
<SidebarCollapse label="Settings" open={true} icon={Bars3Icon}>
|
||||
<ReactRouterSidebarItem icon={CogIcon} href="/general-settings">
|
||||
General
|
||||
<div className="h-screen flex flex-col rounded-lg border border-neutral-300 overflow-hidden">
|
||||
<Routes>
|
||||
<Route path="/overview" element={<Titlebar title={"Firezone"} />} />
|
||||
<Route
|
||||
path="/general-settings"
|
||||
element={<Titlebar title={"General Settings"} />}
|
||||
/>
|
||||
<Route
|
||||
path="/advanced-settings"
|
||||
element={<Titlebar title={"Advanced Settings"} />}
|
||||
/>
|
||||
<Route
|
||||
path="/diagnostics"
|
||||
element={<Titlebar title={"Diagnostics"} />}
|
||||
/>
|
||||
<Route path="/about" element={<Titlebar title={"About"} />} />
|
||||
<Route
|
||||
path="/colour-palette"
|
||||
element={<Titlebar title={"Colour Palette"} />}
|
||||
/>
|
||||
</Routes>
|
||||
<div className="flex-1 bg-neutral-50 flex flex-row">
|
||||
<Sidebar
|
||||
aria-label="Sidebar"
|
||||
className="w-52 flex-shrink-0 border-r border-neutral-200"
|
||||
>
|
||||
<SidebarItems>
|
||||
<SidebarItemGroup>
|
||||
<ReactRouterSidebarItem icon={HomeIcon} href="/overview">
|
||||
Overview
|
||||
</ReactRouterSidebarItem>
|
||||
<SidebarCollapse label="Settings" open={true} icon={Bars3Icon}>
|
||||
<ReactRouterSidebarItem icon={CogIcon} href="/general-settings">
|
||||
General
|
||||
</ReactRouterSidebarItem>
|
||||
<ReactRouterSidebarItem
|
||||
icon={WrenchScrewdriverIcon}
|
||||
href="/advanced-settings"
|
||||
>
|
||||
Advanced
|
||||
</ReactRouterSidebarItem>
|
||||
</SidebarCollapse>
|
||||
<ReactRouterSidebarItem
|
||||
icon={DocumentMagnifyingGlassIcon}
|
||||
href="/diagnostics"
|
||||
>
|
||||
Diagnostics
|
||||
</ReactRouterSidebarItem>
|
||||
<ReactRouterSidebarItem
|
||||
icon={WrenchScrewdriverIcon}
|
||||
href="/advanced-settings"
|
||||
icon={InformationCircleIcon}
|
||||
href="/about"
|
||||
>
|
||||
Advanced
|
||||
</ReactRouterSidebarItem>
|
||||
</SidebarCollapse>
|
||||
<ReactRouterSidebarItem
|
||||
icon={DocumentMagnifyingGlassIcon}
|
||||
href="/diagnostics"
|
||||
>
|
||||
Diagnostics
|
||||
</ReactRouterSidebarItem>
|
||||
<ReactRouterSidebarItem icon={InformationCircleIcon} href="/about">
|
||||
About
|
||||
</ReactRouterSidebarItem>
|
||||
</SidebarItemGroup>
|
||||
{isDev && (
|
||||
<SidebarItemGroup>
|
||||
<ReactRouterSidebarItem icon={SwatchIcon} href="/colour-palette">
|
||||
Color Palette
|
||||
About
|
||||
</ReactRouterSidebarItem>
|
||||
</SidebarItemGroup>
|
||||
)}
|
||||
</SidebarItems>
|
||||
</Sidebar>
|
||||
<main className="flex-grow overflow-auto">
|
||||
<Routes>
|
||||
<Route
|
||||
path="/overview"
|
||||
element={
|
||||
<Overview
|
||||
session={session}
|
||||
signIn={commands.signIn}
|
||||
signOut={commands.signOut}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/general-settings"
|
||||
element={
|
||||
<GeneralSettingsPage
|
||||
settings={generalSettings}
|
||||
saveSettings={commands.applyGeneralSettings}
|
||||
resetSettings={commands.resetGeneralSettings}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/advanced-settings"
|
||||
element={
|
||||
<AdvancedSettingsPage
|
||||
settings={advancedSettings}
|
||||
saveSettings={commands.applyAdvancedSettings}
|
||||
resetSettings={commands.resetAdvancedSettings}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/diagnostics"
|
||||
element={
|
||||
<Diagnostics
|
||||
logCount={logCount}
|
||||
exportLogs={commands.exportLogs}
|
||||
clearLogs={commands.clearLogs}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/colour-palette" element={<ColorPalette />} />
|
||||
</Routes>
|
||||
</main>
|
||||
{isDev && (
|
||||
<SidebarItemGroup>
|
||||
<ReactRouterSidebarItem
|
||||
icon={SwatchIcon}
|
||||
href="/colour-palette"
|
||||
>
|
||||
Color Palette
|
||||
</ReactRouterSidebarItem>
|
||||
</SidebarItemGroup>
|
||||
)}
|
||||
</SidebarItems>
|
||||
</Sidebar>
|
||||
<main className="flex-grow overflow-auto">
|
||||
<Routes>
|
||||
<Route
|
||||
path="/overview"
|
||||
element={
|
||||
<Overview
|
||||
session={session}
|
||||
signIn={commands.signIn}
|
||||
signOut={commands.signOut}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/general-settings"
|
||||
element={
|
||||
<GeneralSettingsPage
|
||||
settings={generalSettings}
|
||||
saveSettings={commands.applyGeneralSettings}
|
||||
resetSettings={commands.resetGeneralSettings}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/advanced-settings"
|
||||
element={
|
||||
<AdvancedSettingsPage
|
||||
settings={advancedSettings}
|
||||
saveSettings={commands.applyAdvancedSettings}
|
||||
resetSettings={commands.resetAdvancedSettings}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/diagnostics"
|
||||
element={
|
||||
<Diagnostics
|
||||
logCount={logCount}
|
||||
exportLogs={commands.exportLogs}
|
||||
clearLogs={commands.clearLogs}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/colour-palette" element={<ColorPalette />} />
|
||||
</Routes>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,6 @@ export default function Diagnostics({
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
<div className="mb-4 pb-2">
|
||||
<h2 className="text-xl font-semibold mb-4">Diagnostic Logs</h2>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-lg">
|
||||
<div className="mt-8 flex justify-center">
|
||||
<p className="mr-1">Log directory size:</p>
|
||||
|
||||
@@ -43,13 +43,8 @@ export default function GeneralSettingsPage({
|
||||
const startMinimizedInputId = useId();
|
||||
const startOnLoginInputId = useId();
|
||||
const connectOnStartInputId = useId();
|
||||
|
||||
return (
|
||||
<div className="container p-4">
|
||||
<div className="pb-2">
|
||||
<h2 className="text-xl font-semibold">General settings</h2>
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -11,7 +11,7 @@ interface OverviewPageProps {
|
||||
|
||||
export default function Overview(props: OverviewPageProps) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center gap-4 min-h-screen">
|
||||
<div className="flex flex-col items-center justify-center gap-4">
|
||||
<img src={logo} alt="Firezone Logo" className="w-40 h-40" />
|
||||
|
||||
<h1 className="text-6xl font-bold">Firezone</h1>
|
||||
|
||||
45
rust/gui-client/src-frontend/components/Titlebar.tsx
Normal file
45
rust/gui-client/src-frontend/components/Titlebar.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { XMarkIcon } from "@heroicons/react/16/solid";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { Navbar } from "flowbite-react";
|
||||
import React from "react";
|
||||
|
||||
export interface TitlebarProps {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export default function Titlebar({ title }: TitlebarProps) {
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
if (e.buttons === 1) {
|
||||
getCurrentWindow().startDragging();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div onMouseDown={handleMouseDown} className="select-none">
|
||||
<Navbar
|
||||
clearTheme={{
|
||||
root: {
|
||||
base: true,
|
||||
},
|
||||
}}
|
||||
theme={{
|
||||
root: {
|
||||
base: "py-1 px-2 bg-gray-100 flex flex-row space-between",
|
||||
inner: {
|
||||
base: "max-w-full",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<h2 className="text-xl font-semibold">{title}</h2>
|
||||
<div
|
||||
className="justify-self-end"
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onClick={() => getCurrentWindow().close()}
|
||||
>
|
||||
<XMarkIcon className="h-6 p-1 rounded-full hover:text-gray-700 hover:bg-gray-200" />
|
||||
</div>
|
||||
</Navbar>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
"core:path:default",
|
||||
"core:event:default",
|
||||
"core:window:default",
|
||||
"core:window:allow-start-dragging",
|
||||
"core:window:allow-close",
|
||||
"core:app:default",
|
||||
"core:resources:default",
|
||||
"core:menu:default",
|
||||
|
||||
@@ -20,12 +20,6 @@ fn main() -> ExitCode {
|
||||
let mut bootstrap_log_guard =
|
||||
Some(firezone_logging::setup_bootstrap().expect("Failed to setup bootstrap logger"));
|
||||
|
||||
// Mitigates a bug in Ubuntu 22.04 - Under Wayland, some features of the window decorations like minimizing, closing the windows, etc., doesn't work unless you double-click the titlebar first.
|
||||
// SAFETY: No other thread is running yet
|
||||
unsafe {
|
||||
std::env::set_var("GDK_BACKEND", "x11");
|
||||
}
|
||||
|
||||
let cli = Cli::parse();
|
||||
|
||||
let mut telemetry = if cli.is_telemetry_allowed() {
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"decorations": false,
|
||||
"label": "main",
|
||||
"title": "Firezone",
|
||||
"url": "index.html",
|
||||
@@ -71,7 +72,8 @@
|
||||
"resizable": false,
|
||||
"width": 900,
|
||||
"height": 500,
|
||||
"visible": false
|
||||
"visible": false,
|
||||
"transparent": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ export default function GUI({ os }: { os: OS }) {
|
||||
Fixes an issue where the order of upstream / system DNS resolvers was
|
||||
not respected.
|
||||
</ChangeItem>
|
||||
{os == OS.Linux && (
|
||||
<ChangeItem pull="10849">
|
||||
Fixes some rendering issues on Wayland-only systems.
|
||||
</ChangeItem>
|
||||
)}
|
||||
</Unreleased>
|
||||
<Entry version="1.5.8" date={new Date("2025-10-16")}>
|
||||
<ChangeItem pull="10509">
|
||||
|
||||
Reference in New Issue
Block a user