Skip to content

Commit e62283d

Browse files
Refactor Cursor handle to support per-zoom handles
- Removed the dedicated `handle` field from Cursor; all native handles are now stored in `zoomLevelToHandle`. - Updated hashCode, equals, isDisposed, and destroy to work with zoom-level handles.
1 parent f6de513 commit e62283d

File tree

2 files changed

+107
-71
lines changed

2 files changed

+107
-71
lines changed

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Cursor.java

Lines changed: 45 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,6 @@
4949
*/
5050
public final class Cursor extends Resource {
5151

52-
/**
53-
* the handle to the OS cursor resource
54-
* (Warning: This field is platform dependent)
55-
* <p>
56-
* <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
57-
* public API. It is marked public only so that it can be shared
58-
* within the packages provided by SWT. It is not available on all
59-
* platforms and should never be accessed from application code.
60-
* </p>
61-
*
62-
*/
63-
private long handle;
6452
/**
6553
* Attribute to cache current native zoom level
6654
*/
@@ -70,6 +58,8 @@ public final class Cursor extends Resource {
7058

7159
private final CursorHandleProvider cursorHandleProvider;
7260

61+
private boolean isDestroyed;
62+
7363
/**
7464
* Constructs a new cursor given a device and a style
7565
* constant describing the desired cursor appearance.
@@ -119,7 +109,6 @@ public final class Cursor extends Resource {
119109
public Cursor(Device device, int style) {
120110
super(device);
121111
this.cursorHandleProvider = new StyleCursorHandleProvider(style);
122-
this.handle = this.cursorHandleProvider.createHandle(this.device, DEFAULT_ZOOM).getHandle();
123112
init();
124113
this.device.registerResourceWithZoomSupport(this);
125114
}
@@ -160,28 +149,11 @@ public Cursor(Device device, int style) {
160149
public Cursor(Device device, ImageData source, ImageData mask, int hotspotX, int hotspotY) {
161150
super(device);
162151
this.cursorHandleProvider = new ImageDataWithMaskCursorHandleProvider(source, mask, hotspotX, hotspotY);
163-
this.handle = this.cursorHandleProvider.createHandle(this.device, DEFAULT_ZOOM).getHandle();
164152
init();
165153
this.device.registerResourceWithZoomSupport(this);
166154
}
167155

168156
private static CursorHandle setupCursorFromImageData(ImageData source, ImageData mask, int hotspotX, int hotspotY) {
169-
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
170-
if (mask == null) {
171-
if (source.getTransparencyType() != SWT.TRANSPARENCY_MASK) {
172-
SWT.error(SWT.ERROR_NULL_ARGUMENT);
173-
}
174-
mask = source.getTransparencyMask();
175-
}
176-
/* Check the bounds. Mask must be the same size as source */
177-
if (mask.width != source.width || mask.height != source.height) {
178-
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
179-
}
180-
/* Check the hotspots */
181-
if (hotspotX >= source.width || hotspotX < 0 ||
182-
hotspotY >= source.height || hotspotY < 0) {
183-
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
184-
}
185157
/* Convert depth to 1 */
186158
mask = ImageData.convertMask(mask);
187159
source = ImageData.convertMask(source);
@@ -229,18 +201,12 @@ private static CursorHandle setupCursorFromImageData(ImageData source, ImageData
229201
public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
230202
super(device);
231203
this.cursorHandleProvider = new ImageDataCursorHandleProvider(source, hotspotX, hotspotY);
232-
this.handle = this.cursorHandleProvider.createHandle(this.device, DEFAULT_ZOOM).getHandle();
233204
init();
234205
this.device.registerResourceWithZoomSupport(this);
235206
}
236207

237208
private static CursorHandle setupCursorFromImageData(Device device, ImageData source, int hotspotX, int hotspotY) {
238209
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
239-
/* Check the hotspots */
240-
if (hotspotX >= source.width || hotspotX < 0 ||
241-
hotspotY >= source.height || hotspotY < 0) {
242-
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
243-
}
244210
long hBitmap = 0;
245211
long hMask = 0;
246212
if (source.maskData == null && source.transparentPixel == -1 && (source.alpha != -1 || source.alphaData != null)) {
@@ -341,7 +307,6 @@ public Cursor(Device device, ImageDataProvider imageDataProvider, int hotspotX,
341307
super(device);
342308
if (imageDataProvider == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
343309
this.cursorHandleProvider = new ImageDataProviderCursorHandleProvider(imageDataProvider, hotspotX, hotspotY);
344-
this.handle = this.cursorHandleProvider.createHandle(this.device, DEFAULT_ZOOM).getHandle();
345310
init();
346311
this.device.registerResourceWithZoomSupport(this);
347312
}
@@ -363,7 +328,7 @@ public Cursor(Device device, ImageDataProvider imageDataProvider, int hotspotX,
363328
*/
364329
public static Long win32_getHandle (Cursor cursor, int zoom) {
365330
if (cursor.isDisposed()) {
366-
return cursor.handle;
331+
return 0L;
367332
}
368333
if (cursor.zoomLevelToHandle.get(zoom) != null) {
369334
return cursor.zoomLevelToHandle.get(zoom).getHandle();
@@ -376,38 +341,19 @@ public static Long win32_getHandle (Cursor cursor, int zoom) {
376341
}
377342

378343
private void setHandleForZoomLevel(CursorHandle handle, Integer zoom) {
379-
if (this.handle == 0) {
380-
this.handle = handle.getHandle(); // Set handle for default zoom level
381-
}
382344
if (zoom != null && !zoomLevelToHandle.containsKey(zoom)) {
383345
zoomLevelToHandle.put(zoom, handle);
384346
}
385347
}
386348

387349
@Override
388350
void destroy () {
389-
/*
390-
* It is an error in Windows to destroy the current
391-
* cursor. Check that the cursor that is about to
392-
* be destroyed is the current cursor. If so, set
393-
* the current cursor to be IDC_ARROW. Note that
394-
* Windows shares predefined cursors so the call to
395-
* LoadCursor() does not leak.
396-
*/
397-
// TEMPORARY CODE
398-
// if (OS.GetCursor() == handle) {
399-
// OS.SetCursor(OS.LoadCursor(0, OS.IDC_ARROW));
400-
// }
401351
device.deregisterResourceWithZoomSupport(this);
402-
destroyHandle();
403-
}
404-
405-
private void destroyHandle () {
406352
for (CursorHandle handle : zoomLevelToHandle.values()) {
407353
handle.destroy();
408354
}
409355
zoomLevelToHandle.clear();
410-
handle = 0;
356+
this.isDestroyed = true;
411357
}
412358

413359
/**
@@ -425,7 +371,7 @@ public boolean equals (Object object) {
425371
if (object == this) return true;
426372
if (!(object instanceof Cursor)) return false;
427373
Cursor cursor = (Cursor) object;
428-
return device == cursor.device && handle == cursor.handle;
374+
return device == cursor.device && win32_getHandle(this, DEFAULT_ZOOM) == win32_getHandle(cursor, DEFAULT_ZOOM);
429375
}
430376

431377
/**
@@ -440,7 +386,7 @@ public boolean equals (Object object) {
440386
*/
441387
@Override
442388
public int hashCode () {
443-
return (int)handle;
389+
return win32_getHandle(this, DEFAULT_ZOOM).intValue();
444390
}
445391

446392
/**
@@ -455,7 +401,7 @@ public int hashCode () {
455401
*/
456402
@Override
457403
public boolean isDisposed() {
458-
return handle == 0;
404+
return isDestroyed;
459405
}
460406

461407
/**
@@ -467,7 +413,7 @@ public boolean isDisposed() {
467413
@Override
468414
public String toString () {
469415
if (isDisposed()) return "Cursor {*DISPOSED*}";
470-
return "Cursor {" + handle + "}";
416+
return "Cursor {" + zoomLevelToHandle + "}";
471417
}
472418

473419
@Override
@@ -531,19 +477,23 @@ private static interface CursorHandleProvider {
531477
}
532478

533479
private static class StyleCursorHandleProvider implements CursorHandleProvider {
534-
private final int style;
480+
private final long lpCursorName;
535481

536482
public StyleCursorHandleProvider(int style) {
537-
this.style = style;
483+
this.lpCursorName = getOSCursorIdFromStyle(style);
538484
}
539485

540486
@Override
541487
public CursorHandle createHandle(Device device, int zoom) {
542488
// zoom ignored, LoadCursor handles scaling internally
543-
return setupCursorFromStyle(this.style);
489+
long handle = OS.LoadCursor(0, lpCursorName);
490+
if (handle == 0) {
491+
SWT.error(SWT.ERROR_NO_HANDLES);
492+
}
493+
return new CustomCursorHandle(handle);
544494
}
545495

546-
private static final CursorHandle setupCursorFromStyle(int style) {
496+
private static final long getOSCursorIdFromStyle(int style) {
547497
long lpCursorName = 0;
548498
switch (style) {
549499
case SWT.CURSOR_HAND:
@@ -615,11 +565,7 @@ private static final CursorHandle setupCursorFromStyle(int style) {
615565
default:
616566
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
617567
}
618-
long handle = OS.LoadCursor(0, lpCursorName);
619-
if (handle == 0) {
620-
SWT.error(SWT.ERROR_NO_HANDLES);
621-
}
622-
return new CustomCursorHandle(handle);
568+
return lpCursorName;
623569
}
624570
}
625571

@@ -646,6 +592,13 @@ private static class ImageDataProviderCursorHandleProvider extends HotspotAwareC
646592

647593
public ImageDataProviderCursorHandleProvider(ImageDataProvider provider, int hotspotX, int hotspotY) {
648594
super(hotspotX, hotspotY);
595+
ImageData source = provider.getImageData(DEFAULT_ZOOM);
596+
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
597+
/* Check the hotspots */
598+
if (hotspotX >= source.width || hotspotX < 0 ||
599+
hotspotY >= source.height || hotspotY < 0) {
600+
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
601+
}
649602
this.provider = provider;
650603
}
651604

@@ -668,6 +621,12 @@ private static class ImageDataCursorHandleProvider extends HotspotAwareCursorHan
668621

669622
public ImageDataCursorHandleProvider(ImageData source, int hotspotX, int hotspotY) {
670623
super(hotspotX, hotspotY);
624+
if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
625+
/* Check the hotspots */
626+
if (hotspotX >= source.width || hotspotX < 0 ||
627+
hotspotY >= source.height || hotspotY < 0) {
628+
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
629+
}
671630
this.source = source;
672631
}
673632

@@ -685,6 +644,21 @@ private static class ImageDataWithMaskCursorHandleProvider extends ImageDataCurs
685644
public ImageDataWithMaskCursorHandleProvider(ImageData source, ImageData mask, int hotspotX, int hotspotY) {
686645
super(source, hotspotX, hotspotY);
687646
this.mask = mask;
647+
validateMask(source, mask);
648+
}
649+
650+
private void validateMask(ImageData source, ImageData mask) {
651+
ImageData testMask = mask == null? null : (ImageData) mask.clone();
652+
if (testMask == null) {
653+
if (source.getTransparencyType() != SWT.TRANSPARENCY_MASK) {
654+
SWT.error(SWT.ERROR_NULL_ARGUMENT);
655+
}
656+
testMask = source.getTransparencyMask();
657+
}
658+
/* Check the bounds. Mask must be the same size as source */
659+
if (testMask.width != source.width || testMask.height != source.height) {
660+
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
661+
}
688662
}
689663

690664
@Override

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Cursor.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.eclipse.swt.graphics.ImageData;
2929
import org.eclipse.swt.graphics.ImageDataProvider;
3030
import org.eclipse.swt.graphics.ImageLoader;
31+
import org.eclipse.swt.graphics.PaletteData;
32+
import org.eclipse.swt.graphics.RGB;
3133
import org.eclipse.swt.widgets.Display;
3234
import org.junit.Before;
3335
import org.junit.Test;
@@ -162,6 +164,66 @@ public void test_ConstructorWithImageDataProvider() {
162164
IllegalArgumentException.class, () -> new Cursor(display, (ImageDataProvider) null, 0, 0));
163165
}
164166

167+
@Test
168+
public void test_InvalidArgumentsForAllConstructors() {
169+
ImageData source = new ImageData(16, 16, 1, new PaletteData(new RGB[] { new RGB(0, 0, 0) }));
170+
ImageData mask = new ImageData(16, 16, 1, new PaletteData(new RGB[] { new RGB(0, 0, 0) }));
171+
172+
assertThrows("When wrong style was provided", IllegalArgumentException.class,
173+
() -> {
174+
Cursor cursor = new Cursor(Display.getDefault(), -99);
175+
cursor.dispose();
176+
});
177+
178+
assertThrows("When source is null", IllegalArgumentException.class, () -> {
179+
Cursor cursorFromImageAndMask = new Cursor(Display.getDefault(), null, mask, 0, 0);
180+
cursorFromImageAndMask.dispose();
181+
});
182+
183+
assertThrows("When mask is null and source doesn't heve a mask",
184+
IllegalArgumentException.class, () -> {
185+
Cursor cursorFromImageAndMask = new Cursor(Display.getDefault(), source, null, 0, 0);
186+
cursorFromImageAndMask.dispose();
187+
});
188+
189+
assertThrows("When source and the mask are not the same size",
190+
IllegalArgumentException.class, () -> {
191+
ImageData source32 = new ImageData(32, 32, 1, new PaletteData(new RGB[] { new RGB(0, 0, 0) }));
192+
ImageData mask16 = new ImageData(16, 16, 1, new PaletteData(new RGB[] { new RGB(0, 0, 0) }));
193+
194+
Cursor cursorFromImageAndMask = new Cursor(Display.getDefault(), source32, mask16, 0, 0);
195+
cursorFromImageAndMask.dispose();
196+
});
197+
198+
assertThrows("When hotspot is outside the bounds of the image",
199+
IllegalArgumentException.class, () -> {
200+
Cursor cursorFromImageAndMask = new Cursor(Display.getDefault(), source, mask, 18, 18);
201+
cursorFromImageAndMask.dispose();
202+
});
203+
204+
assertThrows("When source image data is null", IllegalArgumentException.class,
205+
() -> {
206+
ImageData nullImageData = null;
207+
Cursor cursorFromSourceOnly = new Cursor(Display.getDefault(), nullImageData, 0, 0);
208+
cursorFromSourceOnly.dispose();
209+
});
210+
211+
assertThrows("When ImageDataProvider is null", IllegalArgumentException.class,
212+
() -> {
213+
ImageDataProvider provider = null;
214+
Cursor cursorFromProvider = new Cursor(Display.getDefault(), provider, 0, 0);
215+
cursorFromProvider.dispose();
216+
});
217+
218+
assertThrows("When source in ImageDataProvider is null",
219+
IllegalArgumentException.class, () -> {
220+
ImageData nullSource = null;
221+
ImageDataProvider provider = zoom -> nullSource;
222+
Cursor cursorFromProvider = new Cursor(Display.getDefault(), provider, 0, 0);
223+
cursorFromProvider.dispose();
224+
});
225+
}
226+
165227
@Test
166228
public void test_equalsLjava_lang_Object() {
167229
/* Note: Two cursors are only considered equal if their handles are equal.

0 commit comments

Comments
 (0)