dinput: Keep an internal refcount for dinput devices references.
[wine.git] / dlls / win32u / window.c
bloba3ff0647dcd9d37ae83ac13682047d539dedf484
1 /*
2 * Window related functions
4 * Copyright 1993, 1994 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #if 0
23 #pragma makedep unix
24 #endif
26 #include <assert.h>
28 #include "ntstatus.h"
29 #define WIN32_NO_STATUS
30 #include "ntgdi_private.h"
31 #include "ntuser_private.h"
32 #include "wine/server.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(win);
37 #define NB_USER_HANDLES ((LAST_USER_HANDLE - FIRST_USER_HANDLE + 1) >> 1)
38 #define USER_HANDLE_TO_INDEX(hwnd) ((LOWORD(hwnd) - FIRST_USER_HANDLE) >> 1)
40 static void *user_handles[NB_USER_HANDLES];
42 #define SWP_AGG_NOGEOMETRYCHANGE \
43 (SWP_NOSIZE | SWP_NOCLIENTSIZE | SWP_NOZORDER)
44 #define SWP_AGG_NOPOSCHANGE \
45 (SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_NOZORDER)
46 #define SWP_AGG_STATUSFLAGS \
47 (SWP_AGG_NOPOSCHANGE | SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_SHOWWINDOW)
48 #define SWP_AGG_NOCLIENTCHANGE \
49 (SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE)
51 #define PLACE_MIN 0x0001
52 #define PLACE_MAX 0x0002
53 #define PLACE_RECT 0x0004
55 /***********************************************************************
56 * alloc_user_handle
58 HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type )
60 HANDLE handle = 0;
62 SERVER_START_REQ( alloc_user_handle )
64 if (!wine_server_call_err( req )) handle = wine_server_ptr_handle( reply->handle );
66 SERVER_END_REQ;
68 if (handle)
70 UINT index = USER_HANDLE_TO_INDEX( handle );
72 assert( index < NB_USER_HANDLES );
73 ptr->handle = handle;
74 ptr->type = type;
75 InterlockedExchangePointer( &user_handles[index], ptr );
77 return handle;
80 /***********************************************************************
81 * get_user_handle_ptr
83 void *get_user_handle_ptr( HANDLE handle, unsigned int type )
85 struct user_object *ptr;
86 WORD index = USER_HANDLE_TO_INDEX( handle );
88 if (index >= NB_USER_HANDLES) return NULL;
90 user_lock();
91 if ((ptr = user_handles[index]))
93 if (ptr->type == type &&
94 ((UINT)(UINT_PTR)ptr->handle == (UINT)(UINT_PTR)handle ||
95 !HIWORD(handle) || HIWORD(handle) == 0xffff))
96 return ptr;
97 ptr = NULL;
99 else ptr = OBJ_OTHER_PROCESS;
100 user_unlock();
101 return ptr;
104 /***********************************************************************
105 * set_user_handle_ptr
107 static void set_user_handle_ptr( HANDLE handle, struct user_object *ptr )
109 WORD index = USER_HANDLE_TO_INDEX(handle);
110 assert( index < NB_USER_HANDLES );
111 InterlockedExchangePointer( &user_handles[index], ptr );
114 /***********************************************************************
115 * release_user_handle_ptr
117 void release_user_handle_ptr( void *ptr )
119 assert( ptr && ptr != OBJ_OTHER_PROCESS );
120 user_unlock();
123 /***********************************************************************
124 * free_user_handle
126 void *free_user_handle( HANDLE handle, unsigned int type )
128 struct user_object *ptr;
129 WORD index = USER_HANDLE_TO_INDEX( handle );
131 if ((ptr = get_user_handle_ptr( handle, type )) && ptr != OBJ_OTHER_PROCESS)
133 SERVER_START_REQ( free_user_handle )
135 req->handle = wine_server_user_handle( handle );
136 if (wine_server_call( req )) ptr = NULL;
137 else InterlockedCompareExchangePointer( &user_handles[index], NULL, ptr );
139 SERVER_END_REQ;
140 user_unlock();
142 return ptr;
145 /***********************************************************************
146 * next_thread_window
148 static WND *next_thread_window_ptr( HWND *hwnd )
150 struct user_object *ptr;
151 WND *win;
152 WORD index = *hwnd ? USER_HANDLE_TO_INDEX( *hwnd ) + 1 : 0;
154 while (index < NB_USER_HANDLES)
156 if (!(ptr = user_handles[index++])) continue;
157 if (ptr->type != NTUSER_OBJ_WINDOW) continue;
158 win = (WND *)ptr;
159 if (win->tid != GetCurrentThreadId()) continue;
160 *hwnd = ptr->handle;
161 return win;
163 return NULL;
166 /*******************************************************************
167 * get_hwnd_message_parent
169 * Return the parent for HWND_MESSAGE windows.
171 HWND get_hwnd_message_parent(void)
173 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
175 if (!thread_info->msg_window) get_desktop_window(); /* trigger creation */
176 return UlongToHandle( thread_info->msg_window );
179 /***********************************************************************
180 * get_full_window_handle
182 * Convert a possibly truncated window handle to a full 32-bit handle.
184 HWND get_full_window_handle( HWND hwnd )
186 WND *win;
188 if (!hwnd || (ULONG_PTR)hwnd >> 16) return hwnd;
189 if (LOWORD(hwnd) <= 1 || LOWORD(hwnd) == 0xffff) return hwnd;
190 /* do sign extension for -2 and -3 */
191 if (LOWORD(hwnd) >= (WORD)-3) return (HWND)(LONG_PTR)(INT16)LOWORD(hwnd);
193 if (!(win = get_win_ptr( hwnd ))) return hwnd;
195 if (win == WND_DESKTOP)
197 if (LOWORD(hwnd) == LOWORD(get_desktop_window())) return get_desktop_window();
198 else return get_hwnd_message_parent();
201 if (win != WND_OTHER_PROCESS)
203 hwnd = win->obj.handle;
204 release_win_ptr( win );
206 else /* may belong to another process */
208 SERVER_START_REQ( get_window_info )
210 req->handle = wine_server_user_handle( hwnd );
211 if (!wine_server_call_err( req )) hwnd = wine_server_ptr_handle( reply->full_handle );
213 SERVER_END_REQ;
215 return hwnd;
218 /*******************************************************************
219 * is_desktop_window
221 * Check if window is the desktop or the HWND_MESSAGE top parent.
223 BOOL is_desktop_window( HWND hwnd )
225 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
227 if (!hwnd) return FALSE;
228 if (hwnd == UlongToHandle( thread_info->top_window )) return TRUE;
229 if (hwnd == UlongToHandle( thread_info->msg_window )) return TRUE;
231 if (!HIWORD(hwnd) || HIWORD(hwnd) == 0xffff)
233 if (LOWORD(thread_info->top_window) == LOWORD(hwnd)) return TRUE;
234 if (LOWORD(thread_info->msg_window) == LOWORD(hwnd)) return TRUE;
236 return FALSE;
239 /***********************************************************************
240 * win_get_ptr
242 * Return a pointer to the WND structure if local to the process,
243 * or WND_OTHER_PROCESS if handle may be valid in other process.
244 * If ret value is a valid pointer, it must be released with WIN_ReleasePtr.
246 WND *get_win_ptr( HWND hwnd )
248 WND *win;
250 if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) == WND_OTHER_PROCESS)
252 if (is_desktop_window( hwnd )) win = WND_DESKTOP;
254 return win;
257 /***********************************************************************
258 * is_current_thread_window
260 * Check whether a given window belongs to the current process (and return the full handle).
262 HWND is_current_thread_window( HWND hwnd )
264 WND *win;
265 HWND ret = 0;
267 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
268 return 0;
269 if (win->tid == GetCurrentThreadId()) ret = win->obj.handle;
270 release_win_ptr( win );
271 return ret;
274 /***********************************************************************
275 * is_current_process_window
277 * Check whether a given window belongs to the current process (and return the full handle).
279 HWND is_current_process_window( HWND hwnd )
281 WND *ptr;
282 HWND ret;
284 if (!(ptr = get_win_ptr( hwnd )) || ptr == WND_OTHER_PROCESS || ptr == WND_DESKTOP) return 0;
285 ret = ptr->obj.handle;
286 release_win_ptr( ptr );
287 return ret;
290 /* see IsWindow */
291 BOOL is_window( HWND hwnd )
293 WND *win;
294 BOOL ret;
296 if (!(win = get_win_ptr( hwnd ))) return FALSE;
297 if (win == WND_DESKTOP) return TRUE;
299 if (win != WND_OTHER_PROCESS)
301 release_win_ptr( win );
302 return TRUE;
305 /* check other processes */
306 SERVER_START_REQ( get_window_info )
308 req->handle = wine_server_user_handle( hwnd );
309 ret = !wine_server_call_err( req );
311 SERVER_END_REQ;
312 return ret;
315 /* see GetWindowThreadProcessId */
316 DWORD get_window_thread( HWND hwnd, DWORD *process )
318 WND *ptr;
319 DWORD tid = 0;
321 if (!(ptr = get_win_ptr( hwnd )))
323 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE);
324 return 0;
327 if (ptr != WND_OTHER_PROCESS && ptr != WND_DESKTOP)
329 /* got a valid window */
330 tid = ptr->tid;
331 if (process) *process = GetCurrentProcessId();
332 release_win_ptr( ptr );
333 return tid;
336 /* check other processes */
337 SERVER_START_REQ( get_window_info )
339 req->handle = wine_server_user_handle( hwnd );
340 if (!wine_server_call_err( req ))
342 tid = (DWORD)reply->tid;
343 if (process) *process = (DWORD)reply->pid;
346 SERVER_END_REQ;
347 return tid;
350 /* see GetParent */
351 HWND get_parent( HWND hwnd )
353 HWND retval = 0;
354 WND *win;
356 if (!(win = get_win_ptr( hwnd )))
358 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
359 return 0;
361 if (win == WND_DESKTOP) return 0;
362 if (win == WND_OTHER_PROCESS)
364 LONG style = get_window_long( hwnd, GWL_STYLE );
365 if (style & (WS_POPUP | WS_CHILD))
367 SERVER_START_REQ( get_window_tree )
369 req->handle = wine_server_user_handle( hwnd );
370 if (!wine_server_call_err( req ))
372 if (style & WS_POPUP) retval = wine_server_ptr_handle( reply->owner );
373 else if (style & WS_CHILD) retval = wine_server_ptr_handle( reply->parent );
376 SERVER_END_REQ;
379 else
381 if (win->dwStyle & WS_POPUP) retval = win->owner;
382 else if (win->dwStyle & WS_CHILD) retval = win->parent;
383 release_win_ptr( win );
385 return retval;
388 /*****************************************************************
389 * NtUserSetParent (win32u.@)
391 HWND WINAPI NtUserSetParent( HWND hwnd, HWND parent )
393 RECT window_rect, old_screen_rect, new_screen_rect;
394 DPI_AWARENESS_CONTEXT context;
395 WINDOWPOS winpos;
396 HWND full_handle;
397 HWND old_parent = 0;
398 BOOL was_visible;
399 WND *win;
400 BOOL ret;
402 TRACE("(%p %p)\n", hwnd, parent);
404 if (is_broadcast(hwnd) || is_broadcast(parent))
406 RtlSetLastWin32Error(ERROR_INVALID_PARAMETER);
407 return 0;
410 if (!parent) parent = get_desktop_window();
411 else if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent();
412 else parent = get_full_window_handle( parent );
414 if (!is_window( parent ))
416 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
417 return 0;
420 /* Some applications try to set a child as a parent */
421 if (is_child( hwnd, parent ))
423 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
424 return 0;
427 if (!(full_handle = is_current_thread_window( hwnd )))
428 return UlongToHandle( send_message( hwnd, WM_WINE_SETPARENT, (WPARAM)parent, 0 ));
430 if (full_handle == parent)
432 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
433 return 0;
436 /* Windows hides the window first, then shows it again
437 * including the WM_SHOWWINDOW messages and all */
438 was_visible = NtUserShowWindow( hwnd, SW_HIDE );
440 win = get_win_ptr( hwnd );
441 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
443 get_window_rects( hwnd, COORDS_PARENT, &window_rect, NULL, get_dpi_for_window(hwnd) );
444 get_window_rects( hwnd, COORDS_SCREEN, &old_screen_rect, NULL, 0 );
446 SERVER_START_REQ( set_parent )
448 req->handle = wine_server_user_handle( hwnd );
449 req->parent = wine_server_user_handle( parent );
450 if ((ret = !wine_server_call_err( req )))
452 old_parent = wine_server_ptr_handle( reply->old_parent );
453 win->parent = parent = wine_server_ptr_handle( reply->full_parent );
454 win->dpi = reply->dpi;
455 win->dpi_awareness = reply->awareness;
459 SERVER_END_REQ;
460 release_win_ptr( win );
461 if (!ret) return 0;
463 get_window_rects( hwnd, COORDS_SCREEN, &new_screen_rect, NULL, 0 );
464 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
466 user_driver->pSetParent( full_handle, parent, old_parent );
468 winpos.hwnd = hwnd;
469 winpos.hwndInsertAfter = HWND_TOP;
470 winpos.x = window_rect.left;
471 winpos.y = window_rect.top;
472 winpos.cx = 0;
473 winpos.cy = 0;
474 winpos.flags = SWP_NOSIZE;
476 set_window_pos( &winpos, new_screen_rect.left - old_screen_rect.left,
477 new_screen_rect.top - old_screen_rect.top );
479 if (was_visible) NtUserShowWindow( hwnd, SW_SHOW );
481 SetThreadDpiAwarenessContext( context );
482 return old_parent;
485 /* see GetWindow */
486 HWND get_window_relative( HWND hwnd, UINT rel )
488 HWND retval = 0;
490 if (rel == GW_OWNER) /* this one may be available locally */
492 WND *win = get_win_ptr( hwnd );
493 if (!win)
495 RtlSetLastWin32Error( ERROR_INVALID_HANDLE );
496 return 0;
498 if (win == WND_DESKTOP) return 0;
499 if (win != WND_OTHER_PROCESS)
501 retval = win->owner;
502 release_win_ptr( win );
503 return retval;
505 /* else fall through to server call */
508 SERVER_START_REQ( get_window_tree )
510 req->handle = wine_server_user_handle( hwnd );
511 if (!wine_server_call_err( req ))
513 switch(rel)
515 case GW_HWNDFIRST:
516 retval = wine_server_ptr_handle( reply->first_sibling );
517 break;
518 case GW_HWNDLAST:
519 retval = wine_server_ptr_handle( reply->last_sibling );
520 break;
521 case GW_HWNDNEXT:
522 retval = wine_server_ptr_handle( reply->next_sibling );
523 break;
524 case GW_HWNDPREV:
525 retval = wine_server_ptr_handle( reply->prev_sibling );
526 break;
527 case GW_OWNER:
528 retval = wine_server_ptr_handle( reply->owner );
529 break;
530 case GW_CHILD:
531 retval = wine_server_ptr_handle( reply->first_child );
532 break;
536 SERVER_END_REQ;
537 return retval;
540 /*******************************************************************
541 * list_window_parents
543 * Build an array of all parents of a given window, starting with
544 * the immediate parent. The array must be freed with free().
546 static HWND *list_window_parents( HWND hwnd )
548 WND *win;
549 HWND current, *list;
550 int i, pos = 0, size = 16, count;
552 if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
554 current = hwnd;
555 for (;;)
557 if (!(win = get_win_ptr( current ))) goto empty;
558 if (win == WND_OTHER_PROCESS) break; /* need to do it the hard way */
559 if (win == WND_DESKTOP)
561 if (!pos) goto empty;
562 list[pos] = 0;
563 return list;
565 list[pos] = current = win->parent;
566 release_win_ptr( win );
567 if (!current) return list;
568 if (++pos == size - 1)
570 /* need to grow the list */
571 HWND *new_list = realloc( list, (size + 16) * sizeof(HWND) );
572 if (!new_list) goto empty;
573 list = new_list;
574 size += 16;
578 /* at least one parent belongs to another process, have to query the server */
580 for (;;)
582 count = 0;
583 SERVER_START_REQ( get_window_parents )
585 req->handle = wine_server_user_handle( hwnd );
586 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
587 if (!wine_server_call( req )) count = reply->count;
589 SERVER_END_REQ;
590 if (!count) goto empty;
591 if (size > count)
593 /* start from the end since HWND is potentially larger than user_handle_t */
594 for (i = count - 1; i >= 0; i--)
595 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
596 list[count] = 0;
597 return list;
599 free( list );
600 size = count + 1;
601 if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
604 empty:
605 free( list );
606 return NULL;
609 /*******************************************************************
610 * list_window_children
612 * Build an array of the children of a given window. The array must be
613 * freed with HeapFree. Returns NULL when no windows are found.
615 HWND *list_window_children( HDESK desktop, HWND hwnd, UNICODE_STRING *class, DWORD tid )
617 HWND *list;
618 int i, size = 128;
619 ATOM atom = class ? get_int_atom_value( class ) : 0;
621 /* empty class is not the same as NULL class */
622 if (!atom && class && !class->Length) return NULL;
624 for (;;)
626 int count = 0;
628 if (!(list = malloc( size * sizeof(HWND) ))) break;
630 SERVER_START_REQ( get_window_children )
632 req->desktop = wine_server_obj_handle( desktop );
633 req->parent = wine_server_user_handle( hwnd );
634 req->tid = tid;
635 req->atom = atom;
636 if (!atom && class) wine_server_add_data( req, class->Buffer, class->Length );
637 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
638 if (!wine_server_call( req )) count = reply->count;
640 SERVER_END_REQ;
641 if (count && count < size)
643 /* start from the end since HWND is potentially larger than user_handle_t */
644 for (i = count - 1; i >= 0; i--)
645 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
646 list[count] = 0;
647 return list;
649 free( list );
650 if (!count) break;
651 size = count + 1; /* restart with a large enough buffer */
653 return NULL;
656 /*****************************************************************
657 * NtUserGetAncestor (win32u.@)
659 HWND WINAPI NtUserGetAncestor( HWND hwnd, UINT type )
661 HWND *list, ret = 0;
662 WND *win;
664 switch(type)
666 case GA_PARENT:
667 if (!(win = get_win_ptr( hwnd )))
669 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
670 return 0;
672 if (win == WND_DESKTOP) return 0;
673 if (win != WND_OTHER_PROCESS)
675 ret = win->parent;
676 release_win_ptr( win );
678 else /* need to query the server */
680 SERVER_START_REQ( get_window_tree )
682 req->handle = wine_server_user_handle( hwnd );
683 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->parent );
685 SERVER_END_REQ;
687 break;
689 case GA_ROOT:
690 if (!(list = list_window_parents( hwnd ))) return 0;
692 if (!list[0] || !list[1]) ret = get_full_window_handle( hwnd ); /* top-level window */
693 else
695 int count = 2;
696 while (list[count]) count++;
697 ret = list[count - 2]; /* get the one before the desktop */
699 free( list );
700 break;
702 case GA_ROOTOWNER:
703 if (is_desktop_window( hwnd )) return 0;
704 ret = get_full_window_handle( hwnd );
705 for (;;)
707 HWND parent = get_parent( ret );
708 if (!parent) break;
709 ret = parent;
711 break;
713 return ret;
716 /* see IsChild */
717 BOOL is_child( HWND parent, HWND child )
719 HWND *list;
720 int i;
721 BOOL ret = FALSE;
723 if (!(get_window_long( child, GWL_STYLE ) & WS_CHILD)) return FALSE;
724 if (!(list = list_window_parents( child ))) return FALSE;
725 parent = get_full_window_handle( parent );
726 for (i = 0; list[i]; i++)
728 if (list[i] == parent)
730 ret = list[i] && list[i+1];
731 break;
733 if (!(get_window_long( list[i], GWL_STYLE ) & WS_CHILD)) break;
735 free( list );
736 return ret;
739 /* see IsWindowVisible */
740 BOOL is_window_visible( HWND hwnd )
742 HWND *list;
743 BOOL retval = TRUE;
744 int i;
746 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)) return FALSE;
747 if (!(list = list_window_parents( hwnd ))) return TRUE;
748 if (list[0])
750 for (i = 0; list[i+1]; i++)
751 if (!(get_window_long( list[i], GWL_STYLE ) & WS_VISIBLE)) break;
752 retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
754 free( list );
755 return retval;
758 /***********************************************************************
759 * is_window_drawable
761 * hwnd is drawable when it is visible, all parents are not
762 * minimized, and it is itself not minimized unless we are
763 * trying to draw its default class icon.
765 BOOL is_window_drawable( HWND hwnd, BOOL icon )
767 HWND *list;
768 BOOL retval = TRUE;
769 int i;
770 LONG style = get_window_long( hwnd, GWL_STYLE );
772 if (!(style & WS_VISIBLE)) return FALSE;
773 if ((style & WS_MINIMIZE) && icon && get_class_long_ptr( hwnd, GCLP_HICON, FALSE )) return FALSE;
775 if (!(list = list_window_parents( hwnd ))) return TRUE;
776 if (list[0])
778 for (i = 0; list[i+1]; i++)
779 if ((get_window_long( list[i], GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != WS_VISIBLE)
780 break;
781 retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
783 free( list );
784 return retval;
787 /* see IsWindowUnicode */
788 BOOL is_window_unicode( HWND hwnd )
790 WND *win;
791 BOOL ret = FALSE;
793 if (!(win = get_win_ptr(hwnd))) return FALSE;
795 if (win == WND_DESKTOP) return TRUE;
797 if (win != WND_OTHER_PROCESS)
799 ret = (win->flags & WIN_ISUNICODE) != 0;
800 release_win_ptr( win );
802 else
804 SERVER_START_REQ( get_window_info )
806 req->handle = wine_server_user_handle( hwnd );
807 if (!wine_server_call_err( req )) ret = reply->is_unicode;
809 SERVER_END_REQ;
811 return ret;
814 /* see EnableWindow */
815 BOOL enable_window( HWND hwnd, BOOL enable )
817 BOOL ret;
819 if (is_broadcast(hwnd))
821 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
822 return FALSE;
825 TRACE( "( %p, %d )\n", hwnd, enable );
827 if (enable)
829 ret = (set_window_style( hwnd, 0, WS_DISABLED ) & WS_DISABLED) != 0;
830 if (ret) send_message( hwnd, WM_ENABLE, TRUE, 0 );
832 else
834 send_message( hwnd, WM_CANCELMODE, 0, 0 );
836 ret = (set_window_style( hwnd, WS_DISABLED, 0 ) & WS_DISABLED) != 0;
837 if (!ret)
839 if (hwnd == get_focus())
840 NtUserSetFocus( 0 ); /* A disabled window can't have the focus */
842 send_message( hwnd, WM_ENABLE, FALSE, 0 );
845 return ret;
848 /* see IsWindowEnabled */
849 BOOL is_window_enabled( HWND hwnd )
851 LONG ret;
853 RtlSetLastWin32Error( NO_ERROR );
854 ret = get_window_long( hwnd, GWL_STYLE );
855 if (!ret && RtlGetLastWin32Error() != NO_ERROR) return FALSE;
856 return !(ret & WS_DISABLED);
859 /* see GetWindowDpiAwarenessContext */
860 DPI_AWARENESS_CONTEXT get_window_dpi_awareness_context( HWND hwnd )
862 DPI_AWARENESS_CONTEXT ret = 0;
863 WND *win;
865 if (!(win = get_win_ptr( hwnd )))
867 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
868 return 0;
870 if (win == WND_DESKTOP) return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
871 if (win != WND_OTHER_PROCESS)
873 ret = ULongToHandle( win->dpi_awareness | 0x10 );
874 release_win_ptr( win );
876 else
878 SERVER_START_REQ( get_window_info )
880 req->handle = wine_server_user_handle( hwnd );
881 if (!wine_server_call_err( req )) ret = ULongToHandle( reply->awareness | 0x10 );
883 SERVER_END_REQ;
885 return ret;
888 /* see GetDpiForWindow */
889 UINT get_dpi_for_window( HWND hwnd )
891 WND *win;
892 UINT ret = 0;
894 if (!(win = get_win_ptr( hwnd )))
896 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
897 return 0;
899 if (win == WND_DESKTOP)
901 POINT pt = { 0, 0 };
902 return get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTOPRIMARY, 0 ));
904 if (win != WND_OTHER_PROCESS)
906 ret = win->dpi;
907 if (!ret) ret = get_win_monitor_dpi( hwnd );
908 release_win_ptr( win );
910 else
912 SERVER_START_REQ( get_window_info )
914 req->handle = wine_server_user_handle( hwnd );
915 if (!wine_server_call_err( req )) ret = reply->dpi;
917 SERVER_END_REQ;
919 return ret;
922 static LONG_PTR get_win_data( const void *ptr, UINT size )
924 if (size == sizeof(WORD))
926 WORD ret;
927 memcpy( &ret, ptr, sizeof(ret) );
928 return ret;
930 else if (size == sizeof(DWORD))
932 DWORD ret;
933 memcpy( &ret, ptr, sizeof(ret) );
934 return ret;
936 else
938 LONG_PTR ret;
939 memcpy( &ret, ptr, sizeof(ret) );
940 return ret;
944 /* helper for set_window_long */
945 static inline void set_win_data( void *ptr, LONG_PTR val, UINT size )
947 if (size == sizeof(WORD))
949 WORD newval = val;
950 memcpy( ptr, &newval, sizeof(newval) );
952 else if (size == sizeof(DWORD))
954 DWORD newval = val;
955 memcpy( ptr, &newval, sizeof(newval) );
957 else
959 memcpy( ptr, &val, sizeof(val) );
963 BOOL is_iconic( HWND hwnd )
965 return (get_window_long( hwnd, GWL_STYLE ) & WS_MINIMIZE) != 0;
968 BOOL is_zoomed( HWND hwnd )
970 return (get_window_long( hwnd, GWL_STYLE ) & WS_MAXIMIZE) != 0;
973 static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi )
975 LONG_PTR retval = 0;
976 WND *win;
978 if (offset == GWLP_HWNDPARENT)
980 HWND parent = NtUserGetAncestor( hwnd, GA_PARENT );
981 if (parent == get_desktop_window())
982 parent = get_window_relative( hwnd, GW_OWNER );
983 return (ULONG_PTR)parent;
986 if (!(win = get_win_ptr( hwnd )))
988 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
989 return 0;
992 if (win == WND_DESKTOP)
994 switch (offset)
996 case GWL_STYLE:
997 retval = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; /* message parent is not visible */
998 if (get_full_window_handle( hwnd ) == get_desktop_window())
999 retval |= WS_VISIBLE;
1000 return retval;
1001 case GWL_EXSTYLE:
1002 case GWLP_USERDATA:
1003 case GWLP_ID:
1004 case GWLP_HINSTANCE:
1005 return 0;
1006 case GWLP_WNDPROC:
1007 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
1008 return 0;
1010 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1011 return 0;
1014 if (win == WND_OTHER_PROCESS)
1016 if (offset == GWLP_WNDPROC)
1018 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
1019 return 0;
1021 SERVER_START_REQ( set_window_info )
1023 req->handle = wine_server_user_handle( hwnd );
1024 req->flags = 0; /* don't set anything, just retrieve */
1025 req->extra_offset = (offset >= 0) ? offset : -1;
1026 req->extra_size = (offset >= 0) ? size : 0;
1027 if (!wine_server_call_err( req ))
1029 switch(offset)
1031 case GWL_STYLE: retval = reply->old_style; break;
1032 case GWL_EXSTYLE: retval = reply->old_ex_style; break;
1033 case GWLP_ID: retval = reply->old_id; break;
1034 case GWLP_HINSTANCE: retval = (ULONG_PTR)wine_server_get_ptr( reply->old_instance ); break;
1035 case GWLP_USERDATA: retval = reply->old_user_data; break;
1036 default:
1037 if (offset >= 0) retval = get_win_data( &reply->old_extra_value, size );
1038 else RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1039 break;
1043 SERVER_END_REQ;
1044 return retval;
1047 /* now we have a valid win */
1049 if (offset >= 0)
1051 if (offset > (int)(win->cbWndExtra - size))
1053 WARN("Invalid offset %d\n", offset );
1054 release_win_ptr( win );
1055 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1056 return 0;
1058 retval = get_win_data( (char *)win->wExtra + offset, size );
1059 release_win_ptr( win );
1060 return retval;
1063 switch(offset)
1065 case GWLP_USERDATA: retval = win->userdata; break;
1066 case GWL_STYLE: retval = win->dwStyle; break;
1067 case GWL_EXSTYLE: retval = win->dwExStyle; break;
1068 case GWLP_ID: retval = win->wIDmenu; break;
1069 case GWLP_HINSTANCE: retval = (ULONG_PTR)win->hInstance; break;
1070 case GWLP_WNDPROC:
1071 /* This looks like a hack only for the edit control (see tests). This makes these controls
1072 * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests
1073 * that the hack is in GetWindowLongPtr[AW], not in winprocs.
1075 if (win->winproc == BUILTIN_WINPROC(WINPROC_EDIT) && (!!ansi != !(win->flags & WIN_ISUNICODE)))
1076 retval = (ULONG_PTR)win->winproc;
1077 else
1078 retval = (ULONG_PTR)get_winproc( win->winproc, ansi );
1079 break;
1080 default:
1081 WARN("Unknown offset %d\n", offset );
1082 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1083 break;
1085 release_win_ptr( win );
1086 return retval;
1089 /* see GetWindowLongW */
1090 DWORD get_window_long( HWND hwnd, INT offset )
1092 return get_window_long_size( hwnd, offset, sizeof(LONG), FALSE );
1095 /* see GetWindowLongPtr */
1096 ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi )
1098 return get_window_long_size( hwnd, offset, sizeof(LONG_PTR), ansi );
1101 /* see GetWindowWord */
1102 static WORD get_window_word( HWND hwnd, INT offset )
1104 if (offset < 0 && offset != GWLP_USERDATA)
1106 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1107 return 0;
1109 return get_window_long_size( hwnd, offset, sizeof(WORD), TRUE );
1112 /***********************************************************************
1113 * set_window_style
1115 * Change the style of a window.
1117 ULONG set_window_style( HWND hwnd, ULONG set_bits, ULONG clear_bits )
1119 BOOL ok, made_visible = FALSE;
1120 STYLESTRUCT style;
1121 WND *win = get_win_ptr( hwnd );
1123 if (!win || win == WND_DESKTOP) return 0;
1124 if (win == WND_OTHER_PROCESS)
1126 if (is_window(hwnd))
1127 return send_message( hwnd, WM_WINE_SETSTYLE, set_bits, clear_bits );
1128 return 0;
1130 style.styleOld = win->dwStyle;
1131 style.styleNew = (win->dwStyle | set_bits) & ~clear_bits;
1132 if (style.styleNew == style.styleOld)
1134 release_win_ptr( win );
1135 return style.styleNew;
1137 SERVER_START_REQ( set_window_info )
1139 req->handle = wine_server_user_handle( hwnd );
1140 req->flags = SET_WIN_STYLE;
1141 req->style = style.styleNew;
1142 req->extra_offset = -1;
1143 if ((ok = !wine_server_call( req )))
1145 style.styleOld = reply->old_style;
1146 win->dwStyle = style.styleNew;
1149 SERVER_END_REQ;
1151 if (ok && ((style.styleOld ^ style.styleNew) & WS_VISIBLE))
1153 made_visible = (style.styleNew & WS_VISIBLE) != 0;
1154 invalidate_dce( win, NULL );
1156 release_win_ptr( win );
1158 if (!ok) return 0;
1160 user_driver->pSetWindowStyle( hwnd, GWL_STYLE, &style );
1161 if (made_visible) update_window_state( hwnd );
1163 return style.styleOld;
1166 static DWORD fix_exstyle( DWORD style, DWORD exstyle )
1168 if ((exstyle & WS_EX_DLGMODALFRAME) ||
1169 (!(exstyle & WS_EX_STATICEDGE) && (style & (WS_DLGFRAME | WS_THICKFRAME))))
1170 exstyle |= WS_EX_WINDOWEDGE;
1171 else
1172 exstyle &= ~WS_EX_WINDOWEDGE;
1173 return exstyle;
1176 /* Change the owner of a window. */
1177 static HWND set_window_owner( HWND hwnd, HWND owner )
1179 WND *win = get_win_ptr( hwnd );
1180 HWND ret = 0;
1182 if (!win || win == WND_DESKTOP) return 0;
1183 if (win == WND_OTHER_PROCESS)
1185 if (is_window(hwnd)) ERR( "cannot set owner %p on other process window %p\n", owner, hwnd );
1186 return 0;
1188 SERVER_START_REQ( set_window_owner )
1190 req->handle = wine_server_user_handle( hwnd );
1191 req->owner = wine_server_user_handle( owner );
1192 if (!wine_server_call( req ))
1194 win->owner = wine_server_ptr_handle( reply->full_owner );
1195 ret = wine_server_ptr_handle( reply->prev_owner );
1198 SERVER_END_REQ;
1199 release_win_ptr( win );
1200 return ret;
1203 /* Helper function for SetWindowLong(). */
1204 LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOOL ansi )
1206 BOOL ok, made_visible = FALSE;
1207 LONG_PTR retval = 0;
1208 STYLESTRUCT style;
1209 WND *win;
1211 TRACE( "%p %d %lx %c\n", hwnd, offset, newval, ansi ? 'A' : 'W' );
1213 if (is_broadcast(hwnd))
1215 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1216 return FALSE;
1219 if (!(win = get_win_ptr( hwnd )))
1221 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1222 return 0;
1224 if (win == WND_DESKTOP)
1226 /* can't change anything on the desktop window */
1227 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
1228 return 0;
1230 if (win == WND_OTHER_PROCESS)
1232 if (offset == GWLP_WNDPROC)
1234 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
1235 return 0;
1237 if (offset > 32767 || offset < -32767)
1239 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1240 return 0;
1242 return send_message( hwnd, WM_WINE_SETWINDOWLONG, MAKEWPARAM( offset, size ), newval );
1245 /* first some special cases */
1246 switch( offset )
1248 case GWL_STYLE:
1249 style.styleOld = win->dwStyle;
1250 style.styleNew = newval;
1251 release_win_ptr( win );
1252 send_message( hwnd, WM_STYLECHANGING, GWL_STYLE, (LPARAM)&style );
1253 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
1254 newval = style.styleNew;
1255 /* WS_CLIPSIBLINGS can't be reset on top-level windows */
1256 if (win->parent == get_desktop_window()) newval |= WS_CLIPSIBLINGS;
1257 /* WS_MINIMIZE can't be reset */
1258 if (win->dwStyle & WS_MINIMIZE) newval |= WS_MINIMIZE;
1259 break;
1260 case GWL_EXSTYLE:
1261 style.styleOld = win->dwExStyle;
1262 style.styleNew = newval;
1263 release_win_ptr( win );
1264 send_message( hwnd, WM_STYLECHANGING, GWL_EXSTYLE, (LPARAM)&style );
1265 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
1266 /* WS_EX_TOPMOST can only be changed through SetWindowPos */
1267 newval = (style.styleNew & ~WS_EX_TOPMOST) | (win->dwExStyle & WS_EX_TOPMOST);
1268 newval = fix_exstyle(win->dwStyle, newval);
1269 break;
1270 case GWLP_HWNDPARENT:
1271 if (win->parent == get_desktop_window())
1273 release_win_ptr( win );
1274 return (ULONG_PTR)set_window_owner( hwnd, (HWND)newval );
1276 else
1278 release_win_ptr( win );
1279 return (ULONG_PTR)NtUserSetParent( hwnd, (HWND)newval );
1281 case GWLP_WNDPROC:
1283 WNDPROC proc;
1284 UINT old_flags = win->flags;
1285 retval = get_window_long_ptr( hwnd, offset, ansi );
1286 proc = alloc_winproc( (WNDPROC)newval, ansi );
1287 if (proc) win->winproc = proc;
1288 if (is_winproc_unicode( proc, !ansi )) win->flags |= WIN_ISUNICODE;
1289 else win->flags &= ~WIN_ISUNICODE;
1290 if (!((old_flags ^ win->flags) & WIN_ISUNICODE))
1292 release_win_ptr( win );
1293 return retval;
1295 /* update is_unicode flag on the server side */
1296 break;
1298 case GWLP_ID:
1299 case GWLP_HINSTANCE:
1300 case GWLP_USERDATA:
1301 break;
1302 default:
1303 if (offset < 0 || offset > (int)(win->cbWndExtra - size))
1305 WARN("Invalid offset %d\n", offset );
1306 release_win_ptr( win );
1307 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1308 return 0;
1310 else if (get_win_data( (char *)win->wExtra + offset, size ) == newval)
1312 /* already set to the same value */
1313 release_win_ptr( win );
1314 return newval;
1316 break;
1319 SERVER_START_REQ( set_window_info )
1321 req->handle = wine_server_user_handle( hwnd );
1322 req->extra_offset = -1;
1323 switch(offset)
1325 case GWL_STYLE:
1326 req->flags = SET_WIN_STYLE | SET_WIN_EXSTYLE;
1327 req->style = newval;
1328 req->ex_style = fix_exstyle(newval, win->dwExStyle);
1329 break;
1330 case GWL_EXSTYLE:
1331 req->flags = SET_WIN_EXSTYLE;
1332 req->ex_style = newval;
1333 break;
1334 case GWLP_ID:
1335 req->flags = SET_WIN_ID;
1336 req->extra_value = newval;
1337 break;
1338 case GWLP_HINSTANCE:
1339 req->flags = SET_WIN_INSTANCE;
1340 req->instance = wine_server_client_ptr( (void *)newval );
1341 break;
1342 case GWLP_WNDPROC:
1343 req->flags = SET_WIN_UNICODE;
1344 req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
1345 break;
1346 case GWLP_USERDATA:
1347 if (size == sizeof(WORD)) newval = MAKELONG( newval, win->userdata >> 16 );
1348 req->flags = SET_WIN_USERDATA;
1349 req->user_data = newval;
1350 break;
1351 default:
1352 req->flags = SET_WIN_EXTRA;
1353 req->extra_offset = offset;
1354 req->extra_size = size;
1355 set_win_data( &req->extra_value, newval, size );
1357 if ((ok = !wine_server_call_err( req )))
1359 switch(offset)
1361 case GWL_STYLE:
1362 win->dwStyle = newval;
1363 win->dwExStyle = fix_exstyle(win->dwStyle, win->dwExStyle);
1364 retval = reply->old_style;
1365 break;
1366 case GWL_EXSTYLE:
1367 win->dwExStyle = newval;
1368 retval = reply->old_ex_style;
1369 break;
1370 case GWLP_ID:
1371 win->wIDmenu = newval;
1372 retval = reply->old_id;
1373 break;
1374 case GWLP_HINSTANCE:
1375 win->hInstance = (HINSTANCE)newval;
1376 retval = (ULONG_PTR)wine_server_get_ptr( reply->old_instance );
1377 break;
1378 case GWLP_WNDPROC:
1379 break;
1380 case GWLP_USERDATA:
1381 win->userdata = newval;
1382 retval = reply->old_user_data;
1383 break;
1384 default:
1385 retval = get_win_data( (char *)win->wExtra + offset, size );
1386 set_win_data( (char *)win->wExtra + offset, newval, size );
1387 break;
1391 SERVER_END_REQ;
1393 if ((offset == GWL_STYLE && ((style.styleOld ^ style.styleNew) & WS_VISIBLE)) ||
1394 (offset == GWL_EXSTYLE && ((style.styleOld ^ style.styleNew) & WS_EX_LAYERED)))
1396 made_visible = (win->dwStyle & WS_VISIBLE) != 0;
1397 invalidate_dce( win, NULL );
1399 release_win_ptr( win );
1401 if (!ok) return 0;
1403 if (offset == GWL_STYLE || offset == GWL_EXSTYLE)
1405 style.styleOld = retval;
1406 style.styleNew = newval;
1407 user_driver->pSetWindowStyle( hwnd, offset, &style );
1408 if (made_visible) update_window_state( hwnd );
1409 send_message( hwnd, WM_STYLECHANGED, offset, (LPARAM)&style );
1412 return retval;
1415 /**********************************************************************
1416 * NtUserSetWindowWord (win32u.@)
1418 WORD WINAPI NtUserSetWindowWord( HWND hwnd, INT offset, WORD newval )
1420 if (offset < 0 && offset != GWLP_USERDATA)
1422 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1423 return 0;
1425 return set_window_long( hwnd, offset, sizeof(WORD), newval, TRUE );
1428 /**********************************************************************
1429 * NtUserSetWindowLong (win32u.@)
1431 LONG WINAPI NtUserSetWindowLong( HWND hwnd, INT offset, LONG newval, BOOL ansi )
1433 return set_window_long( hwnd, offset, sizeof(LONG), newval, ansi );
1436 /*****************************************************************************
1437 * NtUserSetWindowLongPtr (win32u.@)
1439 LONG_PTR WINAPI NtUserSetWindowLongPtr( HWND hwnd, INT offset, LONG_PTR newval, BOOL ansi )
1441 return set_window_long( hwnd, offset, sizeof(LONG_PTR), newval, ansi );
1444 static BOOL set_window_pixel_format( HWND hwnd, int format )
1446 WND *win = get_win_ptr( hwnd );
1448 if (!win || win == WND_DESKTOP || win == WND_OTHER_PROCESS)
1450 WARN( "setting format %d on win %p not supported\n", format, hwnd );
1451 return FALSE;
1453 win->pixel_format = format;
1454 release_win_ptr( win );
1456 update_window_state( hwnd );
1457 return TRUE;
1460 /***********************************************************************
1461 * NtUserGetProp (win32u.@)
1463 * NOTE Native allows only ATOMs as the second argument. We allow strings
1464 * to save extra server call in GetPropW.
1466 HANDLE WINAPI NtUserGetProp( HWND hwnd, const WCHAR *str )
1468 ULONG_PTR ret = 0;
1470 SERVER_START_REQ( get_window_property )
1472 req->window = wine_server_user_handle( hwnd );
1473 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1474 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1475 if (!wine_server_call_err( req )) ret = reply->data;
1477 SERVER_END_REQ;
1478 return (HANDLE)ret;
1481 /*****************************************************************************
1482 * NtUserSetProp (win32u.@)
1484 * NOTE Native allows only ATOMs as the second argument. We allow strings
1485 * to save extra server call in SetPropW.
1487 BOOL WINAPI NtUserSetProp( HWND hwnd, const WCHAR *str, HANDLE handle )
1489 BOOL ret;
1491 SERVER_START_REQ( set_window_property )
1493 req->window = wine_server_user_handle( hwnd );
1494 req->data = (ULONG_PTR)handle;
1495 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1496 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1497 ret = !wine_server_call_err( req );
1499 SERVER_END_REQ;
1500 return ret;
1504 /***********************************************************************
1505 * NtUserRemoveProp (win32u.@)
1507 * NOTE Native allows only ATOMs as the second argument. We allow strings
1508 * to save extra server call in RemovePropW.
1510 HANDLE WINAPI NtUserRemoveProp( HWND hwnd, const WCHAR *str )
1512 ULONG_PTR ret = 0;
1514 SERVER_START_REQ( remove_window_property )
1516 req->window = wine_server_user_handle( hwnd );
1517 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1518 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1519 if (!wine_server_call_err( req )) ret = reply->data;
1521 SERVER_END_REQ;
1523 return (HANDLE)ret;
1526 static void mirror_rect( const RECT *window_rect, RECT *rect )
1528 int width = window_rect->right - window_rect->left;
1529 int tmp = rect->left;
1530 rect->left = width - rect->right;
1531 rect->right = width - tmp;
1534 /***********************************************************************
1535 * get_window_rects
1537 * Get the window and client rectangles.
1539 BOOL get_window_rects( HWND hwnd, enum coords_relative relative, RECT *window_rect,
1540 RECT *client_rect, UINT dpi )
1542 WND *win = get_win_ptr( hwnd );
1543 BOOL ret = TRUE;
1545 if (!win)
1547 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1548 return FALSE;
1550 if (win == WND_DESKTOP)
1552 RECT rect;
1553 rect.left = rect.top = 0;
1554 if (hwnd == get_hwnd_message_parent())
1556 rect.right = 100;
1557 rect.bottom = 100;
1558 rect = map_dpi_rect( rect, get_dpi_for_window( hwnd ), dpi );
1560 else
1562 rect = get_primary_monitor_rect( dpi );
1564 if (window_rect) *window_rect = rect;
1565 if (client_rect) *client_rect = rect;
1566 return TRUE;
1568 if (win != WND_OTHER_PROCESS)
1570 UINT window_dpi = get_dpi_for_window( hwnd );
1571 RECT window = win->window_rect;
1572 RECT client = win->client_rect;
1574 switch (relative)
1576 case COORDS_CLIENT:
1577 OffsetRect( &window, -win->client_rect.left, -win->client_rect.top );
1578 OffsetRect( &client, -win->client_rect.left, -win->client_rect.top );
1579 if (win->dwExStyle & WS_EX_LAYOUTRTL)
1580 mirror_rect( &win->client_rect, &window );
1581 break;
1582 case COORDS_WINDOW:
1583 OffsetRect( &window, -win->window_rect.left, -win->window_rect.top );
1584 OffsetRect( &client, -win->window_rect.left, -win->window_rect.top );
1585 if (win->dwExStyle & WS_EX_LAYOUTRTL)
1586 mirror_rect( &win->window_rect, &client );
1587 break;
1588 case COORDS_PARENT:
1589 if (win->parent)
1591 WND *parent = get_win_ptr( win->parent );
1592 if (parent == WND_DESKTOP) break;
1593 if (!parent || parent == WND_OTHER_PROCESS)
1595 release_win_ptr( win );
1596 goto other_process;
1598 if (parent->flags & WIN_CHILDREN_MOVED)
1600 release_win_ptr( parent );
1601 release_win_ptr( win );
1602 goto other_process;
1604 if (parent->dwExStyle & WS_EX_LAYOUTRTL)
1606 mirror_rect( &parent->client_rect, &window );
1607 mirror_rect( &parent->client_rect, &client );
1609 release_win_ptr( parent );
1611 break;
1612 case COORDS_SCREEN:
1613 while (win->parent)
1615 WND *parent = get_win_ptr( win->parent );
1616 if (parent == WND_DESKTOP) break;
1617 if (!parent || parent == WND_OTHER_PROCESS)
1619 release_win_ptr( win );
1620 goto other_process;
1622 release_win_ptr( win );
1623 if (parent->flags & WIN_CHILDREN_MOVED)
1625 release_win_ptr( parent );
1626 goto other_process;
1628 win = parent;
1629 if (win->parent)
1631 OffsetRect( &window, win->client_rect.left, win->client_rect.top );
1632 OffsetRect( &client, win->client_rect.left, win->client_rect.top );
1635 break;
1637 if (window_rect) *window_rect = map_dpi_rect( window, window_dpi, dpi );
1638 if (client_rect) *client_rect = map_dpi_rect( client, window_dpi, dpi );
1639 release_win_ptr( win );
1640 return TRUE;
1643 other_process:
1644 SERVER_START_REQ( get_window_rectangles )
1646 req->handle = wine_server_user_handle( hwnd );
1647 req->relative = relative;
1648 req->dpi = dpi;
1649 if ((ret = !wine_server_call_err( req )))
1651 if (window_rect)
1653 window_rect->left = reply->window.left;
1654 window_rect->top = reply->window.top;
1655 window_rect->right = reply->window.right;
1656 window_rect->bottom = reply->window.bottom;
1658 if (client_rect)
1660 client_rect->left = reply->client.left;
1661 client_rect->top = reply->client.top;
1662 client_rect->right = reply->client.right;
1663 client_rect->bottom = reply->client.bottom;
1667 SERVER_END_REQ;
1668 return ret;
1671 /* see GetWindowRect */
1672 BOOL get_window_rect( HWND hwnd, RECT *rect, UINT dpi )
1674 return get_window_rects( hwnd, COORDS_SCREEN, rect, NULL, dpi );
1677 /* see GetClientRect */
1678 BOOL get_client_rect( HWND hwnd, RECT *rect )
1680 return get_window_rects( hwnd, COORDS_CLIENT, NULL, rect, get_thread_dpi() );
1683 /* see GetWindowInfo */
1684 static BOOL get_window_info( HWND hwnd, WINDOWINFO *info )
1687 if (!info || !get_window_rects( hwnd, COORDS_SCREEN, &info->rcWindow,
1688 &info->rcClient, get_thread_dpi() ))
1689 return FALSE;
1691 info->dwStyle = get_window_long( hwnd, GWL_STYLE );
1692 info->dwExStyle = get_window_long( hwnd, GWL_EXSTYLE );
1693 info->dwWindowStatus = get_active_window() == hwnd ? WS_ACTIVECAPTION : 0;
1694 info->cxWindowBorders = info->rcClient.left - info->rcWindow.left;
1695 info->cyWindowBorders = info->rcWindow.bottom - info->rcClient.bottom;
1696 info->atomWindowType = get_class_long( hwnd, GCW_ATOM, FALSE );
1697 info->wCreatorVersion = 0x0400;
1698 return TRUE;
1701 /***********************************************************************
1702 * update_surface_region
1704 static void update_surface_region( HWND hwnd )
1706 NTSTATUS status;
1707 HRGN region = 0;
1708 RGNDATA *data;
1709 size_t size = 256;
1710 WND *win = get_win_ptr( hwnd );
1712 if (!win || win == WND_DESKTOP || win == WND_OTHER_PROCESS) return;
1713 if (!win->surface) goto done;
1717 if (!(data = malloc( FIELD_OFFSET( RGNDATA, Buffer[size] )))) goto done;
1719 SERVER_START_REQ( get_surface_region )
1721 req->window = wine_server_user_handle( hwnd );
1722 wine_server_set_reply( req, data->Buffer, size );
1723 if (!(status = wine_server_call( req )))
1725 size_t reply_size = wine_server_reply_size( reply );
1726 if (reply_size)
1728 data->rdh.dwSize = sizeof(data->rdh);
1729 data->rdh.iType = RDH_RECTANGLES;
1730 data->rdh.nCount = reply_size / sizeof(RECT);
1731 data->rdh.nRgnSize = reply_size;
1732 region = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1733 NtGdiOffsetRgn( region, -reply->visible_rect.left, -reply->visible_rect.top );
1736 else size = reply->total_size;
1738 SERVER_END_REQ;
1739 free( data );
1740 } while (status == STATUS_BUFFER_OVERFLOW);
1742 if (status) goto done;
1744 win->surface->funcs->set_region( win->surface, region );
1745 if (region) NtGdiDeleteObjectApp( region );
1747 done:
1748 release_win_ptr( win );
1751 /***********************************************************************
1752 * apply_window_pos
1754 * Backend implementation of SetWindowPos.
1756 static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags,
1757 const RECT *window_rect, const RECT *client_rect, const RECT *valid_rects )
1759 WND *win;
1760 HWND surface_win = 0, parent = NtUserGetAncestor( hwnd, GA_PARENT );
1761 BOOL ret, needs_update = FALSE;
1762 RECT visible_rect, old_visible_rect, old_window_rect, old_client_rect, extra_rects[3];
1763 struct window_surface *old_surface, *new_surface = NULL;
1765 if (!parent || parent == get_desktop_window())
1767 new_surface = &dummy_surface; /* provide a default surface for top-level windows */
1768 window_surface_add_ref( new_surface );
1770 visible_rect = *window_rect;
1771 if (!(ret = user_driver->pWindowPosChanging( hwnd, insert_after, swp_flags,
1772 window_rect, client_rect, &visible_rect, &new_surface )))
1774 if (IsRectEmpty( window_rect )) visible_rect = *window_rect;
1775 else
1777 visible_rect = get_virtual_screen_rect( get_thread_dpi() );
1778 intersect_rect( &visible_rect, &visible_rect, window_rect );
1782 get_window_rects( hwnd, COORDS_SCREEN, &old_window_rect, NULL, get_thread_dpi() );
1783 if (IsRectEmpty( &valid_rects[0] )) valid_rects = NULL;
1785 if (!(win = get_win_ptr( hwnd )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS)
1787 if (new_surface) window_surface_release( new_surface );
1788 return FALSE;
1791 /* create or update window surface for top-level windows if the driver doesn't implement WindowPosChanging */
1792 if (!ret && new_surface && !IsRectEmpty( &visible_rect ) &&
1793 (!(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) ||
1794 NtUserGetLayeredWindowAttributes( hwnd, NULL, NULL, NULL )))
1796 window_surface_release( new_surface );
1797 if ((new_surface = win->surface)) window_surface_add_ref( new_surface );
1798 create_offscreen_window_surface( &visible_rect, &new_surface );
1801 old_visible_rect = win->visible_rect;
1802 old_client_rect = win->client_rect;
1803 old_surface = win->surface;
1804 if (old_surface != new_surface) swp_flags |= SWP_FRAMECHANGED; /* force refreshing non-client area */
1805 if (new_surface == &dummy_surface) swp_flags |= SWP_NOREDRAW;
1806 else if (old_surface == &dummy_surface)
1808 swp_flags |= SWP_NOCOPYBITS;
1809 valid_rects = NULL;
1812 SERVER_START_REQ( set_window_pos )
1814 req->handle = wine_server_user_handle( hwnd );
1815 req->previous = wine_server_user_handle( insert_after );
1816 req->swp_flags = swp_flags;
1817 req->window.left = window_rect->left;
1818 req->window.top = window_rect->top;
1819 req->window.right = window_rect->right;
1820 req->window.bottom = window_rect->bottom;
1821 req->client.left = client_rect->left;
1822 req->client.top = client_rect->top;
1823 req->client.right = client_rect->right;
1824 req->client.bottom = client_rect->bottom;
1825 if (!EqualRect( window_rect, &visible_rect ) || new_surface || valid_rects)
1827 extra_rects[0] = extra_rects[1] = visible_rect;
1828 if (new_surface)
1830 extra_rects[1] = new_surface->rect;
1831 OffsetRect( &extra_rects[1], visible_rect.left, visible_rect.top );
1833 if (valid_rects) extra_rects[2] = valid_rects[0];
1834 else SetRectEmpty( &extra_rects[2] );
1835 wine_server_add_data( req, extra_rects, sizeof(extra_rects) );
1837 if (new_surface) req->paint_flags |= SET_WINPOS_PAINT_SURFACE;
1838 if (win->pixel_format) req->paint_flags |= SET_WINPOS_PIXEL_FORMAT;
1840 if ((ret = !wine_server_call( req )))
1842 win->dwStyle = reply->new_style;
1843 win->dwExStyle = reply->new_ex_style;
1844 win->window_rect = *window_rect;
1845 win->client_rect = *client_rect;
1846 win->visible_rect = visible_rect;
1847 win->surface = new_surface;
1848 surface_win = wine_server_ptr_handle( reply->surface_win );
1849 needs_update = reply->needs_update;
1850 if (get_window_long( win->parent, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
1852 RECT client;
1853 get_window_rects( win->parent, COORDS_CLIENT, NULL, &client, get_thread_dpi() );
1854 mirror_rect( &client, &win->window_rect );
1855 mirror_rect( &client, &win->client_rect );
1856 mirror_rect( &client, &win->visible_rect );
1858 /* if an RTL window is resized the children have moved */
1859 if (win->dwExStyle & WS_EX_LAYOUTRTL &&
1860 client_rect->right - client_rect->left != old_client_rect.right - old_client_rect.left)
1861 win->flags |= WIN_CHILDREN_MOVED;
1864 SERVER_END_REQ;
1866 if (ret)
1868 if (needs_update) update_surface_region( surface_win );
1869 if (((swp_flags & SWP_AGG_NOPOSCHANGE) != SWP_AGG_NOPOSCHANGE) ||
1870 (swp_flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_STATECHANGED | SWP_FRAMECHANGED)))
1871 invalidate_dce( win, &old_window_rect );
1874 release_win_ptr( win );
1876 if (ret)
1878 TRACE( "win %p surface %p -> %p\n", hwnd, old_surface, new_surface );
1879 register_window_surface( old_surface, new_surface );
1880 if (old_surface)
1882 if (valid_rects)
1884 move_window_bits( hwnd, old_surface, new_surface, &visible_rect,
1885 &old_visible_rect, window_rect, valid_rects );
1886 valid_rects = NULL; /* prevent the driver from trying to also move the bits */
1888 window_surface_release( old_surface );
1890 else if (surface_win && surface_win != hwnd)
1892 if (valid_rects)
1894 RECT rects[2];
1895 int x_offset = old_visible_rect.left - visible_rect.left;
1896 int y_offset = old_visible_rect.top - visible_rect.top;
1898 /* if all that happened is that the whole window moved, copy everything */
1899 if (!(swp_flags & SWP_FRAMECHANGED) &&
1900 old_visible_rect.right - visible_rect.right == x_offset &&
1901 old_visible_rect.bottom - visible_rect.bottom == y_offset &&
1902 old_client_rect.left - client_rect->left == x_offset &&
1903 old_client_rect.right - client_rect->right == x_offset &&
1904 old_client_rect.top - client_rect->top == y_offset &&
1905 old_client_rect.bottom - client_rect->bottom == y_offset &&
1906 EqualRect( &valid_rects[0], client_rect ))
1908 rects[0] = visible_rect;
1909 rects[1] = old_visible_rect;
1910 valid_rects = rects;
1912 move_window_bits_parent( hwnd, surface_win, window_rect, valid_rects );
1913 valid_rects = NULL; /* prevent the driver from trying to also move the bits */
1917 user_driver->pWindowPosChanged( hwnd, insert_after, swp_flags, window_rect,
1918 client_rect, &visible_rect, valid_rects, new_surface );
1920 else if (new_surface) window_surface_release( new_surface );
1922 return ret;
1925 /*******************************************************************
1926 * NtUserGetWindowRgnEx (win32u.@)
1928 int WINAPI NtUserGetWindowRgnEx( HWND hwnd, HRGN hrgn, UINT unk )
1930 NTSTATUS status;
1931 HRGN win_rgn = 0;
1932 RGNDATA *data;
1933 size_t size = 256;
1934 int ret = ERROR;
1938 if (!(data = malloc( sizeof(*data) + size - 1 )))
1940 RtlSetLastWin32Error( ERROR_OUTOFMEMORY );
1941 return ERROR;
1943 SERVER_START_REQ( get_window_region )
1945 req->window = wine_server_user_handle( hwnd );
1946 wine_server_set_reply( req, data->Buffer, size );
1947 if (!(status = wine_server_call( req )))
1949 size_t reply_size = wine_server_reply_size( reply );
1950 if (reply_size)
1952 data->rdh.dwSize = sizeof(data->rdh);
1953 data->rdh.iType = RDH_RECTANGLES;
1954 data->rdh.nCount = reply_size / sizeof(RECT);
1955 data->rdh.nRgnSize = reply_size;
1956 win_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1959 else size = reply->total_size;
1961 SERVER_END_REQ;
1962 free( data );
1963 } while (status == STATUS_BUFFER_OVERFLOW);
1965 if (set_ntstatus( status ) && win_rgn)
1967 ret = NtGdiCombineRgn( hrgn, win_rgn, 0, RGN_COPY );
1968 NtGdiDeleteObjectApp( win_rgn );
1970 return ret;
1973 /***********************************************************************
1974 * NtUserSetWindowRgn (win32u.@)
1976 int WINAPI NtUserSetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw )
1978 static const RECT empty_rect;
1979 BOOL ret;
1981 if (hrgn)
1983 RGNDATA *data;
1984 DWORD size;
1986 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return FALSE;
1987 if (!(data = malloc( size ))) return FALSE;
1988 if (!NtGdiGetRegionData( hrgn, size, data ))
1990 free( data );
1991 return FALSE;
1993 SERVER_START_REQ( set_window_region )
1995 req->window = wine_server_user_handle( hwnd );
1996 req->redraw = redraw != 0;
1997 if (data->rdh.nCount)
1998 wine_server_add_data( req, data->Buffer, data->rdh.nCount * sizeof(RECT) );
1999 else
2000 wine_server_add_data( req, &empty_rect, sizeof(empty_rect) );
2001 ret = !wine_server_call_err( req );
2003 SERVER_END_REQ;
2004 free( data );
2006 else /* clear existing region */
2008 SERVER_START_REQ( set_window_region )
2010 req->window = wine_server_user_handle( hwnd );
2011 req->redraw = redraw != 0;
2012 ret = !wine_server_call_err( req );
2014 SERVER_END_REQ;
2017 if (ret)
2019 UINT swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED |
2020 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE;
2021 if (!redraw) swp_flags |= SWP_NOREDRAW;
2022 user_driver->pSetWindowRgn( hwnd, hrgn, redraw );
2023 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, swp_flags );
2024 if (hrgn) NtGdiDeleteObjectApp( hrgn );
2026 return ret;
2029 /***********************************************************************
2030 * NtUserMoveWindow (win32u.@)
2032 BOOL WINAPI NtUserMoveWindow( HWND hwnd, INT x, INT y, INT cx, INT cy, BOOL repaint )
2034 int flags = SWP_NOZORDER | SWP_NOACTIVATE;
2035 if (!repaint) flags |= SWP_NOREDRAW;
2036 TRACE( "%p %d,%d %dx%d %d\n", hwnd, x, y, cx, cy, repaint );
2037 return NtUserSetWindowPos( hwnd, 0, x, y, cx, cy, flags );
2040 /*****************************************************************************
2041 * NtUserGetLayeredWindowAttributes (win32u.@)
2043 BOOL WINAPI NtUserGetLayeredWindowAttributes( HWND hwnd, COLORREF *key, BYTE *alpha, DWORD *flags )
2045 BOOL ret;
2047 SERVER_START_REQ( get_window_layered_info )
2049 req->handle = wine_server_user_handle( hwnd );
2050 if ((ret = !wine_server_call_err( req )))
2052 if (key) *key = reply->color_key;
2053 if (alpha) *alpha = reply->alpha;
2054 if (flags) *flags = reply->flags;
2057 SERVER_END_REQ;
2059 return ret;
2062 /*****************************************************************************
2063 * NtUserSetLayeredWindowAttributes (win32u.@)
2065 BOOL WINAPI NtUserSetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags )
2067 BOOL ret;
2069 TRACE( "(%p,%s,%d,%x)\n", hwnd, debugstr_color(key), alpha, (int)flags );
2071 SERVER_START_REQ( set_window_layered_info )
2073 req->handle = wine_server_user_handle( hwnd );
2074 req->color_key = key;
2075 req->alpha = alpha;
2076 req->flags = flags;
2077 ret = !wine_server_call_err( req );
2079 SERVER_END_REQ;
2081 if (ret)
2083 user_driver->pSetLayeredWindowAttributes( hwnd, key, alpha, flags );
2084 update_window_state( hwnd );
2087 return ret;
2090 /*****************************************************************************
2091 * UpdateLayeredWindow (win32u.@)
2093 BOOL WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_dst, const SIZE *size,
2094 HDC hdc_src, const POINT *pts_src, COLORREF key,
2095 const BLENDFUNCTION *blend, DWORD flags, const RECT *dirty )
2097 DWORD swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW;
2098 RECT window_rect, client_rect;
2099 UPDATELAYEREDWINDOWINFO info;
2100 SIZE offset;
2102 if (flags & ~(ULW_COLORKEY | ULW_ALPHA | ULW_OPAQUE | ULW_EX_NORESIZE) ||
2103 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) ||
2104 NtUserGetLayeredWindowAttributes( hwnd, NULL, NULL, NULL ))
2106 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2107 return FALSE;
2110 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
2112 if (pts_dst)
2114 offset.cx = pts_dst->x - window_rect.left;
2115 offset.cy = pts_dst->y - window_rect.top;
2116 OffsetRect( &client_rect, offset.cx, offset.cy );
2117 OffsetRect( &window_rect, offset.cx, offset.cy );
2118 swp_flags &= ~SWP_NOMOVE;
2120 if (size)
2122 offset.cx = size->cx - (window_rect.right - window_rect.left);
2123 offset.cy = size->cy - (window_rect.bottom - window_rect.top);
2124 if (size->cx <= 0 || size->cy <= 0)
2126 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2127 return FALSE;
2129 if ((flags & ULW_EX_NORESIZE) && (offset.cx || offset.cy))
2131 RtlSetLastWin32Error( ERROR_INCORRECT_SIZE );
2132 return FALSE;
2134 client_rect.right += offset.cx;
2135 client_rect.bottom += offset.cy;
2136 window_rect.right += offset.cx;
2137 window_rect.bottom += offset.cy;
2138 swp_flags &= ~SWP_NOSIZE;
2141 TRACE( "window %p win %s client %s\n", hwnd,
2142 wine_dbgstr_rect(&window_rect), wine_dbgstr_rect(&client_rect) );
2144 apply_window_pos( hwnd, 0, swp_flags, &window_rect, &client_rect, NULL );
2146 info.cbSize = sizeof(info);
2147 info.hdcDst = hdc_dst;
2148 info.pptDst = pts_dst;
2149 info.psize = size;
2150 info.hdcSrc = hdc_src;
2151 info.pptSrc = pts_src;
2152 info.crKey = key;
2153 info.pblend = blend;
2154 info.dwFlags = flags;
2155 info.prcDirty = dirty;
2156 return user_driver->pUpdateLayeredWindow( hwnd, &info, &window_rect );
2159 /***********************************************************************
2160 * list_children_from_point
2162 * Get the list of children that can contain point from the server.
2163 * Point is in screen coordinates.
2164 * Returned list must be freed by caller.
2166 static HWND *list_children_from_point( HWND hwnd, POINT pt )
2168 int i, size = 128;
2169 HWND *list;
2171 for (;;)
2173 int count = 0;
2175 if (!(list = malloc( size * sizeof(HWND) ))) break;
2177 SERVER_START_REQ( get_window_children_from_point )
2179 req->parent = wine_server_user_handle( hwnd );
2180 req->x = pt.x;
2181 req->y = pt.y;
2182 req->dpi = get_thread_dpi();
2183 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
2184 if (!wine_server_call( req )) count = reply->count;
2186 SERVER_END_REQ;
2187 if (count && count < size)
2189 /* start from the end since HWND is potentially larger than user_handle_t */
2190 for (i = count - 1; i >= 0; i--)
2191 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
2192 list[count] = 0;
2193 return list;
2195 free( list );
2196 if (!count) break;
2197 size = count + 1; /* restart with a large enough buffer */
2199 return NULL;
2202 /***********************************************************************
2203 * window_from_point
2205 * Find the window and hittest for a given point.
2207 HWND window_from_point( HWND hwnd, POINT pt, INT *hittest )
2209 int i, res;
2210 HWND ret, *list;
2211 POINT win_pt;
2213 if (!hwnd) hwnd = get_desktop_window();
2215 *hittest = HTNOWHERE;
2217 if (!(list = list_children_from_point( hwnd, pt ))) return 0;
2219 /* now determine the hittest */
2221 for (i = 0; list[i]; i++)
2223 LONG style = get_window_long( list[i], GWL_STYLE );
2225 /* If window is minimized or disabled, return at once */
2226 if (style & WS_DISABLED)
2228 *hittest = HTERROR;
2229 break;
2231 /* Send WM_NCCHITTEST (if same thread) */
2232 if (!is_current_thread_window( list[i] ))
2234 *hittest = HTCLIENT;
2235 break;
2237 win_pt = map_dpi_point( pt, get_thread_dpi(), get_dpi_for_window( list[i] ));
2238 res = send_message( list[i], WM_NCHITTEST, 0, MAKELPARAM( win_pt.x, win_pt.y ));
2239 if (res != HTTRANSPARENT)
2241 *hittest = res; /* Found the window */
2242 break;
2244 /* continue search with next window in z-order */
2246 ret = list[i];
2247 free( list );
2248 TRACE( "scope %p (%d,%d) returning %p\n", hwnd, (int)pt.x, (int)pt.y, ret );
2249 return ret;
2252 /*******************************************************************
2253 * NtUserWindowFromPoint (win32u.@)
2255 HWND WINAPI NtUserWindowFromPoint( LONG x, LONG y )
2257 POINT pt = { .x = x, .y = y };
2258 INT hittest;
2259 return window_from_point( 0, pt, &hittest );
2262 /*******************************************************************
2263 * NtUserChildWindowFromPointEx (win32u.@)
2265 HWND WINAPI NtUserChildWindowFromPointEx( HWND parent, LONG x, LONG y, UINT flags )
2267 POINT pt = { .x = x, .y = y }; /* in the client coordinates */
2268 HWND *list;
2269 int i;
2270 RECT rect;
2271 HWND ret;
2273 get_client_rect( parent, &rect );
2274 if (!PtInRect( &rect, pt )) return 0;
2275 if (!(list = list_window_children( 0, parent, NULL, 0 ))) return parent;
2277 for (i = 0; list[i]; i++)
2279 if (!get_window_rects( list[i], COORDS_PARENT, &rect, NULL, get_thread_dpi() )) continue;
2280 if (!PtInRect( &rect, pt )) continue;
2281 if (flags & (CWP_SKIPINVISIBLE|CWP_SKIPDISABLED))
2283 LONG style = get_window_long( list[i], GWL_STYLE );
2284 if ((flags & CWP_SKIPINVISIBLE) && !(style & WS_VISIBLE)) continue;
2285 if ((flags & CWP_SKIPDISABLED) && (style & WS_DISABLED)) continue;
2287 if (flags & CWP_SKIPTRANSPARENT)
2289 if (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TRANSPARENT) continue;
2291 break;
2293 ret = list[i];
2294 free( list );
2295 if (!ret) ret = parent;
2296 return ret;
2299 /*******************************************************************
2300 * NtUserRealChildWindowFromPoint (win32u.@)
2302 HWND WINAPI NtUserRealChildWindowFromPoint( HWND parent, LONG x, LONG y )
2304 return NtUserChildWindowFromPointEx( parent, x, y, CWP_SKIPTRANSPARENT | CWP_SKIPINVISIBLE );
2307 /*******************************************************************
2308 * get_work_rect
2310 * Get the work area that a maximized window can cover, depending on style.
2312 static BOOL get_work_rect( HWND hwnd, RECT *rect )
2314 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
2315 MONITORINFO mon_info;
2316 DWORD style;
2318 if (!monitor) return FALSE;
2320 mon_info.cbSize = sizeof(mon_info);
2321 get_monitor_info( monitor, &mon_info );
2322 *rect = mon_info.rcMonitor;
2324 style = get_window_long( hwnd, GWL_STYLE );
2325 if (style & WS_MAXIMIZEBOX)
2327 if ((style & WS_CAPTION) == WS_CAPTION || !(style & (WS_CHILD | WS_POPUP)))
2328 *rect = mon_info.rcWork;
2330 return TRUE;
2333 static RECT get_maximized_work_rect( HWND hwnd )
2335 RECT work_rect = { 0 };
2337 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_MINIMIZE | WS_MAXIMIZE)) == WS_MAXIMIZE)
2339 if (!get_work_rect( hwnd, &work_rect ))
2340 work_rect = get_primary_monitor_rect( get_thread_dpi() );
2342 return work_rect;
2345 /*******************************************************************
2346 * update_maximized_pos
2348 * For top level windows covering the work area, we might have to
2349 * "forget" the maximized position. Windows presumably does this
2350 * to avoid situations where the border style changes, which would
2351 * lead the window to be outside the screen, or the window gets
2352 * reloaded on a different screen, and the "saved" position no
2353 * longer applies to it (despite being maximized).
2355 * Some applications (e.g. Imperiums: Greek Wars) depend on this.
2357 static void update_maximized_pos( WND *wnd, RECT *work_rect )
2359 if (wnd->parent && wnd->parent != get_desktop_window())
2360 return;
2362 if (wnd->dwStyle & WS_MAXIMIZE)
2364 if (wnd->window_rect.left <= work_rect->left && wnd->window_rect.top <= work_rect->top &&
2365 wnd->window_rect.right >= work_rect->right && wnd->window_rect.bottom >= work_rect->bottom)
2366 wnd->max_pos.x = wnd->max_pos.y = -1;
2368 else
2369 wnd->max_pos.x = wnd->max_pos.y = -1;
2372 static BOOL empty_point( POINT pt )
2374 return pt.x == -1 && pt.y == -1;
2377 /***********************************************************************
2378 * NtUserGetWindowPlacement (win32u.@)
2380 BOOL WINAPI NtUserGetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *placement )
2382 RECT work_rect = get_maximized_work_rect( hwnd );
2383 WND *win = get_win_ptr( hwnd );
2384 UINT win_dpi;
2386 if (!win) return FALSE;
2388 if (win == WND_DESKTOP)
2390 placement->length = sizeof(*placement);
2391 placement->showCmd = SW_SHOWNORMAL;
2392 placement->flags = 0;
2393 placement->ptMinPosition.x = -1;
2394 placement->ptMinPosition.y = -1;
2395 placement->ptMaxPosition.x = -1;
2396 placement->ptMaxPosition.y = -1;
2397 get_window_rect( hwnd, &placement->rcNormalPosition, get_thread_dpi() );
2398 return TRUE;
2400 if (win == WND_OTHER_PROCESS)
2402 RECT normal_position;
2403 DWORD style;
2405 if (!get_window_rect( hwnd, &normal_position, get_thread_dpi() ))
2406 return FALSE;
2408 FIXME("not fully supported on other process window %p.\n", hwnd);
2410 placement->length = sizeof(*placement);
2411 style = get_window_long( hwnd, GWL_STYLE );
2412 if (style & WS_MINIMIZE)
2413 placement->showCmd = SW_SHOWMINIMIZED;
2414 else
2415 placement->showCmd = (style & WS_MAXIMIZE) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL;
2416 /* provide some dummy information */
2417 placement->flags = 0;
2418 placement->ptMinPosition.x = -1;
2419 placement->ptMinPosition.y = -1;
2420 placement->ptMaxPosition.x = -1;
2421 placement->ptMaxPosition.y = -1;
2422 placement->rcNormalPosition = normal_position;
2423 return TRUE;
2426 /* update the placement according to the current style */
2427 if (win->dwStyle & WS_MINIMIZE)
2429 win->min_pos.x = win->window_rect.left;
2430 win->min_pos.y = win->window_rect.top;
2432 else if (win->dwStyle & WS_MAXIMIZE)
2434 win->max_pos.x = win->window_rect.left;
2435 win->max_pos.y = win->window_rect.top;
2437 else
2439 win->normal_rect = win->window_rect;
2441 update_maximized_pos( win, &work_rect );
2443 placement->length = sizeof(*placement);
2444 if (win->dwStyle & WS_MINIMIZE)
2445 placement->showCmd = SW_SHOWMINIMIZED;
2446 else
2447 placement->showCmd = ( win->dwStyle & WS_MAXIMIZE ) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL ;
2448 if (win->flags & WIN_RESTORE_MAX)
2449 placement->flags = WPF_RESTORETOMAXIMIZED;
2450 else
2451 placement->flags = 0;
2452 win_dpi = get_dpi_for_window( hwnd );
2453 placement->ptMinPosition = empty_point(win->min_pos) ? win->min_pos
2454 : map_dpi_point( win->min_pos, win_dpi, get_thread_dpi() );
2455 placement->ptMaxPosition = empty_point(win->max_pos) ? win->max_pos
2456 : map_dpi_point( win->max_pos, win_dpi, get_thread_dpi() );
2457 placement->rcNormalPosition = map_dpi_rect( win->normal_rect, win_dpi, get_thread_dpi() );
2458 release_win_ptr( win );
2460 TRACE( "%p: returning min %d,%d max %d,%d normal %s\n",
2461 hwnd, (int)placement->ptMinPosition.x, (int)placement->ptMinPosition.y,
2462 (int)placement->ptMaxPosition.x, (int)placement->ptMaxPosition.y,
2463 wine_dbgstr_rect(&placement->rcNormalPosition) );
2464 return TRUE;
2467 /* make sure the specified rect is visible on screen */
2468 static void make_rect_onscreen( RECT *rect )
2470 MONITORINFO info;
2471 HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTONEAREST, get_thread_dpi() );
2473 info.cbSize = sizeof(info);
2474 if (!monitor || !get_monitor_info( monitor, &info )) return;
2475 /* FIXME: map coordinates from rcWork to rcMonitor */
2476 if (rect->right <= info.rcWork.left)
2478 rect->right += info.rcWork.left - rect->left;
2479 rect->left = info.rcWork.left;
2481 else if (rect->left >= info.rcWork.right)
2483 rect->left += info.rcWork.right - rect->right;
2484 rect->right = info.rcWork.right;
2486 if (rect->bottom <= info.rcWork.top)
2488 rect->bottom += info.rcWork.top - rect->top;
2489 rect->top = info.rcWork.top;
2491 else if (rect->top >= info.rcWork.bottom)
2493 rect->top += info.rcWork.bottom - rect->bottom;
2494 rect->bottom = info.rcWork.bottom;
2498 /***********************************************************************
2499 * NtUserGetInternalWindowPos (win32u.@)
2501 UINT WINAPI NtUserGetInternalWindowPos( HWND hwnd, RECT *rect, POINT *pt )
2503 WINDOWPLACEMENT placement;
2505 placement.length = sizeof(placement);
2506 if (!NtUserGetWindowPlacement( hwnd, &placement )) return 0;
2507 if (rect) *rect = placement.rcNormalPosition;
2508 if (pt) *pt = placement.ptMinPosition;
2509 return placement.showCmd;
2512 /* make sure the specified point is visible on screen */
2513 static void make_point_onscreen( POINT *pt )
2515 RECT rect;
2517 SetRect( &rect, pt->x, pt->y, pt->x + 1, pt->y + 1 );
2518 make_rect_onscreen( &rect );
2519 pt->x = rect.left;
2520 pt->y = rect.top;
2523 static BOOL set_window_placement( HWND hwnd, const WINDOWPLACEMENT *wndpl, UINT flags )
2525 RECT work_rect = get_maximized_work_rect( hwnd );
2526 WND *win = get_win_ptr( hwnd );
2527 WINDOWPLACEMENT wp = *wndpl;
2528 DWORD style;
2530 if (flags & PLACE_MIN) make_point_onscreen( &wp.ptMinPosition );
2531 if (flags & PLACE_MAX) make_point_onscreen( &wp.ptMaxPosition );
2532 if (flags & PLACE_RECT) make_rect_onscreen( &wp.rcNormalPosition );
2534 TRACE( "%p: setting min %d,%d max %d,%d normal %s flags %x adjusted to min %d,%d max %d,%d normal %s\n",
2535 hwnd, (int)wndpl->ptMinPosition.x, (int)wndpl->ptMinPosition.y,
2536 (int)wndpl->ptMaxPosition.x, (int)wndpl->ptMaxPosition.y,
2537 wine_dbgstr_rect(&wndpl->rcNormalPosition), flags,
2538 (int)wp.ptMinPosition.x, (int)wp.ptMinPosition.y,
2539 (int)wp.ptMaxPosition.x, (int)wp.ptMaxPosition.y,
2540 wine_dbgstr_rect(&wp.rcNormalPosition) );
2542 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
2544 if (flags & PLACE_MIN) win->min_pos = point_thread_to_win_dpi( hwnd, wp.ptMinPosition );
2545 if (flags & PLACE_MAX)
2547 win->max_pos = point_thread_to_win_dpi( hwnd, wp.ptMaxPosition );
2548 update_maximized_pos( win, &work_rect );
2550 if (flags & PLACE_RECT) win->normal_rect = rect_thread_to_win_dpi( hwnd, wp.rcNormalPosition );
2552 style = win->dwStyle;
2554 release_win_ptr( win );
2556 if (style & WS_MINIMIZE)
2558 if (flags & PLACE_MIN)
2560 NtUserSetWindowPos( hwnd, 0, wp.ptMinPosition.x, wp.ptMinPosition.y, 0, 0,
2561 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
2564 else if (style & WS_MAXIMIZE)
2566 if (flags & PLACE_MAX)
2567 NtUserSetWindowPos( hwnd, 0, wp.ptMaxPosition.x, wp.ptMaxPosition.y, 0, 0,
2568 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
2570 else if (flags & PLACE_RECT)
2571 NtUserSetWindowPos( hwnd, 0, wp.rcNormalPosition.left, wp.rcNormalPosition.top,
2572 wp.rcNormalPosition.right - wp.rcNormalPosition.left,
2573 wp.rcNormalPosition.bottom - wp.rcNormalPosition.top,
2574 SWP_NOZORDER | SWP_NOACTIVATE );
2576 NtUserShowWindow( hwnd, wndpl->showCmd );
2578 if (is_iconic( hwnd ))
2580 if (wndpl->flags & WPF_RESTORETOMAXIMIZED)
2581 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
2583 return TRUE;
2586 /***********************************************************************
2587 * NtUserSetWindowPlacement (win32u.@)
2589 BOOL WINAPI NtUserSetWindowPlacement( HWND hwnd, const WINDOWPLACEMENT *wpl )
2591 UINT flags = PLACE_MAX | PLACE_RECT;
2592 if (!wpl) return FALSE;
2593 if (wpl->length != sizeof(*wpl))
2595 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
2596 return FALSE;
2598 if (wpl->flags & WPF_SETMINPOSITION) flags |= PLACE_MIN;
2599 return set_window_placement( hwnd, wpl, flags );
2602 /*****************************************************************************
2603 * NtUserBuildHwndList (win32u.@)
2605 NTSTATUS WINAPI NtUserBuildHwndList( HDESK desktop, ULONG unk2, ULONG unk3, ULONG unk4,
2606 ULONG thread_id, ULONG count, HWND *buffer, ULONG *size )
2608 user_handle_t *list = (user_handle_t *)buffer;
2609 int i;
2610 NTSTATUS status;
2612 SERVER_START_REQ( get_window_children )
2614 req->desktop = wine_server_obj_handle( desktop );
2615 req->tid = thread_id;
2616 if (count) wine_server_set_reply( req, list, (count - 1) * sizeof(user_handle_t) );
2617 status = wine_server_call( req );
2618 if (status && status != STATUS_BUFFER_TOO_SMALL) return status;
2619 *size = reply->count + 1;
2621 SERVER_END_REQ;
2622 if (*size > count) return STATUS_BUFFER_TOO_SMALL;
2624 /* start from the end since HWND is potentially larger than user_handle_t */
2625 for (i = *size - 2; i >= 0; i--)
2626 buffer[i] = wine_server_ptr_handle( list[i] );
2627 buffer[*size - 1] = HWND_BOTTOM;
2628 return STATUS_SUCCESS;
2631 /***********************************************************************
2632 * NtUserFindWindowEx (USER32.@)
2634 HWND WINAPI NtUserFindWindowEx( HWND parent, HWND child, UNICODE_STRING *class, UNICODE_STRING *title,
2635 ULONG unk )
2637 HWND *list;
2638 HWND retvalue = 0;
2639 int i = 0, len = 0, title_len;
2640 WCHAR *buffer = NULL;
2642 if (!parent && child) parent = get_desktop_window();
2643 else if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent();
2645 if (title)
2647 len = title->Length / sizeof(WCHAR) + 1; /* one extra char to check for chars beyond the end */
2648 if (!(buffer = malloc( (len + 1) * sizeof(WCHAR) ))) return 0;
2651 if (!(list = list_window_children( 0, parent, class, 0 ))) goto done;
2653 if (child)
2655 child = get_full_window_handle( child );
2656 while (list[i] && list[i] != child) i++;
2657 if (!list[i]) goto done;
2658 i++; /* start from next window */
2661 if (title)
2663 while (list[i])
2665 title_len = NtUserInternalGetWindowText( list[i], buffer, len + 1 );
2666 if (title_len * sizeof(WCHAR) == title->Length &&
2667 (!title_len || !wcsnicmp( buffer, title->Buffer, title_len )))
2668 break;
2669 i++;
2672 retvalue = list[i];
2674 done:
2675 free( list );
2676 free( buffer );
2677 return retvalue;
2680 /* Retrieve the window text from the server. */
2681 static data_size_t get_server_window_text( HWND hwnd, WCHAR *text, data_size_t count )
2683 data_size_t len = 0, needed = 0;
2685 SERVER_START_REQ( get_window_text )
2687 req->handle = wine_server_user_handle( hwnd );
2688 if (count) wine_server_set_reply( req, text, (count - 1) * sizeof(WCHAR) );
2689 if (!wine_server_call_err( req ))
2691 needed = reply->length;
2692 len = wine_server_reply_size(reply);
2695 SERVER_END_REQ;
2696 if (text) text[len / sizeof(WCHAR)] = 0;
2697 return needed;
2700 /*******************************************************************
2701 * NtUserInternalGetWindowText (win32u.@)
2703 INT WINAPI NtUserInternalGetWindowText( HWND hwnd, WCHAR *text, INT count )
2705 WND *win;
2707 if (count <= 0) return 0;
2708 if (!(win = get_win_ptr( hwnd ))) return 0;
2709 if (win == WND_DESKTOP) text[0] = 0;
2710 else if (win != WND_OTHER_PROCESS)
2712 if (win->text) lstrcpynW( text, win->text, count );
2713 else text[0] = 0;
2714 release_win_ptr( win );
2716 else
2718 get_server_window_text( hwnd, text, count );
2720 return lstrlenW(text);
2723 /*******************************************************************
2724 * get_windows_offset
2726 * Calculate the offset between the origin of the two windows. Used
2727 * to implement MapWindowPoints.
2729 static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mirrored, POINT *ret_offset )
2731 WND *win;
2732 POINT offset;
2733 BOOL mirror_from, mirror_to, ret;
2734 HWND hwnd;
2736 offset.x = offset.y = 0;
2737 *mirrored = mirror_from = mirror_to = FALSE;
2739 /* Translate source window origin to screen coords */
2740 if (hwnd_from)
2742 if (!(win = get_win_ptr( hwnd_from )))
2744 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2745 return FALSE;
2747 if (win == WND_OTHER_PROCESS) goto other_process;
2748 if (win != WND_DESKTOP)
2750 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2752 mirror_from = TRUE;
2753 offset.x += win->client_rect.right - win->client_rect.left;
2755 while (win->parent)
2757 offset.x += win->client_rect.left;
2758 offset.y += win->client_rect.top;
2759 hwnd = win->parent;
2760 release_win_ptr( win );
2761 if (!(win = get_win_ptr( hwnd ))) break;
2762 if (win == WND_OTHER_PROCESS) goto other_process;
2763 if (win == WND_DESKTOP) break;
2764 if (win->flags & WIN_CHILDREN_MOVED)
2766 release_win_ptr( win );
2767 goto other_process;
2770 if (win && win != WND_DESKTOP) release_win_ptr( win );
2771 offset = map_dpi_point( offset, get_dpi_for_window( hwnd_from ), dpi );
2775 /* Translate origin to destination window coords */
2776 if (hwnd_to)
2778 if (!(win = get_win_ptr( hwnd_to )))
2780 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2781 return FALSE;
2783 if (win == WND_OTHER_PROCESS) goto other_process;
2784 if (win != WND_DESKTOP)
2786 POINT pt = { 0, 0 };
2787 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2789 mirror_to = TRUE;
2790 pt.x += win->client_rect.right - win->client_rect.left;
2792 while (win->parent)
2794 pt.x += win->client_rect.left;
2795 pt.y += win->client_rect.top;
2796 hwnd = win->parent;
2797 release_win_ptr( win );
2798 if (!(win = get_win_ptr( hwnd ))) break;
2799 if (win == WND_OTHER_PROCESS) goto other_process;
2800 if (win == WND_DESKTOP) break;
2801 if (win->flags & WIN_CHILDREN_MOVED)
2803 release_win_ptr( win );
2804 goto other_process;
2807 if (win && win != WND_DESKTOP) release_win_ptr( win );
2808 pt = map_dpi_point( pt, get_dpi_for_window( hwnd_to ), dpi );
2809 offset.x -= pt.x;
2810 offset.y -= pt.y;
2814 *mirrored = mirror_from ^ mirror_to;
2815 if (mirror_from) offset.x = -offset.x;
2816 *ret_offset = offset;
2817 return TRUE;
2819 other_process: /* one of the parents may belong to another process, do it the hard way */
2820 SERVER_START_REQ( get_windows_offset )
2822 req->from = wine_server_user_handle( hwnd_from );
2823 req->to = wine_server_user_handle( hwnd_to );
2824 req->dpi = dpi;
2825 if ((ret = !wine_server_call_err( req )))
2827 ret_offset->x = reply->x;
2828 ret_offset->y = reply->y;
2829 *mirrored = reply->mirror;
2832 SERVER_END_REQ;
2833 return ret;
2836 /* see ClientToScreen */
2837 BOOL client_to_screen( HWND hwnd, POINT *pt )
2839 POINT offset;
2840 BOOL mirrored;
2842 if (!hwnd)
2844 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2845 return FALSE;
2848 if (!get_windows_offset( hwnd, 0, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2849 pt->x += offset.x;
2850 pt->y += offset.y;
2851 if (mirrored) pt->x = -pt->x;
2852 return TRUE;
2855 /* see ScreenToClient */
2856 BOOL screen_to_client( HWND hwnd, POINT *pt )
2858 POINT offset;
2859 BOOL mirrored;
2861 if (!hwnd)
2863 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2864 return FALSE;
2866 if (!get_windows_offset( 0, hwnd, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2867 pt->x += offset.x;
2868 pt->y += offset.y;
2869 if (mirrored) pt->x = -pt->x;
2870 return TRUE;
2873 /* map coordinates of a window region */
2874 void map_window_region( HWND from, HWND to, HRGN hrgn )
2876 BOOL mirrored;
2877 POINT offset;
2878 UINT i, size;
2879 RGNDATA *data;
2880 HRGN new_rgn;
2881 RECT *rect;
2883 if (!get_windows_offset( from, to, get_thread_dpi(), &mirrored, &offset )) return;
2885 if (!mirrored)
2887 NtGdiOffsetRgn( hrgn, offset.x, offset.y );
2888 return;
2890 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return;
2891 if (!(data = malloc( size ))) return;
2892 NtGdiGetRegionData( hrgn, size, data );
2893 rect = (RECT *)data->Buffer;
2894 for (i = 0; i < data->rdh.nCount; i++)
2896 int tmp = -(rect[i].left + offset.x);
2897 rect[i].left = -(rect[i].right + offset.x);
2898 rect[i].right = tmp;
2899 rect[i].top += offset.y;
2900 rect[i].bottom += offset.y;
2902 if ((new_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data )))
2904 NtGdiCombineRgn( hrgn, new_rgn, 0, RGN_COPY );
2905 NtGdiDeleteObjectApp( new_rgn );
2907 free( data );
2910 /* see MapWindowPoints */
2911 int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count, UINT dpi )
2913 BOOL mirrored;
2914 POINT offset;
2915 UINT i;
2917 if (!get_windows_offset( hwnd_from, hwnd_to, dpi, &mirrored, &offset )) return 0;
2919 for (i = 0; i < count; i++)
2921 points[i].x += offset.x;
2922 points[i].y += offset.y;
2923 if (mirrored) points[i].x = -points[i].x;
2925 if (mirrored && count == 2) /* special case for rectangle */
2927 int tmp = points[0].x;
2928 points[0].x = points[1].x;
2929 points[1].x = tmp;
2931 return MAKELONG( LOWORD(offset.x), LOWORD(offset.y) );
2934 /***********************************************************************
2935 * dump_winpos_flags
2937 static void dump_winpos_flags( UINT flags )
2939 static const UINT dumped_flags = (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW |
2940 SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW |
2941 SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOOWNERZORDER |
2942 SWP_NOSENDCHANGING | SWP_DEFERERASE | SWP_ASYNCWINDOWPOS |
2943 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_STATECHANGED);
2944 TRACE( "flags:" );
2945 if(flags & SWP_NOSIZE) TRACE( " SWP_NOSIZE" );
2946 if(flags & SWP_NOMOVE) TRACE( " SWP_NOMOVE" );
2947 if(flags & SWP_NOZORDER) TRACE( " SWP_NOZORDER" );
2948 if(flags & SWP_NOREDRAW) TRACE( " SWP_NOREDRAW" );
2949 if(flags & SWP_NOACTIVATE) TRACE( " SWP_NOACTIVATE" );
2950 if(flags & SWP_FRAMECHANGED) TRACE( " SWP_FRAMECHANGED" );
2951 if(flags & SWP_SHOWWINDOW) TRACE( " SWP_SHOWWINDOW" );
2952 if(flags & SWP_HIDEWINDOW) TRACE( " SWP_HIDEWINDOW" );
2953 if(flags & SWP_NOCOPYBITS) TRACE( " SWP_NOCOPYBITS" );
2954 if(flags & SWP_NOOWNERZORDER) TRACE( " SWP_NOOWNERZORDER" );
2955 if(flags & SWP_NOSENDCHANGING) TRACE( " SWP_NOSENDCHANGING" );
2956 if(flags & SWP_DEFERERASE) TRACE( " SWP_DEFERERASE" );
2957 if(flags & SWP_ASYNCWINDOWPOS) TRACE( " SWP_ASYNCWINDOWPOS" );
2958 if(flags & SWP_NOCLIENTSIZE) TRACE( " SWP_NOCLIENTSIZE" );
2959 if(flags & SWP_NOCLIENTMOVE) TRACE( " SWP_NOCLIENTMOVE" );
2960 if(flags & SWP_STATECHANGED) TRACE( " SWP_STATECHANGED" );
2962 if(flags & ~dumped_flags) TRACE( " %08x", flags & ~dumped_flags );
2963 TRACE( "\n" );
2966 /***********************************************************************
2967 * map_dpi_winpos
2969 static void map_dpi_winpos( WINDOWPOS *winpos )
2971 UINT dpi_from = get_thread_dpi();
2972 UINT dpi_to = get_dpi_for_window( winpos->hwnd );
2974 if (!dpi_from) dpi_from = get_win_monitor_dpi( winpos->hwnd );
2975 if (dpi_from == dpi_to) return;
2976 winpos->x = muldiv( winpos->x, dpi_to, dpi_from );
2977 winpos->y = muldiv( winpos->y, dpi_to, dpi_from );
2978 winpos->cx = muldiv( winpos->cx, dpi_to, dpi_from );
2979 winpos->cy = muldiv( winpos->cy, dpi_to, dpi_from );
2982 /***********************************************************************
2983 * calc_winpos
2985 static BOOL calc_winpos( WINDOWPOS *winpos, RECT *old_window_rect, RECT *old_client_rect,
2986 RECT *new_window_rect, RECT *new_client_rect )
2988 WND *win;
2990 /* Send WM_WINDOWPOSCHANGING message */
2991 if (!(winpos->flags & SWP_NOSENDCHANGING)
2992 && !((winpos->flags & SWP_AGG_NOCLIENTCHANGE) && (winpos->flags & SWP_SHOWWINDOW)))
2993 send_message( winpos->hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos );
2995 if (!(win = get_win_ptr( winpos->hwnd )) ||
2996 win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
2998 /* Calculate new position and size */
2999 get_window_rects( winpos->hwnd, COORDS_PARENT, old_window_rect, old_client_rect, get_thread_dpi() );
3000 *new_window_rect = *old_window_rect;
3001 *new_client_rect = *old_client_rect;
3003 if (!(winpos->flags & SWP_NOSIZE))
3005 if (win->dwStyle & WS_MINIMIZE)
3007 new_window_rect->right = new_window_rect->left + get_system_metrics( SM_CXMINIMIZED );
3008 new_window_rect->bottom = new_window_rect->top + get_system_metrics( SM_CYMINIMIZED );
3010 else
3012 new_window_rect->right = new_window_rect->left + winpos->cx;
3013 new_window_rect->bottom = new_window_rect->top + winpos->cy;
3017 if (!(winpos->flags & SWP_NOMOVE))
3019 /* If the window is toplevel minimized off-screen, force keep it there */
3020 if ((win->dwStyle & WS_MINIMIZE) &&
3021 win->window_rect.left <= -32000 && win->window_rect.top <= -32000 &&
3022 (!win->parent || win->parent == get_desktop_window()))
3024 winpos->x = -32000;
3025 winpos->y = -32000;
3027 new_window_rect->left = winpos->x;
3028 new_window_rect->top = winpos->y;
3029 new_window_rect->right += winpos->x - old_window_rect->left;
3030 new_window_rect->bottom += winpos->y - old_window_rect->top;
3032 OffsetRect( new_client_rect, winpos->x - old_window_rect->left,
3033 winpos->y - old_window_rect->top );
3035 winpos->flags |= SWP_NOCLIENTMOVE | SWP_NOCLIENTSIZE;
3037 TRACE( "hwnd %p, after %p, swp %d,%d %dx%d flags %08x current %s style %08x new %s\n",
3038 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3039 winpos->cx, winpos->cy, winpos->flags,
3040 wine_dbgstr_rect( old_window_rect ), win->dwStyle,
3041 wine_dbgstr_rect( new_window_rect ));
3043 release_win_ptr( win );
3044 return TRUE;
3047 /***********************************************************************
3048 * get_valid_rects
3050 * Compute the valid rects from the old and new client rect and WVR_* flags.
3051 * Helper for WM_NCCALCSIZE handling.
3053 static inline void get_valid_rects( const RECT *old_client, const RECT *new_client, UINT flags,
3054 RECT *valid )
3056 int cx, cy;
3058 if (flags & WVR_REDRAW)
3060 SetRectEmpty( &valid[0] );
3061 SetRectEmpty( &valid[1] );
3062 return;
3065 if (flags & WVR_VALIDRECTS)
3067 if (!intersect_rect( &valid[0], &valid[0], new_client ) ||
3068 !intersect_rect( &valid[1], &valid[1], old_client ))
3070 SetRectEmpty( &valid[0] );
3071 SetRectEmpty( &valid[1] );
3072 return;
3074 flags = WVR_ALIGNLEFT | WVR_ALIGNTOP;
3076 else
3078 valid[0] = *new_client;
3079 valid[1] = *old_client;
3082 /* make sure the rectangles have the same size */
3083 cx = min( valid[0].right - valid[0].left, valid[1].right - valid[1].left );
3084 cy = min( valid[0].bottom - valid[0].top, valid[1].bottom - valid[1].top );
3086 if (flags & WVR_ALIGNBOTTOM)
3088 valid[0].top = valid[0].bottom - cy;
3089 valid[1].top = valid[1].bottom - cy;
3091 else
3093 valid[0].bottom = valid[0].top + cy;
3094 valid[1].bottom = valid[1].top + cy;
3096 if (flags & WVR_ALIGNRIGHT)
3098 valid[0].left = valid[0].right - cx;
3099 valid[1].left = valid[1].right - cx;
3101 else
3103 valid[0].right = valid[0].left + cx;
3104 valid[1].right = valid[1].left + cx;
3108 static UINT calc_ncsize( WINDOWPOS *winpos, const RECT *old_window_rect, const RECT *old_client_rect,
3109 const RECT *new_window_rect, RECT *new_client_rect, RECT *valid_rects,
3110 int parent_x, int parent_y )
3112 UINT wvr_flags = 0;
3114 /* Send WM_NCCALCSIZE message to get new client area */
3115 if ((winpos->flags & (SWP_FRAMECHANGED | SWP_NOSIZE)) != SWP_NOSIZE)
3117 NCCALCSIZE_PARAMS params;
3118 WINDOWPOS winposCopy;
3119 UINT class_style;
3121 params.rgrc[0] = *new_window_rect;
3122 params.rgrc[1] = *old_window_rect;
3123 params.rgrc[2] = *old_client_rect;
3124 params.lppos = &winposCopy;
3125 winposCopy = *winpos;
3127 if (winpos->flags & SWP_NOMOVE)
3129 winposCopy.x = old_window_rect->left;
3130 winposCopy.y = old_window_rect->top;
3133 if (winpos->flags & SWP_NOSIZE)
3135 winposCopy.cx = old_window_rect->right - old_window_rect->left;
3136 winposCopy.cy = old_window_rect->bottom - old_window_rect->top;
3139 class_style = get_class_long( winpos->hwnd, GCL_STYLE, FALSE );
3140 if (class_style & CS_VREDRAW) wvr_flags |= WVR_VREDRAW;
3141 if (class_style & CS_HREDRAW) wvr_flags |= WVR_HREDRAW;
3143 wvr_flags |= send_message( winpos->hwnd, WM_NCCALCSIZE, TRUE, (LPARAM)&params );
3145 *new_client_rect = params.rgrc[0];
3147 TRACE( "hwnd %p old win %s old client %s new win %s new client %s\n", winpos->hwnd,
3148 wine_dbgstr_rect(old_window_rect), wine_dbgstr_rect(old_client_rect),
3149 wine_dbgstr_rect(new_window_rect), wine_dbgstr_rect(new_client_rect) );
3151 if (new_client_rect->left != old_client_rect->left - parent_x ||
3152 new_client_rect->top != old_client_rect->top - parent_y)
3153 winpos->flags &= ~SWP_NOCLIENTMOVE;
3155 if ((new_client_rect->right - new_client_rect->left !=
3156 old_client_rect->right - old_client_rect->left))
3157 winpos->flags &= ~SWP_NOCLIENTSIZE;
3158 else
3159 wvr_flags &= ~WVR_HREDRAW;
3161 if (new_client_rect->bottom - new_client_rect->top !=
3162 old_client_rect->bottom - old_client_rect->top)
3163 winpos->flags &= ~SWP_NOCLIENTSIZE;
3164 else
3165 wvr_flags &= ~WVR_VREDRAW;
3167 valid_rects[0] = params.rgrc[1];
3168 valid_rects[1] = params.rgrc[2];
3170 else
3172 if (!(winpos->flags & SWP_NOMOVE) &&
3173 (new_client_rect->left != old_client_rect->left - parent_x ||
3174 new_client_rect->top != old_client_rect->top - parent_y))
3175 winpos->flags &= ~SWP_NOCLIENTMOVE;
3178 if (winpos->flags & (SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_HIDEWINDOW))
3180 SetRectEmpty( &valid_rects[0] );
3181 SetRectEmpty( &valid_rects[1] );
3183 else get_valid_rects( old_client_rect, new_client_rect, wvr_flags, valid_rects );
3185 return wvr_flags;
3188 /* fix redundant flags and values in the WINDOWPOS structure */
3189 static BOOL fixup_swp_flags( WINDOWPOS *winpos, const RECT *old_window_rect, int parent_x, int parent_y )
3191 HWND parent;
3192 WND *win = get_win_ptr( winpos->hwnd );
3193 BOOL ret = TRUE;
3195 if (!win || win == WND_OTHER_PROCESS)
3197 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
3198 return FALSE;
3200 winpos->hwnd = win->obj.handle; /* make it a full handle */
3202 /* Finally make sure that all coordinates are valid */
3203 if (winpos->x < -32768) winpos->x = -32768;
3204 else if (winpos->x > 32767) winpos->x = 32767;
3205 if (winpos->y < -32768) winpos->y = -32768;
3206 else if (winpos->y > 32767) winpos->y = 32767;
3208 if (winpos->cx < 0) winpos->cx = 0;
3209 else if (winpos->cx > 32767) winpos->cx = 32767;
3210 if (winpos->cy < 0) winpos->cy = 0;
3211 else if (winpos->cy > 32767) winpos->cy = 32767;
3213 parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3214 if (!is_window_visible( parent )) winpos->flags |= SWP_NOREDRAW;
3216 if (win->dwStyle & WS_VISIBLE) winpos->flags &= ~SWP_SHOWWINDOW;
3217 else
3219 winpos->flags &= ~SWP_HIDEWINDOW;
3220 if (!(winpos->flags & SWP_SHOWWINDOW)) winpos->flags |= SWP_NOREDRAW;
3223 if ((old_window_rect->right - old_window_rect->left == winpos->cx) &&
3224 (old_window_rect->bottom - old_window_rect->top == winpos->cy))
3225 winpos->flags |= SWP_NOSIZE; /* Already the right size */
3227 if ((old_window_rect->left - parent_x == winpos->x) && (old_window_rect->top - parent_y == winpos->y))
3228 winpos->flags |= SWP_NOMOVE; /* Already the right position */
3230 if ((win->dwStyle & (WS_POPUP | WS_CHILD)) != WS_CHILD)
3232 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)) && /* Bring to the top when activating */
3233 (winpos->flags & SWP_NOZORDER ||
3234 (winpos->hwndInsertAfter != HWND_TOPMOST && winpos->hwndInsertAfter != HWND_NOTOPMOST)))
3236 winpos->flags &= ~SWP_NOZORDER;
3237 winpos->hwndInsertAfter = HWND_TOP;
3241 /* Check hwndInsertAfter */
3242 if (winpos->flags & SWP_NOZORDER) goto done;
3244 if (winpos->hwndInsertAfter == HWND_TOP)
3246 if (get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
3247 winpos->flags |= SWP_NOZORDER;
3249 else if (winpos->hwndInsertAfter == HWND_BOTTOM)
3251 if (!(win->dwExStyle & WS_EX_TOPMOST) &&
3252 get_window_relative( winpos->hwnd, GW_HWNDLAST ) == winpos->hwnd)
3253 winpos->flags |= SWP_NOZORDER;
3255 else if (winpos->hwndInsertAfter == HWND_TOPMOST)
3257 if ((win->dwExStyle & WS_EX_TOPMOST) &&
3258 get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
3259 winpos->flags |= SWP_NOZORDER;
3261 else if (winpos->hwndInsertAfter == HWND_NOTOPMOST)
3263 if (!(win->dwExStyle & WS_EX_TOPMOST))
3264 winpos->flags |= SWP_NOZORDER;
3266 else
3268 if ((winpos->hwnd == winpos->hwndInsertAfter) ||
3269 (winpos->hwnd == get_window_relative( winpos->hwndInsertAfter, GW_HWNDNEXT )))
3270 winpos->flags |= SWP_NOZORDER;
3272 done:
3273 release_win_ptr( win );
3274 return ret;
3277 /***********************************************************************
3278 * swp_owner_popups
3280 * fix Z order taking into account owned popups -
3281 * basically we need to maintain them above the window that owns them
3283 * FIXME: hide/show owned popups when owner visibility changes.
3285 static HWND swp_owner_popups( HWND hwnd, HWND after )
3287 HWND owner, *list = NULL;
3288 unsigned int i;
3290 TRACE( "(%p) after = %p\n", hwnd, after );
3292 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) return after;
3294 if ((owner = get_window_relative( hwnd, GW_OWNER )))
3296 /* make sure this popup stays above the owner */
3298 if (after != HWND_TOPMOST)
3300 if (!(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) return after;
3302 for (i = 0; list[i]; i++)
3304 BOOL topmost = (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST) != 0;
3306 if (list[i] == owner)
3308 if (i > 0) after = list[i-1];
3309 else after = topmost ? HWND_TOPMOST : HWND_TOP;
3310 break;
3313 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3315 if (!topmost) break;
3317 else if (list[i] == after) break;
3322 if (after == HWND_BOTTOM) goto done;
3323 if (!list && !(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) goto done;
3325 i = 0;
3326 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3328 if (after == HWND_NOTOPMOST ||
3329 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST))
3331 /* skip all the topmost windows */
3332 while (list[i] && (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST)) i++;
3335 else if (after != HWND_TOPMOST)
3337 /* skip windows that are already placed correctly */
3338 for (i = 0; list[i]; i++)
3340 if (list[i] == after) break;
3341 if (list[i] == hwnd) goto done; /* nothing to do if window is moving backwards in z-order */
3345 for ( ; list[i]; i++)
3347 if (list[i] == hwnd) break;
3348 if (get_window_relative( list[i], GW_OWNER ) != hwnd) continue;
3349 TRACE( "moving %p owned by %p after %p\n", list[i], hwnd, after );
3350 NtUserSetWindowPos( list[i], after, 0, 0, 0, 0,
3351 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_DEFERERASE );
3352 after = list[i];
3355 done:
3356 free( list );
3357 return after;
3360 /* NtUserSetWindowPos implementation */
3361 BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y )
3363 RECT old_window_rect, old_client_rect, new_window_rect, new_client_rect, valid_rects[2];
3364 UINT orig_flags;
3365 BOOL ret = FALSE;
3366 DPI_AWARENESS_CONTEXT context;
3368 orig_flags = winpos->flags;
3370 /* First, check z-order arguments. */
3371 if (!(winpos->flags & SWP_NOZORDER))
3373 /* fix sign extension */
3374 if (winpos->hwndInsertAfter == (HWND)0xffff) winpos->hwndInsertAfter = HWND_TOPMOST;
3375 else if (winpos->hwndInsertAfter == (HWND)0xfffe) winpos->hwndInsertAfter = HWND_NOTOPMOST;
3377 if (!(winpos->hwndInsertAfter == HWND_TOP ||
3378 winpos->hwndInsertAfter == HWND_BOTTOM ||
3379 winpos->hwndInsertAfter == HWND_TOPMOST ||
3380 winpos->hwndInsertAfter == HWND_NOTOPMOST))
3382 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3383 HWND insertafter_parent = NtUserGetAncestor( winpos->hwndInsertAfter, GA_PARENT );
3385 /* hwndInsertAfter must be a sibling of the window */
3386 if (!insertafter_parent) return FALSE;
3387 if (insertafter_parent != parent) return TRUE;
3391 /* Make sure that coordinates are valid for WM_WINDOWPOSCHANGING */
3392 if (!(winpos->flags & SWP_NOMOVE))
3394 if (winpos->x < -32768) winpos->x = -32768;
3395 else if (winpos->x > 32767) winpos->x = 32767;
3396 if (winpos->y < -32768) winpos->y = -32768;
3397 else if (winpos->y > 32767) winpos->y = 32767;
3399 if (!(winpos->flags & SWP_NOSIZE))
3401 if (winpos->cx < 0) winpos->cx = 0;
3402 else if (winpos->cx > 32767) winpos->cx = 32767;
3403 if (winpos->cy < 0) winpos->cy = 0;
3404 else if (winpos->cy > 32767) winpos->cy = 32767;
3407 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( winpos->hwnd ));
3409 if (!calc_winpos( winpos, &old_window_rect, &old_client_rect,
3410 &new_window_rect, &new_client_rect )) goto done;
3412 /* Fix redundant flags */
3413 if (!fixup_swp_flags( winpos, &old_window_rect, parent_x, parent_y )) goto done;
3415 if((winpos->flags & (SWP_NOZORDER | SWP_HIDEWINDOW | SWP_SHOWWINDOW)) != SWP_NOZORDER)
3417 if (NtUserGetAncestor( winpos->hwnd, GA_PARENT ) == get_desktop_window())
3418 winpos->hwndInsertAfter = swp_owner_popups( winpos->hwnd, winpos->hwndInsertAfter );
3421 /* Common operations */
3423 calc_ncsize( winpos, &old_window_rect, &old_client_rect,
3424 &new_window_rect, &new_client_rect, valid_rects, parent_x, parent_y );
3426 if (!apply_window_pos( winpos->hwnd, winpos->hwndInsertAfter, winpos->flags,
3427 &new_window_rect, &new_client_rect, valid_rects ))
3428 goto done;
3430 if (winpos->flags & SWP_HIDEWINDOW)
3431 NtUserHideCaret( winpos->hwnd );
3432 else if (winpos->flags & SWP_SHOWWINDOW)
3433 NtUserShowCaret( winpos->hwnd );
3435 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)))
3437 /* child windows get WM_CHILDACTIVATE message */
3438 if ((get_window_long( winpos->hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD)
3439 send_message( winpos->hwnd, WM_CHILDACTIVATE, 0, 0 );
3440 else
3441 set_foreground_window( winpos->hwnd, FALSE );
3444 if(!(orig_flags & SWP_DEFERERASE))
3446 /* erase parent when hiding or resizing child */
3447 if ((orig_flags & SWP_HIDEWINDOW) ||
3448 (!(orig_flags & SWP_SHOWWINDOW) &&
3449 (winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOGEOMETRYCHANGE))
3451 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3452 if (!parent || parent == get_desktop_window()) parent = winpos->hwnd;
3453 erase_now( parent, 0 );
3456 /* Give newly shown windows a chance to redraw */
3457 if(((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3458 && !(orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW))
3460 erase_now(winpos->hwnd, 0);
3464 /* And last, send the WM_WINDOWPOSCHANGED message */
3466 TRACE( "\tstatus flags = %04x\n", winpos->flags & SWP_AGG_STATUSFLAGS );
3468 if (((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3469 && !((orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW)))
3471 /* WM_WINDOWPOSCHANGED is sent even if SWP_NOSENDCHANGING is set
3472 and always contains final window position.
3474 winpos->x = new_window_rect.left;
3475 winpos->y = new_window_rect.top;
3476 winpos->cx = new_window_rect.right - new_window_rect.left;
3477 winpos->cy = new_window_rect.bottom - new_window_rect.top;
3478 send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos );
3480 ret = TRUE;
3481 done:
3482 SetThreadDpiAwarenessContext( context );
3483 return ret;
3486 /*******************************************************************
3487 * NtUserSetWindowPos (win32u.@)
3489 BOOL WINAPI NtUserSetWindowPos( HWND hwnd, HWND after, INT x, INT y, INT cx, INT cy, UINT flags )
3491 WINDOWPOS winpos;
3493 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n", hwnd, after, x, y, cx, cy, flags );
3494 if(TRACE_ON(win)) dump_winpos_flags(flags);
3496 if (is_broadcast( hwnd ))
3498 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3499 return FALSE;
3502 winpos.hwnd = get_full_window_handle( hwnd );
3503 winpos.hwndInsertAfter = get_full_window_handle( after );
3504 winpos.x = x;
3505 winpos.y = y;
3506 winpos.cx = cx;
3507 winpos.cy = cy;
3508 winpos.flags = flags;
3510 map_dpi_winpos( &winpos );
3512 if (is_current_thread_window( hwnd ))
3513 return set_window_pos( &winpos, 0, 0 );
3515 if (flags & SWP_ASYNCWINDOWPOS)
3516 return NtUserMessageCall( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos,
3517 0, NtUserSendNotifyMessage, FALSE );
3518 else
3519 return send_message( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos );
3522 typedef struct
3524 struct user_object obj;
3525 INT count;
3526 INT suggested_count;
3527 HWND parent;
3528 WINDOWPOS *winpos;
3529 } DWP;
3531 /* see BeginDeferWindowPos */
3532 HDWP begin_defer_window_pos( INT count )
3534 HDWP handle = 0;
3535 DWP *dwp;
3537 TRACE( "%d\n", count );
3539 if (count < 0)
3541 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3542 return 0;
3544 /* Windows allows zero count, in which case it allocates context for 8 moves */
3545 if (count == 0) count = 8;
3547 if (!(dwp = malloc( sizeof(DWP) ))) return 0;
3549 dwp->count = 0;
3550 dwp->parent = 0;
3551 dwp->suggested_count = count;
3553 if (!(dwp->winpos = malloc( count * sizeof(WINDOWPOS) )) ||
3554 !(handle = alloc_user_handle( &dwp->obj, NTUSER_OBJ_WINPOS )))
3556 free( dwp->winpos );
3557 free( dwp );
3560 TRACE( "returning %p\n", handle );
3561 return handle;
3564 /***********************************************************************
3565 * NtUserDeferWindowPosAndBand (win32u.@)
3567 HDWP WINAPI NtUserDeferWindowPosAndBand( HDWP hdwp, HWND hwnd, HWND after,
3568 INT x, INT y, INT cx, INT cy,
3569 UINT flags, UINT unk1, UINT unk2 )
3571 HDWP retvalue = hdwp;
3572 WINDOWPOS winpos;
3573 DWP *dwp;
3574 int i;
3576 TRACE( "hdwp %p, hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3577 hdwp, hwnd, after, x, y, cx, cy, flags );
3579 winpos.hwnd = get_full_window_handle( hwnd );
3580 if (is_desktop_window( winpos.hwnd ) || !is_window( winpos.hwnd ))
3582 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
3583 return 0;
3586 winpos.hwndInsertAfter = get_full_window_handle( after );
3587 winpos.flags = flags;
3588 winpos.x = x;
3589 winpos.y = y;
3590 winpos.cx = cx;
3591 winpos.cy = cy;
3592 map_dpi_winpos( &winpos );
3594 if (!(dwp = get_user_handle_ptr( hdwp, NTUSER_OBJ_WINPOS ))) return 0;
3595 if (dwp == OBJ_OTHER_PROCESS)
3597 FIXME( "other process handle %p\n", hdwp );
3598 return 0;
3601 for (i = 0; i < dwp->count; i++)
3603 if (dwp->winpos[i].hwnd == winpos.hwnd)
3605 /* Merge with the other changes */
3606 if (!(flags & SWP_NOZORDER))
3608 dwp->winpos[i].hwndInsertAfter = winpos.hwndInsertAfter;
3610 if (!(flags & SWP_NOMOVE))
3612 dwp->winpos[i].x = winpos.x;
3613 dwp->winpos[i].y = winpos.y;
3615 if (!(flags & SWP_NOSIZE))
3617 dwp->winpos[i].cx = winpos.cx;
3618 dwp->winpos[i].cy = winpos.cy;
3620 dwp->winpos[i].flags &= flags | ~(SWP_NOSIZE | SWP_NOMOVE |
3621 SWP_NOZORDER | SWP_NOREDRAW |
3622 SWP_NOACTIVATE | SWP_NOCOPYBITS|
3623 SWP_NOOWNERZORDER);
3624 dwp->winpos[i].flags |= flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW |
3625 SWP_FRAMECHANGED);
3626 goto done;
3629 if (dwp->count >= dwp->suggested_count)
3631 WINDOWPOS *newpos = realloc( dwp->winpos, dwp->suggested_count * 2 * sizeof(WINDOWPOS) );
3632 if (!newpos)
3634 retvalue = 0;
3635 goto done;
3637 dwp->suggested_count *= 2;
3638 dwp->winpos = newpos;
3640 dwp->winpos[dwp->count++] = winpos;
3641 done:
3642 release_user_handle_ptr( dwp );
3643 return retvalue;
3646 /***********************************************************************
3647 * NtUserEndDeferWindowPosEx (win32u.@)
3649 BOOL WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async )
3651 WINDOWPOS *winpos;
3652 DWP *dwp;
3653 int i;
3655 TRACE( "%p\n", hdwp );
3657 if (async) FIXME( "async not supported\n" );
3659 if (!(dwp = free_user_handle( hdwp, NTUSER_OBJ_WINPOS ))) return FALSE;
3660 if (dwp == OBJ_OTHER_PROCESS)
3662 FIXME( "other process handle %p\n", hdwp );
3663 return FALSE;
3666 for (i = 0, winpos = dwp->winpos; i < dwp->count; i++, winpos++)
3668 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3669 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3670 winpos->cx, winpos->cy, winpos->flags );
3672 if (is_current_thread_window( winpos->hwnd ))
3673 set_window_pos( winpos, 0, 0 );
3674 else
3675 send_message( winpos->hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)winpos );
3677 free( dwp->winpos );
3678 free( dwp );
3679 return TRUE;
3682 /***********************************************************************
3683 * NtUserSetInternalWindowPos (win32u.@)
3685 void WINAPI NtUserSetInternalWindowPos( HWND hwnd, UINT cmd, RECT *rect, POINT *pt )
3687 WINDOWPLACEMENT wndpl;
3688 UINT flags;
3690 wndpl.length = sizeof(wndpl);
3691 wndpl.showCmd = cmd;
3692 wndpl.flags = flags = 0;
3694 if (pt)
3696 flags |= PLACE_MIN;
3697 wndpl.flags |= WPF_SETMINPOSITION;
3698 wndpl.ptMinPosition = *pt;
3700 if( rect )
3702 flags |= PLACE_RECT;
3703 wndpl.rcNormalPosition = *rect;
3705 set_window_placement( hwnd, &wndpl, flags );
3708 /***********************************************************************
3709 * win_set_flags
3711 * Set the flags of a window and return the previous value.
3713 UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask )
3715 WND *win = get_win_ptr( hwnd );
3716 UINT ret;
3718 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
3719 ret = win->flags;
3720 win->flags = (ret & ~clear_mask) | set_mask;
3721 release_win_ptr( win );
3722 return ret;
3725 /*******************************************************************
3726 * can_activate_window
3728 * Check if we can activate the specified window.
3730 static BOOL can_activate_window( HWND hwnd )
3732 LONG style;
3734 if (!hwnd) return FALSE;
3735 style = get_window_long( hwnd, GWL_STYLE );
3736 if (!(style & WS_VISIBLE)) return FALSE;
3737 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
3738 return !(style & WS_DISABLED);
3741 /*******************************************************************
3742 * activate_other_window
3744 * Activates window other than hwnd.
3746 static void activate_other_window( HWND hwnd )
3748 HWND hwnd_to, fg;
3750 if ((get_window_long( hwnd, GWL_STYLE ) & WS_POPUP) &&
3751 (hwnd_to = get_window_relative( hwnd, GW_OWNER )))
3753 hwnd_to = NtUserGetAncestor( hwnd_to, GA_ROOT );
3754 if (can_activate_window( hwnd_to )) goto done;
3757 hwnd_to = hwnd;
3758 for (;;)
3760 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3761 if (can_activate_window( hwnd_to )) goto done;
3764 hwnd_to = get_window_relative( get_desktop_window(), GW_CHILD );
3765 for (;;)
3767 if (hwnd_to == hwnd)
3769 hwnd_to = 0;
3770 break;
3772 if (can_activate_window( hwnd_to )) goto done;
3773 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3776 done:
3777 fg = NtUserGetForegroundWindow();
3778 TRACE( "win = %p fg = %p\n", hwnd_to, fg );
3779 if (!fg || hwnd == fg)
3781 if (set_foreground_window( hwnd_to, FALSE )) return;
3783 if (NtUserSetActiveWindow( hwnd_to )) NtUserSetActiveWindow( 0 );
3786 /*******************************************************************
3787 * send_parent_notify
3789 static void send_parent_notify( HWND hwnd, UINT msg )
3791 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD &&
3792 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY))
3794 HWND parent = get_parent( hwnd );
3795 if (parent && parent != get_desktop_window())
3796 send_message( parent, WM_PARENTNOTIFY,
3797 MAKEWPARAM( msg, get_window_long( hwnd, GWLP_ID )), (LPARAM)hwnd );
3801 /*******************************************************************
3802 * get_min_max_info
3804 * Get the minimized and maximized information for a window.
3806 MINMAXINFO get_min_max_info( HWND hwnd )
3808 LONG style = get_window_long( hwnd, GWL_STYLE );
3809 LONG exstyle = get_window_long( hwnd, GWL_EXSTYLE );
3810 DPI_AWARENESS_CONTEXT context;
3811 RECT rc_work, rc_primary;
3812 LONG adjusted_style;
3813 MINMAXINFO minmax;
3814 INT xinc, yinc;
3815 RECT rc;
3816 WND *win;
3818 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
3820 /* Compute default values */
3822 get_window_rect( hwnd, &rc, get_thread_dpi() );
3823 minmax.ptReserved.x = rc.left;
3824 minmax.ptReserved.y = rc.top;
3826 if ((style & WS_CAPTION) == WS_CAPTION)
3827 adjusted_style = style & ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */
3828 else
3829 adjusted_style = style;
3831 get_client_rect( NtUserGetAncestor( hwnd, GA_PARENT ), &rc );
3832 AdjustWindowRectEx( &rc, adjusted_style, (style & WS_POPUP) && get_menu( hwnd ), exstyle );
3834 xinc = -rc.left;
3835 yinc = -rc.top;
3837 minmax.ptMaxSize.x = rc.right - rc.left;
3838 minmax.ptMaxSize.y = rc.bottom - rc.top;
3839 if (style & (WS_DLGFRAME | WS_BORDER))
3841 minmax.ptMinTrackSize.x = get_system_metrics( SM_CXMINTRACK );
3842 minmax.ptMinTrackSize.y = get_system_metrics( SM_CYMINTRACK );
3844 else
3846 minmax.ptMinTrackSize.x = 2 * xinc;
3847 minmax.ptMinTrackSize.y = 2 * yinc;
3849 minmax.ptMaxTrackSize.x = get_system_metrics( SM_CXMAXTRACK );
3850 minmax.ptMaxTrackSize.y = get_system_metrics( SM_CYMAXTRACK );
3851 minmax.ptMaxPosition.x = -xinc;
3852 minmax.ptMaxPosition.y = -yinc;
3854 if ((win = get_win_ptr( hwnd )) && win != WND_DESKTOP && win != WND_OTHER_PROCESS)
3856 if (!empty_point( win->max_pos )) minmax.ptMaxPosition = win->max_pos;
3857 release_win_ptr( win );
3860 send_message( hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax );
3862 /* if the app didn't change the values, adapt them for the current monitor */
3864 if (get_work_rect( hwnd, &rc_work ))
3866 rc_primary = get_primary_monitor_rect( get_thread_dpi() );
3867 if (minmax.ptMaxSize.x == (rc_primary.right - rc_primary.left) + 2 * xinc &&
3868 minmax.ptMaxSize.y == (rc_primary.bottom - rc_primary.top) + 2 * yinc)
3870 minmax.ptMaxSize.x = (rc_work.right - rc_work.left) + 2 * xinc;
3871 minmax.ptMaxSize.y = (rc_work.bottom - rc_work.top) + 2 * yinc;
3873 if (minmax.ptMaxPosition.x == -xinc && minmax.ptMaxPosition.y == -yinc)
3875 minmax.ptMaxPosition.x = rc_work.left - xinc;
3876 minmax.ptMaxPosition.y = rc_work.top - yinc;
3880 TRACE( "%d %d / %d %d / %d %d / %d %d\n",
3881 (int)minmax.ptMaxSize.x, (int)minmax.ptMaxSize.y,
3882 (int)minmax.ptMaxPosition.x, (int)minmax.ptMaxPosition.y,
3883 (int)minmax.ptMaxTrackSize.x, (int)minmax.ptMaxTrackSize.y,
3884 (int)minmax.ptMinTrackSize.x, (int)minmax.ptMinTrackSize.y );
3886 minmax.ptMaxTrackSize.x = max( minmax.ptMaxTrackSize.x, minmax.ptMinTrackSize.x );
3887 minmax.ptMaxTrackSize.y = max( minmax.ptMaxTrackSize.y, minmax.ptMinTrackSize.y );
3889 SetThreadDpiAwarenessContext( context );
3890 return minmax;
3893 static POINT get_first_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
3894 int width, int height )
3896 POINT ret;
3898 if (mm->iArrange & ARW_STARTRIGHT)
3899 ret.x = parent->right - mm->iHorzGap - width;
3900 else
3901 ret.x = parent->left + mm->iHorzGap;
3902 if (mm->iArrange & ARW_STARTTOP)
3903 ret.y = parent->top + mm->iVertGap;
3904 else
3905 ret.y = parent->bottom - mm->iVertGap - height;
3907 return ret;
3910 static void get_next_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
3911 int width, int height, POINT *pos )
3913 BOOL next;
3915 if (mm->iArrange & ARW_UP) /* == ARW_DOWN */
3917 if (mm->iArrange & ARW_STARTTOP)
3919 pos->y += height + mm->iVertGap;
3920 if ((next = pos->y + height > parent->bottom))
3921 pos->y = parent->top + mm->iVertGap;
3923 else
3925 pos->y -= height + mm->iVertGap;
3926 if ((next = pos->y < parent->top))
3927 pos->y = parent->bottom - mm->iVertGap - height;
3930 if (next)
3932 if (mm->iArrange & ARW_STARTRIGHT)
3933 pos->x -= width + mm->iHorzGap;
3934 else
3935 pos->x += width + mm->iHorzGap;
3938 else
3940 if (mm->iArrange & ARW_STARTRIGHT)
3942 pos->x -= width + mm->iHorzGap;
3943 if ((next = pos->x < parent->left))
3944 pos->x = parent->right - mm->iHorzGap - width;
3946 else
3948 pos->x += width + mm->iHorzGap;
3949 if ((next = pos->x + width > parent->right))
3950 pos->x = parent->left + mm->iHorzGap;
3953 if (next)
3955 if (mm->iArrange & ARW_STARTTOP)
3956 pos->y += height + mm->iVertGap;
3957 else
3958 pos->y -= height + mm->iVertGap;
3963 static POINT get_minimized_pos( HWND hwnd, POINT pt )
3965 RECT rect, parent_rect;
3966 HWND parent, child;
3967 HRGN hrgn, tmp;
3968 MINIMIZEDMETRICS metrics;
3969 int width, height;
3971 parent = NtUserGetAncestor( hwnd, GA_PARENT );
3972 if (parent == get_desktop_window())
3974 MONITORINFO mon_info;
3975 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
3977 mon_info.cbSize = sizeof( mon_info );
3978 get_monitor_info( monitor, &mon_info );
3979 parent_rect = mon_info.rcWork;
3981 else get_client_rect( parent, &parent_rect );
3983 if (pt.x >= parent_rect.left && (pt.x + get_system_metrics( SM_CXMINIMIZED ) < parent_rect.right) &&
3984 pt.y >= parent_rect.top && (pt.y + get_system_metrics( SM_CYMINIMIZED ) < parent_rect.bottom))
3985 return pt; /* The icon already has a suitable position */
3987 width = get_system_metrics( SM_CXMINIMIZED );
3988 height = get_system_metrics( SM_CYMINIMIZED );
3990 metrics.cbSize = sizeof(metrics);
3991 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
3993 /* Check if another icon already occupies this spot */
3994 /* FIXME: this is completely inefficient */
3996 hrgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
3997 tmp = NtGdiCreateRectRgn( 0, 0, 0, 0 );
3998 for (child = get_window_relative( parent, GW_CHILD );
3999 child;
4000 child = get_window_relative( child, GW_HWNDNEXT ))
4002 if (child == hwnd) continue;
4003 if ((get_window_long( child, GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != (WS_VISIBLE|WS_MINIMIZE))
4004 continue;
4005 if (get_window_rects( child, COORDS_PARENT, &rect, NULL, get_thread_dpi() ))
4007 NtGdiSetRectRgn( tmp, rect.left, rect.top, rect.right, rect.bottom );
4008 NtGdiCombineRgn( hrgn, hrgn, tmp, RGN_OR );
4011 NtGdiDeleteObjectApp( tmp );
4013 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
4014 for (;;)
4016 SetRect( &rect, pt.x, pt.y, pt.x + width, pt.y + height );
4017 if (!NtGdiRectInRegion( hrgn, &rect ))
4018 break;
4020 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
4023 NtGdiDeleteObjectApp( hrgn );
4024 return pt;
4027 /***********************************************************************
4028 * window_min_maximize
4030 static UINT window_min_maximize( HWND hwnd, UINT cmd, RECT *rect )
4032 UINT swp_flags = 0;
4033 LONG old_style;
4034 MINMAXINFO minmax;
4035 WINDOWPLACEMENT wpl;
4037 TRACE( "%p %u\n", hwnd, cmd );
4039 wpl.length = sizeof(wpl);
4040 NtUserGetWindowPlacement( hwnd, &wpl );
4042 if (call_hooks( WH_CBT, HCBT_MINMAX, (WPARAM)hwnd, cmd, 0 ))
4043 return SWP_NOSIZE | SWP_NOMOVE;
4045 if (is_iconic( hwnd ))
4047 switch (cmd)
4049 case SW_SHOWMINNOACTIVE:
4050 case SW_SHOWMINIMIZED:
4051 case SW_FORCEMINIMIZE:
4052 case SW_MINIMIZE:
4053 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
4055 SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
4056 wpl.ptMinPosition.x + get_system_metrics( SM_CXMINIMIZED ),
4057 wpl.ptMinPosition.y + get_system_metrics( SM_CYMINIMIZED ));
4058 return SWP_NOSIZE | SWP_NOMOVE;
4060 if (!send_message( hwnd, WM_QUERYOPEN, 0, 0 )) return SWP_NOSIZE | SWP_NOMOVE;
4061 swp_flags |= SWP_NOCOPYBITS;
4064 switch( cmd )
4066 case SW_SHOWMINNOACTIVE:
4067 case SW_SHOWMINIMIZED:
4068 case SW_FORCEMINIMIZE:
4069 case SW_MINIMIZE:
4070 if (is_zoomed( hwnd )) win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
4071 else win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
4073 if (get_focus() == hwnd)
4075 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
4076 NtUserSetFocus( NtUserGetAncestor( hwnd, GA_PARENT ));
4077 else
4078 NtUserSetFocus( 0 );
4081 old_style = set_window_style( hwnd, WS_MINIMIZE, WS_MAXIMIZE );
4083 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
4085 if (!(old_style & WS_MINIMIZE)) swp_flags |= SWP_STATECHANGED;
4086 SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
4087 wpl.ptMinPosition.x + get_system_metrics(SM_CXMINIMIZED),
4088 wpl.ptMinPosition.y + get_system_metrics(SM_CYMINIMIZED) );
4089 swp_flags |= SWP_NOCOPYBITS;
4090 break;
4092 case SW_MAXIMIZE:
4093 old_style = get_window_long( hwnd, GWL_STYLE );
4094 if ((old_style & WS_MAXIMIZE) && (old_style & WS_VISIBLE)) return SWP_NOSIZE | SWP_NOMOVE;
4096 minmax = get_min_max_info( hwnd );
4098 old_style = set_window_style( hwnd, WS_MAXIMIZE, WS_MINIMIZE );
4099 if (old_style & WS_MINIMIZE)
4100 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
4102 if (!(old_style & WS_MAXIMIZE)) swp_flags |= SWP_STATECHANGED;
4103 SetRect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
4104 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
4105 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
4106 break;
4108 case SW_SHOWNOACTIVATE:
4109 win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
4110 /* fall through */
4111 case SW_SHOWNORMAL:
4112 case SW_RESTORE:
4113 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
4114 old_style = set_window_style( hwnd, 0, WS_MINIMIZE | WS_MAXIMIZE );
4115 if (old_style & WS_MINIMIZE)
4117 if (win_get_flags( hwnd ) & WIN_RESTORE_MAX)
4119 /* Restore to maximized position */
4120 minmax = get_min_max_info( hwnd );
4121 set_window_style( hwnd, WS_MAXIMIZE, 0 );
4122 swp_flags |= SWP_STATECHANGED;
4123 SetRect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
4124 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
4125 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
4126 break;
4129 else if (!(old_style & WS_MAXIMIZE)) break;
4131 swp_flags |= SWP_STATECHANGED;
4133 /* Restore to normal position */
4135 *rect = wpl.rcNormalPosition;
4136 break;
4139 return swp_flags;
4142 /* see ArrangeIconicWindows */
4143 static UINT arrange_iconic_windows( HWND parent )
4145 int width, height, count = 0;
4146 MINIMIZEDMETRICS metrics;
4147 RECT parent_rect;
4148 HWND child;
4149 POINT pt;
4151 metrics.cbSize = sizeof(metrics);
4152 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
4153 width = get_system_metrics( SM_CXMINIMIZED );
4154 height = get_system_metrics( SM_CYMINIMIZED );
4156 if (parent == get_desktop_window())
4158 MONITORINFO mon_info;
4159 HMONITOR monitor = monitor_from_window( 0, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4161 mon_info.cbSize = sizeof( mon_info );
4162 get_monitor_info( monitor, &mon_info );
4163 parent_rect = mon_info.rcWork;
4165 else get_client_rect( parent, &parent_rect );
4167 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
4169 child = get_window_relative( parent, GW_CHILD );
4170 while (child)
4172 if (is_iconic( child ))
4174 NtUserSetWindowPos( child, 0, pt.x, pt.y, 0, 0,
4175 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
4176 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
4177 count++;
4179 child = get_window_relative( child, GW_HWNDNEXT );
4181 return count;
4184 /*******************************************************************
4185 * update_window_state
4187 * Trigger an update of the window's driver state and surface.
4189 void update_window_state( HWND hwnd )
4191 DPI_AWARENESS_CONTEXT context;
4192 RECT window_rect, client_rect, valid_rects[2];
4194 if (!is_current_thread_window( hwnd ))
4196 NtUserPostMessage( hwnd, WM_WINE_UPDATEWINDOWSTATE, 0, 0 );
4197 return;
4200 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
4201 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
4202 valid_rects[0] = valid_rects[1] = client_rect;
4203 apply_window_pos( hwnd, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE |
4204 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW,
4205 &window_rect, &client_rect, valid_rects );
4206 SetThreadDpiAwarenessContext( context );
4209 /***********************************************************************
4210 * show_window
4212 * Implementation of ShowWindow and ShowWindowAsync.
4214 static BOOL show_window( HWND hwnd, INT cmd )
4216 WND *win;
4217 HWND parent;
4218 DPI_AWARENESS_CONTEXT context;
4219 LONG style = get_window_long( hwnd, GWL_STYLE );
4220 BOOL was_visible = (style & WS_VISIBLE) != 0;
4221 BOOL show_flag = TRUE;
4222 RECT newPos = {0, 0, 0, 0};
4223 UINT new_swp, swp = 0;
4225 TRACE( "hwnd=%p, cmd=%d, was_visible %d\n", hwnd, cmd, was_visible );
4227 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
4229 switch(cmd)
4231 case SW_HIDE:
4232 if (!was_visible) goto done;
4233 show_flag = FALSE;
4234 swp |= SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4235 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4236 break;
4238 case SW_SHOWMINNOACTIVE:
4239 case SW_MINIMIZE:
4240 case SW_FORCEMINIMIZE: /* FIXME: Does not work if thread is hung. */
4241 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4242 /* fall through */
4243 case SW_SHOWMINIMIZED:
4244 swp |= SWP_SHOWWINDOW | SWP_FRAMECHANGED;
4245 swp |= window_min_maximize( hwnd, cmd, &newPos );
4246 if ((style & WS_MINIMIZE) && was_visible) goto done;
4247 break;
4249 case SW_SHOWMAXIMIZED: /* same as SW_MAXIMIZE */
4250 if (!was_visible) swp |= SWP_SHOWWINDOW;
4251 swp |= SWP_FRAMECHANGED;
4252 swp |= window_min_maximize( hwnd, SW_MAXIMIZE, &newPos );
4253 if ((style & WS_MAXIMIZE) && was_visible) goto done;
4254 break;
4256 case SW_SHOWNA:
4257 swp |= SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4258 if (style & WS_CHILD) swp |= SWP_NOZORDER;
4259 break;
4261 case SW_SHOW:
4262 if (was_visible) goto done;
4263 swp |= SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
4264 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4265 break;
4267 case SW_SHOWNOACTIVATE:
4268 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4269 /* fall through */
4270 case SW_RESTORE:
4271 /* fall through */
4272 case SW_SHOWNORMAL: /* same as SW_NORMAL: */
4273 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
4274 if (!was_visible) swp |= SWP_SHOWWINDOW;
4275 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
4277 swp |= SWP_FRAMECHANGED;
4278 swp |= window_min_maximize( hwnd, cmd, &newPos );
4280 else
4282 if (was_visible) goto done;
4283 swp |= SWP_NOSIZE | SWP_NOMOVE;
4285 if (style & WS_CHILD && !(swp & SWP_STATECHANGED)) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
4286 break;
4288 default:
4289 goto done;
4292 if ((show_flag != was_visible || cmd == SW_SHOWNA) && cmd != SW_SHOWMAXIMIZED && !(swp & SWP_STATECHANGED))
4294 send_message( hwnd, WM_SHOWWINDOW, show_flag, 0 );
4295 if (!is_window( hwnd )) goto done;
4298 if (IsRectEmpty( &newPos )) new_swp = swp;
4299 else if ((new_swp = user_driver->pShowWindow( hwnd, cmd, &newPos, swp )) == ~0)
4301 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) new_swp = swp;
4302 else if (is_iconic( hwnd ) && (newPos.left != -32000 || newPos.top != -32000))
4304 OffsetRect( &newPos, -32000 - newPos.left, -32000 - newPos.top );
4305 new_swp = swp & ~(SWP_NOMOVE | SWP_NOCLIENTMOVE);
4307 else new_swp = swp;
4309 swp = new_swp;
4311 parent = NtUserGetAncestor( hwnd, GA_PARENT );
4312 if (parent && !is_window_visible( parent ) && !(swp & SWP_STATECHANGED))
4314 /* if parent is not visible simply toggle WS_VISIBLE and return */
4315 if (show_flag) set_window_style( hwnd, WS_VISIBLE, 0 );
4316 else set_window_style( hwnd, 0, WS_VISIBLE );
4318 else
4319 NtUserSetWindowPos( hwnd, HWND_TOP, newPos.left, newPos.top,
4320 newPos.right - newPos.left, newPos.bottom - newPos.top, swp );
4322 if (cmd == SW_HIDE)
4324 HWND hFocus;
4326 /* FIXME: This will cause the window to be activated irrespective
4327 * of whether it is owned by the same thread. Has to be done
4328 * asynchronously.
4331 if (hwnd == get_active_window()) activate_other_window( hwnd );
4333 /* Revert focus to parent */
4334 hFocus = get_focus();
4335 if (hwnd == hFocus)
4337 HWND parent = NtUserGetAncestor(hwnd, GA_PARENT);
4338 if (parent == get_desktop_window()) parent = 0;
4339 NtUserSetFocus(parent);
4341 goto done;
4344 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) goto done;
4346 if (win->flags & WIN_NEED_SIZE)
4348 /* should happen only in CreateWindowEx() */
4349 int wParam = SIZE_RESTORED;
4350 RECT client;
4351 LPARAM lparam;
4353 get_window_rects( hwnd, COORDS_PARENT, NULL, &client, get_thread_dpi() );
4354 lparam = MAKELONG( client.right - client.left, client.bottom - client.top );
4355 win->flags &= ~WIN_NEED_SIZE;
4356 if (win->dwStyle & WS_MAXIMIZE) wParam = SIZE_MAXIMIZED;
4357 else if (win->dwStyle & WS_MINIMIZE)
4359 wParam = SIZE_MINIMIZED;
4360 lparam = 0;
4362 release_win_ptr( win );
4364 send_message( hwnd, WM_SIZE, wParam, lparam );
4365 send_message( hwnd, WM_MOVE, 0, MAKELONG( client.left, client.top ));
4367 else release_win_ptr( win );
4369 /* if previous state was minimized Windows sets focus to the window */
4370 if (style & WS_MINIMIZE)
4372 NtUserSetFocus( hwnd );
4373 /* Send a WM_ACTIVATE message for a top level window, even if the window is already active */
4374 if (NtUserGetAncestor( hwnd, GA_ROOT ) == hwnd && !(swp & SWP_NOACTIVATE))
4375 send_message( hwnd, WM_ACTIVATE, WA_ACTIVE, 0 );
4378 done:
4379 SetThreadDpiAwarenessContext( context );
4380 return was_visible;
4383 /***********************************************************************
4384 * NtUserShowWindowAsync (win32u.@)
4386 * doesn't wait; returns immediately.
4387 * used by threads to toggle windows in other (possibly hanging) threads
4389 BOOL WINAPI NtUserShowWindowAsync( HWND hwnd, INT cmd )
4391 HWND full_handle;
4393 if (is_broadcast(hwnd))
4395 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4396 return FALSE;
4399 if ((full_handle = is_current_thread_window( hwnd )))
4400 return show_window( full_handle, cmd );
4402 return NtUserMessageCall( hwnd, WM_WINE_SHOWWINDOW, cmd, 0, 0,
4403 NtUserSendNotifyMessage, FALSE );
4406 /***********************************************************************
4407 * NtUserShowWindow (win32u.@)
4409 BOOL WINAPI NtUserShowWindow( HWND hwnd, INT cmd )
4411 HWND full_handle;
4413 if (is_broadcast(hwnd))
4415 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4416 return FALSE;
4418 if ((full_handle = is_current_thread_window( hwnd )))
4419 return show_window( full_handle, cmd );
4421 if ((cmd == SW_HIDE) && !(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4422 return FALSE;
4424 if ((cmd == SW_SHOW) && (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4425 return TRUE;
4427 return send_message( hwnd, WM_WINE_SHOWWINDOW, cmd, 0 );
4430 /* see ShowOwnedPopups */
4431 BOOL show_owned_popups( HWND owner, BOOL show )
4433 int count = 0;
4434 HWND *win_array = list_window_children( 0, get_desktop_window(), NULL, 0 );
4436 if (!win_array) return TRUE;
4438 while (win_array[count]) count++;
4439 while (--count >= 0)
4441 if (get_window_relative( win_array[count], GW_OWNER ) != owner) continue;
4442 if (show)
4444 if (win_get_flags( win_array[count] ) & WIN_NEEDS_SHOW_OWNEDPOPUP)
4445 /* In Windows, ShowOwnedPopups(TRUE) generates
4446 * WM_SHOWWINDOW messages with SW_PARENTOPENING,
4447 * regardless of the state of the owner
4449 send_message( win_array[count], WM_SHOWWINDOW, SW_SHOWNORMAL, SW_PARENTOPENING );
4451 else
4453 if (get_window_long( win_array[count], GWL_STYLE ) & WS_VISIBLE)
4454 /* In Windows, ShowOwnedPopups(FALSE) generates
4455 * WM_SHOWWINDOW messages with SW_PARENTCLOSING,
4456 * regardless of the state of the owner
4458 send_message( win_array[count], WM_SHOWWINDOW, SW_HIDE, SW_PARENTCLOSING );
4462 free( win_array );
4463 return TRUE;
4466 /*******************************************************************
4467 * NtUserFlashWindowEx (win32u.@)
4469 BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info )
4471 WND *win;
4473 TRACE( "%p\n", info );
4475 if (!info)
4477 RtlSetLastWin32Error( ERROR_NOACCESS );
4478 return FALSE;
4481 if (!info->hwnd || info->cbSize != sizeof(FLASHWINFO) || !is_window( info->hwnd ))
4483 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4484 return FALSE;
4486 FIXME( "%p - semi-stub\n", info );
4488 if (is_iconic( info->hwnd ))
4490 NtUserRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME );
4492 win = get_win_ptr( info->hwnd );
4493 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4494 if (info->dwFlags && !(win->flags & WIN_NCACTIVATED))
4496 win->flags |= WIN_NCACTIVATED;
4498 else
4500 win->flags &= ~WIN_NCACTIVATED;
4502 release_win_ptr( win );
4503 user_driver->pFlashWindowEx( info );
4504 return TRUE;
4506 else
4508 WPARAM wparam;
4509 HWND hwnd = info->hwnd;
4511 win = get_win_ptr( hwnd );
4512 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4513 hwnd = win->obj.handle; /* make it a full handle */
4515 if (info->dwFlags) wparam = !(win->flags & WIN_NCACTIVATED);
4516 else wparam = (hwnd == NtUserGetForegroundWindow());
4518 release_win_ptr( win );
4519 send_message( hwnd, WM_NCACTIVATE, wparam, 0 );
4520 user_driver->pFlashWindowEx( info );
4521 return wparam;
4525 /* see GetWindowContextHelpId */
4526 DWORD get_window_context_help_id( HWND hwnd )
4528 DWORD retval;
4529 WND *win = get_win_ptr( hwnd );
4530 if (!win || win == WND_DESKTOP) return 0;
4531 if (win == WND_OTHER_PROCESS)
4533 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4534 return 0;
4536 retval = win->helpContext;
4537 release_win_ptr( win );
4538 return retval;
4541 /* see SetWindowContextHelpId */
4542 static BOOL set_window_context_help_id( HWND hwnd, DWORD id )
4544 WND *win = get_win_ptr( hwnd );
4545 if (!win || win == WND_DESKTOP) return FALSE;
4546 if (win == WND_OTHER_PROCESS)
4548 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4549 return FALSE;
4551 win->helpContext = id;
4552 release_win_ptr( win );
4553 return TRUE;
4556 /***********************************************************************
4557 * NtUserInternalGetWindowIcon (win32u.@)
4559 HICON WINAPI NtUserInternalGetWindowIcon( HWND hwnd, UINT type )
4561 WND *win = get_win_ptr( hwnd );
4562 HICON ret;
4564 TRACE( "hwnd %p, type %#x\n", hwnd, type );
4566 if (!win)
4568 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
4569 return 0;
4571 if (win == WND_OTHER_PROCESS || win == WND_DESKTOP)
4573 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4574 return 0;
4577 switch (type)
4579 case ICON_BIG:
4580 ret = win->hIcon;
4581 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
4582 break;
4584 case ICON_SMALL:
4585 case ICON_SMALL2:
4586 ret = win->hIconSmall ? win->hIconSmall : win->hIconSmall2;
4587 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICONSM, FALSE );
4588 if (!ret) ret = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
4589 break;
4591 default:
4592 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
4593 release_win_ptr( win );
4594 return 0;
4596 release_win_ptr( win );
4598 if (!ret) ret = LoadImageW( 0, (const WCHAR *)IDI_APPLICATION, IMAGE_ICON,
4599 0, 0, LR_SHARED | LR_DEFAULTSIZE );
4601 return CopyImage( ret, IMAGE_ICON, 0, 0, 0 );
4604 /***********************************************************************
4605 * send_destroy_message
4607 static void send_destroy_message( HWND hwnd )
4609 GUITHREADINFO info;
4611 info.cbSize = sizeof(info);
4612 if (NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ))
4614 if (hwnd == info.hwndCaret) destroy_caret();
4615 if (hwnd == info.hwndActive) activate_other_window( hwnd );
4618 if (hwnd == NtUserGetClipboardOwner()) release_clipboard_owner( hwnd );
4620 send_message( hwnd, WM_DESTROY, 0, 0);
4623 * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
4624 * make sure that the window still exists when we come back.
4626 if (is_window(hwnd))
4628 HWND *children;
4629 int i;
4631 if (!(children = list_window_children( 0, hwnd, NULL, 0 ))) return;
4633 for (i = 0; children[i]; i++)
4635 if (is_window( children[i] )) send_destroy_message( children[i] );
4637 free( children );
4639 else
4640 WARN( "\tdestroyed itself while in WM_DESTROY!\n" );
4643 /***********************************************************************
4644 * free_window_handle
4646 * Free a window handle.
4648 static void free_window_handle( HWND hwnd )
4650 WND *win;
4652 TRACE( "\n" );
4654 if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) && win != OBJ_OTHER_PROCESS)
4656 SERVER_START_REQ( destroy_window )
4658 req->handle = wine_server_user_handle( hwnd );
4659 wine_server_call( req );
4660 set_user_handle_ptr( hwnd, NULL );
4662 SERVER_END_REQ;
4663 user_unlock();
4664 free( win->pScroll );
4665 free( win->text );
4666 free( win );
4670 /***********************************************************************
4671 * destroy_window
4673 LRESULT destroy_window( HWND hwnd )
4675 struct window_surface *surface;
4676 HMENU menu = 0, sys_menu;
4677 WND *win;
4678 HWND *children;
4680 TRACE( "%p\n", hwnd );
4682 unregister_imm_window( hwnd );
4684 /* free child windows */
4685 if ((children = list_window_children( 0, hwnd, NULL, 0 )))
4687 int i;
4688 for (i = 0; children[i]; i++)
4690 if (is_current_thread_window( children[i] ))
4691 destroy_window( children[i] );
4692 else
4693 NtUserMessageCall( children[i], WM_WINE_DESTROYWINDOW, 0, 0,
4694 0, NtUserSendNotifyMessage, FALSE );
4696 free( children );
4699 /* Unlink now so we won't bother with the children later on */
4700 SERVER_START_REQ( set_parent )
4702 req->handle = wine_server_user_handle( hwnd );
4703 req->parent = 0;
4704 wine_server_call( req );
4706 SERVER_END_REQ;
4708 send_message( hwnd, WM_NCDESTROY, 0, 0 );
4710 /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
4712 /* free resources associated with the window */
4714 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
4715 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
4716 menu = (HMENU)win->wIDmenu;
4717 sys_menu = win->hSysMenu;
4718 free_dce( win->dce, hwnd );
4719 win->dce = NULL;
4720 NtUserDestroyCursor( win->hIconSmall2, 0 );
4721 surface = win->surface;
4722 win->surface = NULL;
4723 release_win_ptr( win );
4725 NtUserDestroyMenu( menu );
4726 NtUserDestroyMenu( sys_menu );
4727 if (surface)
4729 register_window_surface( surface, NULL );
4730 window_surface_release( surface );
4733 user_driver->pDestroyWindow( hwnd );
4735 free_window_handle( hwnd );
4736 return 0;
4739 /***********************************************************************
4740 * NtUserDestroyWindow (win32u.@)
4742 BOOL WINAPI NtUserDestroyWindow( HWND hwnd )
4744 BOOL is_child;
4746 if (!(hwnd = is_current_thread_window( hwnd )) || is_desktop_window( hwnd ))
4748 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
4749 return FALSE;
4752 TRACE( "(%p)\n", hwnd );
4754 if (call_hooks( WH_CBT, HCBT_DESTROYWND, (WPARAM)hwnd, 0, 0 )) return FALSE;
4756 if (is_menu_active() == hwnd) NtUserEndMenu();
4758 is_child = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) != 0;
4760 if (is_child)
4762 if (!is_exiting_thread( GetCurrentThreadId() ))
4763 send_parent_notify( hwnd, WM_DESTROY );
4765 else if (!get_window_relative( hwnd, GW_OWNER ))
4767 call_hooks( WH_SHELL, HSHELL_WINDOWDESTROYED, (WPARAM)hwnd, 0, 0 );
4768 /* FIXME: clean up palette - see "Internals" p.352 */
4771 if (!is_window( hwnd )) return TRUE;
4773 /* Hide the window */
4774 if (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)
4776 /* Only child windows receive WM_SHOWWINDOW in DestroyWindow() */
4777 if (is_child)
4778 NtUserShowWindow( hwnd, SW_HIDE );
4779 else
4780 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
4781 SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW );
4784 if (!is_window( hwnd )) return TRUE;
4786 /* Recursively destroy child windows */
4787 if (!is_child)
4789 for (;;)
4791 BOOL got_one = FALSE;
4792 HWND *children;
4793 unsigned int i;
4795 if (!(children = list_window_children( 0, get_desktop_window(), NULL, 0 ))) break;
4797 for (i = 0; children[i]; i++)
4799 if (get_window_relative( children[i], GW_OWNER ) != hwnd) continue;
4800 if (is_current_thread_window( children[i] ))
4802 NtUserDestroyWindow( children[i] );
4803 got_one = TRUE;
4804 continue;
4806 set_window_owner( children[i], 0 );
4808 free( children );
4809 if (!got_one) break;
4813 send_destroy_message( hwnd );
4814 if (!is_window( hwnd )) return TRUE;
4816 destroy_window( hwnd );
4817 return TRUE;
4820 /*****************************************************************************
4821 * destroy_thread_windows
4823 * Destroy all window owned by the current thread.
4825 void destroy_thread_windows(void)
4827 WND *win, *free_list = NULL;
4828 HWND hwnd = 0;
4830 user_lock();
4831 while ((win = next_thread_window_ptr( &hwnd )))
4833 free_dce( win->dce, win->obj.handle );
4834 set_user_handle_ptr( hwnd, NULL );
4835 win->obj.handle = free_list;
4836 free_list = win;
4838 if (free_list)
4840 SERVER_START_REQ( destroy_window )
4842 req->handle = 0; /* destroy all thread windows */
4843 wine_server_call( req );
4845 SERVER_END_REQ;
4847 user_unlock();
4849 while ((win = free_list))
4851 free_list = win->obj.handle;
4852 TRACE( "destroying %p\n", win );
4854 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD && win->wIDmenu)
4855 NtUserDestroyMenu( UlongToHandle(win->wIDmenu) );
4856 if (win->hSysMenu) NtUserDestroyMenu( win->hSysMenu );
4857 if (win->surface)
4859 register_window_surface( win->surface, NULL );
4860 window_surface_release( win->surface );
4862 free( win->pScroll );
4863 free( win->text );
4864 free( win );
4868 /***********************************************************************
4869 * create_window_handle
4871 * Create a window handle with the server.
4873 static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
4874 HINSTANCE instance, BOOL ansi,
4875 DWORD style, DWORD ex_style )
4877 DPI_AWARENESS awareness = get_thread_dpi_awareness();
4878 HWND handle = 0, full_parent = 0, full_owner = 0;
4879 struct tagCLASS *class = NULL;
4880 int extra_bytes = 0;
4881 UINT dpi = 0;
4882 WND *win;
4884 SERVER_START_REQ( create_window )
4886 req->parent = wine_server_user_handle( parent );
4887 req->owner = wine_server_user_handle( owner );
4888 req->instance = wine_server_client_ptr( instance );
4889 req->dpi = get_system_dpi();
4890 req->awareness = awareness;
4891 req->style = style;
4892 req->ex_style = ex_style;
4893 if (!(req->atom = get_int_atom_value( name )) && name->Length)
4894 wine_server_add_data( req, name->Buffer, name->Length );
4895 if (!wine_server_call_err( req ))
4897 handle = wine_server_ptr_handle( reply->handle );
4898 full_parent = wine_server_ptr_handle( reply->parent );
4899 full_owner = wine_server_ptr_handle( reply->owner );
4900 extra_bytes = reply->extra;
4901 dpi = reply->dpi;
4902 awareness = reply->awareness;
4903 class = wine_server_get_ptr( reply->class_ptr );
4906 SERVER_END_REQ;
4908 if (!handle)
4910 WARN( "error %d creating window\n", (int)RtlGetLastWin32Error() );
4911 return NULL;
4914 if (!(win = calloc( 1, FIELD_OFFSET(WND, wExtra) + extra_bytes )))
4916 SERVER_START_REQ( destroy_window )
4918 req->handle = wine_server_user_handle( handle );
4919 wine_server_call( req );
4921 SERVER_END_REQ;
4922 RtlSetLastWin32Error( ERROR_NOT_ENOUGH_MEMORY );
4923 return NULL;
4926 if (!parent) /* if parent is 0 we don't have a desktop window yet */
4928 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
4930 if (name->Buffer == (const WCHAR *)DESKTOP_CLASS_ATOM)
4932 if (!thread_info->top_window)
4933 thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle );
4934 else assert( full_parent == UlongToHandle( thread_info->top_window ));
4935 if (full_parent &&
4936 !user_driver->pCreateDesktopWindow( UlongToHandle( thread_info->top_window )))
4937 ERR( "failed to create desktop window\n" );
4938 register_builtin_classes();
4940 else /* HWND_MESSAGE parent */
4942 if (!thread_info->msg_window && !full_parent)
4943 thread_info->msg_window = HandleToUlong( handle );
4947 user_lock();
4949 win->obj.handle = handle;
4950 win->obj.type = NTUSER_OBJ_WINDOW;
4951 win->parent = full_parent;
4952 win->owner = full_owner;
4953 win->class = class;
4954 win->winproc = get_class_winproc( class );
4955 win->cbWndExtra = extra_bytes;
4956 win->dpi = dpi;
4957 win->dpi_awareness = awareness;
4958 set_user_handle_ptr( handle, &win->obj );
4959 if (is_winproc_unicode( win->winproc, !ansi )) win->flags |= WIN_ISUNICODE;
4960 return win;
4963 static BOOL is_default_coord( int x )
4965 return x == CW_USEDEFAULT || x == 0x8000;
4968 /***********************************************************************
4969 * fix_cs_coordinates
4971 * Fix the coordinates and return default show mode in sw.
4973 static void fix_cs_coordinates( CREATESTRUCTW *cs, INT *sw )
4975 if (cs->style & (WS_CHILD | WS_POPUP))
4977 if (is_default_coord(cs->x)) cs->x = cs->y = 0;
4978 if (is_default_coord(cs->cx)) cs->cx = cs->cy = 0;
4980 else /* overlapped window */
4982 RTL_USER_PROCESS_PARAMETERS *params = NtCurrentTeb()->Peb->ProcessParameters;
4983 HMONITOR monitor;
4984 MONITORINFO mon_info;
4986 if (!is_default_coord( cs->x ) && !is_default_coord( cs->cx ) && !is_default_coord( cs->cy ))
4987 return;
4989 monitor = monitor_from_window( cs->hwndParent, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4990 mon_info.cbSize = sizeof(mon_info);
4991 get_monitor_info( monitor, &mon_info );
4993 if (is_default_coord( cs->x ))
4995 if (!is_default_coord( cs->y )) *sw = cs->y;
4996 cs->x = (params->dwFlags & STARTF_USEPOSITION) ? params->dwX : mon_info.rcWork.left;
4997 cs->y = (params->dwFlags & STARTF_USEPOSITION) ? params->dwY : mon_info.rcWork.top;
5000 if (is_default_coord( cs->cx ))
5002 if (params->dwFlags & STARTF_USESIZE)
5004 cs->cx = params->dwXSize;
5005 cs->cy = params->dwYSize;
5007 else
5009 cs->cx = (mon_info.rcWork.right - mon_info.rcWork.left) * 3 / 4 - cs->x;
5010 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
5013 /* neither x nor cx are default. Check the y values.
5014 * In the trace we see Outlook and Outlook Express using
5015 * cy set to CW_USEDEFAULT when opening the address book.
5017 else if (is_default_coord( cs->cy ))
5019 FIXME( "Strange use of CW_USEDEFAULT in cy\n" );
5020 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
5025 /***********************************************************************
5026 * map_dpi_create_struct
5028 static void map_dpi_create_struct( CREATESTRUCTW *cs, UINT dpi_from, UINT dpi_to )
5030 if (!dpi_from && !dpi_to) return;
5031 if (!dpi_from || !dpi_to)
5033 POINT pt = { cs->x, cs->y };
5034 UINT mon_dpi = get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTONEAREST, dpi_from ));
5035 if (!dpi_from) dpi_from = mon_dpi;
5036 else dpi_to = mon_dpi;
5038 if (dpi_from == dpi_to) return;
5039 cs->x = muldiv( cs->x, dpi_to, dpi_from );
5040 cs->y = muldiv( cs->y, dpi_to, dpi_from );
5041 cs->cx = muldiv( cs->cx, dpi_to, dpi_from );
5042 cs->cy = muldiv( cs->cy, dpi_to, dpi_from );
5045 /***********************************************************************
5046 * NtUserCreateWindowEx (win32u.@)
5048 HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
5049 UNICODE_STRING *version, UNICODE_STRING *window_name,
5050 DWORD style, INT x, INT y, INT cx, INT cy,
5051 HWND parent, HMENU menu, HINSTANCE instance, void *params,
5052 DWORD flags, HINSTANCE client_instance, DWORD unk, BOOL ansi )
5054 UINT win_dpi, thread_dpi = get_thread_dpi();
5055 DPI_AWARENESS_CONTEXT context;
5056 CBT_CREATEWNDW cbtc;
5057 HWND hwnd, owner = 0;
5058 CREATESTRUCTW cs;
5059 INT sw = SW_SHOW;
5060 RECT rect;
5061 WND *win;
5063 static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
5065 cs.lpCreateParams = params;
5066 cs.hInstance = client_instance ? client_instance : instance;
5067 cs.hMenu = menu;
5068 cs.hwndParent = parent;
5069 cs.style = style;
5070 cs.dwExStyle = ex_style;
5071 cs.lpszName = window_name ? window_name->Buffer : NULL;
5072 cs.lpszClass = class_name ? class_name->Buffer : NULL;
5073 cs.x = x;
5074 cs.y = y;
5075 cs.cx = cx;
5076 cs.cy = cy;
5078 /* Find the parent window */
5079 if (parent == HWND_MESSAGE)
5081 cs.hwndParent = parent = get_hwnd_message_parent();
5083 else if (parent)
5085 if ((cs.style & (WS_CHILD|WS_POPUP)) != WS_CHILD)
5087 owner = parent;
5088 parent = get_desktop_window();
5090 else
5092 DWORD parent_style = get_window_long( parent, GWL_EXSTYLE );
5093 if ((parent_style & WS_EX_LAYOUTRTL) && !(parent_style & WS_EX_NOINHERITLAYOUT))
5094 cs.dwExStyle |= WS_EX_LAYOUTRTL;
5097 else
5099 if ((cs.style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
5101 WARN( "No parent for child window\n" );
5102 RtlSetLastWin32Error( ERROR_TLW_WITH_WSCHILD );
5103 return 0; /* WS_CHILD needs a parent, but WS_POPUP doesn't */
5106 /* are we creating the desktop or HWND_MESSAGE parent itself? */
5107 if (class_name->Buffer != (LPCWSTR)DESKTOP_CLASS_ATOM &&
5108 (class_name->Length != sizeof(messageW) ||
5109 wcsnicmp( class_name->Buffer, messageW, ARRAYSIZE(messageW) )))
5111 if (get_process_layout() & LAYOUT_RTL) cs.dwExStyle |= WS_EX_LAYOUTRTL;
5112 parent = get_desktop_window();
5116 fix_cs_coordinates( &cs, &sw );
5117 cs.dwExStyle = fix_exstyle( cs.style, cs.dwExStyle );
5119 /* Create the window structure */
5121 style = cs.style & ~WS_VISIBLE;
5122 ex_style = cs.dwExStyle & ~WS_EX_LAYERED;
5123 if (!(win = create_window_handle( parent, owner, class_name, instance, ansi, style, ex_style )))
5124 return 0;
5125 hwnd = win->obj.handle;
5127 /* Fill the window structure */
5129 win->tid = GetCurrentThreadId();
5130 win->hInstance = cs.hInstance;
5131 win->text = NULL;
5132 win->dwStyle = style;
5133 win->dwExStyle = ex_style;
5134 win->wIDmenu = 0;
5135 win->helpContext = 0;
5136 win->pScroll = NULL;
5137 win->userdata = 0;
5138 win->hIcon = 0;
5139 win->hIconSmall = 0;
5140 win->hIconSmall2 = 0;
5141 win->hSysMenu = 0;
5143 win->min_pos.x = win->min_pos.y = -1;
5144 win->max_pos.x = win->max_pos.y = -1;
5145 SetRect( &win->normal_rect, cs.x, cs.y, cs.x + cs.cx, cs.y + cs.cy );
5147 if (win->dwStyle & WS_SYSMENU) NtUserSetSystemMenu( hwnd, 0 );
5149 win->imc = get_default_input_context();
5151 /* call the WH_CBT hook */
5153 release_win_ptr( win );
5154 cbtc.hwndInsertAfter = HWND_TOP;
5155 cbtc.lpcs = &cs;
5156 if (call_hooks( WH_CBT, HCBT_CREATEWND, (WPARAM)hwnd, (LPARAM)&cbtc, sizeof(cbtc) ))
5158 free_window_handle( hwnd );
5159 return 0;
5161 if (!(win = get_win_ptr( hwnd ))) return 0;
5164 * Correct the window styles.
5166 * It affects only the style loaded into the WND structure.
5169 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
5171 win->dwStyle |= WS_CLIPSIBLINGS;
5172 if (!(win->dwStyle & WS_POPUP)) win->dwStyle |= WS_CAPTION;
5175 win->dwExStyle = cs.dwExStyle;
5176 /* WS_EX_WINDOWEDGE depends on some other styles */
5177 if ((win->dwStyle & (WS_DLGFRAME | WS_THICKFRAME)) &&
5178 !(win->dwStyle & (WS_CHILD | WS_POPUP)))
5179 win->dwExStyle |= WS_EX_WINDOWEDGE;
5181 if (!(win->dwStyle & (WS_CHILD | WS_POPUP))) win->flags |= WIN_NEED_SIZE;
5183 SERVER_START_REQ( set_window_info )
5185 req->handle = wine_server_user_handle( hwnd );
5186 req->flags = SET_WIN_STYLE | SET_WIN_EXSTYLE | SET_WIN_INSTANCE | SET_WIN_UNICODE;
5187 req->style = win->dwStyle;
5188 req->ex_style = win->dwExStyle;
5189 req->instance = wine_server_client_ptr( win->hInstance );
5190 req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
5191 req->extra_offset = -1;
5192 wine_server_call( req );
5194 SERVER_END_REQ;
5196 /* Set the window menu */
5198 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
5200 if (cs.hMenu && !set_window_menu( hwnd, cs.hMenu ))
5202 release_win_ptr( win );
5203 free_window_handle( hwnd );
5204 return 0;
5207 else NtUserSetWindowLongPtr( hwnd, GWLP_ID, (ULONG_PTR)cs.hMenu, FALSE );
5209 win_dpi = win->dpi;
5210 release_win_ptr( win );
5212 if (parent) map_dpi_create_struct( &cs, thread_dpi, win_dpi );
5214 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
5216 /* send the WM_GETMINMAXINFO message and fix the size if needed */
5218 cx = cs.cx;
5219 cy = cs.cy;
5220 if ((cs.style & WS_THICKFRAME) || !(cs.style & (WS_POPUP | WS_CHILD)))
5222 MINMAXINFO info = get_min_max_info( hwnd );
5223 cx = max( min( cx, info.ptMaxTrackSize.x ), info.ptMinTrackSize.x );
5224 cy = max( min( cy, info.ptMaxTrackSize.y ), info.ptMinTrackSize.y );
5227 if (cx < 0) cx = 0;
5228 if (cy < 0) cy = 0;
5229 SetRect( &rect, cs.x, cs.y, cs.x + cx, cs.y + cy );
5230 /* check for wraparound */
5231 if (cs.x > 0x7fffffff - cx) rect.right = 0x7fffffff;
5232 if (cs.y > 0x7fffffff - cy) rect.bottom = 0x7fffffff;
5233 if (!apply_window_pos( hwnd, 0, SWP_NOZORDER | SWP_NOACTIVATE, &rect, &rect, NULL )) goto failed;
5235 /* send WM_NCCREATE */
5237 TRACE( "hwnd %p cs %d,%d %dx%d %s\n", hwnd, cs.x, cs.y, cs.cx, cs.cy, wine_dbgstr_rect(&rect) );
5238 if (!send_message_timeout( hwnd, WM_NCCREATE, 0, (LPARAM)&cs, SMTO_NORMAL, 0, ansi ))
5240 WARN( "%p: aborted by WM_NCCREATE\n", hwnd );
5241 goto failed;
5244 /* create default IME window */
5246 if (!is_desktop_window( hwnd ) && parent != get_hwnd_message_parent() &&
5247 register_imm_window( hwnd ))
5249 TRACE( "register IME window for %p\n", hwnd );
5250 win_set_flags( hwnd, WIN_HAS_IME_WIN, 0 );
5253 /* send WM_NCCALCSIZE */
5255 if (get_window_rects( hwnd, COORDS_PARENT, &rect, NULL, win_dpi ))
5257 /* yes, even if the CBT hook was called with HWND_TOP */
5258 HWND insert_after = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) ? HWND_BOTTOM : HWND_TOP;
5259 RECT client_rect = rect;
5261 /* the rectangle is in screen coords for WM_NCCALCSIZE when wparam is FALSE */
5262 map_window_points( parent, 0, (POINT *)&client_rect, 2, win_dpi );
5263 send_message( hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&client_rect );
5264 map_window_points( 0, parent, (POINT *)&client_rect, 2, win_dpi );
5265 apply_window_pos( hwnd, insert_after, SWP_NOACTIVATE, &rect, &client_rect, NULL );
5267 else goto failed;
5269 /* send WM_CREATE */
5270 if (send_message_timeout( hwnd, WM_CREATE, 0, (LPARAM)&cs, SMTO_NORMAL, 0, ansi ) == -1)
5271 goto failed;
5273 /* call the driver */
5275 if (!user_driver->pCreateWindow( hwnd )) goto failed;
5277 NtUserNotifyWinEvent( EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0 );
5279 /* send the size messages */
5281 if (!(win_get_flags( hwnd ) & WIN_NEED_SIZE))
5283 get_window_rects( hwnd, COORDS_PARENT, NULL, &rect, win_dpi );
5284 send_message( hwnd, WM_SIZE, SIZE_RESTORED,
5285 MAKELONG(rect.right-rect.left, rect.bottom-rect.top));
5286 send_message( hwnd, WM_MOVE, 0, MAKELONG( rect.left, rect.top ) );
5289 /* Show the window, maximizing or minimizing if needed */
5291 style = set_window_style( hwnd, 0, WS_MAXIMIZE | WS_MINIMIZE );
5292 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
5294 RECT new_pos;
5295 UINT sw_flags = (style & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
5297 sw_flags = window_min_maximize( hwnd, sw_flags, &new_pos );
5298 sw_flags |= SWP_FRAMECHANGED; /* Frame always gets changed */
5299 if (!(style & WS_VISIBLE) || (style & WS_CHILD) || get_active_window())
5300 sw_flags |= SWP_NOACTIVATE;
5301 NtUserSetWindowPos( hwnd, 0, new_pos.left, new_pos.top, new_pos.right - new_pos.left,
5302 new_pos.bottom - new_pos.top, sw_flags );
5305 /* Notify the parent window only */
5307 send_parent_notify( hwnd, WM_CREATE );
5308 if (!is_window( hwnd ))
5310 SetThreadDpiAwarenessContext( context );
5311 return 0;
5314 if (parent == get_desktop_window())
5315 NtUserPostMessage( parent, WM_PARENTNOTIFY, WM_CREATE, (LPARAM)hwnd );
5317 if (cs.style & WS_VISIBLE)
5319 if (cs.style & WS_MAXIMIZE)
5320 sw = SW_SHOW;
5321 else if (cs.style & WS_MINIMIZE)
5322 sw = SW_SHOWMINIMIZED;
5324 NtUserShowWindow( hwnd, sw );
5325 if (cs.dwExStyle & WS_EX_MDICHILD)
5327 send_message( cs.hwndParent, WM_MDIREFRESHMENU, 0, 0 );
5328 /* ShowWindow won't activate child windows */
5329 NtUserSetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE );
5333 /* Call WH_SHELL hook */
5335 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) && !get_window_relative( hwnd, GW_OWNER ))
5336 call_hooks( WH_SHELL, HSHELL_WINDOWCREATED, (WPARAM)hwnd, 0, 0 );
5338 TRACE( "created window %p\n", hwnd );
5339 SetThreadDpiAwarenessContext( context );
5340 return hwnd;
5342 failed:
5343 destroy_window( hwnd );
5344 SetThreadDpiAwarenessContext( context );
5345 return 0;
5348 static void *get_dialog_info( HWND hwnd )
5350 WND *win;
5351 void *ret;
5353 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
5355 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
5356 return NULL;
5359 ret = win->dlgInfo;
5360 release_win_ptr( win );
5361 return ret;
5364 static BOOL set_dialog_info( HWND hwnd, void *info )
5366 WND *win;
5368 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
5369 win->dlgInfo = info;
5370 release_win_ptr( win );
5371 return TRUE;
5374 /*****************************************************************************
5375 * NtUserCallHwnd (win32u.@)
5377 ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code )
5379 switch (code)
5381 case NtUserCallHwnd_ActivateOtherWindow:
5382 activate_other_window( hwnd );
5383 return 0;
5385 case NtUserCallHwnd_ArrangeIconicWindows:
5386 return arrange_iconic_windows( hwnd );
5388 case NtUserCallHwnd_DrawMenuBar:
5389 return draw_menu_bar( hwnd );
5391 case NtUserCallHwnd_GetDefaultImeWindow:
5392 return HandleToUlong( get_default_ime_window( hwnd ));
5394 case NtUserCallHwnd_GetDpiForWindow:
5395 return get_dpi_for_window( hwnd );
5397 case NtUserCallHwnd_GetParent:
5398 return HandleToUlong( get_parent( hwnd ));
5400 case NtUserCallHwnd_GetDialogInfo:
5401 return (ULONG_PTR)get_dialog_info( hwnd );
5403 case NtUserCallHwnd_GetMDIClientInfo:
5404 if (!(win_get_flags( hwnd ) & WIN_ISMDICLIENT)) return 0;
5405 return get_window_long_ptr( hwnd, sizeof(void *), FALSE );
5407 case NtUserCallHwnd_GetWindowContextHelpId:
5408 return get_window_context_help_id( hwnd );
5410 case NtUserCallHwnd_GetWindowDpiAwarenessContext:
5411 return (ULONG_PTR)get_window_dpi_awareness_context( hwnd );
5413 case NtUserCallHwnd_GetWindowInputContext:
5414 return HandleToUlong( get_window_input_context( hwnd ));
5416 case NtUserCallHwnd_GetWindowSysSubMenu:
5417 return HandleToUlong( get_window_sys_sub_menu( hwnd ));
5419 case NtUserCallHwnd_GetWindowTextLength:
5420 return get_server_window_text( hwnd, NULL, 0 );
5422 case NtUserCallHwnd_IsWindow:
5423 return is_window( hwnd );
5425 case NtUserCallHwnd_IsWindowEnabled:
5426 return is_window_enabled( hwnd );
5428 case NtUserCallHwnd_IsWindowUnicode:
5429 return is_window_unicode( hwnd );
5431 case NtUserCallHwnd_IsWindowVisible:
5432 return is_window_visible( hwnd );
5434 case NtUserCallHwnd_SetForegroundWindow:
5435 return set_foreground_window( hwnd, FALSE );
5437 case NtUserCallHwnd_SetProgmanWindow:
5438 return HandleToUlong( set_progman_window( hwnd ));
5440 case NtUserCallHwnd_SetTaskmanWindow:
5441 return HandleToUlong( set_taskman_window( hwnd ));
5443 /* temporary exports */
5444 case NtUserGetFullWindowHandle:
5445 return HandleToUlong( get_full_window_handle( hwnd ));
5447 case NtUserIsCurrehtProcessWindow:
5448 return HandleToUlong( is_current_process_window( hwnd ));
5450 case NtUserIsCurrehtThreadWindow:
5451 return HandleToUlong( is_current_thread_window( hwnd ));
5453 default:
5454 FIXME( "invalid code %u\n", (int)code );
5455 return 0;
5459 /*****************************************************************************
5460 * NtUserCallHwndParam (win32u.@)
5462 ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
5464 switch (code)
5466 case NtUserCallHwndParam_ClientToScreen:
5467 return client_to_screen( hwnd, (POINT *)param );
5469 case NtUserCallHwndParam_EnableWindow:
5470 return enable_window( hwnd, param );
5472 case NtUserCallHwndParam_GetChildRect:
5473 return get_window_rects( hwnd, COORDS_PARENT, (RECT *)param, NULL, get_thread_dpi() );
5475 case NtUserCallHwndParam_GetClassLongA:
5476 return get_class_long( hwnd, param, TRUE );
5478 case NtUserCallHwndParam_GetClassLongW:
5479 return get_class_long( hwnd, param, FALSE );
5481 case NtUserCallHwndParam_GetClassLongPtrA:
5482 return get_class_long_ptr( hwnd, param, TRUE );
5484 case NtUserCallHwndParam_GetClassLongPtrW:
5485 return get_class_long_ptr( hwnd, param, FALSE );
5487 case NtUserCallHwndParam_GetClassWord:
5488 return get_class_word( hwnd, param );
5490 case NtUserCallHwndParam_GetClientRect:
5491 return get_client_rect( hwnd, (RECT *)param );
5493 case NtUserCallHwndParam_GetScrollInfo:
5495 struct get_scroll_info_params *params = (void *)param;
5496 return get_scroll_info( hwnd, params->bar, params->info );
5499 case NtUserCallHwndParam_GetWindowInfo:
5500 return get_window_info( hwnd, (WINDOWINFO *)param );
5502 case NtUserCallHwndParam_GetWindowLongA:
5503 return get_window_long_size( hwnd, param, sizeof(LONG), TRUE );
5505 case NtUserCallHwndParam_GetWindowLongW:
5506 return get_window_long( hwnd, param );
5508 case NtUserCallHwndParam_GetWindowLongPtrA:
5509 return get_window_long_ptr( hwnd, param, TRUE );
5511 case NtUserCallHwndParam_GetWindowLongPtrW:
5512 return get_window_long_ptr( hwnd, param, FALSE );
5514 case NtUserCallHwndParam_GetWindowRect:
5515 return get_window_rect( hwnd, (RECT *)param, get_thread_dpi() );
5517 case NtUserCallHwndParam_GetWindowRelative:
5518 return HandleToUlong( get_window_relative( hwnd, param ));
5520 case NtUserCallHwndParam_GetWindowThread:
5521 return get_window_thread( hwnd, (DWORD *)param );
5523 case NtUserCallHwndParam_GetWindowWord:
5524 return get_window_word( hwnd, param );
5526 case NtUserCallHwndParam_IsChild:
5527 return is_child( hwnd, UlongToHandle(param) );
5529 case NtUserCallHwndParam_KillSystemTimer:
5530 return kill_system_timer( hwnd, param );
5532 case NtUserCallHwndParam_MapWindowPoints:
5534 struct map_window_points_params *params = (void *)param;
5535 return map_window_points( hwnd, params->hwnd_to, params->points, params->count,
5536 get_thread_dpi() );
5539 case NtUserCallHwndParam_MirrorRgn:
5540 return mirror_window_region( hwnd, UlongToHandle(param) );
5542 case NtUserCallHwndParam_MonitorFromWindow:
5543 return HandleToUlong( monitor_from_window( hwnd, param, get_thread_dpi() ));
5545 case NtUserCallHwndParam_ScreenToClient:
5546 return screen_to_client( hwnd, (POINT *)param );
5548 case NtUserCallHwndParam_SetDialogInfo:
5549 return set_dialog_info( hwnd, (void *)param );
5551 case NtUserCallHwndParam_SetMDIClientInfo:
5552 NtUserSetWindowLongPtr( hwnd, sizeof(void *), param, FALSE );
5553 return win_set_flags( hwnd, WIN_ISMDICLIENT, 0 );
5555 case NtUserCallHwndParam_SetWindowContextHelpId:
5556 return set_window_context_help_id( hwnd, param );
5558 case NtUserCallHwndParam_SetWindowPixelFormat:
5559 return set_window_pixel_format( hwnd, param );
5561 case NtUserCallHwndParam_ShowOwnedPopups:
5562 return show_owned_popups( hwnd, param );
5564 /* temporary exports */
5565 case NtUserSetWindowStyle:
5567 STYLESTRUCT *style = (void *)param;
5568 return set_window_style( hwnd, style->styleNew, style->styleOld );
5571 default:
5572 FIXME( "invalid code %u\n", (int)code );
5573 return 0;
5577 /*******************************************************************
5578 * NtUserDragDetect (win32u.@)
5580 BOOL WINAPI NtUserDragDetect( HWND hwnd, int x, int y )
5582 WORD width, height;
5583 RECT rect;
5584 MSG msg;
5586 TRACE( "%p (%d,%d)\n", hwnd, x, y );
5588 if (!(NtUserGetKeyState( VK_LBUTTON ) & 0x8000)) return FALSE;
5590 width = get_system_metrics( SM_CXDRAG );
5591 height = get_system_metrics( SM_CYDRAG );
5592 SetRect( &rect, x - width, y - height, x + width, y + height );
5594 NtUserSetCapture( hwnd );
5596 for (;;)
5598 while (NtUserPeekMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE ))
5600 if (msg.message == WM_LBUTTONUP)
5602 release_capture();
5603 return FALSE;
5605 if (msg.message == WM_MOUSEMOVE)
5607 POINT tmp;
5608 tmp.x = (short)LOWORD( msg.lParam );
5609 tmp.y = (short)HIWORD( msg.lParam );
5610 if (!PtInRect( &rect, tmp ))
5612 release_capture();
5613 return TRUE;
5617 NtUserMsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 );
5619 return FALSE;
5622 /*******************************************************************
5623 * NtUserDragObject (win32u.@)
5625 DWORD WINAPI NtUserDragObject( HWND parent, HWND hwnd, UINT fmt, ULONG_PTR data, HCURSOR cursor )
5627 FIXME( "%p, %p, %u, %#lx, %p stub!\n", parent, hwnd, fmt, data, cursor );
5629 return 0;