KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > graphics > Cursor


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.swt.graphics;
12
13
14 import org.eclipse.swt.internal.carbon.*;
15 import org.eclipse.swt.*;
16
17 /**
18  * Instances of this class manage operating system resources that
19  * specify the appearance of the on-screen pointer. To create a
20  * cursor you specify the device and either a simple cursor style
21  * describing one of the standard operating system provided cursors
22  * or the image and mask data for the desired appearance.
23  * <p>
24  * Application code must explicitly invoke the <code>Cursor.dispose()</code>
25  * method to release the operating system resources managed by each instance
26  * when those instances are no longer required.
27  * </p>
28  * <dl>
29  * <dt><b>Styles:</b></dt>
30  * <dd>
31  * CURSOR_ARROW, CURSOR_WAIT, CURSOR_CROSS, CURSOR_APPSTARTING, CURSOR_HELP,
32  * CURSOR_SIZEALL, CURSOR_SIZENESW, CURSOR_SIZENS, CURSOR_SIZENWSE, CURSOR_SIZEWE,
33  * CURSOR_SIZEN, CURSOR_SIZES, CURSOR_SIZEE, CURSOR_SIZEW, CURSOR_SIZENE, CURSOR_SIZESE,
34  * CURSOR_SIZESW, CURSOR_SIZENW, CURSOR_UPARROW, CURSOR_IBEAM, CURSOR_NO, CURSOR_HAND
35  * </dd>
36  * </dl>
37  * <p>
38  * Note: Only one of the above styles may be specified.
39  * </p>
40  */

