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 /***********************************************************************
78 * NtUserShowCursor (win32u.@)
80 INT WINAPI
NtUserShowCursor( BOOL show
)
83 int increment
= show
? 1 : -1;
86 SERVER_START_REQ( set_cursor
)
88 req
->flags
= SET_CURSOR_COUNT
;
89 req
->show_count
= increment
;
90 wine_server_call( req
);
91 cursor
= wine_server_ptr_handle( reply
->prev_handle
);
92 count
= reply
->prev_count
+ increment
;
96 TRACE("%d, count=%d\n", show
, count
);
98 if (show
&& !count
) user_driver
->pSetCursor( cursor
);
99 else if (!show
&& count
== -1) user_driver
->pSetCursor( 0 );
104 /***********************************************************************
105 * NtUserSetCursor (win32u.@)
107 HCURSOR WINAPI
NtUserSetCursor( HCURSOR cursor
)
109 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
)))
122 old_cursor
= wine_server_ptr_handle( reply
->prev_handle
);
123 show_count
= reply
->prev_count
;
129 user_driver
->pSetCursor( show_count
>= 0 ? cursor
: 0 );
131 if (!(obj
= get_icon_ptr( old_cursor
))) return 0;
132 release_user_handle_ptr( obj
);
136 /***********************************************************************
137 * NtUserGetCursor (win32u.@)
139 HCURSOR WINAPI
NtUserGetCursor(void)
143 SERVER_START_REQ( set_cursor
)
146 wine_server_call( req
);
147 ret
= wine_server_ptr_handle( reply
->prev_handle
);
153 HICON
alloc_cursoricon_handle( BOOL is_icon
)
155 struct cursoricon_object
*obj
;
158 if (!(obj
= calloc( 1, sizeof(*obj
) ))) return NULL
;
159 obj
->is_icon
= is_icon
;
160 if (!(handle
= alloc_user_handle( &obj
->obj
, NTUSER_OBJ_ICON
))) free( obj
);
164 static struct cursoricon_object
*get_icon_frame_ptr( HICON handle
, UINT step
)
166 struct cursoricon_object
*obj
, *ret
;
168 if (!(obj
= get_icon_ptr( handle
))) return NULL
;
169 if (!obj
->is_ani
) return obj
;
170 if (step
>= obj
->ani
.num_steps
)
172 release_user_handle_ptr( obj
);
175 ret
= get_icon_ptr( obj
->ani
.frames
[step
] );
176 release_user_handle_ptr( obj
);
180 static BOOL
free_icon_handle( HICON handle
)
182 struct cursoricon_object
*obj
= free_user_handle( handle
, NTUSER_OBJ_ICON
);
184 if (obj
== OBJ_OTHER_PROCESS
) WARN( "icon handle %p from other process\n", handle
);
187 ULONG param
= obj
->param
;
192 assert( !obj
->rsrc
); /* shared icons can't be freed */
196 if (obj
->frame
.alpha
) NtGdiDeleteObjectApp( obj
->frame
.alpha
);
197 if (obj
->frame
.color
) NtGdiDeleteObjectApp( obj
->frame
.color
);
198 if (obj
->frame
.mask
) NtGdiDeleteObjectApp( obj
->frame
.mask
);
202 for (i
= 0; i
< obj
->ani
.num_steps
; i
++)
204 HICON hFrame
= obj
->ani
.frames
[i
];
210 free_icon_handle( obj
->ani
.frames
[i
] );
211 for (j
= 0; j
< obj
->ani
.num_steps
; j
++)
213 if (obj
->ani
.frames
[j
] == hFrame
) obj
->ani
.frames
[j
] = 0;
217 free( obj
->ani
.frames
);
219 if (!IS_INTRESOURCE( obj
->resname
)) free( obj
->resname
);
221 if (param
) KeUserModeCallback( NtUserCallFreeIcon
, ¶m
, sizeof(param
), &ret_ptr
, &ret_len
);
222 user_driver
->pDestroyCursorIcon( handle
);
228 /***********************************************************************
229 * NtUserDestroyCursor (win32u.@)
231 BOOL WINAPI
NtUserDestroyCursor( HCURSOR cursor
, ULONG arg
)
233 struct cursoricon_object
*obj
;
236 TRACE( "%p\n", cursor
);
238 if (!(obj
= get_icon_ptr( cursor
))) return FALSE
;
239 shared
= obj
->is_shared
;
240 release_user_handle_ptr( obj
);
241 ret
= NtUserGetCursor() != cursor
;
242 if (!shared
) free_icon_handle( cursor
);
246 /***********************************************************************
247 * NtUserSetCursorIconData (win32u.@)
249 BOOL WINAPI
NtUserSetCursorIconData( HCURSOR cursor
, UNICODE_STRING
*module
, UNICODE_STRING
*res_name
,
250 struct cursoricon_desc
*desc
)
252 struct cursoricon_object
*obj
;
255 if (!(obj
= get_icon_ptr( cursor
))) return FALSE
;
257 if (obj
->is_ani
|| obj
->frame
.width
)
259 /* already initialized */
260 release_user_handle_ptr( obj
);
261 RtlSetLastWin32Error( ERROR_INVALID_CURSOR_HANDLE
);
265 obj
->delay
= desc
->delay
;
269 if (!(obj
->ani
.frames
= calloc( desc
->num_steps
, sizeof(*obj
->ani
.frames
) )))
271 release_user_handle_ptr( obj
);
275 obj
->ani
.num_steps
= desc
->num_steps
;
276 obj
->ani
.num_frames
= desc
->num_frames
;
278 else obj
->frame
= desc
->frames
[0];
282 else if (res_name
->Length
)
284 obj
->resname
= malloc( res_name
->Length
+ sizeof(WCHAR
) );
287 memcpy( obj
->resname
, res_name
->Buffer
, res_name
->Length
);
288 obj
->resname
[res_name
->Length
/ sizeof(WCHAR
)] = 0;
292 obj
->resname
= MAKEINTRESOURCEW( LOWORD(res_name
->Buffer
) );
294 if (module
&& module
->Length
&& (obj
->module
.Buffer
= malloc( module
->Length
)))
296 memcpy( obj
->module
.Buffer
, module
->Buffer
, module
->Length
);
297 obj
->module
.Length
= module
->Length
;
302 /* Setup the animated frames in the correct sequence */
303 for (i
= 0; i
< desc
->num_steps
; i
++)
305 struct cursoricon_desc frame_desc
;
308 if (obj
->ani
.frames
[i
]) continue; /* already set */
310 frame_id
= desc
->frame_seq
? desc
->frame_seq
[i
] : i
;
311 if (frame_id
>= obj
->ani
.num_frames
)
313 frame_id
= obj
->ani
.num_frames
- 1;
314 ERR_(cursor
)( "Sequence indicates frame past end of list, corrupt?\n" );
316 memset( &frame_desc
, 0, sizeof(frame_desc
) );
317 frame_desc
.delay
= desc
->frame_rates
? desc
->frame_rates
[i
] : desc
->delay
;
318 frame_desc
.frames
= &desc
->frames
[frame_id
];
319 if (!(obj
->ani
.frames
[i
] = alloc_cursoricon_handle( obj
->is_icon
)) ||
320 !NtUserSetCursorIconData( obj
->ani
.frames
[i
], NULL
, NULL
, &frame_desc
))
322 release_user_handle_ptr( obj
);
328 for (j
= i
+ 1; j
< obj
->ani
.num_steps
; j
++)
330 if (desc
->frame_seq
[j
] == frame_id
) obj
->ani
.frames
[j
] = obj
->ani
.frames
[i
];
336 if (desc
->flags
& LR_SHARED
)
338 obj
->is_shared
= TRUE
;
339 if (obj
->module
.Length
)
341 obj
->rsrc
= desc
->rsrc
;
342 list_add_head( &icon_cache
, &obj
->entry
);
346 release_user_handle_ptr( obj
);
350 /***********************************************************************
351 * NtUserFindExistingCursorIcon (win32u.@)
353 HICON WINAPI
NtUserFindExistingCursorIcon( UNICODE_STRING
*module
, UNICODE_STRING
*res_name
, void *desc
)
355 struct cursoricon_object
*ptr
;
359 LIST_FOR_EACH_ENTRY( ptr
, &icon_cache
, struct cursoricon_object
, entry
)
361 if (ptr
->module
.Length
!= module
->Length
) continue;
362 if (memcmp( ptr
->module
.Buffer
, module
->Buffer
, module
->Length
)) continue;
363 /* We pass rsrc as desc argument, this is not compatible with Windows */
364 if (ptr
->rsrc
!= desc
) continue;
365 ret
= ptr
->obj
.handle
;
372 /***********************************************************************
373 * NtUserGetIconSize (win32u.@)
375 BOOL WINAPI
NtUserGetIconSize( HICON handle
, UINT step
, LONG
*width
, LONG
*height
)
377 struct cursoricon_object
*obj
;
379 if (!(obj
= get_icon_frame_ptr( handle
, step
)))
381 RtlSetLastWin32Error( ERROR_INVALID_CURSOR_HANDLE
);
385 *width
= obj
->frame
.width
;
386 *height
= obj
->frame
.height
* 2;
387 release_user_handle_ptr( obj
);
391 /**********************************************************************
392 * NtUserGetCursorFrameInfo (win32u.@)
394 HCURSOR WINAPI
NtUserGetCursorFrameInfo( HCURSOR cursor
, DWORD istep
, DWORD
*rate_jiffies
,
397 struct cursoricon_object
*obj
;
401 if (!rate_jiffies
|| !num_steps
) return 0;
403 if (!(obj
= get_icon_ptr( cursor
))) return 0;
405 TRACE( "%p => %d %p %p\n", cursor
, (int)istep
, rate_jiffies
, num_steps
);
407 icon_steps
= obj
->is_ani
? obj
->ani
.num_steps
: 1;
408 if (istep
< icon_steps
|| !obj
->is_ani
)
410 UINT icon_frames
= 1;
413 icon_frames
= obj
->ani
.num_frames
;
414 if (obj
->is_ani
&& icon_frames
> 1)
415 ret
= obj
->ani
.frames
[istep
];
418 if (icon_frames
== 1)
423 else if (icon_steps
== 1)
426 *rate_jiffies
= obj
->delay
;
428 else if (istep
< icon_steps
)
430 struct cursoricon_object
*frame
;
432 *num_steps
= icon_steps
;
433 frame
= get_icon_ptr( obj
->ani
.frames
[istep
] );
434 if (obj
->ani
.num_steps
== 1)
437 *num_steps
= obj
->ani
.num_steps
;
438 *rate_jiffies
= frame
->delay
;
439 release_user_handle_ptr( frame
);
443 release_user_handle_ptr( obj
);
447 /***********************************************************************
450 * Helper function to duplicate a bitmap.
452 static HBITMAP
copy_bitmap( HBITMAP bitmap
)
455 HBITMAP new_bitmap
= 0;
458 if (!bitmap
) return 0;
459 if (!NtGdiExtGetObjectW( bitmap
, sizeof(bmp
), &bmp
)) return 0;
461 if ((src
= NtGdiCreateCompatibleDC( 0 )) && (dst
= NtGdiCreateCompatibleDC( 0 )))
463 NtGdiSelectBitmap( src
, bitmap
);
464 if ((new_bitmap
= NtGdiCreateCompatibleBitmap( src
, bmp
.bmWidth
, bmp
.bmHeight
)))
466 NtGdiSelectBitmap( dst
, new_bitmap
);
467 NtGdiBitBlt( dst
, 0, 0, bmp
.bmWidth
, bmp
.bmHeight
, src
, 0, 0, SRCCOPY
, 0, 0 );
470 NtGdiDeleteObjectApp( dst
);
471 NtGdiDeleteObjectApp( src
);
475 /**********************************************************************
476 * NtUserGetIconInfo (win32u.@)
478 BOOL WINAPI
NtUserGetIconInfo( HICON icon
, ICONINFO
*info
, UNICODE_STRING
*module
,
479 UNICODE_STRING
*res_name
, DWORD
*bpp
, LONG unk
)
481 struct cursoricon_object
*obj
, *frame_obj
;
484 if (!(obj
= get_icon_ptr( icon
)))
486 RtlSetLastWin32Error( ERROR_INVALID_CURSOR_HANDLE
);
489 if (!(frame_obj
= get_icon_frame_ptr( icon
, 0 )))
491 release_user_handle_ptr( obj
);
495 TRACE( "%p => %dx%d\n", icon
, frame_obj
->frame
.width
, frame_obj
->frame
.height
);
497 info
->fIcon
= obj
->is_icon
;
498 info
->xHotspot
= frame_obj
->frame
.hotspot
.x
;
499 info
->yHotspot
= frame_obj
->frame
.hotspot
.y
;
500 info
->hbmColor
= copy_bitmap( frame_obj
->frame
.color
);
501 info
->hbmMask
= copy_bitmap( frame_obj
->frame
.mask
);
502 if (!info
->hbmMask
|| (!info
->hbmColor
&& frame_obj
->frame
.color
))
504 NtGdiDeleteObjectApp( info
->hbmMask
);
505 NtGdiDeleteObjectApp( info
->hbmColor
);
508 else if (obj
->module
.Length
)
512 size_t size
= min( module
->MaximumLength
, obj
->module
.Length
);
513 if (size
) memcpy( module
->Buffer
, obj
->module
.Buffer
, size
);
514 module
->Length
= size
/ sizeof(WCHAR
); /* length in chars, not bytes */
518 if (IS_INTRESOURCE( obj
->resname
))
520 res_name
->Buffer
= obj
->resname
;
521 res_name
->Length
= 0;
525 size_t size
= min( res_name
->MaximumLength
, lstrlenW( obj
->resname
) * sizeof(WCHAR
) );
526 if (size
) memcpy( res_name
->Buffer
, obj
->resname
, size
);
527 res_name
->Length
= size
/ sizeof(WCHAR
); /* length in chars, not bytes */
533 if (module
) module
->Length
= 0;
536 res_name
->Length
= 0;
537 res_name
->Buffer
= NULL
;
540 release_user_handle_ptr( frame_obj
);
541 release_user_handle_ptr( obj
);
545 /******************************************************************************
546 * NtUserDrawIconEx (win32u.@)
548 BOOL WINAPI
NtUserDrawIconEx( HDC hdc
, INT x0
, INT y0
, HICON icon
, INT width
,
549 INT height
, UINT step
, HBRUSH brush
, UINT flags
)
551 struct cursoricon_object
*obj
;
552 HBITMAP offscreen_bitmap
= 0;
553 HDC hdc_dest
, mem_dc
;
554 COLORREF old_fg
, old_bg
;
555 INT x
, y
, nStretchMode
;
558 TRACE_(icon
)( "(hdc=%p,pos=%d.%d,hicon=%p,extend=%d.%d,step=%d,br=%p,flags=0x%08x)\n",
559 hdc
, x0
, y0
, icon
, width
, height
, step
, brush
, flags
);
561 if (!(obj
= get_icon_frame_ptr( icon
, step
)))
563 FIXME_(icon
)("Error retrieving icon frame %d\n", step
);
566 if (!(mem_dc
= NtGdiCreateCompatibleDC( hdc
)))
568 release_user_handle_ptr( obj
);
572 if (flags
& DI_NOMIRROR
)
573 FIXME_(icon
)("Ignoring flag DI_NOMIRROR\n");
575 /* Calculate the size of the destination image. */
578 if (flags
& DI_DEFAULTSIZE
)
579 width
= get_system_metrics( SM_CXICON
);
581 width
= obj
->frame
.width
;
585 if (flags
& DI_DEFAULTSIZE
)
586 height
= get_system_metrics( SM_CYICON
);
588 height
= obj
->frame
.height
;
591 if (get_gdi_object_type( brush
) == NTGDI_OBJ_BRUSH
)
596 SetRect(&r
, 0, 0, width
, width
);
598 if (!(hdc_dest
= NtGdiCreateCompatibleDC(hdc
))) goto failed
;
599 if (!(offscreen_bitmap
= NtGdiCreateCompatibleBitmap(hdc
, width
, height
)))
601 NtGdiDeleteObjectApp( hdc_dest
);
604 NtGdiSelectBitmap( hdc_dest
, offscreen_bitmap
);
606 prev_brush
= NtGdiSelectBrush( hdc_dest
, brush
);
607 NtGdiPatBlt( hdc_dest
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
, PATCOPY
);
608 if (prev_brush
) NtGdiSelectBrush( hdc_dest
, prev_brush
);
618 nStretchMode
= set_stretch_blt_mode( hdc
, STRETCH_DELETESCANS
);
619 NtGdiGetAndSetDCDword( hdc
, NtGdiSetTextColor
, RGB(0,0,0), &old_fg
);
620 NtGdiGetAndSetDCDword( hdc
, NtGdiSetBkColor
, RGB(255,255,255), &old_bg
);
622 if (obj
->frame
.alpha
&& (flags
& DI_IMAGE
))
624 BOOL alpha_blend
= TRUE
;
626 if (get_gdi_object_type( hdc_dest
) == NTGDI_OBJ_MEMDC
)
629 HBITMAP bmp
= NtGdiGetDCObject( hdc_dest
, NTGDI_OBJ_SURF
);
630 alpha_blend
= NtGdiExtGetObjectW( bmp
, sizeof(bm
), &bm
) && bm
.bmBitsPixel
> 8;
634 NtGdiSelectBitmap( mem_dc
, obj
->frame
.alpha
);
635 if (NtGdiAlphaBlend( hdc_dest
, x
, y
, width
, height
, mem_dc
,
636 0, 0, obj
->frame
.width
, obj
->frame
.height
,
637 MAKEFOURCC( AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
), 0 ))
644 DWORD rop
= (flags
& DI_IMAGE
) ? SRCAND
: SRCCOPY
;
645 NtGdiSelectBitmap( mem_dc
, obj
->frame
.mask
);
646 NtGdiStretchBlt( hdc_dest
, x
, y
, width
, height
,
647 mem_dc
, 0, 0, obj
->frame
.width
, obj
->frame
.height
, rop
, 0 );
650 if (flags
& DI_IMAGE
)
652 if (obj
->frame
.color
)
654 DWORD rop
= (flags
& DI_MASK
) ? SRCINVERT
: SRCCOPY
;
655 NtGdiSelectBitmap( mem_dc
, obj
->frame
.color
);
656 NtGdiStretchBlt( hdc_dest
, x
, y
, width
, height
,
657 mem_dc
, 0, 0, obj
->frame
.width
, obj
->frame
.height
, rop
, 0 );
661 DWORD rop
= (flags
& DI_MASK
) ? SRCINVERT
: SRCCOPY
;
662 NtGdiSelectBitmap( mem_dc
, obj
->frame
.mask
);
663 NtGdiStretchBlt( hdc_dest
, x
, y
, width
, height
,
664 mem_dc
, 0, obj
->frame
.height
, obj
->frame
.width
,
665 obj
->frame
.height
, rop
, 0 );
670 if (offscreen_bitmap
) NtGdiBitBlt( hdc
, x0
, y0
, width
, height
, hdc_dest
, 0, 0, SRCCOPY
, 0, 0 );
672 NtGdiGetAndSetDCDword( hdc
, NtGdiSetTextColor
, old_fg
, NULL
);
673 NtGdiGetAndSetDCDword( hdc
, NtGdiSetBkColor
, old_bg
, NULL
);
674 nStretchMode
= set_stretch_blt_mode( hdc
, nStretchMode
);
677 if (hdc_dest
!= hdc
) NtGdiDeleteObjectApp( hdc_dest
);
678 if (offscreen_bitmap
) NtGdiDeleteObjectApp( offscreen_bitmap
);
680 NtGdiDeleteObjectApp( mem_dc
);
681 release_user_handle_ptr( obj
);
685 ULONG_PTR
get_icon_param( HICON handle
)
688 struct cursoricon_object
*obj
= get_user_handle_ptr( handle
, NTUSER_OBJ_ICON
);
690 if (obj
== OBJ_OTHER_PROCESS
) WARN( "icon handle %p from other process\n", handle
);
694 release_user_handle_ptr( obj
);
699 ULONG_PTR
set_icon_param( HICON handle
, ULONG_PTR param
)
702 struct cursoricon_object
*obj
= get_user_handle_ptr( handle
, NTUSER_OBJ_ICON
);
704 if (obj
== OBJ_OTHER_PROCESS
) WARN( "icon handle %p from other process\n", handle
);
709 release_user_handle_ptr( obj
);
714 /******************************************************************************
715 * CopyImage (win32u.so)
717 HANDLE WINAPI
CopyImage( HANDLE hwnd
, UINT type
, INT dx
, INT dy
, UINT flags
)
722 struct copy_image_params params
=
723 { .hwnd
= hwnd
, .type
= type
, .dx
= dx
, .dy
= dy
, .flags
= flags
};
725 ret
= KeUserModeCallback( NtUserCopyImage
, ¶ms
, sizeof(params
), &ret_ptr
, &ret_len
);
726 return UlongToHandle( ret
);
729 /******************************************************************************
730 * LoadImage (win32u.so)
732 HANDLE WINAPI
LoadImageW( HINSTANCE hinst
, const WCHAR
*name
, UINT type
,
733 INT dx
, INT dy
, UINT flags
)
738 struct load_image_params params
=
739 { .hinst
= hinst
, .name
= name
, .type
= type
, .dx
= dx
, .dy
= dy
, .flags
= flags
};
743 ERR( "name %s not supported in Unix modules\n", debugstr_w( name
));
746 ret
= KeUserModeCallback( NtUserLoadImage
, ¶ms
, sizeof(params
), &ret_ptr
, &ret_len
);
747 return UlongToHandle( ret
);