Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Too many mouse listeners registered, and all unregistered when a project is closed #1168

Open
malueck opened this issue Mar 1, 2025 · 1 comment

Comments

@malueck
Copy link
Contributor

malueck commented Mar 1, 2025

The plugins solutions that register mouse listeners register too many listeners.

This happens when I open multiple projects in MPS.
I used MPS 2024.1 and the current branch maintenance/mps20241.

I get these messages after adding some trace logs, opened 3 projects, opened a single editor and clicked a few times.
Notice how each project contributes a listener to the same editor component.

Installing DelegatingMouseListener de.itemis.mps.mouselistener.runtime.plugin.MouseListenerPlugin_ProjectPluginPart@2433481d to component jetbrains.mps.nodeEditor.NodeEditorComponent[,0,0,0x0,invalid,layout=jetbrains.mps.nodeEditor.EditorComponentLayoutManager,alignmentX=0.0,alignmentY=0.0,border=ViewBorder{myInsets=com.intellij.util.ui.JBInsets[top=0,left=0,bottom=0,right=0], myBorder=null},flags=321,maximumSize=,minimumSize=,preferredSize=]
Installing DelegatingMouseListener de.itemis.mps.mouselistener.runtime.plugin.MouseListenerPlugin_ProjectPluginPart@4bf576cf to component jetbrains.mps.nodeEditor.NodeEditorComponent[,0,0,0x0,invalid,layout=jetbrains.mps.nodeEditor.EditorComponentLayoutManager,alignmentX=0.0,alignmentY=0.0,border=ViewBorder{myInsets=com.intellij.util.ui.JBInsets[top=0,left=0,bottom=0,right=0], myBorder=null},flags=321,maximumSize=,minimumSize=,preferredSize=]
Installing DelegatingMouseListener de.itemis.mps.mouselistener.runtime.plugin.MouseListenerPlugin_ProjectPluginPart@2a80f8de to component jetbrains.mps.nodeEditor.NodeEditorComponent[,0,0,0x0,invalid,layout=jetbrains.mps.nodeEditor.EditorComponentLayoutManager,alignmentX=0.0,alignmentY=0.0,border=ViewBorder{myInsets=com.intellij.util.ui.JBInsets[top=0,left=0,bottom=0,right=0], myBorder=null},flags=321,maximumSize=,minimumSize=,preferredSize=]
Installing DelegatingMouseListener de.itemis.mps.mouselistener.runtime.plugin.MouseListenerPlugin_ProjectPluginPart@2433481d to component jetbrains.mps.nodeEditor.NodeEditorComponent[,0,0,0x0,invalid,layout=jetbrains.mps.nodeEditor.EditorComponentLayoutManager,alignmentX=0.0,alignmentY=0.0,border=ViewBorder{myInsets=com.intellij.util.ui.JBInsets[top=0,left=0,bottom=0,right=0], myBorder=null},flags=321,maximumSize=,minimumSize=,preferredSize=]
Installing DelegatingMouseListener de.itemis.mps.mouselistener.runtime.plugin.MouseListenerPlugin_ProjectPluginPart@4bf576cf to component jetbrains.mps.nodeEditor.NodeEditorComponent[,0,0,0x0,invalid,layout=jetbrains.mps.nodeEditor.EditorComponentLayoutManager,alignmentX=0.0,alignmentY=0.0,border=ViewBorder{myInsets=com.intellij.util.ui.JBInsets[top=0,left=0,bottom=0,right=0], myBorder=null},flags=321,maximumSize=,minimumSize=,preferredSize=]
Installing DelegatingMouseListener de.itemis.mps.mouselistener.runtime.plugin.MouseListenerPlugin_ProjectPluginPart@2a80f8de to component jetbrains.mps.nodeEditor.NodeEditorComponent[,0,0,0x0,invalid,layout=jetbrains.mps.nodeEditor.EditorComponentLayoutManager,alignmentX=0.0,alignmentY=0.0,border=ViewBorder{myInsets=com.intellij.util.ui.JBInsets[top=0,left=0,bottom=0,right=0], myBorder=null},flags=321,maximumSize=,minimumSize=,preferredSize=]
Click event at 86 182
Click event at 86 182
Click event at 86 182
Click event at 513 13
Click event at 513 13
Click event at 513 13
Click event at 216 351
Click event at 216 351
Click event at 216 351
Click event at 211 325
Click event at 211 325
Click event at 211 325
Click event at 210 307
Click event at 210 307
Click event at 210 307

This is from de.itemis.mps.mouselistener.runtime, but I can reproduce the same issue with de.itemis.mps.selection.runtime.

The reason is that these are project plugins and instantiated per project, so there exist multiple instances of e.g. de.itemis.mps.mouselistener.runtime.plugin.MouseListenerPlugin which all register on every editor component. Perhaps these should check each editor component if it belongs to their project before registering.

Or, even cleaner, they should just be ApplicationPluginDeclaration instead of ProjectPluginDeclaration, and so are instantiated only once.

Also, as a second bug, when I close one of the projects, all mouse listeners stop working in the others. This is because in the (project-specific) dispose(), there is a call DelegatingMouseListener.uninstallAll(); resp. DragSelectionMouseListener.uninstallAll();

Changing this to application plugins solved the issue for me locally, they are also normally reloaded like project plugins when I rebuild the plugin in MPS.
Maybe there are other reasons I don't know why these are project plugins? But if we want to refactor this, I can create a pull request with my changes.

@malueck
Copy link
Contributor Author

malueck commented Mar 6, 2025

I have to revisit my previous comment: When having multiple projects open and closing one, it is not the case that the mouse listener stops working completely. However, it looks like this works only "by chance".

One shared DelegatingMouseListener is used for the same EditorComponent by all project instances of the plugin. This leads to the same mouse listener object being registered multiple times.

It is not really clear from the AWT Javadoc if it is allowed to call java.awt.Component.addMousteListener() twice with the same object, see also https://github.com/JetBrains/JetBrainsRuntime/blob/main/src/java.desktop/share/classes/java/awt/Component.java#L5785, but it looks like the same MouseListener is indeed registered multiple times.

It seems to depend on the AWT implementation. Other implementations like https://developer.classpath.org/doc/java/awt/Component-source.html indeed say that the listener will simply be in the array twice when registered twice.

On the other hand, when uninstalling, the current implementation of DelegatingMouseListener will always remove itself only once from the list of mouse listeners because it then removes itself from its cache before the second, third etc. project unregisters it.

This means that the mouse listeners are additionally "leaking", and without the line instances.removeKey(getEditorComponent()); I can indeed produce the problem that closing one project destroys the mouse listeners in all the other projects.

After having tested this a bit further, I believe that this is not by design and relies on the specific JDK implementation of addMouseListener/removeMouseListener. I will try to convert this to an application plugin so that the mouse listener mechanics is cleaned up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant