@@ -4,13 +4,16 @@ import com.intellij.openapi.Disposable
44import com.intellij.openapi.components.Service
55import com.intellij.openapi.components.service
66import com.intellij.openapi.diagnostic.Logger
7+ import com.intellij.openapi.project.Project
78import com.intellij.openapi.util.Disposer
89import com.intellij.openapi.util.SystemInfo
910import kotlinx.coroutines.CancellationException
1011import kotlinx.coroutines.CoroutineScope
1112import kotlinx.coroutines.delay
1213import kotlinx.coroutines.isActive
1314import kotlinx.coroutines.launch
15+ import kotlinx.datetime.Clock
16+ import org.digma.intellij.plugin.common.isProjectValid
1417import org.digma.intellij.plugin.errorreporting.ErrorReporter
1518import org.digma.intellij.plugin.log.Log
1619import java.awt.Component
@@ -20,7 +23,7 @@ import java.beans.PropertyChangeListener
2023import java.util.Queue
2124import java.util.concurrent.ConcurrentLinkedQueue
2225import javax.swing.JComponent
23- import javax.swing.JPanel
26+ import kotlin.time.Duration.Companion.seconds
2427
2528
2629/* *
@@ -43,7 +46,7 @@ class ReloadObserver(cs: CoroutineScope) {
4346 try {
4447 val event = propertyChangedEvents.poll()
4548 if (event == null ) {
46- delay(2000 )
49+ delay(1000 )
4750 } else {
4851 checkChangesAndReload(event)
4952 }
@@ -58,7 +61,18 @@ class ReloadObserver(cs: CoroutineScope) {
5861 }
5962
6063
61- fun register (jcefWrapperPanel : JPanel , jcefUiComponent : JComponent , parentDisposable : Disposable ) {
64+ companion object {
65+ private fun buildGraphicsDeviceList (): List <String > {
66+ return GraphicsEnvironment .getLocalGraphicsEnvironment().screenDevices.map { it.iDstring }
67+ }
68+
69+ private fun deviceListEquals (list1 : List <String >, list2 : List <String >): Boolean {
70+ return list1.size == list2.size && list1.containsAll(list2)
71+ }
72+ }
73+
74+
75+ fun register (project : Project , appName : String , jcefUiComponent : JComponent , parentDisposable : Disposable ) {
6276
6377 if (GraphicsEnvironment .isHeadless()) {
6478 Log .log(logger::trace, " GraphicsEnvironment is headless, not registering components" )
@@ -71,7 +85,7 @@ class ReloadObserver(cs: CoroutineScope) {
7185 }
7286
7387 val jcefPropertyChangeListener =
74- MyPropertyChangeListener (jcefUiComponent, " ${jcefWrapperPanel.javaClass.simpleName} .jcefUiComponent" )
88+ MyPropertyChangeListener (project, jcefUiComponent, " $appName .jcefUiComponent" )
7589 jcefUiComponent.addPropertyChangeListener(jcefPropertyChangeListener)
7690
7791 Disposer .register(parentDisposable) {
@@ -84,53 +98,90 @@ class ReloadObserver(cs: CoroutineScope) {
8498 private suspend fun checkChangesAndReload (event : Pair <ComponentDetails , PropertyChangeEvent >) {
8599 try {
86100
87- Log .log(logger::trace, " checking graphics changes for component {}" , event.first.componentName)
88-
89101 val componentDetails = event.first
90102 val component = componentDetails.component
103+ val componentName = componentDetails.componentName
104+ val project = componentDetails.project
105+
106+ Log .log(logger::trace, " checking graphics changes for component {} in project {}" , componentName, project.name)
107+
108+
109+ if (! isProjectValid(project)) {
110+ Log .log(
111+ logger::trace,
112+ " skipping checking graphics changes for component {} in project {} because project is invalid" ,
113+ componentName,
114+ project.name
115+ )
116+ return
117+ }
91118
92119 val currentDisplayMode = component.graphicsConfiguration?.device?.displayMode
93120 val currentGraphicsDevice = component.graphicsConfiguration?.device?.iDstring
94121 if (currentGraphicsDevice != componentDetails.graphicDevice) {
95122 Log .log(
96123 logger::trace,
97- " component {} moved to another graphics device, oldValue:{},newValue:{}" ,
98- componentDetails.componentName,
124+ " component {} in project {} moved to another graphics device, oldValue:{},newValue:{}" ,
125+ componentName,
126+ project.name,
99127 componentDetails.graphicDevice,
100128 currentGraphicsDevice
101129 )
102130
103131 componentDetails.graphicDevice = currentGraphicsDevice
104132 componentDetails.displayMode = currentDisplayMode
105133
106- delay(1000 )
134+ // it may take the GraphicsEnvironment some time to refresh the screen devices. it depends on native OS calls timing,
135+ // it may take the OS some seconds to notify the GraphicsEnvironment about changes.
136+ // if we don't catch the change we may miss a reload when it's needed.
137+ // so wait for some seconds to try to detect the change. if there is no change no harm will be done and this
138+ // coroutine will just finish doing nothing
139+ var currentGraphicsDeviceList = buildGraphicsDeviceList()
140+ val endWait = Clock .System .now() + 3 .seconds
141+ while (deviceListEquals(currentGraphicsDeviceList, componentDetails.graphicsDeviceList) &&
142+ Clock .System .now() < endWait
143+ ) {
144+ Log .log(logger::trace, " waiting for device list to refresh for component {} in project {}" , componentName, project.name)
145+ delay(100 )
146+ currentGraphicsDeviceList = buildGraphicsDeviceList()
147+ }
107148
108- val currentGraphicsDeviceNumber = GraphicsEnvironment .getLocalGraphicsEnvironment().screenDevices.size
109- if (currentGraphicsDeviceNumber != componentDetails.graphicsDeviceNumber) {
149+ if (! deviceListEquals(currentGraphicsDeviceList, componentDetails.graphicsDeviceList)) {
110150 Log .log(
111151 logger::trace,
112- " graphics device number has changed for component {} ,oldValue:{},newValue:{}" ,
113- componentDetails.componentName,
114- componentDetails.graphicsDeviceNumber,
115- currentGraphicsDeviceNumber
152+ " graphics device list has changed for component {} in project {},oldValue:{},newValue:{}" ,
153+ componentName,
154+ project.name,
155+ componentDetails.graphicsDeviceList,
156+ currentGraphicsDeviceList
116157 )
117- componentDetails.graphicsDeviceNumber = currentGraphicsDeviceNumber
158+ componentDetails.graphicsDeviceList = currentGraphicsDeviceList
118159
119- service<ReloadService >().reload()
160+ service<ReloadService >().reload(project)
161+ } else {
162+ Log .log(
163+ logger::trace,
164+ " graphics device list has NOT changed for component {} in project {},oldValue:{},newValue:{}" ,
165+ componentName,
166+ project.name,
167+ componentDetails.graphicsDeviceList,
168+ currentGraphicsDeviceList
169+ )
120170 }
121171 } else if (currentDisplayMode != componentDetails.displayMode) {
122172 Log .log(
123173 logger::trace,
124- " component {} display mode has changed, oldValue:{},newValue:{}" ,
125- componentDetails.componentName,
174+ " component {} in project {} display mode has changed, oldValue:{},newValue:{}" ,
175+ componentName,
176+ project.name,
126177 componentDetails.displayMode,
127178 currentDisplayMode
128179 )
129180 componentDetails.displayMode = currentDisplayMode
130181
131- service<ReloadService >().reload()
182+ service<ReloadService >().reload(project )
132183 } else {
133- Log .log(logger::trace, " no graphics changes for component {}" , componentDetails. componentName)
184+ Log .log(logger::trace, " no graphics changes for component {} in project {} " , componentName, project.name )
134185 }
135186
136187 } catch (e: Throwable ) {
@@ -139,24 +190,36 @@ class ReloadObserver(cs: CoroutineScope) {
139190 }
140191
141192
142- private class ComponentDetails (val component : Component , val componentName : String ) {
193+ private class ComponentDetails (val project : Project , val component : Component , val componentName : String ) {
143194 var graphicDevice = component.graphicsConfiguration?.device?.iDstring
144195 var displayMode = component.graphicsConfiguration?.device?.displayMode
145- var graphicsDeviceNumber = GraphicsEnvironment .getLocalGraphicsEnvironment().screenDevices.size
196+ var graphicsDeviceList = buildGraphicsDeviceList()
197+
198+
199+ override fun toString (): String {
200+ return " ComponentDetails(project=${project.name} , " +
201+ " component=${component::class .java.simpleName} , " +
202+ " componentName='$componentName ', " +
203+ " graphicDevice=$graphicDevice , " +
204+ " displayMode=$displayMode , " +
205+ " graphicsDeviceList=$graphicsDeviceList )"
206+ }
207+
146208 }
147209
148210
149211 private inner class MyPropertyChangeListener (
212+ project : Project ,
150213 component : Component ,
151214 private val componentName : String ,
152215 ) :
153216 PropertyChangeListener {
154217
155- private val componentDetails = ComponentDetails (component, componentName)
218+ private val componentDetails = ComponentDetails (project, component, componentName)
156219
157220 override fun propertyChange (evt : PropertyChangeEvent ) {
158221 if (evt.propertyName == " graphicsConfiguration" ) {
159- Log .log(logger::trace, " got PropertyChangeEvent for {}, {} " , componentName, evt)
222+ Log .log(logger::trace, " got PropertyChangeEvent for {}, event {}, component details {} " , componentName, evt, componentDetails )
160223 // putting the events in a queue ensures we process them in the order they arrive.
161224 // just launching a coroutine here does not guarantee order and may cause wrong decisions
162225 // about reloading
0 commit comments