Skip to content

Commit cd442a3

Browse files
committed
added functionality to reset pressed keys after 1 second. this fixes an issue that a key got virtualy stuck sometimes
1 parent f4b0a75 commit cd442a3

File tree

2 files changed

+62
-70
lines changed

2 files changed

+62
-70
lines changed

src/main/java/de/doubleslash/keeptime/Main.java

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public class Main extends Application {
4747
private Model model;
4848
private Controller controller;
4949

50+
private ViewController viewController;
51+
52+
private GlobalScreenListener globalScreenListener;
53+
5054
@Override
5155
public void init() throws Exception {
5256
LOG.info("Starting KeepTime {}", VERSION);
@@ -64,7 +68,6 @@ public void start(final Stage primaryStage) throws Exception {
6468

6569
LOG.debug("Reading configuration");
6670

67-
// TODO there should just be one instance of settings in the repo
6871
final List<Settings> settingsList = model.settingsRepository.findAll();
6972
final Settings settings;
7073
if (settingsList.isEmpty()) {
@@ -118,17 +121,8 @@ public void start(final Stage primaryStage) throws Exception {
118121
}
119122

120123
primaryStage.setOnHiding((we) -> {
121-
// shutdown();
122124
popupViewStage.close();
123-
globalScreenListener.register(false); // deregister, as this will keep app running
124-
});
125-
126-
Runtime.getRuntime().addShutdownHook(new Thread() {
127-
@Override
128-
public void run() {
129-
// TODO this does not work - spring uses own hooks and may already be shutdown
130-
// shutdown();
131-
}
125+
globalScreenListener.shutdown(); // deregister, as this will keep app running
132126
});
133127

134128
try {
@@ -144,8 +138,6 @@ public void run() {
144138
}
145139
}
146140

147-
GlobalScreenListener globalScreenListener;
148-
149141
private void initialisePopupUI(final Stage primaryStage) throws IOException {
150142
// TODO register only if it is enabled
151143
globalScreenListener = new GlobalScreenListener();
@@ -174,18 +166,8 @@ private void initialisePopupUI(final Stage primaryStage) throws IOException {
174166
viewControllerPopupController.setStage(popupViewStage, popupScene);
175167
viewControllerPopupController.setController(controller, model);
176168
globalScreenListener.setViewController(viewControllerPopupController);
177-
178169
}
179170

180-
private void shutdown() {
181-
LOG.info("Shutting down");
182-
// viewController.changeProject(model.idleProject, 0); // TODO not so nice (view has the comments for the current
183-
// // job)
184-
controller.shutdown();
185-
}
186-
187-
ViewController viewController;
188-
189171
private void initialiseUI(final Stage primaryStage) {
190172
try {
191173
Pane mainPane;
@@ -210,17 +192,12 @@ private void initialiseUI(final Stage primaryStage) {
210192
@Override
211193
public void handle(final WindowEvent event) {
212194
LOG.info("On close request");
213-
// shutdown();
214-
// Platform.exit();
215195
}
216196
});
217197

218198
viewController = loader.getController();
219199
// Give the controller access to the main app.
220200
viewController.setStage(primaryStage);
221-
222-
// controller = springContext.getBean(Controller.class, model, new RealDateProvider());
223-
224201
viewController.setController(controller, model);
225202

226203
primaryStage.show();
Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package de.doubleslash.keeptime.viewPopup;
22

33
import java.awt.Point;
4+
import java.util.concurrent.Executors;
5+
import java.util.concurrent.ScheduledExecutorService;
6+
import java.util.concurrent.ScheduledFuture;
7+
import java.util.concurrent.TimeUnit;
48
import java.util.logging.Level;
59
import java.util.logging.Logger;
610

@@ -9,30 +13,47 @@
913
import org.jnativehook.keyboard.NativeKeyEvent;
1014
import org.jnativehook.keyboard.NativeKeyListener;
1115
import org.jnativehook.mouse.NativeMouseEvent;
12-
import org.jnativehook.mouse.NativeMouseInputListener;
13-
import org.jnativehook.mouse.NativeMouseWheelEvent;
14-
import org.jnativehook.mouse.NativeMouseWheelListener;
16+
import org.jnativehook.mouse.NativeMouseMotionListener;
1517
import org.slf4j.LoggerFactory;
1618

1719
import javafx.application.Platform;
1820

19-
public class GlobalScreenListener implements NativeKeyListener, NativeMouseInputListener, NativeMouseWheelListener {
20-
private static final int LEFT_WINDOWS = 91;
21+
/**
22+
* Class captures all key events and implements a hotkey mechanism. Needs to be stopped by calling shutdown.
23+
*
24+
* @author nmutter
25+
*/
26+
public class GlobalScreenListener implements NativeKeyListener, NativeMouseMotionListener {
27+
private static final int LEFT_WINDOWS_CODE = 91;
2128

22-
private static final int LEFT_CTRL = 162;
29+
private static final int LEFT_CTRL_CODE = 162;
2330

2431
private final org.slf4j.Logger LOG = LoggerFactory.getLogger(this.getClass());
2532

2633
private ViewControllerPopup viewController;
2734

35+
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
36+
37+
private ScheduledFuture<?> resetKeyPressesFuture;
38+
39+
private final Runnable resetPressedKeysRunnable = () -> {
40+
LOG.debug("Resetting pressed keys");
41+
controllPressed = false;
42+
windowsPressed = false;
43+
};
44+
45+
// TODO find better hotkey - maybe configurable?
46+
private boolean controllPressed = false;
47+
private boolean windowsPressed = false;
48+
49+
private Point mouseLocation = new Point(0, 0);
50+
2851
public GlobalScreenListener() {
2952

30-
setGlobalScreenLogLevel();
53+
disableJNativeHookLogger();
3154

3255
GlobalScreen.addNativeKeyListener(this);
33-
GlobalScreen.addNativeMouseListener(this);
3456
GlobalScreen.addNativeMouseMotionListener(this);
35-
GlobalScreen.addNativeMouseWheelListener(this);
3657
}
3758

3859
public void register(final boolean register) {
@@ -49,7 +70,7 @@ public void register(final boolean register) {
4970
}
5071
}
5172

52-
public void setGlobalScreenLogLevel() {
73+
private void disableJNativeHookLogger() {
5374
// Get the logger for "org.jnativehook" and set the level to off.
5475
final Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
5576

@@ -63,61 +84,55 @@ public void setViewController(final ViewControllerPopup viewController) {
6384
this.viewController = viewController;
6485
}
6586

66-
boolean controllPressed = false;
67-
boolean windowsPressed = false;
68-
69-
private Point mouseLocation = new Point(0, 0);
70-
7187
@Override
7288
public void nativeKeyPressed(final NativeKeyEvent e) {
73-
// TODO find better hotkey - maybe configurable?
74-
7589
switch (e.getRawCode()) {
76-
case LEFT_CTRL:
90+
case LEFT_CTRL_CODE:
7791
controllPressed = true;
7892
break;
79-
case LEFT_WINDOWS:
93+
case LEFT_WINDOWS_CODE:
8094
windowsPressed = true;
8195
break;
8296
default:
97+
return;
8398
}
8499

85100
if (controllPressed && windowsPressed) {
86101
Platform.runLater(() -> {
87102
viewController.show(mouseLocation);
88103
});
89104
}
105+
106+
// Logic to reset key presses if no input for n seconds
107+
// if u win+l (lock) windows, the key release events are not received
108+
if (resetKeyPressesFuture != null) {
109+
resetKeyPressesFuture.cancel(false);
110+
}
111+
resetKeyPressesFuture = executor.schedule(resetPressedKeysRunnable, 1, TimeUnit.SECONDS);
90112
}
91113

92114
@Override
93115
public void nativeKeyReleased(final NativeKeyEvent e) {
94116
switch (e.getRawCode()) {
95-
case LEFT_CTRL:
117+
case LEFT_CTRL_CODE:
96118
controllPressed = false;
97119
break;
98-
case LEFT_WINDOWS:
120+
case LEFT_WINDOWS_CODE:
99121
windowsPressed = false;
100122
break;
101123
default:
124+
return;
102125
}
103-
}
104-
105-
@Override
106-
public void nativeKeyTyped(final NativeKeyEvent e) {}
107-
108-
@Override
109-
public void nativeMouseClicked(final NativeMouseEvent e) {}
110-
111-
@Override
112-
public void nativeMousePressed(final NativeMouseEvent nativeEvent) {
113-
// TODO Auto-generated method stub
114126

127+
if (!controllPressed && !windowsPressed && resetKeyPressesFuture != null) {
128+
// nothing to reset so we can cancel
129+
resetKeyPressesFuture.cancel(false);
130+
}
115131
}
116132

117133
@Override
118-
public void nativeMouseReleased(final NativeMouseEvent nativeEvent) {
119-
// TODO Auto-generated method stub
120-
134+
public void nativeKeyTyped(final NativeKeyEvent e) {
135+
// Not needed
121136
}
122137

123138
@Override
@@ -127,14 +142,14 @@ public void nativeMouseMoved(final NativeMouseEvent nativeEvent) {
127142

128143
@Override
129144
public void nativeMouseDragged(final NativeMouseEvent nativeEvent) {
130-
// TODO Auto-generated method stub
131-
145+
// Not needed
132146
}
133147

134-
@Override
135-
public void nativeMouseWheelMoved(final NativeMouseWheelEvent nativeEvent) {
136-
// TODO Auto-generated method stub
137-
148+
/**
149+
* Shuts down the threads and listeners. Instance is not usable anymore after calling this.
150+
*/
151+
public void shutdown() {
152+
executor.shutdown();
153+
register(false);
138154
}
139-
140155
}

0 commit comments

Comments
 (0)