Skip to content

Commit 2f2cbb7

Browse files
Fix various Linux bugs around GNOME Shell, improve Linux doctor checks, misc fixes (#23)
1 parent 2dd0c27 commit 2f2cbb7

File tree

8 files changed

+181
-18
lines changed

8 files changed

+181
-18
lines changed

apps/dashboard/src/App.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -240,20 +240,26 @@ const useNavData = () => {
240240
};
241241

242242
function Layout() {
243+
const [onboardingComplete] = useLocalStateZodDefault(
244+
"desktop.completedOnboarding",
245+
z.boolean(),
246+
false,
247+
);
243248
const platformInfo = usePlatformInfo();
244249
const [accessibilityCheck] = useAccessibilityCheck();
245250
const [dotfilesCheck] = useDotfilesCheck();
246251
const [gnomeExtensionCheck] = useGnomeExtensionCheck();
247252
const navData = useNavData();
248253
const error =
249-
accessibilityCheck === false ||
250-
dotfilesCheck === false ||
251-
(platformInfo &&
252-
matchesPlatformRestrictions(
253-
platformInfo,
254-
gnomeExtensionInstallCheck.platformRestrictions,
255-
) &&
256-
gnomeExtensionCheck === false);
254+
onboardingComplete &&
255+
(accessibilityCheck === false ||
256+
dotfilesCheck === false ||
257+
(platformInfo &&
258+
matchesPlatformRestrictions(
259+
platformInfo,
260+
gnomeExtensionInstallCheck.platformRestrictions,
261+
) &&
262+
gnomeExtensionCheck === false));
257263

258264
// eslint-disable-next-line
259265
const [notifCount, _] = useLocalStateZodDefault(

apps/dashboard/src/components/installs/modal/login/index.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ export default function LoginModal({ next }: { next: () => void }) {
2727

2828
// used for pkce
2929
const [currAuthRequestId, setAuthRequestId] = useAuthRequest();
30+
const [pkceTimedOut, setPkceTimedOut] = useState(false);
31+
useEffect(() => {
32+
if (pkceTimedOut) return;
33+
if (loginMethod === "pkce" && loginState === "loading") {
34+
const timer = setTimeout(() => setPkceTimedOut(true), 4000);
35+
return () => clearTimeout(timer);
36+
}
37+
}, [loginMethod, loginState, pkceTimedOut]);
3038

3139
// used for device code
3240
const [loginCode, setLoginCode] = useState<string | null>(null);
@@ -44,6 +52,12 @@ export default function LoginModal({ next }: { next: () => void }) {
4452
const auth = useAuth();
4553
const refreshAuth = useRefreshAuth();
4654

55+
useEffect(() => {
56+
// Reset the auth request id so that we don't present the "OAuth cancelled" error
57+
// to the user.
58+
setAuthRequestId("");
59+
}, [loginMethod]);
60+
4761
async function handleLogin(startUrl?: string, region?: string) {
4862
if (loginMethod === "pkce") {
4963
handlePkceAuth(startUrl, region);
@@ -54,6 +68,7 @@ export default function LoginModal({ next }: { next: () => void }) {
5468

5569
async function handlePkceAuth(issuerUrl?: string, region?: string) {
5670
setLoginState("loading");
71+
setPkceTimedOut(false);
5772
setError(null);
5873
// We need to reset the auth request state before attempting, otherwise
5974
// an expected auth request cancellation will be presented to the user
@@ -72,9 +87,6 @@ export default function LoginModal({ next }: { next: () => void }) {
7287
setAuthRequestId(init.authRequestId);
7388

7489
Native.open(init.url).catch((err) => {
75-
// Reset the auth request id so that we don't present the "OAuth cancelled" error
76-
// to the user.
77-
setAuthRequestId("");
7890
console.error(err);
7991
setError(
8092
"Failed to open the browser. As an alternative, try logging in with device code.",
@@ -195,6 +207,21 @@ export default function LoginModal({ next }: { next: () => void }) {
195207
<p className="text-center w-80">
196208
Waiting for authentication in the browser to complete...
197209
</p>
210+
{pkceTimedOut && (
211+
<div className="text-center w-80">
212+
<p>Browser not opening?</p>
213+
<Button
214+
variant="ghost"
215+
className="h-auto p-1 px-2 hover:bg-white/20 hover:text-white italic"
216+
onClick={() => {
217+
setLoginMethod("deviceCode");
218+
setLoginState("not started");
219+
}}
220+
>
221+
Try authenticating with device code
222+
</Button>
223+
</div>
224+
)}
198225
<Button
199226
variant="glass"
200227
className="self-center w-32"

apps/dashboard/src/index.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,10 @@
163163
#301673;
164164
}
165165
}
166+
167+
/* Currently an issue with Linux webkit CSS variables */
168+
/* https://github.com/tailwindlabs/tailwindcss/issues/13844 */
169+
.backdrop-blur-lg {
170+
backdrop-filter: blur(16px);
171+
-webkit-backdrop-filter: blur(16px);
172+
}

build-config/buildspec-linux-ubuntu.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ phases:
88
run-as: root
99
commands:
1010
- apt-get update
11-
- apt-get install -y -qq build-essential pkg-config jq dpkg curl wget zsh zstd cmake clang libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libdbus-1-dev libwebkit2gtk-4.0-37 libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev valac libibus-1.0-dev libglib2.0-dev sqlite3 libxdo-dev protobuf-compiler libfuse2 dpkg-sig
11+
- apt-get install -y -qq build-essential pkg-config jq dpkg curl wget zsh zstd cmake clang libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libdbus-1-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev valac libibus-1.0-dev libglib2.0-dev sqlite3 libxdo-dev protobuf-compiler libfuse2 dpkg-sig
1212
pre_build:
1313
commands:
1414
- export HOME=/home/codebuild-user

crates/dbus/src/dbus/gnome_shell.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,29 @@ pub enum ExtensionState {
9292

9393
/// The extension is loaded and in an error state.
9494
Errored,
95+
96+
// Unused
97+
OutOfDate,
98+
Downloading,
99+
Initialized,
100+
Deactivating,
101+
Activating,
95102
}
96103

97104
impl ExtensionState {
98105
fn from_u32(state: u32) -> Result<Self, ExtensionsError> {
99106
// This mapping is done just from observation, not sure if this is entirely correct in
100107
// practice.
108+
// Reference: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/misc/extensionUtils.js?ref_type=heads#L15
101109
match state {
102110
1 => Ok(ExtensionState::Enabled),
103111
2 => Ok(ExtensionState::Disabled),
104112
3 => Ok(ExtensionState::Errored),
113+
4 => Ok(ExtensionState::OutOfDate),
114+
5 => Ok(ExtensionState::Downloading),
115+
6 => Ok(ExtensionState::Initialized),
116+
7 => Ok(ExtensionState::Deactivating),
117+
8 => Ok(ExtensionState::Activating),
105118
_ => Err(ExtensionsError::UnknownState(state)),
106119
}
107120
}
@@ -112,6 +125,11 @@ impl ExtensionState {
112125
ExtensionState::Enabled => Some(1),
113126
ExtensionState::Disabled => Some(2),
114127
ExtensionState::Errored => Some(3),
128+
ExtensionState::OutOfDate => Some(4),
129+
ExtensionState::Downloading => Some(5),
130+
ExtensionState::Initialized => Some(6),
131+
ExtensionState::Deactivating => Some(7),
132+
ExtensionState::Activating => Some(8),
115133
}
116134
}
117135
}
@@ -162,7 +180,6 @@ where
162180
Ok(ExtensionInstallationStatus::Enabled)
163181
}
164182
},
165-
ExtensionState::Errored => Ok(ExtensionInstallationStatus::Errored),
166183
ExtensionState::NotLoaded => {
167184
// This could mean the extension is *technically* installed but just not loaded into
168185
// gnome shell's js jit, or the extension literally is not installed.
@@ -199,6 +216,10 @@ where
199216

200217
Ok(ExtensionInstallationStatus::NotInstalled)
201218
},
219+
ExtensionState::Errored => Ok(ExtensionInstallationStatus::Errored),
220+
// Currently not sure how to handle the other states as of yet, therefore default
221+
// to assuming an error and requiring manual intervention by the user.
222+
_ => Ok(ExtensionInstallationStatus::Errored),
202223
}
203224
}
204225

crates/fig_desktop/src/install.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ pub async fn initialize_fig_dir(env: &fig_os_shim::Env) -> anyhow::Result<()> {
390390
async fn run_linux_install(ctx: Arc<Context>, settings: Arc<fig_settings::Settings>, state: Arc<fig_settings::State>) {
391391
use dbus::gnome_shell::ShellExtensions;
392392
use fig_settings::State;
393+
use fig_util::system_info::linux::get_display_server;
393394

394395
// install binaries under home local bin
395396
if ctx.env().in_appimage() {
@@ -402,6 +403,24 @@ async fn run_linux_install(ctx: Arc<Context>, settings: Arc<fig_settings::Settin
402403
});
403404
}
404405

406+
// Important we log an error if we cannot detect the display server in use.
407+
// If this isn't wayland or x11, the user will probably just see a blank screen.
408+
match get_display_server(&ctx) {
409+
Ok(_) => (),
410+
Err(fig_util::Error::UnknownDisplayServer(server)) => {
411+
error!(
412+
"Unknown value set for XDG_SESSION_TYPE: {}. This must be set to x11 or wayland.",
413+
server
414+
);
415+
},
416+
Err(err) => {
417+
error!(
418+
"Unknown error occurred when detecting the display server: {:?}. Is XDG_SESSION_TYPE set to x11 or wayland?",
419+
err
420+
);
421+
},
422+
}
423+
405424
// GNOME Shell Extension
406425
{
407426
let ctx_clone = Arc::clone(&ctx);
@@ -457,8 +476,21 @@ where
457476
bundled_gnome_extension_version_path,
458477
bundled_gnome_extension_zip_path,
459478
};
479+
use fig_util::system_info::linux::{
480+
DisplayServer,
481+
get_display_server,
482+
};
460483
use tracing::debug;
461484

485+
let display_server = get_display_server(ctx)?;
486+
if display_server != DisplayServer::Wayland {
487+
debug!(
488+
"Detected non-Wayland display server: `{:?}`. Not installing the extension.",
489+
display_server
490+
);
491+
return Ok(());
492+
}
493+
462494
if !state.get_bool_or("desktop.gnomeExtensionInstallationPermissionGranted", false) {
463495
debug!("Permission is not granted to install GNOME extension, doing nothing.");
464496
return Ok(());
@@ -961,6 +993,7 @@ echo "{binary_name} {version}"
961993
.await
962994
.unwrap()
963995
.with_env_var("APPIMAGE", "1")
996+
.with_env_var("XDG_SESSION_TYPE", "wayland")
964997
.with_os(Os::Linux)
965998
.with_running_processes(&[GNOME_SHELL_PROCESS_NAME])
966999
.build_fake();
@@ -1017,6 +1050,38 @@ echo "{binary_name} {version}"
10171050
.unwrap();
10181051
assert!(matches!(status, ExtensionInstallationStatus::NotInstalled));
10191052
}
1053+
1054+
#[tokio::test]
1055+
async fn test_extension_not_installed_if_not_wayland() {
1056+
let ctx = Context::builder()
1057+
.with_test_home()
1058+
.await
1059+
.unwrap()
1060+
.with_env_var("APPIMAGE", "1")
1061+
.with_os(Os::Linux)
1062+
.with_running_processes(&[GNOME_SHELL_PROCESS_NAME])
1063+
.build_fake();
1064+
let shell_extensions = ShellExtensions::new_fake(Arc::downgrade(&ctx));
1065+
let extension_version = 1;
1066+
write_extension_bundle(
1067+
&ctx,
1068+
&shell_extensions.extension_uuid().await.unwrap(),
1069+
extension_version,
1070+
)
1071+
.await;
1072+
let state = State::from_slice(&[("desktop.gnomeExtensionInstallationPermissionGranted", true.into())]);
1073+
1074+
// When
1075+
install_gnome_shell_extension(&ctx, &shell_extensions, &state)
1076+
.await
1077+
.unwrap();
1078+
1079+
// Then
1080+
let status = get_extension_status(&ctx, &shell_extensions, Some(extension_version))
1081+
.await
1082+
.unwrap();
1083+
assert!(matches!(status, ExtensionInstallationStatus::NotInstalled));
1084+
}
10201085
}
10211086

10221087
#[cfg(target_os = "linux")]

crates/q_cli/src/cli/doctor/checks/linux.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,33 @@ pub async fn get_linux_context() -> eyre::Result<LinuxContext> {
6666
Ok(LinuxContext::new(ctx, shell_extensions))
6767
}
6868

69+
pub struct DisplayServerCheck;
70+
71+
#[async_trait]
72+
impl DoctorCheck<LinuxContext> for DisplayServerCheck {
73+
fn name(&self) -> Cow<'static, str> {
74+
"Display Server Check".into()
75+
}
76+
77+
async fn get_type(&self, _: &LinuxContext, _: Platform) -> DoctorCheckType {
78+
DoctorCheckType::NormalCheck
79+
}
80+
81+
async fn check(&self, ctx: &LinuxContext) -> Result<(), DoctorError> {
82+
match get_display_server(&ctx.ctx) {
83+
Ok(_) => Ok(()),
84+
Err(fig_util::Error::UnknownDisplayServer(server)) => Err(doctor_error!(
85+
"Unknown value set for XDG_SESSION_TYPE: {}. This must be set to x11 or wayland.",
86+
server
87+
)),
88+
Err(err) => Err(doctor_error!(
89+
"Unknown error occurred when detecting the display server: {:?}. Is XDG_SESSION_TYPE set to x11 or wayland?",
90+
err
91+
)),
92+
}
93+
}
94+
}
95+
6996
pub struct IBusEnvCheck;
7097

7198
#[async_trait]
@@ -290,11 +317,19 @@ impl DoctorCheck<LinuxContext> for IBusRunningCheck {
290317
// -r - replace current ibus-daemon, if running
291318
// -x - execute XIM server
292319
// -R - restarts other ibus subprocesses if they end
293-
let output = Command::new("ibus-daemon").arg("-drxR").output()?;
294-
if !output.status.success() {
295-
let stdout = String::from_utf8_lossy(&output.stdout);
296-
let stderr = String::from_utf8_lossy(&output.stderr);
297-
eyre::bail!("ibus-daemon launch failed:\nstdout: {stdout}\nstderr: {stderr}\n");
320+
match Command::new("ibus-daemon").arg("-drxR").output() {
321+
Ok(output) if !output.status.success() => {
322+
let stdout = String::from_utf8_lossy(&output.stdout);
323+
let stderr = String::from_utf8_lossy(&output.stderr);
324+
eyre::bail!("ibus-daemon launch failed:\nstdout: {stdout}\nstderr: {stderr}\n");
325+
},
326+
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
327+
eyre::bail!("Could not find ibus-daemon. Is ibus installed?");
328+
},
329+
Err(err) => {
330+
eyre::bail!("An unknown error occurred launching ibus-daemon: {:?}", err);
331+
}
332+
Ok(_) => ()
298333
}
299334
// Wait some time for ibus-daemon to launch.
300335
std::thread::sleep(std::time::Duration::from_secs(1));

crates/q_cli/src/cli/doctor/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2237,6 +2237,7 @@ pub async fn doctor_cli(all: bool, strict: bool) -> Result<ExitCode> {
22372237
#[cfg(target_os = "linux")]
22382238
{
22392239
use checks::linux::{
2240+
DisplayServerCheck,
22402241
GnomeExtensionCheck,
22412242
IBusConnectionCheck,
22422243
IBusEnvCheck,
@@ -2249,6 +2250,7 @@ pub async fn doctor_cli(all: bool, strict: bool) -> Result<ExitCode> {
22492250
run_checks_with_context(
22502251
"Let's check Linux integrations",
22512252
vec![
2253+
&DisplayServerCheck,
22522254
&IBusEnvCheck,
22532255
&GnomeExtensionCheck,
22542256
&IBusRunningCheck,

0 commit comments

Comments
 (0)