41
42 public final class Cursor extends Resource {
43     
44     /**
45      * the handle to the OS cursor resource
46      * (Warning: This field is platform dependent)
47      * <p>
48      * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
49      * public API. It is marked public only so that it can be shared
50      * within the packages provided by SWT. It is not available on all
51      * platforms and should never be accessed from application code.
52      * </p>
53      */

54     public int handle;
55     
56     /**
57      * data and mask used to create a Resize NS Cursor
58      */

59     static final short [] SIZENS_SOURCE = new short[] {
60         (short)0x0000,
61         (short)0x0180,
62         (short)0x03C0,
63         (short)0x07E0,
64         (short)0x0180,
65         (short)0x0180,
66         (short)0x0180,
67         (short)0x7FFE,
68         (short)0x7FFE,
69         (short)0x0180,
70         (short)0x0180,
71         (short)0x0180,
72         (short)0x07E0,
73         (short)0x03C0,
74         (short)0x0180,
75         (short)0x0000,
76     };
77     static final short [] SIZENS_MASK = new short[] {
78         (short)0x0180,
79         (short)0x03C0,
80         (short)0x07E0,
81         (short)0x0FF0,
82         (short)0x0FF0,
83         (short)0x03C0,
84         (short)0xFFFF,
85         (short)0xFFFF,
86         (short)0xFFFF,
87         (short)0xFFFF,
88         (short)0x03C0,
89         (short)0x0FF0,
90         (short)0x0FF0,
91         (short)0x07E0,
92         (short)0x03C0,
93         (short)0x0180,
94     };
95     
96 /**
97  * Prevents uninitialized instances from being created outside the package.
98  */

99 Cursor() {
100 }
101
102 /**
103  * Constructs a new cursor given a device and a style
104  * constant describing the desired cursor appearance.
105  * <p>
106  * You must dispose the cursor when it is no longer required.
107  * </p>
108  *
109  * @param device the device on which to allocate the cursor
110  * @param style the style of cursor to allocate
111  *
112  * @exception IllegalArgumentException <ul>
113  * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
114  * <li>ERROR_INVALID_ARGUMENT - when an unknown style is specified</li>
115  * </ul>
116  * @exception SWTError <ul>
117  * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
118  * </ul>
119  *
120  * @see SWT#CURSOR_ARROW
121  * @see SWT#CURSOR_WAIT
122  * @see SWT#CURSOR_CROSS
123  * @see SWT#CURSOR_APPSTARTING
124  * @see SWT#CURSOR_HELP
125  * @see SWT#CURSOR_SIZEALL
126  * @see SWT#CURSOR_SIZENESW
127  * @see SWT#CURSOR_SIZENS
128  * @see SWT#CURSOR_SIZENWSE
129  * @see SWT#CURSOR_SIZEWE
130  * @see SWT#CURSOR_SIZEN
131  * @see SWT#CURSOR_SIZES
132  * @see SWT#CURSOR_SIZEE
133  * @see SWT#CURSOR_SIZEW
134  * @see SWT#CURSOR_SIZENE
135  * @see SWT#CURSOR_SIZESE
136  * @see SWT#CURSOR_SIZESW
137  * @see SWT#CURSOR_SIZENW
138  * @see SWT#CURSOR_UPARROW
139  * @see SWT#CURSOR_IBEAM
140  * @see SWT#CURSOR_NO
141  * @see SWT#CURSOR_HAND
142  */

143 public Cursor(Device device, int style) {
144     if (device == null) device = Device.getDevice();
145     if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
146     this.device = device;
147     switch (style) {
148         case SWT.CURSOR_HAND: handle = OS.kThemePointingHandCursor; break;
149         case SWT.CURSOR_ARROW: handle = OS.kThemeArrowCursor; break;
150         case SWT.CURSOR_WAIT: handle = OS.kThemeSpinningCursor; break;
151         case SWT.CURSOR_CROSS: handle = OS.kThemeCrossCursor; break;
152         case SWT.CURSOR_APPSTARTING: handle = OS.kThemeArrowCursor; break;
153         case SWT.CURSOR_HELP: handle = OS.kThemeCrossCursor; break;
154         case SWT.CURSOR_SIZEALL: handle = OS.kThemeCrossCursor; break;
155         case SWT.CURSOR_SIZENESW: handle = OS.kThemeCrossCursor; break;
156         case SWT.CURSOR_SIZENS: {
157             org.eclipse.swt.internal.carbon.Cursor cursor = new org.eclipse.swt.internal.carbon.Cursor();
158             cursor.data = SIZENS_SOURCE;
159             cursor.mask = SIZENS_MASK;
160             cursor.hotSpot_h = 7;
161             cursor.hotSpot_v = 7;
162             handle = OS.NewPtr(org.eclipse.swt.internal.carbon.Cursor.sizeof);
163             if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES);
164             OS.memcpy(handle, cursor, org.eclipse.swt.internal.carbon.Cursor.sizeof);
165             break;
166         }
167         case SWT.CURSOR_SIZENWSE: handle = OS.kThemeCrossCursor; break;
168         case SWT.CURSOR_SIZEWE: handle = OS.kThemeResizeLeftRightCursor; break;
169         case SWT.CURSOR_SIZEN: handle = OS.kThemeCrossCursor; break;
170         case SWT.CURSOR_SIZES: handle = OS.kThemeCrossCursor; break;
171         case SWT.CURSOR_SIZEE: handle = OS.kThemeResizeRightCursor; break;
172         case SWT.CURSOR_SIZEW: handle = OS.kThemeResizeLeftCursor; break;
173         case SWT.CURSOR_SIZENE: handle = OS.kThemeCrossCursor; break;
174         case SWT.CURSOR_SIZESE: handle = OS.kThemeCrossCursor; break;
175         case SWT.CURSOR_SIZESW: handle = OS.kThemeCrossCursor; break;
176         case SWT.CURSOR_SIZENW: handle = OS.kThemeCrossCursor; break;
177         case SWT.CURSOR_UPARROW: handle = OS.kThemeCrossCursor; break;
178         case SWT.CURSOR_IBEAM: handle = OS.kThemeIBeamCursor; break;
179         case SWT.CURSOR_NO: handle = OS.kThemeNotAllowedCursor; break;
180         default:
181             SWT.error(SWT.ERROR_INVALID_ARGUMENT);
182     }
183 }
184
185 /**
186  * Constructs a new cursor given a device, image and mask
187  * data describing the desired cursor appearance, and the x
188  * and y coordinates of the <em>hotspot</em> (that is, the point
189  * within the area covered by the cursor which is considered
190  * to be where the on-screen pointer is "pointing").
191  * <p>
192  * The mask data is allowed to be null, but in this case the source
193  * must be an ImageData representing an icon that specifies both
194  * color data and mask data.
195  * <p>
196  * You must dispose the cursor when it is no longer required.
197  * </p>
198  *
199  * @param device the device on which to allocate the cursor
200  * @param source the color data for the cursor
201  * @param mask the mask data for the cursor (or null)
202  * @param hotspotX the x coordinate of the cursor's hotspot
203  * @param hotspotY the y coordinate of the cursor's hotspot
204  *
205  * @exception IllegalArgumentException <ul>
206  * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
207  * <li>ERROR_NULL_ARGUMENT - if the source is null</li>
208  * <li>ERROR_NULL_ARGUMENT - if the mask is null and the source does not have a mask</li>
209  * <li>ERROR_INVALID_ARGUMENT - if the source and the mask are not the same
210  * size, or if the hotspot is outside the bounds of the image</li>
211  * </ul>
212  * @exception SWTError <ul>
213  * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
214  * </ul>
215  */

