windows.media.speech: Add IIterable<IInspectable*> stubs.
[wine.git] / dlls / win32u / window.c
blob59761b8185c6791fee4baa7621731a3a48abaac9
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 /***********************************************************************
52 * alloc_user_handle
54 HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type )
56 HANDLE handle = 0;
58 SERVER_START_REQ( alloc_user_handle )
60 if (!wine_server_call_err( req )) handle = wine_server_ptr_handle( reply->handle );
62 SERVER_END_REQ;
64 if (handle)
66 UINT index = USER_HANDLE_TO_INDEX( handle );
68 assert( index < NB_USER_HANDLES );
69 ptr->handle = handle;
70 ptr->type = type;
71 InterlockedExchangePointer( &user_handles[index], ptr );
73 return handle;
76 /***********************************************************************
77 * get_user_handle_ptr
79 void *get_user_handle_ptr( HANDLE handle, unsigned int type )
81 struct user_object *ptr;
82 WORD index = USER_HANDLE_TO_INDEX( handle );
84 if (index >= NB_USER_HANDLES) return NULL;
86 user_lock();
87 if ((ptr = user_handles[index]))
89 if (ptr->type == type &&
90 ((UINT)(UINT_PTR)ptr->handle == (UINT)(UINT_PTR)handle ||
91 !HIWORD(handle) || HIWORD(handle) == 0xffff))
92 return ptr;
93 ptr = NULL;
95 else ptr = OBJ_OTHER_PROCESS;
96 user_unlock();
97 return ptr;
100 /***********************************************************************
101 * set_user_handle_ptr
103 static void set_user_handle_ptr( HANDLE handle, struct user_object *ptr )
105 WORD index = USER_HANDLE_TO_INDEX(handle);
106 assert( index < NB_USER_HANDLES );
107 InterlockedExchangePointer( &user_handles[index], ptr );
110 /***********************************************************************
111 * release_user_handle_ptr
113 void release_user_handle_ptr( void *ptr )
115 assert( ptr && ptr != OBJ_OTHER_PROCESS );
116 user_unlock();
119 /***********************************************************************
120 * free_user_handle
122 void *free_user_handle( HANDLE handle, unsigned int type )
124 struct user_object *ptr;
125 WORD index = USER_HANDLE_TO_INDEX( handle );
127 if ((ptr = get_user_handle_ptr( handle, type )) && ptr != OBJ_OTHER_PROCESS)
129 SERVER_START_REQ( free_user_handle )
131 req->handle = wine_server_user_handle( handle );
132 if (wine_server_call( req )) ptr = NULL;
133 else InterlockedCompareExchangePointer( &user_handles[index], NULL, ptr );
135 SERVER_END_REQ;
136 user_unlock();
138 return ptr;
141 /***********************************************************************
142 * next_thread_window
144 static WND *next_thread_window_ptr( HWND *hwnd )
146 struct user_object *ptr;
147 WND *win;
148 WORD index = *hwnd ? USER_HANDLE_TO_INDEX( *hwnd ) + 1 : 0;
150 while (index < NB_USER_HANDLES)
152 if (!(ptr = user_handles[index++])) continue;
153 if (ptr->type != NTUSER_OBJ_WINDOW) continue;
154 win = (WND *)ptr;
155 if (win->tid != GetCurrentThreadId()) continue;
156 *hwnd = ptr->handle;
157 return win;
159 return NULL;
162 /*******************************************************************
163 * get_hwnd_message_parent
165 * Return the parent for HWND_MESSAGE windows.
167 HWND get_hwnd_message_parent(void)
169 struct user_thread_info *thread_info = get_user_thread_info();
171 if (!thread_info->msg_window) get_desktop_window(); /* trigger creation */
172 return thread_info->msg_window;
175 /***********************************************************************
176 * get_full_window_handle
178 * Convert a possibly truncated window handle to a full 32-bit handle.
180 HWND get_full_window_handle( HWND hwnd )
182 WND *win;
184 if (!hwnd || (ULONG_PTR)hwnd >> 16) return hwnd;
185 if (LOWORD(hwnd) <= 1 || LOWORD(hwnd) == 0xffff) return hwnd;
186 /* do sign extension for -2 and -3 */
187 if (LOWORD(hwnd) >= (WORD)-3) return (HWND)(LONG_PTR)(INT16)LOWORD(hwnd);
189 if (!(win = get_win_ptr( hwnd ))) return hwnd;
191 if (win == WND_DESKTOP)
193 if (LOWORD(hwnd) == LOWORD(get_desktop_window())) return get_desktop_window();
194 else return get_hwnd_message_parent();
197 if (win != WND_OTHER_PROCESS)
199 hwnd = win->obj.handle;
200 release_win_ptr( win );
202 else /* may belong to another process */
204 SERVER_START_REQ( get_window_info )
206 req->handle = wine_server_user_handle( hwnd );
207 if (!wine_server_call_err( req )) hwnd = wine_server_ptr_handle( reply->full_handle );
209 SERVER_END_REQ;
211 return hwnd;
214 /*******************************************************************
215 * is_desktop_window
217 * Check if window is the desktop or the HWND_MESSAGE top parent.
219 BOOL is_desktop_window( HWND hwnd )
221 struct user_thread_info *thread_info = get_user_thread_info();
223 if (!hwnd) return FALSE;
224 if (hwnd == thread_info->top_window) return TRUE;
225 if (hwnd == thread_info->msg_window) return TRUE;
227 if (!HIWORD(hwnd) || HIWORD(hwnd) == 0xffff)
229 if (LOWORD(thread_info->top_window) == LOWORD(hwnd)) return TRUE;
230 if (LOWORD(thread_info->msg_window) == LOWORD(hwnd)) return TRUE;
232 return FALSE;
235 /***********************************************************************
236 * win_get_ptr
238 * Return a pointer to the WND structure if local to the process,
239 * or WND_OTHER_PROCESS if handle may be valid in other process.
240 * If ret value is a valid pointer, it must be released with WIN_ReleasePtr.
242 WND *get_win_ptr( HWND hwnd )
244 WND *win;
246 if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) == WND_OTHER_PROCESS)
248 if (is_desktop_window( hwnd )) win = WND_DESKTOP;
250 return win;
253 /***********************************************************************
254 * is_current_thread_window
256 * Check whether a given window belongs to the current process (and return the full handle).
258 HWND is_current_thread_window( HWND hwnd )
260 WND *win;
261 HWND ret = 0;
263 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP)
264 return 0;
265 if (win->tid == GetCurrentThreadId()) ret = win->obj.handle;
266 release_win_ptr( win );
267 return ret;
270 /***********************************************************************
271 * is_current_process_window
273 * Check whether a given window belongs to the current process (and return the full handle).
275 HWND is_current_process_window( HWND hwnd )
277 WND *ptr;
278 HWND ret;
280 if (!(ptr = get_win_ptr( hwnd )) || ptr == WND_OTHER_PROCESS || ptr == WND_DESKTOP) return 0;
281 ret = ptr->obj.handle;
282 release_win_ptr( ptr );
283 return ret;
286 /* see IsWindow */
287 BOOL is_window( HWND hwnd )
289 WND *win;
290 BOOL ret;
292 if (!(win = get_win_ptr( hwnd ))) return FALSE;
293 if (win == WND_DESKTOP) return TRUE;
295 if (win != WND_OTHER_PROCESS)
297 release_win_ptr( win );
298 return TRUE;
301 /* check other processes */
302 SERVER_START_REQ( get_window_info )
304 req->handle = wine_server_user_handle( hwnd );
305 ret = !wine_server_call_err( req );
307 SERVER_END_REQ;
308 return ret;
311 /* see GetWindowThreadProcessId */
312 DWORD get_window_thread( HWND hwnd, DWORD *process )
314 WND *ptr;
315 DWORD tid = 0;
317 if (!(ptr = get_win_ptr( hwnd )))
319 SetLastError( ERROR_INVALID_WINDOW_HANDLE);
320 return 0;
323 if (ptr != WND_OTHER_PROCESS && ptr != WND_DESKTOP)
325 /* got a valid window */
326 tid = ptr->tid;
327 if (process) *process = GetCurrentProcessId();
328 release_win_ptr( ptr );
329 return tid;
332 /* check other processes */
333 SERVER_START_REQ( get_window_info )
335 req->handle = wine_server_user_handle( hwnd );
336 if (!wine_server_call_err( req ))
338 tid = (DWORD)reply->tid;
339 if (process) *process = (DWORD)reply->pid;
342 SERVER_END_REQ;
343 return tid;
346 /* see GetParent */
347 static HWND get_parent( HWND hwnd )
349 HWND retval = 0;
350 WND *win;
352 if (!(win = get_win_ptr( hwnd )))
354 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
355 return 0;
357 if (win == WND_DESKTOP) return 0;
358 if (win == WND_OTHER_PROCESS)
360 LONG style = get_window_long( hwnd, GWL_STYLE );
361 if (style & (WS_POPUP | WS_CHILD))
363 SERVER_START_REQ( get_window_tree )
365 req->handle = wine_server_user_handle( hwnd );
366 if (!wine_server_call_err( req ))
368 if (style & WS_POPUP) retval = wine_server_ptr_handle( reply->owner );
369 else if (style & WS_CHILD) retval = wine_server_ptr_handle( reply->parent );
372 SERVER_END_REQ;
375 else
377 if (win->dwStyle & WS_POPUP) retval = win->owner;
378 else if (win->dwStyle & WS_CHILD) retval = win->parent;
379 release_win_ptr( win );
381 return retval;
384 /*****************************************************************
385 * NtUserSetParent (win32u.@)
387 HWND WINAPI NtUserSetParent( HWND hwnd, HWND parent )
389 RECT window_rect, old_screen_rect, new_screen_rect;
390 DPI_AWARENESS_CONTEXT context;
391 WINDOWPOS winpos;
392 HWND full_handle;
393 HWND old_parent = 0;
394 BOOL was_visible;
395 WND *win;
396 BOOL ret;
398 TRACE("(%p %p)\n", hwnd, parent);
400 if (is_broadcast(hwnd) || is_broadcast(parent))
402 SetLastError(ERROR_INVALID_PARAMETER);
403 return 0;
406 if (!parent) parent = get_desktop_window();
407 else if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent();
408 else parent = get_full_window_handle( parent );
410 if (!is_window( parent ))
412 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
413 return 0;
416 /* Some applications try to set a child as a parent */
417 if (is_child( hwnd, parent ))
419 SetLastError( ERROR_INVALID_PARAMETER );
420 return 0;
423 if (!(full_handle = is_current_thread_window( hwnd )))
424 return UlongToHandle( send_message( hwnd, WM_WINE_SETPARENT, (WPARAM)parent, 0 ));
426 if (full_handle == parent)
428 SetLastError( ERROR_INVALID_PARAMETER );
429 return 0;
432 /* Windows hides the window first, then shows it again
433 * including the WM_SHOWWINDOW messages and all */
434 was_visible = NtUserShowWindow( hwnd, SW_HIDE );
436 win = get_win_ptr( hwnd );
437 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
439 get_window_rects( hwnd, COORDS_PARENT, &window_rect, NULL, get_dpi_for_window(hwnd) );
440 get_window_rects( hwnd, COORDS_SCREEN, &old_screen_rect, NULL, 0 );
442 SERVER_START_REQ( set_parent )
444 req->handle = wine_server_user_handle( hwnd );
445 req->parent = wine_server_user_handle( parent );
446 if ((ret = !wine_server_call_err( req )))
448 old_parent = wine_server_ptr_handle( reply->old_parent );
449 win->parent = parent = wine_server_ptr_handle( reply->full_parent );
450 win->dpi = reply->dpi;
451 win->dpi_awareness = reply->awareness;
455 SERVER_END_REQ;
456 release_win_ptr( win );
457 if (!ret) return 0;
459 get_window_rects( hwnd, COORDS_SCREEN, &new_screen_rect, NULL, 0 );
460 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
462 user_driver->pSetParent( full_handle, parent, old_parent );
464 winpos.hwnd = hwnd;
465 winpos.hwndInsertAfter = HWND_TOP;
466 winpos.x = window_rect.left;
467 winpos.y = window_rect.top;
468 winpos.cx = 0;
469 winpos.cy = 0;
470 winpos.flags = SWP_NOSIZE;
472 set_window_pos( &winpos, new_screen_rect.left - old_screen_rect.left,
473 new_screen_rect.top - old_screen_rect.top );
475 if (was_visible) NtUserShowWindow( hwnd, SW_SHOW );
477 set_thread_dpi_awareness_context( context );
478 return old_parent;
481 /* see GetWindow */
482 static HWND get_window_relative( HWND hwnd, UINT rel )
484 HWND retval = 0;
486 if (rel == GW_OWNER) /* this one may be available locally */
488 WND *win = get_win_ptr( hwnd );
489 if (!win)
491 SetLastError( ERROR_INVALID_HANDLE );
492 return 0;
494 if (win == WND_DESKTOP) return 0;
495 if (win != WND_OTHER_PROCESS)
497 retval = win->owner;
498 release_win_ptr( win );
499 return retval;
501 /* else fall through to server call */
504 SERVER_START_REQ( get_window_tree )
506 req->handle = wine_server_user_handle( hwnd );
507 if (!wine_server_call_err( req ))
509 switch(rel)
511 case GW_HWNDFIRST:
512 retval = wine_server_ptr_handle( reply->first_sibling );
513 break;
514 case GW_HWNDLAST:
515 retval = wine_server_ptr_handle( reply->last_sibling );
516 break;
517 case GW_HWNDNEXT:
518 retval = wine_server_ptr_handle( reply->next_sibling );
519 break;
520 case GW_HWNDPREV:
521 retval = wine_server_ptr_handle( reply->prev_sibling );
522 break;
523 case GW_OWNER:
524 retval = wine_server_ptr_handle( reply->owner );
525 break;
526 case GW_CHILD:
527 retval = wine_server_ptr_handle( reply->first_child );
528 break;
532 SERVER_END_REQ;
533 return retval;
536 /*******************************************************************
537 * list_window_parents
539 * Build an array of all parents of a given window, starting with
540 * the immediate parent. The array must be freed with free().
542 static HWND *list_window_parents( HWND hwnd )
544 WND *win;
545 HWND current, *list;
546 int i, pos = 0, size = 16, count;
548 if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
550 current = hwnd;
551 for (;;)
553 if (!(win = get_win_ptr( current ))) goto empty;
554 if (win == WND_OTHER_PROCESS) break; /* need to do it the hard way */
555 if (win == WND_DESKTOP)
557 if (!pos) goto empty;
558 list[pos] = 0;
559 return list;
561 list[pos] = current = win->parent;
562 release_win_ptr( win );
563 if (!current) return list;
564 if (++pos == size - 1)
566 /* need to grow the list */
567 HWND *new_list = realloc( list, (size + 16) * sizeof(HWND) );
568 if (!new_list) goto empty;
569 list = new_list;
570 size += 16;
574 /* at least one parent belongs to another process, have to query the server */
576 for (;;)
578 count = 0;
579 SERVER_START_REQ( get_window_parents )
581 req->handle = wine_server_user_handle( hwnd );
582 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
583 if (!wine_server_call( req )) count = reply->count;
585 SERVER_END_REQ;
586 if (!count) goto empty;
587 if (size > count)
589 /* start from the end since HWND is potentially larger than user_handle_t */
590 for (i = count - 1; i >= 0; i--)
591 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
592 list[count] = 0;
593 return list;
595 free( list );
596 size = count + 1;
597 if (!(list = malloc( size * sizeof(HWND) ))) return NULL;
600 empty:
601 free( list );
602 return NULL;
605 /*******************************************************************
606 * list_window_children
608 * Build an array of the children of a given window. The array must be
609 * freed with HeapFree. Returns NULL when no windows are found.
611 HWND *list_window_children( HDESK desktop, HWND hwnd, UNICODE_STRING *class, DWORD tid )
613 HWND *list;
614 int i, size = 128;
615 ATOM atom = class ? get_int_atom_value( class ) : 0;
617 /* empty class is not the same as NULL class */
618 if (!atom && class && !class->Length) return NULL;
620 for (;;)
622 int count = 0;
624 if (!(list = malloc( size * sizeof(HWND) ))) break;
626 SERVER_START_REQ( get_window_children )
628 req->desktop = wine_server_obj_handle( desktop );
629 req->parent = wine_server_user_handle( hwnd );
630 req->tid = tid;
631 req->atom = atom;
632 if (!atom && class) wine_server_add_data( req, class->Buffer, class->Length );
633 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
634 if (!wine_server_call( req )) count = reply->count;
636 SERVER_END_REQ;
637 if (count && count < size)
639 /* start from the end since HWND is potentially larger than user_handle_t */
640 for (i = count - 1; i >= 0; i--)
641 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
642 list[count] = 0;
643 return list;
645 free( list );
646 if (!count) break;
647 size = count + 1; /* restart with a large enough buffer */
649 return NULL;
652 /*****************************************************************
653 * NtUserGetAncestor (win32u.@)
655 HWND WINAPI NtUserGetAncestor( HWND hwnd, UINT type )
657 HWND *list, ret = 0;
658 WND *win;
660 switch(type)
662 case GA_PARENT:
663 if (!(win = get_win_ptr( hwnd )))
665 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
666 return 0;
668 if (win == WND_DESKTOP) return 0;
669 if (win != WND_OTHER_PROCESS)
671 ret = win->parent;
672 release_win_ptr( win );
674 else /* need to query the server */
676 SERVER_START_REQ( get_window_tree )
678 req->handle = wine_server_user_handle( hwnd );
679 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->parent );
681 SERVER_END_REQ;
683 break;
685 case GA_ROOT:
686 if (!(list = list_window_parents( hwnd ))) return 0;
688 if (!list[0] || !list[1]) ret = get_full_window_handle( hwnd ); /* top-level window */
689 else
691 int count = 2;
692 while (list[count]) count++;
693 ret = list[count - 2]; /* get the one before the desktop */
695 free( list );
696 break;
698 case GA_ROOTOWNER:
699 if (is_desktop_window( hwnd )) return 0;
700 ret = get_full_window_handle( hwnd );
701 for (;;)
703 HWND parent = get_parent( ret );
704 if (!parent) break;
705 ret = parent;
707 break;
709 return ret;
712 /* see IsChild */
713 BOOL is_child( HWND parent, HWND child )
715 HWND *list;
716 int i;
717 BOOL ret = FALSE;
719 if (!(get_window_long( child, GWL_STYLE ) & WS_CHILD)) return FALSE;
720 if (!(list = list_window_parents( child ))) return FALSE;
721 parent = get_full_window_handle( parent );
722 for (i = 0; list[i]; i++)
724 if (list[i] == parent)
726 ret = list[i] && list[i+1];
727 break;
729 if (!(get_window_long( list[i], GWL_STYLE ) & WS_CHILD)) break;
731 free( list );
732 return ret;
735 /* see IsWindowVisible */
736 static BOOL is_window_visible( HWND hwnd )
738 HWND *list;
739 BOOL retval = TRUE;
740 int i;
742 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)) return FALSE;
743 if (!(list = list_window_parents( hwnd ))) return TRUE;
744 if (list[0])
746 for (i = 0; list[i+1]; i++)
747 if (!(get_window_long( list[i], GWL_STYLE ) & WS_VISIBLE)) break;
748 retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
750 free( list );
751 return retval;
754 /***********************************************************************
755 * is_window_drawable
757 * hwnd is drawable when it is visible, all parents are not
758 * minimized, and it is itself not minimized unless we are
759 * trying to draw its default class icon.
761 static BOOL is_window_drawable( HWND hwnd, BOOL icon )
763 HWND *list;
764 BOOL retval = TRUE;
765 int i;
766 LONG style = get_window_long( hwnd, GWL_STYLE );
768 if (!(style & WS_VISIBLE)) return FALSE;
769 if ((style & WS_MINIMIZE) && icon && get_class_long_ptr( hwnd, GCLP_HICON, FALSE )) return FALSE;
771 if (!(list = list_window_parents( hwnd ))) return TRUE;
772 if (list[0])
774 for (i = 0; list[i+1]; i++)
775 if ((get_window_long( list[i], GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != WS_VISIBLE)
776 break;
777 retval = !list[i+1] && (list[i] == get_desktop_window()); /* top message window isn't visible */
779 free( list );
780 return retval;
783 /* see IsWindowUnicode */
784 BOOL is_window_unicode( HWND hwnd )
786 WND *win;
787 BOOL ret = FALSE;
789 if (!(win = get_win_ptr(hwnd))) return FALSE;
791 if (win == WND_DESKTOP) return TRUE;
793 if (win != WND_OTHER_PROCESS)
795 ret = (win->flags & WIN_ISUNICODE) != 0;
796 release_win_ptr( win );
798 else
800 SERVER_START_REQ( get_window_info )
802 req->handle = wine_server_user_handle( hwnd );
803 if (!wine_server_call_err( req )) ret = reply->is_unicode;
805 SERVER_END_REQ;
807 return ret;
810 /* see GetWindowDpiAwarenessContext */
811 DPI_AWARENESS_CONTEXT get_window_dpi_awareness_context( HWND hwnd )
813 DPI_AWARENESS_CONTEXT ret = 0;
814 WND *win;
816 if (!(win = get_win_ptr( hwnd )))
818 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
819 return 0;
821 if (win == WND_DESKTOP) return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
822 if (win != WND_OTHER_PROCESS)
824 ret = ULongToHandle( win->dpi_awareness | 0x10 );
825 release_win_ptr( win );
827 else
829 SERVER_START_REQ( get_window_info )
831 req->handle = wine_server_user_handle( hwnd );
832 if (!wine_server_call_err( req )) ret = ULongToHandle( reply->awareness | 0x10 );
834 SERVER_END_REQ;
836 return ret;
839 /* see GetDpiForWindow */
840 UINT get_dpi_for_window( HWND hwnd )
842 WND *win;
843 UINT ret = 0;
845 if (!(win = get_win_ptr( hwnd )))
847 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
848 return 0;
850 if (win == WND_DESKTOP)
852 POINT pt = { 0, 0 };
853 return get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTOPRIMARY, 0 ));
855 if (win != WND_OTHER_PROCESS)
857 ret = win->dpi;
858 if (!ret) ret = get_win_monitor_dpi( hwnd );
859 release_win_ptr( win );
861 else
863 SERVER_START_REQ( get_window_info )
865 req->handle = wine_server_user_handle( hwnd );
866 if (!wine_server_call_err( req )) ret = reply->dpi;
868 SERVER_END_REQ;
870 return ret;
873 static LONG_PTR get_win_data( const void *ptr, UINT size )
875 if (size == sizeof(WORD))
877 WORD ret;
878 memcpy( &ret, ptr, sizeof(ret) );
879 return ret;
881 else if (size == sizeof(DWORD))
883 DWORD ret;
884 memcpy( &ret, ptr, sizeof(ret) );
885 return ret;
887 else
889 LONG_PTR ret;
890 memcpy( &ret, ptr, sizeof(ret) );
891 return ret;
895 /* helper for set_window_long */
896 static inline void set_win_data( void *ptr, LONG_PTR val, UINT size )
898 if (size == sizeof(WORD))
900 WORD newval = val;
901 memcpy( ptr, &newval, sizeof(newval) );
903 else if (size == sizeof(DWORD))
905 DWORD newval = val;
906 memcpy( ptr, &newval, sizeof(newval) );
908 else
910 memcpy( ptr, &val, sizeof(val) );
914 BOOL is_iconic( HWND hwnd )
916 return (get_window_long( hwnd, GWL_STYLE ) & WS_MINIMIZE) != 0;
919 static BOOL is_zoomed( HWND hwnd )
921 return (get_window_long( hwnd, GWL_STYLE ) & WS_MAXIMIZE) != 0;
924 static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi )
926 LONG_PTR retval = 0;
927 WND *win;
929 if (offset == GWLP_HWNDPARENT)
931 HWND parent = NtUserGetAncestor( hwnd, GA_PARENT );
932 if (parent == get_desktop_window())
933 parent = get_window_relative( hwnd, GW_OWNER );
934 return (ULONG_PTR)parent;
937 if (!(win = get_win_ptr( hwnd )))
939 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
940 return 0;
943 if (win == WND_DESKTOP)
945 switch (offset)
947 case GWL_STYLE:
948 retval = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; /* message parent is not visible */
949 if (get_full_window_handle( hwnd ) == get_desktop_window())
950 retval |= WS_VISIBLE;
951 return retval;
952 case GWL_EXSTYLE:
953 case GWLP_USERDATA:
954 case GWLP_ID:
955 case GWLP_HINSTANCE:
956 return 0;
957 case GWLP_WNDPROC:
958 SetLastError( ERROR_ACCESS_DENIED );
959 return 0;
961 SetLastError( ERROR_INVALID_INDEX );
962 return 0;
965 if (win == WND_OTHER_PROCESS)
967 if (offset == GWLP_WNDPROC)
969 SetLastError( ERROR_ACCESS_DENIED );
970 return 0;
972 SERVER_START_REQ( set_window_info )
974 req->handle = wine_server_user_handle( hwnd );
975 req->flags = 0; /* don't set anything, just retrieve */
976 req->extra_offset = (offset >= 0) ? offset : -1;
977 req->extra_size = (offset >= 0) ? size : 0;
978 if (!wine_server_call_err( req ))
980 switch(offset)
982 case GWL_STYLE: retval = reply->old_style; break;
983 case GWL_EXSTYLE: retval = reply->old_ex_style; break;
984 case GWLP_ID: retval = reply->old_id; break;
985 case GWLP_HINSTANCE: retval = (ULONG_PTR)wine_server_get_ptr( reply->old_instance ); break;
986 case GWLP_USERDATA: retval = reply->old_user_data; break;
987 default:
988 if (offset >= 0) retval = get_win_data( &reply->old_extra_value, size );
989 else SetLastError( ERROR_INVALID_INDEX );
990 break;
994 SERVER_END_REQ;
995 return retval;
998 /* now we have a valid win */
1000 if (offset >= 0)
1002 if (offset > (int)(win->cbWndExtra - size))
1004 WARN("Invalid offset %d\n", offset );
1005 release_win_ptr( win );
1006 SetLastError( ERROR_INVALID_INDEX );
1007 return 0;
1009 retval = get_win_data( (char *)win->wExtra + offset, size );
1011 /* Special case for dialog window procedure */
1012 if ((offset == DWLP_DLGPROC) && (size == sizeof(LONG_PTR)) && win->dlgInfo)
1013 retval = (LONG_PTR)get_winproc( (WNDPROC)retval, ansi );
1014 release_win_ptr( win );
1015 return retval;
1018 switch(offset)
1020 case GWLP_USERDATA: retval = win->userdata; break;
1021 case GWL_STYLE: retval = win->dwStyle; break;
1022 case GWL_EXSTYLE: retval = win->dwExStyle; break;
1023 case GWLP_ID: retval = win->wIDmenu; break;
1024 case GWLP_HINSTANCE: retval = (ULONG_PTR)win->hInstance; break;
1025 case GWLP_WNDPROC:
1026 /* This looks like a hack only for the edit control (see tests). This makes these controls
1027 * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests
1028 * that the hack is in GetWindowLongPtr[AW], not in winprocs.
1030 if (win->winproc == BUILTIN_WINPROC(WINPROC_EDIT) && (!!ansi != !(win->flags & WIN_ISUNICODE)))
1031 retval = (ULONG_PTR)win->winproc;
1032 else
1033 retval = (ULONG_PTR)get_winproc( win->winproc, ansi );
1034 break;
1035 default:
1036 WARN("Unknown offset %d\n", offset );
1037 SetLastError( ERROR_INVALID_INDEX );
1038 break;
1040 release_win_ptr( win );
1041 return retval;
1044 /* see GetWindowLongW */
1045 DWORD get_window_long( HWND hwnd, INT offset )
1047 return get_window_long_size( hwnd, offset, sizeof(LONG), FALSE );
1050 /* see GetWindowLongPtr */
1051 static ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi )
1053 return get_window_long_size( hwnd, offset, sizeof(LONG_PTR), ansi );
1056 /* see GetWindowWord */
1057 static WORD get_window_word( HWND hwnd, INT offset )
1059 switch(offset)
1061 case GWLP_ID:
1062 case GWLP_HINSTANCE:
1063 case GWLP_HWNDPARENT:
1064 break;
1065 default:
1066 if (offset < 0)
1068 WARN("Invalid offset %d\n", offset );
1069 SetLastError( ERROR_INVALID_INDEX );
1070 return 0;
1072 break;
1074 return get_window_long_size( hwnd, offset, sizeof(WORD), TRUE );
1077 /***********************************************************************
1078 * set_window_style
1080 * Change the style of a window.
1082 ULONG set_window_style( HWND hwnd, ULONG set_bits, ULONG clear_bits )
1084 BOOL ok, made_visible = FALSE;
1085 STYLESTRUCT style;
1086 WND *win = get_win_ptr( hwnd );
1088 if (!win || win == WND_DESKTOP) return 0;
1089 if (win == WND_OTHER_PROCESS)
1091 if (is_window(hwnd))
1092 return send_message( hwnd, WM_WINE_SETSTYLE, set_bits, clear_bits );
1093 return 0;
1095 style.styleOld = win->dwStyle;
1096 style.styleNew = (win->dwStyle | set_bits) & ~clear_bits;
1097 if (style.styleNew == style.styleOld)
1099 release_win_ptr( win );
1100 return style.styleNew;
1102 SERVER_START_REQ( set_window_info )
1104 req->handle = wine_server_user_handle( hwnd );
1105 req->flags = SET_WIN_STYLE;
1106 req->style = style.styleNew;
1107 req->extra_offset = -1;
1108 if ((ok = !wine_server_call( req )))
1110 style.styleOld = reply->old_style;
1111 win->dwStyle = style.styleNew;
1114 SERVER_END_REQ;
1116 if (ok && ((style.styleOld ^ style.styleNew) & WS_VISIBLE))
1118 made_visible = (style.styleNew & WS_VISIBLE) != 0;
1119 invalidate_dce( win, NULL );
1121 release_win_ptr( win );
1123 if (!ok) return 0;
1125 user_driver->pSetWindowStyle( hwnd, GWL_STYLE, &style );
1126 if (made_visible) update_window_state( hwnd );
1128 return style.styleOld;
1131 static DWORD fix_exstyle( DWORD style, DWORD exstyle )
1133 if ((exstyle & WS_EX_DLGMODALFRAME) ||
1134 (!(exstyle & WS_EX_STATICEDGE) && (style & (WS_DLGFRAME | WS_THICKFRAME))))
1135 exstyle |= WS_EX_WINDOWEDGE;
1136 else
1137 exstyle &= ~WS_EX_WINDOWEDGE;
1138 return exstyle;
1141 /* Change the owner of a window. */
1142 static HWND set_window_owner( HWND hwnd, HWND owner )
1144 WND *win = get_win_ptr( hwnd );
1145 HWND ret = 0;
1147 if (!win || win == WND_DESKTOP) return 0;
1148 if (win == WND_OTHER_PROCESS)
1150 if (is_window(hwnd)) ERR( "cannot set owner %p on other process window %p\n", owner, hwnd );
1151 return 0;
1153 SERVER_START_REQ( set_window_owner )
1155 req->handle = wine_server_user_handle( hwnd );
1156 req->owner = wine_server_user_handle( owner );
1157 if (!wine_server_call( req ))
1159 win->owner = wine_server_ptr_handle( reply->full_owner );
1160 ret = wine_server_ptr_handle( reply->prev_owner );
1163 SERVER_END_REQ;
1164 release_win_ptr( win );
1165 return ret;
1168 /* Helper function for SetWindowLong(). */
1169 LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOOL ansi )
1171 BOOL ok, made_visible = FALSE;
1172 LONG_PTR retval = 0;
1173 STYLESTRUCT style;
1174 WND *win;
1176 TRACE( "%p %d %lx %c\n", hwnd, offset, newval, ansi ? 'A' : 'W' );
1178 if (is_broadcast(hwnd))
1180 SetLastError( ERROR_INVALID_PARAMETER );
1181 return FALSE;
1184 if (!(win = get_win_ptr( hwnd )))
1186 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1187 return 0;
1189 if (win == WND_DESKTOP)
1191 /* can't change anything on the desktop window */
1192 SetLastError( ERROR_ACCESS_DENIED );
1193 return 0;
1195 if (win == WND_OTHER_PROCESS)
1197 if (offset == GWLP_WNDPROC)
1199 SetLastError( ERROR_ACCESS_DENIED );
1200 return 0;
1202 if (offset > 32767 || offset < -32767)
1204 SetLastError( ERROR_INVALID_INDEX );
1205 return 0;
1207 return send_message( hwnd, WM_WINE_SETWINDOWLONG, MAKEWPARAM( offset, size ), newval );
1210 /* first some special cases */
1211 switch( offset )
1213 case GWL_STYLE:
1214 style.styleOld = win->dwStyle;
1215 style.styleNew = newval;
1216 release_win_ptr( win );
1217 send_message( hwnd, WM_STYLECHANGING, GWL_STYLE, (LPARAM)&style );
1218 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
1219 newval = style.styleNew;
1220 /* WS_CLIPSIBLINGS can't be reset on top-level windows */
1221 if (win->parent == get_desktop_window()) newval |= WS_CLIPSIBLINGS;
1222 /* WS_MINIMIZE can't be reset */
1223 if (win->dwStyle & WS_MINIMIZE) newval |= WS_MINIMIZE;
1224 break;
1225 case GWL_EXSTYLE:
1226 style.styleOld = win->dwExStyle;
1227 style.styleNew = newval;
1228 release_win_ptr( win );
1229 send_message( hwnd, WM_STYLECHANGING, GWL_EXSTYLE, (LPARAM)&style );
1230 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
1231 /* WS_EX_TOPMOST can only be changed through SetWindowPos */
1232 newval = (style.styleNew & ~WS_EX_TOPMOST) | (win->dwExStyle & WS_EX_TOPMOST);
1233 newval = fix_exstyle(win->dwStyle, newval);
1234 break;
1235 case GWLP_HWNDPARENT:
1236 if (win->parent == get_desktop_window())
1238 release_win_ptr( win );
1239 return (ULONG_PTR)set_window_owner( hwnd, (HWND)newval );
1241 else
1243 release_win_ptr( win );
1244 return (ULONG_PTR)NtUserSetParent( hwnd, (HWND)newval );
1246 case GWLP_WNDPROC:
1248 WNDPROC proc;
1249 UINT old_flags = win->flags;
1250 retval = get_window_long_ptr( hwnd, offset, ansi );
1251 proc = alloc_winproc( (WNDPROC)newval, ansi );
1252 if (proc) win->winproc = proc;
1253 if (is_winproc_unicode( proc, !ansi )) win->flags |= WIN_ISUNICODE;
1254 else win->flags &= ~WIN_ISUNICODE;
1255 if (!((old_flags ^ win->flags) & WIN_ISUNICODE))
1257 release_win_ptr( win );
1258 return retval;
1260 /* update is_unicode flag on the server side */
1261 break;
1263 case GWLP_ID:
1264 case GWLP_HINSTANCE:
1265 case GWLP_USERDATA:
1266 break;
1267 case DWLP_DLGPROC:
1268 if ((win->cbWndExtra - sizeof(LONG_PTR) >= DWLP_DLGPROC) &&
1269 (size == sizeof(LONG_PTR)) && win->dlgInfo)
1271 WNDPROC *ptr = (WNDPROC *)((char *)win->wExtra + DWLP_DLGPROC);
1272 retval = (ULONG_PTR)get_winproc( *ptr, ansi );
1273 *ptr = alloc_winproc( (WNDPROC)newval, ansi );
1274 release_win_ptr( win );
1275 return retval;
1277 /* fall through */
1278 default:
1279 if (offset < 0 || offset > (int)(win->cbWndExtra - size))
1281 WARN("Invalid offset %d\n", offset );
1282 release_win_ptr( win );
1283 SetLastError( ERROR_INVALID_INDEX );
1284 return 0;
1286 else if (get_win_data( (char *)win->wExtra + offset, size ) == newval)
1288 /* already set to the same value */
1289 release_win_ptr( win );
1290 return newval;
1292 break;
1295 SERVER_START_REQ( set_window_info )
1297 req->handle = wine_server_user_handle( hwnd );
1298 req->extra_offset = -1;
1299 switch(offset)
1301 case GWL_STYLE:
1302 req->flags = SET_WIN_STYLE | SET_WIN_EXSTYLE;
1303 req->style = newval;
1304 req->ex_style = fix_exstyle(newval, win->dwExStyle);
1305 break;
1306 case GWL_EXSTYLE:
1307 req->flags = SET_WIN_EXSTYLE;
1308 req->ex_style = newval;
1309 break;
1310 case GWLP_ID:
1311 req->flags = SET_WIN_ID;
1312 req->extra_value = newval;
1313 break;
1314 case GWLP_HINSTANCE:
1315 req->flags = SET_WIN_INSTANCE;
1316 req->instance = wine_server_client_ptr( (void *)newval );
1317 break;
1318 case GWLP_WNDPROC:
1319 req->flags = SET_WIN_UNICODE;
1320 req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
1321 break;
1322 case GWLP_USERDATA:
1323 req->flags = SET_WIN_USERDATA;
1324 req->user_data = newval;
1325 break;
1326 default:
1327 req->flags = SET_WIN_EXTRA;
1328 req->extra_offset = offset;
1329 req->extra_size = size;
1330 set_win_data( &req->extra_value, newval, size );
1332 if ((ok = !wine_server_call_err( req )))
1334 switch(offset)
1336 case GWL_STYLE:
1337 win->dwStyle = newval;
1338 win->dwExStyle = fix_exstyle(win->dwStyle, win->dwExStyle);
1339 retval = reply->old_style;
1340 break;
1341 case GWL_EXSTYLE:
1342 win->dwExStyle = newval;
1343 retval = reply->old_ex_style;
1344 break;
1345 case GWLP_ID:
1346 win->wIDmenu = newval;
1347 retval = reply->old_id;
1348 break;
1349 case GWLP_HINSTANCE:
1350 win->hInstance = (HINSTANCE)newval;
1351 retval = (ULONG_PTR)wine_server_get_ptr( reply->old_instance );
1352 break;
1353 case GWLP_WNDPROC:
1354 break;
1355 case GWLP_USERDATA:
1356 win->userdata = newval;
1357 retval = reply->old_user_data;
1358 break;
1359 default:
1360 retval = get_win_data( (char *)win->wExtra + offset, size );
1361 set_win_data( (char *)win->wExtra + offset, newval, size );
1362 break;
1366 SERVER_END_REQ;
1368 if ((offset == GWL_STYLE && ((style.styleOld ^ style.styleNew) & WS_VISIBLE)) ||
1369 (offset == GWL_EXSTYLE && ((style.styleOld ^ style.styleNew) & WS_EX_LAYERED)))
1371 made_visible = (win->dwStyle & WS_VISIBLE) != 0;
1372 invalidate_dce( win, NULL );
1374 release_win_ptr( win );
1376 if (!ok) return 0;
1378 if (offset == GWL_STYLE || offset == GWL_EXSTYLE)
1380 style.styleOld = retval;
1381 style.styleNew = newval;
1382 user_driver->pSetWindowStyle( hwnd, offset, &style );
1383 if (made_visible) update_window_state( hwnd );
1384 send_message( hwnd, WM_STYLECHANGED, offset, (LPARAM)&style );
1387 return retval;
1390 /**********************************************************************
1391 * NtUserSetWindowWord (win32u.@)
1393 WORD WINAPI NtUserSetWindowWord( HWND hwnd, INT offset, WORD newval )
1395 switch(offset)
1397 case GWLP_ID:
1398 case GWLP_HINSTANCE:
1399 case GWLP_HWNDPARENT:
1400 break;
1401 default:
1402 if (offset < 0)
1404 WARN("Invalid offset %d\n", offset );
1405 SetLastError( ERROR_INVALID_INDEX );
1406 return 0;
1408 break;
1410 return set_window_long( hwnd, offset, sizeof(WORD), newval, TRUE );
1413 /**********************************************************************
1414 * NtUserSetWindowLong (win32u.@)
1416 LONG WINAPI NtUserSetWindowLong( HWND hwnd, INT offset, LONG newval, BOOL ansi )
1418 return set_window_long( hwnd, offset, sizeof(LONG), newval, ansi );
1421 /*****************************************************************************
1422 * NtUserSetWindowLongPtr (win32u.@)
1424 LONG_PTR WINAPI NtUserSetWindowLongPtr( HWND hwnd, INT offset, LONG_PTR newval, BOOL ansi )
1426 return set_window_long( hwnd, offset, sizeof(LONG_PTR), newval, ansi );
1429 static BOOL set_window_pixel_format( HWND hwnd, int format )
1431 WND *win = get_win_ptr( hwnd );
1433 if (!win || win == WND_DESKTOP || win == WND_OTHER_PROCESS)
1435 WARN( "setting format %d on win %p not supported\n", format, hwnd );
1436 return FALSE;
1438 win->pixel_format = format;
1439 release_win_ptr( win );
1441 update_window_state( hwnd );
1442 return TRUE;
1445 /***********************************************************************
1446 * NtUserGetProp (win32u.@)
1448 * NOTE Native allows only ATOMs as the second argument. We allow strings
1449 * to save extra server call in GetPropW.
1451 HANDLE WINAPI NtUserGetProp( HWND hwnd, const WCHAR *str )
1453 ULONG_PTR ret = 0;
1455 SERVER_START_REQ( get_window_property )
1457 req->window = wine_server_user_handle( hwnd );
1458 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1459 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1460 if (!wine_server_call_err( req )) ret = reply->data;
1462 SERVER_END_REQ;
1463 return (HANDLE)ret;
1466 /*****************************************************************************
1467 * NtUserSetProp (win32u.@)
1469 * NOTE Native allows only ATOMs as the second argument. We allow strings
1470 * to save extra server call in SetPropW.
1472 BOOL WINAPI NtUserSetProp( HWND hwnd, const WCHAR *str, HANDLE handle )
1474 BOOL ret;
1476 SERVER_START_REQ( set_window_property )
1478 req->window = wine_server_user_handle( hwnd );
1479 req->data = (ULONG_PTR)handle;
1480 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1481 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1482 ret = !wine_server_call_err( req );
1484 SERVER_END_REQ;
1485 return ret;
1489 /***********************************************************************
1490 * NtUserRemoveProp (win32u.@)
1492 * NOTE Native allows only ATOMs as the second argument. We allow strings
1493 * to save extra server call in RemovePropW.
1495 HANDLE WINAPI NtUserRemoveProp( HWND hwnd, const WCHAR *str )
1497 ULONG_PTR ret = 0;
1499 SERVER_START_REQ( remove_window_property )
1501 req->window = wine_server_user_handle( hwnd );
1502 if (IS_INTRESOURCE(str)) req->atom = LOWORD(str);
1503 else wine_server_add_data( req, str, lstrlenW(str) * sizeof(WCHAR) );
1504 if (!wine_server_call_err( req )) ret = reply->data;
1506 SERVER_END_REQ;
1508 return (HANDLE)ret;
1511 static void mirror_rect( const RECT *window_rect, RECT *rect )
1513 int width = window_rect->right - window_rect->left;
1514 int tmp = rect->left;
1515 rect->left = width - rect->right;
1516 rect->right = width - tmp;
1519 /***********************************************************************
1520 * get_window_rects
1522 * Get the window and client rectangles.
1524 BOOL get_window_rects( HWND hwnd, enum coords_relative relative, RECT *window_rect,
1525 RECT *client_rect, UINT dpi )
1527 WND *win = get_win_ptr( hwnd );
1528 BOOL ret = TRUE;
1530 if (!win)
1532 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1533 return FALSE;
1535 if (win == WND_DESKTOP)
1537 RECT rect;
1538 rect.left = rect.top = 0;
1539 if (hwnd == get_hwnd_message_parent())
1541 rect.right = 100;
1542 rect.bottom = 100;
1543 rect = map_dpi_rect( rect, get_dpi_for_window( hwnd ), dpi );
1545 else
1547 rect = get_primary_monitor_rect( dpi );
1549 if (window_rect) *window_rect = rect;
1550 if (client_rect) *client_rect = rect;
1551 return TRUE;
1553 if (win != WND_OTHER_PROCESS)
1555 UINT window_dpi = get_dpi_for_window( hwnd );
1556 RECT window = win->window_rect;
1557 RECT client = win->client_rect;
1559 switch (relative)
1561 case COORDS_CLIENT:
1562 OffsetRect( &window, -win->client_rect.left, -win->client_rect.top );
1563 OffsetRect( &client, -win->client_rect.left, -win->client_rect.top );
1564 if (win->dwExStyle & WS_EX_LAYOUTRTL)
1565 mirror_rect( &win->client_rect, &window );
1566 break;
1567 case COORDS_WINDOW:
1568 OffsetRect( &window, -win->window_rect.left, -win->window_rect.top );
1569 OffsetRect( &client, -win->window_rect.left, -win->window_rect.top );
1570 if (win->dwExStyle & WS_EX_LAYOUTRTL)
1571 mirror_rect( &win->window_rect, &client );
1572 break;
1573 case COORDS_PARENT:
1574 if (win->parent)
1576 WND *parent = get_win_ptr( win->parent );
1577 if (parent == WND_DESKTOP) break;
1578 if (!parent || parent == WND_OTHER_PROCESS)
1580 release_win_ptr( win );
1581 goto other_process;
1583 if (parent->flags & WIN_CHILDREN_MOVED)
1585 release_win_ptr( parent );
1586 release_win_ptr( win );
1587 goto other_process;
1589 if (parent->dwExStyle & WS_EX_LAYOUTRTL)
1591 mirror_rect( &parent->client_rect, &window );
1592 mirror_rect( &parent->client_rect, &client );
1594 release_win_ptr( parent );
1596 break;
1597 case COORDS_SCREEN:
1598 while (win->parent)
1600 WND *parent = get_win_ptr( win->parent );
1601 if (parent == WND_DESKTOP) break;
1602 if (!parent || parent == WND_OTHER_PROCESS)
1604 release_win_ptr( win );
1605 goto other_process;
1607 release_win_ptr( win );
1608 if (parent->flags & WIN_CHILDREN_MOVED)
1610 release_win_ptr( parent );
1611 goto other_process;
1613 win = parent;
1614 if (win->parent)
1616 offset_rect( &window, win->client_rect.left, win->client_rect.top );
1617 offset_rect( &client, win->client_rect.left, win->client_rect.top );
1620 break;
1622 if (window_rect) *window_rect = map_dpi_rect( window, window_dpi, dpi );
1623 if (client_rect) *client_rect = map_dpi_rect( client, window_dpi, dpi );
1624 release_win_ptr( win );
1625 return TRUE;
1628 other_process:
1629 SERVER_START_REQ( get_window_rectangles )
1631 req->handle = wine_server_user_handle( hwnd );
1632 req->relative = relative;
1633 req->dpi = dpi;
1634 if ((ret = !wine_server_call_err( req )))
1636 if (window_rect)
1638 window_rect->left = reply->window.left;
1639 window_rect->top = reply->window.top;
1640 window_rect->right = reply->window.right;
1641 window_rect->bottom = reply->window.bottom;
1643 if (client_rect)
1645 client_rect->left = reply->client.left;
1646 client_rect->top = reply->client.top;
1647 client_rect->right = reply->client.right;
1648 client_rect->bottom = reply->client.bottom;
1652 SERVER_END_REQ;
1653 return ret;
1656 /* see GetWindowRect */
1657 BOOL get_window_rect( HWND hwnd, RECT *rect, UINT dpi )
1659 return get_window_rects( hwnd, COORDS_SCREEN, rect, NULL, dpi );
1662 /* see GetClientRect */
1663 BOOL get_client_rect( HWND hwnd, RECT *rect )
1665 return get_window_rects( hwnd, COORDS_CLIENT, NULL, rect, get_thread_dpi() );
1668 /* see GetWindowInfo */
1669 static BOOL get_window_info( HWND hwnd, WINDOWINFO *info )
1672 if (!info || !get_window_rects( hwnd, COORDS_SCREEN, &info->rcWindow,
1673 &info->rcClient, get_thread_dpi() ))
1674 return FALSE;
1676 info->dwStyle = get_window_long( hwnd, GWL_STYLE );
1677 info->dwExStyle = get_window_long( hwnd, GWL_EXSTYLE );
1678 info->dwWindowStatus = get_active_window() == hwnd ? WS_ACTIVECAPTION : 0;
1679 info->cxWindowBorders = info->rcClient.left - info->rcWindow.left;
1680 info->cyWindowBorders = info->rcWindow.bottom - info->rcClient.bottom;
1681 info->atomWindowType = get_class_long( hwnd, GCW_ATOM, FALSE );
1682 info->wCreatorVersion = 0x0400;
1683 return TRUE;
1686 /***********************************************************************
1687 * update_surface_region
1689 static void update_surface_region( HWND hwnd )
1691 NTSTATUS status;
1692 HRGN region = 0;
1693 RGNDATA *data;
1694 size_t size = 256;
1695 WND *win = get_win_ptr( hwnd );
1697 if (!win || win == WND_DESKTOP || win == WND_OTHER_PROCESS) return;
1698 if (!win->surface) goto done;
1702 if (!(data = malloc( FIELD_OFFSET( RGNDATA, Buffer[size] )))) goto done;
1704 SERVER_START_REQ( get_surface_region )
1706 req->window = wine_server_user_handle( hwnd );
1707 wine_server_set_reply( req, data->Buffer, size );
1708 if (!(status = wine_server_call( req )))
1710 size_t reply_size = wine_server_reply_size( reply );
1711 if (reply_size)
1713 data->rdh.dwSize = sizeof(data->rdh);
1714 data->rdh.iType = RDH_RECTANGLES;
1715 data->rdh.nCount = reply_size / sizeof(RECT);
1716 data->rdh.nRgnSize = reply_size;
1717 region = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1718 NtGdiOffsetRgn( region, -reply->visible_rect.left, -reply->visible_rect.top );
1721 else size = reply->total_size;
1723 SERVER_END_REQ;
1724 free( data );
1725 } while (status == STATUS_BUFFER_OVERFLOW);
1727 if (status) goto done;
1729 win->surface->funcs->set_region( win->surface, region );
1730 if (region) NtGdiDeleteObjectApp( region );
1732 done:
1733 release_win_ptr( win );
1736 /***********************************************************************
1737 * apply_window_pos
1739 * Backend implementation of SetWindowPos.
1741 static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags,
1742 const RECT *window_rect, const RECT *client_rect, const RECT *valid_rects )
1744 WND *win;
1745 HWND surface_win = 0, parent = NtUserGetAncestor( hwnd, GA_PARENT );
1746 BOOL ret, needs_update = FALSE;
1747 RECT visible_rect, old_visible_rect, old_window_rect, old_client_rect, extra_rects[3];
1748 struct window_surface *old_surface, *new_surface = NULL;
1750 if (!parent || parent == get_desktop_window())
1752 new_surface = &dummy_surface; /* provide a default surface for top-level windows */
1753 window_surface_add_ref( new_surface );
1755 visible_rect = *window_rect;
1756 if (!(ret = user_driver->pWindowPosChanging( hwnd, insert_after, swp_flags,
1757 window_rect, client_rect, &visible_rect, &new_surface )))
1759 if (IsRectEmpty( window_rect )) visible_rect = *window_rect;
1760 else
1762 visible_rect = get_virtual_screen_rect( get_thread_dpi() );
1763 intersect_rect( &visible_rect, &visible_rect, window_rect );
1767 get_window_rects( hwnd, COORDS_SCREEN, &old_window_rect, NULL, get_thread_dpi() );
1768 if (IsRectEmpty( &valid_rects[0] )) valid_rects = NULL;
1770 if (!(win = get_win_ptr( hwnd )) || win == WND_DESKTOP || win == WND_OTHER_PROCESS)
1772 if (new_surface) window_surface_release( new_surface );
1773 return FALSE;
1776 /* create or update window surface for top-level windows if the driver doesn't implement WindowPosChanging */
1777 if (!ret && new_surface && !IsRectEmpty( &visible_rect ) &&
1778 (!(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) ||
1779 NtUserGetLayeredWindowAttributes( hwnd, NULL, NULL, NULL )))
1781 window_surface_release( new_surface );
1782 if ((new_surface = win->surface)) window_surface_add_ref( new_surface );
1783 create_offscreen_window_surface( &visible_rect, &new_surface );
1786 old_visible_rect = win->visible_rect;
1787 old_client_rect = win->client_rect;
1788 old_surface = win->surface;
1789 if (old_surface != new_surface) swp_flags |= SWP_FRAMECHANGED; /* force refreshing non-client area */
1790 if (new_surface == &dummy_surface) swp_flags |= SWP_NOREDRAW;
1791 else if (old_surface == &dummy_surface)
1793 swp_flags |= SWP_NOCOPYBITS;
1794 valid_rects = NULL;
1797 SERVER_START_REQ( set_window_pos )
1799 req->handle = wine_server_user_handle( hwnd );
1800 req->previous = wine_server_user_handle( insert_after );
1801 req->swp_flags = swp_flags;
1802 req->window.left = window_rect->left;
1803 req->window.top = window_rect->top;
1804 req->window.right = window_rect->right;
1805 req->window.bottom = window_rect->bottom;
1806 req->client.left = client_rect->left;
1807 req->client.top = client_rect->top;
1808 req->client.right = client_rect->right;
1809 req->client.bottom = client_rect->bottom;
1810 if (!EqualRect( window_rect, &visible_rect ) || new_surface || valid_rects)
1812 extra_rects[0] = extra_rects[1] = visible_rect;
1813 if (new_surface)
1815 extra_rects[1] = new_surface->rect;
1816 OffsetRect( &extra_rects[1], visible_rect.left, visible_rect.top );
1818 if (valid_rects) extra_rects[2] = valid_rects[0];
1819 else SetRectEmpty( &extra_rects[2] );
1820 wine_server_add_data( req, extra_rects, sizeof(extra_rects) );
1822 if (new_surface) req->paint_flags |= SET_WINPOS_PAINT_SURFACE;
1823 if (win->pixel_format) req->paint_flags |= SET_WINPOS_PIXEL_FORMAT;
1825 if ((ret = !wine_server_call( req )))
1827 win->dwStyle = reply->new_style;
1828 win->dwExStyle = reply->new_ex_style;
1829 win->window_rect = *window_rect;
1830 win->client_rect = *client_rect;
1831 win->visible_rect = visible_rect;
1832 win->surface = new_surface;
1833 surface_win = wine_server_ptr_handle( reply->surface_win );
1834 needs_update = reply->needs_update;
1835 if (get_window_long( win->parent, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
1837 RECT client;
1838 get_window_rects( win->parent, COORDS_CLIENT, NULL, &client, get_thread_dpi() );
1839 mirror_rect( &client, &win->window_rect );
1840 mirror_rect( &client, &win->client_rect );
1841 mirror_rect( &client, &win->visible_rect );
1843 /* if an RTL window is resized the children have moved */
1844 if (win->dwExStyle & WS_EX_LAYOUTRTL &&
1845 client_rect->right - client_rect->left != old_client_rect.right - old_client_rect.left)
1846 win->flags |= WIN_CHILDREN_MOVED;
1849 SERVER_END_REQ;
1851 if (ret)
1853 if (needs_update) update_surface_region( surface_win );
1854 if (((swp_flags & SWP_AGG_NOPOSCHANGE) != SWP_AGG_NOPOSCHANGE) ||
1855 (swp_flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_STATECHANGED | SWP_FRAMECHANGED)))
1856 invalidate_dce( win, &old_window_rect );
1859 release_win_ptr( win );
1861 if (ret)
1863 TRACE( "win %p surface %p -> %p\n", hwnd, old_surface, new_surface );
1864 register_window_surface( old_surface, new_surface );
1865 if (old_surface)
1867 if (valid_rects)
1869 move_window_bits( hwnd, old_surface, new_surface, &visible_rect,
1870 &old_visible_rect, window_rect, valid_rects );
1871 valid_rects = NULL; /* prevent the driver from trying to also move the bits */
1873 window_surface_release( old_surface );
1875 else if (surface_win && surface_win != hwnd)
1877 if (valid_rects)
1879 RECT rects[2];
1880 int x_offset = old_visible_rect.left - visible_rect.left;
1881 int y_offset = old_visible_rect.top - visible_rect.top;
1883 /* if all that happened is that the whole window moved, copy everything */
1884 if (!(swp_flags & SWP_FRAMECHANGED) &&
1885 old_visible_rect.right - visible_rect.right == x_offset &&
1886 old_visible_rect.bottom - visible_rect.bottom == y_offset &&
1887 old_client_rect.left - client_rect->left == x_offset &&
1888 old_client_rect.right - client_rect->right == x_offset &&
1889 old_client_rect.top - client_rect->top == y_offset &&
1890 old_client_rect.bottom - client_rect->bottom == y_offset &&
1891 EqualRect( &valid_rects[0], client_rect ))
1893 rects[0] = visible_rect;
1894 rects[1] = old_visible_rect;
1895 valid_rects = rects;
1897 move_window_bits_parent( hwnd, surface_win, window_rect, valid_rects );
1898 valid_rects = NULL; /* prevent the driver from trying to also move the bits */
1902 user_driver->pWindowPosChanged( hwnd, insert_after, swp_flags, window_rect,
1903 client_rect, &visible_rect, valid_rects, new_surface );
1905 else if (new_surface) window_surface_release( new_surface );
1907 return ret;
1910 /*******************************************************************
1911 * NtUserGetWindowRgnEx (win32u.@)
1913 int WINAPI NtUserGetWindowRgnEx( HWND hwnd, HRGN hrgn, UINT unk )
1915 NTSTATUS status;
1916 HRGN win_rgn = 0;
1917 RGNDATA *data;
1918 size_t size = 256;
1919 int ret = ERROR;
1923 if (!(data = malloc( sizeof(*data) + size - 1 )))
1925 SetLastError( ERROR_OUTOFMEMORY );
1926 return ERROR;
1928 SERVER_START_REQ( get_window_region )
1930 req->window = wine_server_user_handle( hwnd );
1931 wine_server_set_reply( req, data->Buffer, size );
1932 if (!(status = wine_server_call( req )))
1934 size_t reply_size = wine_server_reply_size( reply );
1935 if (reply_size)
1937 data->rdh.dwSize = sizeof(data->rdh);
1938 data->rdh.iType = RDH_RECTANGLES;
1939 data->rdh.nCount = reply_size / sizeof(RECT);
1940 data->rdh.nRgnSize = reply_size;
1941 win_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1944 else size = reply->total_size;
1946 SERVER_END_REQ;
1947 free( data );
1948 } while (status == STATUS_BUFFER_OVERFLOW);
1950 if (set_ntstatus( status ) && win_rgn)
1952 ret = NtGdiCombineRgn( hrgn, win_rgn, 0, RGN_COPY );
1953 NtGdiDeleteObjectApp( win_rgn );
1955 return ret;
1958 /***********************************************************************
1959 * NtUserSetWindowRgn (win32u.@)
1961 int WINAPI NtUserSetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw )
1963 static const RECT empty_rect;
1964 BOOL ret;
1966 if (hrgn)
1968 RGNDATA *data;
1969 DWORD size;
1971 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return FALSE;
1972 if (!(data = malloc( size ))) return FALSE;
1973 if (!NtGdiGetRegionData( hrgn, size, data ))
1975 free( data );
1976 return FALSE;
1978 SERVER_START_REQ( set_window_region )
1980 req->window = wine_server_user_handle( hwnd );
1981 req->redraw = redraw != 0;
1982 if (data->rdh.nCount)
1983 wine_server_add_data( req, data->Buffer, data->rdh.nCount * sizeof(RECT) );
1984 else
1985 wine_server_add_data( req, &empty_rect, sizeof(empty_rect) );
1986 ret = !wine_server_call_err( req );
1988 SERVER_END_REQ;
1989 free( data );
1991 else /* clear existing region */
1993 SERVER_START_REQ( set_window_region )
1995 req->window = wine_server_user_handle( hwnd );
1996 req->redraw = redraw != 0;
1997 ret = !wine_server_call_err( req );
1999 SERVER_END_REQ;
2002 if (ret)
2004 UINT swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED |
2005 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE;
2006 if (!redraw) swp_flags |= SWP_NOREDRAW;
2007 user_driver->pSetWindowRgn( hwnd, hrgn, redraw );
2008 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, swp_flags );
2009 if (hrgn) NtGdiDeleteObjectApp( hrgn );
2011 return ret;
2014 /***********************************************************************
2015 * NtUserMoveWindow (win32u.@)
2017 BOOL WINAPI NtUserMoveWindow( HWND hwnd, INT x, INT y, INT cx, INT cy, BOOL repaint )
2019 int flags = SWP_NOZORDER | SWP_NOACTIVATE;
2020 if (!repaint) flags |= SWP_NOREDRAW;
2021 TRACE( "%p %d,%d %dx%d %d\n", hwnd, x, y, cx, cy, repaint );
2022 return NtUserSetWindowPos( hwnd, 0, x, y, cx, cy, flags );
2025 /*****************************************************************************
2026 * NtUserGetLayeredWindowAttributes (win32u.@)
2028 BOOL WINAPI NtUserGetLayeredWindowAttributes( HWND hwnd, COLORREF *key, BYTE *alpha, DWORD *flags )
2030 BOOL ret;
2032 SERVER_START_REQ( get_window_layered_info )
2034 req->handle = wine_server_user_handle( hwnd );
2035 if ((ret = !wine_server_call_err( req )))
2037 if (key) *key = reply->color_key;
2038 if (alpha) *alpha = reply->alpha;
2039 if (flags) *flags = reply->flags;
2042 SERVER_END_REQ;
2044 return ret;
2047 /*****************************************************************************
2048 * NtUserSetLayeredWindowAttributes (win32u.@)
2050 BOOL WINAPI NtUserSetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags )
2052 BOOL ret;
2054 TRACE( "(%p,%08x,%d,%x)\n", hwnd, key, alpha, flags );
2056 SERVER_START_REQ( set_window_layered_info )
2058 req->handle = wine_server_user_handle( hwnd );
2059 req->color_key = key;
2060 req->alpha = alpha;
2061 req->flags = flags;
2062 ret = !wine_server_call_err( req );
2064 SERVER_END_REQ;
2066 if (ret)
2068 user_driver->pSetLayeredWindowAttributes( hwnd, key, alpha, flags );
2069 update_window_state( hwnd );
2072 return ret;
2075 /*****************************************************************************
2076 * UpdateLayeredWindow (win32u.@)
2078 BOOL WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_dst, const SIZE *size,
2079 HDC hdc_src, const POINT *pts_src, COLORREF key,
2080 const BLENDFUNCTION *blend, DWORD flags, const RECT *dirty )
2082 DWORD swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW;
2083 RECT window_rect, client_rect;
2084 UPDATELAYEREDWINDOWINFO info;
2085 SIZE offset;
2087 if (flags & ~(ULW_COLORKEY | ULW_ALPHA | ULW_OPAQUE | ULW_EX_NORESIZE) ||
2088 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) ||
2089 NtUserGetLayeredWindowAttributes( hwnd, NULL, NULL, NULL ))
2091 SetLastError( ERROR_INVALID_PARAMETER );
2092 return FALSE;
2095 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
2097 if (pts_dst)
2099 offset.cx = pts_dst->x - window_rect.left;
2100 offset.cy = pts_dst->y - window_rect.top;
2101 OffsetRect( &client_rect, offset.cx, offset.cy );
2102 OffsetRect( &window_rect, offset.cx, offset.cy );
2103 swp_flags &= ~SWP_NOMOVE;
2105 if (size)
2107 offset.cx = size->cx - (window_rect.right - window_rect.left);
2108 offset.cy = size->cy - (window_rect.bottom - window_rect.top);
2109 if (size->cx <= 0 || size->cy <= 0)
2111 SetLastError( ERROR_INVALID_PARAMETER );
2112 return FALSE;
2114 if ((flags & ULW_EX_NORESIZE) && (offset.cx || offset.cy))
2116 SetLastError( ERROR_INCORRECT_SIZE );
2117 return FALSE;
2119 client_rect.right += offset.cx;
2120 client_rect.bottom += offset.cy;
2121 window_rect.right += offset.cx;
2122 window_rect.bottom += offset.cy;
2123 swp_flags &= ~SWP_NOSIZE;
2126 TRACE( "window %p win %s client %s\n", hwnd,
2127 wine_dbgstr_rect(&window_rect), wine_dbgstr_rect(&client_rect) );
2129 apply_window_pos( hwnd, 0, swp_flags, &window_rect, &client_rect, NULL );
2131 info.cbSize = sizeof(info);
2132 info.hdcDst = hdc_dst;
2133 info.pptDst = pts_dst;
2134 info.psize = size;
2135 info.hdcSrc = hdc_src;
2136 info.pptSrc = pts_src;
2137 info.crKey = key;
2138 info.pblend = blend;
2139 info.dwFlags = flags;
2140 info.prcDirty = dirty;
2141 return user_driver->pUpdateLayeredWindow( hwnd, &info, &window_rect );
2144 /***********************************************************************
2145 * list_children_from_point
2147 * Get the list of children that can contain point from the server.
2148 * Point is in screen coordinates.
2149 * Returned list must be freed by caller.
2151 static HWND *list_children_from_point( HWND hwnd, POINT pt )
2153 int i, size = 128;
2154 HWND *list;
2156 for (;;)
2158 int count = 0;
2160 if (!(list = malloc( size * sizeof(HWND) ))) break;
2162 SERVER_START_REQ( get_window_children_from_point )
2164 req->parent = wine_server_user_handle( hwnd );
2165 req->x = pt.x;
2166 req->y = pt.y;
2167 req->dpi = get_thread_dpi();
2168 wine_server_set_reply( req, list, (size-1) * sizeof(user_handle_t) );
2169 if (!wine_server_call( req )) count = reply->count;
2171 SERVER_END_REQ;
2172 if (count && count < size)
2174 /* start from the end since HWND is potentially larger than user_handle_t */
2175 for (i = count - 1; i >= 0; i--)
2176 list[i] = wine_server_ptr_handle( ((user_handle_t *)list)[i] );
2177 list[count] = 0;
2178 return list;
2180 free( list );
2181 if (!count) break;
2182 size = count + 1; /* restart with a large enough buffer */
2184 return NULL;
2187 /***********************************************************************
2188 * window_from_point
2190 * Find the window and hittest for a given point.
2192 static HWND window_from_point( HWND hwnd, POINT pt, INT *hittest )
2194 int i, res;
2195 HWND ret, *list;
2196 POINT win_pt;
2198 if (!hwnd) hwnd = get_desktop_window();
2200 *hittest = HTNOWHERE;
2202 if (!(list = list_children_from_point( hwnd, pt ))) return 0;
2204 /* now determine the hittest */
2206 for (i = 0; list[i]; i++)
2208 LONG style = get_window_long( list[i], GWL_STYLE );
2210 /* If window is minimized or disabled, return at once */
2211 if (style & WS_DISABLED)
2213 *hittest = HTERROR;
2214 break;
2216 /* Send WM_NCCHITTEST (if same thread) */
2217 if (!is_current_thread_window( list[i] ))
2219 *hittest = HTCLIENT;
2220 break;
2222 win_pt = map_dpi_point( pt, get_thread_dpi(), get_dpi_for_window( list[i] ));
2223 res = send_message( list[i], WM_NCHITTEST, 0, MAKELPARAM( win_pt.x, win_pt.y ));
2224 if (res != HTTRANSPARENT)
2226 *hittest = res; /* Found the window */
2227 break;
2229 /* continue search with next window in z-order */
2231 ret = list[i];
2232 free( list );
2233 TRACE( "scope %p (%d,%d) returning %p\n", hwnd, pt.x, pt.y, ret );
2234 return ret;
2237 /*******************************************************************
2238 * NtUserWindowFromPoint (win32u.@)
2240 HWND WINAPI NtUserWindowFromPoint( LONG x, LONG y )
2242 POINT pt = { .x = x, .y = y };
2243 INT hittest;
2244 return window_from_point( 0, pt, &hittest );
2247 /*******************************************************************
2248 * get_work_rect
2250 * Get the work area that a maximized window can cover, depending on style.
2252 static BOOL get_work_rect( HWND hwnd, RECT *rect )
2254 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
2255 MONITORINFO mon_info;
2256 DWORD style;
2258 if (!monitor) return FALSE;
2260 mon_info.cbSize = sizeof(mon_info);
2261 get_monitor_info( monitor, &mon_info );
2262 *rect = mon_info.rcMonitor;
2264 style = get_window_long( hwnd, GWL_STYLE );
2265 if (style & WS_MAXIMIZEBOX)
2267 if ((style & WS_CAPTION) == WS_CAPTION || !(style & (WS_CHILD | WS_POPUP)))
2268 *rect = mon_info.rcWork;
2270 return TRUE;
2273 static RECT get_maximized_work_rect( HWND hwnd )
2275 RECT work_rect = { 0 };
2277 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_MINIMIZE | WS_MAXIMIZE)) == WS_MAXIMIZE)
2279 if (!get_work_rect( hwnd, &work_rect ))
2280 work_rect = get_primary_monitor_rect( get_thread_dpi() );
2282 return work_rect;
2285 /*******************************************************************
2286 * update_maximized_pos
2288 * For top level windows covering the work area, we might have to
2289 * "forget" the maximized position. Windows presumably does this
2290 * to avoid situations where the border style changes, which would
2291 * lead the window to be outside the screen, or the window gets
2292 * reloaded on a different screen, and the "saved" position no
2293 * longer applies to it (despite being maximized).
2295 * Some applications (e.g. Imperiums: Greek Wars) depend on this.
2297 static void update_maximized_pos( WND *wnd, RECT *work_rect )
2299 if (wnd->parent && wnd->parent != get_desktop_window())
2300 return;
2302 if (wnd->dwStyle & WS_MAXIMIZE)
2304 if (wnd->window_rect.left <= work_rect->left && wnd->window_rect.top <= work_rect->top &&
2305 wnd->window_rect.right >= work_rect->right && wnd->window_rect.bottom >= work_rect->bottom)
2306 wnd->max_pos.x = wnd->max_pos.y = -1;
2308 else
2309 wnd->max_pos.x = wnd->max_pos.y = -1;
2312 static BOOL empty_point( POINT pt )
2314 return pt.x == -1 && pt.y == -1;
2317 /* see GetWindowPlacement */
2318 BOOL get_window_placement( HWND hwnd, WINDOWPLACEMENT *placement )
2320 RECT work_rect = get_maximized_work_rect( hwnd );
2321 WND *win = get_win_ptr( hwnd );
2322 UINT win_dpi;
2324 if (!win) return FALSE;
2326 if (win == WND_DESKTOP)
2328 placement->length = sizeof(*placement);
2329 placement->showCmd = SW_SHOWNORMAL;
2330 placement->flags = 0;
2331 placement->ptMinPosition.x = -1;
2332 placement->ptMinPosition.y = -1;
2333 placement->ptMaxPosition.x = -1;
2334 placement->ptMaxPosition.y = -1;
2335 get_window_rect( hwnd, &placement->rcNormalPosition, get_thread_dpi() );
2336 return TRUE;
2338 if (win == WND_OTHER_PROCESS)
2340 RECT normal_position;
2341 DWORD style;
2343 if (!get_window_rect( hwnd, &normal_position, get_thread_dpi() ))
2344 return FALSE;
2346 FIXME("not fully supported on other process window %p.\n", hwnd);
2348 placement->length = sizeof(*placement);
2349 style = get_window_long( hwnd, GWL_STYLE );
2350 if (style & WS_MINIMIZE)
2351 placement->showCmd = SW_SHOWMINIMIZED;
2352 else
2353 placement->showCmd = (style & WS_MAXIMIZE) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL;
2354 /* provide some dummy information */
2355 placement->flags = 0;
2356 placement->ptMinPosition.x = -1;
2357 placement->ptMinPosition.y = -1;
2358 placement->ptMaxPosition.x = -1;
2359 placement->ptMaxPosition.y = -1;
2360 placement->rcNormalPosition = normal_position;
2361 return TRUE;
2364 /* update the placement according to the current style */
2365 if (win->dwStyle & WS_MINIMIZE)
2367 win->min_pos.x = win->window_rect.left;
2368 win->min_pos.y = win->window_rect.top;
2370 else if (win->dwStyle & WS_MAXIMIZE)
2372 win->max_pos.x = win->window_rect.left;
2373 win->max_pos.y = win->window_rect.top;
2375 else
2377 win->normal_rect = win->window_rect;
2379 update_maximized_pos( win, &work_rect );
2381 placement->length = sizeof(*placement);
2382 if (win->dwStyle & WS_MINIMIZE)
2383 placement->showCmd = SW_SHOWMINIMIZED;
2384 else
2385 placement->showCmd = ( win->dwStyle & WS_MAXIMIZE ) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL ;
2386 if (win->flags & WIN_RESTORE_MAX)
2387 placement->flags = WPF_RESTORETOMAXIMIZED;
2388 else
2389 placement->flags = 0;
2390 win_dpi = get_dpi_for_window( hwnd );
2391 placement->ptMinPosition = empty_point(win->min_pos) ? win->min_pos
2392 : map_dpi_point( win->min_pos, win_dpi, get_thread_dpi() );
2393 placement->ptMaxPosition = empty_point(win->max_pos) ? win->max_pos
2394 : map_dpi_point( win->max_pos, win_dpi, get_thread_dpi() );
2395 placement->rcNormalPosition = map_dpi_rect( win->normal_rect, win_dpi, get_thread_dpi() );
2396 release_win_ptr( win );
2398 TRACE( "%p: returning min %d,%d max %d,%d normal %s\n",
2399 hwnd, placement->ptMinPosition.x, placement->ptMinPosition.y,
2400 placement->ptMaxPosition.x, placement->ptMaxPosition.y,
2401 wine_dbgstr_rect(&placement->rcNormalPosition) );
2402 return TRUE;
2405 /*****************************************************************************
2406 * NtUserBuildHwndList (win32u.@)
2408 NTSTATUS WINAPI NtUserBuildHwndList( HDESK desktop, ULONG unk2, ULONG unk3, ULONG unk4,
2409 ULONG thread_id, ULONG count, HWND *buffer, ULONG *size )
2411 user_handle_t *list = (user_handle_t *)buffer;
2412 int i;
2413 NTSTATUS status;
2415 SERVER_START_REQ( get_window_children )
2417 req->desktop = wine_server_obj_handle( desktop );
2418 req->tid = thread_id;
2419 if (count) wine_server_set_reply( req, list, (count - 1) * sizeof(user_handle_t) );
2420 status = wine_server_call( req );
2421 if (status && status != STATUS_BUFFER_TOO_SMALL) return status;
2422 *size = reply->count + 1;
2424 SERVER_END_REQ;
2425 if (*size > count) return STATUS_BUFFER_TOO_SMALL;
2427 /* start from the end since HWND is potentially larger than user_handle_t */
2428 for (i = *size - 2; i >= 0; i--)
2429 buffer[i] = wine_server_ptr_handle( list[i] );
2430 buffer[*size - 1] = HWND_BOTTOM;
2431 return STATUS_SUCCESS;
2434 /* Retrieve the window text from the server. */
2435 static data_size_t get_server_window_text( HWND hwnd, WCHAR *text, data_size_t count )
2437 data_size_t len = 0, needed = 0;
2439 SERVER_START_REQ( get_window_text )
2441 req->handle = wine_server_user_handle( hwnd );
2442 if (count) wine_server_set_reply( req, text, (count - 1) * sizeof(WCHAR) );
2443 if (!wine_server_call_err( req ))
2445 needed = reply->length;
2446 len = wine_server_reply_size(reply);
2449 SERVER_END_REQ;
2450 if (text) text[len / sizeof(WCHAR)] = 0;
2451 return needed;
2454 /*******************************************************************
2455 * NtUserInternalGetWindowText (win32u.@)
2457 INT WINAPI NtUserInternalGetWindowText( HWND hwnd, WCHAR *text, INT count )
2459 WND *win;
2461 if (count <= 0) return 0;
2462 if (!(win = get_win_ptr( hwnd ))) return 0;
2463 if (win == WND_DESKTOP) text[0] = 0;
2464 else if (win != WND_OTHER_PROCESS)
2466 if (win->text) lstrcpynW( text, win->text, count );
2467 else text[0] = 0;
2468 release_win_ptr( win );
2470 else
2472 get_server_window_text( hwnd, text, count );
2474 return lstrlenW(text);
2477 /*******************************************************************
2478 * get_windows_offset
2480 * Calculate the offset between the origin of the two windows. Used
2481 * to implement MapWindowPoints.
2483 static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mirrored, POINT *ret_offset )
2485 WND *win;
2486 POINT offset;
2487 BOOL mirror_from, mirror_to, ret;
2488 HWND hwnd;
2490 offset.x = offset.y = 0;
2491 *mirrored = mirror_from = mirror_to = FALSE;
2493 /* Translate source window origin to screen coords */
2494 if (hwnd_from)
2496 if (!(win = get_win_ptr( hwnd_from )))
2498 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
2499 return FALSE;
2501 if (win == WND_OTHER_PROCESS) goto other_process;
2502 if (win != WND_DESKTOP)
2504 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2506 mirror_from = TRUE;
2507 offset.x += win->client_rect.right - win->client_rect.left;
2509 while (win->parent)
2511 offset.x += win->client_rect.left;
2512 offset.y += win->client_rect.top;
2513 hwnd = win->parent;
2514 release_win_ptr( win );
2515 if (!(win = get_win_ptr( hwnd ))) break;
2516 if (win == WND_OTHER_PROCESS) goto other_process;
2517 if (win == WND_DESKTOP) break;
2518 if (win->flags & WIN_CHILDREN_MOVED)
2520 release_win_ptr( win );
2521 goto other_process;
2524 if (win && win != WND_DESKTOP) release_win_ptr( win );
2525 offset = map_dpi_point( offset, get_dpi_for_window( hwnd_from ), dpi );
2529 /* Translate origin to destination window coords */
2530 if (hwnd_to)
2532 if (!(win = get_win_ptr( hwnd_to )))
2534 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
2535 return FALSE;
2537 if (win == WND_OTHER_PROCESS) goto other_process;
2538 if (win != WND_DESKTOP)
2540 POINT pt = { 0, 0 };
2541 if (win->dwExStyle & WS_EX_LAYOUTRTL)
2543 mirror_to = TRUE;
2544 pt.x += win->client_rect.right - win->client_rect.left;
2546 while (win->parent)
2548 pt.x += win->client_rect.left;
2549 pt.y += win->client_rect.top;
2550 hwnd = win->parent;
2551 release_win_ptr( win );
2552 if (!(win = get_win_ptr( hwnd ))) break;
2553 if (win == WND_OTHER_PROCESS) goto other_process;
2554 if (win == WND_DESKTOP) break;
2555 if (win->flags & WIN_CHILDREN_MOVED)
2557 release_win_ptr( win );
2558 goto other_process;
2561 if (win && win != WND_DESKTOP) release_win_ptr( win );
2562 pt = map_dpi_point( pt, get_dpi_for_window( hwnd_to ), dpi );
2563 offset.x -= pt.x;
2564 offset.y -= pt.y;
2568 *mirrored = mirror_from ^ mirror_to;
2569 if (mirror_from) offset.x = -offset.x;
2570 *ret_offset = offset;
2571 return TRUE;
2573 other_process: /* one of the parents may belong to another process, do it the hard way */
2574 SERVER_START_REQ( get_windows_offset )
2576 req->from = wine_server_user_handle( hwnd_from );
2577 req->to = wine_server_user_handle( hwnd_to );
2578 req->dpi = dpi;
2579 if ((ret = !wine_server_call_err( req )))
2581 ret_offset->x = reply->x;
2582 ret_offset->y = reply->y;
2583 *mirrored = reply->mirror;
2586 SERVER_END_REQ;
2587 return ret;
2590 /* see ScreenToClient */
2591 static BOOL screen_to_client( HWND hwnd, POINT *pt )
2593 POINT offset;
2594 BOOL mirrored;
2596 if (!hwnd)
2598 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
2599 return FALSE;
2601 if (!get_windows_offset( 0, hwnd, get_thread_dpi(), &mirrored, &offset )) return FALSE;
2602 pt->x += offset.x;
2603 pt->y += offset.y;
2604 if (mirrored) pt->x = -pt->x;
2605 return TRUE;
2608 /* map coordinates of a window region */
2609 void map_window_region( HWND from, HWND to, HRGN hrgn )
2611 BOOL mirrored;
2612 POINT offset;
2613 UINT i, size;
2614 RGNDATA *data;
2615 HRGN new_rgn;
2616 RECT *rect;
2618 if (!get_windows_offset( from, to, get_thread_dpi(), &mirrored, &offset )) return;
2620 if (!mirrored)
2622 NtGdiOffsetRgn( hrgn, offset.x, offset.y );
2623 return;
2625 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return;
2626 if (!(data = malloc( size ))) return;
2627 NtGdiGetRegionData( hrgn, size, data );
2628 rect = (RECT *)data->Buffer;
2629 for (i = 0; i < data->rdh.nCount; i++)
2631 int tmp = -(rect[i].left + offset.x);
2632 rect[i].left = -(rect[i].right + offset.x);
2633 rect[i].right = tmp;
2634 rect[i].top += offset.y;
2635 rect[i].bottom += offset.y;
2637 if ((new_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data )))
2639 NtGdiCombineRgn( hrgn, new_rgn, 0, RGN_COPY );
2640 NtGdiDeleteObjectApp( new_rgn );
2642 free( data );
2645 /* see MapWindowPoints */
2646 int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count, UINT dpi )
2648 BOOL mirrored;
2649 POINT offset;
2650 UINT i;
2652 if (!get_windows_offset( hwnd_from, hwnd_to, dpi, &mirrored, &offset )) return 0;
2654 for (i = 0; i < count; i++)
2656 points[i].x += offset.x;
2657 points[i].y += offset.y;
2658 if (mirrored) points[i].x = -points[i].x;
2660 if (mirrored && count == 2) /* special case for rectangle */
2662 int tmp = points[0].x;
2663 points[0].x = points[1].x;
2664 points[1].x = tmp;
2666 return MAKELONG( LOWORD(offset.x), LOWORD(offset.y) );
2669 /***********************************************************************
2670 * dump_winpos_flags
2672 static void dump_winpos_flags( UINT flags )
2674 static const DWORD dumped_flags = (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW |
2675 SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW |
2676 SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOOWNERZORDER |
2677 SWP_NOSENDCHANGING | SWP_DEFERERASE | SWP_ASYNCWINDOWPOS |
2678 SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_STATECHANGED);
2679 TRACE( "flags:" );
2680 if(flags & SWP_NOSIZE) TRACE( " SWP_NOSIZE" );
2681 if(flags & SWP_NOMOVE) TRACE( " SWP_NOMOVE" );
2682 if(flags & SWP_NOZORDER) TRACE( " SWP_NOZORDER" );
2683 if(flags & SWP_NOREDRAW) TRACE( " SWP_NOREDRAW" );
2684 if(flags & SWP_NOACTIVATE) TRACE( " SWP_NOACTIVATE" );
2685 if(flags & SWP_FRAMECHANGED) TRACE( " SWP_FRAMECHANGED" );
2686 if(flags & SWP_SHOWWINDOW) TRACE( " SWP_SHOWWINDOW" );
2687 if(flags & SWP_HIDEWINDOW) TRACE( " SWP_HIDEWINDOW" );
2688 if(flags & SWP_NOCOPYBITS) TRACE( " SWP_NOCOPYBITS" );
2689 if(flags & SWP_NOOWNERZORDER) TRACE( " SWP_NOOWNERZORDER" );
2690 if(flags & SWP_NOSENDCHANGING) TRACE( " SWP_NOSENDCHANGING" );
2691 if(flags & SWP_DEFERERASE) TRACE( " SWP_DEFERERASE" );
2692 if(flags & SWP_ASYNCWINDOWPOS) TRACE( " SWP_ASYNCWINDOWPOS" );
2693 if(flags & SWP_NOCLIENTSIZE) TRACE( " SWP_NOCLIENTSIZE" );
2694 if(flags & SWP_NOCLIENTMOVE) TRACE( " SWP_NOCLIENTMOVE" );
2695 if(flags & SWP_STATECHANGED) TRACE( " SWP_STATECHANGED" );
2697 if(flags & ~dumped_flags) TRACE( " %08x", flags & ~dumped_flags );
2698 TRACE( "\n" );
2701 /***********************************************************************
2702 * map_dpi_winpos
2704 static void map_dpi_winpos( WINDOWPOS *winpos )
2706 UINT dpi_from = get_thread_dpi();
2707 UINT dpi_to = get_dpi_for_window( winpos->hwnd );
2709 if (!dpi_from) dpi_from = get_win_monitor_dpi( winpos->hwnd );
2710 if (dpi_from == dpi_to) return;
2711 winpos->x = muldiv( winpos->x, dpi_to, dpi_from );
2712 winpos->y = muldiv( winpos->y, dpi_to, dpi_from );
2713 winpos->cx = muldiv( winpos->cx, dpi_to, dpi_from );
2714 winpos->cy = muldiv( winpos->cy, dpi_to, dpi_from );
2717 /***********************************************************************
2718 * calc_winpos
2720 static BOOL calc_winpos( WINDOWPOS *winpos, RECT *old_window_rect, RECT *old_client_rect,
2721 RECT *new_window_rect, RECT *new_client_rect )
2723 WND *win;
2725 /* Send WM_WINDOWPOSCHANGING message */
2726 if (!(winpos->flags & SWP_NOSENDCHANGING)
2727 && !((winpos->flags & SWP_AGG_NOCLIENTCHANGE) && (winpos->flags & SWP_SHOWWINDOW)))
2728 send_message( winpos->hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos );
2730 if (!(win = get_win_ptr( winpos->hwnd )) ||
2731 win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
2733 /* Calculate new position and size */
2734 get_window_rects( winpos->hwnd, COORDS_PARENT, old_window_rect, old_client_rect, get_thread_dpi() );
2735 *new_window_rect = *old_window_rect;
2736 *new_client_rect = *old_client_rect;
2738 if (!(winpos->flags & SWP_NOSIZE))
2740 if (win->dwStyle & WS_MINIMIZE)
2742 new_window_rect->right = new_window_rect->left + get_system_metrics( SM_CXMINIMIZED );
2743 new_window_rect->bottom = new_window_rect->top + get_system_metrics( SM_CYMINIMIZED );
2745 else
2747 new_window_rect->right = new_window_rect->left + winpos->cx;
2748 new_window_rect->bottom = new_window_rect->top + winpos->cy;
2752 if (!(winpos->flags & SWP_NOMOVE))
2754 /* If the window is toplevel minimized off-screen, force keep it there */
2755 if ((win->dwStyle & WS_MINIMIZE) &&
2756 win->window_rect.left <= -32000 && win->window_rect.top <= -32000 &&
2757 (!win->parent || win->parent == get_desktop_window()))
2759 winpos->x = -32000;
2760 winpos->y = -32000;
2762 new_window_rect->left = winpos->x;
2763 new_window_rect->top = winpos->y;
2764 new_window_rect->right += winpos->x - old_window_rect->left;
2765 new_window_rect->bottom += winpos->y - old_window_rect->top;
2767 offset_rect( new_client_rect, winpos->x - old_window_rect->left,
2768 winpos->y - old_window_rect->top );
2770 winpos->flags |= SWP_NOCLIENTMOVE | SWP_NOCLIENTSIZE;
2772 TRACE( "hwnd %p, after %p, swp %d,%d %dx%d flags %08x current %s style %08x new %s\n",
2773 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
2774 winpos->cx, winpos->cy, winpos->flags,
2775 wine_dbgstr_rect( old_window_rect ), win->dwStyle,
2776 wine_dbgstr_rect( new_window_rect ));
2778 release_win_ptr( win );
2779 return TRUE;
2782 /***********************************************************************
2783 * get_valid_rects
2785 * Compute the valid rects from the old and new client rect and WVR_* flags.
2786 * Helper for WM_NCCALCSIZE handling.
2788 static inline void get_valid_rects( const RECT *old_client, const RECT *new_client, UINT flags,
2789 RECT *valid )
2791 int cx, cy;
2793 if (flags & WVR_REDRAW)
2795 SetRectEmpty( &valid[0] );
2796 SetRectEmpty( &valid[1] );
2797 return;
2800 if (flags & WVR_VALIDRECTS)
2802 if (!intersect_rect( &valid[0], &valid[0], new_client ) ||
2803 !intersect_rect( &valid[1], &valid[1], old_client ))
2805 SetRectEmpty( &valid[0] );
2806 SetRectEmpty( &valid[1] );
2807 return;
2809 flags = WVR_ALIGNLEFT | WVR_ALIGNTOP;
2811 else
2813 valid[0] = *new_client;
2814 valid[1] = *old_client;
2817 /* make sure the rectangles have the same size */
2818 cx = min( valid[0].right - valid[0].left, valid[1].right - valid[1].left );
2819 cy = min( valid[0].bottom - valid[0].top, valid[1].bottom - valid[1].top );
2821 if (flags & WVR_ALIGNBOTTOM)
2823 valid[0].top = valid[0].bottom - cy;
2824 valid[1].top = valid[1].bottom - cy;
2826 else
2828 valid[0].bottom = valid[0].top + cy;
2829 valid[1].bottom = valid[1].top + cy;
2831 if (flags & WVR_ALIGNRIGHT)
2833 valid[0].left = valid[0].right - cx;
2834 valid[1].left = valid[1].right - cx;
2836 else
2838 valid[0].right = valid[0].left + cx;
2839 valid[1].right = valid[1].left + cx;
2843 static UINT calc_ncsize( WINDOWPOS *winpos, const RECT *old_window_rect, const RECT *old_client_rect,
2844 const RECT *new_window_rect, RECT *new_client_rect, RECT *valid_rects,
2845 int parent_x, int parent_y )
2847 UINT wvr_flags = 0;
2849 /* Send WM_NCCALCSIZE message to get new client area */
2850 if ((winpos->flags & (SWP_FRAMECHANGED | SWP_NOSIZE)) != SWP_NOSIZE)
2852 NCCALCSIZE_PARAMS params;
2853 WINDOWPOS winposCopy;
2855 params.rgrc[0] = *new_window_rect;
2856 params.rgrc[1] = *old_window_rect;
2857 params.rgrc[2] = *old_client_rect;
2858 params.lppos = &winposCopy;
2859 winposCopy = *winpos;
2861 if (winpos->flags & SWP_NOMOVE)
2863 winposCopy.x = old_window_rect->left;
2864 winposCopy.y = old_window_rect->top;
2867 if (winpos->flags & SWP_NOSIZE)
2869 winposCopy.cx = old_window_rect->right - old_window_rect->left;
2870 winposCopy.cy = old_window_rect->bottom - old_window_rect->top;
2873 wvr_flags = send_message( winpos->hwnd, WM_NCCALCSIZE, TRUE, (LPARAM)&params );
2875 *new_client_rect = params.rgrc[0];
2877 TRACE( "hwnd %p old win %s old client %s new win %s new client %s\n", winpos->hwnd,
2878 wine_dbgstr_rect(old_window_rect), wine_dbgstr_rect(old_client_rect),
2879 wine_dbgstr_rect(new_window_rect), wine_dbgstr_rect(new_client_rect) );
2881 if (new_client_rect->left != old_client_rect->left - parent_x ||
2882 new_client_rect->top != old_client_rect->top - parent_y)
2883 winpos->flags &= ~SWP_NOCLIENTMOVE;
2885 if ((new_client_rect->right - new_client_rect->left !=
2886 old_client_rect->right - old_client_rect->left))
2887 winpos->flags &= ~SWP_NOCLIENTSIZE;
2888 else
2889 wvr_flags &= ~WVR_HREDRAW;
2891 if (new_client_rect->bottom - new_client_rect->top !=
2892 old_client_rect->bottom - old_client_rect->top)
2893 winpos->flags &= ~SWP_NOCLIENTSIZE;
2894 else
2895 wvr_flags &= ~WVR_VREDRAW;
2897 valid_rects[0] = params.rgrc[1];
2898 valid_rects[1] = params.rgrc[2];
2900 else
2902 if (!(winpos->flags & SWP_NOMOVE) &&
2903 (new_client_rect->left != old_client_rect->left - parent_x ||
2904 new_client_rect->top != old_client_rect->top - parent_y))
2905 winpos->flags &= ~SWP_NOCLIENTMOVE;
2908 if (winpos->flags & (SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_HIDEWINDOW))
2910 SetRectEmpty( &valid_rects[0] );
2911 SetRectEmpty( &valid_rects[1] );
2913 else get_valid_rects( old_client_rect, new_client_rect, wvr_flags, valid_rects );
2915 return wvr_flags;
2918 /* fix redundant flags and values in the WINDOWPOS structure */
2919 static BOOL fixup_swp_flags( WINDOWPOS *winpos, const RECT *old_window_rect, int parent_x, int parent_y )
2921 HWND parent;
2922 WND *win = get_win_ptr( winpos->hwnd );
2923 BOOL ret = TRUE;
2925 if (!win || win == WND_OTHER_PROCESS)
2927 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
2928 return FALSE;
2930 winpos->hwnd = win->obj.handle; /* make it a full handle */
2932 /* Finally make sure that all coordinates are valid */
2933 if (winpos->x < -32768) winpos->x = -32768;
2934 else if (winpos->x > 32767) winpos->x = 32767;
2935 if (winpos->y < -32768) winpos->y = -32768;
2936 else if (winpos->y > 32767) winpos->y = 32767;
2938 if (winpos->cx < 0) winpos->cx = 0;
2939 else if (winpos->cx > 32767) winpos->cx = 32767;
2940 if (winpos->cy < 0) winpos->cy = 0;
2941 else if (winpos->cy > 32767) winpos->cy = 32767;
2943 parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
2944 if (!is_window_visible( parent )) winpos->flags |= SWP_NOREDRAW;
2946 if (win->dwStyle & WS_VISIBLE) winpos->flags &= ~SWP_SHOWWINDOW;
2947 else
2949 winpos->flags &= ~SWP_HIDEWINDOW;
2950 if (!(winpos->flags & SWP_SHOWWINDOW)) winpos->flags |= SWP_NOREDRAW;
2953 if ((old_window_rect->right - old_window_rect->left == winpos->cx) &&
2954 (old_window_rect->bottom - old_window_rect->top == winpos->cy))
2955 winpos->flags |= SWP_NOSIZE; /* Already the right size */
2957 if ((old_window_rect->left - parent_x == winpos->x) && (old_window_rect->top - parent_y == winpos->y))
2958 winpos->flags |= SWP_NOMOVE; /* Already the right position */
2960 if ((win->dwStyle & (WS_POPUP | WS_CHILD)) != WS_CHILD)
2962 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)) && /* Bring to the top when activating */
2963 (winpos->flags & SWP_NOZORDER ||
2964 (winpos->hwndInsertAfter != HWND_TOPMOST && winpos->hwndInsertAfter != HWND_NOTOPMOST)))
2966 winpos->flags &= ~SWP_NOZORDER;
2967 winpos->hwndInsertAfter = HWND_TOP;
2971 /* Check hwndInsertAfter */
2972 if (winpos->flags & SWP_NOZORDER) goto done;
2974 if (winpos->hwndInsertAfter == HWND_TOP)
2976 if (get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
2977 winpos->flags |= SWP_NOZORDER;
2979 else if (winpos->hwndInsertAfter == HWND_BOTTOM)
2981 if (!(win->dwExStyle & WS_EX_TOPMOST) &&
2982 get_window_relative( winpos->hwnd, GW_HWNDLAST ) == winpos->hwnd)
2983 winpos->flags |= SWP_NOZORDER;
2985 else if (winpos->hwndInsertAfter == HWND_TOPMOST)
2987 if ((win->dwExStyle & WS_EX_TOPMOST) &&
2988 get_window_relative( winpos->hwnd, GW_HWNDFIRST ) == winpos->hwnd)
2989 winpos->flags |= SWP_NOZORDER;
2991 else if (winpos->hwndInsertAfter == HWND_NOTOPMOST)
2993 if (!(win->dwExStyle & WS_EX_TOPMOST))
2994 winpos->flags |= SWP_NOZORDER;
2996 else
2998 if ((winpos->hwnd == winpos->hwndInsertAfter) ||
2999 (winpos->hwnd == get_window_relative( winpos->hwndInsertAfter, GW_HWNDNEXT )))
3000 winpos->flags |= SWP_NOZORDER;
3002 done:
3003 release_win_ptr( win );
3004 return ret;
3007 /***********************************************************************
3008 * swp_owner_popups
3010 * fix Z order taking into account owned popups -
3011 * basically we need to maintain them above the window that owns them
3013 * FIXME: hide/show owned popups when owner visibility changes.
3015 static HWND swp_owner_popups( HWND hwnd, HWND after )
3017 HWND owner, *list = NULL;
3018 unsigned int i;
3020 TRACE( "(%p) after = %p\n", hwnd, after );
3022 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) return after;
3024 if ((owner = get_window_relative( hwnd, GW_OWNER )))
3026 /* make sure this popup stays above the owner */
3028 if (after != HWND_TOPMOST)
3030 if (!(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) return after;
3032 for (i = 0; list[i]; i++)
3034 BOOL topmost = (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST) != 0;
3036 if (list[i] == owner)
3038 if (i > 0) after = list[i-1];
3039 else after = topmost ? HWND_TOPMOST : HWND_TOP;
3040 break;
3043 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3045 if (!topmost) break;
3047 else if (list[i] == after) break;
3052 if (after == HWND_BOTTOM) goto done;
3053 if (!list && !(list = list_window_children( 0, get_desktop_window(), NULL, 0 ))) goto done;
3055 i = 0;
3056 if (after == HWND_TOP || after == HWND_NOTOPMOST)
3058 if (after == HWND_NOTOPMOST ||
3059 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST))
3061 /* skip all the topmost windows */
3062 while (list[i] && (get_window_long( list[i], GWL_EXSTYLE ) & WS_EX_TOPMOST)) i++;
3065 else if (after != HWND_TOPMOST)
3067 /* skip windows that are already placed correctly */
3068 for (i = 0; list[i]; i++)
3070 if (list[i] == after) break;
3071 if (list[i] == hwnd) goto done; /* nothing to do if window is moving backwards in z-order */
3075 for ( ; list[i]; i++)
3077 if (list[i] == hwnd) break;
3078 if (get_window_relative( list[i], GW_OWNER ) != hwnd) continue;
3079 TRACE( "moving %p owned by %p after %p\n", list[i], hwnd, after );
3080 NtUserSetWindowPos( list[i], after, 0, 0, 0, 0,
3081 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_DEFERERASE );
3082 after = list[i];
3085 done:
3086 free( list );
3087 return after;
3090 /* NtUserSetWindowPos implementation */
3091 BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y )
3093 RECT old_window_rect, old_client_rect, new_window_rect, new_client_rect, valid_rects[2];
3094 UINT orig_flags;
3095 BOOL ret = FALSE;
3096 DPI_AWARENESS_CONTEXT context;
3098 orig_flags = winpos->flags;
3100 /* First, check z-order arguments. */
3101 if (!(winpos->flags & SWP_NOZORDER))
3103 /* fix sign extension */
3104 if (winpos->hwndInsertAfter == (HWND)0xffff) winpos->hwndInsertAfter = HWND_TOPMOST;
3105 else if (winpos->hwndInsertAfter == (HWND)0xfffe) winpos->hwndInsertAfter = HWND_NOTOPMOST;
3107 if (!(winpos->hwndInsertAfter == HWND_TOP ||
3108 winpos->hwndInsertAfter == HWND_BOTTOM ||
3109 winpos->hwndInsertAfter == HWND_TOPMOST ||
3110 winpos->hwndInsertAfter == HWND_NOTOPMOST))
3112 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3113 HWND insertafter_parent = NtUserGetAncestor( winpos->hwndInsertAfter, GA_PARENT );
3115 /* hwndInsertAfter must be a sibling of the window */
3116 if (!insertafter_parent) return FALSE;
3117 if (insertafter_parent != parent) return TRUE;
3121 /* Make sure that coordinates are valid for WM_WINDOWPOSCHANGING */
3122 if (!(winpos->flags & SWP_NOMOVE))
3124 if (winpos->x < -32768) winpos->x = -32768;
3125 else if (winpos->x > 32767) winpos->x = 32767;
3126 if (winpos->y < -32768) winpos->y = -32768;
3127 else if (winpos->y > 32767) winpos->y = 32767;
3129 if (!(winpos->flags & SWP_NOSIZE))
3131 if (winpos->cx < 0) winpos->cx = 0;
3132 else if (winpos->cx > 32767) winpos->cx = 32767;
3133 if (winpos->cy < 0) winpos->cy = 0;
3134 else if (winpos->cy > 32767) winpos->cy = 32767;
3137 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( winpos->hwnd ));
3139 if (!calc_winpos( winpos, &old_window_rect, &old_client_rect,
3140 &new_window_rect, &new_client_rect )) goto done;
3142 /* Fix redundant flags */
3143 if (!fixup_swp_flags( winpos, &old_window_rect, parent_x, parent_y )) goto done;
3145 if((winpos->flags & (SWP_NOZORDER | SWP_HIDEWINDOW | SWP_SHOWWINDOW)) != SWP_NOZORDER)
3147 if (NtUserGetAncestor( winpos->hwnd, GA_PARENT ) == get_desktop_window())
3148 winpos->hwndInsertAfter = swp_owner_popups( winpos->hwnd, winpos->hwndInsertAfter );
3151 /* Common operations */
3153 calc_ncsize( winpos, &old_window_rect, &old_client_rect,
3154 &new_window_rect, &new_client_rect, valid_rects, parent_x, parent_y );
3156 if (!apply_window_pos( winpos->hwnd, winpos->hwndInsertAfter, winpos->flags,
3157 &new_window_rect, &new_client_rect, valid_rects ))
3158 goto done;
3160 if (user_callbacks)
3162 if (winpos->flags & SWP_HIDEWINDOW)
3163 user_callbacks->pHideCaret( winpos->hwnd );
3164 else if (winpos->flags & SWP_SHOWWINDOW)
3165 user_callbacks->pShowCaret( winpos->hwnd );
3168 if (!(winpos->flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW)))
3170 /* child windows get WM_CHILDACTIVATE message */
3171 if ((get_window_long( winpos->hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD)
3172 send_message( winpos->hwnd, WM_CHILDACTIVATE, 0, 0 );
3173 else if (user_callbacks)
3174 set_foreground_window( winpos->hwnd, FALSE );
3177 if(!(orig_flags & SWP_DEFERERASE))
3179 /* erase parent when hiding or resizing child */
3180 if ((orig_flags & SWP_HIDEWINDOW) ||
3181 (!(orig_flags & SWP_SHOWWINDOW) &&
3182 (winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOGEOMETRYCHANGE))
3184 HWND parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT );
3185 if (!parent || parent == get_desktop_window()) parent = winpos->hwnd;
3186 erase_now( parent, 0 );
3189 /* Give newly shown windows a chance to redraw */
3190 if(((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3191 && !(orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW))
3193 erase_now(winpos->hwnd, 0);
3197 /* And last, send the WM_WINDOWPOSCHANGED message */
3199 TRACE( "\tstatus flags = %04x\n", winpos->flags & SWP_AGG_STATUSFLAGS );
3201 if (((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE)
3202 && !((orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW)))
3204 /* WM_WINDOWPOSCHANGED is sent even if SWP_NOSENDCHANGING is set
3205 and always contains final window position.
3207 winpos->x = new_window_rect.left;
3208 winpos->y = new_window_rect.top;
3209 winpos->cx = new_window_rect.right - new_window_rect.left;
3210 winpos->cy = new_window_rect.bottom - new_window_rect.top;
3211 send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos );
3213 ret = TRUE;
3214 done:
3215 set_thread_dpi_awareness_context( context );
3216 return ret;
3219 /*******************************************************************
3220 * NtUserSetWindowPos (win32u.@)
3222 BOOL WINAPI NtUserSetWindowPos( HWND hwnd, HWND after, INT x, INT y, INT cx, INT cy, UINT flags )
3224 WINDOWPOS winpos;
3226 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n", hwnd, after, x, y, cx, cy, flags );
3227 if(TRACE_ON(win)) dump_winpos_flags(flags);
3229 if (is_broadcast( hwnd ))
3231 SetLastError( ERROR_INVALID_PARAMETER );
3232 return FALSE;
3235 winpos.hwnd = get_full_window_handle( hwnd );
3236 winpos.hwndInsertAfter = get_full_window_handle( after );
3237 winpos.x = x;
3238 winpos.y = y;
3239 winpos.cx = cx;
3240 winpos.cy = cy;
3241 winpos.flags = flags;
3243 map_dpi_winpos( &winpos );
3245 if (is_current_thread_window( hwnd ))
3246 return set_window_pos( &winpos, 0, 0 );
3248 if (flags & SWP_ASYNCWINDOWPOS)
3249 return NtUserMessageCall( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos,
3250 0, FNID_SENDNOTIFYMESSAGE, FALSE );
3251 else
3252 return send_message( winpos.hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)&winpos );
3255 typedef struct
3257 struct user_object obj;
3258 INT count;
3259 INT suggested_count;
3260 HWND parent;
3261 WINDOWPOS *winpos;
3262 } DWP;
3264 /* see BeginDeferWindowPos */
3265 HDWP begin_defer_window_pos( INT count )
3267 HDWP handle = 0;
3268 DWP *dwp;
3270 TRACE( "%d\n", count );
3272 if (count < 0)
3274 SetLastError( ERROR_INVALID_PARAMETER );
3275 return 0;
3277 /* Windows allows zero count, in which case it allocates context for 8 moves */
3278 if (count == 0) count = 8;
3280 if (!(dwp = malloc( sizeof(DWP) ))) return 0;
3282 dwp->count = 0;
3283 dwp->parent = 0;
3284 dwp->suggested_count = count;
3286 if (!(dwp->winpos = malloc( count * sizeof(WINDOWPOS) )) ||
3287 !(handle = alloc_user_handle( &dwp->obj, NTUSER_OBJ_WINPOS )))
3289 free( dwp->winpos );
3290 free( dwp );
3293 TRACE( "returning %p\n", handle );
3294 return handle;
3297 /***********************************************************************
3298 * NtUserDeferWindowPosAndBand (win32u.@)
3300 HDWP WINAPI NtUserDeferWindowPosAndBand( HDWP hdwp, HWND hwnd, HWND after,
3301 INT x, INT y, INT cx, INT cy,
3302 UINT flags, UINT unk1, UINT unk2 )
3304 HDWP retvalue = hdwp;
3305 WINDOWPOS winpos;
3306 DWP *dwp;
3307 int i;
3309 TRACE( "hdwp %p, hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3310 hdwp, hwnd, after, x, y, cx, cy, flags );
3312 winpos.hwnd = get_full_window_handle( hwnd );
3313 if (is_desktop_window( winpos.hwnd ) || !is_window( winpos.hwnd ))
3315 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
3316 return 0;
3319 winpos.hwndInsertAfter = get_full_window_handle( after );
3320 winpos.flags = flags;
3321 winpos.x = x;
3322 winpos.y = y;
3323 winpos.cx = cx;
3324 winpos.cy = cy;
3325 map_dpi_winpos( &winpos );
3327 if (!(dwp = get_user_handle_ptr( hdwp, NTUSER_OBJ_WINPOS ))) return 0;
3328 if (dwp == OBJ_OTHER_PROCESS)
3330 FIXME( "other process handle %p\n", hdwp );
3331 return 0;
3334 for (i = 0; i < dwp->count; i++)
3336 if (dwp->winpos[i].hwnd == winpos.hwnd)
3338 /* Merge with the other changes */
3339 if (!(flags & SWP_NOZORDER))
3341 dwp->winpos[i].hwndInsertAfter = winpos.hwndInsertAfter;
3343 if (!(flags & SWP_NOMOVE))
3345 dwp->winpos[i].x = winpos.x;
3346 dwp->winpos[i].y = winpos.y;
3348 if (!(flags & SWP_NOSIZE))
3350 dwp->winpos[i].cx = winpos.cx;
3351 dwp->winpos[i].cy = winpos.cy;
3353 dwp->winpos[i].flags &= flags | ~(SWP_NOSIZE | SWP_NOMOVE |
3354 SWP_NOZORDER | SWP_NOREDRAW |
3355 SWP_NOACTIVATE | SWP_NOCOPYBITS|
3356 SWP_NOOWNERZORDER);
3357 dwp->winpos[i].flags |= flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW |
3358 SWP_FRAMECHANGED);
3359 goto done;
3362 if (dwp->count >= dwp->suggested_count)
3364 WINDOWPOS *newpos = realloc( dwp->winpos, dwp->suggested_count * 2 * sizeof(WINDOWPOS) );
3365 if (!newpos)
3367 retvalue = 0;
3368 goto done;
3370 dwp->suggested_count *= 2;
3371 dwp->winpos = newpos;
3373 dwp->winpos[dwp->count++] = winpos;
3374 done:
3375 release_user_handle_ptr( dwp );
3376 return retvalue;
3379 /***********************************************************************
3380 * NtUserEndDeferWindowPosEx (win32u.@)
3382 BOOL WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async )
3384 WINDOWPOS *winpos;
3385 DWP *dwp;
3386 int i;
3388 TRACE( "%p\n", hdwp );
3390 if (async) FIXME( "async not supported\n" );
3392 if (!(dwp = free_user_handle( hdwp, NTUSER_OBJ_WINPOS ))) return FALSE;
3393 if (dwp == OBJ_OTHER_PROCESS)
3395 FIXME( "other process handle %p\n", hdwp );
3396 return FALSE;
3399 for (i = 0, winpos = dwp->winpos; i < dwp->count; i++, winpos++)
3401 TRACE( "hwnd %p, after %p, %d,%d (%dx%d), flags %08x\n",
3402 winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
3403 winpos->cx, winpos->cy, winpos->flags );
3405 if (is_current_thread_window( winpos->hwnd ))
3406 set_window_pos( winpos, 0, 0 );
3407 else
3408 send_message( winpos->hwnd, WM_WINE_SETWINDOWPOS, 0, (LPARAM)winpos );
3410 free( dwp->winpos );
3411 free( dwp );
3412 return TRUE;
3415 /***********************************************************************
3416 * win_set_flags
3418 * Set the flags of a window and return the previous value.
3420 UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask )
3422 WND *win = get_win_ptr( hwnd );
3423 UINT ret;
3425 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return 0;
3426 ret = win->flags;
3427 win->flags = (ret & ~clear_mask) | set_mask;
3428 release_win_ptr( win );
3429 return ret;
3432 /*******************************************************************
3433 * can_activate_window
3435 * Check if we can activate the specified window.
3437 static BOOL can_activate_window( HWND hwnd )
3439 LONG style;
3441 if (!hwnd) return FALSE;
3442 style = get_window_long( hwnd, GWL_STYLE );
3443 if (!(style & WS_VISIBLE)) return FALSE;
3444 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
3445 return !(style & WS_DISABLED);
3448 /*******************************************************************
3449 * activate_other_window
3451 * Activates window other than hwnd.
3453 static void activate_other_window( HWND hwnd )
3455 HWND hwnd_to, fg;
3457 if ((get_window_long( hwnd, GWL_STYLE ) & WS_POPUP) &&
3458 (hwnd_to = get_window_relative( hwnd, GW_OWNER )))
3460 hwnd_to = NtUserGetAncestor( hwnd_to, GA_ROOT );
3461 if (can_activate_window( hwnd_to )) goto done;
3464 hwnd_to = hwnd;
3465 for (;;)
3467 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3468 if (can_activate_window( hwnd_to )) goto done;
3471 hwnd_to = get_window_relative( get_desktop_window(), GW_CHILD );
3472 for (;;)
3474 if (hwnd_to == hwnd)
3476 hwnd_to = 0;
3477 break;
3479 if (can_activate_window( hwnd_to )) goto done;
3480 if (!(hwnd_to = get_window_relative( hwnd_to, GW_HWNDNEXT ))) break;
3483 done:
3484 fg = NtUserGetForegroundWindow();
3485 TRACE( "win = %p fg = %p\n", hwnd_to, fg );
3486 if (!fg || hwnd == fg)
3488 if (set_foreground_window( hwnd_to, FALSE )) return;
3490 if (NtUserSetActiveWindow( hwnd_to )) NtUserSetActiveWindow( 0 );
3493 /*******************************************************************
3494 * send_parent_notify
3496 static void send_parent_notify( HWND hwnd, UINT msg )
3498 if ((get_window_long( hwnd, GWL_STYLE ) & (WS_CHILD | WS_POPUP)) == WS_CHILD &&
3499 !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY))
3501 HWND parent = get_parent( hwnd );
3502 if (parent && parent != get_desktop_window())
3503 send_message( parent, WM_PARENTNOTIFY,
3504 MAKEWPARAM( msg, get_window_long( hwnd, GWLP_ID )), (LPARAM)hwnd );
3508 /*******************************************************************
3509 * get_min_max_info
3511 * Get the minimized and maximized information for a window.
3513 static MINMAXINFO get_min_max_info( HWND hwnd )
3515 LONG style = get_window_long( hwnd, GWL_STYLE );
3516 LONG exstyle = get_window_long( hwnd, GWL_EXSTYLE );
3517 DPI_AWARENESS_CONTEXT context;
3518 RECT rc_work, rc_primary;
3519 LONG adjusted_style;
3520 MINMAXINFO minmax;
3521 INT xinc, yinc;
3522 RECT rc;
3523 WND *win;
3525 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
3527 /* Compute default values */
3529 get_window_rect( hwnd, &rc, get_thread_dpi() );
3530 minmax.ptReserved.x = rc.left;
3531 minmax.ptReserved.y = rc.top;
3533 if ((style & WS_CAPTION) == WS_CAPTION)
3534 adjusted_style = style & ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */
3535 else
3536 adjusted_style = style;
3538 get_client_rect( NtUserGetAncestor( hwnd, GA_PARENT ), &rc );
3539 if (user_callbacks)
3540 user_callbacks->pAdjustWindowRectEx( &rc, adjusted_style,
3541 (style & WS_POPUP) && get_menu( hwnd ), exstyle);
3543 xinc = -rc.left;
3544 yinc = -rc.top;
3546 minmax.ptMaxSize.x = rc.right - rc.left;
3547 minmax.ptMaxSize.y = rc.bottom - rc.top;
3548 if (style & (WS_DLGFRAME | WS_BORDER))
3550 minmax.ptMinTrackSize.x = get_system_metrics( SM_CXMINTRACK );
3551 minmax.ptMinTrackSize.y = get_system_metrics( SM_CYMINTRACK );
3553 else
3555 minmax.ptMinTrackSize.x = 2 * xinc;
3556 minmax.ptMinTrackSize.y = 2 * yinc;
3558 minmax.ptMaxTrackSize.x = get_system_metrics( SM_CXMAXTRACK );
3559 minmax.ptMaxTrackSize.y = get_system_metrics( SM_CYMAXTRACK );
3560 minmax.ptMaxPosition.x = -xinc;
3561 minmax.ptMaxPosition.y = -yinc;
3563 if ((win = get_win_ptr( hwnd )) && win != WND_DESKTOP && win != WND_OTHER_PROCESS)
3565 if (!empty_point( win->max_pos )) minmax.ptMaxPosition = win->max_pos;
3566 release_win_ptr( win );
3569 send_message( hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax );
3571 /* if the app didn't change the values, adapt them for the current monitor */
3573 if (get_work_rect( hwnd, &rc_work ))
3575 rc_primary = get_primary_monitor_rect( get_thread_dpi() );
3576 if (minmax.ptMaxSize.x == (rc_primary.right - rc_primary.left) + 2 * xinc &&
3577 minmax.ptMaxSize.y == (rc_primary.bottom - rc_primary.top) + 2 * yinc)
3579 minmax.ptMaxSize.x = (rc_work.right - rc_work.left) + 2 * xinc;
3580 minmax.ptMaxSize.y = (rc_work.bottom - rc_work.top) + 2 * yinc;
3582 if (minmax.ptMaxPosition.x == -xinc && minmax.ptMaxPosition.y == -yinc)
3584 minmax.ptMaxPosition.x = rc_work.left - xinc;
3585 minmax.ptMaxPosition.y = rc_work.top - yinc;
3589 TRACE( "%d %d / %d %d / %d %d / %d %d\n",
3590 minmax.ptMaxSize.x, minmax.ptMaxSize.y,
3591 minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
3592 minmax.ptMaxTrackSize.x, minmax.ptMaxTrackSize.y,
3593 minmax.ptMinTrackSize.x, minmax.ptMinTrackSize.y );
3595 minmax.ptMaxTrackSize.x = max( minmax.ptMaxTrackSize.x, minmax.ptMinTrackSize.x );
3596 minmax.ptMaxTrackSize.y = max( minmax.ptMaxTrackSize.y, minmax.ptMinTrackSize.y );
3598 set_thread_dpi_awareness_context( context );
3599 return minmax;
3602 static POINT get_first_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
3603 int width, int height )
3605 POINT ret;
3607 if (mm->iArrange & ARW_STARTRIGHT)
3608 ret.x = parent->right - mm->iHorzGap - width;
3609 else
3610 ret.x = parent->left + mm->iHorzGap;
3611 if (mm->iArrange & ARW_STARTTOP)
3612 ret.y = parent->top + mm->iVertGap;
3613 else
3614 ret.y = parent->bottom - mm->iVertGap - height;
3616 return ret;
3619 static void get_next_minimized_child_pos( const RECT *parent, const MINIMIZEDMETRICS *mm,
3620 int width, int height, POINT *pos )
3622 BOOL next;
3624 if (mm->iArrange & ARW_UP) /* == ARW_DOWN */
3626 if (mm->iArrange & ARW_STARTTOP)
3628 pos->y += height + mm->iVertGap;
3629 if ((next = pos->y + height > parent->bottom))
3630 pos->y = parent->top + mm->iVertGap;
3632 else
3634 pos->y -= height + mm->iVertGap;
3635 if ((next = pos->y < parent->top))
3636 pos->y = parent->bottom - mm->iVertGap - height;
3639 if (next)
3641 if (mm->iArrange & ARW_STARTRIGHT)
3642 pos->x -= width + mm->iHorzGap;
3643 else
3644 pos->x += width + mm->iHorzGap;
3647 else
3649 if (mm->iArrange & ARW_STARTRIGHT)
3651 pos->x -= width + mm->iHorzGap;
3652 if ((next = pos->x < parent->left))
3653 pos->x = parent->right - mm->iHorzGap - width;
3655 else
3657 pos->x += width + mm->iHorzGap;
3658 if ((next = pos->x + width > parent->right))
3659 pos->x = parent->left + mm->iHorzGap;
3662 if (next)
3664 if (mm->iArrange & ARW_STARTTOP)
3665 pos->y += height + mm->iVertGap;
3666 else
3667 pos->y -= height + mm->iVertGap;
3672 static POINT get_minimized_pos( HWND hwnd, POINT pt )
3674 RECT rect, parent_rect;
3675 HWND parent, child;
3676 HRGN hrgn, tmp;
3677 MINIMIZEDMETRICS metrics;
3678 int width, height;
3680 parent = NtUserGetAncestor( hwnd, GA_PARENT );
3681 if (parent == get_desktop_window())
3683 MONITORINFO mon_info;
3684 HMONITOR monitor = monitor_from_window( hwnd, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
3686 mon_info.cbSize = sizeof( mon_info );
3687 get_monitor_info( monitor, &mon_info );
3688 parent_rect = mon_info.rcWork;
3690 else get_client_rect( parent, &parent_rect );
3692 if (pt.x >= parent_rect.left && (pt.x + get_system_metrics( SM_CXMINIMIZED ) < parent_rect.right) &&
3693 pt.y >= parent_rect.top && (pt.y + get_system_metrics( SM_CYMINIMIZED ) < parent_rect.bottom))
3694 return pt; /* The icon already has a suitable position */
3696 width = get_system_metrics( SM_CXMINIMIZED );
3697 height = get_system_metrics( SM_CYMINIMIZED );
3699 metrics.cbSize = sizeof(metrics);
3700 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
3702 /* Check if another icon already occupies this spot */
3703 /* FIXME: this is completely inefficient */
3705 hrgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
3706 tmp = NtGdiCreateRectRgn( 0, 0, 0, 0 );
3707 for (child = get_window_relative( parent, GW_CHILD );
3708 child;
3709 child = get_window_relative( child, GW_HWNDNEXT ))
3711 if (child == hwnd) continue;
3712 if ((get_window_long( child, GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != (WS_VISIBLE|WS_MINIMIZE))
3713 continue;
3714 if (get_window_rects( child, COORDS_PARENT, &rect, NULL, get_thread_dpi() ))
3716 NtGdiSetRectRgn( tmp, rect.left, rect.top, rect.right, rect.bottom );
3717 NtGdiCombineRgn( hrgn, hrgn, tmp, RGN_OR );
3720 NtGdiDeleteObjectApp( tmp );
3722 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
3723 for (;;)
3725 set_rect( &rect, pt.x, pt.y, pt.x + width, pt.y + height );
3726 if (!NtGdiRectInRegion( hrgn, &rect ))
3727 break;
3729 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
3732 NtGdiDeleteObjectApp( hrgn );
3733 return pt;
3736 /***********************************************************************
3737 * window_min_maximize
3739 static UINT window_min_maximize( HWND hwnd, UINT cmd, RECT *rect )
3741 UINT swp_flags = 0;
3742 LONG old_style;
3743 MINMAXINFO minmax;
3744 WINDOWPLACEMENT wpl;
3746 TRACE( "%p %u\n", hwnd, cmd );
3748 wpl.length = sizeof(wpl);
3749 get_window_placement( hwnd, &wpl );
3751 if (call_hooks( WH_CBT, HCBT_MINMAX, (WPARAM)hwnd, cmd, TRUE ))
3752 return SWP_NOSIZE | SWP_NOMOVE;
3754 if (is_iconic( hwnd ))
3756 switch (cmd)
3758 case SW_SHOWMINNOACTIVE:
3759 case SW_SHOWMINIMIZED:
3760 case SW_FORCEMINIMIZE:
3761 case SW_MINIMIZE:
3762 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
3764 set_rect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
3765 wpl.ptMinPosition.x + get_system_metrics( SM_CXMINIMIZED ),
3766 wpl.ptMinPosition.y + get_system_metrics( SM_CYMINIMIZED ));
3767 return SWP_NOSIZE | SWP_NOMOVE;
3769 if (!send_message( hwnd, WM_QUERYOPEN, 0, 0 )) return SWP_NOSIZE | SWP_NOMOVE;
3770 swp_flags |= SWP_NOCOPYBITS;
3773 switch( cmd )
3775 case SW_SHOWMINNOACTIVE:
3776 case SW_SHOWMINIMIZED:
3777 case SW_FORCEMINIMIZE:
3778 case SW_MINIMIZE:
3779 if (is_zoomed( hwnd )) win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
3780 else win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
3782 if (get_focus() == hwnd)
3784 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
3785 NtUserSetFocus( NtUserGetAncestor( hwnd, GA_PARENT ));
3786 else
3787 NtUserSetFocus( 0 );
3790 old_style = set_window_style( hwnd, WS_MINIMIZE, WS_MAXIMIZE );
3792 wpl.ptMinPosition = get_minimized_pos( hwnd, wpl.ptMinPosition );
3794 if (!(old_style & WS_MINIMIZE)) swp_flags |= SWP_STATECHANGED;
3795 set_rect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
3796 wpl.ptMinPosition.x + get_system_metrics(SM_CXMINIMIZED),
3797 wpl.ptMinPosition.y + get_system_metrics(SM_CYMINIMIZED) );
3798 swp_flags |= SWP_NOCOPYBITS;
3799 break;
3801 case SW_MAXIMIZE:
3802 old_style = get_window_long( hwnd, GWL_STYLE );
3803 if ((old_style & WS_MAXIMIZE) && (old_style & WS_VISIBLE)) return SWP_NOSIZE | SWP_NOMOVE;
3805 minmax = get_min_max_info( hwnd );
3807 old_style = set_window_style( hwnd, WS_MAXIMIZE, WS_MINIMIZE );
3808 if (old_style & WS_MINIMIZE)
3809 win_set_flags( hwnd, WIN_RESTORE_MAX, 0 );
3811 if (!(old_style & WS_MAXIMIZE)) swp_flags |= SWP_STATECHANGED;
3812 set_rect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
3813 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
3814 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
3815 break;
3817 case SW_SHOWNOACTIVATE:
3818 win_set_flags( hwnd, 0, WIN_RESTORE_MAX );
3819 /* fall through */
3820 case SW_SHOWNORMAL:
3821 case SW_RESTORE:
3822 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
3823 old_style = set_window_style( hwnd, 0, WS_MINIMIZE | WS_MAXIMIZE );
3824 if (old_style & WS_MINIMIZE)
3826 if (win_get_flags( hwnd ) & WIN_RESTORE_MAX)
3828 /* Restore to maximized position */
3829 minmax = get_min_max_info( hwnd );
3830 set_window_style( hwnd, WS_MAXIMIZE, 0 );
3831 swp_flags |= SWP_STATECHANGED;
3832 set_rect( rect, minmax.ptMaxPosition.x, minmax.ptMaxPosition.y,
3833 minmax.ptMaxPosition.x + minmax.ptMaxSize.x,
3834 minmax.ptMaxPosition.y + minmax.ptMaxSize.y );
3835 break;
3838 else if (!(old_style & WS_MAXIMIZE)) break;
3840 swp_flags |= SWP_STATECHANGED;
3842 /* Restore to normal position */
3844 *rect = wpl.rcNormalPosition;
3845 break;
3848 return swp_flags;
3851 /* see ArrangeIconicWindows */
3852 static UINT arrange_iconic_windows( HWND parent )
3854 int width, height, count = 0;
3855 MINIMIZEDMETRICS metrics;
3856 RECT parent_rect;
3857 HWND child;
3858 POINT pt;
3860 metrics.cbSize = sizeof(metrics);
3861 NtUserSystemParametersInfo( SPI_GETMINIMIZEDMETRICS, sizeof(metrics), &metrics, 0 );
3862 width = get_system_metrics( SM_CXMINIMIZED );
3863 height = get_system_metrics( SM_CYMINIMIZED );
3865 if (parent == get_desktop_window())
3867 MONITORINFO mon_info;
3868 HMONITOR monitor = monitor_from_window( 0, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
3870 mon_info.cbSize = sizeof( mon_info );
3871 get_monitor_info( monitor, &mon_info );
3872 parent_rect = mon_info.rcWork;
3874 else get_client_rect( parent, &parent_rect );
3876 pt = get_first_minimized_child_pos( &parent_rect, &metrics, width, height );
3878 child = get_window_relative( parent, GW_CHILD );
3879 while (child)
3881 if (is_iconic( child ))
3883 NtUserSetWindowPos( child, 0, pt.x, pt.y, 0, 0,
3884 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
3885 get_next_minimized_child_pos( &parent_rect, &metrics, width, height, &pt );
3886 count++;
3888 child = get_window_relative( child, GW_HWNDNEXT );
3890 return count;
3893 /*******************************************************************
3894 * update_window_state
3896 * Trigger an update of the window's driver state and surface.
3898 void update_window_state( HWND hwnd )
3900 DPI_AWARENESS_CONTEXT context;
3901 RECT window_rect, client_rect, valid_rects[2];
3903 if (!is_current_thread_window( hwnd ))
3905 post_message( hwnd, WM_WINE_UPDATEWINDOWSTATE, 0, 0 );
3906 return;
3909 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
3910 get_window_rects( hwnd, COORDS_PARENT, &window_rect, &client_rect, get_thread_dpi() );
3911 valid_rects[0] = valid_rects[1] = client_rect;
3912 apply_window_pos( hwnd, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE |
3913 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW,
3914 &window_rect, &client_rect, valid_rects );
3915 set_thread_dpi_awareness_context( context );
3918 /***********************************************************************
3919 * show_window
3921 * Implementation of ShowWindow and ShowWindowAsync.
3923 static BOOL show_window( HWND hwnd, INT cmd )
3925 WND *win;
3926 HWND parent;
3927 DPI_AWARENESS_CONTEXT context;
3928 LONG style = get_window_long( hwnd, GWL_STYLE );
3929 BOOL was_visible = (style & WS_VISIBLE) != 0;
3930 BOOL show_flag = TRUE;
3931 RECT newPos = {0, 0, 0, 0};
3932 UINT new_swp, swp = 0;
3934 TRACE( "hwnd=%p, cmd=%d, was_visible %d\n", hwnd, cmd, was_visible );
3936 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
3938 switch(cmd)
3940 case SW_HIDE:
3941 if (!was_visible) goto done;
3942 show_flag = FALSE;
3943 swp |= SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE;
3944 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
3945 break;
3947 case SW_SHOWMINNOACTIVE:
3948 case SW_MINIMIZE:
3949 case SW_FORCEMINIMIZE: /* FIXME: Does not work if thread is hung. */
3950 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
3951 /* fall through */
3952 case SW_SHOWMINIMIZED:
3953 swp |= SWP_SHOWWINDOW | SWP_FRAMECHANGED;
3954 swp |= window_min_maximize( hwnd, cmd, &newPos );
3955 if ((style & WS_MINIMIZE) && was_visible) goto done;
3956 break;
3958 case SW_SHOWMAXIMIZED: /* same as SW_MAXIMIZE */
3959 if (!was_visible) swp |= SWP_SHOWWINDOW;
3960 swp |= SWP_FRAMECHANGED;
3961 swp |= window_min_maximize( hwnd, SW_MAXIMIZE, &newPos );
3962 if ((style & WS_MAXIMIZE) && was_visible) goto done;
3963 break;
3965 case SW_SHOWNA:
3966 swp |= SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
3967 if (style & WS_CHILD) swp |= SWP_NOZORDER;
3968 break;
3970 case SW_SHOW:
3971 if (was_visible) goto done;
3972 swp |= SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
3973 if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
3974 break;
3976 case SW_SHOWNOACTIVATE:
3977 swp |= SWP_NOACTIVATE | SWP_NOZORDER;
3978 /* fall through */
3979 case SW_RESTORE:
3980 /* fall through */
3981 case SW_SHOWNORMAL: /* same as SW_NORMAL: */
3982 case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
3983 if (!was_visible) swp |= SWP_SHOWWINDOW;
3984 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
3986 swp |= SWP_FRAMECHANGED;
3987 swp |= window_min_maximize( hwnd, cmd, &newPos );
3989 else
3991 if (was_visible) goto done;
3992 swp |= SWP_NOSIZE | SWP_NOMOVE;
3994 if (style & WS_CHILD && !(swp & SWP_STATECHANGED)) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
3995 break;
3997 default:
3998 goto done;
4001 if ((show_flag != was_visible || cmd == SW_SHOWNA) && cmd != SW_SHOWMAXIMIZED && !(swp & SWP_STATECHANGED))
4003 send_message( hwnd, WM_SHOWWINDOW, show_flag, 0 );
4004 if (!is_window( hwnd )) goto done;
4007 if (IsRectEmpty( &newPos )) new_swp = swp;
4008 else if ((new_swp = user_driver->pShowWindow( hwnd, cmd, &newPos, swp )) == ~0)
4010 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) new_swp = swp;
4011 else if (is_iconic( hwnd ) && (newPos.left != -32000 || newPos.top != -32000))
4013 offset_rect( &newPos, -32000 - newPos.left, -32000 - newPos.top );
4014 new_swp = swp & ~(SWP_NOMOVE | SWP_NOCLIENTMOVE);
4016 else new_swp = swp;
4018 swp = new_swp;
4020 parent = NtUserGetAncestor( hwnd, GA_PARENT );
4021 if (parent && !is_window_visible( parent ) && !(swp & SWP_STATECHANGED))
4023 /* if parent is not visible simply toggle WS_VISIBLE and return */
4024 if (show_flag) set_window_style( hwnd, WS_VISIBLE, 0 );
4025 else set_window_style( hwnd, 0, WS_VISIBLE );
4027 else
4028 NtUserSetWindowPos( hwnd, HWND_TOP, newPos.left, newPos.top,
4029 newPos.right - newPos.left, newPos.bottom - newPos.top, swp );
4031 if (cmd == SW_HIDE)
4033 HWND hFocus;
4035 /* FIXME: This will cause the window to be activated irrespective
4036 * of whether it is owned by the same thread. Has to be done
4037 * asynchronously.
4040 if (hwnd == get_active_window()) activate_other_window( hwnd );
4042 /* Revert focus to parent */
4043 hFocus = get_focus();
4044 if (hwnd == hFocus)
4046 HWND parent = NtUserGetAncestor(hwnd, GA_PARENT);
4047 if (parent == get_desktop_window()) parent = 0;
4048 NtUserSetFocus(parent);
4050 goto done;
4053 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) goto done;
4055 if (win->flags & WIN_NEED_SIZE)
4057 /* should happen only in CreateWindowEx() */
4058 int wParam = SIZE_RESTORED;
4059 RECT client;
4060 LPARAM lparam;
4062 get_window_rects( hwnd, COORDS_PARENT, NULL, &client, get_thread_dpi() );
4063 lparam = MAKELONG( client.right - client.left, client.bottom - client.top );
4064 win->flags &= ~WIN_NEED_SIZE;
4065 if (win->dwStyle & WS_MAXIMIZE) wParam = SIZE_MAXIMIZED;
4066 else if (win->dwStyle & WS_MINIMIZE)
4068 wParam = SIZE_MINIMIZED;
4069 lparam = 0;
4071 release_win_ptr( win );
4073 send_message( hwnd, WM_SIZE, wParam, lparam );
4074 send_message( hwnd, WM_MOVE, 0, MAKELONG( client.left, client.top ));
4076 else release_win_ptr( win );
4078 /* if previous state was minimized Windows sets focus to the window */
4079 if (style & WS_MINIMIZE)
4081 NtUserSetFocus( hwnd );
4082 /* Send a WM_ACTIVATE message for a top level window, even if the window is already active */
4083 if (NtUserGetAncestor( hwnd, GA_ROOT ) == hwnd && !(swp & SWP_NOACTIVATE))
4084 send_message( hwnd, WM_ACTIVATE, WA_ACTIVE, 0 );
4087 done:
4088 set_thread_dpi_awareness_context( context );
4089 return was_visible;
4092 /***********************************************************************
4093 * NtUserShowWindowAsync (win32u.@)
4095 * doesn't wait; returns immediately.
4096 * used by threads to toggle windows in other (possibly hanging) threads
4098 BOOL WINAPI NtUserShowWindowAsync( HWND hwnd, INT cmd )
4100 HWND full_handle;
4102 if (is_broadcast(hwnd))
4104 SetLastError( ERROR_INVALID_PARAMETER );
4105 return FALSE;
4108 if ((full_handle = is_current_thread_window( hwnd )))
4109 return show_window( full_handle, cmd );
4111 return NtUserMessageCall( hwnd, WM_WINE_SHOWWINDOW, cmd, 0, 0,
4112 FNID_SENDNOTIFYMESSAGE, FALSE );
4115 /***********************************************************************
4116 * NtUserShowWindow (win32u.@)
4118 BOOL WINAPI NtUserShowWindow( HWND hwnd, INT cmd )
4120 HWND full_handle;
4122 if (is_broadcast(hwnd))
4124 SetLastError( ERROR_INVALID_PARAMETER );
4125 return FALSE;
4127 if ((full_handle = is_current_thread_window( hwnd )))
4128 return show_window( full_handle, cmd );
4130 if ((cmd == SW_HIDE) && !(get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4131 return FALSE;
4133 if ((cmd == SW_SHOW) && (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE))
4134 return TRUE;
4136 return send_message( hwnd, WM_WINE_SHOWWINDOW, cmd, 0 );
4139 /*******************************************************************
4140 * NtUserFlashWindowEx (win32u.@)
4142 BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info )
4144 WND *win;
4146 TRACE( "%p\n", info );
4148 if (!info)
4150 SetLastError( ERROR_NOACCESS );
4151 return FALSE;
4154 if (!info->hwnd || info->cbSize != sizeof(FLASHWINFO) || !is_window( info->hwnd ))
4156 SetLastError( ERROR_INVALID_PARAMETER );
4157 return FALSE;
4159 FIXME( "%p - semi-stub\n", info );
4161 if (is_iconic( info->hwnd ))
4163 NtUserRedrawWindow( info->hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_FRAME );
4165 win = get_win_ptr( info->hwnd );
4166 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4167 if (info->dwFlags && !(win->flags & WIN_NCACTIVATED))
4169 win->flags |= WIN_NCACTIVATED;
4171 else
4173 win->flags &= ~WIN_NCACTIVATED;
4175 release_win_ptr( win );
4176 user_driver->pFlashWindowEx( info );
4177 return TRUE;
4179 else
4181 WPARAM wparam;
4182 HWND hwnd = info->hwnd;
4184 win = get_win_ptr( hwnd );
4185 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
4186 hwnd = win->obj.handle; /* make it a full handle */
4188 if (info->dwFlags) wparam = !(win->flags & WIN_NCACTIVATED);
4189 else wparam = (hwnd == NtUserGetForegroundWindow());
4191 release_win_ptr( win );
4192 send_message( hwnd, WM_NCACTIVATE, wparam, 0 );
4193 user_driver->pFlashWindowEx( info );
4194 return wparam;
4198 /* see GetWindowContextHelpId */
4199 static DWORD get_window_context_help_id( HWND hwnd )
4201 DWORD retval;
4202 WND *win = get_win_ptr( hwnd );
4203 if (!win || win == WND_DESKTOP) return 0;
4204 if (win == WND_OTHER_PROCESS)
4206 if (is_window( hwnd )) FIXME( "not supported on other process window %p\n", hwnd );
4207 return 0;
4209 retval = win->helpContext;
4210 release_win_ptr( win );
4211 return retval;
4214 /***********************************************************************
4215 * send_destroy_message
4217 static void send_destroy_message( HWND hwnd )
4219 GUITHREADINFO info;
4221 info.cbSize = sizeof(info);
4222 if (NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ))
4224 if (hwnd == info.hwndCaret && user_callbacks) user_callbacks->pDestroyCaret();
4225 if (hwnd == info.hwndActive) activate_other_window( hwnd );
4228 if (hwnd == NtUserGetClipboardOwner()) release_clipboard_owner( hwnd );
4230 send_message( hwnd, WM_DESTROY, 0, 0);
4233 * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
4234 * make sure that the window still exists when we come back.
4236 if (is_window(hwnd))
4238 HWND *children;
4239 int i;
4241 if (!(children = list_window_children( 0, hwnd, NULL, 0 ))) return;
4243 for (i = 0; children[i]; i++)
4245 if (is_window( children[i] )) send_destroy_message( children[i] );
4247 free( children );
4249 else
4250 WARN( "\tdestroyed itself while in WM_DESTROY!\n" );
4253 /***********************************************************************
4254 * free_window_handle
4256 * Free a window handle.
4258 static void free_window_handle( HWND hwnd )
4260 WND *win;
4262 TRACE( "\n" );
4264 if ((win = get_user_handle_ptr( hwnd, NTUSER_OBJ_WINDOW )) && win != OBJ_OTHER_PROCESS)
4266 SERVER_START_REQ( destroy_window )
4268 req->handle = wine_server_user_handle( hwnd );
4269 wine_server_call( req );
4270 set_user_handle_ptr( hwnd, NULL );
4272 SERVER_END_REQ;
4273 user_unlock();
4274 if (user_callbacks) user_callbacks->free_win_ptr( win );
4275 free( win );
4279 /***********************************************************************
4280 * destroy_window
4282 LRESULT destroy_window( HWND hwnd )
4284 struct window_surface *surface;
4285 HMENU menu = 0, sys_menu;
4286 WND *win;
4287 HWND *children;
4289 TRACE( "%p\n", hwnd );
4291 /* destroy default IME window */
4292 if (win_set_flags( hwnd, 0, WIN_HAS_IME_WIN ) & WIN_HAS_IME_WIN)
4294 TRACE("unregister IME window for %p\n", hwnd);
4295 if (user_callbacks) user_callbacks->unregister_imm( hwnd );
4298 /* free child windows */
4299 if ((children = list_window_children( 0, hwnd, NULL, 0 )))
4301 int i;
4302 for (i = 0; children[i]; i++)
4304 if (is_current_thread_window( children[i] ))
4305 destroy_window( children[i] );
4306 else
4307 NtUserMessageCall( children[i], WM_WINE_DESTROYWINDOW, 0, 0,
4308 0, FNID_SENDNOTIFYMESSAGE, FALSE );
4310 free( children );
4313 /* Unlink now so we won't bother with the children later on */
4314 SERVER_START_REQ( set_parent )
4316 req->handle = wine_server_user_handle( hwnd );
4317 req->parent = 0;
4318 wine_server_call( req );
4320 SERVER_END_REQ;
4322 send_message( hwnd, WM_NCDESTROY, 0, 0 );
4324 /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
4326 /* free resources associated with the window */
4328 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return 0;
4329 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
4330 menu = (HMENU)win->wIDmenu;
4331 sys_menu = win->hSysMenu;
4332 free_dce( win->dce, hwnd );
4333 win->dce = NULL;
4334 NtUserDestroyCursor( win->hIconSmall2, 0 );
4335 surface = win->surface;
4336 win->surface = NULL;
4337 release_win_ptr( win );
4339 NtUserDestroyMenu( menu );
4340 NtUserDestroyMenu( sys_menu );
4341 if (surface)
4343 register_window_surface( surface, NULL );
4344 window_surface_release( surface );
4347 user_driver->pDestroyWindow( hwnd );
4349 free_window_handle( hwnd );
4350 return 0;
4353 /***********************************************************************
4354 * NtUserDestroyWindow (win32u.@)
4356 BOOL WINAPI NtUserDestroyWindow( HWND hwnd )
4358 BOOL is_child;
4360 if (!(hwnd = is_current_thread_window( hwnd )) || is_desktop_window( hwnd ))
4362 SetLastError( ERROR_ACCESS_DENIED );
4363 return FALSE;
4366 TRACE( "(%p)\n", hwnd );
4368 if (call_hooks( WH_CBT, HCBT_DESTROYWND, (WPARAM)hwnd, 0, TRUE )) return FALSE;
4370 if (user_callbacks && user_callbacks->is_menu_active() == hwnd)
4371 user_callbacks->pEndMenu();
4373 is_child = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) != 0;
4375 if (is_child)
4377 if (!is_exiting_thread( GetCurrentThreadId() ))
4378 send_parent_notify( hwnd, WM_DESTROY );
4380 else if (!get_window_relative( hwnd, GW_OWNER ))
4382 call_hooks( WH_SHELL, HSHELL_WINDOWDESTROYED, (WPARAM)hwnd, 0L, TRUE );
4383 /* FIXME: clean up palette - see "Internals" p.352 */
4386 if (!is_window( hwnd )) return TRUE;
4388 /* Hide the window */
4389 if (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE)
4391 /* Only child windows receive WM_SHOWWINDOW in DestroyWindow() */
4392 if (is_child)
4393 NtUserShowWindow( hwnd, SW_HIDE );
4394 else
4395 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
4396 SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW );
4399 if (!is_window( hwnd )) return TRUE;
4401 /* Recursively destroy child windows */
4402 if (!is_child)
4404 for (;;)
4406 BOOL got_one = FALSE;
4407 HWND *children;
4408 unsigned int i;
4410 if (!(children = list_window_children( 0, get_desktop_window(), NULL, 0 ))) break;
4412 for (i = 0; children[i]; i++)
4414 if (get_window_relative( children[i], GW_OWNER ) != hwnd) continue;
4415 if (is_current_thread_window( children[i] ))
4417 NtUserDestroyWindow( children[i] );
4418 got_one = TRUE;
4419 continue;
4421 set_window_owner( children[i], 0 );
4423 free( children );
4424 if (!got_one) break;
4428 send_destroy_message( hwnd );
4429 if (!is_window( hwnd )) return TRUE;
4431 destroy_window( hwnd );
4432 return TRUE;
4435 /*****************************************************************************
4436 * destroy_thread_windows
4438 * Destroy all window owned by the current thread.
4440 void destroy_thread_windows(void)
4442 WND *win, *free_list = NULL;
4443 HWND hwnd = 0;
4445 user_lock();
4446 while ((win = next_thread_window_ptr( &hwnd )))
4448 free_dce( win->dce, win->obj.handle );
4449 set_user_handle_ptr( hwnd, NULL );
4450 win->obj.handle = free_list;
4451 free_list = win;
4453 if (free_list)
4455 SERVER_START_REQ( destroy_window )
4457 req->handle = 0; /* destroy all thread windows */
4458 wine_server_call( req );
4460 SERVER_END_REQ;
4462 user_unlock();
4464 while ((win = free_list))
4466 free_list = win->obj.handle;
4467 TRACE( "destroying %p\n", win );
4469 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD && win->wIDmenu)
4470 NtUserDestroyMenu( UlongToHandle(win->wIDmenu) );
4471 if (win->hSysMenu) NtUserDestroyMenu( win->hSysMenu );
4472 if (win->surface)
4474 register_window_surface( win->surface, NULL );
4475 window_surface_release( win->surface );
4477 if (user_callbacks) user_callbacks->free_win_ptr( win );
4478 free( win );
4482 /***********************************************************************
4483 * create_window_handle
4485 * Create a window handle with the server.
4487 static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name,
4488 HINSTANCE instance, BOOL ansi,
4489 DWORD style, DWORD ex_style )
4491 DPI_AWARENESS awareness = get_thread_dpi_awareness();
4492 HWND handle = 0, full_parent = 0, full_owner = 0;
4493 struct tagCLASS *class = NULL;
4494 int extra_bytes = 0;
4495 UINT dpi = 0;
4496 WND *win;
4498 SERVER_START_REQ( create_window )
4500 req->parent = wine_server_user_handle( parent );
4501 req->owner = wine_server_user_handle( owner );
4502 req->instance = wine_server_client_ptr( instance );
4503 req->dpi = get_system_dpi();
4504 req->awareness = awareness;
4505 req->style = style;
4506 req->ex_style = ex_style;
4507 if (!(req->atom = get_int_atom_value( name )) && name->Length)
4508 wine_server_add_data( req, name->Buffer, name->Length );
4509 if (!wine_server_call_err( req ))
4511 handle = wine_server_ptr_handle( reply->handle );
4512 full_parent = wine_server_ptr_handle( reply->parent );
4513 full_owner = wine_server_ptr_handle( reply->owner );
4514 extra_bytes = reply->extra;
4515 dpi = reply->dpi;
4516 awareness = reply->awareness;
4517 class = wine_server_get_ptr( reply->class_ptr );
4520 SERVER_END_REQ;
4522 if (!handle)
4524 WARN( "error %d creating window\n", GetLastError() );
4525 return NULL;
4528 if (!(win = calloc( 1, FIELD_OFFSET(WND, wExtra) + extra_bytes )))
4530 SERVER_START_REQ( destroy_window )
4532 req->handle = wine_server_user_handle( handle );
4533 wine_server_call( req );
4535 SERVER_END_REQ;
4536 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
4537 return NULL;
4540 if (!parent) /* if parent is 0 we don't have a desktop window yet */
4542 struct user_thread_info *thread_info = get_user_thread_info();
4544 if (name->Buffer == (const WCHAR *)DESKTOP_CLASS_ATOM)
4546 if (!thread_info->top_window) thread_info->top_window = full_parent ? full_parent : handle;
4547 else assert( full_parent == thread_info->top_window );
4548 if (full_parent && !user_driver->pCreateDesktopWindow( thread_info->top_window ))
4549 ERR( "failed to create desktop window\n" );
4550 if (user_callbacks) user_callbacks->register_builtin_classes();
4552 else /* HWND_MESSAGE parent */
4554 if (!thread_info->msg_window && !full_parent) thread_info->msg_window = handle;
4558 user_lock();
4560 win->obj.handle = handle;
4561 win->obj.type = NTUSER_OBJ_WINDOW;
4562 win->parent = full_parent;
4563 win->owner = full_owner;
4564 win->class = class;
4565 win->winproc = get_class_winproc( class );
4566 win->cbWndExtra = extra_bytes;
4567 win->dpi = dpi;
4568 win->dpi_awareness = awareness;
4569 set_user_handle_ptr( handle, &win->obj );
4570 if (is_winproc_unicode( win->winproc, !ansi )) win->flags |= WIN_ISUNICODE;
4571 return win;
4574 static BOOL is_default_coord( int x )
4576 return x == CW_USEDEFAULT || x == 0x8000;
4579 /***********************************************************************
4580 * fix_cs_coordinates
4582 * Fix the coordinates and return default show mode in sw.
4584 static void fix_cs_coordinates( CREATESTRUCTW *cs, INT *sw )
4586 if (cs->style & (WS_CHILD | WS_POPUP))
4588 if (is_default_coord(cs->x)) cs->x = cs->y = 0;
4589 if (is_default_coord(cs->cx)) cs->cx = cs->cy = 0;
4591 else /* overlapped window */
4593 RTL_USER_PROCESS_PARAMETERS *params = NtCurrentTeb()->Peb->ProcessParameters;
4594 HMONITOR monitor;
4595 MONITORINFO mon_info;
4597 if (!is_default_coord( cs->x ) && !is_default_coord( cs->cx ) && !is_default_coord( cs->cy ))
4598 return;
4600 monitor = monitor_from_window( cs->hwndParent, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
4601 mon_info.cbSize = sizeof(mon_info);
4602 get_monitor_info( monitor, &mon_info );
4604 if (is_default_coord( cs->x ))
4606 if (!is_default_coord( cs->y )) *sw = cs->y;
4607 cs->x = (params->dwFlags & STARTF_USEPOSITION) ? params->dwX : mon_info.rcWork.left;
4608 cs->y = (params->dwFlags & STARTF_USEPOSITION) ? params->dwY : mon_info.rcWork.top;
4611 if (is_default_coord( cs->cx ))
4613 if (params->dwFlags & STARTF_USESIZE)
4615 cs->cx = params->dwXSize;
4616 cs->cy = params->dwYSize;
4618 else
4620 cs->cx = (mon_info.rcWork.right - mon_info.rcWork.left) * 3 / 4 - cs->x;
4621 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
4624 /* neither x nor cx are default. Check the y values.
4625 * In the trace we see Outlook and Outlook Express using
4626 * cy set to CW_USEDEFAULT when opening the address book.
4628 else if (is_default_coord( cs->cy ))
4630 FIXME( "Strange use of CW_USEDEFAULT in cy\n" );
4631 cs->cy = (mon_info.rcWork.bottom - mon_info.rcWork.top) * 3 / 4 - cs->y;
4636 /***********************************************************************
4637 * map_dpi_create_struct
4639 static void map_dpi_create_struct( CREATESTRUCTW *cs, UINT dpi_from, UINT dpi_to )
4641 if (!dpi_from && !dpi_to) return;
4642 if (!dpi_from || !dpi_to)
4644 POINT pt = { cs->x, cs->y };
4645 UINT mon_dpi = get_monitor_dpi( monitor_from_point( pt, MONITOR_DEFAULTTONEAREST, dpi_from ));
4646 if (!dpi_from) dpi_from = mon_dpi;
4647 else dpi_to = mon_dpi;
4649 if (dpi_from == dpi_to) return;
4650 cs->x = muldiv( cs->x, dpi_to, dpi_from );
4651 cs->y = muldiv( cs->y, dpi_to, dpi_from );
4652 cs->cx = muldiv( cs->cx, dpi_to, dpi_from );
4653 cs->cy = muldiv( cs->cy, dpi_to, dpi_from );
4656 /***********************************************************************
4657 * NtUserCreateWindowEx (win32u.@)
4659 HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
4660 UNICODE_STRING *version, UNICODE_STRING *window_name,
4661 DWORD style, INT x, INT y, INT cx, INT cy,
4662 HWND parent, HMENU menu, HINSTANCE instance, void *params,
4663 DWORD flags, CBT_CREATEWNDW *cbtc, DWORD unk, BOOL ansi )
4665 CREATESTRUCTW cs, *client_cs = cbtc->lpcs;
4666 UINT win_dpi, thread_dpi = get_thread_dpi();
4667 DPI_AWARENESS_CONTEXT context;
4668 HWND hwnd, owner = 0;
4669 INT sw = SW_SHOW;
4670 LRESULT result;
4671 RECT rect;
4672 WND *win;
4674 static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
4676 cs.lpCreateParams = params;
4677 cs.hInstance = client_cs->hInstance; /* may be different than instance for win16 */
4678 cs.hMenu = menu;
4679 cs.hwndParent = parent;
4680 cs.style = style;
4681 cs.dwExStyle = ex_style;
4682 cs.x = x;
4683 cs.y = y;
4684 cs.cx = cx;
4685 cs.cy = cy;
4686 /* We use client_cs to pass original class and name pointers,
4687 * that's probably not how native handles it. */
4688 cs.lpszName = client_cs->lpszName;
4689 cs.lpszClass = client_cs->lpszClass;
4691 /* Find the parent window */
4692 if (parent == HWND_MESSAGE)
4694 cs.hwndParent = parent = get_hwnd_message_parent();
4696 else if (parent)
4698 if ((cs.style & (WS_CHILD|WS_POPUP)) != WS_CHILD)
4700 owner = parent;
4701 parent = get_desktop_window();
4703 else
4705 DWORD parent_style = get_window_long( parent, GWL_EXSTYLE );
4706 if ((parent_style & WS_EX_LAYOUTRTL) && !(parent_style & WS_EX_NOINHERITLAYOUT))
4707 cs.dwExStyle |= WS_EX_LAYOUTRTL;
4710 else
4712 if ((cs.style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
4714 WARN( "No parent for child window\n" );
4715 SetLastError( ERROR_TLW_WITH_WSCHILD );
4716 return 0; /* WS_CHILD needs a parent, but WS_POPUP doesn't */
4719 /* are we creating the desktop or HWND_MESSAGE parent itself? */
4720 if (class_name->Buffer != (LPCWSTR)DESKTOP_CLASS_ATOM &&
4721 (class_name->Length != sizeof(messageW) ||
4722 wcsnicmp( class_name->Buffer, messageW, ARRAYSIZE(messageW) )))
4724 parent = get_desktop_window();
4728 fix_cs_coordinates( &cs, &sw );
4729 cs.dwExStyle = fix_exstyle( cs.style, cs.dwExStyle );
4731 /* Create the window structure */
4733 style = cs.style & ~WS_VISIBLE;
4734 ex_style = cs.dwExStyle & ~WS_EX_LAYERED;
4735 if (!(win = create_window_handle( parent, owner, class_name, instance, ansi, style, ex_style )))
4736 return 0;
4737 hwnd = win->obj.handle;
4739 /* Fill the window structure */
4741 win->tid = GetCurrentThreadId();
4742 win->hInstance = cs.hInstance;
4743 win->text = NULL;
4744 win->dwStyle = style;
4745 win->dwExStyle = ex_style;
4746 win->wIDmenu = 0;
4747 win->helpContext = 0;
4748 win->pScroll = NULL;
4749 win->userdata = 0;
4750 win->hIcon = 0;
4751 win->hIconSmall = 0;
4752 win->hIconSmall2 = 0;
4753 win->hSysMenu = 0;
4755 win->min_pos.x = win->min_pos.y = -1;
4756 win->max_pos.x = win->max_pos.y = -1;
4757 SetRect( &win->normal_rect, cs.x, cs.y, cs.x + cs.cx, cs.y + cs.cy );
4759 if (win->dwStyle & WS_SYSMENU) NtUserSetSystemMenu( hwnd, 0 );
4761 /* call the WH_CBT hook */
4763 release_win_ptr( win );
4764 *client_cs = cs;
4765 cbtc->hwndInsertAfter = HWND_TOP;
4766 if (call_hooks( WH_CBT, HCBT_CREATEWND, (WPARAM)hwnd, (LPARAM)cbtc, !ansi ))
4768 free_window_handle( hwnd );
4769 return 0;
4771 if (!(win = get_win_ptr( hwnd ))) return 0;
4774 * Correct the window styles.
4776 * It affects only the style loaded into the WND structure.
4779 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
4781 win->dwStyle |= WS_CLIPSIBLINGS;
4782 if (!(win->dwStyle & WS_POPUP)) win->dwStyle |= WS_CAPTION;
4785 win->dwExStyle = cs.dwExStyle;
4786 /* WS_EX_WINDOWEDGE depends on some other styles */
4787 if ((win->dwStyle & (WS_DLGFRAME | WS_THICKFRAME)) &&
4788 !(win->dwStyle & (WS_CHILD | WS_POPUP)))
4789 win->dwExStyle |= WS_EX_WINDOWEDGE;
4791 if (!(win->dwStyle & (WS_CHILD | WS_POPUP))) win->flags |= WIN_NEED_SIZE;
4793 SERVER_START_REQ( set_window_info )
4795 req->handle = wine_server_user_handle( hwnd );
4796 req->flags = SET_WIN_STYLE | SET_WIN_EXSTYLE | SET_WIN_INSTANCE | SET_WIN_UNICODE;
4797 req->style = win->dwStyle;
4798 req->ex_style = win->dwExStyle;
4799 req->instance = wine_server_client_ptr( win->hInstance );
4800 req->is_unicode = (win->flags & WIN_ISUNICODE) != 0;
4801 req->extra_offset = -1;
4802 wine_server_call( req );
4804 SERVER_END_REQ;
4806 /* Set the window menu */
4808 if ((win->dwStyle & (WS_CHILD | WS_POPUP)) != WS_CHILD)
4810 if (cs.hMenu && user_callbacks && !user_callbacks->set_menu( hwnd, cs.hMenu ))
4812 release_win_ptr( win );
4813 free_window_handle( hwnd );
4814 return 0;
4817 else NtUserSetWindowLongPtr( hwnd, GWLP_ID, (ULONG_PTR)cs.hMenu, FALSE );
4819 win_dpi = win->dpi;
4820 release_win_ptr( win );
4822 if (parent) map_dpi_create_struct( &cs, thread_dpi, win_dpi );
4824 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
4826 /* send the WM_GETMINMAXINFO message and fix the size if needed */
4828 cx = cs.cx;
4829 cy = cs.cy;
4830 if ((cs.style & WS_THICKFRAME) || !(cs.style & (WS_POPUP | WS_CHILD)))
4832 MINMAXINFO info = get_min_max_info( hwnd );
4833 cx = max( min( cx, info.ptMaxTrackSize.x ), info.ptMinTrackSize.x );
4834 cy = max( min( cy, info.ptMaxTrackSize.y ), info.ptMinTrackSize.y );
4837 if (cx < 0) cx = 0;
4838 if (cy < 0) cy = 0;
4839 set_rect( &rect, cs.x, cs.y, cs.x + cx, cs.y + cy );
4840 /* check for wraparound */
4841 if (cs.x > 0x7fffffff - cx) rect.right = 0x7fffffff;
4842 if (cs.y > 0x7fffffff - cy) rect.bottom = 0x7fffffff;
4843 if (!apply_window_pos( hwnd, 0, SWP_NOZORDER | SWP_NOACTIVATE, &rect, &rect, NULL )) goto failed;
4845 /* send WM_NCCREATE */
4847 TRACE( "hwnd %p cs %d,%d %dx%d %s\n", hwnd, cs.x, cs.y, cs.cx, cs.cy, wine_dbgstr_rect(&rect) );
4848 *client_cs = cs;
4849 if (!NtUserMessageCall( hwnd, WM_NCCREATE, 0, (LPARAM)client_cs, (ULONG_PTR)&result,
4850 FNID_SENDMESSAGE, ansi ) || !result)
4852 WARN( "%p: aborted by WM_NCCREATE\n", hwnd );
4853 goto failed;
4856 /* create default IME window */
4858 if (!is_desktop_window( hwnd ) && parent != get_hwnd_message_parent() &&
4859 user_callbacks && user_callbacks->register_imm( hwnd ))
4861 TRACE( "register IME window for %p\n", hwnd );
4862 win_set_flags( hwnd, WIN_HAS_IME_WIN, 0 );
4865 /* send WM_NCCALCSIZE */
4867 if (get_window_rects( hwnd, COORDS_PARENT, &rect, NULL, win_dpi ))
4869 /* yes, even if the CBT hook was called with HWND_TOP */
4870 HWND insert_after = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) ? HWND_BOTTOM : HWND_TOP;
4871 RECT client_rect = rect;
4873 /* the rectangle is in screen coords for WM_NCCALCSIZE when wparam is FALSE */
4874 map_window_points( parent, 0, (POINT *)&client_rect, 2, win_dpi );
4875 send_message( hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&client_rect );
4876 map_window_points( 0, parent, (POINT *)&client_rect, 2, win_dpi );
4877 apply_window_pos( hwnd, insert_after, SWP_NOACTIVATE, &rect, &client_rect, NULL );
4879 else goto failed;
4881 /* send WM_CREATE */
4882 if (!NtUserMessageCall( hwnd, WM_CREATE, 0, (LPARAM)client_cs, (ULONG_PTR)&result,
4883 FNID_SENDMESSAGE, ansi ) || result == -1) goto failed;
4884 cs = *client_cs;
4886 /* call the driver */
4888 if (!user_driver->pCreateWindow( hwnd )) goto failed;
4890 NtUserNotifyWinEvent( EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0 );
4892 /* send the size messages */
4894 if (!(win_get_flags( hwnd ) & WIN_NEED_SIZE))
4896 get_window_rects( hwnd, COORDS_PARENT, NULL, &rect, win_dpi );
4897 send_message( hwnd, WM_SIZE, SIZE_RESTORED,
4898 MAKELONG(rect.right-rect.left, rect.bottom-rect.top));
4899 send_message( hwnd, WM_MOVE, 0, MAKELONG( rect.left, rect.top ) );
4902 /* Show the window, maximizing or minimizing if needed */
4904 style = set_window_style( hwnd, 0, WS_MAXIMIZE | WS_MINIMIZE );
4905 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
4907 RECT new_pos;
4908 UINT sw_flags = (style & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
4910 sw_flags = window_min_maximize( hwnd, sw_flags, &new_pos );
4911 sw_flags |= SWP_FRAMECHANGED; /* Frame always gets changed */
4912 if (!(style & WS_VISIBLE) || (style & WS_CHILD) || get_active_window())
4913 sw_flags |= SWP_NOACTIVATE;
4914 NtUserSetWindowPos( hwnd, 0, new_pos.left, new_pos.top, new_pos.right - new_pos.left,
4915 new_pos.bottom - new_pos.top, sw_flags );
4918 /* Notify the parent window only */
4920 send_parent_notify( hwnd, WM_CREATE );
4921 if (!is_window( hwnd ))
4923 set_thread_dpi_awareness_context( context );
4924 return 0;
4927 if (parent == get_desktop_window())
4928 post_message( parent, WM_PARENTNOTIFY, WM_CREATE, (LPARAM)hwnd );
4930 if (cs.style & WS_VISIBLE)
4932 if (cs.style & WS_MAXIMIZE)
4933 sw = SW_SHOW;
4934 else if (cs.style & WS_MINIMIZE)
4935 sw = SW_SHOWMINIMIZED;
4937 NtUserShowWindow( hwnd, sw );
4938 if (cs.dwExStyle & WS_EX_MDICHILD)
4940 send_message( cs.hwndParent, WM_MDIREFRESHMENU, 0, 0 );
4941 /* ShowWindow won't activate child windows */
4942 NtUserSetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE );
4946 /* Call WH_SHELL hook */
4948 if (!(get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) && !get_window_relative( hwnd, GW_OWNER ))
4949 call_hooks( WH_SHELL, HSHELL_WINDOWCREATED, (WPARAM)hwnd, 0, TRUE );
4951 TRACE( "created window %p\n", hwnd );
4952 set_thread_dpi_awareness_context( context );
4953 return hwnd;
4955 failed:
4956 destroy_window( hwnd );
4957 set_thread_dpi_awareness_context( context );
4958 return 0;
4961 /*****************************************************************************
4962 * NtUserCallHwnd (win32u.@)
4964 ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code )
4966 switch (code)
4968 case NtUserArrangeIconicWindows:
4969 return arrange_iconic_windows( hwnd );
4970 case NtUserGetDpiForWindow:
4971 return get_dpi_for_window( hwnd );
4972 case NtUserGetParent:
4973 return HandleToUlong( get_parent( hwnd ));
4974 case NtUserGetWindowContextHelpId:
4975 return get_window_context_help_id( hwnd );
4976 case NtUserGetWindowDpiAwarenessContext:
4977 return (ULONG_PTR)get_window_dpi_awareness_context( hwnd );
4978 case NtUserGetWindowTextLength:
4979 return get_server_window_text( hwnd, NULL, 0 );
4980 case NtUserIsWindow:
4981 return is_window( hwnd );
4982 case NtUserIsWindowUnicode:
4983 return is_window_unicode( hwnd );
4984 case NtUserIsWindowVisible:
4985 return is_window_visible( hwnd );
4986 default:
4987 FIXME( "invalid code %u\n", code );
4988 return 0;
4992 /*****************************************************************************
4993 * NtUserCallHwndParam (win32u.@)
4995 ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
4997 switch (code)
4999 case NtUserGetClassLongA:
5000 return get_class_long( hwnd, param, TRUE );
5001 case NtUserGetClassLongW:
5002 return get_class_long( hwnd, param, FALSE );
5003 case NtUserGetClassLongPtrA:
5004 return get_class_long_ptr( hwnd, param, TRUE );
5005 case NtUserGetClassLongPtrW:
5006 return get_class_long_ptr( hwnd, param, FALSE );
5007 case NtUserGetClassWord:
5008 return get_class_word( hwnd, param );
5009 case NtUserGetClientRect:
5010 return get_client_rect( hwnd, (RECT *)param );
5011 case NtUserGetMinMaxInfo:
5012 *(MINMAXINFO *)param = get_min_max_info( hwnd );
5013 return 0;
5014 case NtUserGetWindowInfo:
5015 return get_window_info( hwnd, (WINDOWINFO *)param );
5016 case NtUserGetWindowLongA:
5017 return get_window_long_size( hwnd, param, sizeof(LONG), TRUE );
5018 case NtUserGetWindowLongW:
5019 return get_window_long( hwnd, param );
5020 case NtUserGetWindowLongPtrA:
5021 return get_window_long_ptr( hwnd, param, TRUE );
5022 case NtUserGetWindowLongPtrW:
5023 return get_window_long_ptr( hwnd, param, FALSE );
5024 case NtUserGetWindowPlacement:
5025 return get_window_placement( hwnd, (WINDOWPLACEMENT *)param );
5026 case NtUserGetWindowRect:
5027 return get_window_rect( hwnd, (RECT *)param, get_thread_dpi() );
5028 case NtUserGetWindowRelative:
5029 return HandleToUlong( get_window_relative( hwnd, param ));
5030 case NtUserGetWindowThread:
5031 return get_window_thread( hwnd, (DWORD *)param );
5032 case NtUserGetWindowWord:
5033 return get_window_word( hwnd, param );
5034 case NtUserIsChild:
5035 return is_child( hwnd, UlongToHandle(param) );
5036 case NtUserKillSystemTimer:
5037 return kill_system_timer( hwnd, param );
5038 case NtUserMonitorFromWindow:
5039 return HandleToUlong( monitor_from_window( hwnd, param, NtUserMonitorFromWindow ));
5040 case NtUserScreenToClient:
5041 return screen_to_client( hwnd, (POINT *)param );
5042 case NtUserSetCaptureWindow:
5043 return set_capture_window( hwnd, param, NULL );
5044 case NtUserSetForegroundWindow:
5045 return set_foreground_window( hwnd, param );
5046 case NtUserSetWindowPixelFormat:
5047 return set_window_pixel_format( hwnd, param );
5048 /* temporary exports */
5049 case NtUserIsWindowDrawable:
5050 return is_window_drawable( hwnd, param );
5051 case NtUserSetWindowStyle:
5053 STYLESTRUCT *style = (void *)param;
5054 return set_window_style( hwnd, style->styleNew, style->styleOld );
5056 case NtUserSpyGetMsgName:
5057 return (UINT_PTR)debugstr_msg_name( param, hwnd );
5058 default:
5059 FIXME( "invalid code %u\n", code );
5060 return 0;