2 * Cursor and icon support
4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Martin Von Loewis
6 * Copyright 1997 Alex Korobka
7 * Copyright 1998 Turchanov Sergey
8 * Copyright 2007 Henri Verbeet
9 * Copyright 2009 Vincent Povirk for CodeWeavers
10 * Copyright 2016 Dmitry Timoshkov
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "ntgdi_private.h"
33 #include "ntuser_private.h"
34 #include "wine/server.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(cursor
);
38 WINE_DECLARE_DEBUG_CHANNEL(icon
);
40 struct cursoricon_object
42 struct user_object obj
; /* object header */
43 struct list entry
; /* entry in shared icons list */
44 ULONG_PTR param
; /* opaque param used by 16-bit code */
45 UNICODE_STRING module
; /* module for icons loaded from resources */
46 WCHAR
*resname
; /* resource name for icons loaded from resources */
47 HRSRC rsrc
; /* resource for shared icons */
48 BOOL is_shared
; /* whether this object is shared */
49 BOOL is_icon
; /* whether icon or cursor */
50 BOOL is_ani
; /* whether this object is a static cursor or an animated cursor */
51 UINT delay
; /* delay between this frame and the next (in jiffies) */
54 struct cursoricon_frame frame
; /* frame-specific icon data */
57 UINT num_frames
; /* number of frames in the icon/cursor */
58 UINT num_steps
; /* number of sequence steps in the icon/cursor */
59 HICON
*frames
; /* list of animated cursor frames */
64 static struct list icon_cache
= LIST_INIT( icon_cache
);
66 static struct cursoricon_object
*get_icon_ptr( HICON handle
)
68 struct cursoricon_object
*obj
= get_user_handle_ptr( handle
, NTUSER_OBJ_ICON
);
69 if (obj
== OBJ_OTHER_PROCESS
)
71 WARN( "icon handle %p from other process\n", handle
);
77 BOOL
process_wine_setcursor( HWND hwnd
, HWND window
, HCURSOR handle
)
79 TRACE( "hwnd %p, window %p, hcursor %p\n", hwnd
, window
, handle
);
80 user_driver
->pSetCursor( window
, handle
);
84 /***********************************************************************
85 * NtUserShowCursor (win32u.@)
87 INT WINAPI
NtUserShowCursor( BOOL show
)
89 int increment
= show
? 1 : -1;
92 SERVER_START_REQ( set_cursor
)
94 req
->flags
= SET_CURSOR_COUNT
;
95 req
->show_count
= increment
;
96 wine_server_call( req
);
97 count
= reply
->prev_count
+ increment
;
101 TRACE("%d, count=%d\n", show
, count
);
105 /***********************************************************************
106 * NtUserSetCursor (win32u.@)
108 HCURSOR WINAPI
NtUserSetCursor( HCURSOR cursor
)
110 struct cursoricon_object
*obj
;
114 TRACE( "%p\n", cursor
);
116 SERVER_START_REQ( set_cursor
)
118 req
->flags
= SET_CURSOR_HANDLE
;
119 req
->handle
= wine_server_user_handle( cursor
);
120 if ((ret
= !wine_server_call_err( req
)))
121 old_cursor
= wine_server_ptr_handle( reply
->prev_handle
);
126 if (!(obj
= get_icon_ptr( old_cursor
))) return 0;
127 release_user_handle_ptr( obj
);
131 /***********************************************************************
132 * NtUserGetCursor (win32u.@)
134 HCURSOR WINAPI
NtUserGetCursor(void)
138 SERVER_START_REQ( set_cursor
)
141 wine_server_call( req
);
142 ret
= wine_server_ptr_handle( reply
->prev_handle
);
148 HICON
alloc_cursoricon_handle( BOOL is_icon
)
150 struct cursoricon_object
*obj
;
153 if (!(obj
= calloc( 1, sizeof(*obj
) ))) return NULL
;
154 obj
->is_icon
= is_icon
;
155 if (!(handle
= alloc_user_handle( &obj
->obj
, NTUSER_OBJ_ICON
))) free( obj
);
159 static struct cursoricon_object
*get_icon_frame_ptr( HICON handle
, UINT step
)
161 struct cursoricon_object
*obj
, *ret
;
163 if (!(obj
= get_icon_ptr( handle
))) return NULL
;
164 if (!obj
->is_ani
) return obj
;
165 if (step
>= obj
->ani
.num_steps
)
167 release_user_handle_ptr( obj
);
170 ret
= get_icon_ptr( obj
->ani
.frames
[step
] );
171 release_user_handle_ptr( obj
);
175 static BOOL
free_icon_handle( HICON handle
)
177 struct cursoricon_object
*obj
= free_user_handle( handle
, NTUSER_OBJ_ICON
);
179 if (obj
== OBJ_OTHER_PROCESS
) WARN( "icon handle %p from other process\n", handle
);
182 ULONG param
= obj
->param
;
187 assert( !obj
->rsrc
); /* shared icons can't be freed */
191 if (obj
->frame
.alpha
) NtGdiDeleteObjectApp( obj
->frame
.alpha
);
192 if (obj
->frame
.color
) NtGdiDeleteObjectApp( obj
->frame
.color
);
193 if (obj
->frame
.mask
) NtGdiDeleteObjectApp( obj
->frame
.mask
);
197 for (i
= 0; i
< obj
->ani
.num_steps
; i
++)
199 HICON hFrame
= obj
->ani
.frames
[i
];
205 free_icon_handle( obj
->ani
.frames
[i
] );
206 for (j
= 0; j
< obj
->ani
.num_steps
; j
++)
208 if (obj
->ani
.frames
[j
] == hFrame
) obj
->ani
.frames
[j
] = 0;
212 free( obj
->ani
.frames
);
214 if (!IS_INTRESOURCE( obj
->resname
)) free( obj
->resname
);
216 if (param
) KeUserModeCallback( NtUserCallFreeIcon
, ¶m
, sizeof(param
), &ret_ptr
, &ret_len
);
217 user_driver
->pDestroyCursorIcon( handle
);
223 /***********************************************************************
224 * NtUserDestroyCursor (win32u.@)
226 BOOL WINAPI
NtUserDestroyCursor( HCURSOR cursor
, ULONG arg
)
228 struct cursoricon_object
*obj
;
231 TRACE( "%p\n", cursor
);
233 if (!(obj
= get_icon_ptr( cursor
))) return FALSE
;
234 shared
= obj
->is_shared
;
235 release_user_handle_ptr( obj
);
236 ret
= NtUserGetCursor() != cursor
;
237 if (!shared
) free_icon_handle( cursor
);
241 /***********************************************************************
242 * NtUserSetCursorIconData (win32u.@)
244 BOOL WINAPI
NtUserSetCursorIconData( HCURSOR cursor
, UNICODE_STRING
*module
, UNICODE_STRING
*res_name
,
245 struct cursoricon_desc
*desc
)
247 struct cursoricon_object
*obj
;
250 if (!(obj
= get_icon_ptr( cursor
))) return FALSE
;
252 if (obj
->is_ani
|| obj
->frame
.width
)
254 /* already initialized */
255 release_user_handle_ptr( obj
);
256 RtlSetLastWin32Error( ERROR_INVALID_CURSOR_HANDLE
);
260 obj
->delay
= desc
->delay
;
264 if (!(obj
->ani
.frames
= calloc( desc
->num_steps
, sizeof(*obj
->ani
.frames
) )))
266 release_user_handle_ptr( obj
);
270 obj
->ani
.num_steps
= desc
->num_steps
;
271 obj
->ani
.num_frames
= desc
->num_frames
;
273 else obj
->frame
= desc
->frames
[0];
277 else if (res_name
->Length
)
279 obj
->resname
= malloc( res_name
->Length
+ sizeof(WCHAR
) );
282 memcpy( obj
->resname
, res_name
->Buffer
, res_name
->Length
);
283 obj
->resname
[res_name
->Length
/ sizeof(WCHAR
)] = 0;
287 obj
->resname
= MAKEINTRESOURCEW( LOWORD(res_name
->Buffer
) );
289 if (module
&& module
->Length
&& (obj
->module
.Buffer
= malloc( module
->Length
)))
291 memcpy( obj
->module
.Buffer
, module
->Buffer
, module
->Length
);
292 obj
->module
.Length
= module
->Length
;
297 /* Setup the animated frames in the correct sequence */
298 for (i
= 0; i
< desc
->num_steps
; i
++)
300 struct cursoricon_desc frame_desc
;
303 if (obj
->ani
.frames
[i
]) continue; /* already set */
305 frame_id
= desc
->frame_seq
? desc
->frame_seq
[i
] : i
;
306 if (frame_id
>= obj
->ani
.num_frames
)
308 frame_id
= obj
->ani
.num_frames
- 1;
309 ERR_(cursor
)( "Sequence indicates frame past end of list, corrupt?\n" );
311 memset( &frame_desc
, 0, sizeof(frame_desc
) );
312 frame_desc
.delay
= desc
->frame_rates
? desc
->frame_rates
[i
] : desc
->delay
;
313 frame_desc
.frames
= &desc
->frames
[frame_id
];
314 if (!(obj
->ani
.frames
[i
] = alloc_cursoricon_handle( obj
->is_icon
)) ||
315 !NtUserSetCursorIconData( obj
->ani
.frames
[i
], NULL
, NULL
, &frame_desc
))
317 release_user_handle_ptr( obj
);
323 for (j
= i
+ 1; j
< obj
->ani
.num_steps
; j
++)
325 if (desc
->frame_seq
[j
] == frame_id
) obj
->ani
.frames
[j
] = obj
->ani
.frames
[i
];
331 if (desc
->flags
& LR_SHARED
)
333 obj
->is_shared
= TRUE
;
334 if (obj
->module
.Length
)
336 obj
->rsrc
= desc
->rsrc
;
337 list_add_head( &icon_cache
, &obj
->entry
);
341 release_user_handle_ptr( obj
);
345 /***********************************************************************
346 * NtUserFindExistingCursorIcon (win32u.@)
348 HICON WINAPI
NtUserFindExistingCursorIcon( UNICODE_STRING
*module
, UNICODE_STRING
*res_name
, void *desc
)
350 struct cursoricon_object
*ptr
;
354 LIST_FOR_EACH_ENTRY( ptr
, &icon_cache
, struct cursoricon_object
, entry
)
356 if (ptr
->module
.Length
!= module
->Length
) continue;
357 if (memcmp( ptr
->module
.Buffer
, module
->Buffer
, module
->Length
)) continue;
358 /* We pass rsrc as desc argument, this is not compatible with Windows */
359 if (ptr
->rsrc
!= desc
) continue;
360 ret
= ptr
->obj
.handle
;
367 /***********************************************************************
368 * NtUserGetIconSize (win32u.@)
370 BOOL WINAPI
NtUserGetIconSize( HICON handle
, UINT step
, LONG
*width
, LONG
*height
)
372 struct cursoricon_object
*obj
;
374 if (!(obj
= get_icon_frame_ptr( handle
, step
)))
376 RtlSetLastWin32Error( ERROR_INVALID_CURSOR_HANDLE
);
380 *width
= obj
->frame
.width
;
381 *height
= obj
->frame
.height
* 2;
382 release_user_handle_ptr( obj
);
386 /**********************************************************************
387 * NtUserGetCursorFrameInfo (win32u.@)
389 HCURSOR WINAPI
NtUserGetCursorFrameInfo( HCURSOR cursor
, DWORD istep
, DWORD
*rate_jiffies
,
392 struct cursoricon_object
*obj
;
396 if (!rate_jiffies
|| !num_steps
) return 0;
398 if (!(obj
= get_icon_ptr( cursor
))) return 0;
400 TRACE( "%p => %d %p %p\n", cursor
, (int)istep
, rate_jiffies
, num_steps
);
402 icon_steps
= obj
->is_ani
? obj
->ani
.num_steps
: 1;
403 if (istep
< icon_steps
|| !obj
->is_ani
)
405 UINT icon_frames
= 1;
408 icon_frames
= obj
->ani
.num_frames
;
409 if (obj
->is_ani
&& icon_frames
> 1)
410 ret
= obj
->ani
.frames
[istep
];
413 if (icon_frames
== 1)
418 else if (icon_steps
== 1)
421 *rate_jiffies
= obj
->delay
;
423 else if (istep
< icon_steps
)
425 struct cursoricon_object
*frame
;
427 *num_steps
= icon_steps
;
428 frame
= get_icon_ptr( obj
->ani
.frames
[istep
] );
429 if (obj
->ani
.num_steps
== 1)
432 *num_steps
= obj
->ani
.num_steps
;
433 *rate_jiffies
= frame
->delay
;
434 release_user_handle_ptr( frame
);
438 release_user_handle_ptr( obj
);
442 /***********************************************************************
445 * Helper function to duplicate a bitmap.
447 static HBITMAP
copy_bitmap( HBITMAP bitmap
)
450 HBITMAP new_bitmap
= 0;
453 if (!bitmap
) return 0;
454 if (!NtGdiExtGetObjectW( bitmap
, sizeof(bmp
), &bmp
)) return 0;
456 if ((src
= NtGdiCreateCompatibleDC( 0 )) && (dst
= NtGdiCreateCompatibleDC( 0 )))
458 NtGdiSelectBitmap( src
, bitmap
);
459 if ((new_bitmap
= NtGdiCreateCompatibleBitmap( src
, bmp
.bmWidth
, bmp
.bmHeight
)))
461 NtGdiSelectBitmap( dst
, new_bitmap
);
462 NtGdiBitBlt( dst
, 0, 0, bmp
.bmWidth
, bmp
.bmHeight
, src
, 0, 0, SRCCOPY
, 0, 0 );
465 NtGdiDeleteObjectApp( dst
);
466 NtGdiDeleteObjectApp( src
);
470 /**********************************************************************
471 * NtUserGetIconInfo (win32u.@)
473 BOOL WINAPI
NtUserGetIconInfo( HICON icon
, ICONINFO
*info
, UNICODE_STRING
*module
,
474 UNICODE_STRING
*res_name
, DWORD
*bpp
, LONG unk
)
476 struct cursoricon_object
*obj
, *frame_obj
;
479 if (!(obj
= get_icon_ptr( icon
)))
481 RtlSetLastWin32Error( ERROR_INVALID_CURSOR_HANDLE
);
484 if (!(frame_obj
= get_icon_frame_ptr( icon
, 0 )))
486 release_user_handle_ptr( obj
);
490 TRACE( "%p => %dx%d\n", icon
, frame_obj
->frame
.width
, frame_obj
->frame
.height
);
492 info
->fIcon
= obj
->is_icon
;
493 info
->xHotspot
= frame_obj
->frame
.hotspot
.x
;
494 info
->yHotspot
= frame_obj
->frame
.hotspot
.y
;
495 info
->hbmColor
= copy_bitmap( frame_obj
->frame
.color
);
496 info
->hbmMask
= copy_bitmap( frame_obj
->frame
.mask
);
497 if (!info
->hbmMask
|| (!info
->hbmColor
&& frame_obj
->frame
.color
))
499 NtGdiDeleteObjectApp( info
->hbmMask
);
500 NtGdiDeleteObjectApp( info
->hbmColor
);
503 else if (obj
->module
.Length
)
507 size_t size
= min( module
->MaximumLength
, obj
->module
.Length
);
508 if (size
) memcpy( module
->Buffer
, obj
->module
.Buffer
, size
);
509 module
->Length
= size
/ sizeof(WCHAR
); /* length in chars, not bytes */
513 if (IS_INTRESOURCE( obj
->resname
))
515 res_name
->Buffer
= obj
->resname
;
516 res_name
->Length
= 0;
520 size_t size
= min( res_name
->MaximumLength
, lstrlenW( obj
->resname
) * sizeof(WCHAR
) );
521 if (size
) memcpy( res_name
->Buffer
, obj
->resname
, size
);
522 res_name
->Length
= size
/ sizeof(WCHAR
); /* length in chars, not bytes */
528 if (module
) module
->Length
= 0;
531 res_name
->Length
= 0;
532 res_name
->Buffer
= NULL
;
535 release_user_handle_ptr( frame_obj
);
536 release_user_handle_ptr( obj
);
540 /******************************************************************************
541 * NtUserDrawIconEx (win32u.@)
543 BOOL WINAPI
NtUserDrawIconEx( HDC hdc
, INT x0
, INT y0
, HICON icon
, INT width
,
544 INT height
, UINT step
, HBRUSH brush
, UINT flags
)
546 struct cursoricon_object
*obj
;
547 HBITMAP offscreen_bitmap
= 0;
548 HDC hdc_dest
, mem_dc
;
549 COLORREF old_fg
, old_bg
;
550 INT x
, y
, nStretchMode
;
553 TRACE_(icon
)( "(hdc=%p,pos=%d.%d,hicon=%p,extend=%d.%d,step=%d,br=%p,flags=0x%08x)\n",
554 hdc
, x0
, y0
, icon
, width
, height
, step
, brush
, flags
);
556 if (!(obj
= get_icon_frame_ptr( icon
, step
)))
558 FIXME_(icon
)("Error retrieving icon frame %d\n", step
);
561 if (!(mem_dc
= NtGdiCreateCompatibleDC( hdc
)))
563 release_user_handle_ptr( obj
);
567 if (flags
& DI_NOMIRROR
)
568 FIXME_(icon
)("Ignoring flag DI_NOMIRROR\n");
570 /* Calculate the size of the destination image. */
573 if (flags
& DI_DEFAULTSIZE
)
574 width
= get_system_metrics( SM_CXICON
);
576 width
= obj
->frame
.width
;
580 if (flags
& DI_DEFAULTSIZE
)
581 height
= get_system_metrics( SM_CYICON
);
583 height
= obj
->frame
.height
;
586 if (get_gdi_object_type( brush
) == NTGDI_OBJ_BRUSH
)
591 SetRect(&r
, 0, 0, width
, width
);
593 if (!(hdc_dest
= NtGdiCreateCompatibleDC(hdc
))) goto failed
;
594 if (!(offscreen_bitmap
= NtGdiCreateCompatibleBitmap(hdc
, width
, height
)))
596 NtGdiDeleteObjectApp( hdc_dest
);
599 NtGdiSelectBitmap( hdc_dest
, offscreen_bitmap
);
601 prev_brush
= NtGdiSelectBrush( hdc_dest
, brush
);
602 NtGdiPatBlt( hdc_dest
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
, PATCOPY
);
603 if (prev_brush
) NtGdiSelectBrush( hdc_dest
, prev_brush
);
613 nStretchMode
= set_stretch_blt_mode( hdc
, STRETCH_DELETESCANS
);
614 NtGdiGetAndSetDCDword( hdc
, NtGdiSetTextColor
, RGB(0,0,0), &old_fg
);
615 NtGdiGetAndSetDCDword( hdc
, NtGdiSetBkColor
, RGB(255,255,255), &old_bg
);
617 if (obj
->frame
.alpha
&& (flags
& DI_IMAGE
))
619 BOOL alpha_blend
= TRUE
;
621 if (get_gdi_object_type( hdc_dest
) == NTGDI_OBJ_MEMDC
)
624 HBITMAP bmp
= NtGdiGetDCObject( hdc_dest
, NTGDI_OBJ_SURF
);
625 alpha_blend
= NtGdiExtGetObjectW( bmp
, sizeof(bm
), &bm
) && bm
.bmBitsPixel
> 8;
629 NtGdiSelectBitmap( mem_dc
, obj
->frame
.alpha
);
630 if (NtGdiAlphaBlend( hdc_dest
, x
, y
, width
, height
, mem_dc
,
631 0, 0, obj
->frame
.width
, obj
->frame
.height
,
632 MAKEFOURCC( AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
), 0 ))
639 DWORD rop
= (flags
& DI_IMAGE
) ? SRCAND
: SRCCOPY
;
640 NtGdiSelectBitmap( mem_dc
, obj
->frame
.mask
);
641 NtGdiStretchBlt( hdc_dest
, x
, y
, width
, height
,
642 mem_dc
, 0, 0, obj
->frame
.width
, obj
->frame
.height
, rop
, 0 );
645 if (flags
& DI_IMAGE
)
647 if (obj
->frame
.color
)
649 DWORD rop
= (flags
& DI_MASK
) ? SRCINVERT
: SRCCOPY
;
650 NtGdiSelectBitmap( mem_dc
, obj
->frame
.color
);
651 NtGdiStretchBlt( hdc_dest
, x
, y
, width
, height
,
652 mem_dc
, 0, 0, obj
->frame
.width
, obj
->frame
.height
, rop
, 0 );
656 DWORD rop
= (flags
& DI_MASK
) ? SRCINVERT
: SRCCOPY
;
657 NtGdiSelectBitmap( mem_dc
, obj
->frame
.mask
);
658 NtGdiStretchBlt( hdc_dest
, x
, y
, width
, height
,
659 mem_dc
, 0, obj
->frame
.height
, obj
->frame
.width
,
660 obj
->frame
.height
, rop
, 0 );
665 if (offscreen_bitmap
) NtGdiBitBlt( hdc
, x0
, y0
, width
, height
, hdc_dest
, 0, 0, SRCCOPY
, 0, 0 );
667 NtGdiGetAndSetDCDword( hdc
, NtGdiSetTextColor
, old_fg
, NULL
);
668 NtGdiGetAndSetDCDword( hdc
, NtGdiSetBkColor
, old_bg
, NULL
);
669 nStretchMode
= set_stretch_blt_mode( hdc
, nStretchMode
);
672 if (hdc_dest
!= hdc
) NtGdiDeleteObjectApp( hdc_dest
);
673 if (offscreen_bitmap
) NtGdiDeleteObjectApp( offscreen_bitmap
);
675 NtGdiDeleteObjectApp( mem_dc
);
676 release_user_handle_ptr( obj
);
680 ULONG_PTR
get_icon_param( HICON handle
)
683 struct cursoricon_object
*obj
= get_user_handle_ptr( handle
, NTUSER_OBJ_ICON
);
685 if (obj
== OBJ_OTHER_PROCESS
) WARN( "icon handle %p from other process\n", handle
);
689 release_user_handle_ptr( obj
);
694 ULONG_PTR
set_icon_param( HICON handle
, ULONG_PTR param
)
697 struct cursoricon_object
*obj
= get_user_handle_ptr( handle
, NTUSER_OBJ_ICON
);
699 if (obj
== OBJ_OTHER_PROCESS
) WARN( "icon handle %p from other process\n", handle
);
704 release_user_handle_ptr( obj
);
709 /******************************************************************************
710 * CopyImage (win32u.so)
712 HANDLE WINAPI
CopyImage( HANDLE hwnd
, UINT type
, INT dx
, INT dy
, UINT flags
)
717 struct copy_image_params params
=
718 { .hwnd
= hwnd
, .type
= type
, .dx
= dx
, .dy
= dy
, .flags
= flags
};
720 ret
= KeUserModeCallback( NtUserCopyImage
, ¶ms
, sizeof(params
), &ret_ptr
, &ret_len
);
721 return UlongToHandle( ret
);
724 /******************************************************************************
725 * LoadImage (win32u.so)
727 HANDLE WINAPI
LoadImageW( HINSTANCE hinst
, const WCHAR
*name
, UINT type
,
728 INT dx
, INT dy
, UINT flags
)
733 struct load_image_params params
=
734 { .hinst
= hinst
, .name
= name
, .type
= type
, .dx
= dx
, .dy
= dy
, .flags
= flags
};
738 ERR( "name %s not supported in Unix modules\n", debugstr_w( name
));
741 ret
= KeUserModeCallback( NtUserLoadImage
, ¶ms
, sizeof(params
), &ret_ptr
, &ret_len
);
742 return UlongToHandle( ret
);