mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
make performance not quadratic revisited: persuaded swing to post all events via IdeEventQueue.postEvent(), test added
This commit is contained in:
@@ -50,17 +50,20 @@ import com.intellij.util.ui.MouseEventAdapter;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import sun.awt.AppContext;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.basic.ComboPopup;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author Vladimir Kondratyev
|
||||
@@ -108,6 +111,8 @@ public class IdeEventQueue extends EventQueue {
|
||||
* Swing event.
|
||||
*/
|
||||
private int myEventCount;
|
||||
final AtomicInteger myKeyboardEventsPosted = new AtomicInteger();
|
||||
final AtomicInteger myKeyboardEventsDispatched = new AtomicInteger();
|
||||
private boolean myIsInInputEvent;
|
||||
private AWTEvent myCurrentEvent;
|
||||
private long myLastActiveTime;
|
||||
@@ -150,8 +155,22 @@ public class IdeEventQueue extends EventQueue {
|
||||
});
|
||||
|
||||
addDispatcher(new WindowsAltSuppressor(), null);
|
||||
|
||||
abracadabraDaberBoreh();
|
||||
}
|
||||
|
||||
private void abracadabraDaberBoreh() {
|
||||
try {
|
||||
Class<?> aClass = Class.forName("sun.awt.PostEventQueue");
|
||||
Constructor<?> constructor = aClass.getDeclaredConstructor(EventQueue.class);
|
||||
constructor.setAccessible(true);
|
||||
Object postEventQueue = constructor.newInstance(this);
|
||||
AppContext.getAppContext().put("PostEventQueue", postEventQueue);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWindowManager(final WindowManagerEx windowManager) {
|
||||
myWindowManager = windowManager;
|
||||
@@ -373,6 +392,15 @@ public class IdeEventQueue extends EventQueue {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AWTEvent getNextEvent() throws InterruptedException {
|
||||
AWTEvent event = super.getNextEvent();
|
||||
if (isKeyboardEvent(event) && myKeyboardEventsDispatched.incrementAndGet() > myKeyboardEventsPosted.get()) {
|
||||
throw new RuntimeException(event + "; posted: " + myKeyboardEventsPosted + "; dispatched: " + myKeyboardEventsDispatched);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static AccessToken startActivity(boolean userActivity) {
|
||||
if (ourTransactionGuard == null && appIsLoaded()) {
|
||||
@@ -558,10 +586,7 @@ public class IdeEventQueue extends EventQueue {
|
||||
enterSuspendModeIfNeeded(e);
|
||||
}
|
||||
|
||||
myKeyboardBusy = e instanceof KeyEvent ||
|
||||
peekEvent(KeyEvent.KEY_PRESSED) != null ||
|
||||
peekEvent(KeyEvent.KEY_RELEASED) != null ||
|
||||
peekEvent(KeyEvent.KEY_TYPED) != null;
|
||||
myKeyboardBusy = e instanceof KeyEvent || myKeyboardEventsPosted.get() > myKeyboardEventsDispatched.get();
|
||||
|
||||
if (e instanceof KeyEvent) {
|
||||
if (e.getID() == KeyEvent.KEY_RELEASED && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_SHIFT) {
|
||||
@@ -1117,6 +1142,9 @@ public class IdeEventQueue extends EventQueue {
|
||||
@Override
|
||||
public void postEvent(@NotNull AWTEvent event) {
|
||||
myFrequentEventDetector.eventHappened(event);
|
||||
if (isKeyboardEvent(event)) {
|
||||
myKeyboardEventsPosted.incrementAndGet();
|
||||
}
|
||||
super.postEvent(event);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,16 @@ import com.intellij.testFramework.PlatformTestUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.InvocationEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class IdeEventQueueTest extends PlatformTestCase {
|
||||
public void testManyEvents() {
|
||||
int N = 100/*000*/;
|
||||
int N = 100000;
|
||||
PlatformTestUtil.startPerformanceTest("Event queue dispatch", 10000, () -> {
|
||||
UIUtil.dispatchAllInvocationEvents();
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
@@ -35,4 +40,59 @@ public class IdeEventQueueTest extends PlatformTestCase {
|
||||
assertEquals(N, count.get());
|
||||
}).assertTiming();
|
||||
}
|
||||
|
||||
public void testKeyboardEventsAreDetected() throws InterruptedException {
|
||||
assertTrue(EventQueue.isDispatchThread());
|
||||
|
||||
IdeEventQueue ideEventQueue = IdeEventQueue.getInstance();
|
||||
EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
|
||||
assertSame(ideEventQueue, eventQueue);
|
||||
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();
|
||||
|
||||
int posted = ideEventQueue.myKeyboardEventsPosted.get();
|
||||
int dispatched = ideEventQueue.myKeyboardEventsDispatched.get();
|
||||
KeyEvent pressX = new KeyEvent(new JLabel(), KeyEvent.KEY_PRESSED, 1, InputEvent.ALT_DOWN_MASK, 11, 'x');
|
||||
eventQueue.postEvent(pressX);
|
||||
assertEquals(posted+1, ideEventQueue.myKeyboardEventsPosted.get());
|
||||
assertEquals(dispatched, ideEventQueue.myKeyboardEventsDispatched.get());
|
||||
assertEquals(pressX, PlatformTestUtil.dispatchNextEventIfAny(ideEventQueue));
|
||||
|
||||
assertEquals(posted+1, ideEventQueue.myKeyboardEventsPosted.get());
|
||||
assertEquals(dispatched+1, ideEventQueue.myKeyboardEventsDispatched.get());
|
||||
|
||||
dispatchAllInvocationEventsOnly(ideEventQueue);
|
||||
|
||||
// do not react to other events
|
||||
MouseEvent mouseClick = new MouseEvent(new JLabel(), MouseEvent.BUTTON1, 1, InputEvent.BUTTON1_DOWN_MASK, 12, 14, 1, true);
|
||||
eventQueue.postEvent(mouseClick);
|
||||
assertEquals(posted+1, ideEventQueue.myKeyboardEventsPosted.get());
|
||||
assertEquals(dispatched+1, ideEventQueue.myKeyboardEventsDispatched.get());
|
||||
assertEquals(mouseClick, PlatformTestUtil.dispatchNextEventIfAny(ideEventQueue));
|
||||
|
||||
assertEquals(posted+1, ideEventQueue.myKeyboardEventsPosted.get());
|
||||
assertEquals(dispatched+1, ideEventQueue.myKeyboardEventsDispatched.get());
|
||||
|
||||
dispatchAllInvocationEventsOnly(ideEventQueue);
|
||||
|
||||
KeyEvent keyRelease = new KeyEvent(new JLabel(), KeyEvent.KEY_RELEASED, 1, InputEvent.ALT_DOWN_MASK, 11, 'x');
|
||||
eventQueue.postEvent(keyRelease);
|
||||
assertEquals(posted+2, ideEventQueue.myKeyboardEventsPosted.get());
|
||||
assertEquals(dispatched+1, ideEventQueue.myKeyboardEventsDispatched.get());
|
||||
assertEquals(keyRelease, PlatformTestUtil.dispatchNextEventIfAny(ideEventQueue));
|
||||
|
||||
assertEquals(posted+2, ideEventQueue.myKeyboardEventsPosted.get());
|
||||
assertEquals(dispatched+2, ideEventQueue.myKeyboardEventsDispatched.get());
|
||||
|
||||
dispatchAllInvocationEventsOnly(ideEventQueue);
|
||||
}
|
||||
|
||||
// need this because IdeEventQueue.dispatchEvent() calls invokeLater() and everybody else does too
|
||||
private static void dispatchAllInvocationEventsOnly(IdeEventQueue ideEventQueue) throws InterruptedException {
|
||||
while (true) {
|
||||
AWTEvent event = ideEventQueue.peekEvent();
|
||||
if (!(event instanceof InvocationEvent)) break;
|
||||
AWTEvent event1 = ideEventQueue.getNextEvent();
|
||||
ideEventQueue.dispatchEvent(event1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,17 +294,32 @@ public class PlatformTestUtil {
|
||||
@TestOnly
|
||||
public static void dispatchAllInvocationEventsInIdeEventQueue() throws InterruptedException {
|
||||
assert SwingUtilities.isEventDispatchThread() : Thread.currentThread();
|
||||
final EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
|
||||
final IdeEventQueue eventQueue = (IdeEventQueue)Toolkit.getDefaultToolkit().getSystemEventQueue();
|
||||
while (true) {
|
||||
AWTEvent event = eventQueue.peekEvent();
|
||||
if (event == null) break;
|
||||
AWTEvent event1 = eventQueue.getNextEvent();
|
||||
if (event1 instanceof InvocationEvent) {
|
||||
IdeEventQueue.getInstance().dispatchEvent(event1);
|
||||
}
|
||||
AWTEvent event1 = eventQueue.getNextEvent();
|
||||
if (event1 instanceof InvocationEvent) {
|
||||
eventQueue.dispatchEvent(event1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public static void dispatchAllEventsInIdeEventQueue() throws InterruptedException {
|
||||
assert SwingUtilities.isEventDispatchThread() : Thread.currentThread();
|
||||
final IdeEventQueue eventQueue = (IdeEventQueue)Toolkit.getDefaultToolkit().getSystemEventQueue();
|
||||
while (dispatchNextEventIfAny(eventQueue) != null);
|
||||
}
|
||||
|
||||
public static AWTEvent dispatchNextEventIfAny(@NotNull IdeEventQueue eventQueue) throws InterruptedException {
|
||||
AWTEvent event = eventQueue.peekEvent();
|
||||
if (event == null) return null;
|
||||
AWTEvent event1 = eventQueue.getNextEvent();
|
||||
eventQueue.dispatchEvent(event1);
|
||||
return event1;
|
||||
}
|
||||
|
||||
private static Date raidDate(Bombed bombed) {
|
||||
final Calendar instance = Calendar.getInstance();
|
||||
instance.set(Calendar.YEAR, bombed.year());
|
||||
|
||||
Reference in New Issue
Block a user