216 public Cursor(Device device, ImageData source, ImageData mask, int hotspotX, int hotspotY) {
217     if (device == null) device = Device.getDevice();
218     if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
219     this.device = device;
220     if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
221     if (mask == null) {
222         if (source.getTransparencyType() != SWT.TRANSPARENCY_MASK) {
223             SWT.error(SWT.ERROR_NULL_ARGUMENT);
224         }
225         mask = source.getTransparencyMask();
226     }
227     /* Check the bounds. Mask must be the same size as source */
228     if (mask.width != source.width || mask.height != source.height) {
229         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
230     }
231     /* Check the hotspots */
232     if (hotspotX >= source.width || hotspotX < 0 ||
233         hotspotY >= source.height || hotspotY < 0) {
234         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
235     }
236     /* Convert depth to 1 */
237     mask = ImageData.convertMask(mask);
238     source = ImageData.convertMask(source);
239
240     /* Find the first non transparent pixel if cursor bigger than 16x16. */
241     int width = source.width;
242     int height = source.height;
243     int minX = 0, minY = 0;
244     if (width > 16 || height > 16) {
245         minX = width;
246         minY = height;
247         int maxX = 0, maxY = 0;
248         for (int y = 0; y < height; y++) {
249             for (int x = 0; x < width; x++) {
250                 if (!(source.getPixel(x, y) == 1 && mask.getPixel(x, y) == 0)) {
251                     minX = Math.min(minX, x);
252                     minY = Math.min(minY, y);
253                     maxX = Math.max(maxX, x);
254                     maxY = Math.max(maxY, y);
255                 }
256             }
257         }
258         width = maxX - minX + 1;
259         height = maxY - minY + 1;
260         
261         /* Stretch cursor if still bigger than 16x16. */
262         if (width > 16 || height > 16) {
263             int newWidth = Math.min(width, 16);
264             int newHeight = Math.min(height, 16);
265             ImageData newSource =
266                 new ImageData(newWidth, newHeight, source.depth, source.palette,
267                     1, null, 0, null, null, -1, -1, source.type,
268                     source.x, source.y, source.disposalMethod, source.delayTime);
269             ImageData newMask = new ImageData(newWidth, newHeight, mask.depth,
270                     mask.palette, 1, null, 0, null, null, -1, -1, mask.type,
271                     mask.x, mask.y, mask.disposalMethod, mask.delayTime);
272             ImageData.blit(ImageData.BLIT_SRC,
273                 source.data, source.depth, source.bytesPerLine, source.getByteOrder(), minX, minY, width, height, null, null, null,
274                 ImageData.ALPHA_OPAQUE, null, 0, minX, minY,
275                 newSource.data, newSource.depth, newSource.bytesPerLine, newSource.getByteOrder(), 0, 0, newWidth, newHeight, null, null, null,
276                 false, false);
277             ImageData.blit(ImageData.BLIT_SRC,
278                 mask.data, mask.depth, mask.bytesPerLine, mask.getByteOrder(), minX, minY, width, height, null, null, null,
279                 ImageData.ALPHA_OPAQUE, null, 0, minX, minY,
280                 newMask.data, newMask.depth, newMask.bytesPerLine, newMask.getByteOrder(), 0, 0, newWidth, newHeight, null, null, null,
281                 false, false);
282             width = newWidth;
283             height = newHeight;
284             minX = minY = 0;
285             source = newSource;
286             mask = newMask;
287         }
288     }
289
290     /* Create the cursor */
291     org.eclipse.swt.internal.carbon.Cursor cursor = new org.eclipse.swt.internal.carbon.Cursor();
292     short[] srcData = cursor.data;
293     short[] maskData = cursor.mask;
294     for (int y = 0; y < height; y++) {
295         short d = 0, m = 0;
296         for (int x = 0; x < width; x++) {
297             int bit = 1 << (width - 1 - x);
298             if (source.getPixel(minX + x, minY + y) == 0) {
299                 m |= bit;
300                 if (mask.getPixel(minX + x, minY + y) == 0) d |= bit;
301             } else if (mask.getPixel(minX + x, minY + y) != 0) {
302                 d |= bit;
303             }
304         }
305         srcData[y] = d;
306         maskData[y] = m;
307     }
308     cursor.hotSpot_h = (short)Math.max(0, Math.min(15, hotspotY - minX));
309     cursor.hotSpot_v = (short)Math.max(0, Math.min(15, hotspotY - minY));
310     handle = OS.NewPtr(org.eclipse.swt.internal.carbon.Cursor.sizeof);
311     if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES);
312     OS.memcpy(handle, cursor, org.eclipse.swt.internal.carbon.Cursor.sizeof);
313 }
314
315 /**
316  * Constructs a new cursor given a device, image data describing
317  * the desired cursor appearance, and the x and y coordinates of
318  * the <em>hotspot</em> (that is, the point within the area
319  * covered by the cursor which is considered to be where the
320  * on-screen pointer is "pointing").
321  * <p>
322  * You must dispose the cursor when it is no longer required.
323  * </p>
324  *
325  * @param device the device on which to allocate the cursor
326  * @param source the image data for the cursor
327  * @param hotspotX the x coordinate of the cursor's hotspot
328  * @param hotspotY the y coordinate of the cursor's hotspot
329  *
330  * @exception IllegalArgumentException <ul>
331  * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
332  * <li>ERROR_NULL_ARGUMENT - if the image is null</li>
333  * <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the
334  * image</li>
335  * </ul>
336  * @exception SWTError <ul>
337  * <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
338  * </ul>
339  *
340  * @since 3.0
341  */

