propane 2.8.0.pre-java → 2.9.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1069 @@
1
+ /* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
+
3
+ /*
4
+ Part of the Processing project - http://processing.org
5
+
6
+ Copyright (c) 2015 The Processing Foundation
7
+
8
+ This library is free software; you can redistribute it and/or
9
+ modify it under the terms of the GNU Lesser General Public
10
+ License as published by the Free Software Foundation, version 2.1.
11
+
12
+ This library is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
+ Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General
18
+ Public License along with this library; if not, write to the
19
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20
+ Boston, MA 02111-1307 USA
21
+ */
22
+
23
+ package processing.javafx;
24
+
25
+ import com.sun.glass.ui.Screen;
26
+
27
+ import java.awt.GraphicsDevice;
28
+ import java.awt.GraphicsEnvironment;
29
+ import java.awt.Rectangle;
30
+ import java.net.URL;
31
+ import java.util.ArrayList;
32
+ import java.util.HashMap;
33
+ import java.util.List;
34
+ import java.util.Map;
35
+ import java.util.concurrent.SynchronousQueue;
36
+
37
+ import javafx.animation.Animation;
38
+ import javafx.animation.KeyFrame;
39
+ import javafx.animation.Timeline;
40
+ import javafx.application.Application;
41
+ import javafx.application.Platform;
42
+ import javafx.beans.value.ChangeListener;
43
+ import javafx.beans.value.ObservableValue;
44
+ import javafx.event.ActionEvent;
45
+ import javafx.event.EventHandler;
46
+ import javafx.event.EventType;
47
+ import javafx.scene.Cursor;
48
+ import javafx.scene.ImageCursor;
49
+ import javafx.scene.Scene;
50
+ import javafx.scene.SceneAntialiasing;
51
+ import javafx.scene.canvas.Canvas;
52
+ import javafx.scene.image.Image;
53
+ import javafx.scene.image.PixelFormat;
54
+ import javafx.scene.image.WritableImage;
55
+ import javafx.scene.input.KeyCode;
56
+ import javafx.scene.input.KeyEvent;
57
+ import javafx.scene.input.MouseEvent;
58
+ import javafx.scene.input.ScrollEvent;
59
+ import javafx.scene.layout.StackPane;
60
+ import javafx.stage.Stage;
61
+ import javafx.stage.StageStyle;
62
+ import javafx.stage.WindowEvent;
63
+ import javafx.util.Duration;
64
+ import processing.core.*;
65
+
66
+
67
+ public class PSurfaceFX implements PSurface {
68
+ PApplet sketch;
69
+
70
+ PGraphicsFX2D fx;
71
+ Stage stage;
72
+ Canvas canvas;
73
+
74
+ final Animation animation;
75
+ float frameRate = 60;
76
+
77
+ private SynchronousQueue<Throwable> drawExceptionQueue = new SynchronousQueue<>();
78
+
79
+ public PSurfaceFX(PGraphicsFX2D graphics) {
80
+ fx = graphics;
81
+ canvas = new ResizableCanvas();
82
+
83
+ // set up main drawing loop
84
+ KeyFrame keyFrame = new KeyFrame(Duration.millis(1000),
85
+ new EventHandler<ActionEvent>() {
86
+ public void handle(ActionEvent event) {
87
+ long startNanoTime = System.nanoTime();
88
+ try {
89
+ sketch.handleDraw();
90
+ } catch (Throwable e) {
91
+ // Let exception handler thread crash with our exception
92
+ drawExceptionQueue.offer(e);
93
+ // Stop animating right now so nothing runs afterwards
94
+ // and crash frame can be for example traced by println()
95
+ animation.stop();
96
+ return;
97
+ }
98
+ long drawNanos = System.nanoTime() - startNanoTime;
99
+
100
+ if (sketch.exitCalled()) {
101
+ // using Platform.runLater() didn't work
102
+ // Platform.runLater(new Runnable() {
103
+ // public void run() {
104
+ // instead of System.exit(), safely shut down JavaFX this way
105
+ Platform.exit();
106
+ // }
107
+ // });
108
+ }
109
+ if (sketch.frameCount > 5) {
110
+ animation.setRate(-PApplet.min(1e9f / drawNanos, frameRate));
111
+ }
112
+ }
113
+ });
114
+ animation = new Timeline(keyFrame);
115
+ animation.setCycleCount(Animation.INDEFINITE);
116
+
117
+ // key frame has duration of 1 second, so the rate of the animation
118
+ // should be set to frames per second
119
+
120
+ // setting rate to negative so that event fires at the start of
121
+ // the key frame and first frame is drawn immediately
122
+ animation.setRate(-frameRate);
123
+ }
124
+
125
+
126
+ public Object getNative() {
127
+ return canvas;
128
+ }
129
+
130
+
131
+ class ResizableCanvas extends Canvas {
132
+
133
+ public ResizableCanvas() {
134
+ widthProperty().addListener(new ChangeListener<Number>() {
135
+ @Override
136
+ public void changed(ObservableValue<? extends Number> value,
137
+ Number oldWidth, Number newWidth) {
138
+ // sketch.width = newWidth.intValue();
139
+ sketch.setSize(newWidth.intValue(), sketch.height);
140
+ // draw();
141
+ fx.setSize(sketch.width, sketch.height);
142
+ }
143
+ });
144
+ heightProperty().addListener(new ChangeListener<Number>() {
145
+ @Override
146
+ public void changed(ObservableValue<? extends Number> value,
147
+ Number oldHeight, Number newHeight) {
148
+ // sketch.height = newHeight.intValue();
149
+ sketch.setSize(sketch.width, newHeight.intValue());
150
+ // draw();
151
+ fx.setSize(sketch.width, sketch.height);
152
+ }
153
+ });
154
+
155
+ //addEventHandler(eventType, eventHandler);
156
+
157
+ EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
158
+ public void handle(MouseEvent e) {
159
+ fxMouseEvent(e);
160
+ }
161
+ };
162
+
163
+ setOnMousePressed(mouseHandler);
164
+ setOnMouseReleased(mouseHandler);
165
+ setOnMouseClicked(mouseHandler);
166
+ setOnMouseEntered(mouseHandler);
167
+ setOnMouseExited(mouseHandler);
168
+
169
+ setOnMouseDragged(mouseHandler);
170
+ setOnMouseMoved(mouseHandler);
171
+
172
+ setOnScroll(new EventHandler<ScrollEvent>() {
173
+ public void handle(ScrollEvent e) {
174
+ fxScrollEvent(e);
175
+ }
176
+ });
177
+
178
+ EventHandler<KeyEvent> keyHandler = new EventHandler<KeyEvent>() {
179
+ public void handle(KeyEvent e) {
180
+ fxKeyEvent(e);
181
+ }
182
+ };
183
+
184
+ setOnKeyPressed(keyHandler);
185
+ setOnKeyReleased(keyHandler);
186
+ setOnKeyTyped(keyHandler);
187
+
188
+ setFocusTraversable(false); // prevent tab from de-focusing
189
+
190
+ focusedProperty().addListener(new ChangeListener<Boolean>() {
191
+ public void changed(ObservableValue<? extends Boolean> value,
192
+ Boolean oldValue, Boolean newValue) {
193
+ if (newValue.booleanValue()) {
194
+ sketch.focused = true;
195
+ sketch.focusGained();
196
+ } else {
197
+ sketch.focused = false;
198
+ sketch.focusLost();
199
+ }
200
+ }
201
+ });
202
+ }
203
+
204
+ public Stage getStage() {
205
+ return stage;
206
+ }
207
+
208
+ @Override
209
+ public boolean isResizable() {
210
+ return true;
211
+ }
212
+
213
+ @Override
214
+ public double prefWidth(double height) {
215
+ return getWidth();
216
+ }
217
+
218
+ @Override
219
+ public double prefHeight(double width) {
220
+ return getHeight();
221
+ }
222
+ }
223
+
224
+
225
+ public void initOffscreen(PApplet sketch) {
226
+ }
227
+
228
+
229
+ // public Component initComponent(PApplet sketch) {
230
+ // return null;
231
+ // }
232
+
233
+
234
+ static public class PApplicationFX extends Application {
235
+ static public PSurfaceFX surface;
236
+ // static String title; // title set at launch
237
+ // static boolean resizable; // set at launch
238
+
239
+ public PApplicationFX() { }
240
+
241
+ @Override
242
+ public void start(final Stage stage) {
243
+ // if (title != null) {
244
+ // stage.setTitle(title);
245
+ // }
246
+
247
+ PApplet sketch = surface.sketch;
248
+
249
+ float renderScale = Screen.getMainScreen().getRenderScale();
250
+ if (PApplet.platform == PConstants.MACOSX) {
251
+ for (Screen s : Screen.getScreens()) {
252
+ renderScale = Math.max(renderScale, s.getRenderScale());
253
+ }
254
+ }
255
+ float uiScale = Screen.getMainScreen().getUIScale();
256
+ if (sketch.pixelDensity == 2 && renderScale < 2) {
257
+ sketch.pixelDensity = 1;
258
+ sketch.g.pixelDensity = 1;
259
+ System.err.println("pixelDensity(2) is not available for this display");
260
+ }
261
+
262
+ // Use AWT display code, because FX orders screens in different way
263
+ GraphicsDevice displayDevice = null;
264
+
265
+ GraphicsEnvironment environment =
266
+ GraphicsEnvironment.getLocalGraphicsEnvironment();
267
+
268
+ int displayNum = sketch.sketchDisplay();
269
+ if (displayNum > 0) { // if -1, use the default device
270
+ GraphicsDevice[] devices = environment.getScreenDevices();
271
+ if (displayNum <= devices.length) {
272
+ displayDevice = devices[displayNum - 1];
273
+ } else {
274
+ System.err.format("Display %d does not exist, " +
275
+ "using the default display instead.%n", displayNum);
276
+ for (int i = 0; i < devices.length; i++) {
277
+ System.err.format("Display %d is %s%n", (i+1), devices[i]);
278
+ }
279
+ }
280
+ }
281
+ if (displayDevice == null) {
282
+ displayDevice = environment.getDefaultScreenDevice();
283
+ }
284
+
285
+ boolean fullScreen = sketch.sketchFullScreen();
286
+ boolean spanDisplays = sketch.sketchDisplay() == PConstants.SPAN;
287
+
288
+ Rectangle primaryScreenRect = displayDevice.getDefaultConfiguration().getBounds();
289
+ Rectangle screenRect = primaryScreenRect;
290
+ if (fullScreen || spanDisplays) {
291
+ double minX = screenRect.getMinX();
292
+ double maxX = screenRect.getMaxX();
293
+ double minY = screenRect.getMinY();
294
+ double maxY = screenRect.getMaxY();
295
+ if (spanDisplays) {
296
+ for (GraphicsDevice s : environment.getScreenDevices()) {
297
+ Rectangle bounds = s.getDefaultConfiguration().getBounds();
298
+ minX = Math.min(minX, bounds.getMinX());
299
+ maxX = Math.max(maxX, bounds.getMaxX());
300
+ minY = Math.min(minY, bounds.getMinY());
301
+ maxY = Math.max(maxY, bounds.getMaxY());
302
+ }
303
+ }
304
+ screenRect = new Rectangle((int) minX, (int) minY,
305
+ (int) (maxX - minX), (int) (maxY - minY));
306
+ }
307
+
308
+ // Set the displayWidth/Height variables inside PApplet, so that they're
309
+ // usable and can even be returned by the sketchWidth()/Height() methods.
310
+ sketch.displayWidth = (int) screenRect.getWidth();
311
+ sketch.displayHeight = (int) screenRect.getHeight();
312
+
313
+ int sketchWidth = sketch.sketchWidth();
314
+ int sketchHeight = sketch.sketchHeight();
315
+
316
+ if (fullScreen || spanDisplays) {
317
+ sketchWidth = (int) (screenRect.getWidth() / uiScale);
318
+ sketchHeight = (int) (screenRect.getHeight() / uiScale);
319
+
320
+ stage.initStyle(StageStyle.UNDECORATED);
321
+ stage.setX(screenRect.getMinX() / uiScale);
322
+ stage.setY(screenRect.getMinY() / uiScale);
323
+ stage.setWidth(screenRect.getWidth() / uiScale);
324
+ stage.setHeight(screenRect.getHeight() / uiScale);
325
+ }
326
+
327
+ Canvas canvas = surface.canvas;
328
+ surface.fx.context = canvas.getGraphicsContext2D();
329
+ StackPane stackPane = new StackPane();
330
+ stackPane.getChildren().add(canvas);
331
+ canvas.widthProperty().bind(stackPane.widthProperty());
332
+ canvas.heightProperty().bind(stackPane.heightProperty());
333
+
334
+ int width = sketchWidth;
335
+ int height = sketchHeight;
336
+ int smooth = sketch.sketchSmooth();
337
+
338
+ // Workaround for https://bugs.openjdk.java.net/browse/JDK-8136495
339
+ // https://github.com/processing/processing/issues/3823
340
+ if ((PApplet.platform == PConstants.MACOSX ||
341
+ PApplet.platform == PConstants.LINUX) &&
342
+ PApplet.javaVersionName.compareTo("1.8.0_60") >= 0 &&
343
+ PApplet.javaVersionName.compareTo("1.8.0_72") < 0) {
344
+ System.err.println("smooth() disabled for JavaFX with this Java version due to Oracle bug");
345
+ System.err.println("https://github.com/processing/processing/issues/3795");
346
+ smooth = 0;
347
+ }
348
+
349
+ SceneAntialiasing sceneAntialiasing = (smooth == 0) ?
350
+ SceneAntialiasing.DISABLED : SceneAntialiasing.BALANCED;
351
+
352
+ stage.setScene(new Scene(stackPane, width, height, false, sceneAntialiasing));
353
+
354
+ // initFrame in different thread is waiting for
355
+ // the stage, assign it only when it is all set up
356
+ surface.stage = stage;
357
+ }
358
+
359
+ @Override
360
+ public void stop() throws Exception {
361
+ surface.sketch.dispose();
362
+ }
363
+ }
364
+
365
+
366
+ //public Frame initFrame(PApplet sketch, java.awt.Color backgroundColor,
367
+ public void initFrame(PApplet sketch) {/*, int backgroundColor,
368
+ int deviceIndex, boolean fullScreen,
369
+ boolean spanDisplays) {*/
370
+ this.sketch = sketch;
371
+ PApplicationFX.surface = this;
372
+ //Frame frame = new DummyFrame();
373
+ new Thread(new Runnable() {
374
+ public void run() {
375
+ Application.launch(PApplicationFX.class);
376
+ }
377
+ }).start();
378
+
379
+ // wait for stage to be initialized on its own thread before continuing
380
+ while (stage == null) {
381
+ try {
382
+ //System.out.println("waiting for launch");
383
+ Thread.sleep(5);
384
+ } catch (InterruptedException e) { }
385
+ }
386
+
387
+ startExceptionHandlerThread();
388
+
389
+ setProcessingIcon(stage);
390
+ }
391
+
392
+
393
+ private void startExceptionHandlerThread() {
394
+ Thread exceptionHandlerThread = new Thread(() -> {
395
+ Throwable drawException;
396
+ try {
397
+ drawException = drawExceptionQueue.take();
398
+ } catch (InterruptedException e) {
399
+ return;
400
+ }
401
+ // Adapted from PSurfaceJOGL
402
+ if (drawException != null) {
403
+ if (drawException instanceof ThreadDeath) {
404
+ // System.out.println("caught ThreadDeath");
405
+ // throw (ThreadDeath)cause;
406
+ } else if (drawException instanceof RuntimeException) {
407
+ throw (RuntimeException) drawException;
408
+ } else if (drawException instanceof UnsatisfiedLinkError) {
409
+ throw new UnsatisfiedLinkError(drawException.getMessage());
410
+ } else {
411
+ throw new RuntimeException(drawException);
412
+ }
413
+ }
414
+ });
415
+ exceptionHandlerThread.setDaemon(true);
416
+ exceptionHandlerThread.setName("Processing-FX-ExceptionHandler");
417
+ exceptionHandlerThread.start();
418
+ }
419
+
420
+
421
+ /** Set the window (and dock, or whatever necessary) title. */
422
+ public void setTitle(String title) {
423
+ // PApplicationFX.title = title; // store this in case the stage still null
424
+ // if (stage != null) {
425
+ stage.setTitle(title);
426
+ // }
427
+ }
428
+
429
+
430
+ /** Show or hide the window. */
431
+ @Override
432
+ public void setVisible(final boolean visible) {
433
+ Platform.runLater(new Runnable() {
434
+ public void run() {
435
+ if (visible) {
436
+ stage.show();
437
+ canvas.requestFocus();
438
+ } else {
439
+ stage.hide();
440
+ }
441
+ }
442
+ });
443
+ }
444
+
445
+
446
+ /** Set true if we want to resize things (default is not resizable) */
447
+ public void setResizable(boolean resizable) {
448
+ // PApplicationFX.resizable = resizable;
449
+ // if (stage != null) {
450
+ stage.setResizable(resizable);
451
+ // }
452
+ }
453
+
454
+
455
+ public void setIcon(PImage icon) {
456
+ int w = icon.pixelWidth;
457
+ int h = icon.pixelHeight;
458
+ WritableImage im = new WritableImage(w, h);
459
+ im.getPixelWriter().setPixels(0, 0, w, h,
460
+ PixelFormat.getIntArgbInstance(),
461
+ icon.pixels,
462
+ 0, w);
463
+
464
+ Stage stage = (Stage) canvas.getScene().getWindow();
465
+ stage.getIcons().clear();
466
+ stage.getIcons().add(im);
467
+ }
468
+
469
+
470
+ List<Image> iconImages;
471
+
472
+ protected void setProcessingIcon(Stage stage) {
473
+ // Adapted from PSurfaceAWT
474
+ // Note: FX chooses wrong icon size, should be fixed in Java 9, see:
475
+ // https://bugs.openjdk.java.net/browse/JDK-8091186
476
+ // Removing smaller sizes helps a bit, but big ones are downsized
477
+ try {
478
+ if (iconImages == null) {
479
+ iconImages = new ArrayList<>();
480
+ final int[] sizes = { 48, 64, 128, 256, 512 };
481
+
482
+ for (int sz : sizes) {
483
+ URL url = PApplet.class.getResource("/icon/icon-" + sz + ".png");
484
+ Image image = new Image(url.toString());
485
+ iconImages.add(image);
486
+ }
487
+ }
488
+ List<Image> icons = stage.getIcons();
489
+ icons.clear();
490
+ icons.addAll(iconImages);
491
+ } catch (Exception e) { } // harmless; keep this to ourselves
492
+ }
493
+
494
+
495
+ @Override
496
+ public void setAlwaysOnTop(boolean always) {
497
+ stage.setAlwaysOnTop(always);
498
+ }
499
+
500
+
501
+ /*
502
+ @Override
503
+ public void placeWindow(int[] location) {
504
+ //setFrameSize();
505
+
506
+ if (location != null) {
507
+ // a specific location was received from the Runner
508
+ // (applet has been run more than once, user placed window)
509
+ stage.setX(location[0]);
510
+ stage.setY(location[1]);
511
+
512
+ } else { // just center on screen
513
+ // Can't use frame.setLocationRelativeTo(null) because it sends the
514
+ // frame to the main display, which undermines the --display setting.
515
+ // frame.setLocation(screenRect.x + (screenRect.width - sketchWidth) / 2,
516
+ // screenRect.y + (screenRect.height - sketchHeight) / 2);
517
+ }
518
+ if (stage.getY() < 0) {
519
+ // Windows actually allows you to place frames where they can't be
520
+ // closed. Awesome. http://dev.processing.org/bugs/show_bug.cgi?id=1508
521
+ //frame.setLocation(frameLoc.x, 30);
522
+ stage.setY(30);
523
+ }
524
+
525
+ //setCanvasSize();
526
+
527
+ // TODO add window closing behavior
528
+ // frame.addWindowListener(new WindowAdapter() {
529
+ // @Override
530
+ // public void windowClosing(WindowEvent e) {
531
+ // System.exit(0);
532
+ // }
533
+ // });
534
+
535
+ // TODO handle frame resizing events
536
+ // setupFrameResizeListener();
537
+
538
+ if (sketch.getGraphics().displayable()) {
539
+ setVisible(true);
540
+ }
541
+ }
542
+ */
543
+
544
+
545
+ @Override
546
+ public void placeWindow(int[] location, int[] editorLocation) {
547
+ if (sketch.sketchFullScreen()) {
548
+ PApplet.hideMenuBar();
549
+ return;
550
+ }
551
+
552
+ int wide = sketch.width; // stage.getWidth() is NaN here
553
+ //int high = sketch.height; // stage.getHeight()
554
+
555
+ if (location != null) {
556
+ // a specific location was received from the Runner
557
+ // (applet has been run more than once, user placed window)
558
+ stage.setX(location[0]);
559
+ stage.setY(location[1]);
560
+
561
+ } else if (editorLocation != null) {
562
+ int locationX = editorLocation[0] - 20;
563
+ int locationY = editorLocation[1];
564
+
565
+ if (locationX - wide > 10) {
566
+ // if it fits to the left of the window
567
+ stage.setX(locationX - wide);
568
+ stage.setY(locationY);
569
+
570
+ } else { // doesn't fit
571
+ stage.centerOnScreen();
572
+ }
573
+ } else { // just center on screen
574
+ stage.centerOnScreen();
575
+ }
576
+ }
577
+
578
+
579
+ // http://download.java.net/jdk8/jfxdocs/javafx/stage/Stage.html#setFullScreenExitHint-java.lang.String-
580
+ // http://download.java.net/jdk8/jfxdocs/javafx/stage/Stage.html#setFullScreenExitKeyCombination-javafx.scene.input.KeyCombination-
581
+ public void placePresent(int stopColor) {
582
+ // TODO Auto-generated method stub
583
+ PApplet.hideMenuBar();
584
+ }
585
+
586
+
587
+ @Override
588
+ public void setupExternalMessages() {
589
+ stage.xProperty().addListener(new ChangeListener<Number>() {
590
+ @Override
591
+ public void changed(ObservableValue<? extends Number> value,
592
+ Number oldX, Number newX) {
593
+ sketch.frameMoved(newX.intValue(), stage.yProperty().intValue());
594
+ }
595
+ });
596
+
597
+ stage.yProperty().addListener(new ChangeListener<Number>() {
598
+ @Override
599
+ public void changed(ObservableValue<? extends Number> value,
600
+ Number oldY, Number newY) {
601
+ sketch.frameMoved(stage.xProperty().intValue(), newY.intValue());
602
+ }
603
+ });
604
+
605
+ stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
606
+ public void handle(WindowEvent we) {
607
+ sketch.exit();
608
+ }
609
+ });
610
+ }
611
+
612
+
613
+ public void setLocation(int x, int y) {
614
+ stage.setX(x);
615
+ stage.setY(y);
616
+ }
617
+
618
+
619
+ public void setSize(int wide, int high) {
620
+ // When the surface is set to resizable via surface.setResizable(true),
621
+ // a crash may occur if the user sets the window to size zero.
622
+ // https://github.com/processing/processing/issues/5052
623
+ if (high <= 0) {
624
+ high = 1;
625
+ }
626
+ if (wide <= 0) {
627
+ wide = 1;
628
+ }
629
+
630
+ //System.out.format("%s.setSize(%d, %d)%n", getClass().getSimpleName(), width, height);
631
+ Scene scene = stage.getScene();
632
+ double decorH = stage.getWidth() - scene.getWidth();
633
+ double decorV = stage.getHeight() - scene.getHeight();
634
+ stage.setWidth(wide + decorH);
635
+ stage.setHeight(high + decorV);
636
+ fx.setSize(wide, high);
637
+ }
638
+
639
+
640
+ // public Component getComponent() {
641
+ // return null;
642
+ // }
643
+
644
+
645
+ public void setSmooth(int level) {
646
+ // TODO Auto-generated method stub
647
+
648
+ }
649
+
650
+
651
+ public void setFrameRate(float fps) {
652
+ // setting rate to negative so that event fires at the start of
653
+ // the key frame and first frame is drawn immediately
654
+ if (fps > 0) {
655
+ frameRate = fps;
656
+ animation.setRate(-frameRate);
657
+ }
658
+ }
659
+
660
+
661
+ // @Override
662
+ // public void requestFocus() {
663
+ // canvas.requestFocus();
664
+ // }
665
+
666
+ Cursor lastCursor = Cursor.DEFAULT;
667
+
668
+ public void setCursor(int kind) {
669
+ Cursor c;
670
+ switch (kind) {
671
+ case PConstants.ARROW: c = Cursor.DEFAULT; break;
672
+ case PConstants.CROSS: c = Cursor.CROSSHAIR; break;
673
+ case PConstants.HAND: c = Cursor.HAND; break;
674
+ case PConstants.MOVE: c = Cursor.MOVE; break;
675
+ case PConstants.TEXT: c = Cursor.TEXT; break;
676
+ case PConstants.WAIT: c = Cursor.WAIT; break;
677
+ default: c = Cursor.DEFAULT; break;
678
+ }
679
+ lastCursor = c;
680
+ canvas.getScene().setCursor(c);
681
+ }
682
+
683
+
684
+ public void setCursor(PImage image, int hotspotX, int hotspotY) {
685
+ int w = image.pixelWidth;
686
+ int h = image.pixelHeight;
687
+ WritableImage im = new WritableImage(w, h);
688
+ im.getPixelWriter().setPixels(0, 0, w, h,
689
+ PixelFormat.getIntArgbInstance(),
690
+ image.pixels,
691
+ 0, w);
692
+ ImageCursor c = new ImageCursor(im, hotspotX, hotspotY);
693
+ lastCursor = c;
694
+ canvas.getScene().setCursor(c);
695
+ }
696
+
697
+
698
+ public void showCursor() {
699
+ canvas.getScene().setCursor(lastCursor);
700
+ }
701
+
702
+
703
+ public void hideCursor() {
704
+ canvas.getScene().setCursor(Cursor.NONE);
705
+ }
706
+
707
+
708
+ public void startThread() {
709
+ animation.play();
710
+ }
711
+
712
+
713
+ public void pauseThread() {
714
+ animation.pause();
715
+ }
716
+
717
+
718
+ public void resumeThread() {
719
+ animation.play();
720
+ }
721
+
722
+
723
+ public boolean stopThread() {
724
+ animation.stop();
725
+ return true;
726
+ }
727
+
728
+
729
+ public boolean isStopped() {
730
+ return animation.getStatus() == Animation.Status.STOPPED;
731
+ }
732
+
733
+
734
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
735
+
736
+
737
+ /*
738
+ protected void addListeners() {
739
+
740
+ canvas.addMouseListener(new MouseListener() {
741
+
742
+ public void mousePressed(java.awt.event.MouseEvent e) {
743
+ nativeMouseEvent(e);
744
+ }
745
+
746
+ public void mouseReleased(java.awt.event.MouseEvent e) {
747
+ nativeMouseEvent(e);
748
+ }
749
+
750
+ public void mouseClicked(java.awt.event.MouseEvent e) {
751
+ nativeMouseEvent(e);
752
+ }
753
+
754
+ public void mouseEntered(java.awt.event.MouseEvent e) {
755
+ nativeMouseEvent(e);
756
+ }
757
+
758
+ public void mouseExited(java.awt.event.MouseEvent e) {
759
+ nativeMouseEvent(e);
760
+ }
761
+ });
762
+
763
+ canvas.addMouseMotionListener(new MouseMotionListener() {
764
+
765
+ public void mouseDragged(java.awt.event.MouseEvent e) {
766
+ nativeMouseEvent(e);
767
+ }
768
+
769
+ public void mouseMoved(java.awt.event.MouseEvent e) {
770
+ nativeMouseEvent(e);
771
+ }
772
+ });
773
+
774
+ canvas.addMouseWheelListener(new MouseWheelListener() {
775
+
776
+ public void mouseWheelMoved(MouseWheelEvent e) {
777
+ nativeMouseEvent(e);
778
+ }
779
+ });
780
+
781
+ canvas.addKeyListener(new KeyListener() {
782
+
783
+ public void keyPressed(java.awt.event.KeyEvent e) {
784
+ nativeKeyEvent(e);
785
+ }
786
+
787
+
788
+ public void keyReleased(java.awt.event.KeyEvent e) {
789
+ nativeKeyEvent(e);
790
+ }
791
+
792
+
793
+ public void keyTyped(java.awt.event.KeyEvent e) {
794
+ nativeKeyEvent(e);
795
+ }
796
+ });
797
+
798
+ canvas.addFocusListener(new FocusListener() {
799
+
800
+ public void focusGained(FocusEvent e) {
801
+ sketch.focused = true;
802
+ sketch.focusGained();
803
+ }
804
+
805
+ public void focusLost(FocusEvent e) {
806
+ sketch.focused = false;
807
+ sketch.focusLost();
808
+ }
809
+ });
810
+ }
811
+ */
812
+
813
+
814
+ static Map<EventType<? extends MouseEvent>, Integer> mouseMap =
815
+ new HashMap<EventType<? extends MouseEvent>, Integer>();
816
+ static {
817
+ mouseMap.put(MouseEvent.MOUSE_PRESSED, processing.event.MouseEvent.PRESS);
818
+ mouseMap.put(MouseEvent.MOUSE_RELEASED, processing.event.MouseEvent.RELEASE);
819
+ mouseMap.put(MouseEvent.MOUSE_CLICKED, processing.event.MouseEvent.CLICK);
820
+ mouseMap.put(MouseEvent.MOUSE_DRAGGED, processing.event.MouseEvent.DRAG);
821
+ mouseMap.put(MouseEvent.MOUSE_MOVED, processing.event.MouseEvent.MOVE);
822
+ mouseMap.put(MouseEvent.MOUSE_ENTERED, processing.event.MouseEvent.ENTER);
823
+ mouseMap.put(MouseEvent.MOUSE_EXITED, processing.event.MouseEvent.EXIT);
824
+ }
825
+
826
+ protected void fxMouseEvent(MouseEvent fxEvent) {
827
+ // the 'amount' is the number of button clicks for a click event,
828
+ // or the number of steps/clicks on the wheel for a mouse wheel event.
829
+ int count = fxEvent.getClickCount();
830
+
831
+ int action = mouseMap.get(fxEvent.getEventType());
832
+
833
+ int modifiers = 0;
834
+ if (fxEvent.isShiftDown()) {
835
+ modifiers |= processing.event.Event.SHIFT;
836
+ }
837
+ if (fxEvent.isControlDown()) {
838
+ modifiers |= processing.event.Event.CTRL;
839
+ }
840
+ if (fxEvent.isMetaDown()) {
841
+ modifiers |= processing.event.Event.META;
842
+ }
843
+ if (fxEvent.isAltDown()) {
844
+ modifiers |= processing.event.Event.ALT;
845
+ }
846
+
847
+ int button = 0;
848
+ switch (fxEvent.getButton()) {
849
+ case PRIMARY:
850
+ button = PConstants.LEFT;
851
+ break;
852
+ case SECONDARY:
853
+ button = PConstants.RIGHT;
854
+ break;
855
+ case MIDDLE:
856
+ button = PConstants.CENTER;
857
+ break;
858
+ case NONE:
859
+ // not currently handled
860
+ break;
861
+ }
862
+
863
+ // If running on Mac OS, allow ctrl-click as right mouse.
864
+ // Verified to be necessary with Java 8u45.
865
+ if (PApplet.platform == PConstants.MACOSX &&
866
+ fxEvent.isControlDown() &&
867
+ button == PConstants.LEFT) {
868
+ button = PConstants.RIGHT;
869
+ }
870
+
871
+ //long when = nativeEvent.getWhen(); // from AWT
872
+ long when = System.currentTimeMillis();
873
+ int x = (int) fxEvent.getX(); // getSceneX()?
874
+ int y = (int) fxEvent.getY();
875
+
876
+ sketch.postEvent(new processing.event.MouseEvent(fxEvent, when,
877
+ action, modifiers,
878
+ x, y, button, count));
879
+ }
880
+
881
+ // https://docs.oracle.com/javase/8/javafx/api/javafx/scene/input/ScrollEvent.html
882
+ protected void fxScrollEvent(ScrollEvent fxEvent) {
883
+ // the number of steps/clicks on the wheel for a mouse wheel event.
884
+ int count = (int) -(fxEvent.getDeltaY() / fxEvent.getMultiplierY());
885
+
886
+ int action = processing.event.MouseEvent.WHEEL;
887
+
888
+ int modifiers = 0;
889
+ if (fxEvent.isShiftDown()) {
890
+ modifiers |= processing.event.Event.SHIFT;
891
+ }
892
+ if (fxEvent.isControlDown()) {
893
+ modifiers |= processing.event.Event.CTRL;
894
+ }
895
+ if (fxEvent.isMetaDown()) {
896
+ modifiers |= processing.event.Event.META;
897
+ }
898
+ if (fxEvent.isAltDown()) {
899
+ modifiers |= processing.event.Event.ALT;
900
+ }
901
+
902
+ // FX does not supply button info
903
+ int button = 0;
904
+
905
+ long when = System.currentTimeMillis();
906
+ int x = (int) fxEvent.getX(); // getSceneX()?
907
+ int y = (int) fxEvent.getY();
908
+
909
+ sketch.postEvent(new processing.event.MouseEvent(fxEvent, when,
910
+ action, modifiers,
911
+ x, y, button, count));
912
+ }
913
+
914
+
915
+ protected void fxKeyEvent(javafx.scene.input.KeyEvent fxEvent) {
916
+ int action = 0;
917
+ EventType<? extends KeyEvent> et = fxEvent.getEventType();
918
+ if (et == KeyEvent.KEY_PRESSED) {
919
+ action = processing.event.KeyEvent.PRESS;
920
+ } else if (et == KeyEvent.KEY_RELEASED) {
921
+ action = processing.event.KeyEvent.RELEASE;
922
+ } else if (et == KeyEvent.KEY_TYPED) {
923
+ action = processing.event.KeyEvent.TYPE;
924
+ }
925
+
926
+ int modifiers = 0;
927
+ if (fxEvent.isShiftDown()) {
928
+ modifiers |= processing.event.Event.SHIFT;
929
+ }
930
+ if (fxEvent.isControlDown()) {
931
+ modifiers |= processing.event.Event.CTRL;
932
+ }
933
+ if (fxEvent.isMetaDown()) {
934
+ modifiers |= processing.event.Event.META;
935
+ }
936
+ if (fxEvent.isAltDown()) {
937
+ modifiers |= processing.event.Event.ALT;
938
+ }
939
+
940
+ long when = System.currentTimeMillis();
941
+
942
+ char keyChar = getKeyChar(fxEvent);
943
+ int keyCode = getKeyCode(fxEvent);
944
+ sketch.postEvent(new processing.event.KeyEvent(fxEvent, when,
945
+ action, modifiers,
946
+ keyChar, keyCode));
947
+ }
948
+
949
+
950
+ @SuppressWarnings("deprecation")
951
+ private int getKeyCode(KeyEvent fxEvent) {
952
+ if (fxEvent.getEventType() == KeyEvent.KEY_TYPED) {
953
+ return 0;
954
+ }
955
+
956
+ KeyCode kc = fxEvent.getCode();
957
+ switch (kc) {
958
+ case ALT_GRAPH:
959
+ return PConstants.ALT;
960
+ default:
961
+ break;
962
+ }
963
+ return kc.impl_getCode();
964
+ }
965
+
966
+
967
+ @SuppressWarnings("deprecation")
968
+ private char getKeyChar(KeyEvent fxEvent) {
969
+ KeyCode kc = fxEvent.getCode();
970
+
971
+ // Overriding chars for some
972
+ // KEY_PRESSED and KEY_RELEASED events
973
+ switch (kc) {
974
+ case UP:
975
+ case KP_UP:
976
+ case DOWN:
977
+ case KP_DOWN:
978
+ case LEFT:
979
+ case KP_LEFT:
980
+ case RIGHT:
981
+ case KP_RIGHT:
982
+ case ALT:
983
+ case ALT_GRAPH:
984
+ case CONTROL:
985
+ case SHIFT:
986
+ case CAPS:
987
+ case META:
988
+ case WINDOWS:
989
+ case CONTEXT_MENU:
990
+ case HOME:
991
+ case PAGE_UP:
992
+ case PAGE_DOWN:
993
+ case END:
994
+ case PAUSE:
995
+ case PRINTSCREEN:
996
+ case INSERT:
997
+ case NUM_LOCK:
998
+ case SCROLL_LOCK:
999
+ case F1:
1000
+ case F2:
1001
+ case F3:
1002
+ case F4:
1003
+ case F5:
1004
+ case F6:
1005
+ case F7:
1006
+ case F8:
1007
+ case F9:
1008
+ case F10:
1009
+ case F11:
1010
+ case F12:
1011
+ return PConstants.CODED;
1012
+ case ENTER:
1013
+ return '\n';
1014
+ case DIVIDE:
1015
+ return '/';
1016
+ case MULTIPLY:
1017
+ return '*';
1018
+ case SUBTRACT:
1019
+ return '-';
1020
+ case ADD:
1021
+ return '+';
1022
+ case NUMPAD0:
1023
+ return '0';
1024
+ case NUMPAD1:
1025
+ return '1';
1026
+ case NUMPAD2:
1027
+ return '2';
1028
+ case NUMPAD3:
1029
+ return '3';
1030
+ case NUMPAD4:
1031
+ return '4';
1032
+ case NUMPAD5:
1033
+ return '5';
1034
+ case NUMPAD6:
1035
+ return '6';
1036
+ case NUMPAD7:
1037
+ return '7';
1038
+ case NUMPAD8:
1039
+ return '8';
1040
+ case NUMPAD9:
1041
+ return '9';
1042
+ case DECIMAL:
1043
+ // KEY_TYPED does not go through here and will produce
1044
+ // dot or comma based on the keyboard layout.
1045
+ // For KEY_PRESSED and KEY_RELEASED, let's just go with
1046
+ // the dot. Users can detect the key by its keyCode.
1047
+ return '.';
1048
+ case UNDEFINED:
1049
+ // KEY_TYPED has KeyCode: UNDEFINED
1050
+ // and falls through here
1051
+ break;
1052
+ default:
1053
+ break;
1054
+ }
1055
+
1056
+ // Just go with what FX gives us for the rest of
1057
+ // KEY_PRESSED and KEY_RELEASED and all of KEY_TYPED
1058
+ String ch;
1059
+ if (fxEvent.getEventType() == KeyEvent.KEY_TYPED) {
1060
+ ch = fxEvent.getCharacter();
1061
+ } else {
1062
+ ch = kc.impl_getChar();
1063
+ }
1064
+
1065
+ if (ch.length() < 1) return PConstants.CODED;
1066
+ if (ch.startsWith("\r")) return '\n'; // normalize enter key
1067
+ return ch.charAt(0);
1068
+ }
1069
+ }