[UI, Wayland] IJPL-213477 Show a notification about the Wayland toolkit

Space-RevId: b29c0d226b8444efaeff1ebbeafffa74819e1ac0

GitOrigin-RevId: 43ee3e50993e4324cb463a5b71fc6da369a4e0f7
This commit is contained in:
Sergei Tachenov
2026-01-22 16:11:37 +00:00
committed by intellij-monorepo-bot
parent 3a75ed6522
commit 959100f701
3 changed files with 128 additions and 0 deletions
@@ -3261,3 +3261,6 @@ folder.open.processor.name=Folder
code.style.cache.dump.success=Code style cache dump successful
code.style.cache.dump.failed=Code style cache dump failed
notification.group.wayland=Wayland
notification.wayland=Wayland native mode is enabled by default in the IDE. For more details see <a href="">this blog post</a>
@@ -0,0 +1,115 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.ui
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate
import com.intellij.ide.IdeBundle
import com.intellij.ide.util.PropertiesComponent
import com.intellij.idea.AppMode
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.event.EditorMouseEvent
import com.intellij.openapi.editor.event.EditorMouseListener
import com.intellij.openapi.editor.event.EditorMouseMotionListener
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiFile
import com.intellij.util.ui.StartupUiUtil
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.TimeSource
internal class WaylandUserActivityTracker : TypedHandlerDelegate(), EditorMouseListener, EditorMouseMotionListener {
override fun mouseClicked(event: EditorMouseEvent) {
userActivityHappened(event.editor.project)
}
override fun mouseMoved(e: EditorMouseEvent) {
userActivityHappened(e.editor.project)
}
override fun charTyped(
c: Char,
project: Project,
editor: Editor,
file: PsiFile,
): Result {
userActivityHappened(project)
return Result.CONTINUE
}
private fun userActivityHappened(project: Project?) {
if (!StartupUiUtil.isWaylandToolkit()) return
if (AppMode.isRemoteDevHost()) return // these notification are only for the frontend or the monolith
// The state is extracted into a separate service because this helper class can have several instances
// (working in different roles: a mouse listener, a typing listener...).
WaylandNotificationService.getInstance().userActivityHappened(project)
}
}
@Service(Service.Level.APP)
internal class WaylandNotificationService {
private var firstActivityTime: TimeSource.Monotonic.ValueTimeMark? = null
private var previousActivityTime: TimeSource.Monotonic.ValueTimeMark? = null
private var notificationAlreadyShown: Boolean? = null
companion object {
@JvmStatic fun getInstance(): WaylandNotificationService = service()
}
fun userActivityHappened(project: Project?) {
if (isNotificationAlreadyShown()) return
val currentActivityTime = TimeSource.Monotonic.markNow()
var firstActivityTime = firstActivityTime
if (firstActivityTime == null) {
firstActivityTime = currentActivityTime
this.firstActivityTime = firstActivityTime
}
val previousActivityTime = previousActivityTime
if (Registry.getInstance().isLoaded && previousActivityTime != null) {
val sinceFirstActivity = currentActivityTime - firstActivityTime
val sincePreviousActivity = currentActivityTime - previousActivityTime
if (
sinceFirstActivity >= delaySinceFirstActivity() &&
sincePreviousActivity >= delaySincePreviousActivity()
) {
showNotification(project)
setNotificationAlreadyShown()
}
}
this.previousActivityTime = currentActivityTime
}
private fun isNotificationAlreadyShown(): Boolean {
var notificationAlreadyShown = notificationAlreadyShown
if (notificationAlreadyShown != null) return notificationAlreadyShown
notificationAlreadyShown = PropertiesComponent.getInstance().getBoolean(WAYLAND_NOTIFICATION_ALREADY_SHOWN, false)
this.notificationAlreadyShown = notificationAlreadyShown
return notificationAlreadyShown
}
private fun setNotificationAlreadyShown() {
this.notificationAlreadyShown = true
PropertiesComponent.getInstance().setValue(WAYLAND_NOTIFICATION_ALREADY_SHOWN, true)
}
}
private fun delaySinceFirstActivity(): Duration = Registry.intValue("wayland.notification.delay.after.first.activity", defaultValue = 20).minutes
private fun delaySincePreviousActivity(): Duration = Registry.intValue("wayland.notification.delay.after.previous.activity", defaultValue = 1).minutes
private fun showNotification(project: Project?) {
Notification(
"Wayland",
IdeBundle.message("notification.wayland", "https://jetbrains.com/TODO-blog-post"), // TODO
NotificationType.INFORMATION
).notify(project)
}
private const val WAYLAND_NOTIFICATION_ALREADY_SHOWN = "WAYLAND_NOTIFICATION_ALREADY_SHOWN"
@@ -2051,6 +2051,16 @@
<applicationService serviceInterface="com.intellij.configurationStore.defaults.DefaultSettingsService" open="true"
serviceImplementation="com.intellij.configurationStore.defaults.DefaultSettingsServiceImpl" />
<!-- Remove all this stuff when the notification is no longer needed -->
<typedHandler implementation="com.intellij.ide.ui.WaylandUserActivityTracker" />
<editorFactoryMouseListener implementation="com.intellij.ide.ui.WaylandUserActivityTracker" />
<editorFactoryMouseMotionListener implementation="com.intellij.ide.ui.WaylandUserActivityTracker" />
<registryKey key="wayland.notification.delay.after.first.activity" defaultValue="20"
description="Show the notification about the native Wayland toolkit after this many minutes have passed since the first user activity" />
<registryKey key="wayland.notification.delay.after.previous.activity" defaultValue="1"
description="Show the notification about the native Wayland toolkit after this many minutes have passed since the previous user activity" />
<notificationGroup id="Wayland" displayType="BALLOON" bundle="messages.IdeBundle" key="notification.group.wayland" isLogByDefault="true"/>
</extensions>
<applicationListeners>