342 public Cursor(Device device, ImageData source, int hotspotX, int hotspotY) {
343     if (device == null) device = Device.getDevice();
344     if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
345     this.device = device;
346     if (source == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
347     if (hotspotX >= source.width || hotspotX < 0 ||
348         hotspotY >= source.height || hotspotY < 0) {
349         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
350     }
351     ImageData mask = source.getTransparencyMask();
352
353     /* Ensure depth is equal to 1 */
354     if (source.depth > 1) {
355         /* Create a destination image with no data */
356         ImageData newSource = new ImageData(
357             source.width, source.height, 1, ImageData.bwPalette(),
358             1, null, 0, null, null, -1, -1, source.type,
359             source.x, source.y, source.disposalMethod, source.delayTime);
360
361         /* Convert the source to a black and white image of depth 1 */
362         PaletteData palette = source.palette;
363         if (palette.isDirect) ImageData.blit(ImageData.BLIT_SRC,
364             source.data, source.depth, source.bytesPerLine, source.getByteOrder(), 0, 0, source.width, source.height, 0, 0, 0,
365             ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
366             newSource.data, newSource.depth, newSource.bytesPerLine, newSource.getByteOrder(), 0, 0, newSource.width, newSource.height, 0, 0, 0,
367             false, false);
368         else ImageData.blit(ImageData.BLIT_SRC,
369             source.data, source.depth, source.bytesPerLine, source.getByteOrder(), 0, 0, source.width, source.height, null, null, null,
370             ImageData.ALPHA_OPAQUE, null, 0, 0, 0,
371             newSource.data, newSource.depth, newSource.bytesPerLine, newSource.getByteOrder(), 0, 0, newSource.width, newSource.height, null, null, null,
372             false, false);
373         source = newSource;
374     }
375
376     /* Find the first non transparent pixel if cursor bigger than 16x16. */
377     int width = source.width;
378     int height = source.height;
379     int minX = 0, minY = 0;
380     if (width > 16 || height > 16) {
381         minX = width;
382         minY = height;
383         int maxX = 0, maxY = 0;
384         for (int y = 0; y < height; y++) {
385             for (int x = 0; x < width; x++) {
386                 if (!(source.getPixel(x, y) == 1 && mask.getPixel(x, y) == 0)) {
387                     minX = Math.min(minX, x);
388                     minY = Math.min(minY, y);
389                     maxX = Math.max(maxX, x);
390                     maxY = Math.max(maxY, y);
391                 }
392             }
393         }
394         width = maxX - minX + 1;
395         height = maxY - minY + 1;
396         
397         /* Stretch cursor if still bigger than 16x16. */
398         if (width > 16 || height > 16) {
399             int newWidth = Math.min(width, 16);
400             int newHeight = Math.min(height, 16);
401             ImageData newSource =
402                 new ImageData(newWidth, newHeight, source.depth, source.palette,
403                     1, null, 0, null, null, -1, -1, source.type,
404                     source.x, source.y, source.disposalMethod, source.delayTime);
405             ImageData newMask = new ImageData(newWidth, newHeight, mask.depth,
406                     mask.palette, 1, null, 0, null, null, -1, -1, mask.type,
407                     mask.x, mask.y, mask.disposalMethod, mask.delayTime);
408             ImageData.blit(ImageData.BLIT_SRC,
409                 source.data, source.depth, source.bytesPerLine, source.getByteOrder(), minX, minY, width, height, null, null, null,
410                 ImageData.ALPHA_OPAQUE, null, 0, minX, minY,
411                 newSource.data, newSource.depth, newSource.bytesPerLine, newSource.getByteOrder(), 0, 0, newWidth, newHeight, null, null, null,
412                 false, false);
413             ImageData.blit(ImageData.BLIT_SRC,
414                 mask.data, mask.depth, mask.bytesPerLine, mask.getByteOrder(), minX, minY, width, height, null, null, null,
415                 ImageData.ALPHA_OPAQUE, null, 0, minX, minY,
416                 newMask.data, newMask.depth, newMask.bytesPerLine, newMask.getByteOrder(), 0, 0, newWidth, newHeight, null, null, null,
417                 false, false);
418             width = newWidth;
419             height = newHeight;
420             minX = minY = 0;
421             source = newSource;
422             mask = newMask;
423         }
424     }
425
426     /* Create the cursor */
427     org.eclipse.swt.internal.carbon.Cursor cursor = new org.eclipse.swt.internal.carbon.Cursor();
428     short[] srcData = cursor.data;
429     short[] maskData = cursor.mask;
430     for (int y= 0; y < height; y++) {
431         short d = 0, m = 0;
432         for (int x = 0; x < width; x++) {
433             int bit = 1 << (width - 1 - x);
434             if (source.getPixel(x + minX, y + minY) == 0) {
435                 if (mask.getPixel(x + minX, y + minY) != 0) {
436                     d |= bit;
437                     m |= bit;
438                 }
439             } else {
440                 if (mask.getPixel(x + minX, y + minY) != 0) m |= bit;
441             }
442         }
443         srcData[y] = d;
444         maskData[y] = m;
445     }
446     cursor.hotSpot_h = (short)Math.max(0, Math.min(15, hotspotY - minX));
447     cursor.hotSpot_v = (short)Math.max(0, Math.min(15, hotspotY - minY));
448     handle = OS.NewPtr(org.eclipse.swt.internal.carbon.Cursor.sizeof);
449     if (handle == 0) SWT.error(SWT.ERROR_NO_HANDLES);
450     OS.memcpy(handle, cursor, org.eclipse.swt.internal.carbon.Cursor.sizeof);
451 }
452
453 /**
454  * Disposes of the operating system resources associated with
455  * the cursor. Applications must dispose of all cursors which
456  * they allocate.
457  */

458 public void dispose () {
459     if (handle == -1) return;
460     if (device.isDisposed()) return;
461     switch (handle) {
462         case OS.kThemePointingHandCursor:
463         case OS.kThemeArrowCursor:
464         case OS.kThemeSpinningCursor:
465         case OS.kThemeCrossCursor:
466         case OS.kThemeWatchCursor:
467         case OS.kThemeIBeamCursor:
468         case OS.kThemeNotAllowedCursor:
469         case OS.kThemeResizeLeftRightCursor:
470         case OS.kThemeResizeLeftCursor:
471         case OS.kThemeResizeRightCursor:
472             break;
473         default:
474             OS.DisposePtr(handle);
475     }
476     handle = -1;
477     device = null;
478 }
479
480 /**
481  * Compares the argument to the receiver, and returns true
482  * if they represent the <em>same</em> object using a class
483  * specific comparison.
484  *
485  * @param object the object to compare with this object
486  * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
487  *
488  * @see #hashCode
489  */

490 public boolean equals (Object JavaDoc object) {
491     if (object == this) return true;
492     if (!(object instanceof Cursor)) return false;
493     Cursor cursor = (Cursor) object;
494     return device == cursor.device && handle == cursor.handle;
495 }
496
497 /**
498  * Returns an integer hash code for the receiver. Any two
499  * objects that return <code>true</code> when passed to
500  * <code>equals</code> must return the same value for this
501  * method.
502  *
503  * @return the receiver's hash
504  *
505  * @see #equals
506  */

507 public int hashCode () {
508     return handle;
509 }
510
511 /**
512  * Returns <code>true</code> if the cursor has been disposed,
513  * and <code>false</code> otherwise.
514  * <p>
515  * This method gets the dispose state for the cursor.
516  * When a cursor has been disposed, it is an error to
517  * invoke any other method using the cursor.
518  *
519  * @return <code>true</code> when the cursor is disposed and <code>false</code> otherwise
520  */

521 public boolean isDisposed() {
522     return handle == -1;
523 }
524
525 /**
526  * Returns a string containing a concise, human-readable
527  * description of the receiver.
528  *
529  * @return a string representation of the receiver
530  */

531 public String JavaDoc toString () {
532     if (isDisposed()) return "Cursor {*DISPOSED*}";
533     return "Cursor {" + handle + "}";
534 }
535
536 /**
537  * Invokes platform specific functionality to allocate a new cursor.
538  * <p>
539  * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
540  * API for <code>Cursor</code>. It is marked public only so that it
541  * can be shared within the packages provided by SWT. It is not
542  * available on all platforms, and should never be called from
543  * application code.
544  * </p>
545  *
546  * @param device the device on which to allocate the color
547  * @param handle the handle for the cursor
548  *
549  * @private
550  */

551 public static Cursor carbon_new(Device device, int handle) {
552     if (device == null) device = Device.getDevice();
553     Cursor cursor = new Cursor();
554     cursor.handle = handle;
555     cursor.device = device;
556     return cursor;
557 }
558
559 }
560
Popular Tags